diff options
52 files changed, 320 insertions, 170 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index 3be131b3..175595f3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,8 +1,3 @@ -# To run the tests, CircleCI needs these environment variables: -# QT_EMAIL - email address for a Qt account -# QT_PASSWORD - password for a Qt account -# (Unfortunately you can't install Qt without logging in.) - version: 2 workflows: version: 2 diff --git a/cli/onionshare_cli/__init__.py b/cli/onionshare_cli/__init__.py index a359f770..4bc00929 100644 --- a/cli/onionshare_cli/__init__.py +++ b/cli/onionshare_cli/__init__.py @@ -201,15 +201,6 @@ def main(cwd=None): disable_csp = bool(args.disable_csp) verbose = bool(args.verbose) - if receive: - mode = "receive" - elif website: - mode = "website" - elif chat: - mode = "chat" - else: - mode = "share" - # Verbose mode? common.verbose = verbose @@ -223,16 +214,26 @@ def main(cwd=None): if persistent_filename: mode_settings = ModeSettings(common, persistent_filename) mode_settings.set("persistent", "enabled", True) - mode_settings.set("persistent", "mode", mode) else: mode_settings = ModeSettings(common) + if receive: + mode = "receive" + elif website: + mode = "website" + elif chat: + mode = "chat" + else: + mode = "share" + if mode_settings.just_created: # This means the mode settings were just created, not loaded from disk mode_settings.set("general", "title", title) mode_settings.set("general", "public", public) mode_settings.set("general", "autostart_timer", autostart_timer) mode_settings.set("general", "autostop_timer", autostop_timer) + if persistent_filename: + mode_settings.set("persistent", "mode", mode) if mode == "share": mode_settings.set("share", "autostop_sharing", autostop_sharing) if mode == "receive": diff --git a/cli/onionshare_cli/web/chat_mode.py b/cli/onionshare_cli/web/chat_mode.py index f6dc2d1a..e92ce385 100644 --- a/cli/onionshare_cli/web/chat_mode.py +++ b/cli/onionshare_cli/web/chat_mode.py @@ -68,15 +68,12 @@ class ChatModeWeb: ) self.web.add_request(self.web.REQUEST_LOAD, request.path) - r = make_response( - render_template( + return render_template( "chat.html", static_url_path=self.web.static_url_path, username=session.get("name"), title=self.web.settings.get("general", "title"), - ) ) - return self.web.add_security_headers(r) @self.web.app.route("/update-session-username", methods=["POST"], provide_automatic_options=False) def update_session_username(): @@ -112,7 +109,7 @@ class ChatModeWeb: success=False, ) ) - return self.web.add_security_headers(r) + return r @self.web.socketio.on("joined", namespace="/chat") def joined(message): diff --git a/cli/onionshare_cli/web/receive_mode.py b/cli/onionshare_cli/web/receive_mode.py index 76abb0a8..6b106d37 100644 --- a/cli/onionshare_cli/web/receive_mode.py +++ b/cli/onionshare_cli/web/receive_mode.py @@ -86,16 +86,13 @@ class ReceiveModeWeb: ) self.web.add_request(self.web.REQUEST_LOAD, request.path) - r = make_response( - render_template( - "receive.html", - static_url_path=self.web.static_url_path, - disable_text=self.web.settings.get("receive", "disable_text"), - disable_files=self.web.settings.get("receive", "disable_files"), - title=self.web.settings.get("general", "title"), - ) + return render_template( + "receive.html", + static_url_path=self.web.static_url_path, + disable_text=self.web.settings.get("receive", "disable_text"), + disable_files=self.web.settings.get("receive", "disable_files"), + title=self.web.settings.get("general", "title") ) - return self.web.add_security_headers(r) @self.web.app.route("/upload", methods=["POST"], provide_automatic_options=False) def upload(ajax=False): @@ -222,12 +219,11 @@ class ReceiveModeWeb: ) else: # It was the last upload and the timer ran out - r = make_response( + return make_response( render_template("thankyou.html"), static_url_path=self.web.static_url_path, title=self.web.settings.get("general", "title"), ) - return self.web.add_security_headers(r) @self.web.app.route("/upload-ajax", methods=["POST"], provide_automatic_options=False) def upload_ajax_public(): diff --git a/cli/onionshare_cli/web/send_base_mode.py b/cli/onionshare_cli/web/send_base_mode.py index e448d2dd..27de598a 100644 --- a/cli/onionshare_cli/web/send_base_mode.py +++ b/cli/onionshare_cli/web/send_base_mode.py @@ -149,10 +149,9 @@ class SendBaseModeWeb: # If filesystem_path is None, this is the root directory listing files, dirs = self.build_directory_listing(path, filenames, filesystem_path) - r = self.directory_listing_template( + return self.directory_listing_template( path, files, dirs, breadcrumbs, breadcrumbs_leaf ) - return self.web.add_security_headers(r) def build_directory_listing(self, path, filenames, filesystem_path): files = [] @@ -286,7 +285,6 @@ class SendBaseModeWeb: "filename*": "UTF-8''%s" % url_quote(basename), } r.headers.set("Content-Disposition", "inline", **filename_dict) - r = self.web.add_security_headers(r) (content_type, _) = mimetypes.guess_type(basename, strict=False) if content_type is not None: r.headers.set("Content-Type", content_type) diff --git a/cli/onionshare_cli/web/share_mode.py b/cli/onionshare_cli/web/share_mode.py index 51ddd674..8ac4055e 100644 --- a/cli/onionshare_cli/web/share_mode.py +++ b/cli/onionshare_cli/web/share_mode.py @@ -25,7 +25,7 @@ import sys import tempfile import zipfile import mimetypes -from datetime import datetime +from datetime import datetime, timezone from flask import Response, request, render_template, make_response, abort from unidecode import unidecode from werkzeug.http import parse_date, http_date @@ -127,7 +127,7 @@ class ShareModeWeb(SendBaseModeWeb): self.download_etag = None self.gzip_etag = None - self.last_modified = datetime.utcnow() + self.last_modified = datetime.now(tz=timezone.utc) def define_routes(self): """ @@ -149,8 +149,7 @@ class ShareModeWeb(SendBaseModeWeb): and self.download_in_progress ) if deny_download: - r = make_response(render_template("denied.html")) - return self.web.add_security_headers(r) + return render_template("denied.html") # If download is allowed to continue, serve download page if self.should_use_gzip(): @@ -172,8 +171,7 @@ class ShareModeWeb(SendBaseModeWeb): and self.download_in_progress ) if deny_download: - r = make_response(render_template("denied.html")) - return self.web.add_security_headers(r) + return render_template("denied.html") # Prepare some variables to use inside generate() function below # which is outside of the request context @@ -232,7 +230,6 @@ class ShareModeWeb(SendBaseModeWeb): "filename*": "UTF-8''%s" % url_quote(basename), } r.headers.set("Content-Disposition", "attachment", **filename_dict) - r = self.web.add_security_headers(r) # guess content type (content_type, _) = mimetypes.guess_type(basename, strict=False) if content_type is not None: @@ -288,6 +285,8 @@ class ShareModeWeb(SendBaseModeWeb): if_unmod = request.headers.get("If-Unmodified-Since") if if_unmod: if_date = parse_date(if_unmod) + if if_date and not if_date.tzinfo: + if_date = if_date.replace(tzinfo=timezone.utc) # Compatible with Flask < 2.0.0 if if_date and if_date > last_modified: abort(412) elif range_header is None: diff --git a/cli/onionshare_cli/web/web.py b/cli/onionshare_cli/web/web.py index 0f2dfe7e..e12fccc7 100644 --- a/cli/onionshare_cli/web/web.py +++ b/cli/onionshare_cli/web/web.py @@ -191,6 +191,21 @@ class Web: Common web app routes between all modes. """ + @self.app.after_request + def add_security_headers(r): + """ + Add security headers to a response + """ + for header, value in self.security_headers: + r.headers.set(header, value) + # Set a CSP header unless in website mode and the user has disabled it + if not self.settings.get("website", "disable_csp") or self.mode != "website": + r.headers.set( + "Content-Security-Policy", + "default-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; img-src 'self' data:;", + ) + return r + @self.app.errorhandler(404) def not_found(e): mode = self.get_mode() @@ -232,10 +247,7 @@ class Web: def error403(self): self.add_request(Web.REQUEST_OTHER, request.path) - r = make_response( - render_template("403.html", static_url_path=self.static_url_path), 403 - ) - return self.add_security_headers(r) + return render_template("403.html", static_url_path=self.static_url_path), 403 def error404(self, history_id): mode = self.get_mode() @@ -247,10 +259,7 @@ class Web: ) self.add_request(Web.REQUEST_OTHER, request.path) - r = make_response( - render_template("404.html", static_url_path=self.static_url_path), 404 - ) - return self.add_security_headers(r) + return render_template("404.html", static_url_path=self.static_url_path), 404 def error405(self, history_id): mode = self.get_mode() @@ -262,10 +271,7 @@ class Web: ) self.add_request(Web.REQUEST_OTHER, request.path) - r = make_response( - render_template("405.html", static_url_path=self.static_url_path), 405 - ) - return self.add_security_headers(r) + return render_template("405.html", static_url_path=self.static_url_path), 405 def error500(self, history_id): mode = self.get_mode() @@ -277,24 +283,7 @@ class Web: ) self.add_request(Web.REQUEST_OTHER, request.path) - r = make_response( - render_template("500.html", static_url_path=self.static_url_path), 500 - ) - return self.add_security_headers(r) - - def add_security_headers(self, r): - """ - Add security headers to a request - """ - for header, value in self.security_headers: - r.headers.set(header, value) - # Set a CSP header unless in website mode and the user has disabled it - if not self.settings.get("website", "disable_csp") or self.mode != "website": - r.headers.set( - "Content-Security-Policy", - "default-src 'self'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; img-src 'self' data:;", - ) - return r + return render_template("500.html", static_url_path=self.static_url_path), 500 def _safe_select_jinja_autoescape(self, filename): if filename is None: @@ -372,9 +361,18 @@ class Web: # To stop flask, load http://shutdown:[shutdown_password]@127.0.0.1/[shutdown_password]/shutdown # (We're putting the shutdown_password in the path as well to make routing simpler) if self.running: - requests.get( - f"http://127.0.0.1:{port}/{self.shutdown_password}/shutdown" - ) + try: + requests.get( + f"http://127.0.0.1:{port}/{self.shutdown_password}/shutdown" + ) + except requests.exceptions.ConnectionError as e: + # The way flask-socketio stops a connection when running using + # eventlet is by raising SystemExit to abort all the processes. + # Hence the connections are closed and no response is returned + # to the above request. So I am just catching the ConnectionError + # to check if it was chat mode, in which case it's okay + if self.mode != "chat": + raise e def cleanup(self): """ diff --git a/cli/tests/test_cli_common.py b/cli/tests/test_cli_common.py index 3288e52b..9f113a84 100644 --- a/cli/tests/test_cli_common.py +++ b/cli/tests/test_cli_common.py @@ -169,7 +169,7 @@ class TestGetTorPaths: obfs4proxy_file_path, ) - @pytest.mark.skipif(sys.platform != "Linux", reason="requires Linux") + @pytest.mark.skipif(sys.platform != "linux", reason="requires Linux") def test_get_tor_paths_linux(self, platform_linux, common_obj): ( tor_path, diff --git a/cli/tests/test_cli_settings.py b/cli/tests/test_cli_settings.py index 4c012901..ed8d5bb9 100644 --- a/cli/tests/test_cli_settings.py +++ b/cli/tests/test_cli_settings.py @@ -123,7 +123,7 @@ class TestSettings: "~/Library/Application Support/OnionShare-testdata/onionshare.json" ) - @pytest.mark.skipif(sys.platform != "Linux", reason="requires Linux") + @pytest.mark.skipif(sys.platform != "linux", reason="requires Linux") def test_filename_linux(self, monkeypatch, platform_linux): obj = settings.Settings(common.Common()) assert obj.filename == os.path.expanduser( diff --git a/cli/tests/test_cli_web.py b/cli/tests/test_cli_web.py index f2b1af62..71bfeeeb 100644 --- a/cli/tests/test_cli_web.py +++ b/cli/tests/test_cli_web.py @@ -569,7 +569,7 @@ class TestRangeRequests: assert resp.status_code == 206 - @pytest.mark.skipif(sys.platform != "Linux", reason="requires Linux") + @pytest.mark.skipif(sys.platform != "linux", reason="requires Linux") @check_unsupported("curl", ["--version"]) def test_curl(self, temp_dir, tmpdir, common_obj): web = web_obj(temp_dir, common_obj, "share", 3) @@ -591,7 +591,7 @@ class TestRangeRequests: ] ) - @pytest.mark.skipif(sys.platform != "Linux", reason="requires Linux") + @pytest.mark.skipif(sys.platform != "linux", reason="requires Linux") @check_unsupported("wget", ["--version"]) def test_wget(self, temp_dir, tmpdir, common_obj): web = web_obj(temp_dir, common_obj, "share", 3) diff --git a/desktop/src/onionshare/resources/locale/ar.json b/desktop/src/onionshare/resources/locale/ar.json index 83e88c28..d5bdc9fe 100644 --- a/desktop/src/onionshare/resources/locale/ar.json +++ b/desktop/src/onionshare/resources/locale/ar.json @@ -230,7 +230,7 @@ "gui_settings_website_label": "اعدادات الموقع", "gui_receive_flatpak_data_dir": "بسبب أنت قد ثبّت OnionShare باستخدام Flatpak، يجب عليك حفظ الملفات داخل مُجلد في المسار ~/OnionShare.", "gui_qr_code_dialog_title": "OnionShare رمز الاستجابة السريعة", - "gui_show_url_qr_code": "إظهار رمز الاستجابة السريعة", + "gui_show_qr_code": "إظهار رمز الاستجابة السريعة", "gui_chat_stop_server": "إيقاف خادم الدردشة", "gui_chat_start_server": "ابدأ خادم الدردشة", "gui_file_selection_remove_all": "إزالة الكُل", diff --git a/desktop/src/onionshare/resources/locale/bn.json b/desktop/src/onionshare/resources/locale/bn.json index 0f87ef3f..5ae4e081 100644 --- a/desktop/src/onionshare/resources/locale/bn.json +++ b/desktop/src/onionshare/resources/locale/bn.json @@ -269,7 +269,7 @@ "gui_tab_name_receive": "গ্রহণ", "gui_tab_name_share": "শেয়ার", "gui_qr_code_dialog_title": "অনিওনশেয়ার কিউআর কোড", - "gui_show_url_qr_code": "কিউআর কোড দেখাও", + "gui_show_qr_code": "কিউআর কোড দেখাও", "gui_receive_flatpak_data_dir": "যেহেতু অনিওনশেয়ার ফ্ল্যাটপ্যাক দিয়ে ইন্সটল করেছো, তাই তোমাকে ~/OnionShare এ ফাইল সংরক্ষণ করতে হবে।", "gui_rendezvous_cleanup": "তোমার ফাইলগুলি সফলভাবে স্থানান্তরিত হয়েছে তা নিশ্চিত হয়ে টর সার্কিট বন্ধের অপেক্ষা করা হচ্ছে।\n\nএটি কয়েক মিনিট সময় নিতে পারে।", "gui_open_folder_error": "xdg-open দিয়ে ফোল্ডার খুলতে ব্যর্থ হয়েছে। ফাইলটি এখানে: {}", diff --git a/desktop/src/onionshare/resources/locale/ca.json b/desktop/src/onionshare/resources/locale/ca.json index 6eb57f3b..132c764a 100644 --- a/desktop/src/onionshare/resources/locale/ca.json +++ b/desktop/src/onionshare/resources/locale/ca.json @@ -267,7 +267,7 @@ "gui_new_tab_share_description": "Trieu els fitxers del vostre ordinador que voleu enviar a algú altre. La persona a qui voleu enviar els fitxers haurà d'usar el navegador Tor per a baixar-los del vostre equip.", "gui_qr_code_description": "Escanegeu aquest codi amb un lector de QR, com ara la càmera del telèfon, per a facilitar la compartició de l'adreça de l'OnionShare amb altres.", "gui_qr_code_dialog_title": "Codi QR de l'OnionShare", - "gui_show_url_qr_code": "Mostra el codi QR", + "gui_show_qr_code": "Mostra el codi QR", "gui_receive_flatpak_data_dir": "Com que heu instal·lat l'OnionShare amb el Flatpak, heu de desar els fitxers en una carpeta dins ~/OnionShare.", "gui_chat_stop_server": "Atura el servidor de xat", "gui_chat_start_server": "Inicia el servidor de xat", diff --git a/desktop/src/onionshare/resources/locale/ckb.json b/desktop/src/onionshare/resources/locale/ckb.json index 8289918c..68122adb 100644 --- a/desktop/src/onionshare/resources/locale/ckb.json +++ b/desktop/src/onionshare/resources/locale/ckb.json @@ -27,7 +27,7 @@ "gui_canceled": "Betal bû", "gui_copied_url_title": "Malpera OnionShare kopî bû", "gui_copied_url": "Malpera OnionShare lis ser taxtê kopî bû", - "gui_show_url_qr_code": "QR kod nîşan bide", + "gui_show_qr_code": "QR kod nîşan bide", "gui_qr_code_dialog_title": "OnionShare QR kod", "gui_waiting_to_start": "Pilankirî ye di {} destpê bike. Bitkîne ji bo betal bike.", "gui_please_wait": "Destpê dike...Bitikîne ji bo betal bike.", diff --git a/desktop/src/onionshare/resources/locale/da.json b/desktop/src/onionshare/resources/locale/da.json index d24f70e7..57ff605a 100644 --- a/desktop/src/onionshare/resources/locale/da.json +++ b/desktop/src/onionshare/resources/locale/da.json @@ -269,7 +269,7 @@ "gui_open_folder_error": "Kunne ikke åbne mappen med xdg-open. Filen er her: {}", "gui_qr_code_description": "Skan QR-koden med en QR-læser såsom kameraet i din telefon for at gøre det lettere at dele OnionShare-adressen med andre.", "gui_qr_code_dialog_title": "QR-kode til OnionShare", - "gui_show_url_qr_code": "Vis QR-kode", + "gui_show_qr_code": "Vis QR-kode", "gui_receive_flatpak_data_dir": "Da du installerede OnionShare med Flatpak, så skal du gemme filer til en mappe i ~/OnionShare.", "gui_chat_stop_server": "Stop chatserver", "gui_chat_start_server": "Start chatserver", diff --git a/desktop/src/onionshare/resources/locale/de.json b/desktop/src/onionshare/resources/locale/de.json index 518a7113..c6cb7731 100644 --- a/desktop/src/onionshare/resources/locale/de.json +++ b/desktop/src/onionshare/resources/locale/de.json @@ -273,7 +273,7 @@ "gui_remove": "Entfernen", "gui_new_tab_chat_button": "Anonym chatten", "gui_qr_code_dialog_title": "OnionShare QR-Code", - "gui_show_url_qr_code": "QR-Code anzeigen", + "gui_show_qr_code": "QR-Code anzeigen", "gui_chat_stop_server": "Chatserver stoppen", "gui_chat_start_server": "Chatserver starten", "gui_main_page_chat_button": "Chat starten", diff --git a/desktop/src/onionshare/resources/locale/el.json b/desktop/src/onionshare/resources/locale/el.json index 25649b4b..41cbdf82 100644 --- a/desktop/src/onionshare/resources/locale/el.json +++ b/desktop/src/onionshare/resources/locale/el.json @@ -273,7 +273,7 @@ "gui_new_tab_tooltip": "Άνοιγμα νέας καρτέλας", "gui_new_tab": "Νέα καρτέλα", "gui_qr_code_dialog_title": "Κώδικας QR OnionShare", - "gui_show_url_qr_code": "Προβολή κώδικα QR", + "gui_show_qr_code": "Προβολή κώδικα QR", "gui_file_selection_remove_all": "Αφαίρεση όλων", "gui_remove": "Αφαίρεση", "error_port_not_available": "Η θύρα OnionShare δεν είναι διαθέσιμη", diff --git a/desktop/src/onionshare/resources/locale/en.json b/desktop/src/onionshare/resources/locale/en.json index e7eba1cb..03694947 100644 --- a/desktop/src/onionshare/resources/locale/en.json +++ b/desktop/src/onionshare/resources/locale/en.json @@ -30,10 +30,12 @@ "gui_copied_url": "OnionShare address copied to clipboard", "gui_copied_client_auth_title": "Copied Private Key", "gui_copied_client_auth": "Private Key copied to clipboard", - "gui_show_url_qr_code": "Show QR Code", + "gui_show_qr_code": "Show QR Code", "gui_qr_code_dialog_title": "OnionShare QR Code", "gui_qr_label_url_title": "OnionShare Address", "gui_qr_label_auth_string_title": "Private Key", + "gui_reveal": "Reveal", + "gui_hide": "Hide", "gui_waiting_to_start": "Scheduled to start in {}. Click to cancel.", "gui_please_wait_no_button": "Starting…", "gui_please_wait": "Starting… Click to cancel.", @@ -100,6 +102,9 @@ "gui_url_label_stay_open": "This share will not auto-stop.", "gui_url_label_onetime": "This share will stop after first completion.", "gui_url_label_onetime_and_persistent": "This share will not auto-stop.<br><br>Every subsequent share will reuse the address. (To use one-time addresses, turn off \"Use persistent address\" in the settings.)", + "gui_url_instructions": "First, send the OnionShare address below:", + "gui_url_instructions_public_mode": "Send the OnionShare address below:", + "gui_client_auth_instructions": "Next, send the private key to allow access to your OnionShare service:", "gui_status_indicator_share_stopped": "Ready to share", "gui_status_indicator_share_working": "Starting…", "gui_status_indicator_share_scheduled": "Scheduled…", @@ -211,4 +216,4 @@ "error_port_not_available": "OnionShare port not available", "history_receive_read_message_button": "Read Message", "error_tor_protocol_error": "There was an error with Tor: {}" -} +}
\ No newline at end of file diff --git a/desktop/src/onionshare/resources/locale/es.json b/desktop/src/onionshare/resources/locale/es.json index 7b04a9e2..c9efc51a 100644 --- a/desktop/src/onionshare/resources/locale/es.json +++ b/desktop/src/onionshare/resources/locale/es.json @@ -277,7 +277,7 @@ "gui_open_folder_error": "Fallo al abrir carpeta con xdg-open. El archivo está aquí: {}", "gui_qr_code_description": "Escanea este código QR con un lector QR, como la cámara de tu teléfono para compartir la dirección OnionShare.", "gui_qr_code_dialog_title": "Código QR de OnionShare", - "gui_show_url_qr_code": "Mostrar Código QR", + "gui_show_qr_code": "Mostrar Código QR", "gui_receive_flatpak_data_dir": "Al instalar OnionShare usando Flatpak, debes guardar los archivos en una carpeta en ~/OnionShare.", "gui_chat_stop_server": "Detener servidor de chat", "gui_chat_start_server": "Iniciar servidor de chat", diff --git a/desktop/src/onionshare/resources/locale/fi.json b/desktop/src/onionshare/resources/locale/fi.json index 25fd2a94..37c28dac 100644 --- a/desktop/src/onionshare/resources/locale/fi.json +++ b/desktop/src/onionshare/resources/locale/fi.json @@ -258,7 +258,7 @@ "gui_chat_url_description": "<b>Kuka tahansa</b> tällä Onionshare-osoitteella voi <b>liittyä tähän keskusteluryhmään</b> käyttämällä <b>Tor-selainta</b>: <img src='{}' />", "gui_please_wait_no_button": "Aloitetaan…", "gui_qr_code_dialog_title": "OnionSharen QR-koodi", - "gui_show_url_qr_code": "Näytä QR-koodi", + "gui_show_qr_code": "Näytä QR-koodi", "gui_receive_flatpak_data_dir": "Koska asensin OnionSharen käyttämällä Flatpakia, sinun täytyy tallentaa tiedostot kansioon sijainnissa ~/OnionShare.", "gui_chat_stop_server": "Pysäytä chat-palvelin", "gui_chat_start_server": "Perusta chat-palvelin" diff --git a/desktop/src/onionshare/resources/locale/fr.json b/desktop/src/onionshare/resources/locale/fr.json index 5ac2c6d0..3bb2c838 100644 --- a/desktop/src/onionshare/resources/locale/fr.json +++ b/desktop/src/onionshare/resources/locale/fr.json @@ -272,7 +272,7 @@ "gui_open_folder_error": "Échec d’ouverture du dossier avec xdg-open. Le fichier est ici : {}", "gui_qr_code_description": "Balayez ce code QR avec un lecteur de code QR, tel que l’appareil photo votre appareil, afin de partager plus facilement l’adresse OnionShare avec quelqu’un.", "gui_qr_code_dialog_title": "Code QR d’OnionShare", - "gui_show_url_qr_code": "Afficher le code QR", + "gui_show_qr_code": "Afficher le code QR", "gui_receive_flatpak_data_dir": "Comme vous avez installé OnionShare grâce à Flatpak, vous devez enregistrer vos fichiers dans un sous-dossier de ~/OnionShare.", "gui_chat_stop_server": "Arrêter le serveur de dialogue en ligne", "gui_chat_start_server": "Démarrer le serveur de dialogue en ligne", diff --git a/desktop/src/onionshare/resources/locale/gl.json b/desktop/src/onionshare/resources/locale/gl.json index 758c3137..97a99ff7 100644 --- a/desktop/src/onionshare/resources/locale/gl.json +++ b/desktop/src/onionshare/resources/locale/gl.json @@ -27,7 +27,7 @@ "gui_canceled": "Cancelado", "gui_copied_url_title": "Enderezo de OnionShare copiado", "gui_copied_url": "Enderezo OnionShare copiado ó portapapeis", - "gui_show_url_qr_code": "Mostrar código QR", + "gui_show_qr_code": "Mostrar código QR", "gui_qr_code_dialog_title": "Código QR OnionShare", "gui_waiting_to_start": "Programado para comezar en {}. Fai clic para cancelar.", "gui_please_wait": "Iniciando... Fai click para cancelar.", diff --git a/desktop/src/onionshare/resources/locale/hi.json b/desktop/src/onionshare/resources/locale/hi.json index 7cc230c8..9351bc6b 100644 --- a/desktop/src/onionshare/resources/locale/hi.json +++ b/desktop/src/onionshare/resources/locale/hi.json @@ -181,7 +181,7 @@ "incorrect_password": "पासवर्ड गलत है", "gui_settings_individual_downloads_label": "विशिष्ट फाइलों के डाउनलोड को मंजूरी देने के लिए अचिन्हित करें", "gui_settings_csp_header_disabled_option": "सामग्री सुरक्षा नियम हेडर को अक्षम करें", - "gui_show_url_qr_code": "क्यूआर कोड दिखाएं", + "gui_show_qr_code": "क्यूआर कोड दिखाएं", "gui_chat_stop_server": "चैट सर्वर बंद करें", "gui_chat_start_server": "चैट सर्वर शुरू करें", "gui_file_selection_remove_all": "सभी हटाएं", diff --git a/desktop/src/onionshare/resources/locale/hr.json b/desktop/src/onionshare/resources/locale/hr.json index efc24f4e..c49e01d2 100644 --- a/desktop/src/onionshare/resources/locale/hr.json +++ b/desktop/src/onionshare/resources/locale/hr.json @@ -215,7 +215,7 @@ "gui_tab_name_website": "Web-stranica", "gui_tab_name_share": "Dijeli", "gui_qr_code_dialog_title": "OnionShare QR-kod", - "gui_show_url_qr_code": "Prikaži QR-kod", + "gui_show_qr_code": "Prikaži QR-kod", "gui_file_selection_remove_all": "Ukloni sve", "gui_remove": "Ukloni", "gui_main_page_chat_button": "Pokreni razgovor", diff --git a/desktop/src/onionshare/resources/locale/id.json b/desktop/src/onionshare/resources/locale/id.json index 55af7794..36b8c41a 100644 --- a/desktop/src/onionshare/resources/locale/id.json +++ b/desktop/src/onionshare/resources/locale/id.json @@ -248,7 +248,7 @@ "systray_share_started_title": "Berbagi dimulai", "gui_color_mode_changed_notice": "Mulai ulang OnionShare agar mode warna baru diterapkan.", "gui_qr_code_dialog_title": "Kode QR OnionShare", - "gui_show_url_qr_code": "Tampilkan kode QR", + "gui_show_qr_code": "Tampilkan kode QR", "error_port_not_available": "Port OnionShare tidak tersedia", "gui_chat_stop_server": "Hentikan server obrolan", "gui_chat_start_server": "Mulai server obrolan", diff --git a/desktop/src/onionshare/resources/locale/is.json b/desktop/src/onionshare/resources/locale/is.json index 91600653..c5f206df 100644 --- a/desktop/src/onionshare/resources/locale/is.json +++ b/desktop/src/onionshare/resources/locale/is.json @@ -270,7 +270,7 @@ "gui_chat_stop_server_autostop_timer": "Stöðva spjallþjón ({})", "gui_qr_code_dialog_title": "QR-kóði OnionShare", "gui_file_selection_remove_all": "Fjarlægja allt", - "gui_show_url_qr_code": "Birta QR-kóða", + "gui_show_qr_code": "Birta QR-kóða", "gui_new_tab_chat_button": "Spjalla nafnlaust", "gui_main_page_chat_button": "Hefja spjall", "gui_main_page_website_button": "Hefja hýsingu", diff --git a/desktop/src/onionshare/resources/locale/it.json b/desktop/src/onionshare/resources/locale/it.json index 56463b14..9063f0f0 100644 --- a/desktop/src/onionshare/resources/locale/it.json +++ b/desktop/src/onionshare/resources/locale/it.json @@ -245,7 +245,7 @@ "gui_new_tab": "Nuova Scheda", "gui_open_folder_error": "Impossibile aprire la cartella con xdg-open. Il file è qui: {}", "gui_qr_code_dialog_title": "OnionShare QR Code", - "gui_show_url_qr_code": "Mostra QR Code", + "gui_show_qr_code": "Mostra QR Code", "gui_receive_flatpak_data_dir": "Dato che hai installato OnionShare usando Flatpak, devi salvare i file nella cartella ~/OnionShare.", "gui_chat_stop_server": "Arresta il server della chat", "gui_file_selection_remove_all": "Rimuovi tutto", diff --git a/desktop/src/onionshare/resources/locale/ja.json b/desktop/src/onionshare/resources/locale/ja.json index 6f7d0cff..3d20f966 100644 --- a/desktop/src/onionshare/resources/locale/ja.json +++ b/desktop/src/onionshare/resources/locale/ja.json @@ -266,7 +266,7 @@ "gui_open_folder_error": "xdg-openでフォルダー開くの失敗。ファイルはここにあります: {}", "gui_qr_code_description": "より簡単にOnionShareアドレスを共有するため、スマホのカメラなどのQRリーダーでこのコードをスキャンして下さい。", "gui_qr_code_dialog_title": "OnionShareのQRコード", - "gui_show_url_qr_code": "QRコードを表示", + "gui_show_qr_code": "QRコードを表示", "gui_receive_flatpak_data_dir": "FlatpakでOnionShareをインストールしたため、ファイルを~/OnionShareの中のフォルダーに保存しなければなりません。", "gui_chat_stop_server": "チャットサーバーを停止", "gui_chat_start_server": "チャットサーバーを始動", diff --git a/desktop/src/onionshare/resources/locale/lt.json b/desktop/src/onionshare/resources/locale/lt.json index 0623a8b1..8c88f066 100644 --- a/desktop/src/onionshare/resources/locale/lt.json +++ b/desktop/src/onionshare/resources/locale/lt.json @@ -205,7 +205,7 @@ "gui_file_selection_remove_all": "Šalinti visus", "gui_remove": "Šalinti", "gui_qr_code_dialog_title": "OnionShare QR kodas", - "gui_show_url_qr_code": "Rodyti QR kodą", + "gui_show_qr_code": "Rodyti QR kodą", "gui_open_folder_error": "Nepavyko atverti aplanko naudojant xdg-open. Failas yra čia: {}", "gui_chat_stop_server": "Sustabdyti pokalbių serverį", "gui_chat_start_server": "Pradėti pokalbių serverį", diff --git a/desktop/src/onionshare/resources/locale/nb_NO.json b/desktop/src/onionshare/resources/locale/nb_NO.json index 33825216..8ab21432 100644 --- a/desktop/src/onionshare/resources/locale/nb_NO.json +++ b/desktop/src/onionshare/resources/locale/nb_NO.json @@ -279,7 +279,7 @@ "gui_new_tab_chat_button": "Sludre anonymt", "gui_qr_code_description": "Skann denne QR-koden med en QR-kodeleser (f.eks. kameraprogrammet på enheten din) for enklere deling av OnionShare-adressen med noen.", "gui_qr_code_dialog_title": "OnionShare-QR-kode", - "gui_show_url_qr_code": "Vis QR-kode", + "gui_show_qr_code": "Vis QR-kode", "gui_main_page_chat_button": "Start sludring", "gui_main_page_website_button": "Start vertsjening", "gui_main_page_receive_button": "Start mottak", diff --git a/desktop/src/onionshare/resources/locale/nl.json b/desktop/src/onionshare/resources/locale/nl.json index ed70622a..2ae1351a 100644 --- a/desktop/src/onionshare/resources/locale/nl.json +++ b/desktop/src/onionshare/resources/locale/nl.json @@ -274,7 +274,7 @@ "gui_new_tab": "Nieuw tabblad", "gui_open_folder_error": "Niet gelukt om de map te openen met xdg-open. Het bestand staat hier: {}", "gui_qr_code_dialog_title": "OnionShare QR Code", - "gui_show_url_qr_code": "Toon QR Code", + "gui_show_qr_code": "Toon QR Code", "gui_receive_flatpak_data_dir": "Omdat je OnionShare installeerde met Flatpak, moet je bestanden opslaan in een folder in ~/OnionShare.", "gui_chat_stop_server": "Stop chat server", "gui_chat_start_server": "Start chat server", diff --git a/desktop/src/onionshare/resources/locale/pl.json b/desktop/src/onionshare/resources/locale/pl.json index 1a2f17a6..f8c2bc53 100644 --- a/desktop/src/onionshare/resources/locale/pl.json +++ b/desktop/src/onionshare/resources/locale/pl.json @@ -246,7 +246,7 @@ "gui_open_folder_error": "Nie udało się otworzyć folderu za pomocą xdg-open. Plik jest tutaj: {}", "gui_qr_code_description": "Zeskanuj ten kod QR za pomocą czytnika QR, takiego jak aparat w telefonie, aby łatwiej udostępnić komuś adres OnionShare.", "gui_qr_code_dialog_title": "Kod QR OnionShare", - "gui_show_url_qr_code": "Pokaż kod QR", + "gui_show_qr_code": "Pokaż kod QR", "gui_receive_flatpak_data_dir": "Ponieważ zainstalowałeś OnionShare przy użyciu Flatpak, musisz zapisywać pliki w folderze w ~ / OnionShare.", "gui_chat_stop_server": "Zatrzymaj serwer czatu", "gui_chat_start_server": "Uruchom serwer czatu", diff --git a/desktop/src/onionshare/resources/locale/pt_BR.json b/desktop/src/onionshare/resources/locale/pt_BR.json index ebbef428..07e6c7d2 100644 --- a/desktop/src/onionshare/resources/locale/pt_BR.json +++ b/desktop/src/onionshare/resources/locale/pt_BR.json @@ -269,7 +269,7 @@ "gui_open_folder_error": "Falha ao abrir a pasta com xdg-open. O arquivo está aqui: {}", "gui_qr_code_description": "Leia este código QR com um leitor, como a câmera do seu celular, para compartilhar mais facilmente o endereço OnionShare com alguém.", "gui_qr_code_dialog_title": "Código QR OnionShare", - "gui_show_url_qr_code": "Mostrar código QR", + "gui_show_qr_code": "Mostrar código QR", "gui_receive_flatpak_data_dir": "Como você instalou o OnionShare usando o Flatpak, você deve salvar os arquivos em uma pasta em ~ / OnionShare.", "gui_chat_stop_server": "Parar o servidor de conversas", "gui_chat_start_server": "Iniciar um servidor de conversas", diff --git a/desktop/src/onionshare/resources/locale/pt_PT.json b/desktop/src/onionshare/resources/locale/pt_PT.json index a699a5f3..f5b092c3 100644 --- a/desktop/src/onionshare/resources/locale/pt_PT.json +++ b/desktop/src/onionshare/resources/locale/pt_PT.json @@ -267,7 +267,7 @@ "gui_new_tab_chat_button": "Converse Anónimamente", "gui_open_folder_error": "Falhou a abrir a pasta com xdc-open. O ficheiro está aqui: {}", "gui_qr_code_dialog_title": "OnionShare Código QR", - "gui_show_url_qr_code": "Mostrar código QR", + "gui_show_qr_code": "Mostrar código QR", "gui_receive_flatpak_data_dir": "Como instalou o OnionShare utilizando Flatpak, deve guardar os ficheiros na pasta ~/OnionShare.", "gui_chat_stop_server": "Para servidor de conversa", "gui_chat_start_server": "Começar servidor de conversa", diff --git a/desktop/src/onionshare/resources/locale/ru.json b/desktop/src/onionshare/resources/locale/ru.json index 967e87b6..a3b57fb4 100644 --- a/desktop/src/onionshare/resources/locale/ru.json +++ b/desktop/src/onionshare/resources/locale/ru.json @@ -266,7 +266,7 @@ "gui_open_folder_error": "Ошибка при попытке открыть папку с помощью xdg-open. Файл находится здесь: {}", "gui_qr_code_description": "Сканируйте этот QR-код считывающим устройством, например камерой Вашего телефона, чтобы было удобнее поделиться ссылкой OnionShare с кем либо.", "gui_qr_code_dialog_title": "Код QR OnionShare", - "gui_show_url_qr_code": "Показать код QR", + "gui_show_qr_code": "Показать код QR", "gui_receive_flatpak_data_dir": "Так как Вы установили OnionShare с помощью Flatpak, Вы должны сохранять файлы в папке ~/OnionShare.", "gui_chat_stop_server": "Остановить сервер чата", "gui_chat_start_server": "Запустить сервер чата", diff --git a/desktop/src/onionshare/resources/locale/si.json b/desktop/src/onionshare/resources/locale/si.json index cb15cc72..32417022 100644 --- a/desktop/src/onionshare/resources/locale/si.json +++ b/desktop/src/onionshare/resources/locale/si.json @@ -27,7 +27,7 @@ "gui_canceled": "", "gui_copied_url_title": "", "gui_copied_url": "", - "gui_show_url_qr_code": "", + "gui_show_qr_code": "", "gui_qr_code_dialog_title": "", "gui_waiting_to_start": "", "gui_please_wait": "", diff --git a/desktop/src/onionshare/resources/locale/sk.json b/desktop/src/onionshare/resources/locale/sk.json index 8ee83a28..841b156e 100644 --- a/desktop/src/onionshare/resources/locale/sk.json +++ b/desktop/src/onionshare/resources/locale/sk.json @@ -27,7 +27,7 @@ "gui_canceled": "Zrušené", "gui_copied_url_title": "Skopírovaná OnionShare adresa", "gui_copied_url": "OnionShare adresa bola skopírovaná do schránky", - "gui_show_url_qr_code": "Zobraziť QR kód", + "gui_show_qr_code": "Zobraziť QR kód", "gui_qr_code_dialog_title": "OnionShare QR kód", "gui_qr_code_description": "Naskenujte tento QR kód pomocou čítačky QR, napríklad fotoaparátom na telefóne, aby ste mohli jednoduchšie zdieľať adresu OnionShare s niekým.", "gui_waiting_to_start": "Naplánované spustenie o {}. Kliknutím zrušíte.", diff --git a/desktop/src/onionshare/resources/locale/sr_Latn.json b/desktop/src/onionshare/resources/locale/sr_Latn.json index da986f48..0241a140 100644 --- a/desktop/src/onionshare/resources/locale/sr_Latn.json +++ b/desktop/src/onionshare/resources/locale/sr_Latn.json @@ -216,7 +216,7 @@ "gui_open_folder_error": "Neuspelo otvaranje fascikle sa xdg-open. Fajl je ovde: {}", "gui_chat_url_description": "<b>Bilo ko</b> sa ovom OnionShare adresom može <b>pristupiti ovoj sobi za ćaskawe</b> koristeći <b>Tor pregledač</b>: <img src='{}' />", "gui_qr_code_dialog_title": "OnionShare QR kod", - "gui_show_url_qr_code": "Prikaži QR kod", + "gui_show_qr_code": "Prikaži QR kod", "gui_receive_flatpak_data_dir": "Pošto ste instalirali OnionShare koristeći Flatpak, morate čuvati fajlove u falcikli ~/OnionShare.", "gui_chat_stop_server": "Zaustavi server za ćaskanje", "gui_chat_start_server": "Pokreni server za ćaskanje", diff --git a/desktop/src/onionshare/resources/locale/sv.json b/desktop/src/onionshare/resources/locale/sv.json index 302166f1..f982d200 100644 --- a/desktop/src/onionshare/resources/locale/sv.json +++ b/desktop/src/onionshare/resources/locale/sv.json @@ -266,7 +266,7 @@ "gui_new_tab": "Ny flik", "gui_qr_code_description": "Skanna den här QR-koden med en QR-läsare, till exempel kameran på din telefon, för att lättare kunna dela OnionShare-adressen med någon.", "gui_qr_code_dialog_title": "OnionShare QR-kod", - "gui_show_url_qr_code": "Visa QR-kod", + "gui_show_qr_code": "Visa QR-kod", "gui_receive_flatpak_data_dir": "Eftersom du installerade OnionShare med Flatpak måste du spara filer i en mapp i ~/OnionShare.", "gui_chat_stop_server": "Stoppa chattservern", "gui_chat_start_server": "Starta chattservern", diff --git a/desktop/src/onionshare/resources/locale/tr.json b/desktop/src/onionshare/resources/locale/tr.json index d8a34d02..b86dd39c 100644 --- a/desktop/src/onionshare/resources/locale/tr.json +++ b/desktop/src/onionshare/resources/locale/tr.json @@ -233,7 +233,7 @@ "gui_chat_start_server": "Sohbet sunucusunu başlat", "gui_chat_stop_server": "Sohbet sunucusunu durdur", "gui_receive_flatpak_data_dir": "OnionShare'i Flatpak kullanarak kurduğunuz için, dosyaları ~/OnionShare içindeki bir klasöre kaydetmelisiniz.", - "gui_show_url_qr_code": "QR Kodu Göster", + "gui_show_qr_code": "QR Kodu Göster", "gui_qr_code_dialog_title": "OnionShare QR Kodu", "gui_qr_code_description": "OnionShare adresini bir başkasıyla daha kolay paylaşmak için bu QR kodunu telefonunuzdaki kamera gibi bir QR okuyucuyla tarayın.", "gui_open_folder_error": "Klasör xdg-open ile açılamadı. Dosya burada: {}", diff --git a/desktop/src/onionshare/resources/locale/uk.json b/desktop/src/onionshare/resources/locale/uk.json index 49762022..2a2684e2 100644 --- a/desktop/src/onionshare/resources/locale/uk.json +++ b/desktop/src/onionshare/resources/locale/uk.json @@ -217,7 +217,7 @@ "gui_chat_start_server": "Запустити сервер чату", "gui_chat_stop_server_autostop_timer": "Зупинити сервер чату ({})", "gui_qr_code_dialog_title": "QR-код OnionShare", - "gui_show_url_qr_code": "Показати QR-код", + "gui_show_qr_code": "Показати QR-код", "gui_main_page_share_button": "Поділитися", "gui_main_page_chat_button": "Почати спілкуватися в чаті", "gui_main_page_website_button": "Почати хостинг", diff --git a/desktop/src/onionshare/resources/locale/zh_Hans.json b/desktop/src/onionshare/resources/locale/zh_Hans.json index 39aa5c3b..f0958614 100644 --- a/desktop/src/onionshare/resources/locale/zh_Hans.json +++ b/desktop/src/onionshare/resources/locale/zh_Hans.json @@ -257,7 +257,7 @@ "gui_new_tab_share_button": "共享文件", "gui_new_tab_tooltip": "打开一个新标签", "gui_new_tab": "新建标签", - "gui_show_url_qr_code": "显示二维码", + "gui_show_qr_code": "显示二维码", "gui_receive_flatpak_data_dir": "因为你用Flatpack安装的OnionShare,你需要把文件储存到在~/OnionShare里的一个文件夹里。", "gui_chat_stop_server": "停止言论服务器", "gui_chat_start_server": "开始言论服务器", diff --git a/desktop/src/onionshare/resources/locale/zh_Hant.json b/desktop/src/onionshare/resources/locale/zh_Hant.json index 29203837..f0a51217 100644 --- a/desktop/src/onionshare/resources/locale/zh_Hant.json +++ b/desktop/src/onionshare/resources/locale/zh_Hant.json @@ -254,7 +254,7 @@ "gui_main_page_share_button": "開始分享", "gui_new_tab_website_button": "架設一個網站", "gui_qr_code_dialog_title": "OnionShare QR Code", - "gui_show_url_qr_code": "顯示QR Code", + "gui_show_qr_code": "顯示QR Code", "gui_chat_stop_server": "停止聊天伺服器", "gui_chat_start_server": "開啟聊天伺服器", "gui_file_selection_remove_all": "全部移除", diff --git a/desktop/src/onionshare/tab/mode/__init__.py b/desktop/src/onionshare/tab/mode/__init__.py index 1e7121bb..d4f2c23a 100644 --- a/desktop/src/onionshare/tab/mode/__init__.py +++ b/desktop/src/onionshare/tab/mode/__init__.py @@ -252,11 +252,9 @@ class Mode(QtWidgets.QWidget): not self.server_status.local_only and not self.app.onion.supports_stealth and not self.settings.get("general", "public") - ): - self.stop_server() - self.start_server_error( - strings._("gui_server_doesnt_support_stealth") - ) + ): + self.stop_server() + self.start_server_error(strings._("gui_server_doesnt_support_stealth")) else: self.common.log("Mode", "start_server", "Starting an onion thread") self.obtain_onion_early = obtain_onion_early diff --git a/desktop/src/onionshare/tab/mode/receive_mode/__init__.py b/desktop/src/onionshare/tab/mode/receive_mode/__init__.py index e9f6b2ce..d5036d1d 100644 --- a/desktop/src/onionshare/tab/mode/receive_mode/__init__.py +++ b/desktop/src/onionshare/tab/mode/receive_mode/__init__.py @@ -183,16 +183,15 @@ class ReceiveMode(Mode): self.main_layout.addWidget(header_label) self.main_layout.addWidget(receive_warning) self.main_layout.addWidget(self.primary_action, stretch=1) - self.main_layout.addWidget(MinimumSizeWidget(525, 0)) + self.main_layout.addWidget(self.server_status) # Row layout content_row = QtWidgets.QHBoxLayout() - content_row.addLayout(self.main_layout) + content_row.addLayout(self.main_layout, stretch=1) content_row.addWidget(self.image) row_layout = QtWidgets.QVBoxLayout() row_layout.addLayout(top_bar_layout) row_layout.addLayout(content_row, stretch=1) - row_layout.addWidget(self.server_status) # Column layout self.column_layout = QtWidgets.QHBoxLayout() diff --git a/desktop/src/onionshare/tab/server_status.py b/desktop/src/onionshare/tab/server_status.py index 3d4c723d..115acfd5 100644 --- a/desktop/src/onionshare/tab/server_status.py +++ b/desktop/src/onionshare/tab/server_status.py @@ -81,6 +81,12 @@ class ServerStatus(QtWidgets.QWidget): self.url_description = QtWidgets.QLabel() self.url_description.setWordWrap(True) self.url_description.setMinimumHeight(50) + + # URL sharing instructions, above the URL and Copy Address/QR Code buttons + self.url_instructions = QtWidgets.QLabel() + self.url_instructions.setWordWrap(True) + + # The URL label itself self.url = QtWidgets.QLabel() self.url.setFont(url_font) self.url.setWordWrap(True) @@ -90,16 +96,16 @@ class ServerStatus(QtWidgets.QWidget): Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard ) + # Copy Onion Address button self.copy_url_button = QtWidgets.QPushButton(strings._("gui_copy_url")) self.copy_url_button.setStyleSheet( self.common.gui.css["server_status_url_buttons"] ) self.copy_url_button.clicked.connect(self.copy_url) - self.copy_client_auth_button = QtWidgets.QPushButton( - strings._("gui_copy_client_auth") - ) + + # Onion Address QR code button self.show_url_qr_code_button = QtWidgets.QPushButton( - strings._("gui_show_url_qr_code") + strings._("gui_show_qr_code") ) self.show_url_qr_code_button.hide() self.show_url_qr_code_button.clicked.connect( @@ -109,22 +115,78 @@ class ServerStatus(QtWidgets.QWidget): self.common.gui.css["server_status_url_buttons"] ) + # Client Auth sharing instructions, above the + # Copy Private Key/QR Code buttons + self.client_auth_instructions = QtWidgets.QLabel() + self.client_auth_instructions.setWordWrap(True) + self.client_auth_instructions.setText(strings._("gui_client_auth_instructions")) + + # The private key itself + self.private_key = QtWidgets.QLabel() + self.private_key.setFont(url_font) + self.private_key.setWordWrap(True) + self.private_key.setMinimumSize(self.private_key.sizeHint()) + self.private_key.setStyleSheet(self.common.gui.css["server_status_url"]) + self.private_key.setTextInteractionFlags(Qt.NoTextInteraction) + self.private_key_hidden = True + + # Copy ClientAuth button + self.copy_client_auth_button = QtWidgets.QPushButton( + strings._("gui_copy_client_auth") + ) self.copy_client_auth_button.setStyleSheet( self.common.gui.css["server_status_url_buttons"] ) self.copy_client_auth_button.clicked.connect(self.copy_client_auth) + + # ClientAuth QR code button + self.show_client_auth_qr_code_button = QtWidgets.QPushButton( + strings._("gui_show_qr_code") + ) + self.show_client_auth_qr_code_button.hide() + self.show_client_auth_qr_code_button.clicked.connect( + self.show_client_auth_qr_code_button_clicked + ) + self.show_client_auth_qr_code_button.setStyleSheet( + self.common.gui.css["server_status_url_buttons"] + ) + + # ClientAuth reveal/hide toggle button + self.client_auth_toggle_button = QtWidgets.QPushButton(strings._("gui_reveal")) + self.client_auth_toggle_button.hide() + self.client_auth_toggle_button.clicked.connect( + self.client_auth_toggle_button_clicked + ) + self.client_auth_toggle_button.setStyleSheet( + self.common.gui.css["server_status_url_buttons"] + ) + + # URL instructions layout url_buttons_layout = QtWidgets.QHBoxLayout() url_buttons_layout.addWidget(self.copy_url_button) - url_buttons_layout.addWidget(self.copy_client_auth_button) url_buttons_layout.addWidget(self.show_url_qr_code_button) url_buttons_layout.addStretch() url_layout = QtWidgets.QVBoxLayout() url_layout.addWidget(self.url_description) + url_layout.addWidget(self.url_instructions) url_layout.addWidget(self.url) url_layout.addLayout(url_buttons_layout) - # Add the widgets + # Private key instructions layout + client_auth_buttons_layout = QtWidgets.QHBoxLayout() + client_auth_buttons_layout.addWidget(self.copy_client_auth_button) + client_auth_buttons_layout.addWidget(self.show_client_auth_qr_code_button) + client_auth_buttons_layout.addWidget(self.client_auth_toggle_button) + client_auth_buttons_layout.addStretch() + + client_auth_layout = QtWidgets.QVBoxLayout() + client_auth_layout.addWidget(self.client_auth_instructions) + client_auth_layout.addWidget(self.private_key) + client_auth_layout.addLayout(client_auth_buttons_layout) + + # Add the widgets and URL/ClientAuth layouts + # to the main ServerStatus layout button_layout = QtWidgets.QHBoxLayout() button_layout.addWidget(self.server_button) button_layout.addStretch() @@ -132,6 +194,7 @@ class ServerStatus(QtWidgets.QWidget): layout = QtWidgets.QVBoxLayout() layout.addLayout(button_layout) layout.addLayout(url_layout) + layout.addLayout(client_auth_layout) self.setLayout(layout) def set_mode(self, share_mode, file_selection=None): @@ -227,16 +290,35 @@ class ServerStatus(QtWidgets.QWidget): else: self.url_description.setToolTip(strings._("gui_url_label_stay_open")) + if self.settings.get("general", "public"): + self.url_instructions.setText(strings._("gui_url_instructions_public_mode")) + else: + self.url_instructions.setText(strings._("gui_url_instructions")) + self.url_instructions.show() self.url.setText(self.get_url()) self.url.show() self.copy_url_button.show() - self.show_url_qr_code_button.show() if self.settings.get("general", "public"): + self.client_auth_instructions.hide() + self.private_key.hide() self.copy_client_auth_button.hide() + self.show_client_auth_qr_code_button.hide() else: + self.client_auth_instructions.show() + if self.private_key_hidden: + self.private_key.setText("*" * len(self.app.auth_string)) + self.private_key.setTextInteractionFlags(Qt.NoTextInteraction) + else: + self.private_key.setText(self.app.auth_string) + self.private_key.setTextInteractionFlags( + Qt.TextSelectableByMouse | Qt.TextSelectableByKeyboard + ) + self.private_key.show() self.copy_client_auth_button.show() + self.show_client_auth_qr_code_button.show() + self.client_auth_toggle_button.show() def update(self): """ @@ -260,10 +342,15 @@ class ServerStatus(QtWidgets.QWidget): ) else: self.url_description.hide() + self.url_instructions.hide() self.url.hide() self.copy_url_button.hide() - self.copy_client_auth_button.hide() self.show_url_qr_code_button.hide() + self.private_key.hide() + self.client_auth_instructions.hide() + self.copy_client_auth_button.hide() + self.show_client_auth_qr_code_button.hide() + self.client_auth_toggle_button.hide() self.mode_settings_widget.update_ui() @@ -411,11 +498,32 @@ class ServerStatus(QtWidgets.QWidget): """ Show a QR code of the onion URL. """ - if self.settings.get("general", "public"): - self.qr_code_dialog = QRCodeDialog(self.common, self.get_url()) + self.qr_code_dialog = QRCodeDialog( + self.common, strings._("gui_qr_label_url_title"), self.get_url() + ) + + def show_client_auth_qr_code_button_clicked(self): + """ + Show a QR code of the private key + """ + self.qr_code_dialog = QRCodeDialog( + self.common, + strings._("gui_qr_label_auth_string_title"), + self.app.auth_string, + ) + + def client_auth_toggle_button_clicked(self): + """ + ClientAuth reveal/hide toggle button clicked + """ + if self.private_key_hidden: + self.private_key_hidden = False + self.client_auth_toggle_button.setText(strings._("gui_hide")) else: - # Make a QR Code for the ClientAuth too - self.qr_code_dialog = QRCodeDialog(self.common, self.get_url(), self.app.auth_string) + self.private_key_hidden = True + self.client_auth_toggle_button.setText(strings._("gui_reveal")) + + self.show_url() def start_server(self): """ diff --git a/desktop/src/onionshare/widgets.py b/desktop/src/onionshare/widgets.py index ec4d5ddc..b396c43f 100644 --- a/desktop/src/onionshare/widgets.py +++ b/desktop/src/onionshare/widgets.py @@ -130,47 +130,26 @@ class QRCodeDialog(QtWidgets.QDialog): A dialog showing a QR code. """ - def __init__(self, common, url, auth_string=None): + def __init__(self, common, title, text): super(QRCodeDialog, self).__init__() self.common = common self.common.log("QrCode", "__init__") - self.qr_label_url = QtWidgets.QLabel(self) - self.qr_label_url.setPixmap(qrcode.make(url, image_factory=Image).pixmap()) - self.qr_label_url.setScaledContents(True) - self.qr_label_url.setFixedSize(350, 350) - self.qr_label_url_title = QtWidgets.QLabel(self) - self.qr_label_url_title.setText(strings._("gui_qr_label_url_title")) - self.qr_label_url_title.setAlignment(QtCore.Qt.AlignCenter) + self.qr_label_title = QtWidgets.QLabel(self) + self.qr_label_title.setText(title) + self.qr_label_title.setAlignment(QtCore.Qt.AlignCenter) + + self.qr_label = QtWidgets.QLabel(self) + self.qr_label.setPixmap(qrcode.make(text, image_factory=Image).pixmap()) + self.qr_label.setScaledContents(True) + self.qr_label.setFixedSize(350, 350) self.setWindowTitle(strings._("gui_qr_code_dialog_title")) self.setWindowIcon(QtGui.QIcon(GuiCommon.get_resource_path("images/logo.png"))) - layout = QtWidgets.QHBoxLayout(self) - url_layout = QtWidgets.QVBoxLayout(self) - url_layout.addWidget(self.qr_label_url_title) - url_layout.addWidget(self.qr_label_url) - - url_code_with_label = QtWidgets.QWidget() - url_code_with_label.setLayout(url_layout) - layout.addWidget(url_code_with_label) - - if auth_string: - self.qr_label_auth_string = QtWidgets.QLabel(self) - self.qr_label_auth_string.setPixmap(qrcode.make(auth_string, image_factory=Image).pixmap()) - self.qr_label_auth_string.setScaledContents(True) - self.qr_label_auth_string.setFixedSize(350, 350) - self.qr_label_auth_string_title = QtWidgets.QLabel(self) - self.qr_label_auth_string_title.setText(strings._("gui_qr_label_auth_string_title")) - self.qr_label_auth_string_title.setAlignment(QtCore.Qt.AlignCenter) - - auth_string_layout = QtWidgets.QVBoxLayout(self) - auth_string_layout.addWidget(self.qr_label_auth_string_title) - auth_string_layout.addWidget(self.qr_label_auth_string) - - auth_string_code_with_label = QtWidgets.QWidget() - auth_string_code_with_label.setLayout(auth_string_layout) - layout.addWidget(auth_string_code_with_label) + layout = QtWidgets.QVBoxLayout(self) + layout.addWidget(self.qr_label_title) + layout.addWidget(self.qr_label) self.exec_() diff --git a/desktop/tests/gui_base_test.py b/desktop/tests/gui_base_test.py index ac2dfc54..83ad5fa3 100644 --- a/desktop/tests/gui_base_test.py +++ b/desktop/tests/gui_base_test.py @@ -270,10 +270,39 @@ class GuiBaseTest(unittest.TestCase): tab.get_mode().server_status.file_selection.add_button.isVisible() ) + def url_shown(self, tab): + """Test that the URL is showing""" + self.assertTrue(tab.get_mode().server_status.url.isVisible()) + def url_description_shown(self, tab): """Test that the URL label is showing""" self.assertTrue(tab.get_mode().server_status.url_description.isVisible()) + def url_instructions_shown(self, tab): + """Test that the URL instructions for sharing are showing""" + self.assertTrue(tab.get_mode().server_status.url_instructions.isVisible()) + + def private_key_shown(self, tab): + """Test that the Private Key is showing when not in public mode""" + if not tab.settings.get("general", "public"): + self.assertTrue(tab.get_mode().server_status.private_key.isVisible()) + else: + self.assertFalse(tab.get_mode().server_status.private_key.isVisible()) + + def client_auth_instructions_shown(self, tab): + """ + Test that the Private Key instructions for sharing + are showing when not in public mode + """ + if not tab.settings.get("general", "public"): + self.assertTrue( + tab.get_mode().server_status.client_auth_instructions.isVisible() + ) + else: + self.assertFalse( + tab.get_mode().server_status.client_auth_instructions.isVisible() + ) + def have_copy_url_button(self, tab): """Test that the Copy URL button is shown and that the clipboard is correct""" self.assertTrue(tab.get_mode().server_status.copy_url_button.isVisible()) @@ -282,7 +311,7 @@ class GuiBaseTest(unittest.TestCase): clipboard = tab.common.gui.qtapp.clipboard() self.assertEqual(clipboard.text(), f"http://127.0.0.1:{tab.app.port}") - def have_show_qr_code_button(self, tab): + def have_show_url_qr_code_button(self, tab): """Test that the Show QR Code URL button is shown and that it loads a QR Code Dialog""" self.assertTrue( tab.get_mode().server_status.show_url_qr_code_button.isVisible() @@ -296,6 +325,28 @@ class GuiBaseTest(unittest.TestCase): QtCore.QTimer.singleShot(500, accept_dialog) tab.get_mode().server_status.show_url_qr_code_button.click() + def have_show_client_auth_qr_code_button(self, tab): + """ + Test that the Show QR Code Client Auth button is shown when + not in public mode and that it loads a QR Code Dialog. + """ + if not tab.settings.get("general", "public"): + self.assertTrue( + tab.get_mode().server_status.show_client_auth_qr_code_button.isVisible() + ) + + def accept_dialog(): + window = tab.common.gui.qtapp.activeWindow() + if window: + window.close() + + QtCore.QTimer.singleShot(500, accept_dialog) + tab.get_mode().server_status.show_client_auth_qr_code_button.click() + else: + self.assertFalse( + tab.get_mode().server_status.show_client_auth_qr_code_button.isVisible() + ) + def server_status_indicator_says_started(self, tab): """Test that the Server Status indicator shows we are started""" if type(tab.get_mode()) == ReceiveMode: @@ -344,10 +395,16 @@ class GuiBaseTest(unittest.TestCase): self.assertFalse(tab.get_mode().server_status.copy_url_button.isVisible()) self.assertFalse(tab.get_mode().server_status.url.isVisible()) self.assertFalse(tab.get_mode().server_status.url_description.isVisible()) + self.assertFalse(tab.get_mode().server_status.url_instructions.isVisible()) + self.assertFalse(tab.get_mode().server_status.private_key.isVisible()) + self.assertFalse( + tab.get_mode().server_status.client_auth_instructions.isVisible() + ) self.assertFalse( tab.get_mode().server_status.copy_client_auth_button.isVisible() ) + def web_server_is_stopped(self, tab): """Test that the web server also stopped""" QtTest.QTest.qWait(800, self.gui.qtapp) diff --git a/desktop/tests/test_gui_chat.py b/desktop/tests/test_gui_chat.py index 246a4494..786782f7 100644 --- a/desktop/tests/test_gui_chat.py +++ b/desktop/tests/test_gui_chat.py @@ -37,8 +37,13 @@ class TestChat(GuiBaseTest): self.server_is_started(tab, startup_time=500) self.web_server_is_running(tab) self.url_description_shown(tab) + self.url_instructions_shown(tab) + self.url_shown(tab) self.have_copy_url_button(tab) - self.have_show_qr_code_button(tab) + self.have_show_url_qr_code_button(tab) + self.private_key_shown(tab) + self.client_auth_instructions_shown(tab) + self.have_show_client_auth_qr_code_button(tab) self.server_status_indicator_says_started(tab) def run_all_chat_mode_stopping_tests(self, tab): diff --git a/desktop/tests/test_gui_receive.py b/desktop/tests/test_gui_receive.py index af04a914..ca69c957 100644 --- a/desktop/tests/test_gui_receive.py +++ b/desktop/tests/test_gui_receive.py @@ -110,8 +110,13 @@ class TestReceive(GuiBaseTest): self.server_is_started(tab) self.web_server_is_running(tab) self.url_description_shown(tab) + self.url_instructions_shown(tab) + self.url_shown(tab) self.have_copy_url_button(tab) - self.have_show_qr_code_button(tab) + self.have_show_url_qr_code_button(tab) + self.client_auth_instructions_shown(tab) + self.private_key_shown(tab) + self.have_show_client_auth_qr_code_button(tab) self.server_status_indicator_says_started(tab) def run_all_receive_mode_tests(self, tab): diff --git a/desktop/tests/test_gui_share.py b/desktop/tests/test_gui_share.py index b7a66a81..d3536569 100644 --- a/desktop/tests/test_gui_share.py +++ b/desktop/tests/test_gui_share.py @@ -182,8 +182,13 @@ class TestShare(GuiBaseTest): self.server_is_started(tab, startup_time) self.web_server_is_running(tab) self.url_description_shown(tab) + self.url_instructions_shown(tab) + self.url_shown(tab) self.have_copy_url_button(tab) - self.have_show_qr_code_button(tab) + self.have_show_url_qr_code_button(tab) + self.private_key_shown(tab) + self.client_auth_instructions_shown(tab) + self.have_show_client_auth_qr_code_button(tab) self.server_status_indicator_says_started(tab) def run_all_share_mode_download_tests(self, tab): diff --git a/desktop/tests/test_gui_website.py b/desktop/tests/test_gui_website.py index d7a75ed6..e736874a 100644 --- a/desktop/tests/test_gui_website.py +++ b/desktop/tests/test_gui_website.py @@ -46,8 +46,13 @@ class TestWebsite(GuiBaseTest): self.server_is_started(tab, startup_time) self.web_server_is_running(tab) self.url_description_shown(tab) + self.url_instructions_shown(tab) + self.url_shown(tab) self.have_copy_url_button(tab) - self.have_show_qr_code_button(tab) + self.have_show_url_qr_code_button(tab) + self.client_auth_instructions_shown(tab) + self.private_key_shown(tab) + self.have_show_client_auth_qr_code_button(tab) self.server_status_indicator_says_started(tab) def run_all_website_mode_download_tests(self, tab): |