Ejemplo n.º 1
0
class AutoConnectTab(QtWidgets.QWidget):
    """
    Initial Tab that appears in the very beginning to ask user if
    should auto connect.
    """

    close_this_tab = QtCore.Signal()
    tor_is_connected = QtCore.Signal()
    tor_is_disconnected = QtCore.Signal()

    def __init__(self, common, tab_id, status_bar, window, parent=None):
        super(AutoConnectTab, self).__init__()
        self.common = common
        self.common.log("AutoConnectTab", "__init__")

        self.status_bar = status_bar
        self.tab_id = tab_id
        self.window = window
        self.parent = parent

        # Was auto connected?
        self.curr_settings = Settings(common)
        self.curr_settings.load()
        self.auto_connect_enabled = self.curr_settings.get("auto_connect")

        # Rocket ship animation images
        self.anim_stars = AnimStars(self, self.window)
        self.anim_ship = AnimShip(self, self.window)
        self.anim_smoke = AnimSmoke(self, self.window)

        # Onionshare logo
        self.image_label = QtWidgets.QLabel()
        self.image_label.setPixmap(
            QtGui.QPixmap.fromImage(
                QtGui.QImage(
                    GuiCommon.get_resource_path(
                        os.path.join(
                            "images",
                            f"{common.gui.color_mode}_logo_text_bg.png")))))
        self.image_label.setFixedSize(322, 65)
        image_layout = QtWidgets.QVBoxLayout()
        image_layout.addWidget(self.image_label)
        self.image = QtWidgets.QWidget()
        self.image.setLayout(image_layout)

        # First launch widget
        self.first_launch_widget = AutoConnectFirstLaunchWidget(
            self.common, self.curr_settings)
        self.first_launch_widget.toggle_auto_connect.connect(
            self.toggle_auto_connect)
        self.first_launch_widget.connect_clicked.connect(
            self.first_launch_widget_connect_clicked)
        self.first_launch_widget.open_tor_settings.connect(
            self.open_tor_settings)
        self.first_launch_widget.show()

        # Use bridge widget
        self.use_bridge_widget = AutoConnectUseBridgeWidget(self.common)
        self.use_bridge_widget.connect_clicked.connect(
            self.use_bridge_connect_clicked)
        self.use_bridge_widget.try_again_clicked.connect(
            self.first_launch_widget_connect_clicked)
        self.use_bridge_widget.open_tor_settings.connect(
            self.open_tor_settings)
        self.use_bridge_widget.hide()

        # Tor connection widget
        self.tor_con = TorConnectionWidget(self.common, self.status_bar)
        self.tor_con.success.connect(self.tor_con_success)
        self.tor_con.fail.connect(self.tor_con_fail)
        self.tor_con.update_progress.connect(self.anim_stars.update)
        self.tor_con.update_progress.connect(self.anim_ship.update)
        self.tor_con.update_progress.connect(self.anim_smoke.update)
        self.tor_con.hide()

        # Layout
        content_layout = QtWidgets.QVBoxLayout()
        content_layout.addStretch()
        content_layout.addWidget(self.image)
        content_layout.addWidget(self.first_launch_widget)
        content_layout.addWidget(self.use_bridge_widget)
        content_layout.addWidget(self.tor_con)
        content_layout.addStretch()
        content_layout.setAlignment(QtCore.Qt.AlignCenter)
        content_widget = QtWidgets.QWidget()
        content_widget.setLayout(content_layout)

        self.layout = QtWidgets.QHBoxLayout()
        self.layout.addWidget(content_widget)
        self.layout.addStretch()

        self.setLayout(self.layout)

    def check_autoconnect(self):
        """
        After rendering, check if autoconnect was clicked, then start connecting
        """
        self.common.log("AutoConnectTab", "autoconnect_checking")
        if self.auto_connect_enabled:
            self.first_launch_widget.enable_autoconnect_checkbox.setChecked(
                True)
            self.first_launch_widget_connect_clicked()

    def toggle_auto_connect(self):
        """
        Auto connect checkbox clicked
        """
        self.common.log("AutoConnectTab", "autoconnect_checkbox_clicked")
        self.curr_settings.set(
            "auto_connect",
            self.first_launch_widget.enable_autoconnect_checkbox.isChecked(),
        )
        self.curr_settings.save()

    def open_tor_settings(self):
        self.parent.open_settings_tab(from_autoconnect=True, active_tab="tor")

    def first_launch_widget_connect_clicked(self):
        """
        Connect button in first launch widget clicked. Try to connect to tor.
        """
        self.common.log("AutoConnectTab",
                        "first_launch_widget_connect_clicked")
        self.first_launch_widget.hide_buttons()

        self.tor_con.show()
        self.tor_con.start(self.curr_settings)

    def _got_bridges(self):
        self.use_bridge_widget.progress.hide()
        self.use_bridge_widget.progress_label.hide()
        # Try and connect again
        self.common.log(
            "AutoConnectTab",
            "_got_bridges",
            "Got bridges. Trying to reconnect to Tor",
        )
        self.tor_con.show()
        self.tor_con.start(self.curr_settings)

    def _got_no_bridges(self):
        # If we got no bridges, even after trying the default bridges
        # provided by the Censorship API, try connecting again using
        # our built-in obfs4 bridges
        self.curr_settings.set("bridges_type", "built-in")
        self.curr_settings.set("bridges_builtin_pt", "obfs4")
        self.curr_settings.set("bridges_enabled", True)
        self.curr_settings.save()

        self._got_bridges()

    def _censorship_progress_update(self, progress, summary):
        self.use_bridge_widget.progress.setValue(int(progress))
        self.use_bridge_widget.progress_label.setText(
            f"<strong>{strings._('gui_autoconnect_circumventing_censorship')}</strong><br>{summary}"
        )

    def network_connection_error(self):
        """
        Display an error if there simply seems no network connection.
        """
        self.use_bridge_widget.connection_status_label.setText(
            strings._("gui_autoconnect_failed_to_connect_to_tor"))
        self.use_bridge_widget.progress.hide()
        self.use_bridge_widget.progress_label.hide()
        self.use_bridge_widget.error_label.show()
        self.use_bridge_widget.country_combobox.setEnabled(True)
        self.use_bridge_widget.show_buttons()
        self.use_bridge_widget.show()

    def use_bridge_connect_clicked(self):
        """
        Connect button in use bridge widget clicked.
        """
        self.common.log(
            "AutoConnectTab",
            "use_bridge_connect_clicked",
            "Trying to automatically obtain bridges",
        )
        self.use_bridge_widget.hide_buttons()
        self.use_bridge_widget.progress.show()
        self.use_bridge_widget.progress_label.show()

        if self.use_bridge_widget.detect_automatic_radio.isChecked():
            country = False
        else:
            country = self.use_bridge_widget.country_combobox.currentData(
            ).lower()

        self._censorship_progress_update(
            50,
            strings._(
                "gui_autoconnect_circumventing_censorship_starting_meek"))
        try:
            self.common.gui.meek.start()
            self.censorship_circumvention = CensorshipCircumvention(
                self.common, self.common.gui.meek)
            self._censorship_progress_update(
                75,
                strings.
                _("gui_autoconnect_circumventing_censorship_requesting_bridges"
                  ),
            )
            bridge_settings = self.censorship_circumvention.request_settings(
                country=country)

            if not bridge_settings:
                # Fall back to trying the default bridges from the API
                self.common.log(
                    "AutoConnectTab",
                    "use_bridge_connect_clicked",
                    "Falling back to trying default bridges provided by the Censorship Circumvention API",
                )
                bridge_settings = (
                    self.censorship_circumvention.request_default_bridges())

            self.common.gui.meek.cleanup()

            if bridge_settings and self.censorship_circumvention.save_settings(
                    self.curr_settings, bridge_settings):
                self._censorship_progress_update(
                    100,
                    strings.
                    _("gui_autoconnect_circumventing_censorship_got_bridges"),
                )
                self._got_bridges()
            else:
                self._got_no_bridges()
        except (
                MeekNotRunning,
                MeekNotFound,
        ) as e:
            self._got_no_bridges()
        except CensorshipCircumventionError as e:
            self.common.log(
                "AutoConnectTab",
                "use_bridge_connect_clicked",
                "Request to the Tor Censorship Circumvention API failed. No network connection?",
            )
            self.network_connection_error()

    def check_for_updates(self):
        """
        Check for OnionShare updates in a new thread, if enabled.
        """
        if self.common.platform == "Windows" or self.common.platform == "Darwin":
            if self.common.settings.get("use_autoupdate"):

                def update_available(update_url, installed_version,
                                     latest_version):
                    Alert(
                        self.common,
                        strings._("update_available").format(
                            update_url, installed_version, latest_version),
                    )

                self.update_thread = UpdateThread(self.common,
                                                  self.common.gui.onion)
                self.update_thread.update_available.connect(update_available)
                self.update_thread.start()

    def tor_con_success(self):
        """
        Finished testing tor connection.
        """
        self.tor_con.hide()
        self.first_launch_widget.show_buttons()
        self.use_bridge_widget.show_buttons()
        self.use_bridge_widget.progress.hide()
        self.use_bridge_widget.progress_label.hide()

        if self.common.gui.onion.is_authenticated(
        ) and not self.tor_con.wasCanceled():
            # Tell the tabs that Tor is connected
            self.tor_is_connected.emit()
            # After connecting to Tor, check for updates
            self.check_for_updates()
            # Close the tab
            self.close_this_tab.emit()

    def tor_con_fail(self, msg):
        """
        Finished testing tor connection.
        """
        self.tor_con.hide()

        # If there is a message, update the text of the bridge widget
        if msg:
            self.use_bridge_widget.connection_error_message.setText(msg)

        # If we're on first launch, check if wasCanceled
        # If cancelled, stay in first launch widget and show buttons
        # Else, switch to use bridge
        if self.first_launch_widget.isVisible():
            if self.tor_con.wasCanceled():
                self.first_launch_widget.show_buttons()
            else:
                self.first_launch_widget.show_buttons()
                self.first_launch_widget.hide()
                self.use_bridge_widget.show()
        else:
            self.use_bridge_widget.show_buttons()

    def reload_settings(self):
        """
        Reload the latest Tor settings, and reset to show the
        first-launch widget if it had been hidden.
        """
        self.curr_settings.load()
        self.auto_connect_enabled = self.curr_settings.get("auto_connect")
        self.first_launch_widget.enable_autoconnect_checkbox.setChecked(
            self.auto_connect_enabled)
        self.use_bridge_widget.hide()
        self.first_launch_widget.show_buttons()
        self.first_launch_widget.show()
