示例#1
0
class InviteReceiver(QObject):

    # Wormhole
    got_welcome = Signal(dict)
    # got_code = Signal(str)
    got_introduction = Signal()
    got_message = Signal(dict)
    closed = Signal()

    # SetupRunner
    grid_already_joined = Signal(str)
    update_progress = Signal(str)
    got_icon = Signal(str)
    client_started = Signal(object)
    joined_folders = Signal(list)
    done = Signal(object)

    def __init__(self, known_gateways=None, use_tor=False):
        super(InviteReceiver, self).__init__()
        self.known_gateways = known_gateways
        self.use_tor = use_tor

        self.setup_runner = SetupRunner(known_gateways, use_tor)
        self.setup_runner.grid_already_joined.connect(
            self.grid_already_joined.emit
        )
        self.setup_runner.update_progress.connect(self.update_progress.emit)
        self.setup_runner.got_icon.connect(self.got_icon.emit)
        self.setup_runner.client_started.connect(self.client_started.emit)
        self.setup_runner.joined_folders.connect(self.joined_folders.emit)
        self.setup_runner.done.connect(self.done.emit)

        self.wormhole = Wormhole(use_tor)
        self.wormhole.got_welcome.connect(self.got_welcome.emit)
        self.wormhole.got_introduction.connect(self.got_introduction.emit)
        self.wormhole.got_message.connect(self.got_message.emit)
        self.wormhole.closed.connect(self.closed.emit)

    def cancel(self):
        self.wormhole.close()

    @inlineCallbacks
    def _run_setup(self, settings, from_wormhole):
        settings = validate_settings(
            settings, self.known_gateways, None, from_wormhole
        )
        yield self.setup_runner.run(settings)

    @inlineCallbacks
    def receive(self, code, settings=None):
        # TODO: Calculate/emit total steps
        if settings:
            yield self._run_setup(settings, from_wormhole=False)
        elif code.split("-")[0] == "0":
            settings = load_settings_from_cheatcode(code[2:])
            if settings:
                yield self._run_setup(settings, from_wormhole=False)
        else:
            settings = yield self.wormhole.receive(code)
            yield self._run_setup(settings, from_wormhole=True)
示例#2
0
def test_run_call_scan_rootcap_after_join_folders(monkeypatch):
    fake_gateway = Mock()
    fake_gateway.monitor.scan_rootcap = Mock()
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.get_gateway", lambda x, y, z: fake_gateway
    )
    monkeypatch.setattr("gridsync.setup.SetupRunner.join_grid", Mock())
    monkeypatch.setattr("gridsync.setup.SetupRunner.ensure_recovery", Mock())
    monkeypatch.setattr("gridsync.setup.SetupRunner.join_folders", Mock())
    sr = SetupRunner([])
    yield sr.run({"nickname": "TestGrid", "magic-folders": {"TestFolder": {}}})
    assert fake_gateway.monitor.scan_rootcap.call_count == 1
示例#3
0
def test_run_emit_done_signal(monkeypatch, qtbot):
    monkeypatch.setattr('gridsync.setup.SetupRunner.get_gateway',
                        lambda x, y, z: 'GatewayObj')
    monkeypatch.setattr('gridsync.setup.SetupRunner.join_grid',
                        lambda x, y: None)
    monkeypatch.setattr('gridsync.setup.SetupRunner.ensure_recovery',
                        lambda x, y: None)
    monkeypatch.setattr('gridsync.setup.SetupRunner.join_folders',
                        lambda x, y: None)
    sr = SetupRunner([])
    settings = {'nickname': 'TestGrid', 'magic-folders': {'TestFolder': {}}}
    with qtbot.wait_signal(sr.done) as blocker:
        yield sr.run(settings)
    assert blocker.args == ['GatewayObj']
