mirror of
https://github.com/sshuttle/sshuttle.git
synced 2025-04-29 05:38:59 +02:00
Remove legacy MACOSX files.
Broken and not been maintained in some time. See #21.
This commit is contained in:
parent
eaad54f68b
commit
9944b97629
@ -1,11 +0,0 @@
|
|||||||
exec >&2
|
|
||||||
UI=
|
|
||||||
[ "$(uname)" = "Darwin" ] && UI=ui-macos/all
|
|
||||||
redo-ifchange sshuttle.8 $UI
|
|
||||||
|
|
||||||
echo
|
|
||||||
echo "What now?"
|
|
||||||
[ -z "$UI" ] || echo "- Try the MacOS GUI: open ui-macos/Sshuttle*.app"
|
|
||||||
echo "- Run sshuttle: ./sshuttle --dns -r HOSTNAME 0/0"
|
|
||||||
echo "- Read the README: less README.md"
|
|
||||||
echo "- Read the man page: less sshuttle.md"
|
|
@ -1,2 +0,0 @@
|
|||||||
redo ui-macos/clean
|
|
||||||
rm -f *~ */*~ .*~ */.*~ *.8 *.tmp */*.tmp *.pyc */*.pyc
|
|
@ -1,7 +0,0 @@
|
|||||||
exec >&2
|
|
||||||
if pandoc </dev/null 2>/dev/null; then
|
|
||||||
pandoc -s -r markdown -w man -o $3 $2.md
|
|
||||||
else
|
|
||||||
echo "Warning: pandoc not installed; can't generate manpages."
|
|
||||||
redo-always
|
|
||||||
fi
|
|
8
sshuttle/ui-macos/.gitignore
vendored
8
sshuttle/ui-macos/.gitignore
vendored
@ -1,8 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*~
|
|
||||||
/*.nib
|
|
||||||
/debug.app
|
|
||||||
/sources.list
|
|
||||||
/Sshuttle VPN.app
|
|
||||||
/*.tar.gz
|
|
||||||
/*.zip
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,40 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>English</string>
|
|
||||||
<key>CFBundleDisplayName</key>
|
|
||||||
<string>Sshuttle VPN</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>Sshuttle</string>
|
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string>app.icns</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>ca.apenwarr.Sshuttle</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>Sshuttle VPN</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>0.0.0</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>0.0.0</string>
|
|
||||||
<key>LSUIElement</key>
|
|
||||||
<string>1</string>
|
|
||||||
<key>LSHasLocalizedDisplayName</key>
|
|
||||||
<false/>
|
|
||||||
<key>NSAppleScriptEnabled</key>
|
|
||||||
<false/>
|
|
||||||
<key>NSHumanReadableCopyright</key>
|
|
||||||
<string>GNU LGPL Version 2</string>
|
|
||||||
<key>NSMainNibFile</key>
|
|
||||||
<string>MainMenu</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string>NSApplication</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
File diff suppressed because it is too large
Load Diff
@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>startAtLogin</key>
|
|
||||||
<false/>
|
|
||||||
<key>autoReconnect</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
@ -1 +0,0 @@
|
|||||||
redo-ifchange debug.app dist
|
|
Binary file not shown.
@ -1,30 +0,0 @@
|
|||||||
import re
|
|
||||||
import subprocess
|
|
||||||
|
|
||||||
|
|
||||||
def askpass(prompt):
|
|
||||||
prompt = prompt.replace('"', "'")
|
|
||||||
|
|
||||||
if 'yes/no' in prompt:
|
|
||||||
return "yes"
|
|
||||||
|
|
||||||
script = """
|
|
||||||
tell application "Finder"
|
|
||||||
activate
|
|
||||||
display dialog "%s" \
|
|
||||||
with title "Sshuttle SSH Connection" \
|
|
||||||
default answer "" \
|
|
||||||
with icon caution \
|
|
||||||
with hidden answer
|
|
||||||
end tell
|
|
||||||
""" % prompt
|
|
||||||
|
|
||||||
p = subprocess.Popen(['osascript', '-e', script], stdout=subprocess.PIPE)
|
|
||||||
out = p.stdout.read()
|
|
||||||
rv = p.wait()
|
|
||||||
if rv:
|
|
||||||
return None
|
|
||||||
g = re.match("text returned:(.*), button returned:.*", out)
|
|
||||||
if not g:
|
|
||||||
return None
|
|
||||||
return g.group(1)
|
|
1
sshuttle/ui-macos/bits/.gitignore
vendored
1
sshuttle/ui-macos/bits/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
/runpython
|
|
@ -1 +0,0 @@
|
|||||||
APPL????
|
|
@ -1,23 +0,0 @@
|
|||||||
/*
|
|
||||||
* This rather pointless program acts like the python interpreter, except
|
|
||||||
* it's intended to sit inside a MacOS .app package, so that its argv[0]
|
|
||||||
* will point inside the package.
|
|
||||||
*
|
|
||||||
* NSApplicationMain() looks for Info.plist using the path in argv[0], which
|
|
||||||
* goes wrong if your interpreter is /usr/bin/python.
|
|
||||||
*/
|
|
||||||
#include <Python/Python.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
char *path = strdup(argv[0]), *cptr;
|
|
||||||
char *args[] = {argv[0], "../Resources/main.py", NULL};
|
|
||||||
cptr = strrchr(path, '/');
|
|
||||||
if (cptr)
|
|
||||||
*cptr = 0;
|
|
||||||
chdir(path);
|
|
||||||
free(path);
|
|
||||||
return Py_Main(2, args);
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
exec >&2
|
|
||||||
redo-ifchange runpython.c
|
|
||||||
ARCHES=""
|
|
||||||
printf "Platforms: "
|
|
||||||
if [ -d /usr/libexec/gcc/darwin ]; then
|
|
||||||
for d in /usr/libexec/gcc/darwin/*; do
|
|
||||||
PLAT=$(basename "$d")
|
|
||||||
[ "$PLAT" != "ppc64" ] || continue # fails for some reason on my Mac
|
|
||||||
ARCHES="$ARCHES -arch $PLAT"
|
|
||||||
printf "$PLAT "
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
printf "\n"
|
|
||||||
PYTHON_LDFLAGS=$(python-config --ldflags)
|
|
||||||
PYTHON_INCLUDES=$(python-config --includes)
|
|
||||||
gcc $ARCHES \
|
|
||||||
-Wall -o $3 runpython.c \
|
|
||||||
$PYTHON_INCLUDES \
|
|
||||||
$PYTHON_LDFLAGS \
|
|
||||||
-framework Python
|
|
@ -1,4 +0,0 @@
|
|||||||
exec >&2
|
|
||||||
find . -name '*~' | xargs rm -f
|
|
||||||
rm -rf *.app *.zip *.tar.gz
|
|
||||||
rm -f bits/runpython *.nib sources.list
|
|
@ -1,15 +0,0 @@
|
|||||||
redo-ifchange bits/runpython MainMenu.nib
|
|
||||||
rm -rf debug.app
|
|
||||||
mkdir debug.app debug.app/Contents
|
|
||||||
cd debug.app/Contents
|
|
||||||
ln -s ../.. Resources
|
|
||||||
ln -s ../.. English.lproj
|
|
||||||
ln -s ../../Info.plist .
|
|
||||||
ln -s ../../app.icns .
|
|
||||||
|
|
||||||
mkdir MacOS
|
|
||||||
cd MacOS
|
|
||||||
ln -s ../../../bits/runpython Sshuttle
|
|
||||||
|
|
||||||
cd ../../..
|
|
||||||
redo-ifchange $(find debug.app -type f)
|
|
@ -1,28 +0,0 @@
|
|||||||
TOP=$PWD
|
|
||||||
redo-ifchange sources.list
|
|
||||||
redo-ifchange Info.plist bits/runpython \
|
|
||||||
$(while read name newname; do echo "$name"; done <sources.list)
|
|
||||||
|
|
||||||
rm -rf "$2.app"
|
|
||||||
mkdir "$2.app" "$2.app/Contents"
|
|
||||||
cd "$2.app/Contents"
|
|
||||||
|
|
||||||
cp "$TOP/Info.plist" .
|
|
||||||
|
|
||||||
mkdir MacOS
|
|
||||||
cp "$TOP/bits/runpython" MacOS/Sshuttle
|
|
||||||
|
|
||||||
mkdir Resources
|
|
||||||
|
|
||||||
cd "$TOP"
|
|
||||||
while read name newname; do
|
|
||||||
[ -z "$name" ] && continue
|
|
||||||
: "${newname:=$name}"
|
|
||||||
outname=$2.app/Contents/Resources/$newname
|
|
||||||
outdir=$(dirname "$outname")
|
|
||||||
[ -d "$outdir" ] || mkdir "$outdir"
|
|
||||||
cp "${name-$newname}" "$outname"
|
|
||||||
done <sources.list
|
|
||||||
|
|
||||||
cd "$2.app"
|
|
||||||
redo-ifchange $(find . -type f)
|
|
@ -1,5 +0,0 @@
|
|||||||
exec >&2
|
|
||||||
IFS="
|
|
||||||
"
|
|
||||||
redo-ifchange $2.app
|
|
||||||
tar -czf $3 $2.app/
|
|
@ -1,5 +0,0 @@
|
|||||||
exec >&2
|
|
||||||
IFS="
|
|
||||||
"
|
|
||||||
redo-ifchange $2.app
|
|
||||||
zip -q -r $3 $2.app/
|
|
@ -1,2 +0,0 @@
|
|||||||
redo-ifchange $2.xib
|
|
||||||
ibtool --compile $3 $2.xib
|
|
@ -1 +0,0 @@
|
|||||||
redo-ifchange "Sshuttle VPN.app.zip" "Sshuttle VPN.app.tar.gz"
|
|
@ -1,19 +0,0 @@
|
|||||||
# update a local branch with pregenerated output files, so people can download
|
|
||||||
# the completed tarballs from github. Since we don't have any real binaries,
|
|
||||||
# our final distribution package contains mostly blobs from the source code,
|
|
||||||
# so this doesn't cost us much extra space in the repo.
|
|
||||||
BRANCH=dist/macos
|
|
||||||
redo-ifchange 'Sshuttle VPN.app'
|
|
||||||
git update-ref refs/heads/$BRANCH origin/$BRANCH '' 2>/dev/null || true
|
|
||||||
|
|
||||||
export GIT_INDEX_FILE=$PWD/gitindex.tmp
|
|
||||||
rm -f "$GIT_INDEX_FILE"
|
|
||||||
git add -f 'Sshuttle VPN.app'
|
|
||||||
|
|
||||||
MSG="MacOS precompiled app package for $(git describe)"
|
|
||||||
TREE=$(git write-tree --prefix=ui-macos)
|
|
||||||
git show-ref refs/heads/$BRANCH >/dev/null && PARENT="-p refs/heads/$BRANCH"
|
|
||||||
COMMITID=$(echo "$MSG" | git commit-tree $TREE $PARENT)
|
|
||||||
|
|
||||||
git update-ref refs/heads/$BRANCH $COMMITID
|
|
||||||
rm -f "$GIT_INDEX_FILE"
|
|
@ -1,401 +0,0 @@
|
|||||||
import sys
|
|
||||||
import os
|
|
||||||
import pty
|
|
||||||
from AppKit import (
|
|
||||||
objc,
|
|
||||||
NSApp,
|
|
||||||
NSApplicationMain,
|
|
||||||
NSAttributedString,
|
|
||||||
NSFileHandle,
|
|
||||||
NSFileHandleDataAvailableNotification,
|
|
||||||
NSImage,
|
|
||||||
NSMenu,
|
|
||||||
NSMenuItem,
|
|
||||||
NSNotificationCenter,
|
|
||||||
NSObject,
|
|
||||||
NSStatusBar,
|
|
||||||
NSVariableStatusItemLength,
|
|
||||||
)
|
|
||||||
import my
|
|
||||||
import models
|
|
||||||
import askpass
|
|
||||||
|
|
||||||
|
|
||||||
def sshuttle_args(host, auto_nets, auto_hosts, dns, nets, debug,
|
|
||||||
no_latency_control):
|
|
||||||
argv = [my.bundle_path('sshuttle/sshuttle', ''), '-r', host]
|
|
||||||
assert(argv[0])
|
|
||||||
if debug:
|
|
||||||
argv.append('-v')
|
|
||||||
if auto_nets:
|
|
||||||
argv.append('--auto-nets')
|
|
||||||
if auto_hosts:
|
|
||||||
argv.append('--auto-hosts')
|
|
||||||
if dns:
|
|
||||||
argv.append('--dns')
|
|
||||||
if no_latency_control:
|
|
||||||
argv.append('--no-latency-control')
|
|
||||||
argv += nets
|
|
||||||
return argv
|
|
||||||
|
|
||||||
|
|
||||||
class _Callback(NSObject):
|
|
||||||
|
|
||||||
def initWithFunc_(self, func):
|
|
||||||
self = super(_Callback, self).init()
|
|
||||||
self.func = func
|
|
||||||
return self
|
|
||||||
|
|
||||||
def func_(self, obj):
|
|
||||||
return self.func(obj)
|
|
||||||
|
|
||||||
|
|
||||||
class Callback:
|
|
||||||
|
|
||||||
def __init__(self, func):
|
|
||||||
self.obj = _Callback.alloc().initWithFunc_(func)
|
|
||||||
self.sel = self.obj.func_
|
|
||||||
|
|
||||||
|
|
||||||
class Runner:
|
|
||||||
|
|
||||||
def __init__(self, argv, logfunc, promptfunc, serverobj):
|
|
||||||
print('in __init__')
|
|
||||||
self.id = argv
|
|
||||||
self.rv = None
|
|
||||||
self.pid = None
|
|
||||||
self.fd = None
|
|
||||||
self.logfunc = logfunc
|
|
||||||
self.promptfunc = promptfunc
|
|
||||||
self.serverobj = serverobj
|
|
||||||
self.buf = ''
|
|
||||||
self.logfunc('\nConnecting to %s.\n' % self.serverobj.host())
|
|
||||||
print('will run: %r' % argv)
|
|
||||||
self.serverobj.setConnected_(False)
|
|
||||||
pid, fd = pty.fork()
|
|
||||||
if pid == 0:
|
|
||||||
# child
|
|
||||||
try:
|
|
||||||
os.execvp(argv[0], argv)
|
|
||||||
except Exception as e:
|
|
||||||
sys.stderr.write('failed to start: %r\n' % e)
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
os._exit(42)
|
|
||||||
# parent
|
|
||||||
self.pid = pid
|
|
||||||
self.file = NSFileHandle.alloc()\
|
|
||||||
.initWithFileDescriptor_closeOnDealloc_(fd, True)
|
|
||||||
self.cb = Callback(self.gotdata)
|
|
||||||
NSNotificationCenter.defaultCenter()\
|
|
||||||
.addObserver_selector_name_object_(
|
|
||||||
self.cb.obj, self.cb.sel,
|
|
||||||
NSFileHandleDataAvailableNotification, self.file)
|
|
||||||
self.file.waitForDataInBackgroundAndNotify()
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.wait()
|
|
||||||
|
|
||||||
def _try_wait(self, options):
|
|
||||||
if self.rv is None and self.pid > 0:
|
|
||||||
pid, code = os.waitpid(self.pid, options)
|
|
||||||
if pid == self.pid:
|
|
||||||
if os.WIFEXITED(code):
|
|
||||||
self.rv = os.WEXITSTATUS(code)
|
|
||||||
else:
|
|
||||||
self.rv = -os.WSTOPSIG(code)
|
|
||||||
self.serverobj.setConnected_(False)
|
|
||||||
self.serverobj.setError_('VPN process died')
|
|
||||||
self.logfunc('Disconnected.\n')
|
|
||||||
print('wait_result: %r' % self.rv)
|
|
||||||
return self.rv
|
|
||||||
|
|
||||||
def wait(self):
|
|
||||||
rv = None
|
|
||||||
while rv is None:
|
|
||||||
self.gotdata(None)
|
|
||||||
rv = self._try_wait(os.WNOHANG)
|
|
||||||
|
|
||||||
def poll(self):
|
|
||||||
return self._try_wait(os.WNOHANG)
|
|
||||||
|
|
||||||
def kill(self):
|
|
||||||
assert(self.pid > 0)
|
|
||||||
print('killing: pid=%r rv=%r' % (self.pid, self.rv))
|
|
||||||
if self.rv is None:
|
|
||||||
self.logfunc('Disconnecting from %s.\n' % self.serverobj.host())
|
|
||||||
os.kill(self.pid, 15)
|
|
||||||
self.wait()
|
|
||||||
|
|
||||||
def gotdata(self, notification):
|
|
||||||
print('gotdata!')
|
|
||||||
d = str(self.file.availableData())
|
|
||||||
if d:
|
|
||||||
self.logfunc(d)
|
|
||||||
self.buf = self.buf + d
|
|
||||||
if 'Connected.\r\n' in self.buf:
|
|
||||||
self.serverobj.setConnected_(True)
|
|
||||||
self.buf = self.buf[-4096:]
|
|
||||||
if self.buf.strip().endswith(':'):
|
|
||||||
lastline = self.buf.rstrip().split('\n')[-1]
|
|
||||||
resp = self.promptfunc(lastline)
|
|
||||||
add = ' (response)\n'
|
|
||||||
self.buf += add
|
|
||||||
self.logfunc(add)
|
|
||||||
self.file.writeData_(my.Data(resp + '\n'))
|
|
||||||
self.file.waitForDataInBackgroundAndNotify()
|
|
||||||
self.poll()
|
|
||||||
# print 'gotdata done!'
|
|
||||||
|
|
||||||
|
|
||||||
class SshuttleApp(NSObject):
|
|
||||||
|
|
||||||
def initialize(self):
|
|
||||||
d = my.PList('UserDefaults')
|
|
||||||
my.Defaults().registerDefaults_(d)
|
|
||||||
|
|
||||||
|
|
||||||
class SshuttleController(NSObject):
|
|
||||||
# Interface builder outlets
|
|
||||||
startAtLoginField = objc.IBOutlet()
|
|
||||||
autoReconnectField = objc.IBOutlet()
|
|
||||||
debugField = objc.IBOutlet()
|
|
||||||
routingField = objc.IBOutlet()
|
|
||||||
prefsWindow = objc.IBOutlet()
|
|
||||||
serversController = objc.IBOutlet()
|
|
||||||
logField = objc.IBOutlet()
|
|
||||||
latencyControlField = objc.IBOutlet()
|
|
||||||
|
|
||||||
servers = []
|
|
||||||
conns = {}
|
|
||||||
|
|
||||||
def _connect(self, server):
|
|
||||||
host = server.host()
|
|
||||||
print('connecting %r' % host)
|
|
||||||
self.fill_menu()
|
|
||||||
|
|
||||||
def logfunc(msg):
|
|
||||||
print('log! (%d bytes)' % len(msg))
|
|
||||||
self.logField.textStorage()\
|
|
||||||
.appendAttributedString_(NSAttributedString.alloc()
|
|
||||||
.initWithString_(msg))
|
|
||||||
self.logField.didChangeText()
|
|
||||||
|
|
||||||
def promptfunc(prompt):
|
|
||||||
print('prompt! %r' % prompt)
|
|
||||||
return askpass.askpass(prompt)
|
|
||||||
nets_mode = server.autoNets()
|
|
||||||
if nets_mode == models.NET_MANUAL:
|
|
||||||
manual_nets = ["%s/%d" % (i.subnet(), i.width())
|
|
||||||
for i in server.nets()]
|
|
||||||
elif nets_mode == models.NET_ALL:
|
|
||||||
manual_nets = ['0/0']
|
|
||||||
else:
|
|
||||||
manual_nets = []
|
|
||||||
noLatencyControl = (server.latencyControl() != models.LAT_INTERACTIVE)
|
|
||||||
conn = Runner(sshuttle_args(host,
|
|
||||||
auto_nets=nets_mode == models.NET_AUTO,
|
|
||||||
auto_hosts=server.autoHosts(),
|
|
||||||
dns=server.useDns(),
|
|
||||||
nets=manual_nets,
|
|
||||||
debug=self.debugField.state(),
|
|
||||||
no_latency_control=noLatencyControl),
|
|
||||||
logfunc=logfunc, promptfunc=promptfunc,
|
|
||||||
serverobj=server)
|
|
||||||
self.conns[host] = conn
|
|
||||||
|
|
||||||
def _disconnect(self, server):
|
|
||||||
host = server.host()
|
|
||||||
print('disconnecting %r' % host)
|
|
||||||
conn = self.conns.get(host)
|
|
||||||
if conn:
|
|
||||||
conn.kill()
|
|
||||||
self.fill_menu()
|
|
||||||
self.logField.textStorage().setAttributedString_(
|
|
||||||
NSAttributedString.alloc().initWithString_(''))
|
|
||||||
|
|
||||||
@objc.IBAction
|
|
||||||
def cmd_connect(self, sender):
|
|
||||||
server = sender.representedObject()
|
|
||||||
server.setWantConnect_(True)
|
|
||||||
|
|
||||||
@objc.IBAction
|
|
||||||
def cmd_disconnect(self, sender):
|
|
||||||
server = sender.representedObject()
|
|
||||||
server.setWantConnect_(False)
|
|
||||||
|
|
||||||
@objc.IBAction
|
|
||||||
def cmd_show(self, sender):
|
|
||||||
self.prefsWindow.makeKeyAndOrderFront_(self)
|
|
||||||
NSApp.activateIgnoringOtherApps_(True)
|
|
||||||
|
|
||||||
@objc.IBAction
|
|
||||||
def cmd_quit(self, sender):
|
|
||||||
NSStatusBar.systemStatusBar().removeStatusItem_(self.statusitem)
|
|
||||||
NSApp.performSelector_withObject_afterDelay_(NSApp.terminate_,
|
|
||||||
None, 0.0)
|
|
||||||
|
|
||||||
def fill_menu(self):
|
|
||||||
menu = self.menu
|
|
||||||
menu.removeAllItems()
|
|
||||||
|
|
||||||
def additem(name, func, obj):
|
|
||||||
it = menu.addItemWithTitle_action_keyEquivalent_(name, None, "")
|
|
||||||
it.setRepresentedObject_(obj)
|
|
||||||
it.setTarget_(self)
|
|
||||||
it.setAction_(func)
|
|
||||||
|
|
||||||
def addnote(name):
|
|
||||||
additem(name, None, None)
|
|
||||||
|
|
||||||
any_inprogress = None
|
|
||||||
any_conn = None
|
|
||||||
any_err = None
|
|
||||||
if len(self.servers):
|
|
||||||
for i in self.servers:
|
|
||||||
host = i.host()
|
|
||||||
title = i.title()
|
|
||||||
want = i.wantConnect()
|
|
||||||
connected = i.connected()
|
|
||||||
numnets = len(list(i.nets()))
|
|
||||||
if not host:
|
|
||||||
additem('Connect Untitled', None, i)
|
|
||||||
elif i.autoNets() == models.NET_MANUAL and not numnets:
|
|
||||||
additem('Connect %s (no routes)' % host, None, i)
|
|
||||||
elif want:
|
|
||||||
any_conn = i
|
|
||||||
additem('Disconnect %s' % title, self.cmd_disconnect, i)
|
|
||||||
else:
|
|
||||||
additem('Connect %s' % title, self.cmd_connect, i)
|
|
||||||
if not want:
|
|
||||||
msg = 'Off'
|
|
||||||
elif i.error():
|
|
||||||
msg = 'ERROR - try reconnecting'
|
|
||||||
any_err = i
|
|
||||||
elif connected:
|
|
||||||
msg = 'Connected'
|
|
||||||
else:
|
|
||||||
msg = 'Connecting...'
|
|
||||||
any_inprogress = i
|
|
||||||
addnote(' State: %s' % msg)
|
|
||||||
else:
|
|
||||||
addnote('No servers defined yet')
|
|
||||||
|
|
||||||
menu.addItem_(NSMenuItem.separatorItem())
|
|
||||||
additem('Preferences...', self.cmd_show, None)
|
|
||||||
additem('Quit Sshuttle VPN', self.cmd_quit, None)
|
|
||||||
|
|
||||||
if any_err:
|
|
||||||
self.statusitem.setImage_(self.img_err)
|
|
||||||
self.statusitem.setTitle_('Error!')
|
|
||||||
elif any_conn:
|
|
||||||
self.statusitem.setImage_(self.img_running)
|
|
||||||
if any_inprogress:
|
|
||||||
self.statusitem.setTitle_('Connecting...')
|
|
||||||
else:
|
|
||||||
self.statusitem.setTitle_('')
|
|
||||||
else:
|
|
||||||
self.statusitem.setImage_(self.img_idle)
|
|
||||||
self.statusitem.setTitle_('')
|
|
||||||
|
|
||||||
def load_servers(self):
|
|
||||||
l = my.Defaults().arrayForKey_('servers') or []
|
|
||||||
sl = []
|
|
||||||
for s in l:
|
|
||||||
host = s.get('host', None)
|
|
||||||
if not host:
|
|
||||||
continue
|
|
||||||
|
|
||||||
nets = s.get('nets', [])
|
|
||||||
nl = []
|
|
||||||
for n in nets:
|
|
||||||
subnet = n[0]
|
|
||||||
width = n[1]
|
|
||||||
net = models.SshuttleNet.alloc().init()
|
|
||||||
net.setSubnet_(subnet)
|
|
||||||
net.setWidth_(width)
|
|
||||||
nl.append(net)
|
|
||||||
|
|
||||||
autoNets = s.get('autoNets', models.NET_AUTO)
|
|
||||||
autoHosts = s.get('autoHosts', True)
|
|
||||||
useDns = s.get('useDns', autoNets == models.NET_ALL)
|
|
||||||
latencyControl = s.get('latencyControl', models.LAT_INTERACTIVE)
|
|
||||||
srv = models.SshuttleServer.alloc().init()
|
|
||||||
srv.setHost_(host)
|
|
||||||
srv.setAutoNets_(autoNets)
|
|
||||||
srv.setAutoHosts_(autoHosts)
|
|
||||||
srv.setNets_(nl)
|
|
||||||
srv.setUseDns_(useDns)
|
|
||||||
srv.setLatencyControl_(latencyControl)
|
|
||||||
sl.append(srv)
|
|
||||||
self.serversController.addObjects_(sl)
|
|
||||||
self.serversController.setSelectionIndex_(0)
|
|
||||||
|
|
||||||
def save_servers(self):
|
|
||||||
l = []
|
|
||||||
for s in self.servers:
|
|
||||||
host = s.host()
|
|
||||||
if not host:
|
|
||||||
continue
|
|
||||||
nets = []
|
|
||||||
for n in s.nets():
|
|
||||||
subnet = n.subnet()
|
|
||||||
if not subnet:
|
|
||||||
continue
|
|
||||||
nets.append((subnet, n.width()))
|
|
||||||
d = dict(host=s.host(),
|
|
||||||
nets=nets,
|
|
||||||
autoNets=s.autoNets(),
|
|
||||||
autoHosts=s.autoHosts(),
|
|
||||||
useDns=s.useDns(),
|
|
||||||
latencyControl=s.latencyControl())
|
|
||||||
l.append(d)
|
|
||||||
my.Defaults().setObject_forKey_(l, 'servers')
|
|
||||||
self.fill_menu()
|
|
||||||
|
|
||||||
def awakeFromNib(self):
|
|
||||||
self.routingField.removeAllItems()
|
|
||||||
tf = self.routingField.addItemWithTitle_
|
|
||||||
tf('Send all traffic through this server')
|
|
||||||
tf('Determine automatically')
|
|
||||||
tf('Custom...')
|
|
||||||
|
|
||||||
self.latencyControlField.removeAllItems()
|
|
||||||
tf = self.latencyControlField.addItemWithTitle_
|
|
||||||
tf('Fast transfer')
|
|
||||||
tf('Low latency')
|
|
||||||
|
|
||||||
# Hmm, even when I mark this as !enabled in the .nib, it still comes
|
|
||||||
# through as enabled. So let's just disable it here (since we don't
|
|
||||||
# support this feature yet).
|
|
||||||
self.startAtLoginField.setEnabled_(False)
|
|
||||||
self.startAtLoginField.setState_(False)
|
|
||||||
self.autoReconnectField.setEnabled_(False)
|
|
||||||
self.autoReconnectField.setState_(False)
|
|
||||||
|
|
||||||
self.load_servers()
|
|
||||||
|
|
||||||
# Initialize our menu item
|
|
||||||
self.menu = NSMenu.alloc().initWithTitle_('Sshuttle')
|
|
||||||
bar = NSStatusBar.systemStatusBar()
|
|
||||||
statusitem = bar.statusItemWithLength_(NSVariableStatusItemLength)
|
|
||||||
self.statusitem = statusitem
|
|
||||||
self.img_idle = NSImage.imageNamed_('ChickenIdleTemplate')
|
|
||||||
self.img_running = NSImage.imageNamed_('ChickenRunningTemplate')
|
|
||||||
self.img_err = NSImage.imageNamed_('ChickenErrorTemplate')
|
|
||||||
statusitem.setImage_(self.img_idle)
|
|
||||||
statusitem.setMenu_(self.menu)
|
|
||||||
self.fill_menu()
|
|
||||||
|
|
||||||
models.configchange_callback = my.DelayedCallback(self.save_servers)
|
|
||||||
|
|
||||||
def sc(server):
|
|
||||||
if server.wantConnect():
|
|
||||||
self._connect(server)
|
|
||||||
else:
|
|
||||||
self._disconnect(server)
|
|
||||||
models.setconnect_callback = sc
|
|
||||||
|
|
||||||
|
|
||||||
# Note: NSApplicationMain calls sys.exit(), so this never returns.
|
|
||||||
NSApplicationMain(sys.argv)
|
|
@ -1,189 +0,0 @@
|
|||||||
from AppKit import (objc, NSObject)
|
|
||||||
import my
|
|
||||||
|
|
||||||
|
|
||||||
configchange_callback = setconnect_callback = None
|
|
||||||
objc_validator = objc.signature('@@:N^@o^@')
|
|
||||||
|
|
||||||
|
|
||||||
def config_changed():
|
|
||||||
if configchange_callback:
|
|
||||||
configchange_callback()
|
|
||||||
|
|
||||||
|
|
||||||
def _validate_ip(v):
|
|
||||||
parts = v.split('.')[:4]
|
|
||||||
if len(parts) < 4:
|
|
||||||
parts += ['0'] * (4 - len(parts))
|
|
||||||
for i in range(4):
|
|
||||||
n = my.atoi(parts[i])
|
|
||||||
if n < 0:
|
|
||||||
n = 0
|
|
||||||
elif n > 255:
|
|
||||||
n = 255
|
|
||||||
parts[i] = str(n)
|
|
||||||
return '.'.join(parts)
|
|
||||||
|
|
||||||
|
|
||||||
def _validate_width(v):
|
|
||||||
n = my.atoi(v)
|
|
||||||
if n < 0:
|
|
||||||
n = 0
|
|
||||||
elif n > 32:
|
|
||||||
n = 32
|
|
||||||
return n
|
|
||||||
|
|
||||||
|
|
||||||
class SshuttleNet(NSObject):
|
|
||||||
|
|
||||||
def subnet(self):
|
|
||||||
return getattr(self, '_k_subnet', None)
|
|
||||||
|
|
||||||
def setSubnet_(self, v):
|
|
||||||
self._k_subnet = v
|
|
||||||
config_changed()
|
|
||||||
|
|
||||||
@objc_validator
|
|
||||||
def validateSubnet_error_(self, value, error):
|
|
||||||
# print 'validateSubnet!'
|
|
||||||
return True, _validate_ip(value), error
|
|
||||||
|
|
||||||
def width(self):
|
|
||||||
return getattr(self, '_k_width', 24)
|
|
||||||
|
|
||||||
def setWidth_(self, v):
|
|
||||||
self._k_width = v
|
|
||||||
config_changed()
|
|
||||||
|
|
||||||
@objc_validator
|
|
||||||
def validateWidth_error_(self, value, error):
|
|
||||||
# print 'validateWidth!'
|
|
||||||
return True, _validate_width(value), error
|
|
||||||
|
|
||||||
NET_ALL = 0
|
|
||||||
NET_AUTO = 1
|
|
||||||
NET_MANUAL = 2
|
|
||||||
|
|
||||||
LAT_BANDWIDTH = 0
|
|
||||||
LAT_INTERACTIVE = 1
|
|
||||||
|
|
||||||
|
|
||||||
class SshuttleServer(NSObject):
|
|
||||||
|
|
||||||
def init(self):
|
|
||||||
self = super(SshuttleServer, self).init()
|
|
||||||
config_changed()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def wantConnect(self):
|
|
||||||
return getattr(self, '_k_wantconnect', False)
|
|
||||||
|
|
||||||
def setWantConnect_(self, v):
|
|
||||||
self._k_wantconnect = v
|
|
||||||
self.setError_(None)
|
|
||||||
config_changed()
|
|
||||||
if setconnect_callback:
|
|
||||||
setconnect_callback(self)
|
|
||||||
|
|
||||||
def connected(self):
|
|
||||||
return getattr(self, '_k_connected', False)
|
|
||||||
|
|
||||||
def setConnected_(self, v):
|
|
||||||
print('setConnected of %r to %r' % (self, v))
|
|
||||||
self._k_connected = v
|
|
||||||
if v:
|
|
||||||
self.setError_(None) # connected ok, so no error
|
|
||||||
config_changed()
|
|
||||||
|
|
||||||
def error(self):
|
|
||||||
return getattr(self, '_k_error', None)
|
|
||||||
|
|
||||||
def setError_(self, v):
|
|
||||||
self._k_error = v
|
|
||||||
config_changed()
|
|
||||||
|
|
||||||
def isValid(self):
|
|
||||||
if not self.host():
|
|
||||||
return False
|
|
||||||
if self.autoNets() == NET_MANUAL and not len(list(self.nets())):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def title(self):
|
|
||||||
host = self.host()
|
|
||||||
if not host:
|
|
||||||
return host
|
|
||||||
an = self.autoNets()
|
|
||||||
suffix = ""
|
|
||||||
if an == NET_ALL:
|
|
||||||
suffix = " (all traffic)"
|
|
||||||
elif an == NET_MANUAL:
|
|
||||||
n = self.nets()
|
|
||||||
suffix = ' (%d subnet%s)' % (len(n), len(n) != 1 and 's' or '')
|
|
||||||
return self.host() + suffix
|
|
||||||
|
|
||||||
def setTitle_(self, v):
|
|
||||||
# title is always auto-generated
|
|
||||||
config_changed()
|
|
||||||
|
|
||||||
def host(self):
|
|
||||||
return getattr(self, '_k_host', None)
|
|
||||||
|
|
||||||
def setHost_(self, v):
|
|
||||||
self._k_host = v
|
|
||||||
self.setTitle_(None)
|
|
||||||
config_changed()
|
|
||||||
|
|
||||||
@objc_validator
|
|
||||||
def validateHost_error_(self, value, error):
|
|
||||||
# print 'validatehost! %r %r %r' % (self, value, error)
|
|
||||||
while value.startswith('-'):
|
|
||||||
value = value[1:]
|
|
||||||
return True, value, error
|
|
||||||
|
|
||||||
def nets(self):
|
|
||||||
return getattr(self, '_k_nets', [])
|
|
||||||
|
|
||||||
def setNets_(self, v):
|
|
||||||
self._k_nets = v
|
|
||||||
self.setTitle_(None)
|
|
||||||
config_changed()
|
|
||||||
|
|
||||||
def netsHidden(self):
|
|
||||||
# print 'checking netsHidden'
|
|
||||||
return self.autoNets() != NET_MANUAL
|
|
||||||
|
|
||||||
def setNetsHidden_(self, v):
|
|
||||||
config_changed()
|
|
||||||
# print 'setting netsHidden to %r' % v
|
|
||||||
|
|
||||||
def autoNets(self):
|
|
||||||
return getattr(self, '_k_autoNets', NET_AUTO)
|
|
||||||
|
|
||||||
def setAutoNets_(self, v):
|
|
||||||
self._k_autoNets = v
|
|
||||||
self.setNetsHidden_(-1)
|
|
||||||
self.setUseDns_(v == NET_ALL)
|
|
||||||
self.setTitle_(None)
|
|
||||||
config_changed()
|
|
||||||
|
|
||||||
def autoHosts(self):
|
|
||||||
return getattr(self, '_k_autoHosts', True)
|
|
||||||
|
|
||||||
def setAutoHosts_(self, v):
|
|
||||||
self._k_autoHosts = v
|
|
||||||
config_changed()
|
|
||||||
|
|
||||||
def useDns(self):
|
|
||||||
return getattr(self, '_k_useDns', False)
|
|
||||||
|
|
||||||
def setUseDns_(self, v):
|
|
||||||
self._k_useDns = v
|
|
||||||
config_changed()
|
|
||||||
|
|
||||||
def latencyControl(self):
|
|
||||||
return getattr(self, '_k_latencyControl', LAT_INTERACTIVE)
|
|
||||||
|
|
||||||
def setLatencyControl_(self, v):
|
|
||||||
self._k_latencyControl = v
|
|
||||||
config_changed()
|
|
@ -1,70 +0,0 @@
|
|||||||
import os
|
|
||||||
from AppKit import (
|
|
||||||
NSBundle,
|
|
||||||
NSData,
|
|
||||||
NSDictionary,
|
|
||||||
NSImage,
|
|
||||||
NSUserDefaults,
|
|
||||||
)
|
|
||||||
import PyObjCTools.AppHelper
|
|
||||||
|
|
||||||
|
|
||||||
def bundle_path(name, typ):
|
|
||||||
if typ:
|
|
||||||
return NSBundle.mainBundle().pathForResource_ofType_(name, typ)
|
|
||||||
else:
|
|
||||||
return os.path.join(NSBundle.mainBundle().resourcePath(), name)
|
|
||||||
|
|
||||||
|
|
||||||
# Load an NSData using a python string
|
|
||||||
def Data(s):
|
|
||||||
return NSData.alloc().initWithBytes_length_(s, len(s))
|
|
||||||
|
|
||||||
|
|
||||||
# Load a property list from a file in the application bundle.
|
|
||||||
def PList(name):
|
|
||||||
path = bundle_path(name, 'plist')
|
|
||||||
return NSDictionary.dictionaryWithContentsOfFile_(path)
|
|
||||||
|
|
||||||
|
|
||||||
# Load an NSImage from a file in the application bundle.
|
|
||||||
def Image(name, ext):
|
|
||||||
bytes = open(bundle_path(name, ext)).read()
|
|
||||||
img = NSImage.alloc().initWithData_(Data(bytes))
|
|
||||||
return img
|
|
||||||
|
|
||||||
|
|
||||||
# Return the NSUserDefaults shared object.
|
|
||||||
def Defaults():
|
|
||||||
return NSUserDefaults.standardUserDefaults()
|
|
||||||
|
|
||||||
|
|
||||||
# Usage:
|
|
||||||
# f = DelayedCallback(func, args...)
|
|
||||||
# later:
|
|
||||||
# f()
|
|
||||||
#
|
|
||||||
# When you call f(), it will schedule a call to func() next time the
|
|
||||||
# ObjC event loop iterates. Multiple calls to f() in a single iteration
|
|
||||||
# will only result in one call to func().
|
|
||||||
#
|
|
||||||
def DelayedCallback(func, *args, **kwargs):
|
|
||||||
flag = [0]
|
|
||||||
|
|
||||||
def _go():
|
|
||||||
if flag[0]:
|
|
||||||
print('running %r (flag=%r)' % (func, flag))
|
|
||||||
flag[0] = 0
|
|
||||||
func(*args, **kwargs)
|
|
||||||
|
|
||||||
def call():
|
|
||||||
flag[0] += 1
|
|
||||||
PyObjCTools.AppHelper.callAfter(_go)
|
|
||||||
return call
|
|
||||||
|
|
||||||
|
|
||||||
def atoi(s):
|
|
||||||
try:
|
|
||||||
return int(s)
|
|
||||||
except ValueError:
|
|
||||||
return 0
|
|
@ -1,4 +0,0 @@
|
|||||||
redo-ifchange debug.app
|
|
||||||
exec >&2
|
|
||||||
./debug.app/Contents/MacOS/Sshuttle
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
|||||||
redo-always
|
|
||||||
exec >$3
|
|
||||||
cat <<-EOF
|
|
||||||
app.icns
|
|
||||||
MainMenu.nib English.lproj/MainMenu.nib
|
|
||||||
UserDefaults.plist
|
|
||||||
ChickenIdleTemplate.pdf
|
|
||||||
ChickenRunningTemplate.pdf
|
|
||||||
ChickenErrorTemplate.pdf
|
|
||||||
EOF
|
|
||||||
for d in *.py sshuttle/*.py sshuttle/sshuttle sshuttle/compat/*.py; do
|
|
||||||
echo $d
|
|
||||||
done
|
|
||||||
redo-stamp <$3
|
|
Loading…
Reference in New Issue
Block a user