From af46ee0bda470004c0c001c21c70148eaf30d652 Mon Sep 17 00:00:00 2001 From: Joshua Thayer Date: Thu, 5 Jun 2014 15:16:56 -0700 Subject: [PATCH 1/4] Adds meta tags to index.html --- onionshare/index.html | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/onionshare/index.html b/onionshare/index.html index 0513278d..8fad6edf 100644 --- a/onionshare/index.html +++ b/onionshare/index.html @@ -3,13 +3,13 @@ OnionShare + + +

{{ filename }} ▼

From 5b03b41bcd744a4f7961d36e9cd0b1081ea266c2 Mon Sep 17 00:00:00 2001 From: Joshua Thayer Date: Thu, 5 Jun 2014 15:52:05 -0700 Subject: [PATCH 2/4] Fixes port bug when using tor browser, adds local-only flag --- onionshare/onionshare.py | 126 ++++++++++++++++++++++++++++----------- 1 file changed, 91 insertions(+), 35 deletions(-) diff --git a/onionshare/onionshare.py b/onionshare/onionshare.py index 59fcfb89..90d3b04f 100644 --- a/onionshare/onionshare.py +++ b/onionshare/onionshare.py @@ -1,4 +1,4 @@ -import os, sys, subprocess, time, hashlib, platform, json, locale, socket +import os, sys, subprocess, time, hashlib, platform, json, locale, socket, re, argparse from random import randint from functools import wraps @@ -15,6 +15,7 @@ from stem.control import Controller from stem import SocketError from flask import Flask, Markup, Response, request, make_response, send_from_directory, render_template_string + app = Flask(__name__) strings = {} @@ -25,6 +26,44 @@ slug = os.urandom(16).encode('hex') # file information filename = filehash = filesize = '' +@app.after_request +def after_request(response): + response.headers.add('Accept-Ranges', 'bytes') + return response + +def send_file_partial(range_header): + global filename + + dirname = os.path.dirname(filename) + basename = os.path.basename(filename) + + size = os.path.getsize(filename) + byte1, byte2 = 0, None + + m = re.search('(\d+)-(\d*)', range_header) + g = m.groups() + + if g[0]: byte1 = int(g[0]) + if g[1]: byte2 = int(g[1]) + + length = size - byte1 + if byte2 is not None: + length = (byte2 - byte1) + 1 + + data = None + with open(filename, 'rb') as f: + f.seek(byte1) + data = f.read(length) + + rv = Response(data, + 206, + # mimetype=mimetypes.guess_type(path)[0], + direct_passthrough=True) + rv.headers.add('Content-Range', 'bytes {0}-{1}/{2}'.format(byte1, byte1 + length - 1, size)) + + return rv + + @app.route("/{0}".format(slug)) def index(): global filename, filesize, filehash, slug, strings @@ -34,6 +73,11 @@ def index(): @app.route("/{0}/download".format(slug)) def download(): global filename + + range_header = request.headers.get('Range', None) + if range_header: + return send_file_partial(range_header) + dirname = os.path.dirname(filename) basename = os.path.basename(filename) return send_from_directory(dirname, basename, as_attachment=True) @@ -81,14 +125,16 @@ def main(): global filename, filehash, filesize load_strings() - # validate filename - if len(sys.argv) != 2: - sys.exit('Usage: {0} [filename]'.format(sys.argv[0])); - filename = sys.argv[1] - if not os.path.isfile(filename): + parser = argparse.ArgumentParser() + parser.add_argument('--local-only', action='store_true', dest='local_only', help='Do not attempt to use tor: for development only') + parser.add_argument('filename', nargs=1) + args = parser.parse_args() + + filename = os.path.abspath(args.filename[0]) + local_only = args.local_only + + if not (filename and os.path.isfile(filename)): sys.exit(strings["not_a_file"].format(filename)) - else: - filename = os.path.abspath(filename) # calculate filehash, file size print strings["calculating_sha1"] @@ -108,41 +154,51 @@ def main(): port = tmpsock.getsockname()[1] tmpsock.close() - # connect to the tor controlport - print strings["connecting_ctrlport"].format(port) - controlports = [9051, 9151] - controller = False - for controlport in controlports: - try: - controller = Controller.from_port(port=controlport) - except SocketError: - pass - if not controller: - sys.exit(strings["cant_connect_ctrlport"].format(controlports)) - controller.authenticate() + local_host = "127.0.0.1:{0}".format(port) - # set up hidden service - controller.set_options([ - ('HiddenServiceDir', get_hidden_service_dir(port)), - ('HiddenServicePort', '80 127.0.0.1:{0}'.format(port)) - ]) - onion_host = get_hidden_service_hostname(port) + if not local_only: + # connect to the tor controlport + print strings["connecting_ctrlport"].format(port) + controlports = [9051, 9151] + controller = False - # punch a hole in the firewall - tails_open_port(port) + for controlport in controlports: + try: + if not controller: + controller = Controller.from_port(port=controlport) + except SocketError: + pass + if not controller: + sys.exit(strings["cant_connect_ctrlport"].format(controlports)) + + controller.authenticate() + + # set up hidden service + controller.set_options([ + ('HiddenServiceDir', get_hidden_service_dir(port)), + ('HiddenServicePort', '80 127.0.0.1:{0}'.format(port)) + ]) + onion_host = get_hidden_service_hostname(port) + + # punch a hole in the firewall + tails_open_port(port) + # instructions print '\n' + strings["give_this_url"] - print 'http://{0}/{1}'.format(onion_host, slug) + if local_only: + print 'http://{0}/{1}'.format(local_host, slug) + else: + print 'http://{0}/{1}'.format(onion_host, slug) print '' print strings["ctrlc_to_stop"] - + # start the web server - app.run(port=port) + app.run(port=port, debug=True) print '\n' - + # shutdown tails_close_port(port) - -if __name__ == '__main__': - main() + + if __name__ == '__main__': + main() From d0b3145e17e2f56afad00f4f8fbefc3139e57cf7 Mon Sep 17 00:00:00 2001 From: Joshua Thayer Date: Thu, 5 Jun 2014 16:11:21 -0700 Subject: [PATCH 3/4] Adds local-only command line flag. --- onionshare/onionshare.py | 175 ++++++++++++++++++--------------------- 1 file changed, 80 insertions(+), 95 deletions(-) diff --git a/onionshare/onionshare.py b/onionshare/onionshare.py index 90d3b04f..790c4497 100644 --- a/onionshare/onionshare.py +++ b/onionshare/onionshare.py @@ -1,4 +1,4 @@ -import os, sys, subprocess, time, hashlib, platform, json, locale, socket, re, argparse +import os, sys, subprocess, time, hashlib, platform, json, locale, socket, argparse from random import randint from functools import wraps @@ -8,14 +8,20 @@ def get_platform(): else: return platform.system() -if get_platform() == 'Tails': +def append_lib_on_tails(): + if get_platform() == 'Tails': sys.path.append(os.path.dirname(__file__)+'/../tails/lib') +append_lib_on_tails() + from stem.control import Controller from stem import SocketError from flask import Flask, Markup, Response, request, make_response, send_from_directory, render_template_string +class NoTor(Exception): + pass + app = Flask(__name__) strings = {} @@ -23,61 +29,26 @@ strings = {} # generate an unguessable string slug = os.urandom(16).encode('hex') -# file information -filename = filehash = filesize = '' - -@app.after_request -def after_request(response): - response.headers.add('Accept-Ranges', 'bytes') - return response - -def send_file_partial(range_header): - global filename - - dirname = os.path.dirname(filename) - basename = os.path.basename(filename) - - size = os.path.getsize(filename) - byte1, byte2 = 0, None - - m = re.search('(\d+)-(\d*)', range_header) - g = m.groups() - - if g[0]: byte1 = int(g[0]) - if g[1]: byte2 = int(g[1]) - - length = size - byte1 - if byte2 is not None: - length = (byte2 - byte1) + 1 - - data = None - with open(filename, 'rb') as f: - f.seek(byte1) - data = f.read(length) - - rv = Response(data, - 206, - # mimetype=mimetypes.guess_type(path)[0], - direct_passthrough=True) - rv.headers.add('Content-Range', 'bytes {0}-{1}/{2}'.format(byte1, byte1 + length - 1, size)) - - return rv - +# information about the file +filename = filesize = filehash = None +def set_file_info(new_filename, new_filehash, new_filesize): + global filename, filehash, filesize + filename = new_filename + filehash = new_filehash + filesize = new_filesize @app.route("/{0}".format(slug)) def index(): global filename, filesize, filehash, slug, strings + print 'filename: {0}'.format(filename) + print 'filehash: {0}'.format(filehash) + print 'filesize: {0}'.format(filesize) return render_template_string(open('{0}/index.html'.format(os.path.dirname(__file__))).read(), slug=slug, filename=os.path.basename(filename), filehash=filehash, filesize=filesize, strings=strings) @app.route("/{0}/download".format(slug)) def download(): global filename - - range_header = request.headers.get('Range', None) - if range_header: - return send_file_partial(range_header) - dirname = os.path.dirname(filename) basename = os.path.basename(filename) return send_from_directory(dirname, basename, as_attachment=True) @@ -120,24 +91,10 @@ def load_strings(default="en"): lang = lc[:2] if lang in translated: strings = translated[lang] + return strings -def main(): - global filename, filehash, filesize - load_strings() - - parser = argparse.ArgumentParser() - parser.add_argument('--local-only', action='store_true', dest='local_only', help='Do not attempt to use tor: for development only') - parser.add_argument('filename', nargs=1) - args = parser.parse_args() - - filename = os.path.abspath(args.filename[0]) - local_only = args.local_only - - if not (filename and os.path.isfile(filename)): - sys.exit(strings["not_a_file"].format(filename)) - +def file_crunching(filename): # calculate filehash, file size - print strings["calculating_sha1"] BLOCKSIZE = 65536 hasher = hashlib.sha1() with open(filename, 'rb') as f: @@ -147,44 +104,72 @@ def main(): buf = f.read(BLOCKSIZE) filehash = hasher.hexdigest() filesize = os.path.getsize(filename) + return filehash, filesize +def choose_port(): # let the OS choose a port tmpsock = socket.socket() tmpsock.bind(("127.0.0.1", 0)) port = tmpsock.getsockname()[1] tmpsock.close() + return port +def start_hidden_service(port): + # connect to the tor controlport + controlports = [9051, 9151] + controller = False + + for controlport in controlports: + try: + controller = Controller.from_port(port=controlport) + except SocketError: + pass + + if not controller: + raise NoTor(strings["cant_connect_ctrlport"].format(controlports)) + + controller.authenticate() + + # set up hidden service + controller.set_options([ + ('HiddenServiceDir', get_hidden_service_dir(port)), + ('HiddenServicePort', '80 127.0.0.1:{0}'.format(port)) + ]) + + onion_host = get_hidden_service_hostname(port) + return onion_host + +def main(): + load_strings() + + parser = argparse.ArgumentParser() + parser.add_argument('--local-only', action='store_true', dest='local_only', help='Do not attempt to use tor: for development only') + parser.add_argument('filename', nargs=1) + args = parser.parse_args() + + filename = os.path.abspath(args.filename[0]) + local_only = args.local_only + + if not (filename and os.path.isfile(filename)): + sys.exit(strings["not_a_file"].format(filename)) + filename = os.path.abspath(filename) + + port = choose_port() local_host = "127.0.0.1:{0}".format(port) if not local_only: - # connect to the tor controlport + # try starting hidden service print strings["connecting_ctrlport"].format(port) - controlports = [9051, 9151] - controller = False - - for controlport in controlports: - try: - if not controller: - controller = Controller.from_port(port=controlport) - except SocketError: - pass - - if not controller: - sys.exit(strings["cant_connect_ctrlport"].format(controlports)) - - controller.authenticate() + try: + onion_host = start_hidden_service(port) + except NoTor as e: + sys.exit(e.args[0]) - # set up hidden service - controller.set_options([ - ('HiddenServiceDir', get_hidden_service_dir(port)), - ('HiddenServicePort', '80 127.0.0.1:{0}'.format(port)) - ]) - onion_host = get_hidden_service_hostname(port) - - # punch a hole in the firewall - tails_open_port(port) - - # instructions + # startup + print strings["calculating_sha1"] + filehash, filesize = file_crunching(filename) + set_file_info(filename, filehash, filesize) + tails_open_port(port) print '\n' + strings["give_this_url"] if local_only: print 'http://{0}/{1}'.format(local_host, slug) @@ -192,13 +177,13 @@ def main(): print 'http://{0}/{1}'.format(onion_host, slug) print '' print strings["ctrlc_to_stop"] - + # start the web server - app.run(port=port, debug=True) + app.run(port=port) print '\n' - + # shutdown tails_close_port(port) - - if __name__ == '__main__': - main() + +if __name__ == '__main__': + main() From d27200208418925882632155af17184f79406c7b Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Fri, 6 Jun 2014 15:48:52 -0400 Subject: [PATCH 4/4] removing unnecessary output, whitespace --- onionshare/onionshare.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/onionshare/onionshare.py b/onionshare/onionshare.py index 790c4497..715af807 100644 --- a/onionshare/onionshare.py +++ b/onionshare/onionshare.py @@ -40,9 +40,6 @@ def set_file_info(new_filename, new_filehash, new_filesize): @app.route("/{0}".format(slug)) def index(): global filename, filesize, filehash, slug, strings - print 'filename: {0}'.format(filename) - print 'filehash: {0}'.format(filehash) - print 'filesize: {0}'.format(filesize) return render_template_string(open('{0}/index.html'.format(os.path.dirname(__file__))).read(), slug=slug, filename=os.path.basename(filename), filehash=filehash, filesize=filesize, strings=strings) @@ -164,7 +161,7 @@ def main(): onion_host = start_hidden_service(port) except NoTor as e: sys.exit(e.args[0]) - + # startup print strings["calculating_sha1"] filehash, filesize = file_crunching(filename)