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/install/scripts/onionshare-nautilus.py b/install/scripts/onionshare-nautilus.py
index db701c40..d5e83919 100644
--- a/install/scripts/onionshare-nautilus.py
+++ b/install/scripts/onionshare-nautilus.py
@@ -1,6 +1,7 @@
-#!/usr/bin/env python
-
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 8f4d257e..3f7bf722 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
@@ -70,9 +69,12 @@ 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
+ 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):
@@ -144,14 +146,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):
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
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
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()
diff --git a/onionshare/web.py b/onionshare/web.py
index aec86bf4..4ae10221 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 .
"""
-from distutils.version import StrictVersion as Version
-import queue, mimetypes, platform, os, sys, socket, logging, hmac
+
+import hmac
+import logging
+import mimetypes
+import os
+import queue
+import socket
+import sys
+import tempfile
+from distutils.version import LooseVersion as Version
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):
"""
@@ -331,11 +351,12 @@ 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()))
- for header,value in security_headers:
+ 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
+
# 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)
diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py
index bdf0d105..2a3b3468 100644
--- a/onionshare_gui/onionshare_gui.py
+++ b/onionshare_gui/onionshare_gui.py
@@ -434,6 +434,9 @@ class OnionShareGui(QtWidgets.QMainWindow):
if not web.get_stay_open():
self.server_status.stop_server()
self.server_status.shutdown_timeout_reset()
+ else:
+ if self.server_status.status == self.server_status.STATUS_STOPPED:
+ self.downloads.cancel_download(event["data"]["id"])
elif event["type"] == web.REQUEST_CANCELED:
download_in_progress = False
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:
diff --git a/share/locale/en.json b/share/locale/en.json
index 03a6a28e..ff288b6a 100644
--- a/share/locale/en.json
+++ b/share/locale/en.json
@@ -118,5 +118,6 @@
"gui_tor_connection_ask_quit": "Quit",
"gui_tor_connection_error_settings": "Try adjusting how OnionShare connects to the Tor network in Settings.",
"gui_server_started_after_timeout": "The server started after your chosen auto-timeout.\nPlease start a new share.",
- "gui_server_timeout_expired": "The chosen timeout has already expired.\nPlease update the timeout and then you may start sharing."
+ "gui_server_timeout_expired": "The chosen timeout has already expired.\nPlease update the timeout and then you may start sharing.",
+ "share_via_onionshare": "Share via OnionShare"
}
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