Skip to content
Snippets Groups Projects
systray.go 6.51 KiB
Newer Older
  • Learn to ignore specific revisions
  • meskio's avatar
    meskio committed
    // 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 main
    
    import (
    
    	"os"
    	"os/signal"
    
    meskio's avatar
    meskio committed
    	"time"
    
    	"0xacab.org/leap/bitmask-systray/bitmask"
    	"0xacab.org/leap/bitmask-systray/icon"
    
    	"github.com/getlantern/systray"
    
    	"github.com/skratchdot/open-golang/open"
    )
    
    type bmTray struct {
    
    	bm            bitmask.Bitmask
    
    meskio's avatar
    meskio committed
    	notify        *notificator
    
    meskio's avatar
    meskio committed
    	waitCh        chan bool
    	mStatus       *systray.MenuItem
    	mTurnOn       *systray.MenuItem
    	mTurnOff      *systray.MenuItem
    
    meskio's avatar
    meskio committed
    	mCancel       *systray.MenuItem
    	activeGateway *gatewayTray
    
    meskio's avatar
    meskio committed
    }
    
    type gatewayTray struct {
    	menuItem *systray.MenuItem
    	name     string
    
    func run(bm bitmask.Bitmask, conf *systrayConfig, notify *notificator, as autostart) {
    	bt := bmTray{bm: bm, conf: conf, notify: notify, autostart: as}
    
    	systray.Run(bt.onReady, bt.onExit)
    }
    
    func (bt bmTray) onExit() {
    
    meskio's avatar
    meskio committed
    	status, _ := bt.bm.GetStatus()
    	if status != "off" {
    		ch := bt.bm.GetStatusCh()
    		bt.bm.StopVPN()
    		status = <-ch
    	}
    
    meskio's avatar
    meskio committed
    	log.Println("Closing systray")
    
    }
    
    func (bt *bmTray) onReady() {
    
    	signalCh := make(chan os.Signal, 1)
    	signal.Notify(signalCh, os.Interrupt)
    
    
    	systray.SetIcon(icon.Off)
    
    
    meskio's avatar
    meskio committed
    	bt.mStatus = systray.AddMenuItem(printer.Sprintf("Checking status..."), "")
    
    	bt.mStatus.Disable()
    
    meskio's avatar
    meskio committed
    	bt.mTurnOn = systray.AddMenuItem(printer.Sprintf("Turn on"), "")
    
    	bt.mTurnOn.Hide()
    
    meskio's avatar
    meskio committed
    	bt.mTurnOff = systray.AddMenuItem(printer.Sprintf("Turn off"), "")
    
    	bt.mTurnOff.Hide()
    
    meskio's avatar
    meskio committed
    	bt.mCancel = systray.AddMenuItem(printer.Sprintf("Cancel"), printer.Sprintf("Cancel connection to %s", applicationName))
    
    	bt.mCancel.Hide()
    
    	systray.AddSeparator()
    
    
    	if bt.conf.SelectGateway {
    
    meskio's avatar
    meskio committed
    	mHelp := systray.AddMenuItem(printer.Sprintf("Help..."), "")
    	bt.mDonate = systray.AddMenuItem(printer.Sprintf("Donate..."), "")
    	mAbout := systray.AddMenuItem(printer.Sprintf("About..."), "")
    
    	systray.AddSeparator()
    
    
    meskio's avatar
    meskio committed
    	mQuit := systray.AddMenuItem(printer.Sprintf("Quit"), "")
    
    		ch := bt.bm.GetStatusCh()
    
    meskio's avatar
    meskio committed
    		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")
    
    meskio's avatar
    meskio committed
    				bt.changeStatus("starting")
    
    				bt.bm.StartVPN(provider)
    
    				bt.conf.setUserStoppedVPN(false)
    
    			case <-bt.mTurnOff.ClickedCh:
    
    				log.Println("off")
    
    meskio's avatar
    meskio committed
    				bt.changeStatus("stopping")
    
    				bt.bm.StopVPN()
    
    				bt.conf.setUserStoppedVPN(true)
    
    			case <-bt.mCancel.ClickedCh:
    
    				log.Println("cancel")
    
    meskio's avatar
    meskio committed
    				bt.changeStatus("stopping")
    
    				bt.bm.StopVPN()
    
    				bt.conf.setUserStoppedVPN(true)
    
    
    			case <-mHelp.ClickedCh:
    
    meskio's avatar
    meskio committed
    				open.Run("https://riseup.net/vpn/support")
    
    meskio's avatar
    meskio committed
    				open.Run("https://riseup.net/vpn/donate")
    			case <-mAbout.ClickedCh:
    
    				bitmaskVersion, err := bt.bm.Version()
    
    				versionStr := version
    
    				if err != nil {
    					log.Printf("Error getting version: %v", err)
    
    				} else if bitmaskVersion != "" {
    					versionStr = fmt.Sprintf("%s (bitmaskd %s)", version, bitmaskVersion)
    
    				bt.notify.about(versionStr)
    
    
    			case <-mQuit.ClickedCh:
    
    				err := bt.autostart.Disable()
    				if err != nil {
    					log.Printf("Error disabling autostart: %v", err)
    				}
    
    				systray.Quit()
    
    			case <-signalCh:
    				systray.Quit()
    
    
    			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)
    				}
    
    meskio's avatar
    meskio committed
    func (bt *bmTray) addGateways() {
    	gatewayList, err := bt.bm.ListGateways(provider)
    	if err != nil {
    		log.Printf("Gateway initialization error: %v", err)
    		return
    	}
    
    
    meskio's avatar
    meskio committed
    	mGateway := systray.AddMenuItem(printer.Sprintf("Route traffic through"), "")
    
    meskio's avatar
    meskio committed
    	mGateway.Disable()
    
    meskio's avatar
    meskio committed
    	for i, city := range gatewayList {
    		menuItem := systray.AddMenuItem(city, printer.Sprintf("Use %s %v gateway", applicationName, city))
    		gateway := gatewayTray{menuItem, city}
    
    meskio's avatar
    meskio committed
    
    		if i == 0 {
    			menuItem.Check()
    
    meskio's avatar
    meskio committed
    			menuItem.SetTitle("*" + city)
    
    meskio's avatar
    meskio committed
    			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)
    			}
    		}(gateway)
    	}
    
    	systray.AddSeparator()
    }
    
    
    func (bt *bmTray) changeStatus(status string) {
    
    meskio's avatar
    meskio committed
    	bt.mTurnOn.SetTitle(printer.Sprintf("Turn on"))
    
    meskio's avatar
    meskio committed
    	if bt.waitCh != nil {
    		bt.waitCh <- true
    		bt.waitCh = nil
    	}
    
    meskio's avatar
    meskio committed
    	var statusStr string
    
    	switch status {
    	case "on":
    		systray.SetIcon(icon.On)
    
    meskio's avatar
    meskio committed
    		statusStr = printer.Sprintf("%s on", applicationName)
    
    		bt.mTurnOn.Hide()
    		bt.mTurnOff.Show()
    		bt.mCancel.Hide()
    
    
    	case "off":
    		systray.SetIcon(icon.Off)
    
    meskio's avatar
    meskio committed
    		statusStr = printer.Sprintf("%s off", applicationName)
    
    		bt.mTurnOn.Show()
    		bt.mTurnOff.Hide()
    		bt.mCancel.Hide()
    
    
    	case "starting":
    
    meskio's avatar
    meskio committed
    		bt.waitCh = make(chan bool)
    		go bt.waitIcon()
    
    meskio's avatar
    meskio committed
    		statusStr = printer.Sprintf("Connecting to %s", applicationName)
    
    		bt.mTurnOn.Hide()
    		bt.mTurnOff.Hide()
    		bt.mCancel.Show()
    
    
    	case "stopping":
    
    meskio's avatar
    meskio committed
    		bt.waitCh = make(chan bool)
    		go bt.waitIcon()
    
    meskio's avatar
    meskio committed
    		statusStr = printer.Sprintf("Stopping %s", applicationName)
    
    		bt.mTurnOn.Hide()
    		bt.mTurnOff.Hide()
    		bt.mCancel.Hide()
    
    elijah's avatar
    elijah committed
    		systray.SetIcon(icon.Blocked)
    
    meskio's avatar
    meskio committed
    		bt.mTurnOn.SetTitle(printer.Sprintf("Retry"))
    
    meskio's avatar
    meskio committed
    		statusStr = printer.Sprintf("%s blocking internet", applicationName)
    
    		bt.mTurnOn.Show()
    		bt.mTurnOff.Show()
    		bt.mCancel.Hide()
    
    meskio's avatar
    meskio committed
    
    	systray.SetTooltip(statusStr)
    	bt.mStatus.SetTitle(statusStr)
    
    meskio's avatar
    meskio committed
    
    func (bt *bmTray) waitIcon() {
    	icons := [][]byte{icon.Wait0, icon.Wait1, icon.Wait2, icon.Wait3}
    
    meskio's avatar
    meskio committed
    	for i := 0; true; i = (i + 1) % 4 {
    
    meskio's avatar
    meskio committed
    		systray.SetIcon(icons[i])
    
    meskio's avatar
    meskio committed
    		select {
    		case <-bt.waitCh:
    			return
    		case <-time.After(time.Millisecond * 500):
    
    meskio's avatar
    meskio committed
    			continue
    
    meskio's avatar
    meskio committed
    		}
    	}
    }