# -*- coding: utf-8 -*- """ OnionShare | https://onionshare.org/ Copyright (C) 2014-2022 Micah Lee, et al. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . """ import os import shutil from pkg_resources import resource_filename from . import strings from onionshare_cli.onion import ( Onion, TorErrorInvalidSetting, TorErrorAutomatic, TorErrorSocketPort, TorErrorSocketFile, TorErrorMissingPassword, TorErrorUnreadableCookieFile, TorErrorAuthError, TorErrorProtocolError, BundledTorTimeout, BundledTorBroken, TorTooOldEphemeral, TorTooOldStealth, PortNotAvailable, ) class GuiCommon: """ The shared code for all of the OnionShare GUI. """ MODE_SHARE = "share" MODE_RECEIVE = "receive" MODE_WEBSITE = "website" MODE_CHAT = "chat" def __init__(self, common, qtapp, local_only): self.common = common self.qtapp = qtapp self.local_only = local_only # Are we running in a flatpak package? self.is_flatpak = os.path.exists("/.flatpak-info") # Load settings self.common.load_settings() # Load strings strings.load_strings(self.common, self.get_resource_path("locale")) # Start the Onion self.onion = Onion(common, get_tor_paths=self.get_tor_paths) # Lock filename self.lock_filename = os.path.join(self.common.build_data_dir(), "lock") # Events filenames self.events_dir = os.path.join(self.common.build_data_dir(), "events") if not os.path.exists(self.events_dir): os.makedirs(self.events_dir, 0o700, True) self.events_filename = os.path.join(self.events_dir, "events") self.css = self.get_css(qtapp.color_mode) self.color_mode = qtapp.color_mode def get_css(self, color_mode): header_color = "#4E064F" # purple in light title_color = "#333333" # dark gray color in main window stop_button_color = "#d0011b" # red button color for stopping server new_tab_button_background = "#ffffff" new_tab_button_border = "#efeff0" new_tab_button_text_color = "#4e0d4e" downloads_uploads_progress_bar_border_color = "#4E064F" downloads_uploads_progress_bar_chunk_color = "#4E064F" share_zip_progess_bar_border_color = "#4E064F" share_zip_progess_bar_chunk_color = "#4E064F" history_background_color = "#ffffff" history_label_color = "#000000" settings_error_color = "#FF0000" if color_mode == "dark": header_color = "#F2F2F2" title_color = "#F2F2F2" stop_button_color = "#C32F2F" new_tab_button_background = "#5F5F5F" new_tab_button_border = "#878787" new_tab_button_text_color = "#FFFFFF" share_zip_progess_bar_border_color = "#F2F2F2" history_background_color = "#191919" history_label_color = "#ffffff" settings_error_color = "#FF9999" return { # OnionShareGui styles "tab_widget": """ QTabBar::tab { width: 170px; height: 30px; } """, "tab_widget_new_tab_button": """ QPushButton { font-weight: bold; font-size: 20px; }""", "mode_new_tab_button": """ QPushButton { font-weight: bold; font-size: 30px; color: #601f61; }""", "mode_header_label": """ QLabel { color: """ + header_color + """; font-size: 48px; margin-bottom: 16px; }""", "settings_button": """ QPushButton { border: 0; border-radius: 0; }""", "server_status_indicator_label": """ QLabel { font-style: italic; color: #666666; padding: 2px; }""", "status_bar": """ QStatusBar { font-style: italic; color: #666666; } QStatusBar::item { border: 0px; }""", # Common styles between modes and their child widgets "mode_settings_toggle_advanced": """ QPushButton { color: #3f7fcf; text-align: left; } """, "mode_info_label": """ QLabel { font-size: 12px; color: #666666; } """, "server_status_url": """ QLabel { background-color: #ffffff; color: #000000; padding: 10px; border: 1px solid #666666; font-size: 12px; } """, "server_status_url_buttons": """ QPushButton { padding: 4px 8px; text-align: center; } """, "server_status_button_stopped": """ QPushButton { background-color: #5fa416; color: #ffffff; padding: 10px 30px 10px 30px; border: 0; border-radius: 5px; }""", "server_status_button_working": """ QPushButton { background-color: #4c8211; color: #ffffff; padding: 10px 30px 10px 30px; border: 0; border-radius: 5px; font-style: italic; }""", "server_status_button_started": """ QPushButton { background-color: """ + stop_button_color + """; color: #ffffff; padding: 10px 30px 10px 30px; border: 0; border-radius: 5px; }""", "downloads_uploads_not_empty": """ QWidget{ background-color: """ + history_background_color + """; }""", "downloads_uploads_empty": """ QWidget { background-color: """ + history_background_color + """; border: 1px solid #999999; } QWidget QLabel { background-color: none; border: 0px; } """, "downloads_uploads_empty_text": """ QLabel { color: #999999; }""", "downloads_uploads_label": """ QLabel { font-weight: bold; font-size 14px; text-align: center; background-color: none; border: none; }""", "downloads_uploads_clear": """ QPushButton { color: #3f7fcf; } """, "download_uploads_indicator": """ QLabel { color: #ffffff; background-color: #f44449; font-weight: bold; font-size: 10px; padding: 2px; border-radius: 7px; text-align: center; }""", "downloads_uploads_progress_bar": """ QProgressBar { border: 1px solid """ + downloads_uploads_progress_bar_border_color + """; background-color: #ffffff !important; text-align: center; color: #9b9b9b; font-size: 14px; } QProgressBar::chunk { background-color: """ + downloads_uploads_progress_bar_chunk_color + """; width: 10px; }""", "history_default_label": """ QLabel { color: """ + history_label_color + """; }""", "history_individual_file_timestamp_label": """ QLabel { color: #666666; }""", "history_individual_file_status_code_label_2xx": """ QLabel { color: #008800; }""", "history_individual_file_status_code_label_4xx": """ QLabel { color: #cc0000; }""", "tor_not_connected_label": """ QLabel { font-size: 16px; font-style: italic; }""", # New tab "new_tab_button_image": """ QLabel { padding: 30px; text-align: center; } """, "new_tab_button_text": """ QLabel { border: 1px solid """ + new_tab_button_border + """; border-radius: 4px; background-color: """ + new_tab_button_background + """; text-align: center; color: """ + new_tab_button_text_color + """; } """, "new_tab_title_text": """ QLabel { text-align: center; color: """ + title_color + """; font-size: 25px; } """, # Share mode and child widget styles "share_delete_all_files_button": """ QPushButton { color: #3f7fcf; } """, "share_zip_progess_bar": """ QProgressBar { border: 1px solid """ + share_zip_progess_bar_border_color + """; background-color: #ffffff !important; text-align: center; color: #9b9b9b; } QProgressBar::chunk { border: 0px; background-color: """ + share_zip_progess_bar_chunk_color + """; width: 10px; }""", "share_filesize_warning": """ QLabel { padding: 10px 0; font-weight: bold; color: """ + title_color + """; } """, "share_file_selection_drop_here_header_label": """ QLabel { color: """ + header_color + """; font-size: 48px; }""", "share_file_selection_drop_here_label": """ QLabel { color: #666666; }""", "share_file_selection_drop_count_label": """ QLabel { color: #ffffff; background-color: #f44449; font-weight: bold; padding: 5px 10px; border-radius: 10px; }""", "share_file_list_drag_enter": """ FileList { border: 3px solid #538ad0; } """, "share_file_list_drag_leave": """ FileList { border: none; } """, "share_file_list_item_size": """ QLabel { color: #666666; font-size: 11px; }""", # Receive mode and child widget styles "receive_file": """ QWidget { background-color: #ffffff; } """, "receive_file_size": """ QLabel { color: #666666; font-size: 11px; }""", "receive_message_button": """ QPushButton { padding: 5px 10px; }""", # Tor Settings dialogs "tor_settings_error": """ QLabel { color: """ + settings_error_color + """; } """, } def get_tor_paths(self): if self.common.platform == "Linux": base_path = self.get_resource_path("tor") if base_path and os.path.isdir(base_path): self.common.log( "GuiCommon", "get_tor_paths", "using paths in resources" ) tor_path = os.path.join(base_path, "tor") tor_geo_ip_file_path = os.path.join(base_path, "geoip") tor_geo_ipv6_file_path = os.path.join(base_path, "geoip6") obfs4proxy_file_path = os.path.join(base_path, "obfs4proxy") snowflake_file_path = os.path.join(base_path, "snowflake-client") meek_client_file_path = os.path.join(base_path, "meek-client") else: # Fallback to looking in the path self.common.log("GuiCommon", "get_tor_paths", "using paths from PATH") tor_path = shutil.which("tor") obfs4proxy_file_path = shutil.which("obfs4proxy") snowflake_file_path = shutil.which("snowflake-client") meek_client_file_path = shutil.which("meek-client") prefix = os.path.dirname(os.path.dirname(tor_path)) tor_geo_ip_file_path = os.path.join(prefix, "share/tor/geoip") tor_geo_ipv6_file_path = os.path.join(prefix, "share/tor/geoip6") if self.common.platform == "Windows": base_path = self.get_resource_path("tor") tor_path = os.path.join(base_path, "Tor", "tor.exe") obfs4proxy_file_path = os.path.join(base_path, "Tor", "obfs4proxy.exe") snowflake_file_path = os.path.join(base_path, "Tor", "snowflake-client.exe") meek_client_file_path = os.path.join(base_path, "Tor", "meek-client.exe") tor_geo_ip_file_path = os.path.join(base_path, "Data", "Tor", "geoip") tor_geo_ipv6_file_path = os.path.join(base_path, "Data", "Tor", "geoip6") elif self.common.platform == "Darwin": base_path = self.get_resource_path("tor") tor_path = os.path.join(base_path, "tor") obfs4proxy_file_path = os.path.join(base_path, "obfs4proxy") snowflake_file_path = os.path.join(base_path, "snowflake-client") meek_client_file_path = os.path.join(base_path, "meek-client") tor_geo_ip_file_path = os.path.join(base_path, "geoip") tor_geo_ipv6_file_path = os.path.join(base_path, "geoip6") elif self.common.platform == "BSD": tor_path = "/usr/local/bin/tor" tor_geo_ip_file_path = "/usr/local/share/tor/geoip" tor_geo_ipv6_file_path = "/usr/local/share/tor/geoip6" obfs4proxy_file_path = "/usr/local/bin/obfs4proxy" meek_client_file_path = "/usr/local/bin/meek-client" snowflake_file_path = "/usr/local/bin/snowflake-client" return ( tor_path, tor_geo_ip_file_path, tor_geo_ipv6_file_path, obfs4proxy_file_path, snowflake_file_path, meek_client_file_path, ) @staticmethod def get_resource_path(filename): """ Returns the absolute path of a resource """ try: return resource_filename("onionshare", os.path.join("resources", filename)) except KeyError: return None @staticmethod def get_translated_tor_error(e): """ Takes an exception defined in onion.py and returns a translated error message """ if type(e) is TorErrorInvalidSetting: return strings._("settings_error_unknown") elif type(e) is TorErrorAutomatic: return strings._("settings_error_automatic") elif type(e) is TorErrorSocketPort: return strings._("settings_error_socket_port").format(e.args[0], e.args[1]) elif type(e) is TorErrorSocketFile: return strings._("settings_error_socket_file").format(e.args[0]) elif type(e) is TorErrorMissingPassword: return strings._("settings_error_missing_password") elif type(e) is TorErrorUnreadableCookieFile: return strings._("settings_error_unreadable_cookie_file") elif type(e) is TorErrorAuthError: return strings._("settings_error_auth").format(e.args[0], e.args[1]) elif type(e) is TorErrorProtocolError: return strings._("error_tor_protocol_error").format(e.args[0]) elif type(e) is BundledTorTimeout: return strings._("settings_error_bundled_tor_timeout") elif type(e) is BundledTorBroken: return strings._("settings_error_bundled_tor_broken").format(e.args[0]) elif type(e) is TorTooOldEphemeral: return strings._("error_ephemeral_not_supported") elif type(e) is TorTooOldStealth: return strings._("error_stealth_not_supported") elif type(e) is PortNotAvailable: return strings._("error_port_not_available") return None