示例#4
0
def test_run_join_grid_use_tor(monkeypatch):
    monkeypatch.setattr('gridsync.tor.get_tor', lambda _: 'FakeTorObject')
    monkeypatch.setattr('gridsync.setup.SetupRunner.get_gateway',
                        lambda x, y, z: None)
    monkeypatch.setattr('gridsync.setup.SetupRunner.join_grid',
                        lambda x, y: None)
    monkeypatch.setattr('gridsync.setup.SetupRunner.ensure_recovery',
                        lambda x, y: None)
    monkeypatch.setattr('gridsync.setup.SetupRunner.join_folders',
                        lambda x, y: None)
    sr = SetupRunner([], use_tor=True)
    settings = {'nickname': 'TestGrid', 'magic-folders': {'TestFolder': {}}}
    yield sr.run(settings)
    assert settings['hide-ip']
示例#5
0
def test_run_join_grid(monkeypatch):
    monkeypatch.setattr('gridsync.setup.SetupRunner.get_gateway',
                        lambda x, y, z: None)

    def fake_join_grid(*_):
        assert True

    monkeypatch.setattr('gridsync.setup.SetupRunner.join_grid', fake_join_grid)
    monkeypatch.setattr('gridsync.setup.SetupRunner.ensure_recovery',
                        lambda x, y: None)
    monkeypatch.setattr('gridsync.setup.SetupRunner.join_folders',
                        lambda x, y: None)
    sr = SetupRunner([])
    settings = {'nickname': 'TestGrid', 'magic-folders': {'TestFolder': {}}}
    yield sr.run(settings)
示例#6
0
def test_run_emit_grid_already_joined_signal(monkeypatch, qtbot):
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.get_gateway", lambda x, y, z: Mock()
    )
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.join_grid", lambda x, y: None
    )
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.ensure_recovery", lambda x, y: None
    )
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.join_folders", lambda x, y: None
    )
    sr = SetupRunner([])
    settings = {"nickname": "TestGrid"}
    with qtbot.wait_signal(sr.grid_already_joined) as blocker:
        yield sr.run(settings)
    assert blocker.args == ["TestGrid"]
示例#7
0
def test_run_join_grid_use_tor(monkeypatch):
    monkeypatch.setattr("gridsync.tor.get_tor", lambda _: "FakeTorObject")
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.get_gateway", lambda x, y, z: Mock()
    )
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.join_grid", lambda x, y: None
    )
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.ensure_recovery", lambda x, y: None
    )
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.join_folders", lambda x, y: None
    )
    sr = SetupRunner([], use_tor=True)
    settings = {"nickname": "TestGrid", "magic-folders": {"TestFolder": {}}}
    yield sr.run(settings)
    assert settings["hide-ip"]
示例#8
0
def test_run_join_grid(monkeypatch):
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.get_gateway", lambda x, y, z: Mock()
    )

    def fake_join_grid(*_):
        assert True

    monkeypatch.setattr("gridsync.setup.SetupRunner.join_grid", fake_join_grid)
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.ensure_recovery", lambda x, y: None
    )
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.join_folders", lambda x, y: None
    )
    sr = SetupRunner([])
    settings = {"nickname": "TestGrid", "magic-folders": {"TestFolder": {}}}
    yield sr.run(settings)
示例#9
0
def test_run_emit_done_signal(monkeypatch, qtbot):
    fake_gateway = Mock()
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.get_gateway", lambda x, y, z: fake_gateway
    )
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.join_grid", lambda x, y: None
    )
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.ensure_recovery", lambda x, y: None
    )
    monkeypatch.setattr(
        "gridsync.setup.SetupRunner.join_folders", lambda x, y: None
    )
    sr = SetupRunner([])
    settings = {"nickname": "TestGrid", "magic-folders": {"TestFolder": {}}}
    with qtbot.wait_signal(sr.done) as blocker:
        yield sr.run(settings)
    assert blocker.args == [fake_gateway]
示例#10
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()
示例#11
0
def test_run_raise_upgrade_required_error():
    sr = SetupRunner([])
    with pytest.raises(UpgradeRequiredError):
        yield sr.run({'version': 9999})
