diff --git a/install/onionshare.nsi b/install/onionshare.nsi index e5f989bb..55ecdbcb 100644 --- a/install/onionshare.nsi +++ b/install/onionshare.nsi @@ -201,6 +201,7 @@ Section "install" File "${BINPATH}\share\html\index.html" SetOutPath "$INSTDIR\share\images" + File "${BINPATH}\share\images\favicon.ico" File "${BINPATH}\share\images\file_delete.png" File "${BINPATH}\share\images\logo.png" File "${BINPATH}\share\images\logo_transparent.png" @@ -209,7 +210,8 @@ Section "install" File "${BINPATH}\share\images\server_stopped.png" File "${BINPATH}\share\images\server_working.png" File "${BINPATH}\share\images\settings.png" - File "${BINPATH}\share\images\settings_inactive.png" + File "${BINPATH}\share\images\web_file.png" + File "${BINPATH}\share\images\web_folder.png" SetOutPath "$INSTDIR\share\locale" File "${BINPATH}\share\locale\cs.json" @@ -380,6 +382,7 @@ FunctionEnd Delete "$INSTDIR\share\html\404.html" Delete "$INSTDIR\share\html\denied.html" Delete "$INSTDIR\share\html\index.html" + Delete "$INSTDIR\share\images\favicon.ico" Delete "$INSTDIR\share\images\file_delete.png" Delete "$INSTDIR\share\images\logo.png" Delete "$INSTDIR\share\images\logo_transparent.png" @@ -388,7 +391,8 @@ FunctionEnd Delete "$INSTDIR\share\images\server_stopped.png" Delete "$INSTDIR\share\images\server_working.png" Delete "$INSTDIR\share\images\settings.png" - Delete "$INSTDIR\share\images\settings_inactive.png" + Delete "$INSTDIR\share\images\web_file.png" + Delete "$INSTDIR\share\images\web_folder.png" Delete "$INSTDIR\share\license.txt" Delete "$INSTDIR\share\locale\cs.json" Delete "$INSTDIR\share\locale\de.json" diff --git a/onionshare/web.py b/onionshare/web.py index 3eef67c7..7d1b72e8 100644 --- a/onionshare/web.py +++ b/onionshare/web.py @@ -26,6 +26,7 @@ import queue import socket import sys import tempfile +import base64 from distutils.version import LooseVersion as Version from urllib.request import urlopen @@ -125,6 +126,12 @@ def add_request(request_type, path, data=None): }) +# Load and base64 encode images to pass into templates +favicon_b64 = base64.b64encode(open(common.get_resource_path('images/favicon.ico'), 'rb').read()).decode() +logo_b64 = base64.b64encode(open(common.get_resource_path('images/logo.png'), 'rb').read()).decode() +folder_b64 = base64.b64encode(open(common.get_resource_path('images/web_folder.png'), 'rb').read()).decode() +file_b64 = base64.b64encode(open(common.get_resource_path('images/web_file.png'), 'rb').read()).decode() + slug = None @@ -206,7 +213,10 @@ def index(slug_candidate): global stay_open, download_in_progress deny_download = not stay_open and download_in_progress if deny_download: - r = make_response(render_template_string(open(common.get_resource_path('html/denied.html')).read())) + r = make_response(render_template_string( + open(common.get_resource_path('html/denied.html')).read(), + favicon_b64=favicon_b64 + )) for header, value in security_headers: r.headers.set(header, value) return r @@ -215,6 +225,10 @@ def index(slug_candidate): r = make_response(render_template_string( open(common.get_resource_path('html/index.html')).read(), + favicon_b64=favicon_b64, + logo_b64=logo_b64, + folder_b64=folder_b64, + file_b64=file_b64, slug=slug, file_info=file_info, filename=os.path.basename(zip_filename), @@ -243,7 +257,10 @@ def download(slug_candidate): global stay_open, download_in_progress, done deny_download = not stay_open and download_in_progress if deny_download: - r = make_response(render_template_string(open(common.get_resource_path('html/denied.html')).read())) + r = make_response(render_template_string( + open(common.get_resource_path('html/denied.html')).read(), + favicon_b64=favicon_b64 + )) for header,value in security_headers: r.headers.set(header, value) return r @@ -355,7 +372,10 @@ def page_not_found(e): force_shutdown() print(strings._('error_rate_limit')) - r = make_response(render_template_string(open(common.get_resource_path('html/404.html')).read()), 404) + r = make_response(render_template_string( + open(common.get_resource_path('html/404.html')).read(), + favicon_b64=favicon_b64 + ), 404) for header, value in security_headers: r.headers.set(header, value) return r diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index d52fd017..b097e501 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -145,7 +145,8 @@ class FileList(QtWidgets.QListWidget): count = len(event.mimeData().urls()) self.drop_count.setText('+{}'.format(count)) - self.drop_count.setGeometry(self.width() - 60, self.height() - 40, 50, 30) + size_hint = self.drop_count.sizeHint() + self.drop_count.setGeometry(self.width() - size_hint.width() - 10, self.height() - size_hint.height() - 10, size_hint.width(), size_hint.height()) self.drop_count.show() event.accept() else: @@ -207,15 +208,24 @@ class FileList(QtWidgets.QListWidget): icon = ip.icon(fileinfo) if os.path.isfile(filename): - size = common.human_readable_filesize(fileinfo.size()) + size_bytes = fileinfo.size() + size_readable = common.human_readable_filesize(size_bytes) else: - size = common.human_readable_filesize(common.dir_size(filename)) - item_name = '{0:s} ({1:s})'.format(basename, size) + size_bytes = common.dir_size(filename) + size_readable = common.human_readable_filesize(size_bytes) # Create a new item - item = QtWidgets.QListWidgetItem(item_name) - item.setToolTip(size) + item = QtWidgets.QListWidgetItem() item.setIcon(icon) + item.size_bytes = size_bytes + + # Item's name and size labels + item_name = QtWidgets.QLabel(basename) + item_name.setWordWrap(False) + item_name.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Fixed) + item_name.setStyleSheet('QLabel { color: #000000; font-size: 13px; }') + item_size = QtWidgets.QLabel(size_readable) + item_size.setStyleSheet('QLabel { color: #666666; font-size: 11px; }') # Item's delete button def delete_item(): @@ -229,16 +239,22 @@ class FileList(QtWidgets.QListWidget): item.item_button.setFlat(True) item.item_button.setIcon( QtGui.QIcon(common.get_resource_path('images/file_delete.png')) ) item.item_button.clicked.connect(delete_item) + item.item_button.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) - # Create an item widget to display on the item - item_widget_layout = QtWidgets.QHBoxLayout() - item_widget_layout.addStretch() - item_widget_layout.addWidget(item.item_button) - item_widget = QtWidgets.QWidget() - item_widget.setLayout(item_widget_layout) + # Create the item's widget and layouts + item_vlayout = QtWidgets.QVBoxLayout() + item_vlayout.addWidget(item_name) + item_vlayout.addWidget(item_size) + item_hlayout = QtWidgets.QHBoxLayout() + item_hlayout.addLayout(item_vlayout) + item_hlayout.addWidget(item.item_button) + widget = QtWidgets.QWidget() + widget.setLayout(item_hlayout) + + item.setSizeHint(widget.sizeHint()) self.addItem(item) - self.setItemWidget(item, item_widget) + self.setItemWidget(item, widget) self.files_updated.emit() @@ -252,12 +268,17 @@ class FileSelection(QtWidgets.QVBoxLayout): super(FileSelection, self).__init__() self.server_on = False - # file list + # Info label + self.info_label = QtWidgets.QLabel() + self.info_label.setStyleSheet('QLabel { font-size: 12px; color: #666666; }') + + # File list self.file_list = FileList() self.file_list.currentItemChanged.connect(self.update) self.file_list.files_dropped.connect(self.update) + self.file_list.files_updated.connect(self.update) - # buttons + # Buttons self.add_button = QtWidgets.QPushButton(strings._('gui_add', True)) self.add_button.clicked.connect(self.add) self.delete_button = QtWidgets.QPushButton(strings._('gui_delete', True)) @@ -267,7 +288,8 @@ class FileSelection(QtWidgets.QVBoxLayout): button_layout.addWidget(self.add_button) button_layout.addWidget(self.delete_button) - # add the widgets + # Add the widgets + self.addWidget(self.info_label) self.addWidget(self.file_list) self.addLayout(button_layout) @@ -277,6 +299,20 @@ class FileSelection(QtWidgets.QVBoxLayout): """ Update the GUI elements based on the current state. """ + # Update the info label + file_count = self.file_list.count() + if file_count == 0: + self.info_label.hide() + else: + total_size_bytes = 0 + for index in range(self.file_list.count()): + item = self.file_list.item(index) + total_size_bytes += item.size_bytes + total_size_readable = common.human_readable_filesize(total_size_bytes) + + self.info_label.setText(strings._('gui_file_info', True).format(file_count, total_size_readable)) + self.info_label.show() + # All buttons should be hidden if the server is on if self.server_on: self.add_button.hide() diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 99cbb479..946000e9 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -156,16 +156,10 @@ class OnionShareGui(QtWidgets.QMainWindow): self.server_share_status_label.setStyleSheet('QLabel { font-style: italic; color: #666666; padding: 2px; }') self.status_bar.insertWidget(0, self.server_share_status_label) - # Persistent URL notification - self.persistent_url_label = QtWidgets.QLabel(strings._('persistent_url_in_use', True)) - self.persistent_url_label.setStyleSheet('font-weight: bold; color: #333333;') - self.persistent_url_label.hide() - # Primary action layout primary_action_layout = QtWidgets.QVBoxLayout() primary_action_layout.addWidget(self.server_status) primary_action_layout.addWidget(self.filesize_warning) - primary_action_layout.addWidget(self.persistent_url_label) primary_action_layout.addWidget(self.downloads_container) self.primary_action = QtWidgets.QWidget() self.primary_action.setLayout(primary_action_layout) @@ -204,7 +198,6 @@ class OnionShareGui(QtWidgets.QMainWindow): self.check_for_updates() def update_primary_action(self): - common.log('OnionShareGui', 'update_primary_action') # Resize window self.adjustSize() @@ -426,9 +419,6 @@ class OnionShareGui(QtWidgets.QMainWindow): self.stop_server() self.start_server_error(strings._('gui_server_started_after_timeout')) - if self.settings.get('save_private_key'): - self.persistent_url_label.show() - def start_server_error(self, error): """ If there's an error when trying to start the onion service @@ -460,7 +450,6 @@ class OnionShareGui(QtWidgets.QMainWindow): # Remove ephemeral service, but don't disconnect from Tor self.onion.cleanup(stop_tor=False) self.filesize_warning.hide() - self.persistent_url_label.hide() self.stop_server_finished.emit() self.set_server_active(False) @@ -599,11 +588,10 @@ class OnionShareGui(QtWidgets.QMainWindow): """ Disable the Settings button while an OnionShare server is active. """ - self.settings_button.setEnabled(not active) if active: - self.settings_button.setIcon( QtGui.QIcon(common.get_resource_path('images/settings_inactive.png')) ) + self.settings_button.hide() else: - self.settings_button.setIcon( QtGui.QIcon(common.get_resource_path('images/settings.png')) ) + self.settings_button.show() # Disable settings menu action when server is active self.settingsAction.setEnabled(not active) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 79fb4d67..5b6fdd52 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -127,10 +127,15 @@ class ServerStatus(QtWidgets.QWidget): if self.status == self.STATUS_STARTED: self.url_description.show() - if self.settings.get('close_after_first_download'): + if self.settings.get('save_private_key'): + self.url_label.setText(strings._('gui_url_label_persistent', True)) + self.url_label.setToolTip(strings._('gui_url_persistence_warning', True)) + elif self.settings.get('close_after_first_download'): self.url_label.setText(strings._('gui_url_label_one_time', True)) + self.url_label.setToolTip('') else: self.url_label.setText(strings._('gui_url_label', True)) + self.url_label.setToolTip('') self.url_label.show() self.url.setText('http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)) diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index d105ea42..ba9b26ed 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -86,12 +86,14 @@ class SettingsDialog(QtWidgets.QDialog): stealth_details.setWordWrap(True) stealth_details.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction) stealth_details.setOpenExternalLinks(True) + stealth_details.setMinimumSize(stealth_details.sizeHint()) self.stealth_checkbox = QtWidgets.QCheckBox() self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked) self.stealth_checkbox.setText(strings._("gui_settings_stealth_option", True)) hidservauth_details = QtWidgets.QLabel(strings._('gui_settings_stealth_hidservauth_string', True)) hidservauth_details.setWordWrap(True) + hidservauth_details.setMinimumSize(hidservauth_details.sizeHint()) hidservauth_details.hide() self.hidservauth_copy_button = QtWidgets.QPushButton(strings._('gui_copy_hidservauth', True)) diff --git a/share/html/404.html b/share/html/404.html index 2e2b06f0..09d0fc3c 100644 --- a/share/html/404.html +++ b/share/html/404.html @@ -2,6 +2,7 @@
{{ filesize_human }} (compressed)
-This zip file contains the following contents:
+ +Type | -Name | +Filename | Size | +|
---|---|---|---|---|
- | {{ info.basename }} | ++ + {{ info.basename }} + | {{ info.size_human }} | +|
- | {{ info.basename }} | ++ + {{ info.basename }} + | {{ info.size_human }} | +