-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
support setting system proxy when vpn switch is toggled
- Loading branch information
Showing
18 changed files
with
1,287 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
package app | ||
|
||
import ( | ||
"fmt" | ||
"path/filepath" | ||
"strings" | ||
"sync" | ||
"time" | ||
|
||
"github.com/getlantern/errors" | ||
"github.com/getlantern/filepersist" | ||
"github.com/getlantern/flashlight/v7/client" | ||
"github.com/getlantern/flashlight/v7/common" | ||
"github.com/getlantern/flashlight/v7/ops" | ||
"github.com/getlantern/sysproxy" | ||
|
||
"github.com/getlantern/android-lantern/desktop/icons" | ||
) | ||
|
||
var ( | ||
_sysproxyOff func() error | ||
sysproxyOffMx sync.Mutex | ||
) | ||
|
||
func setUpSysproxyTool() error { | ||
var iconFile string | ||
if common.Platform == "darwin" { | ||
icon, err := icons.Asset(appIcon("connected")) | ||
if err != nil { | ||
return fmt.Errorf("unable to load escalation prompt icon: %v", err) | ||
} | ||
// We have to use a short filepath here because Cocoa won't display the | ||
// icon if the path is too long. | ||
iconFile = filepath.Join("/tmp", appIcon("escalate")) | ||
err = filepersist.Save(iconFile, icon, 0644) | ||
if err != nil { | ||
log.Errorf("Unable to persist icon to disk, fallback to default icon: %v", err) | ||
} else { | ||
log.Debugf("Saved icon file to: %v", iconFile) | ||
} | ||
} | ||
err := sysproxy.EnsureHelperToolPresent("sysproxy-cmd", "Lantern would like to be your system proxy", iconFile) | ||
if err != nil { | ||
return fmt.Errorf("unable to set up sysproxy setting tool: %v", err) | ||
} | ||
return nil | ||
} | ||
|
||
func SysproxyOn() (err error) { | ||
op := ops.Begin("sysproxy_on") | ||
defer op.End() | ||
addr, found := getProxyAddr() | ||
if !found { | ||
err = errors.New("Unable to set lantern as system proxy, no proxy address available") | ||
op.FailIf(log.Error(err)) | ||
return | ||
} | ||
log.Debugf("Setting lantern as system proxy at: %v", addr) | ||
off, e := sysproxy.On(addr) | ||
if e != nil { | ||
err = errors.New("Unable to set lantern as system proxy: %v", e) | ||
op.FailIf(log.Error(err)) | ||
return | ||
} | ||
sysproxyOffMx.Lock() | ||
_sysproxyOff = off | ||
sysproxyOffMx.Unlock() | ||
log.Debug("Finished setting lantern as system proxy") | ||
return | ||
} | ||
|
||
func SysProxyOff() { | ||
sysproxyOffMx.Lock() | ||
off := _sysproxyOff | ||
_sysproxyOff = nil | ||
sysproxyOffMx.Unlock() | ||
|
||
if off != nil { | ||
doSysproxyOff(off) | ||
} | ||
|
||
op := ops.Begin("sysproxy_off_force") | ||
defer op.End() | ||
log.Debug("Force clearing system proxy directly, just in case") | ||
addr, found := getProxyAddr() | ||
if !found { | ||
op.FailIf(log.Error("Unable to find proxy address, can't force clear system proxy")) | ||
return | ||
} | ||
doSysproxyClear(op, addr) | ||
} | ||
|
||
func doSysproxyOff(off func() error) { | ||
op := ops.Begin("sysproxy_off") | ||
defer op.End() | ||
log.Debug("Unsetting lantern as system proxy using off function") | ||
err := off() | ||
if err != nil { | ||
op.FailIf(log.Errorf("Unable to unset lantern as system proxy using off function: %v", err)) | ||
return | ||
} | ||
log.Debug("Unset lantern as system proxy using off function") | ||
} | ||
|
||
// clearSysproxyFor is like sysproxyOffFor, but records its activity under the | ||
// sysproxy_clear op instead of the sysproxy_off op. | ||
func clearSysproxyFor(addr string) { | ||
op := ops.Begin("sysproxy_clear") | ||
doSysproxyClear(op, addr) | ||
op.End() | ||
} | ||
|
||
func doSysproxyClear(op *ops.Op, addr string) { | ||
log.Debugf("Clearing lantern as system proxy at: %v", addr) | ||
err := sysproxy.Off(addr) | ||
if err != nil { | ||
op.FailIf(log.Errorf("Unable to clear lantern as system proxy: %v", err)) | ||
} else { | ||
log.Debug("Cleared lantern as system proxy") | ||
} | ||
} | ||
|
||
func getProxyAddr() (addr string, found bool) { | ||
var _addr interface{} | ||
_addr, found = client.Addr(5 * time.Minute) | ||
if found { | ||
addr = _addr.(string) | ||
} | ||
return | ||
} | ||
|
||
func appIcon(name string) string { | ||
return strings.ToLower(common.DefaultAppName) + "_" + fmt.Sprintf(iconTemplate(), name) | ||
} | ||
|
||
func iconTemplate() string { | ||
if common.Platform == "darwin" { | ||
if common.DefaultAppName == "Beam" { | ||
return "%s_32.png" | ||
} | ||
// Lantern doesn't have png files to support dark mode yet | ||
return "%s_32.ico" | ||
} | ||
return "%s_32.ico" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
//go:build !headless | ||
// +build !headless | ||
|
||
package app | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
|
||
"github.com/getlantern/flashlight/v7/common" | ||
"github.com/getlantern/flashlight/v7/stats" | ||
"github.com/getlantern/i18n" | ||
"github.com/getlantern/systray" | ||
|
||
"github.com/getlantern/lantern-desktop/icons" | ||
) | ||
|
||
var menu struct { | ||
enable bool | ||
st stats.Stats | ||
stMx sync.RWMutex | ||
status *systray.MenuItem | ||
toggle *systray.MenuItem | ||
show *systray.MenuItem | ||
upgrade *systray.MenuItem | ||
quit *systray.MenuItem | ||
} | ||
|
||
var iconsByName = make(map[string][]byte) | ||
|
||
type systrayCallbacks interface { | ||
WaitForExit() error | ||
AddExitFunc(string, func()) | ||
Exit(error) bool | ||
OnTrayShow() | ||
OnTrayUpgrade() | ||
Connect() | ||
Disconnect() | ||
OnStatsChange(func(stats.Stats)) | ||
} | ||
|
||
func RunOnSystrayReady(standalone bool, a systrayCallbacks, onReady func()) { | ||
onExit := func() { | ||
if a.Exit(nil) { | ||
err := a.WaitForExit() | ||
if err != nil { | ||
log.Errorf("Error exiting app: %v", err) | ||
} | ||
} | ||
} | ||
|
||
if standalone { | ||
log.Error("Standalone mode currently not supported, opening in system browser") | ||
// TODO: re-enable standalone mode when systray library has been stabilized | ||
// systray.RunWithAppWindow(i18n.T("TRAY_LANTERN"), 1024, 768, onReady, onExit) | ||
// } else { | ||
} | ||
systray.Run(onReady, onExit) | ||
} | ||
|
||
func QuitSystray(a *App) { | ||
// Typically, systray.Quit will actually be what causes the app to exit, but | ||
// in the case of an uncaught Fatal error or CTRL-C, the app will exit before the | ||
// systray and we need it to call systray.Quit(). | ||
systray.Quit() | ||
} | ||
|
||
func configureSystemTray(a systrayCallbacks) error { | ||
for _, name := range []string{"connected", "connectedalert", "disconnected", "disconnectedalert"} { | ||
icon, err := icons.Asset(appIcon(name)) | ||
if err != nil { | ||
return fmt.Errorf("Unable to load %v icon for system tray: %v", name, err) | ||
} | ||
iconsByName[name] = icon | ||
} | ||
|
||
menu.status = systray.AddMenuItem("", "") | ||
menu.status.Disable() | ||
menu.toggle = systray.AddMenuItem("", "") | ||
systray.AddSeparator() | ||
menu.upgrade = systray.AddMenuItem("", "") | ||
menu.show = systray.AddMenuItem("", "") | ||
systray.AddSeparator() | ||
menu.quit = systray.AddMenuItem("", "") | ||
refreshMenuItems() | ||
|
||
// Suppress showing "Update to Pro" until user status is got from pro-server. | ||
if common.ProAvailable { | ||
menu.st.IsPro = true | ||
} else { | ||
menu.upgrade.Hide() | ||
} | ||
|
||
menu.st.Status = stats.STATUS_CONNECTING | ||
statsUpdated() | ||
a.OnStatsChange(func(newStats stats.Stats) { | ||
menu.stMx.Lock() | ||
menu.st = newStats | ||
menu.stMx.Unlock() | ||
statsUpdated() | ||
}) | ||
|
||
go func() { | ||
for { | ||
select { | ||
case <-menu.toggle.ClickedCh: | ||
menu.stMx.Lock() | ||
disconnected := menu.st.Disconnected | ||
menu.stMx.Unlock() | ||
if disconnected { | ||
a.Connect() | ||
} else { | ||
a.Disconnect() | ||
} | ||
case <-menu.show.ClickedCh: | ||
a.OnTrayShow() | ||
case <-menu.upgrade.ClickedCh: | ||
a.OnTrayUpgrade() | ||
case <-menu.quit.ClickedCh: | ||
systray.Quit() | ||
} | ||
} | ||
}() | ||
|
||
return nil | ||
} | ||
|
||
func refreshSystray(language string) { | ||
if !menu.enable { | ||
return | ||
} | ||
if _, err := i18n.SetLocale(language); err != nil { | ||
log.Errorf("i18n.SetLocale(%s) failed: %q", language, err) | ||
return | ||
} | ||
refreshMenuItems() | ||
statsUpdated() | ||
} | ||
|
||
func refreshMenuItems() { | ||
menu.upgrade.SetTitle(i18n.T("TRAY_UPGRADE_TO_PRO")) | ||
systray.SetTooltip(i18n.T(translationAppName)) | ||
menu.show.SetTitle(i18n.T("TRAY_SHOW", i18n.T(translationAppName))) | ||
menu.quit.SetTitle(i18n.T("TRAY_QUIT", i18n.T(translationAppName))) | ||
} | ||
|
||
func statsUpdated() { | ||
menu.stMx.RLock() | ||
st := menu.st | ||
menu.stMx.RUnlock() | ||
|
||
iconName := "connected" | ||
statusKey := st.Status | ||
if st.Disconnected || !st.HasSucceedingProxy { | ||
iconName = "disconnected" | ||
} | ||
|
||
if st.HitDataCap && !st.IsPro { | ||
iconName += "alert" | ||
if !st.Disconnected { | ||
statusKey = "throttled" | ||
} | ||
} else if len(st.Alerts) > 0 { | ||
iconName += "alert" | ||
// Show the first one as status if there are multiple alerts | ||
statusKey = st.Alerts[0].Alert() | ||
} | ||
|
||
if common.DefaultAppName == "Beam" { | ||
systray.SetTemplateIcon(iconsByName[iconName], iconsByName[iconName]) | ||
} else { | ||
// Lantern icons do not support dark mode yet | ||
systray.SetIcon(iconsByName[iconName]) | ||
} | ||
status := i18n.T("TRAY_STATUS", i18n.T("status."+statusKey)) | ||
menu.status.SetTitle(status) | ||
|
||
if st.IsPro || !common.ProAvailable { | ||
menu.upgrade.Hide() | ||
} else { | ||
menu.upgrade.Show() | ||
} | ||
|
||
if st.Disconnected { | ||
menu.toggle.SetTitle(i18n.T("TRAY_CONNECT")) | ||
} else { | ||
menu.toggle.SetTitle(i18n.T("TRAY_DISCONNECT")) | ||
} | ||
} |
Oops, something went wrong.