mirror of
https://github.com/onionshare/onionshare.git
synced 2025-02-17 21:04:07 -05:00
commit
35c373b5a3
@ -9,7 +9,7 @@ VERSION=`cat share/version.txt`
|
|||||||
rm -r build dist >/dev/null 2>&1
|
rm -r build dist >/dev/null 2>&1
|
||||||
|
|
||||||
# build binary package
|
# build binary package
|
||||||
python3 setup.py bdist_rpm --requires="python3-flask, python3-stem, python3-qt5, python3-crypto, python3-pysocks, nautilus-python, tor, obfs4"
|
python3 setup.py bdist_rpm --requires="python3-flask, python3-flask-httpauth, python3-stem, python3-qt5, python3-crypto, python3-pysocks, nautilus-python, tor, obfs4"
|
||||||
|
|
||||||
# install it
|
# install it
|
||||||
echo ""
|
echo ""
|
||||||
|
@ -59,6 +59,7 @@ def main():
|
|||||||
files_in(dir, 'onionshare_gui/mode') + \
|
files_in(dir, 'onionshare_gui/mode') + \
|
||||||
files_in(dir, 'onionshare_gui/mode/share_mode') + \
|
files_in(dir, 'onionshare_gui/mode/share_mode') + \
|
||||||
files_in(dir, 'onionshare_gui/mode/receive_mode') + \
|
files_in(dir, 'onionshare_gui/mode/receive_mode') + \
|
||||||
|
files_in(dir, 'onionshare_gui/mode/website_mode') + \
|
||||||
files_in(dir, 'install/scripts') + \
|
files_in(dir, 'install/scripts') + \
|
||||||
files_in(dir, 'tests')
|
files_in(dir, 'tests')
|
||||||
pysrc = [p for p in src if p.endswith('.py')]
|
pysrc = [p for p in src if p.endswith('.py')]
|
||||||
|
@ -3,6 +3,7 @@ certifi==2019.3.9
|
|||||||
chardet==3.0.4
|
chardet==3.0.4
|
||||||
Click==7.0
|
Click==7.0
|
||||||
Flask==1.0.2
|
Flask==1.0.2
|
||||||
|
Flask-HTTPAuth==3.2.4
|
||||||
future==0.17.1
|
future==0.17.1
|
||||||
idna==2.8
|
idna==2.8
|
||||||
itsdangerous==1.1.0
|
itsdangerous==1.1.0
|
||||||
|
@ -51,6 +51,7 @@ def main(cwd=None):
|
|||||||
parser.add_argument('--connect-timeout', metavar='<int>', dest='connect_timeout', default=120, help="Give up connecting to Tor after a given amount of seconds (default: 120)")
|
parser.add_argument('--connect-timeout', metavar='<int>', dest='connect_timeout', default=120, help="Give up connecting to Tor after a given amount of seconds (default: 120)")
|
||||||
parser.add_argument('--stealth', action='store_true', dest='stealth', help="Use client authorization (advanced)")
|
parser.add_argument('--stealth', action='store_true', dest='stealth', help="Use client authorization (advanced)")
|
||||||
parser.add_argument('--receive', action='store_true', dest='receive', help="Receive shares instead of sending them")
|
parser.add_argument('--receive', action='store_true', dest='receive', help="Receive shares instead of sending them")
|
||||||
|
parser.add_argument('--website', action='store_true', dest='website', help="Publish a static website")
|
||||||
parser.add_argument('--config', metavar='config', default=False, help="Custom JSON config file location (optional)")
|
parser.add_argument('--config', metavar='config', default=False, help="Custom JSON config file location (optional)")
|
||||||
parser.add_argument('-v', '--verbose', action='store_true', dest='verbose', help="Log OnionShare errors to stdout, and web errors to disk")
|
parser.add_argument('-v', '--verbose', action='store_true', dest='verbose', help="Log OnionShare errors to stdout, and web errors to disk")
|
||||||
parser.add_argument('filename', metavar='filename', nargs='*', help="List of files or folders to share")
|
parser.add_argument('filename', metavar='filename', nargs='*', help="List of files or folders to share")
|
||||||
@ -68,10 +69,13 @@ def main(cwd=None):
|
|||||||
connect_timeout = int(args.connect_timeout)
|
connect_timeout = int(args.connect_timeout)
|
||||||
stealth = bool(args.stealth)
|
stealth = bool(args.stealth)
|
||||||
receive = bool(args.receive)
|
receive = bool(args.receive)
|
||||||
|
website = bool(args.website)
|
||||||
config = args.config
|
config = args.config
|
||||||
|
|
||||||
if receive:
|
if receive:
|
||||||
mode = 'receive'
|
mode = 'receive'
|
||||||
|
elif website:
|
||||||
|
mode = 'website'
|
||||||
else:
|
else:
|
||||||
mode = 'share'
|
mode = 'share'
|
||||||
|
|
||||||
@ -168,6 +172,15 @@ def main(cwd=None):
|
|||||||
print(e.args[0])
|
print(e.args[0])
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
if mode == 'website':
|
||||||
|
# Prepare files to share
|
||||||
|
print("Preparing files to publish website...")
|
||||||
|
try:
|
||||||
|
web.website_mode.set_file_info(filenames)
|
||||||
|
except OSError as e:
|
||||||
|
print(e.strerror)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if mode == 'share':
|
if mode == 'share':
|
||||||
# Prepare files to share
|
# Prepare files to share
|
||||||
print("Compressing files.")
|
print("Compressing files.")
|
||||||
@ -206,6 +219,8 @@ def main(cwd=None):
|
|||||||
# Build the URL
|
# Build the URL
|
||||||
if common.settings.get('public_mode'):
|
if common.settings.get('public_mode'):
|
||||||
url = 'http://{0:s}'.format(app.onion_host)
|
url = 'http://{0:s}'.format(app.onion_host)
|
||||||
|
elif mode == 'website':
|
||||||
|
url = 'http://onionshare:{0:s}@{1:s}'.format(web.slug, app.onion_host)
|
||||||
else:
|
else:
|
||||||
url = 'http://{0:s}/{1:s}'.format(app.onion_host, web.slug)
|
url = 'http://{0:s}/{1:s}'.format(app.onion_host, web.slug)
|
||||||
|
|
||||||
@ -242,7 +257,7 @@ def main(cwd=None):
|
|||||||
if app.autostop_timer > 0:
|
if app.autostop_timer > 0:
|
||||||
# if the auto-stop timer was set and has run out, stop the server
|
# if the auto-stop timer was set and has run out, stop the server
|
||||||
if not app.autostop_timer_thread.is_alive():
|
if not app.autostop_timer_thread.is_alive():
|
||||||
if mode == 'share':
|
if mode == 'share' or (mode == 'website'):
|
||||||
# If there were no attempts to download the share, or all downloads are done, we can stop
|
# If there were no attempts to download the share, or all downloads are done, we can stop
|
||||||
if web.share_mode.download_count == 0 or web.done:
|
if web.share_mode.download_count == 0 or web.done:
|
||||||
print("Stopped because auto-stop timer ran out")
|
print("Stopped because auto-stop timer ran out")
|
||||||
|
@ -18,6 +18,9 @@ class ReceiveModeWeb(object):
|
|||||||
|
|
||||||
self.web = web
|
self.web = web
|
||||||
|
|
||||||
|
# Reset assets path
|
||||||
|
self.web.app.static_folder=self.common.get_resource_path('static')
|
||||||
|
|
||||||
self.can_upload = True
|
self.can_upload = True
|
||||||
self.upload_count = 0
|
self.upload_count = 0
|
||||||
self.uploads_in_progress = []
|
self.uploads_in_progress = []
|
||||||
|
@ -34,6 +34,10 @@ class ShareModeWeb(object):
|
|||||||
# one download at a time.
|
# one download at a time.
|
||||||
self.download_in_progress = False
|
self.download_in_progress = False
|
||||||
|
|
||||||
|
# Reset assets path
|
||||||
|
self.web.app.static_folder=self.common.get_resource_path('static')
|
||||||
|
|
||||||
|
|
||||||
self.define_routes()
|
self.define_routes()
|
||||||
|
|
||||||
def define_routes(self):
|
def define_routes(self):
|
||||||
|
@ -15,7 +15,7 @@ from .. import strings
|
|||||||
|
|
||||||
from .share_mode import ShareModeWeb
|
from .share_mode import ShareModeWeb
|
||||||
from .receive_mode import ReceiveModeWeb, ReceiveModeWSGIMiddleware, ReceiveModeRequest
|
from .receive_mode import ReceiveModeWeb, ReceiveModeWSGIMiddleware, ReceiveModeRequest
|
||||||
|
from .website_mode import WebsiteModeWeb
|
||||||
|
|
||||||
# Stub out flask's show_server_banner function, to avoiding showing warnings that
|
# Stub out flask's show_server_banner function, to avoiding showing warnings that
|
||||||
# are not applicable to OnionShare
|
# are not applicable to OnionShare
|
||||||
@ -111,13 +111,15 @@ class Web(object):
|
|||||||
self.receive_mode = None
|
self.receive_mode = None
|
||||||
if self.mode == 'receive':
|
if self.mode == 'receive':
|
||||||
self.receive_mode = ReceiveModeWeb(self.common, self)
|
self.receive_mode = ReceiveModeWeb(self.common, self)
|
||||||
|
elif self.mode == 'website':
|
||||||
|
self.website_mode = WebsiteModeWeb(self.common, self)
|
||||||
elif self.mode == 'share':
|
elif self.mode == 'share':
|
||||||
self.share_mode = ShareModeWeb(self.common, self)
|
self.share_mode = ShareModeWeb(self.common, self)
|
||||||
|
|
||||||
|
|
||||||
def define_common_routes(self):
|
def define_common_routes(self):
|
||||||
"""
|
"""
|
||||||
Common web app routes between sending and receiving
|
Common web app routes between sending, receiving and website modes.
|
||||||
"""
|
"""
|
||||||
@self.app.errorhandler(404)
|
@self.app.errorhandler(404)
|
||||||
def page_not_found(e):
|
def page_not_found(e):
|
||||||
|
202
onionshare/web/website_mode.py
Normal file
202
onionshare/web/website_mode.py
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import mimetypes
|
||||||
|
from flask import Response, request, render_template, make_response, send_from_directory
|
||||||
|
from flask_httpauth import HTTPBasicAuth
|
||||||
|
|
||||||
|
from .. import strings
|
||||||
|
|
||||||
|
|
||||||
|
class WebsiteModeWeb(object):
|
||||||
|
"""
|
||||||
|
All of the web logic for share mode
|
||||||
|
"""
|
||||||
|
def __init__(self, common, web):
|
||||||
|
self.common = common
|
||||||
|
self.common.log('WebsiteModeWeb', '__init__')
|
||||||
|
|
||||||
|
self.web = web
|
||||||
|
self.auth = HTTPBasicAuth()
|
||||||
|
|
||||||
|
# Dictionary mapping file paths to filenames on disk
|
||||||
|
self.files = {}
|
||||||
|
self.visit_count = 0
|
||||||
|
|
||||||
|
# Reset assets path
|
||||||
|
self.web.app.static_folder=self.common.get_resource_path('static')
|
||||||
|
|
||||||
|
self.users = { }
|
||||||
|
|
||||||
|
self.define_routes()
|
||||||
|
|
||||||
|
def define_routes(self):
|
||||||
|
"""
|
||||||
|
The web app routes for sharing a website
|
||||||
|
"""
|
||||||
|
|
||||||
|
@self.auth.get_password
|
||||||
|
def get_pw(username):
|
||||||
|
self.users['onionshare'] = self.web.slug
|
||||||
|
|
||||||
|
if username in self.users:
|
||||||
|
return self.users.get(username)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@self.web.app.before_request
|
||||||
|
def conditional_auth_check():
|
||||||
|
if not self.common.settings.get('public_mode'):
|
||||||
|
@self.auth.login_required
|
||||||
|
def _check_login():
|
||||||
|
return None
|
||||||
|
|
||||||
|
return _check_login()
|
||||||
|
|
||||||
|
@self.web.app.route('/', defaults={'path': ''})
|
||||||
|
@self.web.app.route('/<path:path>')
|
||||||
|
def path_public(path):
|
||||||
|
return path_logic(path)
|
||||||
|
|
||||||
|
def path_logic(path=''):
|
||||||
|
"""
|
||||||
|
Render the onionshare website.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Each download has a unique id
|
||||||
|
visit_id = self.visit_count
|
||||||
|
self.visit_count += 1
|
||||||
|
|
||||||
|
# Tell GUI the page has been visited
|
||||||
|
self.web.add_request(self.web.REQUEST_STARTED, path, {
|
||||||
|
'id': visit_id,
|
||||||
|
'action': 'visit'
|
||||||
|
})
|
||||||
|
|
||||||
|
if path in self.files:
|
||||||
|
filesystem_path = self.files[path]
|
||||||
|
|
||||||
|
# If it's a directory
|
||||||
|
if os.path.isdir(filesystem_path):
|
||||||
|
# Is there an index.html?
|
||||||
|
index_path = os.path.join(path, 'index.html')
|
||||||
|
if index_path in self.files:
|
||||||
|
# Render it
|
||||||
|
dirname = os.path.dirname(self.files[index_path])
|
||||||
|
basename = os.path.basename(self.files[index_path])
|
||||||
|
return send_from_directory(dirname, basename)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Otherwise, render directory listing
|
||||||
|
filenames = []
|
||||||
|
for filename in os.listdir(filesystem_path):
|
||||||
|
if os.path.isdir(os.path.join(filesystem_path, filename)):
|
||||||
|
filenames.append(filename + '/')
|
||||||
|
else:
|
||||||
|
filenames.append(filename)
|
||||||
|
filenames.sort()
|
||||||
|
return self.directory_listing(path, filenames, filesystem_path)
|
||||||
|
|
||||||
|
# If it's a file
|
||||||
|
elif os.path.isfile(filesystem_path):
|
||||||
|
dirname = os.path.dirname(filesystem_path)
|
||||||
|
basename = os.path.basename(filesystem_path)
|
||||||
|
return send_from_directory(dirname, basename)
|
||||||
|
|
||||||
|
# If it's not a directory or file, throw a 404
|
||||||
|
else:
|
||||||
|
return self.web.error404()
|
||||||
|
else:
|
||||||
|
# Special case loading /
|
||||||
|
if path == '':
|
||||||
|
index_path = 'index.html'
|
||||||
|
if index_path in self.files:
|
||||||
|
# Render it
|
||||||
|
dirname = os.path.dirname(self.files[index_path])
|
||||||
|
basename = os.path.basename(self.files[index_path])
|
||||||
|
return send_from_directory(dirname, basename)
|
||||||
|
else:
|
||||||
|
# Root directory listing
|
||||||
|
filenames = list(self.root_files)
|
||||||
|
filenames.sort()
|
||||||
|
return self.directory_listing(path, filenames)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# If the path isn't found, throw a 404
|
||||||
|
return self.web.error404()
|
||||||
|
|
||||||
|
def directory_listing(self, path, filenames, filesystem_path=None):
|
||||||
|
# If filesystem_path is None, this is the root directory listing
|
||||||
|
files = []
|
||||||
|
dirs = []
|
||||||
|
|
||||||
|
for filename in filenames:
|
||||||
|
if filesystem_path:
|
||||||
|
this_filesystem_path = os.path.join(filesystem_path, filename)
|
||||||
|
else:
|
||||||
|
this_filesystem_path = self.files[filename]
|
||||||
|
|
||||||
|
is_dir = os.path.isdir(this_filesystem_path)
|
||||||
|
|
||||||
|
if is_dir:
|
||||||
|
dirs.append({
|
||||||
|
'basename': filename
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
size = os.path.getsize(this_filesystem_path)
|
||||||
|
size_human = self.common.human_readable_filesize(size)
|
||||||
|
files.append({
|
||||||
|
'basename': filename,
|
||||||
|
'size_human': size_human
|
||||||
|
})
|
||||||
|
|
||||||
|
r = make_response(render_template('listing.html',
|
||||||
|
path=path,
|
||||||
|
files=files,
|
||||||
|
dirs=dirs))
|
||||||
|
return self.web.add_security_headers(r)
|
||||||
|
|
||||||
|
def set_file_info(self, filenames):
|
||||||
|
"""
|
||||||
|
Build a data structure that describes the list of files that make up
|
||||||
|
the static website.
|
||||||
|
"""
|
||||||
|
self.common.log("WebsiteModeWeb", "set_file_info")
|
||||||
|
|
||||||
|
# This is a dictionary that maps HTTP routes to filenames on disk
|
||||||
|
self.files = {}
|
||||||
|
|
||||||
|
# This is only the root files and dirs, as opposed to all of them
|
||||||
|
self.root_files = {}
|
||||||
|
|
||||||
|
# If there's just one folder, replace filenames with a list of files inside that folder
|
||||||
|
if len(filenames) == 1 and os.path.isdir(filenames[0]):
|
||||||
|
filenames = [os.path.join(filenames[0], x) for x in os.listdir(filenames[0])]
|
||||||
|
|
||||||
|
# Loop through the files
|
||||||
|
for filename in filenames:
|
||||||
|
basename = os.path.basename(filename.rstrip('/'))
|
||||||
|
|
||||||
|
# If it's a filename, add it
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
self.files[basename] = filename
|
||||||
|
self.root_files[basename] = filename
|
||||||
|
|
||||||
|
# If it's a directory, add it recursively
|
||||||
|
elif os.path.isdir(filename):
|
||||||
|
self.root_files[basename + '/'] = filename
|
||||||
|
|
||||||
|
for root, _, nested_filenames in os.walk(filename):
|
||||||
|
# Normalize the root path. So if the directory name is "/home/user/Documents/some_folder",
|
||||||
|
# and it has a nested folder foobar, the root is "/home/user/Documents/some_folder/foobar".
|
||||||
|
# The normalized_root should be "some_folder/foobar"
|
||||||
|
normalized_root = os.path.join(basename, root[len(filename):].lstrip('/')).rstrip('/')
|
||||||
|
|
||||||
|
# Add the dir itself
|
||||||
|
self.files[normalized_root + '/'] = root
|
||||||
|
|
||||||
|
# Add the files in this dir
|
||||||
|
for nested_filename in nested_filenames:
|
||||||
|
self.files[os.path.join(normalized_root, nested_filename)] = os.path.join(root, nested_filename)
|
||||||
|
|
||||||
|
return True
|
@ -22,7 +22,7 @@ from PyQt5 import QtCore, QtWidgets, QtGui
|
|||||||
|
|
||||||
from onionshare import strings
|
from onionshare import strings
|
||||||
|
|
||||||
from ...widgets import Alert, AddFileDialog
|
from ..widgets import Alert, AddFileDialog
|
||||||
|
|
||||||
class DropHereLabel(QtWidgets.QLabel):
|
class DropHereLabel(QtWidgets.QLabel):
|
||||||
"""
|
"""
|
@ -341,6 +341,35 @@ class ReceiveHistoryItem(HistoryItem):
|
|||||||
self.label.setText(self.get_canceled_label_text(self.started))
|
self.label.setText(self.get_canceled_label_text(self.started))
|
||||||
|
|
||||||
|
|
||||||
|
class VisitHistoryItem(HistoryItem):
|
||||||
|
"""
|
||||||
|
Download history item, for share mode
|
||||||
|
"""
|
||||||
|
def __init__(self, common, id, total_bytes):
|
||||||
|
super(VisitHistoryItem, self).__init__()
|
||||||
|
self.status = HistoryItem.STATUS_STARTED
|
||||||
|
self.common = common
|
||||||
|
|
||||||
|
self.id = id
|
||||||
|
self.visited = time.time()
|
||||||
|
self.visited_dt = datetime.fromtimestamp(self.visited)
|
||||||
|
|
||||||
|
# Label
|
||||||
|
self.label = QtWidgets.QLabel(strings._('gui_visit_started').format(self.visited_dt.strftime("%b %d, %I:%M%p")))
|
||||||
|
|
||||||
|
# Layout
|
||||||
|
layout = QtWidgets.QVBoxLayout()
|
||||||
|
layout.addWidget(self.label)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.label.setText(self.get_finished_label_text(self.started_dt))
|
||||||
|
self.status = HistoryItem.STATUS_FINISHED
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
self.progress_bar.setFormat(strings._('gui_canceled'))
|
||||||
|
self.status = HistoryItem.STATUS_CANCELED
|
||||||
|
|
||||||
class HistoryItemList(QtWidgets.QScrollArea):
|
class HistoryItemList(QtWidgets.QScrollArea):
|
||||||
"""
|
"""
|
||||||
List of items
|
List of items
|
||||||
@ -404,7 +433,6 @@ class HistoryItemList(QtWidgets.QScrollArea):
|
|||||||
Reset all items, emptying the list. Override this method.
|
Reset all items, emptying the list. Override this method.
|
||||||
"""
|
"""
|
||||||
for key, item in self.items.copy().items():
|
for key, item in self.items.copy().items():
|
||||||
if item.status != HistoryItem.STATUS_STARTED:
|
|
||||||
self.items_layout.removeWidget(item)
|
self.items_layout.removeWidget(item)
|
||||||
item.close()
|
item.close()
|
||||||
del self.items[key]
|
del self.items[key]
|
||||||
@ -414,9 +442,10 @@ class History(QtWidgets.QWidget):
|
|||||||
A history of what's happened so far in this mode. This contains an internal
|
A history of what's happened so far in this mode. This contains an internal
|
||||||
object full of a scrollable list of items.
|
object full of a scrollable list of items.
|
||||||
"""
|
"""
|
||||||
def __init__(self, common, empty_image, empty_text, header_text):
|
def __init__(self, common, empty_image, empty_text, header_text, mode=''):
|
||||||
super(History, self).__init__()
|
super(History, self).__init__()
|
||||||
self.common = common
|
self.common = common
|
||||||
|
self.mode = mode
|
||||||
|
|
||||||
self.setMinimumWidth(350)
|
self.setMinimumWidth(350)
|
||||||
|
|
||||||
@ -535,10 +564,12 @@ class History(QtWidgets.QWidget):
|
|||||||
"""
|
"""
|
||||||
Update the 'in progress' widget.
|
Update the 'in progress' widget.
|
||||||
"""
|
"""
|
||||||
|
if self.mode != 'website':
|
||||||
if self.in_progress_count == 0:
|
if self.in_progress_count == 0:
|
||||||
image = self.common.get_resource_path('images/share_in_progress_none.png')
|
image = self.common.get_resource_path('images/share_in_progress_none.png')
|
||||||
else:
|
else:
|
||||||
image = self.common.get_resource_path('images/share_in_progress.png')
|
image = self.common.get_resource_path('images/share_in_progress.png')
|
||||||
|
|
||||||
self.in_progress_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.in_progress_count))
|
self.in_progress_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.in_progress_count))
|
||||||
self.in_progress_label.setToolTip(strings._('history_in_progress_tooltip').format(self.in_progress_count))
|
self.in_progress_label.setToolTip(strings._('history_in_progress_tooltip').format(self.in_progress_count))
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ from onionshare.onion import *
|
|||||||
from onionshare.common import Common
|
from onionshare.common import Common
|
||||||
from onionshare.web import Web
|
from onionshare.web import Web
|
||||||
|
|
||||||
from .file_selection import FileSelection
|
from ..file_selection import FileSelection
|
||||||
from .threads import CompressThread
|
from .threads import CompressThread
|
||||||
from .. import Mode
|
from .. import Mode
|
||||||
from ..history import History, ToggleHistory, ShareHistoryItem
|
from ..history import History, ToggleHistory, ShareHistoryItem
|
||||||
|
274
onionshare_gui/mode/website_mode/__init__.py
Normal file
274
onionshare_gui/mode/website_mode/__init__.py
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
OnionShare | https://onionshare.org/
|
||||||
|
|
||||||
|
Copyright (C) 2014-2018 Micah Lee <micah@micahflee.com>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
|
from PyQt5 import QtCore, QtWidgets, QtGui
|
||||||
|
|
||||||
|
from onionshare import strings
|
||||||
|
from onionshare.onion import *
|
||||||
|
from onionshare.common import Common
|
||||||
|
from onionshare.web import Web
|
||||||
|
|
||||||
|
from ..file_selection import FileSelection
|
||||||
|
from .. import Mode
|
||||||
|
from ..history import History, ToggleHistory, VisitHistoryItem
|
||||||
|
from ...widgets import Alert
|
||||||
|
|
||||||
|
class WebsiteMode(Mode):
|
||||||
|
"""
|
||||||
|
Parts of the main window UI for sharing files.
|
||||||
|
"""
|
||||||
|
success = QtCore.pyqtSignal()
|
||||||
|
error = QtCore.pyqtSignal(str)
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
"""
|
||||||
|
Custom initialization for ReceiveMode.
|
||||||
|
"""
|
||||||
|
# Create the Web object
|
||||||
|
self.web = Web(self.common, True, 'website')
|
||||||
|
|
||||||
|
# File selection
|
||||||
|
self.file_selection = FileSelection(self.common, self)
|
||||||
|
if self.filenames:
|
||||||
|
for filename in self.filenames:
|
||||||
|
self.file_selection.file_list.add_file(filename)
|
||||||
|
|
||||||
|
# Server status
|
||||||
|
self.server_status.set_mode('website', self.file_selection)
|
||||||
|
self.server_status.server_started.connect(self.file_selection.server_started)
|
||||||
|
self.server_status.server_stopped.connect(self.file_selection.server_stopped)
|
||||||
|
self.server_status.server_stopped.connect(self.update_primary_action)
|
||||||
|
self.server_status.server_canceled.connect(self.file_selection.server_stopped)
|
||||||
|
self.server_status.server_canceled.connect(self.update_primary_action)
|
||||||
|
self.file_selection.file_list.files_updated.connect(self.server_status.update)
|
||||||
|
self.file_selection.file_list.files_updated.connect(self.update_primary_action)
|
||||||
|
# Tell server_status about web, then update
|
||||||
|
self.server_status.web = self.web
|
||||||
|
self.server_status.update()
|
||||||
|
|
||||||
|
# Filesize warning
|
||||||
|
self.filesize_warning = QtWidgets.QLabel()
|
||||||
|
self.filesize_warning.setWordWrap(True)
|
||||||
|
self.filesize_warning.setStyleSheet(self.common.css['share_filesize_warning'])
|
||||||
|
self.filesize_warning.hide()
|
||||||
|
|
||||||
|
# Download history
|
||||||
|
self.history = History(
|
||||||
|
self.common,
|
||||||
|
QtGui.QPixmap.fromImage(QtGui.QImage(self.common.get_resource_path('images/share_icon_transparent.png'))),
|
||||||
|
strings._('gui_website_mode_no_files'),
|
||||||
|
strings._('gui_all_modes_history'),
|
||||||
|
'website'
|
||||||
|
)
|
||||||
|
self.history.hide()
|
||||||
|
|
||||||
|
# Info label
|
||||||
|
self.info_label = QtWidgets.QLabel()
|
||||||
|
self.info_label.hide()
|
||||||
|
|
||||||
|
# Toggle history
|
||||||
|
self.toggle_history = ToggleHistory(
|
||||||
|
self.common, self, self.history,
|
||||||
|
QtGui.QIcon(self.common.get_resource_path('images/share_icon_toggle.png')),
|
||||||
|
QtGui.QIcon(self.common.get_resource_path('images/share_icon_toggle_selected.png'))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Top bar
|
||||||
|
top_bar_layout = QtWidgets.QHBoxLayout()
|
||||||
|
top_bar_layout.addWidget(self.info_label)
|
||||||
|
top_bar_layout.addStretch()
|
||||||
|
top_bar_layout.addWidget(self.toggle_history)
|
||||||
|
|
||||||
|
# Primary action layout
|
||||||
|
self.primary_action_layout.addWidget(self.filesize_warning)
|
||||||
|
self.primary_action.hide()
|
||||||
|
self.update_primary_action()
|
||||||
|
|
||||||
|
# Main layout
|
||||||
|
self.main_layout = QtWidgets.QVBoxLayout()
|
||||||
|
self.main_layout.addLayout(top_bar_layout)
|
||||||
|
self.main_layout.addLayout(self.file_selection)
|
||||||
|
self.main_layout.addWidget(self.primary_action)
|
||||||
|
self.main_layout.addWidget(self.min_width_widget)
|
||||||
|
|
||||||
|
# Wrapper layout
|
||||||
|
self.wrapper_layout = QtWidgets.QHBoxLayout()
|
||||||
|
self.wrapper_layout.addLayout(self.main_layout)
|
||||||
|
self.wrapper_layout.addWidget(self.history, stretch=1)
|
||||||
|
self.setLayout(self.wrapper_layout)
|
||||||
|
|
||||||
|
# Always start with focus on file selection
|
||||||
|
self.file_selection.setFocus()
|
||||||
|
|
||||||
|
def get_stop_server_autostop_timer_text(self):
|
||||||
|
"""
|
||||||
|
Return the string to put on the stop server button, if there's an auto-stop timer
|
||||||
|
"""
|
||||||
|
return strings._('gui_share_stop_server_autostop_timer')
|
||||||
|
|
||||||
|
def autostop_timer_finished_should_stop_server(self):
|
||||||
|
"""
|
||||||
|
The auto-stop timer expired, should we stop the server? Returns a bool
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.server_status.stop_server()
|
||||||
|
self.server_status_label.setText(strings._('close_on_autostop_timer'))
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def start_server_custom(self):
|
||||||
|
"""
|
||||||
|
Starting the server.
|
||||||
|
"""
|
||||||
|
# Reset web counters
|
||||||
|
self.web.website_mode.visit_count = 0
|
||||||
|
self.web.error404_count = 0
|
||||||
|
|
||||||
|
# Hide and reset the downloads if we have previously shared
|
||||||
|
self.reset_info_counters()
|
||||||
|
|
||||||
|
def start_server_step2_custom(self):
|
||||||
|
"""
|
||||||
|
Step 2 in starting the server. Zipping up files.
|
||||||
|
"""
|
||||||
|
self.filenames = []
|
||||||
|
for index in range(self.file_selection.file_list.count()):
|
||||||
|
self.filenames.append(self.file_selection.file_list.item(index).filename)
|
||||||
|
|
||||||
|
# Continue
|
||||||
|
self.starting_server_step3.emit()
|
||||||
|
self.start_server_finished.emit()
|
||||||
|
|
||||||
|
|
||||||
|
def start_server_step3_custom(self):
|
||||||
|
"""
|
||||||
|
Step 3 in starting the server. Display large filesize
|
||||||
|
warning, if applicable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if self.web.website_mode.set_file_info(self.filenames):
|
||||||
|
self.success.emit()
|
||||||
|
else:
|
||||||
|
# Cancelled
|
||||||
|
pass
|
||||||
|
|
||||||
|
def start_server_error_custom(self):
|
||||||
|
"""
|
||||||
|
Start server error.
|
||||||
|
"""
|
||||||
|
if self._zip_progress_bar is not None:
|
||||||
|
self.status_bar.removeWidget(self._zip_progress_bar)
|
||||||
|
self._zip_progress_bar = None
|
||||||
|
|
||||||
|
def stop_server_custom(self):
|
||||||
|
"""
|
||||||
|
Stop server.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.filesize_warning.hide()
|
||||||
|
self.history.completed_count = 0
|
||||||
|
self.file_selection.file_list.adjustSize()
|
||||||
|
|
||||||
|
def cancel_server_custom(self):
|
||||||
|
"""
|
||||||
|
Log that the server has been cancelled
|
||||||
|
"""
|
||||||
|
self.common.log('WebsiteMode', 'cancel_server')
|
||||||
|
|
||||||
|
|
||||||
|
def handle_tor_broke_custom(self):
|
||||||
|
"""
|
||||||
|
Connection to Tor broke.
|
||||||
|
"""
|
||||||
|
self.primary_action.hide()
|
||||||
|
|
||||||
|
def handle_request_load(self, event):
|
||||||
|
"""
|
||||||
|
Handle REQUEST_LOAD event.
|
||||||
|
"""
|
||||||
|
self.system_tray.showMessage(strings._('systray_site_loaded_title'), strings._('systray_site_loaded_message'))
|
||||||
|
|
||||||
|
def handle_request_started(self, event):
|
||||||
|
"""
|
||||||
|
Handle REQUEST_STARTED event.
|
||||||
|
"""
|
||||||
|
if ( (event["path"] == '') or (event["path"].find(".htm") != -1 ) ):
|
||||||
|
item = VisitHistoryItem(self.common, event["data"]["id"], 0)
|
||||||
|
|
||||||
|
self.history.add(event["data"]["id"], item)
|
||||||
|
self.toggle_history.update_indicator(True)
|
||||||
|
self.history.completed_count += 1
|
||||||
|
self.history.update_completed()
|
||||||
|
|
||||||
|
self.system_tray.showMessage(strings._('systray_website_started_title'), strings._('systray_website_started_message'))
|
||||||
|
|
||||||
|
|
||||||
|
def on_reload_settings(self):
|
||||||
|
"""
|
||||||
|
If there were some files listed for sharing, we should be ok to re-enable
|
||||||
|
the 'Start Sharing' button now.
|
||||||
|
"""
|
||||||
|
if self.server_status.file_selection.get_num_files() > 0:
|
||||||
|
self.primary_action.show()
|
||||||
|
self.info_label.show()
|
||||||
|
|
||||||
|
def update_primary_action(self):
|
||||||
|
self.common.log('WebsiteMode', 'update_primary_action')
|
||||||
|
|
||||||
|
# Show or hide primary action layout
|
||||||
|
file_count = self.file_selection.file_list.count()
|
||||||
|
if file_count > 0:
|
||||||
|
self.primary_action.show()
|
||||||
|
self.info_label.show()
|
||||||
|
|
||||||
|
# Update the file count in the info label
|
||||||
|
total_size_bytes = 0
|
||||||
|
for index in range(self.file_selection.file_list.count()):
|
||||||
|
item = self.file_selection.file_list.item(index)
|
||||||
|
total_size_bytes += item.size_bytes
|
||||||
|
total_size_readable = self.common.human_readable_filesize(total_size_bytes)
|
||||||
|
|
||||||
|
if file_count > 1:
|
||||||
|
self.info_label.setText(strings._('gui_file_info').format(file_count, total_size_readable))
|
||||||
|
else:
|
||||||
|
self.info_label.setText(strings._('gui_file_info_single').format(file_count, total_size_readable))
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.primary_action.hide()
|
||||||
|
self.info_label.hide()
|
||||||
|
|
||||||
|
def reset_info_counters(self):
|
||||||
|
"""
|
||||||
|
Set the info counters back to zero.
|
||||||
|
"""
|
||||||
|
self.history.reset()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _compute_total_size(filenames):
|
||||||
|
total_size = 0
|
||||||
|
for filename in filenames:
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
total_size += os.path.getsize(filename)
|
||||||
|
if os.path.isdir(filename):
|
||||||
|
total_size += Common.dir_size(filename)
|
||||||
|
return total_size
|
@ -25,6 +25,7 @@ from onionshare.web import Web
|
|||||||
|
|
||||||
from .mode.share_mode import ShareMode
|
from .mode.share_mode import ShareMode
|
||||||
from .mode.receive_mode import ReceiveMode
|
from .mode.receive_mode import ReceiveMode
|
||||||
|
from .mode.website_mode import WebsiteMode
|
||||||
|
|
||||||
from .tor_connection_dialog import TorConnectionDialog
|
from .tor_connection_dialog import TorConnectionDialog
|
||||||
from .settings_dialog import SettingsDialog
|
from .settings_dialog import SettingsDialog
|
||||||
@ -39,6 +40,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
"""
|
"""
|
||||||
MODE_SHARE = 'share'
|
MODE_SHARE = 'share'
|
||||||
MODE_RECEIVE = 'receive'
|
MODE_RECEIVE = 'receive'
|
||||||
|
MODE_WEBSITE = 'website'
|
||||||
|
|
||||||
def __init__(self, common, onion, qtapp, app, filenames, config=False, local_only=False):
|
def __init__(self, common, onion, qtapp, app, filenames, config=False, local_only=False):
|
||||||
super(OnionShareGui, self).__init__()
|
super(OnionShareGui, self).__init__()
|
||||||
@ -92,6 +94,9 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
self.receive_mode_button = QtWidgets.QPushButton(strings._('gui_mode_receive_button'));
|
self.receive_mode_button = QtWidgets.QPushButton(strings._('gui_mode_receive_button'));
|
||||||
self.receive_mode_button.setFixedHeight(50)
|
self.receive_mode_button.setFixedHeight(50)
|
||||||
self.receive_mode_button.clicked.connect(self.receive_mode_clicked)
|
self.receive_mode_button.clicked.connect(self.receive_mode_clicked)
|
||||||
|
self.website_mode_button = QtWidgets.QPushButton(strings._('gui_mode_website_button'));
|
||||||
|
self.website_mode_button.setFixedHeight(50)
|
||||||
|
self.website_mode_button.clicked.connect(self.website_mode_clicked)
|
||||||
self.settings_button = QtWidgets.QPushButton()
|
self.settings_button = QtWidgets.QPushButton()
|
||||||
self.settings_button.setDefault(False)
|
self.settings_button.setDefault(False)
|
||||||
self.settings_button.setFixedWidth(40)
|
self.settings_button.setFixedWidth(40)
|
||||||
@ -103,6 +108,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
mode_switcher_layout.setSpacing(0)
|
mode_switcher_layout.setSpacing(0)
|
||||||
mode_switcher_layout.addWidget(self.share_mode_button)
|
mode_switcher_layout.addWidget(self.share_mode_button)
|
||||||
mode_switcher_layout.addWidget(self.receive_mode_button)
|
mode_switcher_layout.addWidget(self.receive_mode_button)
|
||||||
|
mode_switcher_layout.addWidget(self.website_mode_button)
|
||||||
mode_switcher_layout.addWidget(self.settings_button)
|
mode_switcher_layout.addWidget(self.settings_button)
|
||||||
|
|
||||||
# Server status indicator on the status bar
|
# Server status indicator on the status bar
|
||||||
@ -154,6 +160,20 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
self.receive_mode.server_status.hidservauth_copied.connect(self.copy_hidservauth)
|
self.receive_mode.server_status.hidservauth_copied.connect(self.copy_hidservauth)
|
||||||
self.receive_mode.set_server_active.connect(self.set_server_active)
|
self.receive_mode.set_server_active.connect(self.set_server_active)
|
||||||
|
|
||||||
|
# Website mode
|
||||||
|
self.website_mode = WebsiteMode(self.common, qtapp, app, self.status_bar, self.server_status_label, self.system_tray, filenames)
|
||||||
|
self.website_mode.init()
|
||||||
|
self.website_mode.server_status.server_started.connect(self.update_server_status_indicator)
|
||||||
|
self.website_mode.server_status.server_stopped.connect(self.update_server_status_indicator)
|
||||||
|
self.website_mode.start_server_finished.connect(self.update_server_status_indicator)
|
||||||
|
self.website_mode.stop_server_finished.connect(self.update_server_status_indicator)
|
||||||
|
self.website_mode.stop_server_finished.connect(self.stop_server_finished)
|
||||||
|
self.website_mode.start_server_finished.connect(self.clear_message)
|
||||||
|
self.website_mode.server_status.button_clicked.connect(self.clear_message)
|
||||||
|
self.website_mode.server_status.url_copied.connect(self.copy_url)
|
||||||
|
self.website_mode.server_status.hidservauth_copied.connect(self.copy_hidservauth)
|
||||||
|
self.website_mode.set_server_active.connect(self.set_server_active)
|
||||||
|
|
||||||
self.update_mode_switcher()
|
self.update_mode_switcher()
|
||||||
self.update_server_status_indicator()
|
self.update_server_status_indicator()
|
||||||
|
|
||||||
@ -162,6 +182,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
contents_layout.setContentsMargins(10, 0, 10, 0)
|
contents_layout.setContentsMargins(10, 0, 10, 0)
|
||||||
contents_layout.addWidget(self.receive_mode)
|
contents_layout.addWidget(self.receive_mode)
|
||||||
contents_layout.addWidget(self.share_mode)
|
contents_layout.addWidget(self.share_mode)
|
||||||
|
contents_layout.addWidget(self.website_mode)
|
||||||
|
|
||||||
layout = QtWidgets.QVBoxLayout()
|
layout = QtWidgets.QVBoxLayout()
|
||||||
layout.setContentsMargins(0, 0, 0, 0)
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
@ -199,15 +220,27 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
if self.mode == self.MODE_SHARE:
|
if self.mode == self.MODE_SHARE:
|
||||||
self.share_mode_button.setStyleSheet(self.common.css['mode_switcher_selected_style'])
|
self.share_mode_button.setStyleSheet(self.common.css['mode_switcher_selected_style'])
|
||||||
self.receive_mode_button.setStyleSheet(self.common.css['mode_switcher_unselected_style'])
|
self.receive_mode_button.setStyleSheet(self.common.css['mode_switcher_unselected_style'])
|
||||||
|
self.website_mode_button.setStyleSheet(self.common.css['mode_switcher_unselected_style'])
|
||||||
|
|
||||||
self.receive_mode.hide()
|
self.receive_mode.hide()
|
||||||
self.share_mode.show()
|
self.share_mode.show()
|
||||||
|
self.website_mode.hide()
|
||||||
|
elif self.mode == self.MODE_WEBSITE:
|
||||||
|
self.share_mode_button.setStyleSheet(self.common.css['mode_switcher_unselected_style'])
|
||||||
|
self.receive_mode_button.setStyleSheet(self.common.css['mode_switcher_unselected_style'])
|
||||||
|
self.website_mode_button.setStyleSheet(self.common.css['mode_switcher_selected_style'])
|
||||||
|
|
||||||
|
self.receive_mode.hide()
|
||||||
|
self.share_mode.hide()
|
||||||
|
self.website_mode.show()
|
||||||
else:
|
else:
|
||||||
self.share_mode_button.setStyleSheet(self.common.css['mode_switcher_unselected_style'])
|
self.share_mode_button.setStyleSheet(self.common.css['mode_switcher_unselected_style'])
|
||||||
self.receive_mode_button.setStyleSheet(self.common.css['mode_switcher_selected_style'])
|
self.receive_mode_button.setStyleSheet(self.common.css['mode_switcher_selected_style'])
|
||||||
|
self.website_mode_button.setStyleSheet(self.common.css['mode_switcher_unselected_style'])
|
||||||
|
|
||||||
self.share_mode.hide()
|
self.share_mode.hide()
|
||||||
self.receive_mode.show()
|
self.receive_mode.show()
|
||||||
|
self.website_mode.hide()
|
||||||
|
|
||||||
self.update_server_status_indicator()
|
self.update_server_status_indicator()
|
||||||
|
|
||||||
@ -223,6 +256,12 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
self.mode = self.MODE_RECEIVE
|
self.mode = self.MODE_RECEIVE
|
||||||
self.update_mode_switcher()
|
self.update_mode_switcher()
|
||||||
|
|
||||||
|
def website_mode_clicked(self):
|
||||||
|
if self.mode != self.MODE_WEBSITE:
|
||||||
|
self.common.log('OnionShareGui', 'website_mode_clicked')
|
||||||
|
self.mode = self.MODE_WEBSITE
|
||||||
|
self.update_mode_switcher()
|
||||||
|
|
||||||
def update_server_status_indicator(self):
|
def update_server_status_indicator(self):
|
||||||
# Set the status image
|
# Set the status image
|
||||||
if self.mode == self.MODE_SHARE:
|
if self.mode == self.MODE_SHARE:
|
||||||
@ -239,6 +278,17 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
elif self.share_mode.server_status.status == ServerStatus.STATUS_STARTED:
|
elif self.share_mode.server_status.status == ServerStatus.STATUS_STARTED:
|
||||||
self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_started))
|
self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_started))
|
||||||
self.server_status_label.setText(strings._('gui_status_indicator_share_started'))
|
self.server_status_label.setText(strings._('gui_status_indicator_share_started'))
|
||||||
|
elif self.mode == self.MODE_WEBSITE:
|
||||||
|
# Website mode
|
||||||
|
if self.website_mode.server_status.status == ServerStatus.STATUS_STOPPED:
|
||||||
|
self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_stopped))
|
||||||
|
self.server_status_label.setText(strings._('gui_status_indicator_share_stopped'))
|
||||||
|
elif self.website_mode.server_status.status == ServerStatus.STATUS_WORKING:
|
||||||
|
self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_working))
|
||||||
|
self.server_status_label.setText(strings._('gui_status_indicator_share_working'))
|
||||||
|
elif self.website_mode.server_status.status == ServerStatus.STATUS_STARTED:
|
||||||
|
self.server_status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.server_status_image_started))
|
||||||
|
self.server_status_label.setText(strings._('gui_status_indicator_share_started'))
|
||||||
else:
|
else:
|
||||||
# Receive mode
|
# Receive mode
|
||||||
if self.receive_mode.server_status.status == ServerStatus.STATUS_STOPPED:
|
if self.receive_mode.server_status.status == ServerStatus.STATUS_STOPPED:
|
||||||
@ -317,18 +367,22 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
self.timer.start(500)
|
self.timer.start(500)
|
||||||
self.share_mode.on_reload_settings()
|
self.share_mode.on_reload_settings()
|
||||||
self.receive_mode.on_reload_settings()
|
self.receive_mode.on_reload_settings()
|
||||||
|
self.website_mode.on_reload_settings()
|
||||||
self.status_bar.clearMessage()
|
self.status_bar.clearMessage()
|
||||||
|
|
||||||
# If we switched off the auto-stop timer setting, ensure the widget is hidden.
|
# If we switched off the auto-stop timer setting, ensure the widget is hidden.
|
||||||
if not self.common.settings.get('autostop_timer'):
|
if not self.common.settings.get('autostop_timer'):
|
||||||
self.share_mode.server_status.autostop_timer_container.hide()
|
self.share_mode.server_status.autostop_timer_container.hide()
|
||||||
self.receive_mode.server_status.autostop_timer_container.hide()
|
self.receive_mode.server_status.autostop_timer_container.hide()
|
||||||
|
self.website_mode.server_status.autostop_timer_container.hide()
|
||||||
# If we switched off the auto-start timer setting, ensure the widget is hidden.
|
# If we switched off the auto-start timer setting, ensure the widget is hidden.
|
||||||
if not self.common.settings.get('autostart_timer'):
|
if not self.common.settings.get('autostart_timer'):
|
||||||
self.share_mode.server_status.autostart_timer_datetime = None
|
self.share_mode.server_status.autostart_timer_datetime = None
|
||||||
self.receive_mode.server_status.autostart_timer_datetime = None
|
self.receive_mode.server_status.autostart_timer_datetime = None
|
||||||
|
self.website_mode.server_status.autostart_timer_datetime = None
|
||||||
self.share_mode.server_status.autostart_timer_container.hide()
|
self.share_mode.server_status.autostart_timer_container.hide()
|
||||||
self.receive_mode.server_status.autostart_timer_container.hide()
|
self.receive_mode.server_status.autostart_timer_container.hide()
|
||||||
|
self.website_mode.server_status.autostart_timer_container.hide()
|
||||||
|
|
||||||
d = SettingsDialog(self.common, self.onion, self.qtapp, self.config, self.local_only)
|
d = SettingsDialog(self.common, self.onion, self.qtapp, self.config, self.local_only)
|
||||||
d.settings_saved.connect(reload_settings)
|
d.settings_saved.connect(reload_settings)
|
||||||
@ -337,6 +391,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
# When settings close, refresh the server status UI
|
# When settings close, refresh the server status UI
|
||||||
self.share_mode.server_status.update()
|
self.share_mode.server_status.update()
|
||||||
self.receive_mode.server_status.update()
|
self.receive_mode.server_status.update()
|
||||||
|
self.website_mode.server_status.update()
|
||||||
|
|
||||||
def check_for_updates(self):
|
def check_for_updates(self):
|
||||||
"""
|
"""
|
||||||
@ -367,10 +422,13 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
|
|
||||||
self.share_mode.handle_tor_broke()
|
self.share_mode.handle_tor_broke()
|
||||||
self.receive_mode.handle_tor_broke()
|
self.receive_mode.handle_tor_broke()
|
||||||
|
self.website_mode.handle_tor_broke()
|
||||||
|
|
||||||
# Process events from the web object
|
# Process events from the web object
|
||||||
if self.mode == self.MODE_SHARE:
|
if self.mode == self.MODE_SHARE:
|
||||||
mode = self.share_mode
|
mode = self.share_mode
|
||||||
|
elif self.mode == self.MODE_WEBSITE:
|
||||||
|
mode = self.website_mode
|
||||||
else:
|
else:
|
||||||
mode = self.receive_mode
|
mode = self.receive_mode
|
||||||
|
|
||||||
@ -450,13 +508,20 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
if self.mode == self.MODE_SHARE:
|
if self.mode == self.MODE_SHARE:
|
||||||
self.share_mode_button.show()
|
self.share_mode_button.show()
|
||||||
self.receive_mode_button.hide()
|
self.receive_mode_button.hide()
|
||||||
|
self.website_mode_button.hide()
|
||||||
|
elif self.mode == self.MODE_WEBSITE:
|
||||||
|
self.share_mode_button.hide()
|
||||||
|
self.receive_mode_button.hide()
|
||||||
|
self.website_mode_button.show()
|
||||||
else:
|
else:
|
||||||
self.share_mode_button.hide()
|
self.share_mode_button.hide()
|
||||||
self.receive_mode_button.show()
|
self.receive_mode_button.show()
|
||||||
|
self.website_mode_button.hide()
|
||||||
else:
|
else:
|
||||||
self.settings_button.show()
|
self.settings_button.show()
|
||||||
self.share_mode_button.show()
|
self.share_mode_button.show()
|
||||||
self.receive_mode_button.show()
|
self.receive_mode_button.show()
|
||||||
|
self.website_mode_button.show()
|
||||||
|
|
||||||
# Disable settings menu action when server is active
|
# Disable settings menu action when server is active
|
||||||
self.settings_action.setEnabled(not active)
|
self.settings_action.setEnabled(not active)
|
||||||
@ -466,6 +531,8 @@ class OnionShareGui(QtWidgets.QMainWindow):
|
|||||||
try:
|
try:
|
||||||
if self.mode == OnionShareGui.MODE_SHARE:
|
if self.mode == OnionShareGui.MODE_SHARE:
|
||||||
server_status = self.share_mode.server_status
|
server_status = self.share_mode.server_status
|
||||||
|
if self.mode == OnionShareGui.MODE_WEBSITE:
|
||||||
|
server_status = self.website_mode.server_status
|
||||||
else:
|
else:
|
||||||
server_status = self.receive_mode.server_status
|
server_status = self.receive_mode.server_status
|
||||||
if server_status.status != server_status.STATUS_STOPPED:
|
if server_status.status != server_status.STATUS_STOPPED:
|
||||||
|
@ -39,6 +39,7 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
|
|
||||||
MODE_SHARE = 'share'
|
MODE_SHARE = 'share'
|
||||||
MODE_RECEIVE = 'receive'
|
MODE_RECEIVE = 'receive'
|
||||||
|
MODE_WEBSITE = 'website'
|
||||||
|
|
||||||
STATUS_STOPPED = 0
|
STATUS_STOPPED = 0
|
||||||
STATUS_WORKING = 1
|
STATUS_WORKING = 1
|
||||||
@ -159,7 +160,7 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
"""
|
"""
|
||||||
self.mode = share_mode
|
self.mode = share_mode
|
||||||
|
|
||||||
if self.mode == ServerStatus.MODE_SHARE:
|
if (self.mode == ServerStatus.MODE_SHARE) or (self.mode == ServerStatus.MODE_WEBSITE):
|
||||||
self.file_selection = file_selection
|
self.file_selection = file_selection
|
||||||
|
|
||||||
self.update()
|
self.update()
|
||||||
@ -207,6 +208,8 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
|
|
||||||
if self.mode == ServerStatus.MODE_SHARE:
|
if self.mode == ServerStatus.MODE_SHARE:
|
||||||
self.url_description.setText(strings._('gui_share_url_description').format(info_image))
|
self.url_description.setText(strings._('gui_share_url_description').format(info_image))
|
||||||
|
elif self.mode == ServerStatus.MODE_WEBSITE:
|
||||||
|
self.url_description.setText(strings._('gui_share_url_description').format(info_image))
|
||||||
else:
|
else:
|
||||||
self.url_description.setText(strings._('gui_receive_url_description').format(info_image))
|
self.url_description.setText(strings._('gui_receive_url_description').format(info_image))
|
||||||
|
|
||||||
@ -258,6 +261,8 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
# Button
|
# Button
|
||||||
if self.mode == ServerStatus.MODE_SHARE and self.file_selection.get_num_files() == 0:
|
if self.mode == ServerStatus.MODE_SHARE and self.file_selection.get_num_files() == 0:
|
||||||
self.server_button.hide()
|
self.server_button.hide()
|
||||||
|
elif self.mode == ServerStatus.MODE_WEBSITE and self.file_selection.get_num_files() == 0:
|
||||||
|
self.server_button.hide()
|
||||||
else:
|
else:
|
||||||
self.server_button.show()
|
self.server_button.show()
|
||||||
|
|
||||||
@ -266,6 +271,8 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
self.server_button.setEnabled(True)
|
self.server_button.setEnabled(True)
|
||||||
if self.mode == ServerStatus.MODE_SHARE:
|
if self.mode == ServerStatus.MODE_SHARE:
|
||||||
self.server_button.setText(strings._('gui_share_start_server'))
|
self.server_button.setText(strings._('gui_share_start_server'))
|
||||||
|
elif self.mode == ServerStatus.MODE_WEBSITE:
|
||||||
|
self.server_button.setText(strings._('gui_share_start_server'))
|
||||||
else:
|
else:
|
||||||
self.server_button.setText(strings._('gui_receive_start_server'))
|
self.server_button.setText(strings._('gui_receive_start_server'))
|
||||||
self.server_button.setToolTip('')
|
self.server_button.setToolTip('')
|
||||||
@ -278,6 +285,8 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
self.server_button.setEnabled(True)
|
self.server_button.setEnabled(True)
|
||||||
if self.mode == ServerStatus.MODE_SHARE:
|
if self.mode == ServerStatus.MODE_SHARE:
|
||||||
self.server_button.setText(strings._('gui_share_stop_server'))
|
self.server_button.setText(strings._('gui_share_stop_server'))
|
||||||
|
if self.mode == ServerStatus.MODE_WEBSITE:
|
||||||
|
self.server_button.setText(strings._('gui_share_stop_server'))
|
||||||
else:
|
else:
|
||||||
self.server_button.setText(strings._('gui_receive_stop_server'))
|
self.server_button.setText(strings._('gui_receive_stop_server'))
|
||||||
if self.common.settings.get('autostart_timer'):
|
if self.common.settings.get('autostart_timer'):
|
||||||
@ -411,6 +420,8 @@ class ServerStatus(QtWidgets.QWidget):
|
|||||||
"""
|
"""
|
||||||
if self.common.settings.get('public_mode'):
|
if self.common.settings.get('public_mode'):
|
||||||
url = 'http://{0:s}'.format(self.app.onion_host)
|
url = 'http://{0:s}'.format(self.app.onion_host)
|
||||||
|
elif self.mode == ServerStatus.MODE_WEBSITE:
|
||||||
|
url = 'http://onionshare:{0:s}@{1:s}'.format(self.web.slug, self.app.onion_host)
|
||||||
else:
|
else:
|
||||||
url = 'http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)
|
url = 'http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)
|
||||||
return url
|
return url
|
||||||
|
3
setup.py
3
setup.py
@ -88,7 +88,8 @@ setup(
|
|||||||
'onionshare_gui',
|
'onionshare_gui',
|
||||||
'onionshare_gui.mode',
|
'onionshare_gui.mode',
|
||||||
'onionshare_gui.mode.share_mode',
|
'onionshare_gui.mode.share_mode',
|
||||||
'onionshare_gui.mode.receive_mode'
|
'onionshare_gui.mode.receive_mode',
|
||||||
|
'onionshare_gui.mode.website_mode'
|
||||||
],
|
],
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
scripts=['install/scripts/onionshare', 'install/scripts/onionshare-gui'],
|
scripts=['install/scripts/onionshare', 'install/scripts/onionshare-gui'],
|
||||||
|
@ -114,6 +114,7 @@
|
|||||||
"gui_use_legacy_v2_onions_checkbox": "Use legacy addresses",
|
"gui_use_legacy_v2_onions_checkbox": "Use legacy addresses",
|
||||||
"gui_save_private_key_checkbox": "Use a persistent address",
|
"gui_save_private_key_checkbox": "Use a persistent address",
|
||||||
"gui_share_url_description": "<b>Anyone</b> with this OnionShare address can <b>download</b> your files using the <b>Tor Browser</b>: <img src='{}' />",
|
"gui_share_url_description": "<b>Anyone</b> with this OnionShare address can <b>download</b> your files using the <b>Tor Browser</b>: <img src='{}' />",
|
||||||
|
"gui_website_url_description": "<b>Anyone</b> with this OnionShare address can <b>visit</b> your website using the <b>Tor Browser</b>: <img src='{}' />",
|
||||||
"gui_receive_url_description": "<b>Anyone</b> with this OnionShare address can <b>upload</b> files to your computer using the <b>Tor Browser</b>: <img src='{}' />",
|
"gui_receive_url_description": "<b>Anyone</b> with this OnionShare address can <b>upload</b> files to your computer using the <b>Tor Browser</b>: <img src='{}' />",
|
||||||
"gui_url_label_persistent": "This share will not auto-stop.<br><br>Every subsequent share reuses the address. (To use one-time addresses, turn off \"Use persistent address\" in the settings.)",
|
"gui_url_label_persistent": "This share will not auto-stop.<br><br>Every subsequent share reuses the address. (To use one-time addresses, turn off \"Use persistent address\" in the settings.)",
|
||||||
"gui_url_label_stay_open": "This share will not auto-stop.",
|
"gui_url_label_stay_open": "This share will not auto-stop.",
|
||||||
@ -135,6 +136,7 @@
|
|||||||
"gui_receive_mode_warning": "Receive mode lets people upload files to your computer.<br><br><b>Some files can potentially take control of your computer if you open them. Only open things from people you trust, or if you know what you are doing.</b>",
|
"gui_receive_mode_warning": "Receive mode lets people upload files to your computer.<br><br><b>Some files can potentially take control of your computer if you open them. Only open things from people you trust, or if you know what you are doing.</b>",
|
||||||
"gui_mode_share_button": "Share Files",
|
"gui_mode_share_button": "Share Files",
|
||||||
"gui_mode_receive_button": "Receive Files",
|
"gui_mode_receive_button": "Receive Files",
|
||||||
|
"gui_mode_website_button": "Publish Website",
|
||||||
"gui_settings_receiving_label": "Receiving settings",
|
"gui_settings_receiving_label": "Receiving settings",
|
||||||
"gui_settings_data_dir_label": "Save files to",
|
"gui_settings_data_dir_label": "Save files to",
|
||||||
"gui_settings_data_dir_browse_button": "Browse",
|
"gui_settings_data_dir_browse_button": "Browse",
|
||||||
@ -145,6 +147,8 @@
|
|||||||
"systray_menu_exit": "Quit",
|
"systray_menu_exit": "Quit",
|
||||||
"systray_page_loaded_title": "Page Loaded",
|
"systray_page_loaded_title": "Page Loaded",
|
||||||
"systray_page_loaded_message": "OnionShare address loaded",
|
"systray_page_loaded_message": "OnionShare address loaded",
|
||||||
|
"systray_site_loaded_title": "Site Loaded",
|
||||||
|
"systray_site_loaded_message": "OnionShare site loaded",
|
||||||
"systray_share_started_title": "Sharing Started",
|
"systray_share_started_title": "Sharing Started",
|
||||||
"systray_share_started_message": "Starting to send files to someone",
|
"systray_share_started_message": "Starting to send files to someone",
|
||||||
"systray_share_completed_title": "Sharing Complete",
|
"systray_share_completed_title": "Sharing Complete",
|
||||||
@ -153,6 +157,8 @@
|
|||||||
"systray_share_canceled_message": "Someone canceled receiving your files",
|
"systray_share_canceled_message": "Someone canceled receiving your files",
|
||||||
"systray_receive_started_title": "Receiving Started",
|
"systray_receive_started_title": "Receiving Started",
|
||||||
"systray_receive_started_message": "Someone is sending files to you",
|
"systray_receive_started_message": "Someone is sending files to you",
|
||||||
|
"systray_website_started_title": "Starting sharing website",
|
||||||
|
"systray_website_started_message": "Someone is visiting your website",
|
||||||
"gui_all_modes_history": "History",
|
"gui_all_modes_history": "History",
|
||||||
"gui_all_modes_clear_history": "Clear All",
|
"gui_all_modes_clear_history": "Clear All",
|
||||||
"gui_all_modes_transfer_started": "Started {}",
|
"gui_all_modes_transfer_started": "Started {}",
|
||||||
@ -165,8 +171,10 @@
|
|||||||
"gui_all_modes_progress_eta": "{0:s}, ETA: {1:s}, %p%",
|
"gui_all_modes_progress_eta": "{0:s}, ETA: {1:s}, %p%",
|
||||||
"gui_share_mode_no_files": "No Files Sent Yet",
|
"gui_share_mode_no_files": "No Files Sent Yet",
|
||||||
"gui_share_mode_autostop_timer_waiting": "Waiting to finish sending",
|
"gui_share_mode_autostop_timer_waiting": "Waiting to finish sending",
|
||||||
|
"gui_website_mode_no_files": "No Website Shared Yet",
|
||||||
"gui_receive_mode_no_files": "No Files Received Yet",
|
"gui_receive_mode_no_files": "No Files Received Yet",
|
||||||
"gui_receive_mode_autostop_timer_waiting": "Waiting to finish receiving",
|
"gui_receive_mode_autostop_timer_waiting": "Waiting to finish receiving",
|
||||||
|
"gui_visit_started": "Someone has visited your website {}",
|
||||||
"receive_mode_upload_starting": "Upload of total size {} is starting",
|
"receive_mode_upload_starting": "Upload of total size {} is starting",
|
||||||
"days_first_letter": "d",
|
"days_first_letter": "d",
|
||||||
"hours_first_letter": "h",
|
"hours_first_letter": "h",
|
||||||
|
47
share/templates/listing.html
Normal file
47
share/templates/listing.html
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>OnionShare</title>
|
||||||
|
<link href="/static/img/favicon.ico" rel="icon" type="image/x-icon" />
|
||||||
|
<link href="/static/css/style.css" rel="stylesheet" type="text/css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<header class="clearfix">
|
||||||
|
<img class="logo" src="/static/img/logo.png" title="OnionShare">
|
||||||
|
<h1>OnionShare</h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<table class="file-list" id="file-list">
|
||||||
|
<tr>
|
||||||
|
<th id="filename-header">Filename</th>
|
||||||
|
<th id="size-header">Size</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% for info in dirs %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img width="30" height="30" title="" alt="" src="/static/img/web_folder.png" />
|
||||||
|
<a href="{{ info.basename }}">
|
||||||
|
{{ info.basename }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>—</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for info in files %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<img width="30" height="30" title="" alt="" src="/static/img/web_file.png" />
|
||||||
|
<a href="{{ info.basename }}">
|
||||||
|
{{ info.basename }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td>{{ info.size_human }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,6 +1,6 @@
|
|||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
Package3: onionshare
|
Package3: onionshare
|
||||||
Depends3: python3, python3-flask, python3-stem, python3-pyqt5, python3-crypto, python3-socks, python-nautilus, tor, obfs4proxy
|
Depends3: python3, python3-flask, python3-flask-httpauth, python3-stem, python3-pyqt5, python3-crypto, python3-socks, python-nautilus, tor, obfs4proxy
|
||||||
Build-Depends: python3, python3-pytest, python3-flask, python3-stem, python3-pyqt5, python3-crypto, python3-socks, python3-requests, python-nautilus, tor, obfs4proxy
|
Build-Depends: python3, python3-pytest, python3-flask, python3-flask-httpauth, python3-stem, python3-pyqt5, python3-crypto, python3-socks, python3-requests, python-nautilus, tor, obfs4proxy
|
||||||
Suite: cosmic
|
Suite: cosmic
|
||||||
X-Python3-Version: >= 3.5.3
|
X-Python3-Version: >= 3.5.3
|
||||||
|
Loading…
x
Reference in New Issue
Block a user