Ejemplo n.º 2
0
    def check(self, force=False):
        self.common.log("UpdateChecker", "check", f"force={force}")
        # Load the settings
        settings = Settings(self.common)
        settings.load()

        # If force=True, then definitely check
        if force:
            check_for_updates = True
        else:
            check_for_updates = False

            # See if it's been 1 day since the last check
            autoupdate_timestamp = settings.get("autoupdate_timestamp")
            if autoupdate_timestamp:
                last_checked = datetime.datetime.fromtimestamp(
                    autoupdate_timestamp)
                now = datetime.datetime.now()

                one_day = datetime.timedelta(days=1)
                if now - last_checked > one_day:
                    check_for_updates = True
            else:
                check_for_updates = True

        # Check for updates
        if check_for_updates:
            self.common.log("UpdateChecker", "check", "checking for updates")
            # Download the latest-version file over Tor
            try:
                # User agent string includes OnionShare version and platform
                user_agent = f"OnionShare {self.common.version}, {self.common.platform}"

                # If the update is forced, add '?force=1' to the URL, to more
                # accurately measure daily users
                path = "/latest-version.txt"
                if force:
                    path += "?force=1"

                if Version(self.onion.tor_version) >= Version("0.3.2.9"):
                    onion_domain = (
                        "lldan5gahapx5k7iafb3s4ikijc4ni7gx5iywdflkba5y2ezyg6sjgyd.onion"
                    )
                else:
                    onion_domain = "elx57ue5uyfplgva.onion"

                self.common.log("UpdateChecker", "check",
                                f"loading http://{onion_domain}{path}")

                (socks_address, socks_port) = self.onion.get_tor_socks_port()
                socks.set_default_proxy(socks.SOCKS5, socks_address,
                                        socks_port)

                s = socks.socksocket()
                s.settimeout(15)  # 15 second timeout
                s.connect((onion_domain, 80))

                http_request = f"GET {path} HTTP/1.0\r\n"
                http_request += f"Host: {onion_domain}\r\n"
                http_request += f"User-Agent: {user_agent}\r\n"
                http_request += "\r\n"
                s.sendall(http_request.encode("utf-8"))

                http_response = s.recv(1024)
                latest_version = (
                    http_response[http_response.find(b"\r\n\r\n"):].strip(
                    ).decode("utf-8"))

                self.common.log(
                    "UpdateChecker",
                    "check",
                    f"latest OnionShare version: {latest_version}",
                )

            except Exception as e:
                self.common.log("UpdateChecker", "check", str(e))
                self.update_error.emit()
                raise UpdateCheckerCheckError

            # Validate that latest_version looks like a version string
            # This regex is: 1-3 dot-separated numeric components
            version_re = r"^(\d+\.)?(\d+\.)?(\d+)$"
            if not re.match(version_re, latest_version):
                self.update_invalid_version.emit(latest_version)
                raise UpdateCheckerInvalidLatestVersion(latest_version)

            # Update the last checked timestamp (dropping the seconds and milliseconds)
            timestamp = (datetime.datetime.now().replace(
                microsecond=0).replace(second=0).timestamp())
            # Re-load the settings first before saving, just in case they've changed since we started our thread
            settings.load()
            settings.set("autoupdate_timestamp", timestamp)
            settings.save()

            # Do we need to update?
            update_url = "https://onionshare.org"
            installed_version = self.common.version
            if installed_version < latest_version:
                self.update_available.emit(update_url, installed_version,
                                           latest_version)
                return

            # No updates are available
            self.update_not_available.emit()