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 00000000..39832d33 Binary files /dev/null and b/onionshare_gui/static/loader_large.gif differ 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()