mirror of
https://github.com/onionshare/onionshare.git
synced 2024-10-01 01:35:40 -04:00
commit
d0bdb9143f
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
'''
|
"""
|
||||||
Check translation lacked or disused.
|
Check translation lacked or disused.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
@ -24,7 +24,7 @@ es disused gui_starting_server
|
|||||||
2. load translation key from locale/*.json.
|
2. load translation key from locale/*.json.
|
||||||
3. compare these.
|
3. compare these.
|
||||||
|
|
||||||
'''
|
"""
|
||||||
|
|
||||||
|
|
||||||
import fileinput, argparse, re, os, codecs, json, sys
|
import fileinput, argparse, re, os, codecs, json, sys
|
||||||
@ -75,13 +75,11 @@ def main():
|
|||||||
key = m.group(1)
|
key = m.group(1)
|
||||||
translate_keys.add(key)
|
translate_keys.add(key)
|
||||||
|
|
||||||
|
|
||||||
if args.show_all_keys:
|
if args.show_all_keys:
|
||||||
for k in sorted(translate_keys):
|
for k in sorted(translate_keys):
|
||||||
print k
|
print k
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
locale_files = [f for f in files_in(dir, 'locale') if f.endswith('.json')]
|
locale_files = [f for f in files_in(dir, 'locale') if f.endswith('.json')]
|
||||||
for locale_file in locale_files:
|
for locale_file in locale_files:
|
||||||
with codecs.open(locale_file, 'r', encoding='utf-8') as f:
|
with codecs.open(locale_file, 'r', encoding='utf-8') as f:
|
||||||
|
@ -32,6 +32,7 @@ def get_platform():
|
|||||||
p = 'Tails'
|
p = 'Tails'
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
def get_onionshare_dir():
|
def get_onionshare_dir():
|
||||||
if get_platform() == 'Darwin':
|
if get_platform() == 'Darwin':
|
||||||
onionshare_dir = os.path.dirname(__file__)
|
onionshare_dir = os.path.dirname(__file__)
|
||||||
@ -39,6 +40,7 @@ def get_onionshare_dir():
|
|||||||
onionshare_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
|
onionshare_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
|
||||||
return onionshare_dir
|
return onionshare_dir
|
||||||
|
|
||||||
|
|
||||||
def constant_time_compare(val1, val2):
|
def constant_time_compare(val1, val2):
|
||||||
_builtin_constant_time_compare = getattr(hmac, 'compare_digest', None)
|
_builtin_constant_time_compare = getattr(hmac, 'compare_digest', None)
|
||||||
if _builtin_constant_time_compare is not None:
|
if _builtin_constant_time_compare is not None:
|
||||||
@ -55,19 +57,21 @@ def constant_time_compare(val1, val2):
|
|||||||
result |= x ^ y
|
result |= x ^ y
|
||||||
return result == 0
|
return result == 0
|
||||||
|
|
||||||
|
|
||||||
def random_string(num_bytes, output_len=None):
|
def random_string(num_bytes, output_len=None):
|
||||||
b = os.urandom(num_bytes)
|
b = os.urandom(num_bytes)
|
||||||
h = hashlib.sha256(b).digest()[:16]
|
h = hashlib.sha256(b).digest()[:16]
|
||||||
s = base64.b32encode(h).lower().replace('=','')
|
s = base64.b32encode(h).lower().replace('=', '')
|
||||||
if not output_len:
|
if not output_len:
|
||||||
return s
|
return s
|
||||||
return s[:output_len]
|
return s[:output_len]
|
||||||
|
|
||||||
|
|
||||||
def human_readable_filesize(b):
|
def human_readable_filesize(b):
|
||||||
thresh = 1024.0
|
thresh = 1024.0
|
||||||
if b < thresh:
|
if b < thresh:
|
||||||
return '{0} B'.format(b)
|
return '{0} B'.format(b)
|
||||||
units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB']
|
units = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
|
||||||
u = 0
|
u = 0
|
||||||
b /= thresh
|
b /= thresh
|
||||||
while b >= thresh:
|
while b >= thresh:
|
||||||
@ -75,9 +79,11 @@ def human_readable_filesize(b):
|
|||||||
u += 1
|
u += 1
|
||||||
return '{0} {1}'.format(round(b, 1), units[u])
|
return '{0} {1}'.format(round(b, 1), units[u])
|
||||||
|
|
||||||
|
|
||||||
def is_root():
|
def is_root():
|
||||||
return os.geteuid() == 0
|
return os.geteuid() == 0
|
||||||
|
|
||||||
|
|
||||||
def dir_size(start_path):
|
def dir_size(start_path):
|
||||||
total_size = 0
|
total_size = 0
|
||||||
for dirpath, dirnames, filenames in os.walk(start_path):
|
for dirpath, dirnames, filenames in os.walk(start_path):
|
||||||
@ -87,6 +93,7 @@ def dir_size(start_path):
|
|||||||
total_size += os.path.getsize(fp)
|
total_size += os.path.getsize(fp)
|
||||||
return total_size
|
return total_size
|
||||||
|
|
||||||
|
|
||||||
def get_tmp_dir():
|
def get_tmp_dir():
|
||||||
if get_platform() == "Windows":
|
if get_platform() == "Windows":
|
||||||
if 'Temp' in os.environ:
|
if 'Temp' in os.environ:
|
||||||
@ -97,6 +104,7 @@ def get_tmp_dir():
|
|||||||
temp = '/tmp'
|
temp = '/tmp'
|
||||||
return temp
|
return temp
|
||||||
|
|
||||||
|
|
||||||
class ZipWriter(object):
|
class ZipWriter(object):
|
||||||
def __init__(self, zip_filename=None):
|
def __init__(self, zip_filename=None):
|
||||||
if zip_filename:
|
if zip_filename:
|
||||||
@ -120,4 +128,3 @@ class ZipWriter(object):
|
|||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.z.close()
|
self.z.close()
|
||||||
|
|
||||||
|
@ -25,18 +25,25 @@ from stem import SocketError
|
|||||||
|
|
||||||
import strings, helpers, web
|
import strings, helpers, web
|
||||||
|
|
||||||
class NoTor(Exception): pass
|
|
||||||
class TailsError(Exception): pass
|
class NoTor(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TailsError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def hsdic2list(dic):
|
def hsdic2list(dic):
|
||||||
"Convert what we get from get_conf_map to what we need for set_options"
|
"""Convert what we get from get_conf_map to what we need for set_options"""
|
||||||
return [
|
return [
|
||||||
pair for pairs in [
|
pair for pairs in [
|
||||||
[('HiddenServiceDir',vals[0]),('HiddenServicePort',vals[1])]
|
[('HiddenServiceDir', vals[0]), ('HiddenServicePort', vals[1])]
|
||||||
for vals in zip(dic.get('HiddenServiceDir',[]),dic.get('HiddenServicePort',[]))
|
for vals in zip(dic.get('HiddenServiceDir', []), dic.get('HiddenServicePort', []))
|
||||||
] for pair in pairs
|
] for pair in pairs
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class OnionShare(object):
|
class OnionShare(object):
|
||||||
def __init__(self, debug=False, local_only=False, stay_open=False):
|
def __init__(self, debug=False, local_only=False, stay_open=False):
|
||||||
self.port = None
|
self.port = None
|
||||||
@ -56,7 +63,6 @@ class OnionShare(object):
|
|||||||
# files and dirs to delete on shutdown
|
# files and dirs to delete on shutdown
|
||||||
self.cleanup_filenames = []
|
self.cleanup_filenames = []
|
||||||
|
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
if self.controller:
|
if self.controller:
|
||||||
# Get fresh hidden services (maybe changed since last time)
|
# Get fresh hidden services (maybe changed since last time)
|
||||||
@ -64,7 +70,7 @@ class OnionShare(object):
|
|||||||
hsdic = self.controller.get_conf_map('HiddenServiceOptions') or {
|
hsdic = self.controller.get_conf_map('HiddenServiceOptions') or {
|
||||||
'HiddenServiceDir': [], 'HiddenServicePort': []
|
'HiddenServiceDir': [], 'HiddenServicePort': []
|
||||||
}
|
}
|
||||||
if self.hidserv_dir and self.hidserv_dir in hsdic.get('HiddenServiceDir',[]):
|
if self.hidserv_dir and self.hidserv_dir in hsdic.get('HiddenServiceDir', []):
|
||||||
dropme = hsdic['HiddenServiceDir'].index(self.hidserv_dir)
|
dropme = hsdic['HiddenServiceDir'].index(self.hidserv_dir)
|
||||||
del hsdic['HiddenServiceDir'][dropme]
|
del hsdic['HiddenServiceDir'][dropme]
|
||||||
del hsdic['HiddenServicePort'][dropme]
|
del hsdic['HiddenServicePort'][dropme]
|
||||||
@ -96,7 +102,7 @@ class OnionShare(object):
|
|||||||
else:
|
else:
|
||||||
args = ['/usr/bin/sudo', '--', '/usr/bin/onionshare']
|
args = ['/usr/bin/sudo', '--', '/usr/bin/onionshare']
|
||||||
p = subprocess.Popen(args+[str(self.port)], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
p = subprocess.Popen(args+[str(self.port)], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
|
||||||
stdout = p.stdout.read(22) # .onion URLs are 22 chars long
|
stdout = p.stdout.read(22) # .onion URLs are 22 chars long
|
||||||
|
|
||||||
if stdout:
|
if stdout:
|
||||||
self.onion_host = stdout
|
self.onion_host = stdout
|
||||||
@ -132,14 +138,14 @@ class OnionShare(object):
|
|||||||
hsdic = self.controller.get_conf_map('HiddenServiceOptions') or {
|
hsdic = self.controller.get_conf_map('HiddenServiceOptions') or {
|
||||||
'HiddenServiceDir': [], 'HiddenServicePort': []
|
'HiddenServiceDir': [], 'HiddenServicePort': []
|
||||||
}
|
}
|
||||||
if self.hidserv_dir in hsdic.get('HiddenServiceDir',[]):
|
if self.hidserv_dir in hsdic.get('HiddenServiceDir', []):
|
||||||
# Maybe a stale service with the wrong local port
|
# Maybe a stale service with the wrong local port
|
||||||
dropme = hsdic['HiddenServiceDir'].index(self.hidserv_dir)
|
dropme = hsdic['HiddenServiceDir'].index(self.hidserv_dir)
|
||||||
del hsdic['HiddenServiceDir'][dropme]
|
del hsdic['HiddenServiceDir'][dropme]
|
||||||
del hsdic['HiddenServicePort'][dropme]
|
del hsdic['HiddenServicePort'][dropme]
|
||||||
hsdic['HiddenServiceDir'] = hsdic.get('HiddenServiceDir',[])+[self.hidserv_dir]
|
hsdic['HiddenServiceDir'] = hsdic.get('HiddenServiceDir', [])+[self.hidserv_dir]
|
||||||
hsdic['HiddenServicePort'] = hsdic.get('HiddenServicePort',[])+[
|
hsdic['HiddenServicePort'] = hsdic.get('HiddenServicePort', [])+[
|
||||||
'80 127.0.0.1:{0}'.format(self.port) ]
|
'80 127.0.0.1:{0}'.format(self.port)]
|
||||||
|
|
||||||
self.controller.set_options(hsdic2list(hsdic))
|
self.controller.set_options(hsdic2list(hsdic))
|
||||||
|
|
||||||
@ -181,16 +187,17 @@ class OnionShare(object):
|
|||||||
ready = True
|
ready = True
|
||||||
|
|
||||||
sys.stdout.write('{0}\n'.format(strings._('wait_for_hs_yup')))
|
sys.stdout.write('{0}\n'.format(strings._('wait_for_hs_yup')))
|
||||||
except socks.SOCKS5Error: # non-Tails error
|
except socks.SOCKS5Error: # non-Tails error
|
||||||
sys.stdout.write('{0}\n'.format(strings._('wait_for_hs_nope')))
|
sys.stdout.write('{0}\n'.format(strings._('wait_for_hs_nope')))
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
except urllib2.HTTPError: # Tails error
|
except urllib2.HTTPError: # Tails error
|
||||||
sys.stdout.write('{0}\n'.format(strings._('wait_for_hs_nope')))
|
sys.stdout.write('{0}\n'.format(strings._('wait_for_hs_nope')))
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def tails_root():
|
def tails_root():
|
||||||
# if running in Tails and as root, do only the things that require root
|
# if running in Tails and as root, do only the things that require root
|
||||||
if helpers.get_platform() == 'Tails' and helpers.is_root():
|
if helpers.get_platform() == 'Tails' and helpers.is_root():
|
||||||
@ -205,7 +212,8 @@ def tails_root():
|
|||||||
sys.exit(-1)
|
sys.exit(-1)
|
||||||
|
|
||||||
# open hole in firewall
|
# open hole in firewall
|
||||||
subprocess.call(['/sbin/iptables', '-I', 'OUTPUT', '-o', 'lo', '-p', 'tcp', '--dport', str(port), '-j', 'ACCEPT'])
|
subprocess.call(['/sbin/iptables', '-I', 'OUTPUT', '-o', 'lo',
|
||||||
|
'-p', 'tcp', '--dport', str(port), '-j', 'ACCEPT'])
|
||||||
|
|
||||||
# start hidden service
|
# start hidden service
|
||||||
app = OnionShare()
|
app = OnionShare()
|
||||||
@ -217,8 +225,10 @@ def tails_root():
|
|||||||
|
|
||||||
# close hole in firewall on shutdown
|
# close hole in firewall on shutdown
|
||||||
import signal
|
import signal
|
||||||
def handler(signum = None, frame = None):
|
|
||||||
subprocess.call(['/sbin/iptables', '-D', 'OUTPUT', '-o', 'lo', '-p', 'tcp', '--dport', str(port), '-j', 'ACCEPT'])
|
def handler(signum=None, frame=None):
|
||||||
|
subprocess.call(['/sbin/iptables', '-D', 'OUTPUT', '-o', 'lo',
|
||||||
|
'-p', 'tcp', '--dport', str(port), '-j', 'ACCEPT'])
|
||||||
sys.exit()
|
sys.exit()
|
||||||
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGHUP, signal.SIGQUIT]:
|
for sig in [signal.SIGTERM, signal.SIGINT, signal.SIGHUP, signal.SIGQUIT]:
|
||||||
signal.signal(sig, handler)
|
signal.signal(sig, handler)
|
||||||
@ -227,6 +237,7 @@ def tails_root():
|
|||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
strings.load_strings()
|
strings.load_strings()
|
||||||
tails_root()
|
tails_root()
|
||||||
@ -273,7 +284,7 @@ def main():
|
|||||||
app.cleanup_filenames.append(web.zip_filename)
|
app.cleanup_filenames.append(web.zip_filename)
|
||||||
|
|
||||||
# warn about sending large files over Tor
|
# warn about sending large files over Tor
|
||||||
if web.zip_filesize >= 157286400: # 150mb
|
if web.zip_filesize >= 157286400: # 150mb
|
||||||
print ''
|
print ''
|
||||||
print strings._("large_filesize")
|
print strings._("large_filesize")
|
||||||
print ''
|
print ''
|
||||||
@ -283,7 +294,7 @@ def main():
|
|||||||
t.daemon = True
|
t.daemon = True
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
try: # Trap Ctrl-C
|
try: # Trap Ctrl-C
|
||||||
# wait for hs
|
# wait for hs
|
||||||
ready = app.wait_for_hs()
|
ready = app.wait_for_hs()
|
||||||
if not ready:
|
if not ready:
|
||||||
|
@ -65,6 +65,7 @@ PRINTABLE_PROXY_TYPES = {SOCKS4: "SOCKS4", SOCKS5: "SOCKS5", HTTP: "HTTP"}
|
|||||||
|
|
||||||
_orgsocket = _orig_socket = socket.socket
|
_orgsocket = _orig_socket = socket.socket
|
||||||
|
|
||||||
|
|
||||||
class ProxyError(IOError):
|
class ProxyError(IOError):
|
||||||
"""
|
"""
|
||||||
socket_err contains original socket.error exception.
|
socket_err contains original socket.error exception.
|
||||||
@ -79,32 +80,54 @@ class ProxyError(IOError):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.msg
|
return self.msg
|
||||||
|
|
||||||
class GeneralProxyError(ProxyError): pass
|
|
||||||
class ProxyConnectionError(ProxyError): pass
|
|
||||||
class SOCKS5AuthError(ProxyError): pass
|
|
||||||
class SOCKS5Error(ProxyError): pass
|
|
||||||
class SOCKS4Error(ProxyError): pass
|
|
||||||
class HTTPError(ProxyError): pass
|
|
||||||
|
|
||||||
SOCKS4_ERRORS = { 0x5B: "Request rejected or failed",
|
class GeneralProxyError(ProxyError):
|
||||||
0x5C: "Request rejected because SOCKS server cannot connect to identd on the client",
|
pass
|
||||||
0x5D: "Request rejected because the client program and identd report different user-ids"
|
|
||||||
}
|
|
||||||
|
|
||||||
SOCKS5_ERRORS = { 0x01: "General SOCKS server failure",
|
|
||||||
0x02: "Connection not allowed by ruleset",
|
|
||||||
0x03: "Network unreachable",
|
|
||||||
0x04: "Host unreachable",
|
|
||||||
0x05: "Connection refused",
|
|
||||||
0x06: "TTL expired",
|
|
||||||
0x07: "Command not supported, or protocol error",
|
|
||||||
0x08: "Address type not supported"
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFAULT_PORTS = { SOCKS4: 1080,
|
class ProxyConnectionError(ProxyError):
|
||||||
SOCKS5: 1080,
|
pass
|
||||||
HTTP: 8080
|
|
||||||
}
|
|
||||||
|
class SOCKS5AuthError(ProxyError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SOCKS5Error(ProxyError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SOCKS4Error(ProxyError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPError(ProxyError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
SOCKS4_ERRORS = {
|
||||||
|
0x5B: "Request rejected or failed",
|
||||||
|
0x5C: "Request rejected because SOCKS server cannot connect to identd on the client",
|
||||||
|
0x5D: "Request rejected because the client program and identd report different user-ids",
|
||||||
|
}
|
||||||
|
|
||||||
|
SOCKS5_ERRORS = {
|
||||||
|
0x01: "General SOCKS server failure",
|
||||||
|
0x02: "Connection not allowed by ruleset",
|
||||||
|
0x03: "Network unreachable",
|
||||||
|
0x04: "Host unreachable",
|
||||||
|
0x05: "Connection refused",
|
||||||
|
0x06: "TTL expired",
|
||||||
|
0x07: "Command not supported, or protocol error",
|
||||||
|
0x08: "Address type not supported",
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFAULT_PORTS = {
|
||||||
|
SOCKS4: 1080,
|
||||||
|
SOCKS5: 1080,
|
||||||
|
HTTP: 8080,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def set_default_proxy(proxy_type=None, addr=None, port=None, rdns=True, username=None, password=None):
|
def set_default_proxy(proxy_type=None, addr=None, port=None, rdns=True, username=None, password=None):
|
||||||
"""
|
"""
|
||||||
@ -119,6 +142,7 @@ def set_default_proxy(proxy_type=None, addr=None, port=None, rdns=True, username
|
|||||||
|
|
||||||
setdefaultproxy = set_default_proxy
|
setdefaultproxy = set_default_proxy
|
||||||
|
|
||||||
|
|
||||||
def get_default_proxy():
|
def get_default_proxy():
|
||||||
"""
|
"""
|
||||||
Returns the default proxy, set by set_default_proxy.
|
Returns the default proxy, set by set_default_proxy.
|
||||||
@ -127,6 +151,7 @@ def get_default_proxy():
|
|||||||
|
|
||||||
getdefaultproxy = get_default_proxy
|
getdefaultproxy = get_default_proxy
|
||||||
|
|
||||||
|
|
||||||
def wrap_module(module):
|
def wrap_module(module):
|
||||||
"""
|
"""
|
||||||
Attempts to replace a module's socket library with a SOCKS socket. Must set
|
Attempts to replace a module's socket library with a SOCKS socket. Must set
|
||||||
@ -141,7 +166,8 @@ def wrap_module(module):
|
|||||||
|
|
||||||
wrapmodule = wrap_module
|
wrapmodule = wrap_module
|
||||||
|
|
||||||
def create_connection(dest_pair, proxy_type=None, proxy_addr=None,
|
|
||||||
|
def create_connection(dest_pair, proxy_type=None, proxy_addr=None,
|
||||||
proxy_port=None, proxy_username=None,
|
proxy_port=None, proxy_username=None,
|
||||||
proxy_password=None, timeout=None):
|
proxy_password=None, timeout=None):
|
||||||
"""create_connection(dest_pair, **proxy_args) -> socket object
|
"""create_connection(dest_pair, **proxy_args) -> socket object
|
||||||
@ -161,6 +187,7 @@ def create_connection(dest_pair, proxy_type=None, proxy_addr=None,
|
|||||||
sock.connect(dest_pair)
|
sock.connect(dest_pair)
|
||||||
return sock
|
return sock
|
||||||
|
|
||||||
|
|
||||||
class socksocket(socket.socket):
|
class socksocket(socket.socket):
|
||||||
"""socksocket([family[, type[, proto]]]) -> socket object
|
"""socksocket([family[, type[, proto]]]) -> socket object
|
||||||
|
|
||||||
@ -181,10 +208,11 @@ class socksocket(socket.socket):
|
|||||||
self.proxy_sockname = None
|
self.proxy_sockname = None
|
||||||
self.proxy_peername = None
|
self.proxy_peername = None
|
||||||
|
|
||||||
self.proxy_negotiators = { SOCKS4: self._negotiate_SOCKS4,
|
self.proxy_negotiators = {
|
||||||
SOCKS5: self._negotiate_SOCKS5,
|
SOCKS4: self._negotiate_SOCKS4,
|
||||||
HTTP: self._negotiate_HTTP
|
SOCKS5: self._negotiate_SOCKS5,
|
||||||
}
|
HTTP: self._negotiate_HTTP,
|
||||||
|
}
|
||||||
|
|
||||||
def _recvall(self, count):
|
def _recvall(self, count):
|
||||||
"""
|
"""
|
||||||
@ -446,7 +474,6 @@ class socksocket(socket.socket):
|
|||||||
self.proxy_sockname = (b"0.0.0.0", 0)
|
self.proxy_sockname = (b"0.0.0.0", 0)
|
||||||
self.proxy_peername = addr, dest_port
|
self.proxy_peername = addr, dest_port
|
||||||
|
|
||||||
|
|
||||||
def connect(self, dest_pair):
|
def connect(self, dest_pair):
|
||||||
"""
|
"""
|
||||||
Connects to the specified destination through a proxy.
|
Connects to the specified destination through a proxy.
|
||||||
@ -465,7 +492,6 @@ class socksocket(socket.socket):
|
|||||||
or not isinstance(dest_port, int)):
|
or not isinstance(dest_port, int)):
|
||||||
raise GeneralProxyError("Invalid destination-connection (host, port) pair")
|
raise GeneralProxyError("Invalid destination-connection (host, port) pair")
|
||||||
|
|
||||||
|
|
||||||
if proxy_type is None:
|
if proxy_type is None:
|
||||||
# Treat like regular socket object
|
# Treat like regular socket object
|
||||||
_orig_socket.connect(self, (dest_addr, dest_port))
|
_orig_socket.connect(self, (dest_addr, dest_port))
|
||||||
|
@ -22,6 +22,7 @@ import helpers
|
|||||||
|
|
||||||
strings = {}
|
strings = {}
|
||||||
|
|
||||||
|
|
||||||
def load_strings(default="en"):
|
def load_strings(default="en"):
|
||||||
global strings
|
global strings
|
||||||
|
|
||||||
@ -49,6 +50,7 @@ def load_strings(default="en"):
|
|||||||
if key in translated[lang]:
|
if key in translated[lang]:
|
||||||
strings[key] = translated[lang][key]
|
strings[key] = translated[lang][key]
|
||||||
|
|
||||||
|
|
||||||
def translated(k, gui=False):
|
def translated(k, gui=False):
|
||||||
if gui:
|
if gui:
|
||||||
return strings[k].encode("utf-8").decode('utf-8', 'replace')
|
return strings[k].encode("utf-8").decode('utf-8', 'replace')
|
||||||
|
@ -28,11 +28,13 @@ app = Flask(__name__)
|
|||||||
file_info = []
|
file_info = []
|
||||||
zip_filename = None
|
zip_filename = None
|
||||||
zip_filesize = None
|
zip_filesize = None
|
||||||
|
|
||||||
|
|
||||||
def set_file_info(filenames):
|
def set_file_info(filenames):
|
||||||
global file_info, zip_filename, zip_filesize
|
global file_info, zip_filename, zip_filesize
|
||||||
|
|
||||||
# build file info list
|
# build file info list
|
||||||
file_info = {'files':[], 'dirs':[]}
|
file_info = {'files': [], 'dirs': []}
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
info = {
|
info = {
|
||||||
'filename': filename,
|
'filename': filename,
|
||||||
@ -59,6 +61,7 @@ def set_file_info(filenames):
|
|||||||
zip_filename = z.zip_filename
|
zip_filename = z.zip_filename
|
||||||
zip_filesize = os.path.getsize(zip_filename)
|
zip_filesize = os.path.getsize(zip_filename)
|
||||||
|
|
||||||
|
|
||||||
REQUEST_LOAD = 0
|
REQUEST_LOAD = 0
|
||||||
REQUEST_DOWNLOAD = 1
|
REQUEST_DOWNLOAD = 1
|
||||||
REQUEST_PROGRESS = 2
|
REQUEST_PROGRESS = 2
|
||||||
@ -66,24 +69,31 @@ REQUEST_OTHER = 3
|
|||||||
REQUEST_CANCELED = 4
|
REQUEST_CANCELED = 4
|
||||||
q = Queue.Queue()
|
q = Queue.Queue()
|
||||||
|
|
||||||
|
|
||||||
def add_request(type, path, data=None):
|
def add_request(type, path, data=None):
|
||||||
global q
|
global q
|
||||||
q.put({
|
q.put({
|
||||||
'type': type,
|
'type': type,
|
||||||
'path': path,
|
'path': path,
|
||||||
'data': data
|
'data': data
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
slug = helpers.random_string(16)
|
slug = helpers.random_string(16)
|
||||||
download_count = 0
|
download_count = 0
|
||||||
|
|
||||||
stay_open = False
|
stay_open = False
|
||||||
|
|
||||||
|
|
||||||
def set_stay_open(new_stay_open):
|
def set_stay_open(new_stay_open):
|
||||||
global stay_open
|
global stay_open
|
||||||
stay_open = new_stay_open
|
stay_open = new_stay_open
|
||||||
|
|
||||||
|
|
||||||
def get_stay_open():
|
def get_stay_open():
|
||||||
return stay_open
|
return stay_open
|
||||||
|
|
||||||
|
|
||||||
def debug_mode():
|
def debug_mode():
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -96,6 +106,7 @@ def debug_mode():
|
|||||||
log_handler.setLevel(logging.WARNING)
|
log_handler.setLevel(logging.WARNING)
|
||||||
app.logger.addHandler(log_handler)
|
app.logger.addHandler(log_handler)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/<slug_candidate>")
|
@app.route("/<slug_candidate>")
|
||||||
def index(slug_candidate):
|
def index(slug_candidate):
|
||||||
if not helpers.constant_time_compare(slug.encode('ascii'), slug_candidate.encode('ascii')):
|
if not helpers.constant_time_compare(slug.encode('ascii'), slug_candidate.encode('ascii')):
|
||||||
@ -112,6 +123,7 @@ def index(slug_candidate):
|
|||||||
strings=strings.strings
|
strings=strings.strings
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/<slug_candidate>/download")
|
@app.route("/<slug_candidate>/download")
|
||||||
def download(slug_candidate):
|
def download(slug_candidate):
|
||||||
global download_count
|
global download_count
|
||||||
@ -128,13 +140,13 @@ def download(slug_candidate):
|
|||||||
path = request.path
|
path = request.path
|
||||||
|
|
||||||
# tell GUI the download started
|
# tell GUI the download started
|
||||||
add_request(REQUEST_DOWNLOAD, path, { 'id':download_id })
|
add_request(REQUEST_DOWNLOAD, path, {'id': download_id})
|
||||||
|
|
||||||
dirname = os.path.dirname(zip_filename)
|
dirname = os.path.dirname(zip_filename)
|
||||||
basename = os.path.basename(zip_filename)
|
basename = os.path.basename(zip_filename)
|
||||||
|
|
||||||
def generate():
|
def generate():
|
||||||
chunk_size = 102400 # 100kb
|
chunk_size = 102400 # 100kb
|
||||||
|
|
||||||
fp = open(zip_filename, 'rb')
|
fp = open(zip_filename, 'rb')
|
||||||
done = False
|
done = False
|
||||||
@ -150,16 +162,17 @@ def download(slug_candidate):
|
|||||||
# tell GUI the progress
|
# tell GUI the progress
|
||||||
downloaded_bytes = fp.tell()
|
downloaded_bytes = fp.tell()
|
||||||
percent = round((1.0 * downloaded_bytes / zip_filesize) * 100, 2)
|
percent = round((1.0 * downloaded_bytes / zip_filesize) * 100, 2)
|
||||||
sys.stdout.write("\r{0}, {1}% ".format(helpers.human_readable_filesize(downloaded_bytes), percent))
|
sys.stdout.write(
|
||||||
|
"\r{0}, {1}% ".format(helpers.human_readable_filesize(downloaded_bytes), percent))
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
add_request(REQUEST_PROGRESS, path, { 'id':download_id, 'bytes':downloaded_bytes })
|
add_request(REQUEST_PROGRESS, path, {'id': download_id, 'bytes': downloaded_bytes})
|
||||||
except:
|
except:
|
||||||
# looks like the download was canceled
|
# looks like the download was canceled
|
||||||
done = True
|
done = True
|
||||||
canceled = True
|
canceled = True
|
||||||
|
|
||||||
# tell the GUI the download has canceled
|
# tell the GUI the download has canceled
|
||||||
add_request(REQUEST_CANCELED, path, { 'id':download_id })
|
add_request(REQUEST_CANCELED, path, {'id': download_id})
|
||||||
|
|
||||||
fp.close()
|
fp.close()
|
||||||
sys.stdout.write("\n")
|
sys.stdout.write("\n")
|
||||||
@ -181,6 +194,7 @@ def download(slug_candidate):
|
|||||||
r.headers.add('Content-Type', content_type)
|
r.headers.add('Content-Type', content_type)
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def page_not_found(e):
|
def page_not_found(e):
|
||||||
add_request(REQUEST_OTHER, request.path)
|
add_request(REQUEST_OTHER, request.path)
|
||||||
@ -188,6 +202,8 @@ def page_not_found(e):
|
|||||||
|
|
||||||
# shutting down the server only works within the context of flask, so the easiest way to do it is over http
|
# shutting down the server only works within the context of flask, so the easiest way to do it is over http
|
||||||
shutdown_slug = helpers.random_string(16)
|
shutdown_slug = helpers.random_string(16)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/<shutdown_slug_candidate>/shutdown")
|
@app.route("/<shutdown_slug_candidate>/shutdown")
|
||||||
def shutdown(shutdown_slug_candidate):
|
def shutdown(shutdown_slug_candidate):
|
||||||
if not helpers.constant_time_compare(shutdown_slug.encode('ascii'), shutdown_slug_candidate.encode('ascii')):
|
if not helpers.constant_time_compare(shutdown_slug.encode('ascii'), shutdown_slug_candidate.encode('ascii')):
|
||||||
@ -201,16 +217,19 @@ def shutdown(shutdown_slug_candidate):
|
|||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
def start(port, stay_open=False):
|
def start(port, stay_open=False):
|
||||||
set_stay_open(stay_open)
|
set_stay_open(stay_open)
|
||||||
app.run(port=port, threaded=True)
|
app.run(port=port, threaded=True)
|
||||||
|
|
||||||
|
|
||||||
def stop(port):
|
def stop(port):
|
||||||
# to stop flask, load http://127.0.0.1:<port>/<shutdown_slug>/shutdown
|
# to stop flask, load http://127.0.0.1:<port>/<shutdown_slug>/shutdown
|
||||||
if helpers.get_platform() == 'Tails':
|
if helpers.get_platform() == 'Tails':
|
||||||
# in Tails everything is proxies over Tor, so we need to get lower level
|
# in Tails everything is proxies over Tor, so we need to get lower level
|
||||||
# to connect not over the proxy
|
# to connect not over the proxy
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
s = socket.socket()
|
s = socket.socket()
|
||||||
s.connect(('127.0.0.1', port))
|
s.connect(('127.0.0.1', port))
|
||||||
s.sendall('GET /{0}/shutdown HTTP/1.1\r\n\r\n'.format(shutdown_slug))
|
s.sendall('GET /{0}/shutdown HTTP/1.1\r\n\r\n'.format(shutdown_slug))
|
||||||
|
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
import os, sys, inspect, platform
|
import os, sys, inspect, platform
|
||||||
|
|
||||||
|
|
||||||
def get_onionshare_gui_dir():
|
def get_onionshare_gui_dir():
|
||||||
if platform.system() == 'Darwin':
|
if platform.system() == 'Darwin':
|
||||||
onionshare_gui_dir = os.path.dirname(__file__)
|
onionshare_gui_dir = os.path.dirname(__file__)
|
||||||
@ -28,6 +29,7 @@ def get_onionshare_gui_dir():
|
|||||||
|
|
||||||
onionshare_gui_dir = get_onionshare_gui_dir()
|
onionshare_gui_dir = get_onionshare_gui_dir()
|
||||||
|
|
||||||
|
|
||||||
def get_image_path(filename):
|
def get_image_path(filename):
|
||||||
if platform.system() == 'Linux':
|
if platform.system() == 'Linux':
|
||||||
prefix = os.path.join(sys.prefix, 'share/onionshare/images')
|
prefix = os.path.join(sys.prefix, 'share/onionshare/images')
|
||||||
|
@ -22,6 +22,7 @@ from PyQt4 import QtCore, QtGui
|
|||||||
import common
|
import common
|
||||||
from onionshare import strings, helpers
|
from onionshare import strings, helpers
|
||||||
|
|
||||||
|
|
||||||
class Downloads(QtGui.QVBoxLayout):
|
class Downloads(QtGui.QVBoxLayout):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(Downloads, self).__init__()
|
super(Downloads, self).__init__()
|
||||||
|
@ -23,6 +23,7 @@ from PyQt4 import QtCore, QtGui
|
|||||||
import common
|
import common
|
||||||
from onionshare import strings, helpers
|
from onionshare import strings, helpers
|
||||||
|
|
||||||
|
|
||||||
class FileList(QtGui.QListWidget):
|
class FileList(QtGui.QListWidget):
|
||||||
files_dropped = QtCore.pyqtSignal()
|
files_dropped = QtCore.pyqtSignal()
|
||||||
files_updated = QtCore.pyqtSignal()
|
files_updated = QtCore.pyqtSignal()
|
||||||
@ -129,7 +130,7 @@ class FileList(QtGui.QListWidget):
|
|||||||
thresh = 1024.0
|
thresh = 1024.0
|
||||||
if b < thresh:
|
if b < thresh:
|
||||||
return '{0} B'.format(b)
|
return '{0} B'.format(b)
|
||||||
units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB']
|
units = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
|
||||||
u = 0
|
u = 0
|
||||||
b /= thresh
|
b /= thresh
|
||||||
while b >= thresh:
|
while b >= thresh:
|
||||||
@ -137,6 +138,7 @@ class FileList(QtGui.QListWidget):
|
|||||||
u += 1
|
u += 1
|
||||||
return '{0} {1}'.format(round(b, 1), units[u])
|
return '{0} {1}'.format(round(b, 1), units[u])
|
||||||
|
|
||||||
|
|
||||||
class FileSelection(QtGui.QVBoxLayout):
|
class FileSelection(QtGui.QVBoxLayout):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(FileSelection, self).__init__()
|
super(FileSelection, self).__init__()
|
||||||
@ -186,14 +188,16 @@ class FileSelection(QtGui.QVBoxLayout):
|
|||||||
self.file_list.update()
|
self.file_list.update()
|
||||||
|
|
||||||
def add_files(self):
|
def add_files(self):
|
||||||
filenames = QtGui.QFileDialog.getOpenFileNames(caption=strings._('gui_choose_files', True), options=QtGui.QFileDialog.ReadOnly)
|
filenames = QtGui.QFileDialog.getOpenFileNames(
|
||||||
|
caption=strings._('gui_choose_files', True), options=QtGui.QFileDialog.ReadOnly)
|
||||||
if filenames:
|
if filenames:
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
self.file_list.add_file(str(filename))
|
self.file_list.add_file(str(filename))
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def add_dir(self):
|
def add_dir(self):
|
||||||
filename = QtGui.QFileDialog.getExistingDirectory(caption=strings._('gui_choose_folder', True), options=QtGui.QFileDialog.ReadOnly)
|
filename = QtGui.QFileDialog.getExistingDirectory(
|
||||||
|
caption=strings._('gui_choose_folder', True), options=QtGui.QFileDialog.ReadOnly)
|
||||||
if filename:
|
if filename:
|
||||||
self.file_list.add_file(str(filename))
|
self.file_list.add_file(str(filename))
|
||||||
self.update()
|
self.update()
|
||||||
|
@ -35,6 +35,7 @@ from server_status import ServerStatus
|
|||||||
from downloads import Downloads
|
from downloads import Downloads
|
||||||
from options import Options
|
from options import Options
|
||||||
|
|
||||||
|
|
||||||
class Application(QtGui.QApplication):
|
class Application(QtGui.QApplication):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
platform = helpers.get_platform()
|
platform = helpers.get_platform()
|
||||||
@ -42,6 +43,7 @@ class Application(QtGui.QApplication):
|
|||||||
self.setAttribute(QtCore.Qt.AA_X11InitThreads, True)
|
self.setAttribute(QtCore.Qt.AA_X11InitThreads, True)
|
||||||
QtGui.QApplication.__init__(self, sys.argv)
|
QtGui.QApplication.__init__(self, sys.argv)
|
||||||
|
|
||||||
|
|
||||||
class OnionShareGui(QtGui.QWidget):
|
class OnionShareGui(QtGui.QWidget):
|
||||||
start_server_finished = QtCore.pyqtSignal()
|
start_server_finished = QtCore.pyqtSignal()
|
||||||
stop_server_finished = QtCore.pyqtSignal()
|
stop_server_finished = QtCore.pyqtSignal()
|
||||||
@ -110,7 +112,7 @@ class OnionShareGui(QtGui.QWidget):
|
|||||||
self.status_bar.showMessage(strings._('gui_starting_server3', True))
|
self.status_bar.showMessage(strings._('gui_starting_server3', True))
|
||||||
|
|
||||||
# warn about sending large files over Tor
|
# warn about sending large files over Tor
|
||||||
if web.zip_filesize >= 157286400: # 150mb
|
if web.zip_filesize >= 157286400: # 150mb
|
||||||
self.filesize_warning.setText(strings._("large_filesize", True))
|
self.filesize_warning.setText(strings._("large_filesize", True))
|
||||||
self.filesize_warning.show()
|
self.filesize_warning.show()
|
||||||
|
|
||||||
@ -151,7 +153,7 @@ class OnionShareGui(QtGui.QWidget):
|
|||||||
self.start_server_finished.emit()
|
self.start_server_finished.emit()
|
||||||
|
|
||||||
self.status_bar.showMessage(strings._('gui_starting_server2', True))
|
self.status_bar.showMessage(strings._('gui_starting_server2', True))
|
||||||
t = threading.Thread(target=finish_starting_server, kwargs={'self':self})
|
t = threading.Thread(target=finish_starting_server, kwargs={'self': self})
|
||||||
t.daemon = True
|
t.daemon = True
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
@ -206,6 +208,7 @@ class OnionShareGui(QtGui.QWidget):
|
|||||||
def clear_message(self):
|
def clear_message(self):
|
||||||
self.status_bar.clearMessage()
|
self.status_bar.clearMessage()
|
||||||
|
|
||||||
|
|
||||||
def alert(msg, icon=QtGui.QMessageBox.NoIcon):
|
def alert(msg, icon=QtGui.QMessageBox.NoIcon):
|
||||||
dialog = QtGui.QMessageBox()
|
dialog = QtGui.QMessageBox()
|
||||||
dialog.setWindowTitle("OnionShare")
|
dialog.setWindowTitle("OnionShare")
|
||||||
@ -214,6 +217,7 @@ def alert(msg, icon=QtGui.QMessageBox.NoIcon):
|
|||||||
dialog.setIcon(icon)
|
dialog.setIcon(icon)
|
||||||
dialog.exec_()
|
dialog.exec_()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
strings.load_strings()
|
strings.load_strings()
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ from PyQt4 import QtCore, QtGui
|
|||||||
import common
|
import common
|
||||||
from onionshare import strings, helpers
|
from onionshare import strings, helpers
|
||||||
|
|
||||||
|
|
||||||
class Options(QtGui.QHBoxLayout):
|
class Options(QtGui.QHBoxLayout):
|
||||||
def __init__(self, web):
|
def __init__(self, web):
|
||||||
super(Options, self).__init__()
|
super(Options, self).__init__()
|
||||||
|
@ -23,6 +23,7 @@ from PyQt4 import QtCore, QtGui
|
|||||||
import common
|
import common
|
||||||
from onionshare import strings, helpers
|
from onionshare import strings, helpers
|
||||||
|
|
||||||
|
|
||||||
class ServerStatus(QtGui.QVBoxLayout):
|
class ServerStatus(QtGui.QVBoxLayout):
|
||||||
server_started = QtCore.pyqtSignal()
|
server_started = QtCore.pyqtSignal()
|
||||||
server_stopped = QtCore.pyqtSignal()
|
server_stopped = QtCore.pyqtSignal()
|
||||||
|
17
setup.py
17
setup.py
@ -27,6 +27,7 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
from distutils.core import setup
|
from distutils.core import setup
|
||||||
|
|
||||||
|
|
||||||
def file_list(path):
|
def file_list(path):
|
||||||
files = []
|
files = []
|
||||||
for filename in os.listdir(path):
|
for filename in os.listdir(path):
|
||||||
@ -37,11 +38,23 @@ def file_list(path):
|
|||||||
|
|
||||||
version = open('version').read().strip()
|
version = open('version').read().strip()
|
||||||
|
|
||||||
|
description = (
|
||||||
|
"""OnionShare lets you securely and anonymously share a file of any size with someone. """
|
||||||
|
"""It works by starting a web server, making it accessible as a Tor hidden service, """
|
||||||
|
"""and generating an unguessable URL to access and download the file.""")
|
||||||
|
|
||||||
|
long_description = description + " " + (
|
||||||
|
"""It doesn't require setting up a server on the internet somewhere or using a third """
|
||||||
|
"""party filesharing service. You host the file on your own computer and use a Tor """
|
||||||
|
"""hidden service to make it temporarily accessible over the internet. The other user """
|
||||||
|
"""just needs to use Tor Browser to download the file from you."""
|
||||||
|
)
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='onionshare',
|
name='onionshare',
|
||||||
version=version,
|
version=version,
|
||||||
description='OnionShare lets you securely and anonymously share a file of any size with someone. It works by starting a web server, making it accessible as a Tor hidden service, and generating an unguessable URL to access and download the file.',
|
description=description,
|
||||||
long_description="""OnionShare lets you securely and anonymously share a file of any size with someone. It works by starting a web server, making it accessible as a Tor hidden service, and generating an unguessable URL to access and download the file. It doesn't require setting up a server on the internet somewhere or using a third party filesharing service. You host the file on your own computer and use a Tor hidden service to make it temporarily accessible over the internet. The other user just needs to use Tor Browser to download the file from you.""",
|
long_description=long_description,
|
||||||
author='Micah Lee',
|
author='Micah Lee',
|
||||||
author_email='micah@micahflee.com',
|
author_email='micah@micahflee.com',
|
||||||
url='https://github.com/micahflee/onionshare',
|
url='https://github.com/micahflee/onionshare',
|
||||||
|
@ -18,7 +18,9 @@ You should have received a copy of the GNU General Public License
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
"""
|
"""
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
import os, sys, subprocess, time, hashlib, platform, json, locale, socket, argparse, Queue, inspect, base64, random, functools, logging, ctypes, hmac, shutil
|
import os, sys, subprocess, time, hashlib, platform, json, locale, socket
|
||||||
|
import argparse, Queue, inspect, base64, random, functools, logging, ctypes
|
||||||
|
import hmac, shutil
|
||||||
from itertools import izip
|
from itertools import izip
|
||||||
import stem, stem.control, flask
|
import stem, stem.control, flask
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
@ -21,13 +21,15 @@ from nose import with_setup
|
|||||||
|
|
||||||
import test_helpers
|
import test_helpers
|
||||||
|
|
||||||
|
|
||||||
def test_get_platform_on_tails():
|
def test_get_platform_on_tails():
|
||||||
"get_platform() returns 'Tails' when hostname is 'amnesia'"
|
"""get_platform() returns 'Tails' when hostname is 'amnesia'"""
|
||||||
helpers.platform.uname = lambda: ('Linux', 'amnesia', '3.14-1-amd64', '#1 SMP Debian 3.14.4-1 (2014-05-13)', 'x86_64', '')
|
helpers.platform.uname = lambda: ('Linux', 'amnesia', '3.14-1-amd64',
|
||||||
|
'#1 SMP Debian 3.14.4-1 (2014-05-13)', 'x86_64', '')
|
||||||
assert helpers.get_platform() == 'Tails'
|
assert helpers.get_platform() == 'Tails'
|
||||||
|
|
||||||
|
|
||||||
def test_get_platform_returns_platform_system():
|
def test_get_platform_returns_platform_system():
|
||||||
"get_platform() returns platform.system() when ONIONSHARE_PLATFORM is not defined"
|
"""get_platform() returns platform.system() when ONIONSHARE_PLATFORM is not defined"""
|
||||||
helpers.platform.system = lambda: 'Sega Saturn'
|
helpers.platform.system = lambda: 'Sega Saturn'
|
||||||
assert helpers.get_platform() == 'Sega Saturn'
|
assert helpers.get_platform() == 'Sega Saturn'
|
||||||
|
|
||||||
|
@ -21,19 +21,21 @@ import locale
|
|||||||
from onionshare import strings
|
from onionshare import strings
|
||||||
from nose import with_setup
|
from nose import with_setup
|
||||||
|
|
||||||
|
|
||||||
def test_starts_with_empty_strings():
|
def test_starts_with_empty_strings():
|
||||||
"creates an empty strings dict by default"
|
"""creates an empty strings dict by default"""
|
||||||
assert strings.strings == {}
|
assert strings.strings == {}
|
||||||
|
|
||||||
|
|
||||||
def test_load_strings_defaults_to_english():
|
def test_load_strings_defaults_to_english():
|
||||||
"load_strings() loads English by default"
|
"""load_strings() loads English by default"""
|
||||||
locale.getdefaultlocale = lambda: ('en_US', 'UTF-8')
|
locale.getdefaultlocale = lambda: ('en_US', 'UTF-8')
|
||||||
strings.load_strings()
|
strings.load_strings()
|
||||||
assert strings._('wait_for_hs') == "Waiting for HS to be ready:"
|
assert strings._('wait_for_hs') == "Waiting for HS to be ready:"
|
||||||
|
|
||||||
|
|
||||||
def test_load_strings_loads_other_languages():
|
def test_load_strings_loads_other_languages():
|
||||||
"load_strings() loads other languages in different locales"
|
"""load_strings() loads other languages in different locales"""
|
||||||
locale.getdefaultlocale = lambda: ('fr_FR', 'UTF-8')
|
locale.getdefaultlocale = lambda: ('fr_FR', 'UTF-8')
|
||||||
strings.load_strings("fr")
|
strings.load_strings("fr")
|
||||||
assert strings._('wait_for_hs') == "En attente du HS:"
|
assert strings._('wait_for_hs') == "En attente du HS:"
|
||||||
|
|
||||||
|
@ -20,14 +20,16 @@ import socket
|
|||||||
from onionshare import OnionShare
|
from onionshare import OnionShare
|
||||||
from nose import with_setup
|
from nose import with_setup
|
||||||
|
|
||||||
|
|
||||||
def test_choose_port_returns_a_port_number():
|
def test_choose_port_returns_a_port_number():
|
||||||
"choose_port() returns a port number"
|
"""choose_port() returns a port number"""
|
||||||
app = OnionShare()
|
app = OnionShare()
|
||||||
app.choose_port()
|
app.choose_port()
|
||||||
assert 1024 <= app.port <= 65535
|
assert 1024 <= app.port <= 65535
|
||||||
|
|
||||||
|
|
||||||
def test_choose_port_returns_an_open_port():
|
def test_choose_port_returns_an_open_port():
|
||||||
"choose_port() returns an open port"
|
"""choose_port() returns an open port"""
|
||||||
app = OnionShare()
|
app = OnionShare()
|
||||||
# choose a new port
|
# choose a new port
|
||||||
app.choose_port()
|
app.choose_port()
|
||||||
|
@ -19,17 +19,17 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
from onionshare import web
|
from onionshare import web
|
||||||
from nose import with_setup
|
from nose import with_setup
|
||||||
|
|
||||||
|
|
||||||
def test_generate_slug_length():
|
def test_generate_slug_length():
|
||||||
"generates a 26-character slug"
|
"""generates a 26-character slug"""
|
||||||
assert len(web.slug) == 26
|
assert len(web.slug) == 26
|
||||||
|
|
||||||
|
|
||||||
def test_generate_slug_characters():
|
def test_generate_slug_characters():
|
||||||
"generates a base32-encoded slug"
|
"""generates a base32-encoded slug"""
|
||||||
|
|
||||||
def is_b32(string):
|
def is_b32(string):
|
||||||
b32_alphabet = "01234556789abcdefghijklmnopqrstuvwxyz"
|
b32_alphabet = "01234556789abcdefghijklmnopqrstuvwxyz"
|
||||||
return all(char in b32_alphabet for char in string)
|
return all(char in b32_alphabet for char in string)
|
||||||
|
|
||||||
assert is_b32(web.slug)
|
assert is_b32(web.slug)
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,22 +18,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
"""
|
"""
|
||||||
import tempfile
|
import tempfile
|
||||||
|
|
||||||
|
|
||||||
class MockSubprocess():
|
class MockSubprocess():
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.last_call = None
|
self.last_call = None
|
||||||
|
|
||||||
def call(self, args):
|
def call(self, args):
|
||||||
self.last_call = args
|
self.last_call = args
|
||||||
|
|
||||||
|
def last_call_args(self):
|
||||||
|
return self.last_call
|
||||||
|
|
||||||
def last_call_args(self):
|
|
||||||
return self.last_call
|
|
||||||
|
|
||||||
def write_tempfile(text):
|
def write_tempfile(text):
|
||||||
tempdir = tempfile.mkdtemp()
|
tempdir = tempfile.mkdtemp()
|
||||||
path = tempdir + "/test-file.txt"
|
path = tempdir + "/test-file.txt"
|
||||||
with open(path, "w") as f:
|
with open(path, "w") as f:
|
||||||
f.write(text)
|
f.write(text)
|
||||||
f.close()
|
f.close()
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user