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()