Ejemplo n.º 1
0
def test_calculate_total_steps_6_need_to_join_grid_and_1_folder(fake_gateway):
    sr = SetupRunner([fake_gateway])
    settings = {
        "introducer": "pb://introducer.other",
        "magic-folders": {"FolderOne": {"code": "URI+URI"}},
    }
    assert sr.calculate_total_steps(settings) == 6
Ejemplo n.º 2
0
def test_calculate_total_steps_6_need_to_join_grid_and_1_folder(fake_gateway):
    sr = SetupRunner([fake_gateway])
    settings = {
        'introducer': 'pb://introducer.other',
        'magic-folders': {
            'FolderOne': {
                'code': 'URI+URI'
            }
        }
    }
    assert sr.calculate_total_steps(settings) == 6
Ejemplo n.º 3
0
class WelcomeDialog(QStackedWidget):
    def __init__(self, gui, known_gateways=None):
        super(WelcomeDialog, self).__init__()
        self.gui = gui
        self.known_gateways = known_gateways
        self.gateway = None
        self.setup_runner = None
        self.recovery_key_importer = None
        self.use_tor = False
        self.prompt_to_export = True
        self.resize(400, 500)
        self.setWindowTitle(APP_NAME)
        self.page_1 = WelcomeWidget(self)
        self.page_2 = ProgressBarWidget()
        self.page_3 = TahoeConfigForm()

        self.addWidget(self.page_1)
        self.addWidget(self.page_2)
        self.addWidget(self.page_3)

        self.lineedit = self.page_1.lineedit
        self.checkbox = self.page_1.invite_code_widget.checkbox
        self.cancel_button = self.page_2.cancel_button
        self.finish_button = self.page_2.finish_button
        self.buttonbox = self.page_3.buttonbox
        self.restore_link = self.page_1.restore_link
        self.configure_link = self.page_1.configure_link
        self.preferences_button = self.page_1.preferences_button

        self.shortcut_close = QShortcut(QKeySequence.Close, self)
        self.shortcut_close.activated.connect(self.close)

        self.shortcut_quit = QShortcut(QKeySequence.Quit, self)
        self.shortcut_quit.activated.connect(self.close)

        self.lineedit.go.connect(self.go)
        self.lineedit.error.connect(self.show_error)
        self.checkbox.stateChanged.connect(self.on_checkbox_state_changed)
        self.cancel_button.clicked.connect(self.cancel_button_clicked)
        self.finish_button.clicked.connect(self.finish_button_clicked)
        self.buttonbox.accepted.connect(self.on_accepted)
        self.buttonbox.rejected.connect(self.reset)
        self.restore_link.linkActivated.connect(self.on_restore_link_activated)
        self.configure_link.linkActivated.connect(
            self.on_configure_link_activated)
        self.preferences_button.clicked.connect(
            self.gui.show_preferences_window)

    def on_checkbox_state_changed(self, state):
        self.use_tor = bool(state)
        log.debug("use_tor=%s", self.use_tor)
        if state:
            self.page_2.tor_label.show()
            self.page_2.progressbar.setStyleSheet(
                'QProgressBar::chunk { background-color: #7D4698; }')
        else:
            self.page_2.tor_label.hide()
            self.page_2.progressbar.setStyleSheet('')

    def on_configure_link_activated(self):
        self.setCurrentIndex(2)

    def update_progress(self, message):
        self.page_2.update_progress(message)

    def show_error(self, message):
        self.page_1.show_error(message)

    def reset(self):
        self.page_1.reset()
        self.page_2.reset()
        self.page_3.reset()
        self.setCurrentIndex(0)

    def load_service_icon(self, filepath):
        pixmap = QPixmap(filepath).scaled(100, 100)
        self.page_2.icon_overlay.setPixmap(pixmap)

    def handle_failure(self, failure):
        log.error(str(failure))
        if failure.type == CancelledError:
            if self.page_2.progressbar.value() <= 2:
                show_failure(failure, self)
                self.show_error("Invite timed out")
                self.reset()
            return
        show_failure(failure, self)
        if failure.type == ServerConnectionError:
            self.show_error("Server connection error")
        if failure.type == WelcomeError:
            self.show_error("Invite refused")
        elif failure.type == WrongPasswordError:
            self.show_error("Invite confirmation failed")
        elif failure.type == UpgradeRequiredError:
            self.show_error("Upgrade required")
        else:
            self.show_error(str(failure.type.__name__))
        self.reset()

    def on_done(self, gateway):
        self.gateway = gateway
        self.page_2.progressbar.setValue(self.page_2.progressbar.maximum())
        self.finish_button.show()

    def on_already_joined(self, grid_name):
        QMessageBox.information(
            self, "Already connected",
            'You are already connected to "{}"'.format(grid_name))
        self.close()

    def verify_settings(self, settings, from_wormhole=True):
        self.show()
        self.raise_()
        settings = validate_settings(settings, self.known_gateways, self,
                                     from_wormhole)
        self.setup_runner = SetupRunner(self.known_gateways, self.use_tor)
        steps = self.setup_runner.calculate_total_steps(settings) + 2
        self.page_2.progressbar.setMaximum(steps)
        self.setup_runner.grid_already_joined.connect(self.on_already_joined)
        self.setup_runner.update_progress.connect(self.update_progress)
        self.setup_runner.got_icon.connect(self.load_service_icon)
        self.setup_runner.client_started.connect(
            lambda gateway: self.gui.populate([gateway]))
        self.setup_runner.done.connect(self.on_done)
        d = self.setup_runner.run(settings)
        d.addErrback(self.handle_failure)

    def on_import_done(self, settings):
        if settings.get('hide-ip'):
            self.on_checkbox_state_changed(1)  # Toggle Tor checkbox "on"
        self.setCurrentIndex(1)
        self.page_2.progressbar.setValue(1)
        self.update_progress('Verifying invitation code...')
        self.prompt_to_export = False
        self.verify_settings(settings, from_wormhole=False)

    def on_restore_link_activated(self):
        self.recovery_key_importer = RecoveryKeyImporter(self.page_1)
        self.recovery_key_importer.done.connect(self.on_import_done)
        self.recovery_key_importer.do_import()

    def go(self, code):
        self.setCurrentIndex(1)
        self.page_2.progressbar.setValue(1)
        self.update_progress('Verifying invitation code...')
        if code.split('-')[0] == "0":
            settings = get_settings_from_cheatcode(code[2:])
            if settings:
                self.verify_settings(settings)
                return
        d = wormhole_receive(code, self.use_tor)  # pylint: disable=assignment-from-no-return
        d.addCallback(self.verify_settings)
        d.addErrback(self.handle_failure)
        reactor.callLater(30, d.cancel)

    def cancel_button_clicked(self):
        if self.page_2.is_complete():
            self.finish_button_clicked()
            return
        msgbox = QMessageBox(self)
        msgbox.setIcon(QMessageBox.Question)
        msgbox.setWindowTitle("Cancel setup?")
        msgbox.setText("Are you sure you wish to cancel the setup process?")
        msgbox.setInformativeText(
            "If you cancel, you may need to obtain a new invite code.")
        msgbox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        msgbox.setDefaultButton(QMessageBox.No)
        if msgbox.exec_() == QMessageBox.Yes:
            self.reset()

    def on_accepted(self):
        settings = self.page_3.get_settings()
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Warning)
        msg.setWindowTitle(APP_NAME)
        msg.setStandardButtons(QMessageBox.Ok)
        if not settings['nickname']:
            msg.setText("Please enter a name.")
            msg.exec_()
        elif not settings['introducer']:
            msg.setText("Please enter an Introducer fURL.")
            msg.exec_()
        elif not is_valid_furl(settings['introducer']):
            msg.setText("Please enter a valid Introducer fURL.")
            msg.exec_()
        else:
            self.setCurrentIndex(1)
            self.verify_settings(settings, from_wormhole=False)

    def prompt_for_export(self, gateway):
        msg = QMessageBox(self)
        msg.setIcon(QMessageBox.Warning)
        msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        msg.setDefaultButton(QMessageBox.Yes)
        button_export = msg.button(QMessageBox.Yes)
        button_export.setText("&Export...")
        button_skip = msg.button(QMessageBox.No)
        button_skip.setText("&Skip")
        msg.setWindowTitle("Export Recovery Key?")
        # "Now that {} is configured..."
        msg.setText(
            "Before uploading any folders to {}, it is recommended that you "
            "export a Recovery Key and store it in a safe location (such as "
            "an encrypted USB drive or password manager).".format(
                gateway.name))
        msg.setInformativeText(
            "{} does not have access to your folders, and cannot restore "
            "access to them. But with a Recovery Key, you can restore access "
            "to uploaded folders in case something goes wrong (e.g., hardware "
            "failure, accidental data-loss).<p><p><a href={}>More information."
            "..</a>".format(gateway.name,
                            global_settings['help']['recovery_url']))
        #msg.setText(
        #    "Before uploading any folders to {}, it is <b>strongly "
        #    "recommended</b> that you <i>export a Recovery Key</i> and store "
        #    "it in a safe and secure location (such as an encrypted USB drive)"
        #    ".<p><p>Possessing a Recovery Key will allow you to restore "
        #    "access to any of the folders you've uploaded to {} in the event "
        #    "that something goes wrong (e.g., hardware failure, accidental "
        #    "data-loss).".format(gateway.name, gateway.name))
        #msg.setDetailedText(
        #    "A 'Recovery Key' is a small file that contains enough information"
        #    " to re-establish a connection with your storage provider and "
        #    "restore your previously-uploaded folders. Because access to this "
        #    "file is sufficient to access to any of the the data you've "
        #    "stored, it is important that you keep this file safe and secure; "
        #    "do not share your Recovery Key with anybody!")
        reply = msg.exec_()
        if reply == QMessageBox.Yes:
            self.gui.main_window.export_recovery_key()  # XXX
        else:
            # TODO: Nag user; "Are you sure?"
            pass

    def finish_button_clicked(self):
        self.gui.show()
        self.close()
        if self.prompt_to_export:
            self.prompt_for_export(self.gateway)
        self.reset()

    def enterEvent(self, event):
        event.accept()
        self.lineedit.update_action_button()

    def closeEvent(self, event):
        if self.gui.main_window.gateways:
            event.accept()
        else:
            event.ignore()
            msgbox = QMessageBox(self)
            msgbox.setIcon(QMessageBox.Question)
            msgbox.setWindowTitle("Exit setup?")
            msgbox.setText("Are you sure you wish to exit?")
            msgbox.setInformativeText(
                "{} has not yet been configured.".format(APP_NAME))
            msgbox.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
            msgbox.setDefaultButton(QMessageBox.No)
            if msgbox.exec_() == QMessageBox.Yes:
                QCoreApplication.instance().quit()
