refactored GUI to use a second flask server for communication

This commit is contained in:
Micah Lee 2014-06-06 21:20:57 -04:00
parent 9daa475cc0
commit 67ea5b5c2c
12 changed files with 177 additions and 193 deletions

View File

@ -2,4 +2,5 @@ include LICENSE
include README.md include README.md
include onionshare/*.html include onionshare/*.html
include onionshare/strings.json include onionshare/strings.json
include onionshare_gui/html/* include onionshare_gui/templates/*
include onionshare_gui/static/*

View File

@ -1,14 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="style.css" />
</head>
<body>
<h1><span id="basename"></span></h1>
<div id="output"></div>
<button class="button" id="copy-button">Copy URL</button>
<script src="jquery-1.11.1.min.js"></script>
<script src="onionshare.js"></script>
</body>
</html>

View File

@ -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 = $('<p></p>').append(msg);
$('#output').append($line);
// scroll to bottom
$('#output').scrollTop($('#output').height());
}

View File

@ -1,13 +1,5 @@
import onionshare, webgui import onionshare, webapp
import os, sys, time, json, gtk, gobject, thread import threading, gtk, gobject, webkit, os, sys
url = None
class Global(object):
quit = False
@classmethod
def set_quit(cls, *args, **kwargs):
cls.quit = True
def alert(msg, type=gtk.MESSAGE_INFO): def alert(msg, type=gtk.MESSAGE_INFO):
dialog = gtk.MessageDialog( dialog = gtk.MessageDialog(
@ -48,84 +40,67 @@ def select_file(strings):
basename = os.path.basename(filename) basename = os.path.basename(filename)
return filename, basename 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(): def main():
global url onionshare.strings = onionshare.load_strings()
strings = onionshare.load_strings()
# try starting hidden service # try starting hidden service
port = onionshare.choose_port() onionshare_port = onionshare.choose_port()
try: try:
onion_host = onionshare.start_hidden_service(port) onion_host = onionshare.start_hidden_service(onionshare_port)
except onionshare.NoTor as e: except onionshare.NoTor as e:
alert(e.args[0], gtk.MESSAGE_ERROR) alert(e.args[0], gtk.MESSAGE_ERROR)
return return
onionshare.tails_open_port(port) onionshare.tails_open_port(onionshare_port)
# select file to share # select file to share
filename, basename = select_file(strings) filename, basename = select_file(onionshare.strings)
if not filename: if not filename:
return return
# open the window, launching webkit browser # start the gui web server
webgui.start_gtk_thread() webapp_port = onionshare.choose_port()
browser, web_recv, web_send = webgui.sync_gtk_msg(webgui.launch_window)( t = threading.Thread(target=start_webapp, kwargs={
title="OnionShare | {0}".format(basename), 'webapp_port': webapp_port,
quit_function=Global.set_quit, 'onionshare_port': onionshare_port,
echo=False) 'filename': filename,
'onion_host': onion_host
})
t.daemon = True
t.start()
# clipboard # launch the window
clipboard = gtk.clipboard_get(gtk.gdk.SELECTION_CLIPBOARD) launch_window(webapp_port, onionshare_port)
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('<strong>{0}</strong>')".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)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@ -0,0 +1,47 @@
$(function(){
onionshare = {}
function update($msg) {
var $line = $('<p></p>').append($msg);
$('#output').append($line);
// scroll to bottom
$('#output').scrollTop($('#output').height());
}
function linebreak() {
update($('<hr>'));
}
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($('<strong>').html(onionshare.url));
linebreak();
copy_to_clipboard();
$('#copy-button').show();
$('#loading').hide();
$('#content').show();
}
});
});

View File

@ -84,3 +84,18 @@ p {
background-position: top left; background-position: top left;
background-repeat: no-repeat; 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;
}

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="static/style.css" />
</head>
<body>
<div id="loading"></div>
<div id="content">
<h1><span id="basename"></span></h1>
<div id="output"></div>
<button class="button" id="copy-button">Copy URL</button>-
</div>
<script src="static/jquery-1.11.1.min.js"></script>
<script src="static/onionshare.js"></script>
</body>
</html>

46
onionshare_gui/webapp.py Normal file
View File

@ -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 ''

View File

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