refactored onionshare_gui to work with new refactored onionshare

This commit is contained in:
Micah Lee 2014-08-26 19:04:39 -07:00
parent 481d51ae69
commit eebcdfb81e
2 changed files with 82 additions and 112 deletions

View file

@ -39,10 +39,14 @@ class OnionShare(object):
self.port = tmpsock.getsockname()[1] self.port = tmpsock.getsockname()[1]
tmpsock.close() tmpsock.close()
def start_hidden_service(self): def start_hidden_service(self, gui=False):
if helpers.get_platform() == 'Tails': if helpers.get_platform() == 'Tails':
# in Tails, start the hidden service in a root process # in Tails, start the hidden service in a root process
p = subprocess.Popen(['/usr/bin/sudo', '--', '/usr/bin/onionshare', str(app.port)], stderr=subprocess.PIPE, stdout=subprocess.PIPE) if gui:
args = ['/usr/bin/gksudo', '-D', 'OnionShare', '--', '/usr/bin/onionshare']
else:
args = ['/usr/bin/sudo', '--', '/usr/bin/onionshare']
p = subprocess.Popen(args+[str(app.port)], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
stdout = p.stdout.read(22) # .onion URLs are 22 chars long stdout = p.stdout.read(22) # .onion URLs are 22 chars long
if stdout: if stdout:

View file

@ -1,41 +1,33 @@
from __future__ import division from __future__ import division
import os, sys, subprocess, inspect, platform, argparse, threading, time, math import os, sys, subprocess, inspect, platform, argparse, threading, time, math, inspect, platform
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
def get_onionshare_gui_dir():
if platform.system() == 'Darwin': if platform.system() == 'Darwin':
onionshare_gui_dir = os.path.dirname(__file__) onionshare_gui_dir = os.path.dirname(__file__)
else: else:
onionshare_gui_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))) onionshare_gui_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
return onionshare_gui_dir
try: try:
import onionshare import onionshare
except ImportError: except ImportError:
sys.path.append(os.path.abspath(onionshare_gui_dir+"/..")) sys.path.append(os.path.abspath(__file__+"/.."))
import onionshare import onionshare
from onionshare import translated from onionshare import strings, helpers, web
app = None
window_icon = None
onion_host = None
port = None
progress = None
# request types
REQUEST_LOAD = 0
REQUEST_DOWNLOAD = 1
REQUEST_PROGRESS = 2
REQUEST_OTHER = 3
class Application(QtGui.QApplication): class Application(QtGui.QApplication):
def __init__(self): def __init__(self):
platform = onionshare.get_platform() platform = helpers.get_platform()
if platform == 'Tails' or platform == 'Linux': if platform == 'Tails' or platform == 'Linux':
self.setAttribute(QtCore.Qt.AA_X11InitThreads, True) self.setAttribute(QtCore.Qt.AA_X11InitThreads, True)
QtGui.QApplication.__init__(self, sys.argv) QtGui.QApplication.__init__(self, sys.argv)
class OnionShareGui(QtGui.QWidget): class OnionShareGui(QtGui.QWidget):
def __init__(self, filename, basename): def __init__(self, app, filename, basename):
super(OnionShareGui, self).__init__() super(OnionShareGui, self).__init__()
self.app = app
# initialize ui # initialize ui
self.init_ui(filename, basename) self.init_ui(filename, basename)
# check for requests every 1000ms # check for requests every 1000ms
@ -72,7 +64,7 @@ class OnionShareGui(QtGui.QWidget):
# logo # logo
self.logoLabel = QtGui.QLabel(self.widget) self.logoLabel = QtGui.QLabel(self.widget)
self.logo = QtGui.QPixmap("{0}/static/logo.png".format(onionshare_gui_dir)) self.logo = QtGui.QPixmap("{0}/static/logo.png".format(get_onionshare_gui_dir()))
self.logoLabel.setPixmap(self.logo) self.logoLabel.setPixmap(self.logo)
self.header.addWidget(self.logoLabel) self.header.addWidget(self.logoLabel)
@ -127,7 +119,7 @@ class OnionShareGui(QtGui.QWidget):
# close automatically checkbox # close automatically checkbox
self.closeAutomatically = QtGui.QCheckBox(self.widget) self.closeAutomatically = QtGui.QCheckBox(self.widget)
self.closeAutomatically.setCheckState(QtCore.Qt.Checked) self.closeAutomatically.setCheckState(QtCore.Qt.Checked)
if onionshare.get_stay_open(): if web.get_stay_open():
self.closeAutomatically.setCheckState(QtCore.Qt.Unchecked) self.closeAutomatically.setCheckState(QtCore.Qt.Unchecked)
self.closeAutomatically.setStyleSheet("font-size: 12px") self.closeAutomatically.setStyleSheet("font-size: 12px")
@ -145,41 +137,40 @@ class OnionShareGui(QtGui.QWidget):
self.footer.addWidget(self.copyURL) self.footer.addWidget(self.copyURL)
self.wrapper.addLayout(self.footer) self.wrapper.addLayout(self.footer)
url = 'http://{0}/{1}'.format(onion_host, onionshare.slug) url = 'http://{0}/{1}'.format(self.app.onion_host, web.slug)
filehash, filesize = onionshare.file_crunching(filename) filehash, filesize = helpers.file_crunching(filename)
onionshare.set_file_info(filename, filehash, filesize) web.set_file_info(filename, filehash, filesize)
onionshare.filesize = filesize
# start onionshare service in new thread # start onionshare service in new thread
t = threading.Thread(target=onionshare.app.run, kwargs={'port': port}) t = threading.Thread(target=web.start, args=(self.app.port, self.app.stay_open))
t.daemon = True t.daemon = True
t.start() t.start()
# show url to share # show url to share
loaded = QtGui.QLabel(translated("give_this_url") + "<br /><strong>" + url + "</strong>") loaded = QtGui.QLabel(strings._("give_this_url") + "<br /><strong>" + url + "</strong>")
loaded.setStyleSheet("color: #000000; font-size: 14px; padding: 5px 10px; border-bottom: 1px solid #cccccc;") loaded.setStyleSheet("color: #000000; font-size: 14px; padding: 5px 10px; border-bottom: 1px solid #cccccc;")
loaded.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) loaded.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
self.log.addWidget(loaded) self.log.addWidget(loaded)
# translate # translate
self.filenameLabel.setText(basename) self.filenameLabel.setText(basename)
self.checksumLabel.setText(translated("sha1_checksum") + ": <strong>" + filehash + "</strong>") self.checksumLabel.setText(strings._("sha1_checksum") + ": <strong>" + filehash + "</strong>")
self.filesizeLabel.setText(translated("filesize") + ": <strong>" + onionshare.human_readable_filesize(filesize) + "</strong>") self.filesizeLabel.setText(strings._("filesize") + ": <strong>" + helpers.human_readable_filesize(filesize) + "</strong>")
self.closeAutomatically.setText(translated("close_on_finish")) self.closeAutomatically.setText(strings._("close_on_finish"))
self.copyURL.setText(translated("copy_url")) self.copyURL.setText(strings._("copy_url"))
# show dialog # show dialog
self.show() self.show()
def update_log(self, event, msg): def update_log(self, event, msg):
global progress global progress
if event["type"] == REQUEST_LOAD: if event["type"] == web.REQUEST_LOAD:
label = QtGui.QLabel(msg) label = QtGui.QLabel(msg)
label.setStyleSheet("color: #009900; font-weight: bold; font-size: 14px; padding: 5px 10px; border-bottom: 1px solid #cccccc;") label.setStyleSheet("color: #009900; font-weight: bold; font-size: 14px; padding: 5px 10px; border-bottom: 1px solid #cccccc;")
label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
self.log.addWidget(label) self.log.addWidget(label)
elif event["type"] == REQUEST_DOWNLOAD: elif event["type"] == web.REQUEST_DOWNLOAD:
download = QtGui.QLabel(msg) download = QtGui.QLabel(msg)
download.setStyleSheet("color: #009900; font-weight: bold; font-size: 14px; padding: 5px 10px; border-bottom: 1px solid #cccccc;") download.setStyleSheet("color: #009900; font-weight: bold; font-size: 14px; padding: 5px 10px; border-bottom: 1px solid #cccccc;")
download.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) download.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
@ -188,7 +179,7 @@ class OnionShareGui(QtGui.QWidget):
progress.setStyleSheet("color: #0000cc; font-weight: bold; font-size: 14px; padding: 5px 10px; border-bottom: 1px solid #cccccc;") progress.setStyleSheet("color: #0000cc; font-weight: bold; font-size: 14px; padding: 5px 10px; border-bottom: 1px solid #cccccc;")
progress.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) progress.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
self.log.addWidget(progress) self.log.addWidget(progress)
elif event["type"] == REQUEST_PROGRESS: elif event["type"] == web.REQUEST_PROGRESS:
progress.setText(msg) progress.setText(msg)
elif event["path"] != '/favicon.ico': elif event["path"] != '/favicon.ico':
other = QtGui.QLabel(msg) other = QtGui.QLabel(msg)
@ -203,22 +194,22 @@ class OnionShareGui(QtGui.QWidget):
done = False done = False
while not done: while not done:
try: try:
r = onionshare.q.get(False) r = web.q.get(False)
events.append(r) events.append(r)
except onionshare.Queue.Empty: except web.Queue.Empty:
done = True done = True
for event in events: for event in events:
if event["type"] == REQUEST_LOAD: if event["type"] == web.REQUEST_LOAD:
self.update_log(event, translated("download_page_loaded")) self.update_log(event, strings._("download_page_loaded"))
elif event["type"] == REQUEST_DOWNLOAD: elif event["type"] == web.REQUEST_DOWNLOAD:
self.update_log(event, translated("download_started")) self.update_log(event, strings._("download_started"))
elif event["type"] == REQUEST_PROGRESS: elif event["type"] == web.REQUEST_PROGRESS:
# is the download complete? # is the download complete?
if event["data"]["bytes"] == onionshare.filesize: if event["data"]["bytes"] == web.filesize:
self.update_log(event, translated("download_finished")) self.update_log(event, strings._("download_finished"))
# close on finish? # close on finish?
if not onionshare.get_stay_open(): if not web.get_stay_open():
time.sleep(1) time.sleep(1)
def close_countdown(i): def close_countdown(i):
if i > 0: if i > 0:
@ -226,28 +217,27 @@ class OnionShareGui(QtGui.QWidget):
else: else:
time.sleep(1) time.sleep(1)
i -= 1 i -= 1
closing.setText(translated("close_countdown").format(str(i))) closing.setText(strings._("close_countdown").format(str(i)))
print translated("close_countdown").format(str(i)) print strings._("close_countdown").format(str(i))
close_countdown(i) close_countdown(i)
closing = QtGui.QLabel(self.widget) closing = QtGui.QLabel(self.widget)
closing.setStyleSheet("font-weight: bold; font-style: italic; font-size: 14px; padding: 5px 10px; border-bottom: 1px solid #cccccc;") closing.setStyleSheet("font-weight: bold; font-style: italic; font-size: 14px; padding: 5px 10px; border-bottom: 1px solid #cccccc;")
closing.setText(translated("close_countdown").format("3")) closing.setText(strings._("close_countdown").format("3"))
closing.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) closing.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
self.log.addWidget(closing) self.log.addWidget(closing)
close_countdown(3) close_countdown(3)
# still in progress # still in progress
else: else:
percent = math.floor((event["data"]["bytes"] / onionshare.filesize) * 100) percent = math.floor((event["data"]["bytes"] / web.filesize) * 100)
self.update_log(event, " " + onionshare.human_readable_filesize(event["data"]["bytes"]) + ', ' + str(percent) +'%') self.update_log(event, " " + helpers.human_readable_filesize(event["data"]["bytes"]) + ', ' + str(percent) +'%')
elif event["path"] != '/favicon.ico': elif event["path"] != '/favicon.ico':
self.update_log(event, translated("other_page_loaded")) self.update_log(event, strings._("other_page_loaded"))
def copy_to_clipboard(self): def copy_to_clipboard(self):
global onion_host url = 'http://{0}/{1}'.format(self.app.onion_host, web.slug)
url = 'http://{0}/{1}'.format(onion_host, onionshare.slug)
if platform.system() == 'Windows': if platform.system() == 'Windows':
# Qt's QClipboard isn't working in Windows # Qt's QClipboard isn't working in Windows
@ -263,10 +253,10 @@ class OnionShareGui(QtGui.QWidget):
ctypes.windll.user32.SetClipboardData(1, hcd) ctypes.windll.user32.SetClipboardData(1, hcd)
ctypes.windll.user32.CloseClipboard() ctypes.windll.user32.CloseClipboard()
else: else:
clipboard = app.clipboard() clipboard = qtapp.clipboard()
clipboard.setText(url) clipboard.setText(url)
copied = QtGui.QLabel(translated("copied_url")) copied = QtGui.QLabel(strings._("copied_url"))
copied.setStyleSheet("font-size: 14px; padding: 5px 10px; border-bottom: 1px solid #cccccc;") copied.setStyleSheet("font-size: 14px; padding: 5px 10px; border-bottom: 1px solid #cccccc;")
copied.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse) copied.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
self.log.addWidget(copied) self.log.addWidget(copied)
@ -274,13 +264,12 @@ class OnionShareGui(QtGui.QWidget):
def stay_open_changed(self, state): def stay_open_changed(self, state):
if state > 0: if state > 0:
onionshare.set_stay_open(False) web.set_stay_open(False)
else: else:
onionshare.set_stay_open(True) web.set_stay_open(True)
return return
def alert(msg, icon=QtGui.QMessageBox.NoIcon): def alert(msg, icon=QtGui.QMessageBox.NoIcon):
global window_icon
dialog = QtGui.QMessageBox() dialog = QtGui.QMessageBox()
dialog.setWindowTitle("OnionShare") dialog.setWindowTitle("OnionShare")
dialog.setWindowIcon(window_icon) dialog.setWindowIcon(window_icon)
@ -288,10 +277,10 @@ def alert(msg, icon=QtGui.QMessageBox.NoIcon):
dialog.setIcon(icon) dialog.setIcon(icon)
dialog.exec_() dialog.exec_()
def select_file(strings, filename=None): def select_file(filename=None):
# get filename, either from argument or file chooser dialog # get filename, either from argument or file chooser dialog
if not filename: if not filename:
filename = QtGui.QFileDialog.getOpenFileName(caption=translated('choose_file'), options=QtGui.QFileDialog.ReadOnly) filename = QtGui.QFileDialog.getOpenFileName(caption=strings._('choose_file'), options=QtGui.QFileDialog.ReadOnly)
if not filename: if not filename:
return False, False return False, False
@ -299,7 +288,7 @@ def select_file(strings, filename=None):
# validate filename # validate filename
if not os.path.isfile(filename): if not os.path.isfile(filename):
alert(translated("not_a_file").format(filename), QtGui.QMessageBox.Warning) alert(strings._("not_a_file").format(filename), QtGui.QMessageBox.Warning)
return False, False return False, False
filename = os.path.abspath(filename) filename = os.path.abspath(filename)
@ -307,19 +296,18 @@ def select_file(strings, filename=None):
return filename, basename return filename, basename
def main(): def main():
global port strings.load_strings()
onionshare.strings = onionshare.load_strings()
# start the Qt app # start the Qt app
global app global qtapp
app = Application() qtapp = Application()
# parse arguments # parse arguments
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('--local-only', action='store_true', dest='local_only', help=translated("help_local_only")) parser.add_argument('--local-only', action='store_true', dest='local_only', help=strings._("help_local_only"))
parser.add_argument('--stay-open', action='store_true', dest='stay_open', help=translated("help_stay_open")) parser.add_argument('--stay-open', action='store_true', dest='stay_open', help=strings._("help_stay_open"))
parser.add_argument('--debug', action='store_true', dest='debug', help=translated("help_debug")) parser.add_argument('--debug', action='store_true', dest='debug', help=strings._("help_debug"))
parser.add_argument('filename', nargs='?', help=translated("help_filename")) parser.add_argument('filename', nargs='?', help=strings._("help_filename"))
args = parser.parse_args() args = parser.parse_args()
filename = args.filename filename = args.filename
@ -327,60 +315,38 @@ def main():
stay_open = bool(args.stay_open) stay_open = bool(args.stay_open)
debug = bool(args.debug) debug = bool(args.debug)
onionshare.set_stay_open(stay_open) web.set_stay_open(stay_open)
if debug:
onionshare.debug_mode()
# create the onionshare icon # create the onionshare icon
global window_icon, onionshare_gui_dir global window_icon
window_icon = QtGui.QIcon("{0}/static/logo.png".format(onionshare_gui_dir)) window_icon = QtGui.QIcon("{0}/static/logo.png".format(get_onionshare_gui_dir()))
# try starting hidden service # start the onionshare app
global onion_host
port = onionshare.choose_port()
local_host = "127.0.0.1:{0}".format(port)
if onionshare.get_platform() == 'Tails':
# if this is tails, start the root process
root_p = subprocess.Popen(['/usr/bin/gksudo', '-D', 'OnionShare', '--', '/usr/bin/onionshare', str(port)], stderr=subprocess.PIPE, stdout=subprocess.PIPE)
stdout = root_p.stdout.read(22) # .onion URLs are 22 chars long
if stdout:
onion_host = stdout
else:
if root_p.poll() == -1:
alert(root_p.stderr.read())
return
else:
alert(translated("error_tails_unknown_root"))
return
else:
# if not tails, start hidden service normally
if not local_only:
try: try:
onion_host = onionshare.start_hidden_service(port) app = onionshare.OnionShare(debug, local_only, stay_open)
app.start_hidden_service(gui=True)
except onionshare.NoTor as e: except onionshare.NoTor as e:
alert(e.args[0], QtGui.QMessageBox.Warning) alert(e.args[0], QtGui.QMessageBox.Warning)
return sys.exit()
else: except onionshare.TailsError as e:
onion_host = local_host alert(e.args[0], QtGui.QMessageBox.Warning)
sys.exit()
# select file to share # select file to share
filename, basename = select_file(onionshare.strings, filename) filename, basename = select_file(filename)
if not filename: if not filename:
return return
# clean up when app quits # clean up when app quits
def shutdown(): def shutdown():
onionshare.execute_cleanup_handlers() app.cleanup()
app.connect(app, QtCore.SIGNAL("aboutToQuit()"), shutdown) qtapp.connect(qtapp, QtCore.SIGNAL("aboutToQuit()"), shutdown)
# launch the gui # launch the gui
gui = OnionShareGui(filename, basename) gui = OnionShareGui(app, filename, basename)
# all done # all done
sys.exit(app.exec_()) sys.exit(qtapp.exec_())
if __name__ == '__main__': if __name__ == '__main__':
main() main()