Пример #1
0
def test_tahoe_create_client_add_storage_servers(tmpdir, monkeypatch):
    nodedir = str(tmpdir.mkdir('TestGrid'))
    os.makedirs(os.path.join(nodedir, 'private'))
    monkeypatch.setattr('os.path.exists',
                        lambda _: False)  # suppress FileExistsError
    monkeypatch.setattr('gridsync.tahoe.Tahoe.command', lambda x, y: None)
    client = Tahoe(nodedir)
    storage_servers = {
        'node-1': {
            'anonymous-storage-FURL': 'pb://test',
            'nickname': 'One'
        }
    }
    settings = {'nickname': 'TestGrid', 'storage': storage_servers}
    yield client.create_client(**settings)
    assert client.get_storage_servers() == storage_servers
Пример #2
0
def test_tahoe_create_client_add_storage_servers(tmpdir, monkeypatch):
    nodedir = str(tmpdir.mkdir("TestGrid"))
    os.makedirs(os.path.join(nodedir, "private"))
    monkeypatch.setattr("os.path.exists",
                        lambda _: False)  # suppress FileExistsError
    monkeypatch.setattr("gridsync.tahoe.Tahoe.command", lambda x, y: None)
    client = Tahoe(nodedir)
    storage_servers = {
        "node-1": {
            "anonymous-storage-FURL": "pb://test",
            "nickname": "One"
        }
    }
    settings = {"nickname": "TestGrid", "storage": storage_servers}
    yield client.create_client(**settings)
    assert client.get_storage_servers() == storage_servers
Пример #3
0
 def start_gateways(self):
     nodedirs = get_nodedirs(config_dir)
     if nodedirs:
         yield self.select_executable()
         logging.debug("Starting Tahoe-LAFS gateway(s)...")
         for nodedir in nodedirs:
             gateway = Tahoe(nodedir, executable=self.executable)
             self.gateways.append(gateway)
             gateway.start()
         self.gui.populate(self.gateways)
     else:
         defaults = settings['default']
         if defaults['provider_name']:
             nodedir = os.path.join(config_dir, defaults['provider_name'])
             yield self.select_executable()
             gateway = Tahoe(nodedir, executable=self.executable)
             self.gateways.append(gateway)
             # TODO: Show setup progress dialog
             yield gateway.create_client(**defaults)
             gateway.start()
             self.gui.populate(self.gateways)
         else:
             self.gui.show_setup_form()
             yield self.select_executable()
Пример #4
0
 def start_gateways(self):
     nodedirs = get_nodedirs(config_dir)
     if nodedirs:
         minimize_preference = get_preference('startup', 'minimize')
         if not minimize_preference or minimize_preference == 'false':
             self.gui.show_main_window()
         yield self.select_executable()
         logging.debug("Starting Tahoe-LAFS gateway(s)...")
         for nodedir in nodedirs:
             gateway = Tahoe(
                 nodedir,
                 executable=self.executable,
                 multi_folder_support=self.multi_folder_support
             )
             self.gateways.append(gateway)
             d = gateway.start()
             d.addCallback(gateway.ensure_folder_links)
         self.gui.populate(self.gateways)
     else:
         defaults = settings['default']
         if defaults['provider_name']:
             nodedir = os.path.join(config_dir, defaults['provider_name'])
             yield self.select_executable()
             gateway = Tahoe(
                 nodedir,
                 executable=self.executable,
                 multi_folder_support=self.multi_folder_support
             )
             self.gateways.append(gateway)
             # TODO: Show setup progress dialog
             yield gateway.create_client(**defaults)
             gateway.start()
             self.gui.populate(self.gateways)
         else:
             self.gui.show_welcome_dialog()
             yield self.select_executable()
