from flask import Flask, render_template, make_response from functools import wraps import threading, json, os, time, platform, sys onionshare = None onionshare_port = None filename = None onion_host = None qtapp = None clipboard = None stay_open = None url = None app = Flask(__name__, template_folder='./templates') def debug_mode(): import logging global app if platform.system() == 'Windows': temp_dir = os.environ['Temp'].replace('\\', '/') else: temp_dir = '/tmp/' log_handler = logging.FileHandler('{0}/onionshare_gui.log'.format(temp_dir)) log_handler.setLevel(logging.WARNING) app.logger.addHandler(log_handler) def add_response_headers(headers={}): def decorator(f): @wraps(f) def decorated_function(*args, **kwargs): resp = make_response(f(*args, **kwargs)) h = resp.headers for header, value in headers.items(): h[header] = value return resp return decorated_function return decorator def csp(f): @wraps(f) # disable inline js, external js @add_response_headers({'Content-Security-Policy': "default-src 'self'; connect-src 'self'"}) # ugh, webkit embedded in Qt4 is stupid old # TODO: remove webkit, build GUI with normal Qt widgets @add_response_headers({'X-WebKit-CSP': "default-src 'self'; connect-src 'self'"}) def decorated_function(*args, **kwargs): return f(*args, **kwargs) return decorated_function @app.route("/") @csp def index(): return render_template('index.html') @app.route("/init_info") @csp def init_info(): global onionshare, filename, stay_open basename = os.path.basename(filename) return json.dumps({ 'strings': onionshare.strings, 'basename': basename, 'stay_open': stay_open }) @app.route("/start_onionshare") @csp def start_onionshare(): global onionshare, onionshare_port, filename, onion_host, url url = 'http://{0}/{1}'.format(onion_host, onionshare.slug) 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({ 'filehash': filehash, 'filesize': filesize, 'url': url }) @app.route("/copy_url") @csp def copy_url(): if platform.system() == 'Windows': # Qt's QClipboard isn't working in Windows # https://github.com/micahflee/onionshare/issues/46 import ctypes GMEM_DDESHARE = 0x2000 ctypes.windll.user32.OpenClipboard(None) ctypes.windll.user32.EmptyClipboard() hcd = ctypes.windll.kernel32.GlobalAlloc(GMEM_DDESHARE, len(bytes(url))+1) pch_data = ctypes.windll.kernel32.GlobalLock(hcd) ctypes.cdll.msvcrt.strcpy(ctypes.c_char_p(pch_data), bytes(url)) ctypes.windll.kernel32.GlobalUnlock(hcd) ctypes.windll.user32.SetClipboardData(1, hcd) ctypes.windll.user32.CloseClipboard() else: global clipboard clipboard.setText(url) return '' @app.route("/stay_open_true") @csp def stay_open_true(): global onionshare onionshare.set_stay_open(True) @app.route("/stay_open_false") @csp def stay_open_false(): global onionshare onionshare.set_stay_open(False) @app.route("/heartbeat") @csp def check_for_requests(): global onionshare events = [] done = False while not done: try: r = onionshare.q.get(False) events.append(r) except onionshare.Queue.Empty: done = True return json.dumps(events) @app.route("/close") @csp def close(): global qtapp time.sleep(1) qtapp.closeAllWindows() return ''