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)
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
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']
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']
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)
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"]
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"]
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)
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]
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()
def test_run_raise_upgrade_required_error(): sr = SetupRunner([]) with pytest.raises(UpgradeRequiredError): yield sr.run({'version': 9999})
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()
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)
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)
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()