Ejemplo n.º 4
0
def test_calculate_total_steps_5_need_to_join_grid(fake_gateway):
    sr = SetupRunner([fake_gateway])
    settings = {'introducer': 'pb://introducer.other'}
    assert sr.calculate_total_steps(settings) == 5
Ejemplo n.º 5
0
def test_calculate_total_steps_1_already_joined_grid(fake_gateway):
    sr = SetupRunner([fake_gateway])
    settings = {'introducer': 'pb://introducer'}
    assert sr.calculate_total_steps(settings) == 1
Ejemplo n.º 6
0
class WelcomeDialog(QStackedWidget):
    def __init__(self, gui, known_gateways=None):
        super(WelcomeDialog, self).__init__()
        self.gui = gui
        self.known_gateways = known_gateways
        self.gateway = None
        self.setup_runner = None
        self.resize(400, 500)
        self.setWindowTitle(APP_NAME)
        self.page_1 = WelcomeWidget(self)
        self.page_2 = ProgressBarWidget()
        self.page_3 = TahoeConfigForm()
        self.page_4 = PreferencesWidget()

        self.addWidget(self.page_1)
        self.addWidget(self.page_2)
        self.addWidget(self.page_3)
        self.addWidget(self.page_4)

        self.lineedit = self.page_1.lineedit
        self.cancel_button = self.page_2.cancel_button
        self.finish_button = self.page_2.finish_button
        self.buttonbox = self.page_3.buttonbox
        self.help = self.page_1.help
        self.preferences_button = self.page_1.preferences_button

        self.shortcut_close = QShortcut(QKeySequence.Close, self)
        self.shortcut_close.activated.connect(self.close)

        self.shortcut_quit = QShortcut(QKeySequence.Quit, self)
        self.shortcut_quit.activated.connect(self.close)

        self.lineedit.go.connect(self.go)
        self.lineedit.error.connect(self.show_error)
        self.cancel_button.clicked.connect(self.cancel_button_clicked)
        self.finish_button.clicked.connect(self.finish_button_clicked)
        self.buttonbox.accepted.connect(self.on_accepted)
        self.buttonbox.rejected.connect(self.reset)
        self.help.linkActivated.connect(self.on_link_activated)
        self.preferences_button.clicked.connect(
            self.on_preferences_button_clicked)
        self.page_4.accepted.connect(self.on_preferences_accepted)

    def on_link_activated(self):
        self.setCurrentIndex(2)

    def on_preferences_button_clicked(self):
        self.setCurrentIndex(3)

    def on_preferences_accepted(self):
        self.setCurrentIndex(0)

    def update_progress(self, message):
        self.page_2.update_progress(message)

    def show_error(self, message):
        self.page_1.show_error(message)

    def reset(self):
        self.page_1.reset()
        self.page_2.reset()
        self.page_3.reset()
        self.setCurrentIndex(0)

    def load_service_icon(self, filepath):
        pixmap = QPixmap(filepath).scaled(100, 100)
        self.page_2.icon_overlay.setPixmap(pixmap)

    def handle_failure(self, failure):
        log.error(str(failure))
        if failure.type == CancelledError:
            if self.page_2.progressbar.value() <= 2:
                show_failure(failure, self)
                self.show_error("Invite timed out")
                self.reset()
            return
        show_failure(failure, self)
        if failure.type == ServerConnectionError:
            self.show_error("Server connection error")
        if failure.type == WelcomeError:
            self.show_error("Invite refused")
        elif failure.type == WrongPasswordError:
            self.show_error("Invite confirmation failed")
        elif failure.type == UpgradeRequiredError:
            self.show_error("Upgrade required")
        else:
            self.show_error(str(failure.type.__name__))
        self.reset()

    def on_done(self, gateway):
        self.gateway = gateway
        self.gui.populate([gateway])
        self.page_2.progressbar.setValue(self.page_2.progressbar.maximum())
        self.finish_button.show()

    def on_already_joined(self, grid_name):
        QMessageBox.information(
            self, "Already connected",
            'You are already connected to "{}"'.format(grid_name))
        self.close()

    def verify_settings(self, settings, from_wormhole=True):
        settings = validate_settings(settings, self.known_gateways, self,
                                     from_wormhole)
        self.setup_runner = SetupRunner(self.known_gateways)
        steps = self.setup_runner.calculate_total_steps(settings) + 2
        self.page_2.progressbar.setMaximum(steps)
        self.setup_runner.grid_already_joined.connect(self.on_already_joined)
        self.setup_runner.update_progress.connect(self.update_progress)
        self.setup_runner.got_icon.connect(self.load_service_icon)
        self.setup_runner.done.connect(self.on_done)
        d = self.setup_runner.run(settings)
        d.addErrback(self.handle_failure)

    def go(self, code):
        self.setCurrentIndex(1)
        self.page_2.progressbar.setValue(1)
        self.update_progress('Verifying invitation code...')
        if code.split('-')[0] == "0":
            settings = get_settings_from_cheatcode(code[2:])
            if settings:
                self.verify_settings(settings)
                return
        d = wormhole_receive(code)
        d.addCallback(self.verify_settings)
        d.addErrback(self.handle_failure)
        reactor.callLater(30, d.cancel)

    def cancel_button_clicked(self):
        if self.page_2.is_complete():
            self.finish_button_clicked()
            return
        reply = QMessageBox.question(
            self, "Cancel setup?",
            "Are you sure you wish to cancel the {} setup process? "
            "If you do, you may need to obtain a new invite code.".format(
                APP_NAME), QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
        if reply == QMessageBox.Yes:
            self.reset()

    def on_accepted(self):
        settings = self.page_3.get_settings()
        msg = QMessageBox()
        msg.setIcon(QMessageBox.Warning)
        msg.setWindowTitle(APP_NAME)
        msg.setStandardButtons(QMessageBox.Ok)
        if not settings['nickname']:
            msg.setText("Please enter a name.")
            msg.exec_()
        elif not settings['introducer']:
            msg.setText("Please enter an Introducer fURL.")
            msg.exec_()
        elif not is_valid_furl(settings['introducer']):
            msg.setText("Please enter a valid Introducer fURL.")
            msg.exec_()
        else:
            self.setCurrentIndex(1)
            self.verify_settings(settings, from_wormhole=False)

    def prompt_for_export(self, gateway):
        msg = QMessageBox(self)
        msg.setIcon(QMessageBox.Warning)
        msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
        msg.setDefaultButton(QMessageBox.Yes)
        button_export = msg.button(QMessageBox.Yes)
        button_export.setText("Export...")
        button_skip = msg.button(QMessageBox.No)
        button_skip.setText("Skip")
        msg.setWindowTitle("Export Recovery Key?")
        # "Now that {} is configured..."
        msg.setText(
            "Before uploading any folders to {}, it is <b>strongly "
            "recommended</b> that you <i>export a Recovery Key</i> and store "
            "it in a safe and secure location (such as an encrypted USB drive)"
            ".<p><p>Possessing a Recovery Key will allow you to restore "
            "access to any of the folders you've uploaded to {} in the event "
            "that something goes wrong (e.g., hardware failure, accidental "
            "data-loss).".format(gateway.name, gateway.name))
        msg.setDetailedText(
            "A 'Recovery Key' is a small file that contains enough information"
            " (a Tahoe-LAFS 'Introducer fURL' and 'rootcap') to re-establish "
            "a connection with your storage provider and restore your "
            "previously-uploaded folders. Because access to this file is "
            "sufficient to access to any of the the data you've stored, it is "
            "important that you keep this file safe and secure; do not share "
            "your Recovery Key with anybody!")
        reply = msg.exec_()
        if reply == QMessageBox.Yes:
            self.gui.main_window.export_recovery_key()  # XXX
        else:
            # TODO: Nag user; "Are you sure?"
            pass

    def finish_button_clicked(self):
        self.gui.show()
        self.close()
        self.prompt_for_export(self.gateway)
        self.reset()

    def closeEvent(self, event):
        if self.gui.main_window.gateways:
            event.accept()
        else:
            event.ignore()
            reply = QMessageBox.question(
                self, "Exit setup?", "{} has not yet been configured. "
                "Are you sure you wish to exit?".format(APP_NAME),
                QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
            if reply == QMessageBox.Yes:
                QCoreApplication.instance().quit()