mirror of
https://github.com/onionshare/onionshare.git
synced 2025-03-05 20:55:59 -05:00
Add docstrings to all classes and methods (fix #198)
This commit is contained in:
parent
a5aff46050
commit
dc58921187
@ -120,6 +120,9 @@ def is_root():
|
|||||||
|
|
||||||
|
|
||||||
def dir_size(start_path):
|
def dir_size(start_path):
|
||||||
|
"""
|
||||||
|
Calculates the total size, in bytes, of all of the files in a directory.
|
||||||
|
"""
|
||||||
total_size = 0
|
total_size = 0
|
||||||
for dirpath, dirnames, filenames in os.walk(start_path):
|
for dirpath, dirnames, filenames in os.walk(start_path):
|
||||||
for f in filenames:
|
for f in filenames:
|
||||||
@ -130,6 +133,11 @@ def dir_size(start_path):
|
|||||||
|
|
||||||
|
|
||||||
class ZipWriter(object):
|
class ZipWriter(object):
|
||||||
|
"""
|
||||||
|
ZipWriter accepts files and directories and compresses them into a zip file
|
||||||
|
with. If a zip_filename is not passed in, it will use the default onionshare
|
||||||
|
filename.
|
||||||
|
"""
|
||||||
def __init__(self, zip_filename=None):
|
def __init__(self, zip_filename=None):
|
||||||
if zip_filename:
|
if zip_filename:
|
||||||
self.zip_filename = zip_filename
|
self.zip_filename = zip_filename
|
||||||
@ -139,9 +147,15 @@ class ZipWriter(object):
|
|||||||
self.z = zipfile.ZipFile(self.zip_filename, 'w', allowZip64=True)
|
self.z = zipfile.ZipFile(self.zip_filename, 'w', allowZip64=True)
|
||||||
|
|
||||||
def add_file(self, filename):
|
def add_file(self, filename):
|
||||||
|
"""
|
||||||
|
Add a file to the zip archive.
|
||||||
|
"""
|
||||||
self.z.write(filename, os.path.basename(filename), zipfile.ZIP_DEFLATED)
|
self.z.write(filename, os.path.basename(filename), zipfile.ZIP_DEFLATED)
|
||||||
|
|
||||||
def add_dir(self, filename):
|
def add_dir(self, filename):
|
||||||
|
"""
|
||||||
|
Add a directory, and all of its children, to the zip archive.
|
||||||
|
"""
|
||||||
dir_to_strip = os.path.dirname(filename.rstrip('/'))+'/'
|
dir_to_strip = os.path.dirname(filename.rstrip('/'))+'/'
|
||||||
for dirpath, dirnames, filenames in os.walk(filename):
|
for dirpath, dirnames, filenames in os.walk(filename):
|
||||||
for f in filenames:
|
for f in filenames:
|
||||||
@ -151,4 +165,7 @@ class ZipWriter(object):
|
|||||||
self.z.write(full_filename, arc_filename, zipfile.ZIP_DEFLATED)
|
self.z.write(full_filename, arc_filename, zipfile.ZIP_DEFLATED)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
"""
|
||||||
|
Close the zip archive.
|
||||||
|
"""
|
||||||
self.z.close()
|
self.z.close()
|
||||||
|
@ -25,12 +25,35 @@ import socks
|
|||||||
import helpers, strings
|
import helpers, strings
|
||||||
|
|
||||||
class NoTor(Exception):
|
class NoTor(Exception):
|
||||||
|
"""
|
||||||
|
This exception is raised if onionshare can't find a Tor control port
|
||||||
|
to connect to, or if it can't find a Tor socks5 proxy to proxy though.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class HSDirError(Exception):
|
class HSDirError(Exception):
|
||||||
|
"""
|
||||||
|
This exception is raised when onionshare tries create a non-ephemeral
|
||||||
|
hidden service and does not have permission to create or write to
|
||||||
|
the hidden service directory.
|
||||||
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class HS(object):
|
class HS(object):
|
||||||
|
"""
|
||||||
|
HS is an abstraction layer for connecting to the Tor control port and
|
||||||
|
creating hidden services. Onionshare supports creating hidden services
|
||||||
|
using two methods:
|
||||||
|
|
||||||
|
- Modifying the Tor configuration through the control port is the old
|
||||||
|
method, and will be deprecated in favor of ephemeral hidden services.
|
||||||
|
- Using the control port to create ephemeral hidden servers is the
|
||||||
|
preferred method.
|
||||||
|
|
||||||
|
This class detects the versions of Tor and stem to determine if ephemeral
|
||||||
|
hidden services are supported. If not, it falls back to modifying the
|
||||||
|
Tor configuration.
|
||||||
|
"""
|
||||||
def __init__(self, transparent_torification=False):
|
def __init__(self, transparent_torification=False):
|
||||||
self.transparent_torification = transparent_torification
|
self.transparent_torification = transparent_torification
|
||||||
|
|
||||||
@ -57,6 +80,10 @@ class HS(object):
|
|||||||
self.supports_ephemeral = callable(list_ephemeral_hidden_services) and tor_version >= '0.2.7.1'
|
self.supports_ephemeral = callable(list_ephemeral_hidden_services) and tor_version >= '0.2.7.1'
|
||||||
|
|
||||||
def start(self, port):
|
def start(self, port):
|
||||||
|
"""
|
||||||
|
Start a hidden service on port 80, pointing to the given port, and
|
||||||
|
return the onion hostname.
|
||||||
|
"""
|
||||||
print strings._("connecting_ctrlport").format(int(port))
|
print strings._("connecting_ctrlport").format(int(port))
|
||||||
if self.supports_ephemeral:
|
if self.supports_ephemeral:
|
||||||
print strings._('using_ephemeral')
|
print strings._('using_ephemeral')
|
||||||
@ -104,6 +131,11 @@ class HS(object):
|
|||||||
return onion_host
|
return onion_host
|
||||||
|
|
||||||
def wait_for_hs(self, onion_host):
|
def wait_for_hs(self, onion_host):
|
||||||
|
"""
|
||||||
|
This function is only required when using non-ephemeral hidden services. After
|
||||||
|
creating a hidden service, continually attempt to connect to it until it
|
||||||
|
successfully connects..
|
||||||
|
"""
|
||||||
# legacy only, this function is no longer required with ephemeral hidden services
|
# legacy only, this function is no longer required with ephemeral hidden services
|
||||||
print strings._('wait_for_hs')
|
print strings._('wait_for_hs')
|
||||||
|
|
||||||
@ -148,6 +180,10 @@ class HS(object):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
|
"""
|
||||||
|
Stop hidden services that were created earlier, and delete any temporary
|
||||||
|
files that were created.
|
||||||
|
"""
|
||||||
if self.supports_ephemeral:
|
if self.supports_ephemeral:
|
||||||
# cleanup the ephemeral hidden service
|
# cleanup the ephemeral hidden service
|
||||||
if self.service_id:
|
if self.service_id:
|
||||||
|
@ -22,6 +22,10 @@ import os, sys, subprocess, time, argparse, inspect, shutil, socket, threading
|
|||||||
import strings, helpers, web, hs
|
import strings, helpers, web, hs
|
||||||
|
|
||||||
class OnionShare(object):
|
class OnionShare(object):
|
||||||
|
"""
|
||||||
|
OnionShare is the main application class. Pass in options and run
|
||||||
|
start_hidden_service and it will do the magic.
|
||||||
|
"""
|
||||||
def __init__(self, debug=False, local_only=False, stay_open=False, transparent_torification=False):
|
def __init__(self, debug=False, local_only=False, stay_open=False, transparent_torification=False):
|
||||||
self.port = None
|
self.port = None
|
||||||
self.hs = None
|
self.hs = None
|
||||||
@ -45,6 +49,9 @@ class OnionShare(object):
|
|||||||
self.transparent_torification = transparent_torification
|
self.transparent_torification = transparent_torification
|
||||||
|
|
||||||
def choose_port(self):
|
def choose_port(self):
|
||||||
|
"""
|
||||||
|
Pick an un-used port to bind to.
|
||||||
|
"""
|
||||||
# let the OS choose a port
|
# let the OS choose a port
|
||||||
tmpsock = socket.socket()
|
tmpsock = socket.socket()
|
||||||
tmpsock.bind(("127.0.0.1", 0))
|
tmpsock.bind(("127.0.0.1", 0))
|
||||||
@ -52,6 +59,9 @@ class OnionShare(object):
|
|||||||
tmpsock.close()
|
tmpsock.close()
|
||||||
|
|
||||||
def start_hidden_service(self, gui=False):
|
def start_hidden_service(self, gui=False):
|
||||||
|
"""
|
||||||
|
Start the onionshare hidden service.
|
||||||
|
"""
|
||||||
if not self.port:
|
if not self.port:
|
||||||
self.choose_port()
|
self.choose_port()
|
||||||
|
|
||||||
@ -65,6 +75,9 @@ class OnionShare(object):
|
|||||||
self.onion_host = self.hs.start(self.port)
|
self.onion_host = self.hs.start(self.port)
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
|
"""
|
||||||
|
Shut everything down and clean up temporary files, etc.
|
||||||
|
"""
|
||||||
# cleanup files
|
# cleanup files
|
||||||
for filename in self.cleanup_filenames:
|
for filename in self.cleanup_filenames:
|
||||||
if os.path.isfile(filename):
|
if os.path.isfile(filename):
|
||||||
@ -79,6 +92,10 @@ class OnionShare(object):
|
|||||||
|
|
||||||
|
|
||||||
def main(cwd=None):
|
def main(cwd=None):
|
||||||
|
"""
|
||||||
|
The main() function implements all of the logic that the command-line version of
|
||||||
|
onionshare uses.
|
||||||
|
"""
|
||||||
strings.load_strings()
|
strings.load_strings()
|
||||||
|
|
||||||
# onionshare CLI in OSX needs to change current working directory (#132)
|
# onionshare CLI in OSX needs to change current working directory (#132)
|
||||||
|
@ -31,6 +31,11 @@ zip_filesize = None
|
|||||||
|
|
||||||
|
|
||||||
def set_file_info(filenames):
|
def set_file_info(filenames):
|
||||||
|
"""
|
||||||
|
Using the list of filenames being shared, fill in details that the web
|
||||||
|
page will need to display. This includes zipping up the file in order to
|
||||||
|
get the zip file's name and size.
|
||||||
|
"""
|
||||||
global file_info, zip_filename, zip_filesize
|
global file_info, zip_filename, zip_filesize
|
||||||
|
|
||||||
# build file info list
|
# build file info list
|
||||||
@ -71,6 +76,9 @@ q = Queue.Queue()
|
|||||||
|
|
||||||
|
|
||||||
def add_request(request_type, path, data=None):
|
def add_request(request_type, path, data=None):
|
||||||
|
"""
|
||||||
|
Add a request to the queue, to communicate with the GUI.
|
||||||
|
"""
|
||||||
global q
|
global q
|
||||||
q.put({
|
q.put({
|
||||||
'type': request_type,
|
'type': request_type,
|
||||||
@ -84,19 +92,34 @@ download_count = 0
|
|||||||
|
|
||||||
stay_open = False
|
stay_open = False
|
||||||
def set_stay_open(new_stay_open):
|
def set_stay_open(new_stay_open):
|
||||||
|
"""
|
||||||
|
Set stay_open variable.
|
||||||
|
"""
|
||||||
global stay_open
|
global stay_open
|
||||||
stay_open = new_stay_open
|
stay_open = new_stay_open
|
||||||
def get_stay_open():
|
def get_stay_open():
|
||||||
|
"""
|
||||||
|
Get stay_open variable.
|
||||||
|
"""
|
||||||
return stay_open
|
return stay_open
|
||||||
|
|
||||||
transparent_torification = False
|
transparent_torification = False
|
||||||
def set_transparent_torification(new_transparent_torification):
|
def set_transparent_torification(new_transparent_torification):
|
||||||
|
"""
|
||||||
|
Set transparent_torification variable.
|
||||||
|
"""
|
||||||
global transparent_torification
|
global transparent_torification
|
||||||
stay_open = new_transparent_torification
|
stay_open = new_transparent_torification
|
||||||
def get_transparent_torification():
|
def get_transparent_torification():
|
||||||
|
"""
|
||||||
|
Get transparent_torification variable."
|
||||||
|
"""
|
||||||
return transparent_torification
|
return transparent_torification
|
||||||
|
|
||||||
def debug_mode():
|
def debug_mode():
|
||||||
|
"""
|
||||||
|
Turn on debugging mode, which will log flask errors to a debug file.
|
||||||
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
if platform.system() == 'Windows':
|
if platform.system() == 'Windows':
|
||||||
@ -111,6 +134,9 @@ def debug_mode():
|
|||||||
|
|
||||||
@app.route("/<slug_candidate>")
|
@app.route("/<slug_candidate>")
|
||||||
def index(slug_candidate):
|
def index(slug_candidate):
|
||||||
|
"""
|
||||||
|
Render the template for the onionshare landing page.
|
||||||
|
"""
|
||||||
if not helpers.constant_time_compare(slug.encode('ascii'), slug_candidate.encode('ascii')):
|
if not helpers.constant_time_compare(slug.encode('ascii'), slug_candidate.encode('ascii')):
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
@ -128,6 +154,9 @@ def index(slug_candidate):
|
|||||||
|
|
||||||
@app.route("/<slug_candidate>/download")
|
@app.route("/<slug_candidate>/download")
|
||||||
def download(slug_candidate):
|
def download(slug_candidate):
|
||||||
|
"""
|
||||||
|
Download the zip file.
|
||||||
|
"""
|
||||||
global download_count
|
global download_count
|
||||||
if not helpers.constant_time_compare(slug.encode('ascii'), slug_candidate.encode('ascii')):
|
if not helpers.constant_time_compare(slug.encode('ascii'), slug_candidate.encode('ascii')):
|
||||||
abort(404)
|
abort(404)
|
||||||
@ -205,6 +234,9 @@ def download(slug_candidate):
|
|||||||
|
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def page_not_found(e):
|
def page_not_found(e):
|
||||||
|
"""
|
||||||
|
404 error page.
|
||||||
|
"""
|
||||||
add_request(REQUEST_OTHER, request.path)
|
add_request(REQUEST_OTHER, request.path)
|
||||||
return render_template_string(open(helpers.get_html_path('404.html')).read())
|
return render_template_string(open(helpers.get_html_path('404.html')).read())
|
||||||
|
|
||||||
@ -214,6 +246,9 @@ shutdown_slug = helpers.random_string(16)
|
|||||||
|
|
||||||
@app.route("/<shutdown_slug_candidate>/shutdown")
|
@app.route("/<shutdown_slug_candidate>/shutdown")
|
||||||
def shutdown(shutdown_slug_candidate):
|
def shutdown(shutdown_slug_candidate):
|
||||||
|
"""
|
||||||
|
Stop the flask web server.
|
||||||
|
"""
|
||||||
if not helpers.constant_time_compare(shutdown_slug.encode('ascii'), shutdown_slug_candidate.encode('ascii')):
|
if not helpers.constant_time_compare(shutdown_slug.encode('ascii'), shutdown_slug_candidate.encode('ascii')):
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
@ -227,12 +262,18 @@ def shutdown(shutdown_slug_candidate):
|
|||||||
|
|
||||||
|
|
||||||
def start(port, stay_open=False, transparent_torification=False):
|
def start(port, stay_open=False, transparent_torification=False):
|
||||||
|
"""
|
||||||
|
Start the flask web server.
|
||||||
|
"""
|
||||||
set_stay_open(stay_open)
|
set_stay_open(stay_open)
|
||||||
set_transparent_torification(transparent_torification)
|
set_transparent_torification(transparent_torification)
|
||||||
app.run(port=port, threaded=True)
|
app.run(port=port, threaded=True)
|
||||||
|
|
||||||
|
|
||||||
def stop(port):
|
def stop(port):
|
||||||
|
"""
|
||||||
|
Stop the flask web server by loading /shutdown.
|
||||||
|
"""
|
||||||
# to stop flask, load http://127.0.0.1:<port>/<shutdown_slug>/shutdown
|
# to stop flask, load http://127.0.0.1:<port>/<shutdown_slug>/shutdown
|
||||||
if transparent_torification:
|
if transparent_torification:
|
||||||
import socket
|
import socket
|
||||||
|
@ -24,6 +24,10 @@ from onionshare import strings, helpers
|
|||||||
|
|
||||||
|
|
||||||
class Downloads(QtGui.QVBoxLayout):
|
class Downloads(QtGui.QVBoxLayout):
|
||||||
|
"""
|
||||||
|
The downloads chunk of the GUI. This lists all of the active download
|
||||||
|
progress bars.
|
||||||
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(Downloads, self).__init__()
|
super(Downloads, self).__init__()
|
||||||
|
|
||||||
@ -37,6 +41,9 @@ class Downloads(QtGui.QVBoxLayout):
|
|||||||
self.addWidget(self.downloads_label)
|
self.addWidget(self.downloads_label)
|
||||||
|
|
||||||
def add_download(self, download_id, total_bytes):
|
def add_download(self, download_id, total_bytes):
|
||||||
|
"""
|
||||||
|
Add a new download progress bar.
|
||||||
|
"""
|
||||||
self.downloads_label.show()
|
self.downloads_label.show()
|
||||||
|
|
||||||
# make a new progress bar
|
# make a new progress bar
|
||||||
@ -57,6 +64,9 @@ class Downloads(QtGui.QVBoxLayout):
|
|||||||
self.update_download(download_id, total_bytes, 0)
|
self.update_download(download_id, total_bytes, 0)
|
||||||
|
|
||||||
def update_download(self, download_id, total_bytes, downloaded_bytes):
|
def update_download(self, download_id, total_bytes, downloaded_bytes):
|
||||||
|
"""
|
||||||
|
Update the progress of a download progress bar.
|
||||||
|
"""
|
||||||
if download_id not in self.progress_bars:
|
if download_id not in self.progress_bars:
|
||||||
self.add_download(download_id, total_bytes)
|
self.add_download(download_id, total_bytes)
|
||||||
|
|
||||||
@ -68,5 +78,8 @@ class Downloads(QtGui.QVBoxLayout):
|
|||||||
pb.setFormat("{0:s}, %p%".format(helpers.human_readable_filesize(downloaded_bytes)))
|
pb.setFormat("{0:s}, %p%".format(helpers.human_readable_filesize(downloaded_bytes)))
|
||||||
|
|
||||||
def cancel_download(self, download_id):
|
def cancel_download(self, download_id):
|
||||||
|
"""
|
||||||
|
Update a download progress bar to show that it has been canceled.
|
||||||
|
"""
|
||||||
pb = self.progress_bars[download_id]
|
pb = self.progress_bars[download_id]
|
||||||
pb.setFormat(strings._('gui_canceled'))
|
pb.setFormat(strings._('gui_canceled'))
|
||||||
|
@ -25,6 +25,9 @@ from onionshare import strings, helpers
|
|||||||
|
|
||||||
|
|
||||||
class FileList(QtGui.QListWidget):
|
class FileList(QtGui.QListWidget):
|
||||||
|
"""
|
||||||
|
The list of files and folders in the GUI.
|
||||||
|
"""
|
||||||
files_dropped = QtCore.pyqtSignal()
|
files_dropped = QtCore.pyqtSignal()
|
||||||
files_updated = QtCore.pyqtSignal()
|
files_updated = QtCore.pyqtSignal()
|
||||||
|
|
||||||
@ -35,6 +38,10 @@ class FileList(QtGui.QListWidget):
|
|||||||
self.setSortingEnabled(True)
|
self.setSortingEnabled(True)
|
||||||
|
|
||||||
class DropHereLabel(QtGui.QLabel):
|
class DropHereLabel(QtGui.QLabel):
|
||||||
|
"""
|
||||||
|
When there are no files or folders in the FileList yet, display the
|
||||||
|
'drop files here' message and graphic.
|
||||||
|
"""
|
||||||
def __init__(self, parent, image=False):
|
def __init__(self, parent, image=False):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
super(DropHereLabel, self).__init__(parent=parent)
|
super(DropHereLabel, self).__init__(parent=parent)
|
||||||
@ -61,6 +68,9 @@ class FileList(QtGui.QListWidget):
|
|||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
"""
|
||||||
|
Update the GUI elements based on the current state.
|
||||||
|
"""
|
||||||
# file list should have a background image if empty
|
# file list should have a background image if empty
|
||||||
if len(self.filenames) == 0:
|
if len(self.filenames) == 0:
|
||||||
self.drop_here_image.show()
|
self.drop_here_image.show()
|
||||||
@ -70,20 +80,32 @@ class FileList(QtGui.QListWidget):
|
|||||||
self.drop_here_text.hide()
|
self.drop_here_text.hide()
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event):
|
||||||
|
"""
|
||||||
|
When the widget is resized, resize the drop files image and text.
|
||||||
|
"""
|
||||||
self.drop_here_image.setGeometry(0, 0, self.width(), self.height())
|
self.drop_here_image.setGeometry(0, 0, self.width(), self.height())
|
||||||
self.drop_here_text.setGeometry(0, 0, self.width(), self.height())
|
self.drop_here_text.setGeometry(0, 0, self.width(), self.height())
|
||||||
|
|
||||||
def dragEnterEvent(self, event):
|
def dragEnterEvent(self, event):
|
||||||
|
"""
|
||||||
|
dragEnterEvent for dragging files and directories into the widget.
|
||||||
|
"""
|
||||||
if event.mimeData().hasUrls:
|
if event.mimeData().hasUrls:
|
||||||
event.accept()
|
event.accept()
|
||||||
else:
|
else:
|
||||||
event.ignore()
|
event.ignore()
|
||||||
|
|
||||||
def dragLeaveEvent(self, event):
|
def dragLeaveEvent(self, event):
|
||||||
|
"""
|
||||||
|
dragLeaveEvent for dragging files and directories into the widget.
|
||||||
|
"""
|
||||||
event.accept()
|
event.accept()
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def dragMoveEvent(self, event):
|
def dragMoveEvent(self, event):
|
||||||
|
"""
|
||||||
|
dragMoveEvent for dragging files and directories into the widget.
|
||||||
|
"""
|
||||||
if event.mimeData().hasUrls:
|
if event.mimeData().hasUrls:
|
||||||
event.setDropAction(QtCore.Qt.CopyAction)
|
event.setDropAction(QtCore.Qt.CopyAction)
|
||||||
event.accept()
|
event.accept()
|
||||||
@ -91,6 +113,9 @@ class FileList(QtGui.QListWidget):
|
|||||||
event.ignore()
|
event.ignore()
|
||||||
|
|
||||||
def dropEvent(self, event):
|
def dropEvent(self, event):
|
||||||
|
"""
|
||||||
|
dropEvent for dragging files and directories into the widget.
|
||||||
|
"""
|
||||||
if event.mimeData().hasUrls:
|
if event.mimeData().hasUrls:
|
||||||
event.setDropAction(QtCore.Qt.CopyAction)
|
event.setDropAction(QtCore.Qt.CopyAction)
|
||||||
event.accept()
|
event.accept()
|
||||||
@ -102,6 +127,9 @@ class FileList(QtGui.QListWidget):
|
|||||||
self.files_dropped.emit()
|
self.files_dropped.emit()
|
||||||
|
|
||||||
def add_file(self, filename):
|
def add_file(self, filename):
|
||||||
|
"""
|
||||||
|
Add a file or directory to this widget.
|
||||||
|
"""
|
||||||
if filename not in self.filenames:
|
if filename not in self.filenames:
|
||||||
# make filenames unicode-safe for Qt (#141)
|
# make filenames unicode-safe for Qt (#141)
|
||||||
filename = filename.encode('utf-8').decode('utf-8', 'replace')
|
filename = filename.encode('utf-8').decode('utf-8', 'replace')
|
||||||
@ -128,6 +156,10 @@ class FileList(QtGui.QListWidget):
|
|||||||
|
|
||||||
|
|
||||||
class FileSelection(QtGui.QVBoxLayout):
|
class FileSelection(QtGui.QVBoxLayout):
|
||||||
|
"""
|
||||||
|
The list of files and folders in the GUI, as well as buttons to add and
|
||||||
|
delete the files and folders.
|
||||||
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(FileSelection, self).__init__()
|
super(FileSelection, self).__init__()
|
||||||
self.server_on = False
|
self.server_on = False
|
||||||
@ -156,6 +188,9 @@ class FileSelection(QtGui.QVBoxLayout):
|
|||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
"""
|
||||||
|
Update the GUI elements based on the current state.
|
||||||
|
"""
|
||||||
# all buttons should be disabled if the server is on
|
# all buttons should be disabled if the server is on
|
||||||
if self.server_on:
|
if self.server_on:
|
||||||
self.add_files_button.setEnabled(False)
|
self.add_files_button.setEnabled(False)
|
||||||
@ -176,6 +211,9 @@ class FileSelection(QtGui.QVBoxLayout):
|
|||||||
self.file_list.update()
|
self.file_list.update()
|
||||||
|
|
||||||
def add_files(self):
|
def add_files(self):
|
||||||
|
"""
|
||||||
|
Add files button clicked.
|
||||||
|
"""
|
||||||
filenames = QtGui.QFileDialog.getOpenFileNames(
|
filenames = QtGui.QFileDialog.getOpenFileNames(
|
||||||
caption=strings._('gui_choose_files', True), options=QtGui.QFileDialog.ReadOnly)
|
caption=strings._('gui_choose_files', True), options=QtGui.QFileDialog.ReadOnly)
|
||||||
if filenames:
|
if filenames:
|
||||||
@ -184,6 +222,9 @@ class FileSelection(QtGui.QVBoxLayout):
|
|||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def add_dir(self):
|
def add_dir(self):
|
||||||
|
"""
|
||||||
|
Add folder button clicked.
|
||||||
|
"""
|
||||||
filename = QtGui.QFileDialog.getExistingDirectory(
|
filename = QtGui.QFileDialog.getExistingDirectory(
|
||||||
caption=strings._('gui_choose_folder', True), options=QtGui.QFileDialog.ReadOnly)
|
caption=strings._('gui_choose_folder', True), options=QtGui.QFileDialog.ReadOnly)
|
||||||
if filename:
|
if filename:
|
||||||
@ -191,20 +232,32 @@ class FileSelection(QtGui.QVBoxLayout):
|
|||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def delete_file(self):
|
def delete_file(self):
|
||||||
|
"""
|
||||||
|
Delete button clicked
|
||||||
|
"""
|
||||||
current_row = self.file_list.currentRow()
|
current_row = self.file_list.currentRow()
|
||||||
self.file_list.filenames.pop(current_row)
|
self.file_list.filenames.pop(current_row)
|
||||||
self.file_list.takeItem(current_row)
|
self.file_list.takeItem(current_row)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def server_started(self):
|
def server_started(self):
|
||||||
|
"""
|
||||||
|
Gets called when the server starts.
|
||||||
|
"""
|
||||||
self.server_on = True
|
self.server_on = True
|
||||||
self.file_list.setAcceptDrops(False)
|
self.file_list.setAcceptDrops(False)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def server_stopped(self):
|
def server_stopped(self):
|
||||||
|
"""
|
||||||
|
Gets called when the server stops.
|
||||||
|
"""
|
||||||
self.server_on = False
|
self.server_on = False
|
||||||
self.file_list.setAcceptDrops(True)
|
self.file_list.setAcceptDrops(True)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def get_num_files(self):
|
def get_num_files(self):
|
||||||
|
"""
|
||||||
|
Returns the total number of files and folders in the list.
|
||||||
|
"""
|
||||||
return len(self.file_list.filenames)
|
return len(self.file_list.filenames)
|
||||||
|
@ -37,6 +37,10 @@ from options import Options
|
|||||||
|
|
||||||
|
|
||||||
class Application(QtGui.QApplication):
|
class Application(QtGui.QApplication):
|
||||||
|
"""
|
||||||
|
This is Qt's QApplication class. It has been overridden to support threads
|
||||||
|
and the quick keyboard shortcut.
|
||||||
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
platform = helpers.get_platform()
|
platform = helpers.get_platform()
|
||||||
if platform == 'Linux':
|
if platform == 'Linux':
|
||||||
@ -53,6 +57,10 @@ class Application(QtGui.QApplication):
|
|||||||
|
|
||||||
|
|
||||||
class OnionShareGui(QtGui.QWidget):
|
class OnionShareGui(QtGui.QWidget):
|
||||||
|
"""
|
||||||
|
OnionShareGui is the main window for the GUI that contains all of the
|
||||||
|
GUI elements.
|
||||||
|
"""
|
||||||
start_server_finished = QtCore.pyqtSignal()
|
start_server_finished = QtCore.pyqtSignal()
|
||||||
stop_server_finished = QtCore.pyqtSignal()
|
stop_server_finished = QtCore.pyqtSignal()
|
||||||
starting_server_step2 = QtCore.pyqtSignal()
|
starting_server_step2 = QtCore.pyqtSignal()
|
||||||
@ -66,6 +74,10 @@ class OnionShareGui(QtGui.QWidget):
|
|||||||
self.setWindowIcon(window_icon)
|
self.setWindowIcon(window_icon)
|
||||||
|
|
||||||
def send_files(self, filenames=None):
|
def send_files(self, filenames=None):
|
||||||
|
"""
|
||||||
|
Build the GUI in send files mode.
|
||||||
|
Note that this is the only mode currently implemented.
|
||||||
|
"""
|
||||||
# file selection
|
# file selection
|
||||||
self.file_selection = FileSelection()
|
self.file_selection = FileSelection()
|
||||||
if filenames:
|
if filenames:
|
||||||
@ -117,12 +129,20 @@ class OnionShareGui(QtGui.QWidget):
|
|||||||
self.timer.start(500)
|
self.timer.start(500)
|
||||||
|
|
||||||
def start_server_step2(self):
|
def start_server_step2(self):
|
||||||
|
"""
|
||||||
|
Step 2 in starting the onionshare server. This displays the large filesize
|
||||||
|
warning, if applicable.
|
||||||
|
"""
|
||||||
# warn about sending large files over Tor
|
# warn about sending large files over Tor
|
||||||
if web.zip_filesize >= 157286400: # 150mb
|
if web.zip_filesize >= 157286400: # 150mb
|
||||||
self.filesize_warning.setText(strings._("large_filesize", True))
|
self.filesize_warning.setText(strings._("large_filesize", True))
|
||||||
self.filesize_warning.show()
|
self.filesize_warning.show()
|
||||||
|
|
||||||
def start_server(self):
|
def start_server(self):
|
||||||
|
"""
|
||||||
|
Start the onionshare server. This uses multiple threads to start the Tor hidden
|
||||||
|
server and the web app.
|
||||||
|
"""
|
||||||
# start the hidden service
|
# start the hidden service
|
||||||
self.status_bar.showMessage(strings._('gui_starting_server1', True))
|
self.status_bar.showMessage(strings._('gui_starting_server1', True))
|
||||||
self.app.choose_port()
|
self.app.choose_port()
|
||||||
@ -162,6 +182,9 @@ class OnionShareGui(QtGui.QWidget):
|
|||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
def stop_server(self):
|
def stop_server(self):
|
||||||
|
"""
|
||||||
|
Stop the onionshare server.
|
||||||
|
"""
|
||||||
if self.server_status.status == self.server_status.STATUS_STARTED:
|
if self.server_status.status == self.server_status.STATUS_STARTED:
|
||||||
web.stop(self.app.port)
|
web.stop(self.app.port)
|
||||||
self.app.cleanup()
|
self.app.cleanup()
|
||||||
@ -169,6 +192,9 @@ class OnionShareGui(QtGui.QWidget):
|
|||||||
self.stop_server_finished.emit()
|
self.stop_server_finished.emit()
|
||||||
|
|
||||||
def check_for_requests(self):
|
def check_for_requests(self):
|
||||||
|
"""
|
||||||
|
Check for messages communicated from the web app, and update the GUI accordingly.
|
||||||
|
"""
|
||||||
self.update()
|
self.update()
|
||||||
# only check for requests if the server is running
|
# only check for requests if the server is running
|
||||||
if self.server_status.status != self.server_status.STATUS_STARTED:
|
if self.server_status.status != self.server_status.STATUS_STARTED:
|
||||||
@ -207,13 +233,22 @@ class OnionShareGui(QtGui.QWidget):
|
|||||||
self.status_bar.showMessage('{0:s}: {1:s}'.format(strings._('other_page_loaded', True), event["path"]))
|
self.status_bar.showMessage('{0:s}: {1:s}'.format(strings._('other_page_loaded', True), event["path"]))
|
||||||
|
|
||||||
def copy_url(self):
|
def copy_url(self):
|
||||||
|
"""
|
||||||
|
When the URL gets copied to the clipboard, display this in the status bar.
|
||||||
|
"""
|
||||||
self.status_bar.showMessage(strings._('gui_copied_url', True), 2000)
|
self.status_bar.showMessage(strings._('gui_copied_url', True), 2000)
|
||||||
|
|
||||||
def clear_message(self):
|
def clear_message(self):
|
||||||
|
"""
|
||||||
|
Clear messages from the status bar.
|
||||||
|
"""
|
||||||
self.status_bar.clearMessage()
|
self.status_bar.clearMessage()
|
||||||
|
|
||||||
|
|
||||||
def alert(msg, icon=QtGui.QMessageBox.NoIcon):
|
def alert(msg, icon=QtGui.QMessageBox.NoIcon):
|
||||||
|
"""
|
||||||
|
Pop up a message in a dialog window.
|
||||||
|
"""
|
||||||
dialog = QtGui.QMessageBox()
|
dialog = QtGui.QMessageBox()
|
||||||
dialog.setWindowTitle("OnionShare")
|
dialog.setWindowTitle("OnionShare")
|
||||||
dialog.setWindowIcon(window_icon)
|
dialog.setWindowIcon(window_icon)
|
||||||
@ -223,6 +258,9 @@ def alert(msg, icon=QtGui.QMessageBox.NoIcon):
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
"""
|
||||||
|
The main() function implements all of the logic that the GUI version of onionshare uses.
|
||||||
|
"""
|
||||||
strings.load_strings()
|
strings.load_strings()
|
||||||
|
|
||||||
# start the Qt app
|
# start the Qt app
|
||||||
|
@ -24,6 +24,9 @@ from onionshare import strings, helpers
|
|||||||
|
|
||||||
|
|
||||||
class Options(QtGui.QHBoxLayout):
|
class Options(QtGui.QHBoxLayout):
|
||||||
|
"""
|
||||||
|
The extra onionshare options in the GUI.
|
||||||
|
"""
|
||||||
def __init__(self, web):
|
def __init__(self, web):
|
||||||
super(Options, self).__init__()
|
super(Options, self).__init__()
|
||||||
|
|
||||||
@ -42,6 +45,9 @@ class Options(QtGui.QHBoxLayout):
|
|||||||
self.addWidget(self.close_automatically)
|
self.addWidget(self.close_automatically)
|
||||||
|
|
||||||
def stay_open_changed(self, state):
|
def stay_open_changed(self, state):
|
||||||
|
"""
|
||||||
|
When the 'close automatically' checkbox is toggled, let the web app know.
|
||||||
|
"""
|
||||||
if state > 0:
|
if state > 0:
|
||||||
self.web.set_stay_open(False)
|
self.web.set_stay_open(False)
|
||||||
else:
|
else:
|
||||||
|
@ -25,6 +25,9 @@ from onionshare import strings, helpers
|
|||||||
|
|
||||||
|
|
||||||
class ServerStatus(QtGui.QVBoxLayout):
|
class ServerStatus(QtGui.QVBoxLayout):
|
||||||
|
"""
|
||||||
|
The server status chunk of the GUI.
|
||||||
|
"""
|
||||||
server_started = QtCore.pyqtSignal()
|
server_started = QtCore.pyqtSignal()
|
||||||
server_stopped = QtCore.pyqtSignal()
|
server_stopped = QtCore.pyqtSignal()
|
||||||
url_copied = QtCore.pyqtSignal()
|
url_copied = QtCore.pyqtSignal()
|
||||||
@ -73,6 +76,9 @@ class ServerStatus(QtGui.QVBoxLayout):
|
|||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
"""
|
||||||
|
Update the GUI elements based on the current state.
|
||||||
|
"""
|
||||||
# set the status image
|
# set the status image
|
||||||
if self.status == self.STATUS_STOPPED:
|
if self.status == self.STATUS_STOPPED:
|
||||||
self.status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.status_image_stopped))
|
self.status_image_label.setPixmap(QtGui.QPixmap.fromImage(self.status_image_stopped))
|
||||||
@ -110,31 +116,49 @@ class ServerStatus(QtGui.QVBoxLayout):
|
|||||||
self.server_button.setText(strings._('gui_please_wait'))
|
self.server_button.setText(strings._('gui_please_wait'))
|
||||||
|
|
||||||
def server_button_clicked(self):
|
def server_button_clicked(self):
|
||||||
|
"""
|
||||||
|
Toggle starting or stopping the server.
|
||||||
|
"""
|
||||||
if self.status == self.STATUS_STOPPED:
|
if self.status == self.STATUS_STOPPED:
|
||||||
self.start_server()
|
self.start_server()
|
||||||
elif self.status == self.STATUS_STARTED:
|
elif self.status == self.STATUS_STARTED:
|
||||||
self.stop_server()
|
self.stop_server()
|
||||||
|
|
||||||
def start_server(self):
|
def start_server(self):
|
||||||
|
"""
|
||||||
|
Start the server.
|
||||||
|
"""
|
||||||
self.status = self.STATUS_WORKING
|
self.status = self.STATUS_WORKING
|
||||||
self.update()
|
self.update()
|
||||||
self.server_started.emit()
|
self.server_started.emit()
|
||||||
|
|
||||||
def start_server_finished(self):
|
def start_server_finished(self):
|
||||||
|
"""
|
||||||
|
The server has finished starting.
|
||||||
|
"""
|
||||||
self.status = self.STATUS_STARTED
|
self.status = self.STATUS_STARTED
|
||||||
self.copy_url()
|
self.copy_url()
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def stop_server(self):
|
def stop_server(self):
|
||||||
|
"""
|
||||||
|
Stop the server.
|
||||||
|
"""
|
||||||
self.status = self.STATUS_WORKING
|
self.status = self.STATUS_WORKING
|
||||||
self.update()
|
self.update()
|
||||||
self.server_stopped.emit()
|
self.server_stopped.emit()
|
||||||
|
|
||||||
def stop_server_finished(self):
|
def stop_server_finished(self):
|
||||||
|
"""
|
||||||
|
The server has finished stopping.
|
||||||
|
"""
|
||||||
self.status = self.STATUS_STOPPED
|
self.status = self.STATUS_STOPPED
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def copy_url(self):
|
def copy_url(self):
|
||||||
|
"""
|
||||||
|
Copy the onionshare URL to the clipboard.
|
||||||
|
"""
|
||||||
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)
|
||||||
|
|
||||||
if platform.system() == 'Windows':
|
if platform.system() == 'Windows':
|
||||||
|
Loading…
x
Reference in New Issue
Block a user