示例#12
0
class InviteReceiver(QDialog):
    done = pyqtSignal(QWidget)
    closed = pyqtSignal(QWidget)

    def __init__(self, gateways):
        super(InviteReceiver, self).__init__()
        self.gateways = gateways
        self.wormhole = None
        self.setup_runner = None
        self.joined_folders = []
        self.use_tor = False

        self.setMinimumSize(500, 300)

        self.mail_closed_icon = QLabel()
        self.mail_closed_icon.setPixmap(
            QPixmap(resource('mail-envelope-closed.png')).scaled(128, 128))
        self.mail_closed_icon.setAlignment(Qt.AlignCenter)

        self.mail_open_icon = QLabel()
        self.mail_open_icon.setPixmap(
            QPixmap(resource('mail-envelope-open.png')).scaled(128, 128))
        self.mail_open_icon.setAlignment(Qt.AlignCenter)

        self.folder_icon = QLabel()
        icon = QFileIconProvider().icon(QFileInfo(config_dir))
        self.folder_icon.setPixmap(icon.pixmap(128, 128))
        self.folder_icon.setAlignment(Qt.AlignCenter)

        self.invite_code_widget = InviteCodeWidget(self)
        self.label = self.invite_code_widget.label
        self.tor_checkbox = self.invite_code_widget.checkbox
        self.tor_checkbox.stateChanged.connect(self.on_checkbox_state_changed)
        self.lineedit = self.invite_code_widget.lineedit
        self.lineedit.error.connect(self.show_error)
        self.lineedit.go.connect(self.go)

        self.tor_label = QLabel()
        self.tor_label.setToolTip(
            "This connection is being routed through the Tor network.")
        self.tor_label.setPixmap(
            QPixmap(resource('tor-onion.png')).scaled(32, 32))

        self.progressbar = QProgressBar(self)
        self.progressbar.setValue(0)
        self.progressbar.setMaximum(6)  # XXX
        self.progressbar.setTextVisible(False)

        self.message_label = QLabel()
        self.message_label.setStyleSheet("color: grey")
        self.message_label.setAlignment(Qt.AlignCenter)

        self.error_label = QLabel()
        self.error_label.setStyleSheet("color: red")
        self.error_label.setAlignment(Qt.AlignCenter)

        self.close_button = QPushButton("Close")
        self.close_button.clicked.connect(self.close)

        layout = QGridLayout(self)
        layout.addItem(QSpacerItem(0, 0, 0, QSizePolicy.Expanding), 0, 0)
        layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, 0), 1, 1)
        layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, 0), 1, 2)
        layout.addWidget(self.mail_closed_icon, 1, 2, 1, 3)
        layout.addWidget(self.mail_open_icon, 1, 2, 1, 3)
        layout.addWidget(self.folder_icon, 1, 2, 1, 3)
        layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, 0), 1, 4)
        layout.addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, 0), 1, 5)
        layout.addWidget(self.label, 2, 3, 1, 1)
        layout.addWidget(self.tor_label, 3, 1, 1, 1,
                         Qt.AlignRight | Qt.AlignVCenter)
        layout.addWidget(self.lineedit, 3, 2, 1, 3)
        layout.addWidget(self.progressbar, 3, 2, 1, 3)
        layout.addWidget(self.tor_checkbox, 4, 2, 1, 3, Qt.AlignCenter)
        layout.addWidget(self.message_label, 5, 1, 1, 5)
        layout.addWidget(self.error_label, 5, 2, 1, 3)
        layout.addWidget(self.close_button, 6, 3)
        layout.addItem(QSpacerItem(0, 0, 0, QSizePolicy.Expanding), 7, 1)

        self.reset()

    def reset(self):
        self.mail_open_icon.hide()
        self.folder_icon.hide()
        self.mail_closed_icon.show()
        self.label.setText("Enter invite code:")
        self.lineedit.show()
        self.lineedit.setText('')
        self.tor_checkbox.show()
        self.progressbar.hide()
        self.message_label.setText(
            "Invite codes can be used to join a grid or a folder")
        self.error_label.setText('')
        self.error_label.hide()
        self.close_button.hide()
        self.tor_label.hide()

    def on_checkbox_state_changed(self, state):
        self.use_tor = bool(state)
        logging.debug("use_tor=%s", self.use_tor)
        if state:
            self.progressbar.setStyleSheet(
                'QProgressBar::chunk {{ background-color: {}; }}'.format(
                    TOR_PURPLE))
        else:
            self.progressbar.setStyleSheet('')

    def show_error(self, text):
        self.error_label.setText(text)
        self.message_label.hide()
        self.error_label.show()
        reactor.callLater(3, self.error_label.hide)
        reactor.callLater(3, self.message_label.show)

    def update_progress(self, message):
        step = self.progressbar.value() + 1
        self.progressbar.setValue(step)
        self.message_label.setText(message)
        if step == 3:
            self.mail_closed_icon.hide()
            self.mail_open_icon.show()
        if step == 4:
            self.mail_open_icon.hide()
            self.folder_icon.show()

    def set_joined_folders(self, folders):
        self.joined_folders = folders

    def on_done(self, _):
        self.progressbar.setValue(self.progressbar.maximum())
        self.close_button.show()
        self.done.emit(self)
        self.label.setPixmap(
            QPixmap(resource('green_checkmark.png')).scaled(32, 32))
        if self.joined_folders and len(self.joined_folders) == 1:
            target = self.joined_folders[0]
            self.message_label.setText(
                'Successfully joined folder "{0}"!\n"{0}" is now available '
                'for download'.format(target))
        elif self.joined_folders:
            target = humanized_list(self.joined_folders, 'folders')
            self.message_label.setText(
                'Successfully joined {0}!\n{0} are now available for '
                'download'.format(target))

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

    def got_message(self, message):
        self.update_progress("Reading invitation...")  # 3
        message = validate_settings(message, self.gateways, self)
        self.setup_runner = SetupRunner(self.gateways, self.use_tor)
        if not message.get('magic-folders'):
            self.setup_runner.grid_already_joined.connect(
                self.on_grid_already_joined)
        self.setup_runner.update_progress.connect(self.update_progress)
        self.setup_runner.joined_folders.connect(self.set_joined_folders)
        self.setup_runner.done.connect(self.on_done)
        self.setup_runner.run(message)

    def got_welcome(self):
        self.update_progress("Connected; waiting for message...")  # 2

    def handle_failure(self, failure):
        logging.error(str(failure))
        if failure.type == CancelledError and self.progressbar.value() > 2:
            return
        show_failure(failure, self)
        self.close()

    def go(self, code):
        self.reset()
        self.label.setText(' ')
        self.lineedit.hide()
        self.tor_checkbox.hide()
        self.progressbar.show()
        if self.use_tor:
            self.tor_label.show()
        self.update_progress("Verifying invitation...")  # 1
        if code.split('-')[0] == "0":
            settings = get_settings_from_cheatcode(code[2:])
            if settings:
                self.got_message(settings)
                return
        self.wormhole = Wormhole(self.use_tor)
        self.wormhole.got_welcome.connect(self.got_welcome)
        self.wormhole.got_message.connect(self.got_message)
        d = self.wormhole.receive(code)
        d.addErrback(self.handle_failure)
        reactor.callLater(30, d.cancel)

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

    def closeEvent(self, event):
        event.accept()
        try:
            self.wormhole.close()
        except AttributeError:
            pass
        self.closed.emit(self)

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.close()
示例#13
0
def test_run_join_grid_use_tor_raise_tor_error(monkeypatch):
    monkeypatch.setattr("gridsync.setup.get_tor_with_prompt", lambda _: None)
    sr = SetupRunner([], use_tor=True)
    settings = {"nickname": "TestGrid", "magic-folders": {"TestFolder": {}}}
    with pytest.raises(TorError):
        yield sr.run(settings)
示例#14
0
def test_run_join_grid_use_tor_raise_tor_error(monkeypatch):
    monkeypatch.setattr('gridsync.setup.get_tor_with_prompt', lambda _: None)
    sr = SetupRunner([], use_tor=True)
    settings = {'nickname': 'TestGrid', 'magic-folders': {'TestFolder': {}}}
    with pytest.raises(TorError):
        yield sr.run(settings)
示例#15
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()