Forked from
leap / bitmask-vpn
853 commits behind the upstream repository.
-
Kali Kaneko authoredKali Kaneko authored
systray.go 7.09 KiB
// Copyright (C) 2018 LEAP
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package systray
import (
"fmt"
"log"
"os"
"os/signal"
"strconv"
"time"
"0xacab.org/leap/bitmask-vpn/icon"
"0xacab.org/leap/bitmask-vpn/pkg/bitmask"
"0xacab.org/leap/bitmask-vpn/pkg/config"
"github.com/getlantern/systray"
"github.com/skratchdot/open-golang/open"
)
type bmTray struct {
bm bitmask.Bitmask
conf *Config
notify *notificator
waitCh chan bool
mStatus *systray.MenuItem
mTurnOn *systray.MenuItem
mTurnOff *systray.MenuItem
mHelp *systray.MenuItem
mDonate *systray.MenuItem
mAbout *systray.MenuItem
mQuit *systray.MenuItem
activeGateway *gatewayTray
autostart bitmask.Autostart
}
type gatewayTray struct {
menuItem *systray.MenuItem
name string
}
func (bt *bmTray) start() {
// XXX this removes the snap error message, but produces an invisible icon.
// https://0xacab.org/leap/riseup_vpn/issues/44
// os.Setenv("TMPDIR", "/var/tmp")
systray.Run(bt.onReady, bt.onExit)
}
func (bt *bmTray) quit() {
systray.Quit()
}
func (bt *bmTray) onExit() {
log.Println("Closing systray")
}
func (bt *bmTray) onReady() {
printer := bt.conf.Printer
systray.SetIcon(icon.Off)
bt.mStatus = systray.AddMenuItem(printer.Sprintf("Checking status..."), "")
bt.mStatus.Disable()
bt.waitCh <- true
}
func (bt *bmTray) setUpSystray() {
printer := bt.conf.Printer
bt.mTurnOn = systray.AddMenuItem(printer.Sprintf("Turn on"), "")
bt.mTurnOn.Hide()
bt.mTurnOff = systray.AddMenuItem(printer.Sprintf("Turn off"), "")
bt.mTurnOff.Hide()
systray.AddSeparator()
if bt.conf.SelectGateway {
bt.addGateways()
}
bt.mHelp = systray.AddMenuItem(printer.Sprintf("Help..."), "")
bt.mDonate = systray.AddMenuItem(printer.Sprintf("Donate..."), "")
bt.mAbout = systray.AddMenuItem(printer.Sprintf("About..."), "")
systray.AddSeparator()
bt.mQuit = systray.AddMenuItem(printer.Sprintf("Quit"), "")
showDonate, err := strconv.ParseBool(config.AskForDonations)
if err != nil {
log.Printf("Error parsing AskForDonations: %v", err)
showDonate = true
}
if !showDonate {
bt.mDonate.Hide()
}
}
func (bt *bmTray) loop(bm bitmask.Bitmask, notify *notificator, as bitmask.Autostart) {
<-bt.waitCh
bt.waitCh = nil
bt.bm = bm
bt.notify = notify
bt.autostart = as
bt.setUpSystray()
signalCh := make(chan os.Signal, 1)
signal.Notify(signalCh, os.Interrupt)
ch := bt.bm.GetStatusCh()
if status, err := bt.bm.GetStatus(); err != nil {
log.Printf("Error getting status: %v", err)
} else {
bt.changeStatus(status)
}
for {
select {
case status := <-ch:
log.Println("status: " + status)
bt.changeStatus(status)
case <-bt.mTurnOn.ClickedCh:
log.Println("on")
bt.changeStatus("starting")
bt.bm.StartVPN(config.Provider)
bt.conf.setUserStoppedVPN(false)
case <-bt.mTurnOff.ClickedCh:
log.Println("off")
bt.changeStatus("stopping")
bt.bm.StopVPN()
bt.conf.setUserStoppedVPN(true)
case <-bt.mHelp.ClickedCh:
open.Run(config.HelpURL)
case <-bt.mDonate.ClickedCh:
bt.conf.setDonated()
open.Run(config.DonateURL)
case <-bt.mAbout.ClickedCh:
bitmaskVersion, err := bt.bm.Version()
versionStr := bt.conf.Version
if err != nil {
log.Printf("Error getting version: %v", err)
} else if bitmaskVersion != "" {
versionStr = fmt.Sprintf("%s (bitmaskd %s)", bt.conf.Version, bitmaskVersion)
}
go bt.notify.about(versionStr)
case <-bt.mQuit.ClickedCh:
err := bt.autostart.Disable()
if err != nil {
log.Printf("Error disabling autostart: %v", err)
}
/* we return and leave bt.quit() to the caller */
return
case <-signalCh:
/* we return and leave bt.quit() to the caller */
return
case <-time.After(5 * time.Second):
if status, err := bt.bm.GetStatus(); err != nil {
log.Printf("Error getting status: %v", err)
} else {
bt.changeStatus(status)
}
}
}
}
func (bt *bmTray) addGateways() {
gatewayList, err := bt.bm.ListGateways(config.Provider)
if err != nil {
log.Printf("Gateway initialization error: %v", err)
return
}
mGateway := systray.AddMenuItem(bt.conf.Printer.Sprintf("Route traffic through:"), "")
mGateway.Disable()
for i, city := range gatewayList {
menuItem := systray.AddMenuItem(city, bt.conf.Printer.Sprintf("Use %s %v gateway", config.ApplicationName, city))
gateway := gatewayTray{menuItem, city}
if i == 0 {
menuItem.Check()
menuItem.SetTitle("*" + city)
bt.activeGateway = &gateway
} else {
menuItem.Uncheck()
}
go func(gateway gatewayTray) {
for {
<-menuItem.ClickedCh
gateway.menuItem.SetTitle("*" + gateway.name)
gateway.menuItem.Check()
bt.activeGateway.menuItem.Uncheck()
bt.activeGateway.menuItem.SetTitle(bt.activeGateway.name)
bt.activeGateway = &gateway
bt.bm.UseGateway(gateway.name)
log.Printf("Manual connection to %s gateway\n", gateway.name)
bt.bm.StartVPN(config.Provider)
}
}(gateway)
}
systray.AddSeparator()
}
func (bt *bmTray) changeStatus(status string) {
printer := bt.conf.Printer
if bt.waitCh != nil {
bt.waitCh <- true
bt.waitCh = nil
}
var statusStr string
switch status {
case "on":
systray.SetIcon(icon.On)
bt.mTurnOff.SetTitle(printer.Sprintf("Turn off"))
statusStr = printer.Sprintf("%s on", config.ApplicationName)
bt.mTurnOn.Hide()
bt.mTurnOff.Show()
case "off":
systray.SetIcon(icon.Off)
bt.mTurnOn.SetTitle(printer.Sprintf("Turn on"))
statusStr = printer.Sprintf("%s off", config.ApplicationName)
bt.mTurnOn.Show()
bt.mTurnOff.Hide()
case "starting":
bt.waitCh = make(chan bool)
go bt.waitIcon()
bt.mTurnOff.SetTitle(printer.Sprintf("Cancel"))
statusStr = printer.Sprintf("Connecting to %s", config.ApplicationName)
bt.mTurnOn.Hide()
bt.mTurnOff.Show()
case "stopping":
bt.waitCh = make(chan bool)
go bt.waitIcon()
statusStr = printer.Sprintf("Stopping %s", config.ApplicationName)
bt.mTurnOn.Hide()
bt.mTurnOff.Hide()
case "failed":
systray.SetIcon(icon.Blocked)
bt.mTurnOn.SetTitle(printer.Sprintf("Reconnect"))
bt.mTurnOff.SetTitle(printer.Sprintf("Turn off"))
statusStr = printer.Sprintf("%s blocking internet", config.ApplicationName)
bt.mTurnOn.Show()
bt.mTurnOff.Show()
}
systray.SetTooltip(statusStr)
bt.mStatus.SetTitle(statusStr)
}
func (bt *bmTray) waitIcon() {
icons := [][]byte{icon.Wait0, icon.Wait1, icon.Wait2, icon.Wait3}
for i := 0; true; i = (i + 1) % 4 {
systray.SetIcon(icons[i])
select {
case <-bt.waitCh:
return
case <-time.After(time.Millisecond * 500):
continue
}
}
}