Пример #5
0
class SetupRunner(QObject):

    grid_already_joined = pyqtSignal(str)
    update_progress = pyqtSignal(str)
    client_started = pyqtSignal(object)
    joined_folders = pyqtSignal(list)
    got_icon = pyqtSignal(str)
    done = pyqtSignal(object)

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

    def get_gateway(self, introducer, servers):
        if not self.known_gateways:
            return None
        for gateway in self.known_gateways:
            target_introducer = gateway.config_get("client", "introducer.furl")
            if introducer and introducer == target_introducer:
                return gateway
            target_servers = gateway.get_storage_servers()
            if servers and servers == target_servers:
                return gateway
        return None

    def calculate_total_steps(self, settings):
        steps = 1  # done
        if not self.get_gateway(settings.get("introducer"),
                                settings.get("storage")):
            steps += 4  # create, start, await_ready, rootcap
        folders = settings.get("magic-folders")
        if folders:
            steps += len(folders)  # join
        return steps

    def decode_icon(self, s, dest):
        with atomic_write(dest, mode="wb", overwrite=True) as f:
            try:
                f.write(base64.b64decode(s))
            except (Error, TypeError):
                return
        self.got_icon.emit(dest)

    @inlineCallbacks
    def fetch_icon(self, url, dest):
        agent = None
        if self.use_tor:
            tor = yield get_tor(reactor)
            if not tor:
                raise TorError("Could not connect to a running Tor daemon")
            agent = tor.web_agent()
        resp = yield treq.get(url, agent=agent)
        if resp.code == 200:
            content = yield treq.content(resp)
            log.debug("Received %i bytes", len(content))
            with atomic_write(dest, mode="wb", overwrite=True) as f:
                f.write(content)
            self.got_icon.emit(dest)
        else:
            log.warning("Error fetching service icon: %i", resp.code)

    @inlineCallbacks  # noqa: max-complexity=14 XXX
    def join_grid(self, settings):  # noqa: max-complexity=14 XXX
        nickname = settings["nickname"]
        if self.use_tor:
            msg = "Connecting to {} via Tor...".format(nickname)
        else:
            msg = "Connecting to {}...".format(nickname)
        self.update_progress.emit(msg)

        icon_path = None
        if nickname == "Least Authority S4":
            icon_path = resource("leastauthority.com.icon")
            self.got_icon.emit(icon_path)
        elif "icon_base64" in settings:
            icon_path = os.path.join(config_dir, ".icon.tmp")
            self.decode_icon(settings["icon_base64"], icon_path)
        elif "icon_url" in settings:
            # A temporary(?) measure to get around the performance issues
            # observed when transferring a base64-encoded icon through Least
            # Authority's wormhole server. Hopefully this will go away.. See:
            # https://github.com/LeastAuthority/leastauthority.com/issues/539
            log.debug("Fetching service icon from %s...", settings["icon_url"])
            icon_path = os.path.join(config_dir, ".icon.tmp")
            try:
                # It's probably not worth cancelling or holding-up the setup
                # process if fetching/writing the icon fails (particularly
                # if doing so would require the user to get a new invite code)
                # so just log a warning for now if something goes wrong...
                yield self.fetch_icon(settings["icon_url"], icon_path)
            except Exception as e:  # pylint: disable=broad-except
                log.warning("Error fetching service icon: %s", str(e))

        executable = yield select_executable()
        nodedir = os.path.join(config_dir, nickname)
        self.gateway = Tahoe(nodedir, executable=executable)
        yield self.gateway.create_client(**settings)

        newscap = settings.get("newscap")
        if newscap:
            with atomic_write(
                    os.path.join(nodedir, "private", "newscap"),
                    mode="w",
                    overwrite=True,
            ) as f:
                f.write(newscap)

        if icon_path:
            try:
                shutil.copy(icon_path, os.path.join(nodedir, "icon"))
            except OSError as err:
                log.warning("Error copying icon file: %s", str(err))
        if "icon_url" in settings:
            try:
                with atomic_write(os.path.join(nodedir, "icon.url"),
                                  mode="w",
                                  overwrite=True) as f:
                    f.write(settings["icon_url"])
            except OSError as err:
                log.warning("Error writing icon url to file: %s", str(err))

        self.update_progress.emit(msg)
        yield self.gateway.start()
        self.client_started.emit(self.gateway)
        self.update_progress.emit(msg)
        yield self.gateway.await_ready()

    @inlineCallbacks
    def ensure_recovery(self, settings):
        settings_path = os.path.join(self.gateway.nodedir, "private",
                                     "settings.json")
        if settings.get("rootcap"):
            self.update_progress.emit("Loading Recovery Key...")
            with atomic_write(self.gateway.rootcap_path,
                              mode="w",
                              overwrite=True) as f:
                f.write(settings["rootcap"])
            with atomic_write(settings_path, mode="w", overwrite=True) as f:
                f.write(json.dumps(settings))
        else:
            self.update_progress.emit("Generating Recovery Key...")
            try:
                settings["rootcap"] = yield self.gateway.create_rootcap()
            except OSError:  # XXX Rootcap file already exists
                pass
            with atomic_write(settings_path, mode="w", overwrite=True) as f:
                f.write(json.dumps(settings))
            settings_cap = yield self.gateway.upload(settings_path)
            yield self.gateway.link(self.gateway.rootcap, "settings.json",
                                    settings_cap)

    @inlineCallbacks
    def join_folders(self, folders_data):
        folders = []
        for folder, data in folders_data.items():
            self.update_progress.emit('Joining folder "{}"...'.format(folder))
            collective, personal = data["code"].split("+")
            yield self.gateway.link(
                self.gateway.get_rootcap(),
                folder + " (collective)",
                collective,
            )
            yield self.gateway.link(self.gateway.get_rootcap(),
                                    folder + " (personal)", personal)
            folders.append(folder)
        if folders:
            self.joined_folders.emit(folders)

    @inlineCallbacks
    def run(self, settings):
        if "version" in settings and int(settings["version"]) > 1:
            raise UpgradeRequiredError

        if self.use_tor or "hide-ip" in settings or is_onion_grid(settings):
            settings["hide-ip"] = True
            self.use_tor = True
            tor = yield get_tor_with_prompt(reactor)
            if not tor:
                raise TorError("Could not connect to a running Tor daemon")

        self.gateway = self.get_gateway(settings.get("introducer"),
                                        settings.get("storage"))
        folders_data = settings.get("magic-folders")
        if not self.gateway:
            yield self.join_grid(settings)
            yield self.ensure_recovery(settings)
        elif not folders_data:
            self.grid_already_joined.emit(settings.get("nickname"))
        if folders_data:
            yield self.join_folders(folders_data)
            yield self.gateway.monitor.scan_rootcap()

        self.update_progress.emit("Done!")
        self.done.emit(self.gateway)
