From 52d16d21f7b46394049d42e0b1188bf69bf42d2f Mon Sep 17 00:00:00 2001 From: Delirious Lettuce Date: Mon, 10 Jul 2017 20:00:19 -0600 Subject: [PATCH 01/13] Remove: unused import, `round`, formatting indexes. Use tuple --- onionshare/common.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/onionshare/common.py b/onionshare/common.py index 89d4695f..4c36d211 100644 --- a/onionshare/common.py +++ b/onionshare/common.py @@ -20,7 +20,6 @@ along with this program. If not, see . import base64 import hashlib import inspect -import math import os import platform import random @@ -143,14 +142,14 @@ def human_readable_filesize(b): """ thresh = 1024.0 if b < thresh: - return '{0:.1f} B'.format(b) - units = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'] + return '{:.1f} B'.format(b) + units = ('KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB') u = 0 b /= thresh while b >= thresh: b /= thresh u += 1 - return '{0:.1f} {1:s}'.format(round(b, 1), units[u]) + return '{:.1f} {}'.format(b, units[u]) def format_seconds(seconds): From 301465a49ad62a296d9444b141f6366e65489618 Mon Sep 17 00:00:00 2001 From: Delirious Lettuce Date: Mon, 10 Jul 2017 20:11:04 -0600 Subject: [PATCH 02/13] Reorder imports, remove `p`, use `ext`, use `json.load(f)` --- onionshare/strings.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/onionshare/strings.py b/onionshare/strings.py index 9b26b4b1..7a1f08a5 100644 --- a/onionshare/strings.py +++ b/onionshare/strings.py @@ -17,17 +17,19 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import json, locale, os +import json +import locale +import os strings = {} + def load_strings(common, default="en"): """ Loads translated strings and fallback to English if the translation does not exist. """ global strings - p = common.get_platform() # find locale dir locale_dir = common.get_resource_path('locale') @@ -37,10 +39,9 @@ def load_strings(common, default="en"): for filename in os.listdir(locale_dir): abs_filename = os.path.join(locale_dir, filename) lang, ext = os.path.splitext(filename) - if abs_filename.endswith('.json'): + if ext == '.json': with open(abs_filename, encoding='utf-8') as f: - lang_json = f.read() - translations[lang] = json.loads(lang_json) + translations[lang] = json.load(f) strings = translations[default] lc, enc = locale.getdefaultlocale() From 56109a7012795673cf14c84cf8f79f9ec8e7f19a Mon Sep 17 00:00:00 2001 From: Delirious Lettuce Date: Mon, 10 Jul 2017 20:13:30 -0600 Subject: [PATCH 03/13] Reorder imports, use `json.load(f)` --- onionshare/settings.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/onionshare/settings.py b/onionshare/settings.py index 18e7dd26..408c8bdc 100644 --- a/onionshare/settings.py +++ b/onionshare/settings.py @@ -18,10 +18,13 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . """ -import platform, os, json +import json +import os +import platform from . import strings, common + class Settings(object): """ This class stores all of the settings for OnionShare, specifically for how @@ -95,7 +98,7 @@ class Settings(object): try: common.log('Settings', 'load', 'Trying to load {}'.format(self.filename)) with open(self.filename, 'r') as f: - self._settings = json.loads(f.read()) + self._settings = json.load(f) self.fill_in_defaults() except: pass From 18545b7aa217ed6d5dd0ed469695a44c0a6ec8e4 Mon Sep 17 00:00:00 2001 From: Delirious Lettuce Date: Mon, 10 Jul 2017 20:40:01 -0600 Subject: [PATCH 04/13] Reorder imports, spacing, use `tempfile.gettempdir` --- onionshare/web.py | 49 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/onionshare/web.py b/onionshare/web.py index aec86bf4..1d8237da 100644 --- a/onionshare/web.py +++ b/onionshare/web.py @@ -17,12 +17,22 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ + +import hmac +import logging +import mimetypes +import os +import queue +import socket +import sys +import tempfile from distutils.version import StrictVersion as Version -import queue, mimetypes, platform, os, sys, socket, logging, hmac from urllib.request import urlopen -from flask import Flask, Response, request, render_template_string, abort, make_response -from flask import __version__ as flask_version +from flask import ( + Flask, Response, request, render_template_string, abort, make_response, + __version__ as flask_version +) from . import strings, common @@ -56,6 +66,7 @@ security_headers = [ ('Server', 'OnionShare') ] + def set_file_info(filenames, processed_size_callback=None): """ Using the list of filenames being shared, fill in details that the web @@ -115,6 +126,8 @@ def add_request(request_type, path, data=None): slug = None + + def generate_slug(): global slug slug = common.build_slug() @@ -123,12 +136,16 @@ download_count = 0 error404_count = 0 stay_open = False + + def set_stay_open(new_stay_open): """ Set stay_open variable. """ global stay_open stay_open = new_stay_open + + def get_stay_open(): """ Get stay_open variable. @@ -138,6 +155,8 @@ def get_stay_open(): # Are we running in GUI mode? gui_mode = False + + def set_gui_mode(): """ Tell the web service that we're running in GUI mode @@ -145,21 +164,19 @@ def set_gui_mode(): global gui_mode gui_mode = True + def debug_mode(): """ Turn on debugging mode, which will log flask errors to a debug file. """ - if platform.system() == 'Windows': - temp_dir = os.environ['Temp'].replace('\\', '/') - else: - temp_dir = '/tmp/' - - log_handler = logging.FileHandler('{0:s}/onionshare_server.log'.format(temp_dir)) + temp_dir = tempfile.gettempdir() + log_handler = logging.FileHandler( + os.path.join(temp_dir, 'onionshare_server.log')) log_handler.setLevel(logging.WARNING) app.logger.addHandler(log_handler) -def check_slug_candidate(slug_candidate, slug_compare = None): - global slug + +def check_slug_candidate(slug_candidate, slug_compare=None): if not slug_compare: slug_compare = slug if not hmac.compare_digest(slug_compare, slug_candidate): @@ -170,6 +187,7 @@ def check_slug_candidate(slug_candidate, slug_compare = None): # one download at a time. download_in_progress = False + @app.route("/") def index(slug_candidate): """ @@ -185,7 +203,7 @@ def index(slug_candidate): deny_download = not stay_open and download_in_progress if deny_download: r = make_response(render_template_string(open(common.get_resource_path('html/denied.html')).read())) - for header,value in security_headers: + for header, value in security_headers: r.headers.set(header, value) return r @@ -198,15 +216,17 @@ def index(slug_candidate): filename=os.path.basename(zip_filename), filesize=zip_filesize, filesize_human=common.human_readable_filesize(zip_filesize))) - for header,value in security_headers: + for header, value in security_headers: r.headers.set(header, value) return r + # If the client closes the OnionShare window while a download is in progress, # it should immediately stop serving the file. The client_cancel global is # used to tell the download function that the client is canceling the download. client_cancel = False + @app.route("//download") def download(slug_candidate): """ @@ -332,10 +352,11 @@ def page_not_found(e): print(strings._('error_rate_limit')) r = make_response(render_template_string(open(common.get_resource_path('html/404.html')).read())) - for header,value in security_headers: + for header, value in security_headers: r.headers.set(header, value) return r + # shutting down the server only works within the context of flask, so the easiest way to do it is over http shutdown_slug = common.random_string(16) From 0fb556baf2a685b17b9880a6454e8f4a99529e98 Mon Sep 17 00:00:00 2001 From: Delirious Lettuce Date: Thu, 13 Jul 2017 18:14:49 -0600 Subject: [PATCH 05/13] Delete temporary tor data directory during cleanup --- onionshare/onion.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/onionshare/onion.py b/onionshare/onion.py index efcd7ae2..aedd98c2 100644 --- a/onionshare/onion.py +++ b/onionshare/onion.py @@ -433,6 +433,13 @@ class Onion(object): self.stealth = False self.service_id = None + try: + # Delete the temporary tor data directory + self.tor_data_directory.cleanup() + except AttributeError: + # Skip if cleanup was somehow run before connect + pass + def get_tor_socks_port(self): """ Returns a (address, port) tuple for the Tor SOCKS port From 5edd4dd22c6c635beeff63156760cf2fab5b531b Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Fri, 3 Nov 2017 12:58:45 +1100 Subject: [PATCH 06/13] #468 If the server is stopped before a download is completed, emit the Canceled message in the progress bar --- onionshare_gui/onionshare_gui.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 8e84acac..022ca0b0 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -376,9 +376,6 @@ class OnionShareGui(QtWidgets.QMainWindow): if self.new_download: self.vbar.setValue(self.vbar.maximum()) self.new_download = False - # only check for requests if the server is running - if self.server_status.status != self.server_status.STATUS_STARTED: - return events = [] @@ -415,6 +412,9 @@ class OnionShareGui(QtWidgets.QMainWindow): # close on finish? if not web.get_stay_open(): self.server_status.stop_server() + else: + if self.server_status.status == self.server_status.STATUS_STOPPED: + self.downloads.cancel_download(event["data"]["id"]) elif event["type"] == web.REQUEST_CANCELED: self.downloads.cancel_download(event["data"]["id"]) From 53faa043f91e5153ab13a099d0a8e4b6c411bd2e Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Mon, 6 Nov 2017 09:16:55 +1100 Subject: [PATCH 07/13] use LooseVersion instead of StrictVersion, so that upstream versions of Flask that contain -dev will still work. Fixes #442 --- onionshare/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onionshare/web.py b/onionshare/web.py index aec86bf4..cc00c680 100644 --- a/onionshare/web.py +++ b/onionshare/web.py @@ -17,7 +17,7 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ -from distutils.version import StrictVersion as Version +from distutils.version import LooseVersion as Version import queue, mimetypes, platform, os, sys, socket, logging, hmac from urllib.request import urlopen From bb46c2de99535b183b03fd3e499c258f13f469a8 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Thu, 9 Nov 2017 17:51:34 +1100 Subject: [PATCH 08/13] send a HTTP 404 code instead of 200, on 404 pages --- onionshare/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onionshare/web.py b/onionshare/web.py index aec86bf4..59febcfb 100644 --- a/onionshare/web.py +++ b/onionshare/web.py @@ -331,7 +331,7 @@ def page_not_found(e): force_shutdown() print(strings._('error_rate_limit')) - r = make_response(render_template_string(open(common.get_resource_path('html/404.html')).read())) + r = make_response(render_template_string(open(common.get_resource_path('html/404.html')).read()), 404) for header,value in security_headers: r.headers.set(header, value) return r From 966beb940653c0e5786628f48e42d0c19809f4f8 Mon Sep 17 00:00:00 2001 From: Miguel Jacq Date: Sun, 12 Nov 2017 17:53:23 +1100 Subject: [PATCH 09/13] add missing arg to onion.connect() (config) which broke the Tor Connection progress dialog (regression from b19f8f) --- onionshare_gui/tor_connection_dialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/onionshare_gui/tor_connection_dialog.py b/onionshare_gui/tor_connection_dialog.py index 93c03bef..fa4c7860 100644 --- a/onionshare_gui/tor_connection_dialog.py +++ b/onionshare_gui/tor_connection_dialog.py @@ -125,7 +125,7 @@ class TorConnectionThread(QtCore.QThread): # Connect to the Onion try: - self.onion.connect(self.settings, self._tor_status_update) + self.onion.connect(self.settings, False, self._tor_status_update) if self.onion.connected_to_tor: self.connected_to_tor.emit() else: From 21e1dac2c3c5acb4131cb6b4824f886f7350cab7 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Nov 2017 15:52:33 -0800 Subject: [PATCH 10/13] Rename test files to have test_ prefix --- test/{onionshare_test.py => test_onionshare.py} | 0 test/{onionshare_common_test.py => test_onionshare_common.py} | 0 test/{onionshare_settings_test.py => test_onionshare_settings.py} | 0 test/{onionshare_strings_test.py => test_onionshare_strings.py} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename test/{onionshare_test.py => test_onionshare.py} (100%) rename test/{onionshare_common_test.py => test_onionshare_common.py} (100%) rename test/{onionshare_settings_test.py => test_onionshare_settings.py} (100%) rename test/{onionshare_strings_test.py => test_onionshare_strings.py} (100%) diff --git a/test/onionshare_test.py b/test/test_onionshare.py similarity index 100% rename from test/onionshare_test.py rename to test/test_onionshare.py diff --git a/test/onionshare_common_test.py b/test/test_onionshare_common.py similarity index 100% rename from test/onionshare_common_test.py rename to test/test_onionshare_common.py diff --git a/test/onionshare_settings_test.py b/test/test_onionshare_settings.py similarity index 100% rename from test/onionshare_settings_test.py rename to test/test_onionshare_settings.py diff --git a/test/onionshare_strings_test.py b/test/test_onionshare_strings.py similarity index 100% rename from test/onionshare_strings_test.py rename to test/test_onionshare_strings.py From d544a0341f9390baf3e5510ea5bef5fe1fafaa2c Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Mon, 20 Nov 2017 16:19:04 -0800 Subject: [PATCH 11/13] Allow tests to find share folders when building .deb --- .gitignore | 1 + MANIFEST.in | 9 +++++---- onionshare/common.py | 3 +++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 20342555..712484d6 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ pip-log.txt .coverage .tox nosetests.xml +.cache # Translations *.mo diff --git a/MANIFEST.in b/MANIFEST.in index 64eca8f9..f4d1c078 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,11 +1,12 @@ include LICENSE include README.md include BUILD.md -include resources/* -include resources/images/* -include resources/locale/* -include resources/html/* +include share/* +include share/images/* +include share/locale/* +include share/html/* include install/onionshare.desktop include install/onionshare.appdata.xml include install/onionshare80.xpm include install/scripts/onionshare-nautilus.py +include test/*.py diff --git a/onionshare/common.py b/onionshare/common.py index 89d4695f..176c7931 100644 --- a/onionshare/common.py +++ b/onionshare/common.py @@ -69,6 +69,9 @@ def get_resource_path(filename): if getattr(sys, 'onionshare_dev_mode', False): # Look for resources directory relative to python file prefix = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))), 'share') + if not os.path.exists(prefix): + # While running tests during stdeb bdist_deb, look 3 directories up for the share folder + prefix = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(prefix)))), 'share') elif p == 'Linux' and sys.argv and sys.argv[0].startswith(sys.prefix): # OnionShare is installed systemwide in Linux From 31126b410d5368fdbcaef38d2d624846cf627dcf Mon Sep 17 00:00:00 2001 From: Kushal Das Date: Tue, 28 Nov 2017 03:55:20 +0530 Subject: [PATCH 12/13] Mentions python2 as the interpreter --- install/scripts/onionshare-nautilus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/scripts/onionshare-nautilus.py b/install/scripts/onionshare-nautilus.py index db701c40..ce275079 100644 --- a/install/scripts/onionshare-nautilus.py +++ b/install/scripts/onionshare-nautilus.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/python2 import os import subprocess From 9910ea5c72602927f194b9ba58c41d99614dfcba Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Fri, 1 Dec 2017 12:57:18 -0800 Subject: [PATCH 13/13] Allow for localization for "Share via OnionShare" string in the nautilus extension --- install/scripts/onionshare-nautilus.py | 57 ++++++++++++++++++++++++-- onionshare/common.py | 4 +- share/locale/en.json | 3 +- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/install/scripts/onionshare-nautilus.py b/install/scripts/onionshare-nautilus.py index ce275079..d5e83919 100644 --- a/install/scripts/onionshare-nautilus.py +++ b/install/scripts/onionshare-nautilus.py @@ -1,6 +1,7 @@ -#!/usr/bin/python2 - import os +import sys +import json +import locale import subprocess import urllib import gi @@ -12,7 +13,55 @@ from gi.repository import GObject # Put me in /usr/share/nautilus-python/extensions/ class OnionShareExtension(GObject.GObject, Nautilus.MenuProvider): def __init__(self): - pass + # Get the localized string for "Share via OnionShare" label + self.label = None + default_label = 'Share via OnionShare' + + try: + # Re-implement localization in python2 + default_locale = 'en' + locale_dir = os.path.join(sys.prefix, 'share/onionshare/locale') + if os.path.exists(locale_dir): + # Load all translations + strings = {} + translations = {} + for filename in os.listdir(locale_dir): + abs_filename = os.path.join(locale_dir, filename) + lang, ext = os.path.splitext(filename) + if ext == '.json': + with open(abs_filename) as f: + translations[lang] = json.load(f) + + strings = translations[default_locale] + lc, enc = locale.getdefaultlocale() + if lc: + lang = lc[:2] + if lang in translations: + # if a string doesn't exist, fallback to English + for key in translations[default_locale]: + if key in translations[lang]: + strings[key] = translations[lang][key] + + self.label = strings['share_via_onionshare'] + + except: + self.label = default_label + + if not self.label: + self.label = default_label + + """ + # This more elegant solution will only work if nautilus is using python3, and onionshare is installed system-wide. + # But nautilus is using python2, so this is commented out. + try: + import onionshare + onionshare.strings.load_strings(onionshare.common) + self.label = onionshare.strings._('share_via_onionshare') + except: + import sys + print('python version: {}').format(sys.version) + self.label = 'Share via OnionShare' + """ def url2path(self,url): file_uri = url.get_activation_uri() @@ -31,7 +80,7 @@ class OnionShareExtension(GObject.GObject, Nautilus.MenuProvider): def get_file_items(self, window, files): menuitem = Nautilus.MenuItem(name='OnionShare::Nautilus', - label='Share via OnionShare', + label=self.label, tip='', icon='') menu = Nautilus.Menu() diff --git a/onionshare/common.py b/onionshare/common.py index a2cc4b7d..7e102124 100644 --- a/onionshare/common.py +++ b/onionshare/common.py @@ -72,8 +72,8 @@ def get_resource_path(filename): # While running tests during stdeb bdist_deb, look 3 directories up for the share folder prefix = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(prefix)))), 'share') - elif p == 'Linux' and sys.argv and sys.argv[0].startswith(sys.prefix): - # OnionShare is installed systemwide in Linux + elif p == 'Linux': + # Assume OnionShare is installed systemwide in Linux, since we're not running in dev mode prefix = os.path.join(sys.prefix, 'share/onionshare') elif getattr(sys, 'frozen', False): diff --git a/share/locale/en.json b/share/locale/en.json index cfd80455..fedd1af4 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -111,5 +111,6 @@ "gui_tor_connection_ask": "Would you like to open OnionShare settings to troubleshoot connecting to Tor?", "gui_tor_connection_ask_open_settings": "Open Settings", "gui_tor_connection_ask_quit": "Quit", - "gui_tor_connection_error_settings": "Try adjusting how OnionShare connects to the Tor network in Settings." + "gui_tor_connection_error_settings": "Try adjusting how OnionShare connects to the Tor network in Settings.", + "share_via_onionshare": "Share via OnionShare" }