From 67ea5b5c2c4d9eb8d361963a7086aa34d8ca119e Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Fri, 6 Jun 2014 21:20:57 -0400 Subject: [PATCH] refactored GUI to use a second flask server for communication --- MANIFEST.in | 3 +- onionshare_gui/html/index.html | 14 -- onionshare_gui/html/onionshare.js | 24 ---- onionshare_gui/onionshare_gui.py | 125 +++++++----------- .../{html => static}/jquery-1.11.1.min.js | 0 onionshare_gui/static/loader_large.gif | Bin 0 -> 38106 bytes .../loader.gif => static/loader_small.gif} | Bin onionshare_gui/static/onionshare.js | 47 +++++++ onionshare_gui/{html => static}/style.css | 15 +++ onionshare_gui/templates/index.html | 17 +++ onionshare_gui/webapp.py | 46 +++++++ onionshare_gui/webgui.py | 79 ----------- 12 files changed, 177 insertions(+), 193 deletions(-) delete mode 100644 onionshare_gui/html/index.html delete mode 100644 onionshare_gui/html/onionshare.js rename onionshare_gui/{html => static}/jquery-1.11.1.min.js (100%) create mode 100644 onionshare_gui/static/loader_large.gif rename onionshare_gui/{html/loader.gif => static/loader_small.gif} (100%) create mode 100644 onionshare_gui/static/onionshare.js rename onionshare_gui/{html => static}/style.css (89%) create mode 100644 onionshare_gui/templates/index.html create mode 100644 onionshare_gui/webapp.py delete mode 100644 onionshare_gui/webgui.py diff --git a/MANIFEST.in b/MANIFEST.in index a2f210a6..57efb0e0 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,4 +2,5 @@ include LICENSE include README.md include onionshare/*.html include onionshare/strings.json -include onionshare_gui/html/* +include onionshare_gui/templates/* +include onionshare_gui/static/* diff --git a/onionshare_gui/html/index.html b/onionshare_gui/html/index.html deleted file mode 100644 index c99e3567..00000000 --- a/onionshare_gui/html/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - -

-
- - - - - - diff --git a/onionshare_gui/html/onionshare.js b/onionshare_gui/html/onionshare.js deleted file mode 100644 index 23e8ebdb..00000000 --- a/onionshare_gui/html/onionshare.js +++ /dev/null @@ -1,24 +0,0 @@ -function send(msg) { - document.title = "null"; - document.title = msg; -} - -function init(basename, strings) { - $('#basename').html(basename).show(); -} - -function url_is_set() { - $('#copy-button') - .click(function(){ - send('copy_url'); - }) - .show(); -} - -function update(msg) { - var $line = $('

').append(msg); - $('#output').append($line); - - // scroll to bottom - $('#output').scrollTop($('#output').height()); -} diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 82b692e5..9adc7a4c 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -1,13 +1,5 @@ -import onionshare, webgui -import os, sys, time, json, gtk, gobject, thread - -url = None - -class Global(object): - quit = False - @classmethod - def set_quit(cls, *args, **kwargs): - cls.quit = True +import onionshare, webapp +import threading, gtk, gobject, webkit, os, sys def alert(msg, type=gtk.MESSAGE_INFO): dialog = gtk.MessageDialog( @@ -48,84 +40,67 @@ def select_file(strings): basename = os.path.basename(filename) return filename, basename +def start_webapp(webapp_port, onionshare_port, filename, onion_host): + webapp.onionshare = onionshare + webapp.onionshare_port = onionshare_port + webapp.filename = filename + webapp.onion_host = onion_host + webapp.app.run(port=webapp_port) + +def launch_window(webapp_port, onionshare_port): + def on_destroy(widget, data=None): + onionshare.tails_close_port(onionshare_port) + gtk.main_quit() + + window = gtk.Window() + window.set_title('OnionShare') + window.resize(550, 300) + window.set_resizable(False) + window.connect('destroy', on_destroy) + + box = gtk.VBox(homogeneous=False, spacing=0) + window.add(box) + + browser = webkit.WebView() + box.pack_start(browser, expand=True, fill=True, padding=0) + + window.show_all() + + # wait half a second for server to start + gobject.timeout_add(500, browser.open, 'http://127.0.0.1:{0}/'.format(webapp_port)) + + gtk.main() + def main(): - global url - strings = onionshare.load_strings() + onionshare.strings = onionshare.load_strings() # try starting hidden service - port = onionshare.choose_port() + onionshare_port = onionshare.choose_port() try: - onion_host = onionshare.start_hidden_service(port) + onion_host = onionshare.start_hidden_service(onionshare_port) except onionshare.NoTor as e: alert(e.args[0], gtk.MESSAGE_ERROR) return - onionshare.tails_open_port(port) + onionshare.tails_open_port(onionshare_port) # select file to share - filename, basename = select_file(strings) + filename, basename = select_file(onionshare.strings) if not filename: return - # open the window, launching webkit browser - webgui.start_gtk_thread() - browser, web_recv, web_send = webgui.sync_gtk_msg(webgui.launch_window)( - title="OnionShare | {0}".format(basename), - quit_function=Global.set_quit, - echo=False) + # start the gui web server + webapp_port = onionshare.choose_port() + t = threading.Thread(target=start_webapp, kwargs={ + 'webapp_port': webapp_port, + 'onionshare_port': onionshare_port, + 'filename': filename, + 'onion_host': onion_host + }) + t.daemon = True + t.start() - # clipboard - clipboard = gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD) - def set_clipboard(): - global url - clipboard.set_text(url) - web_send("update('{0}')".format('Copied secret URL to clipboard.')) - - # the async nature of things requires startup to be split into multiple functions - def startup_async(): - global url - filehash, filesize = onionshare.file_crunching(filename) - onionshare.set_file_info(filename, filehash, filesize) - url = 'http://{0}/{1}'.format(onion_host, onionshare.slug) - web_send("update('{0}')".format(strings['give_this_url'].replace('\'', '\\\''))) - web_send("update('{0}')".format(url)) - web_send("url_is_set()") - - # clipboard needs a bit of time before copying url - gobject.timeout_add(500, set_clipboard) - - def startup_sync(): - web_send("init('{0}', {1});".format(basename, json.dumps(strings))) - web_send("update('{0}')".format(strings['calculating_sha1'])) - - # run other startup in the background - thread_crunch = thread.start_new_thread(startup_async, ()) - - # start the web server - thread_web = thread.start_new_thread(onionshare.app.run, (), {"port": port}) - - gobject.timeout_add(500, startup_sync) - - # main loop - last_second = time.time() - uptime_seconds = 1 - clicks = 0 - while not Global.quit: - - current_time = time.time() - again = False - msg = web_recv() - if msg: - again = True - - # check msg for messages from the browser - if msg == 'copy_url': - set_clipboard() - - if not again: - time.sleep(0.1) - - # shutdown - onionshare.tails_close_port(port) + # launch the window + launch_window(webapp_port, onionshare_port) if __name__ == '__main__': main() diff --git a/onionshare_gui/html/jquery-1.11.1.min.js b/onionshare_gui/static/jquery-1.11.1.min.js similarity index 100% rename from onionshare_gui/html/jquery-1.11.1.min.js rename to onionshare_gui/static/jquery-1.11.1.min.js diff --git a/onionshare_gui/static/loader_large.gif b/onionshare_gui/static/loader_large.gif new file mode 100644 index 0000000000000000000000000000000000000000..39832d33bc10f8018afcde449a6c84e871043dc1 GIT binary patch literal 38106 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 literal 0 HcmV?d00001 diff --git a/onionshare_gui/html/loader.gif b/onionshare_gui/static/loader_small.gif similarity index 100% rename from onionshare_gui/html/loader.gif rename to onionshare_gui/static/loader_small.gif diff --git a/onionshare_gui/static/onionshare.js b/onionshare_gui/static/onionshare.js new file mode 100644 index 00000000..e9d10125 --- /dev/null +++ b/onionshare_gui/static/onionshare.js @@ -0,0 +1,47 @@ +$(function(){ + onionshare = {} + + function update($msg) { + var $line = $('

').append($msg); + $('#output').append($line); + + // scroll to bottom + $('#output').scrollTop($('#output').height()); + } + + function linebreak() { + update($('
')); + } + + function copy_to_clipboard() { + $.ajax({ + url: '/copy_url', + success: function(data, textStatus, jqXHR){ + update('Copied secret URL to clipboard.'); + } + }); + } + $('#copy-button').click(copy_to_clipboard); + + // start onionshare + $.ajax({ + url: '/start_onionshare', + success: function(data, textStatus, jqXHR){ + onionshare = JSON.parse(data); + + $('#basename').html(onionshare.basename); + update("Sharing file: "+onionshare.basename+" ("+onionshare.filesize+" bytes)"); + update("SHA1 checksum: "+onionshare.filehash); + linebreak(); + update(onionshare.strings['give_this_url']); + update($('').html(onionshare.url)); + linebreak(); + copy_to_clipboard(); + $('#copy-button').show(); + + $('#loading').hide(); + $('#content').show(); + } + }); + +}); diff --git a/onionshare_gui/html/style.css b/onionshare_gui/static/style.css similarity index 89% rename from onionshare_gui/html/style.css rename to onionshare_gui/static/style.css index 604b7104..3e94e720 100644 --- a/onionshare_gui/html/style.css +++ b/onionshare_gui/static/style.css @@ -84,3 +84,18 @@ p { background-position: top left; background-repeat: no-repeat; } + +#loading { + width: 550px; + height: 300px; + background-color: #333333; + background-image: url('/static/loader_large.gif'); + background-repeat: no-repeat; + background-position: center center; +} + +#content { + width: 550px; + height: 300px; + display: none; +} diff --git a/onionshare_gui/templates/index.html b/onionshare_gui/templates/index.html new file mode 100644 index 00000000..cdeda357 --- /dev/null +++ b/onionshare_gui/templates/index.html @@ -0,0 +1,17 @@ + + + + + + +
+
+

+
+ - +
+ + + + + diff --git a/onionshare_gui/webapp.py b/onionshare_gui/webapp.py new file mode 100644 index 00000000..8269685a --- /dev/null +++ b/onionshare_gui/webapp.py @@ -0,0 +1,46 @@ +from flask import Flask, render_template +import threading, json, os, gtk + +onionshare = None +onionshare_port = None +filename = None +onion_host = None + +clipboard = gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD) +url = None + +app = Flask(__name__, template_folder='./templates') + +@app.route("/") +def index(): + return render_template('index.html') + +@app.route("/start_onionshare") +def start_onionshare(): + global onionshare, onionshare_port, filename, onion_host, url + + url = 'http://{0}/{1}'.format(onion_host, onionshare.slug) + + basename = os.path.basename(filename) + filehash, filesize = onionshare.file_crunching(filename) + onionshare.set_file_info(filename, filehash, filesize) + + # start onionshare service in new thread + t = threading.Thread(target=onionshare.app.run, kwargs={'port': onionshare_port}) + t.daemon = True + t.start() + + return json.dumps({ + 'strings': onionshare.strings, + 'basename': basename, + 'filehash': filehash, + 'filesize': filesize, + 'url': url + }) + +@app.route("/copy_url") +def copy_url(): + global clipboard + clipboard.set_text(url) + return '' + diff --git a/onionshare_gui/webgui.py b/onionshare_gui/webgui.py deleted file mode 100644 index 6b8f0f91..00000000 --- a/onionshare_gui/webgui.py +++ /dev/null @@ -1,79 +0,0 @@ -import time, Queue, thread, gtk, gobject, os, webkit - -def async_gtk_msg(fun): - def worker((function, args, kwargs)): - apply(function, args, kwargs) - - def fun2(*args, **kwargs): - gobject.idle_add(worker, (fun, args, kwargs)) - - return fun2 - -def sync_gtk_msg(fun): - class NoResult: pass - - def worker((R, function, args, kwargs)): - R.result = apply(function, args, kwargs) - - def fun2(*args, **kwargs): - class R: result = NoResult - gobject.idle_add(worker, (R, fun, args, kwargs)) - while R.result is NoResult: time.sleep(0.01) - return R.result - - return fun2 - -def launch_window(title='OnionShare', quit_function=None, echo=True): - window = gtk.Window() - window.set_title(title) - browser = webkit.WebView() - - box = gtk.VBox(homogeneous=False, spacing=0) - window.add(box) - - if quit_function is not None: - window.connect('destroy', quit_function) - - box.pack_start(browser, expand=True, fill=True, padding=0) - - window.set_default_size(600, 600) - window.set_resizable(False) - window.show_all() - - message_queue = Queue.Queue() - - def callback_wrapper(widget, frame, title): - if title != 'null': - message_queue.put(title) - browser.connect('title-changed', callback_wrapper) - - browser.open('file://'+os.path.abspath(os.path.dirname(__file__))+'/html/index.html') - - def web_recv(): - if message_queue.empty(): - return None - else: - msg = message_queue.get() - if echo: print '>>>', msg - return msg - - def web_send(msg): - if echo: print '<<<', msg - async_gtk_msg(browser.execute_script)(msg) - - return browser, web_recv, web_send - - -def start_gtk_thread(): - # Start GTK in its own thread: - gtk.gdk.threads_init() - thread.start_new_thread(gtk.main, ()) - -def kill_gtk_thread(): - async_gtk_msg(gtk.main_quit)() - -def main(): - if not select_file(): - return - - launch_browser()