Пример #6
0
class SetupRunner(QObject):

    grid_already_joined = pyqtSignal(str)
    update_progress = pyqtSignal(str)
    joined_folders = pyqtSignal(list)
    got_icon = pyqtSignal(str)
    done = pyqtSignal(object)

    def __init__(self, known_gateways):
        super(SetupRunner, self).__init__()
        self.known_gateways = known_gateways
        self.gateway = None

    def get_gateway(self, introducer, servers):
        if not self.known_gateways:
            return None
        for gateway in self.known_gateways:
            target_introducer = gateway.config_get('client', 'introducer.furl')
            if introducer and introducer == target_introducer:
                return gateway
            target_servers = gateway.get_storage_servers()
            if servers and servers == target_servers:
                return gateway
        return None

    def calculate_total_steps(self, settings):
        steps = 1  # done
        if not self.get_gateway(settings.get('introducer'),
                                settings.get('storage')):
            steps += 4  # create, start, await_ready, rootcap
        folders = settings.get('magic-folders')
        if folders:
            steps += len(folders)  # join
        return steps

    def decode_icon(self, s, dest):
        with open(dest, 'wb') as f:
            try:
                f.write(base64.b64decode(s))
            except (Error, TypeError):
                return
        self.got_icon.emit(dest)

    @inlineCallbacks
    def fetch_icon(self, url, dest):
        resp = yield treq.get(url)
        if resp.code == 200:
            content = yield treq.content(resp)
            log.debug("Received %i bytes", len(content))
            with open(dest, 'wb') as f:
                f.write(content)
            self.got_icon.emit(dest)
        else:
            log.warning("Error fetching service icon: %i", resp.code)

    @inlineCallbacks  # noqa: max-complexity=13 XXX
    def join_grid(self, settings):
        nickname = settings['nickname']
        self.update_progress.emit('Connecting to {}...'.format(nickname))
        icon_path = None
        if nickname == 'Least Authority S4':
            icon_path = resource('leastauthority.com.icon')
            self.got_icon.emit(icon_path)
        elif 'icon_base64' in settings:
            icon_path = os.path.join(config_dir, '.icon.tmp')
            self.decode_icon(settings['icon_base64'], icon_path)
        elif 'icon_url' in settings:
            # A temporary(?) measure to get around the performance issues
            # observed when transferring a base64-encoded icon through Least
            # Authority's wormhole server. Hopefully this will go away.. See:
            # https://github.com/LeastAuthority/leastauthority.com/issues/539
            log.debug("Fetching service icon from %s...", settings['icon_url'])
            icon_path = os.path.join(config_dir, '.icon.tmp')
            try:
                # It's probably not worth cancelling or holding-up the setup
                # process if fetching/writing the icon fails (particularly
                # if doing so would require the user to get a new invite code)
                # so just log a warning for now if something goes wrong...
                yield self.fetch_icon(settings['icon_url'], icon_path)
            except Exception as e:  # pylint: disable=broad-except
                log.warning("Error fetching service icon: %s", str(e))

        executable, multi_folder_support = yield select_executable()
        nodedir = os.path.join(config_dir, nickname)
        self.gateway = Tahoe(nodedir,
                             executable=executable,
                             multi_folder_support=multi_folder_support)
        yield self.gateway.create_client(**settings)

        if icon_path:
            try:
                shutil.copy(icon_path, os.path.join(nodedir, 'icon'))
            except OSError as err:
                log.warning("Error copying icon file: %s", str(err))
        if 'icon_url' in settings:
            try:
                with open(os.path.join(nodedir, 'icon.url'), 'w') as f:
                    f.write(settings['icon_url'])
            except OSError as err:
                log.warning("Error writing icon url to file: %s", str(err))

        self.update_progress.emit('Connecting to {}...'.format(nickname))
        yield self.gateway.start()

        self.update_progress.emit('Connecting to {}...'.format(nickname))
        yield self.gateway.await_ready()

    @inlineCallbacks
    def ensure_recovery(self, settings):
        settings_path = os.path.join(self.gateway.nodedir, 'private',
                                     'settings.json')
        if settings.get('rootcap'):
            self.update_progress.emit('Loading Recovery Key...')
            with open(self.gateway.rootcap_path, 'w') as f:  # XXX
                f.write(settings['rootcap'])
            with open(settings_path, 'w') as f:
                f.write(json.dumps(settings))
        else:
            self.update_progress.emit('Generating Recovery Key...')
            try:
                settings['rootcap'] = yield self.gateway.create_rootcap()
            except OSError:  # XXX Rootcap file already exists
                pass
            with open(settings_path, 'w') as f:
                f.write(json.dumps(settings))
            settings_cap = yield self.gateway.upload(settings_path)
            yield self.gateway.link(self.gateway.rootcap, 'settings.json',
                                    settings_cap)

    @inlineCallbacks
    def join_folders(self, folders_data):
        folders = []
        for folder, data in folders_data.items():
            self.update_progress.emit('Joining folder "{}"...'.format(folder))
            collective, personal = data['code'].split('+')
            yield self.gateway.link(self.gateway.get_rootcap(),
                                    folder + ' (collective)', collective)
            yield self.gateway.link(self.gateway.get_rootcap(),
                                    folder + ' (personal)', personal)
            folders.append(folder)
        if folders:
            self.joined_folders.emit(folders)

    @inlineCallbacks
    def run(self, settings):
        if 'version' in settings and int(settings['version']) > 1:
            raise UpgradeRequiredError

        self.gateway = self.get_gateway(settings.get('introducer'),
                                        settings.get('storage'))
        if not self.gateway:
            yield self.join_grid(settings)
        else:
            self.grid_already_joined.emit(settings.get('nickname'))

        yield self.ensure_recovery(settings)

        folders_data = settings.get('magic-folders')
        if folders_data:
            yield self.join_folders(folders_data)

        self.update_progress.emit('Done!')
        self.done.emit(self.gateway)