mirror of
https://github.com/onionshare/onionshare.git
synced 2024-12-24 23:09:42 -05:00
refactored GUI to use a second flask server for communication
This commit is contained in:
parent
9daa475cc0
commit
67ea5b5c2c
@ -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/*
|
||||
|
@ -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>
|
@ -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());
|
||||
}
|
@ -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('<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)
|
||||
# launch the window
|
||||
launch_window(webapp_port, onionshare_port)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
BIN
onionshare_gui/static/loader_large.gif
Normal file
BIN
onionshare_gui/static/loader_large.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 8.3 KiB |
47
onionshare_gui/static/onionshare.js
Normal file
47
onionshare_gui/static/onionshare.js
Normal 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();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
@ -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;
|
||||
}
|
17
onionshare_gui/templates/index.html
Normal file
17
onionshare_gui/templates/index.html
Normal 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
46
onionshare_gui/webapp.py
Normal 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 ''
|
||||
|
@ -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()
|
Loading…
Reference in New Issue
Block a user