コード例 #1
0
ファイル: daemon.py プロジェクト: wellic/pantalaimon
    def __attrs_post_init__(self):
        loop = asyncio.get_event_loop()

        self.homeserver_url = self.homeserver.geturl()
        self.hostname = self.homeserver.hostname
        self.store = PanStore(self.data_dir)
        accounts = self.store.load_users(self.name)
        self.media_info = self.store.load_media(self.name)

        for user_id, device_id in accounts:
            if self.conf.keyring:
                try:
                    token = keyring.get_password(
                        "pantalaimon", f"{user_id}-{device_id}-token"
                    )
                except RuntimeError as e:
                    logger.error(e)
            else:
                token = self.store.load_access_token(user_id, device_id)

            if not token:
                logger.warn(
                    f"Not restoring client for {user_id} {device_id}, "
                    f"missing access token."
                )
                continue

            logger.info(f"Restoring client for {user_id} {device_id}")

            pan_client = PanClient(
                self.name,
                self.store,
                self.conf,
                self.homeserver_url,
                self.send_queue,
                user_id,
                device_id,
                store_path=self.data_dir,
                ssl=self.ssl,
                proxy=self.proxy,
                store_class=self.client_store_class,
                media_info=self.media_info,
            )
            pan_client.user_id = user_id
            pan_client.access_token = token
            pan_client.load_store()
            self.pan_clients[user_id] = pan_client

            loop.create_task(
                self.send_ui_message(
                    UpdateUsersMessage(self.name, user_id, pan_client.device_id)
                )
            )

            loop.create_task(pan_client.send_update_devices(pan_client.device_store))

            pan_client.start_loop()
コード例 #2
0
ファイル: daemon.py プロジェクト: wellic/pantalaimon
    async def start_pan_client(self, access_token, user, user_id, password):
        client = ClientInfo(user_id, access_token)
        self.client_info[access_token] = client
        self.store.save_server_user(self.name, user_id)

        if user_id in self.pan_clients:
            logger.info(
                f"Background sync client already exists for {user_id},"
                f" not starting new one"
            )
            return

        pan_client = PanClient(
            self.name,
            self.store,
            self.conf,
            self.homeserver_url,
            self.send_queue,
            user_id,
            store_path=self.data_dir,
            ssl=self.ssl,
            proxy=self.proxy,
            store_class=self.client_store_class,
            media_info=self.media_info,
        )
        response = await pan_client.login(password, "pantalaimon")

        if not isinstance(response, LoginResponse):
            await pan_client.close()
            return

        logger.info(f"Succesfully started new background sync client for " f"{user_id}")

        await self.send_ui_message(
            UpdateUsersMessage(self.name, user_id, pan_client.device_id)
        )

        self.pan_clients[user_id] = pan_client

        if self.conf.keyring:
            try:
                keyring.set_password(
                    "pantalaimon",
                    f"{user_id}-{pan_client.device_id}-token",
                    pan_client.access_token,
                )
            except RuntimeError as e:
                logger.error(e)
        else:
            self.store.save_access_token(
                user_id, pan_client.device_id, pan_client.access_token
            )

        pan_client.start_loop()
コード例 #3
0
        def run(self):
            self.loop = GLib.MainLoop()

            if self.config.notifications:
                try:
                    notify2.init("pantalaimon", mainloop=self.loop)
                    self.notifications = True
                except dbus.DBusException:
                    logger.error(
                        "Notifications are enabled but no notification "
                        "server could be found, disabling notifications.")
                    self.notifications = False

            GLib.timeout_add(100, self.message_callback)
            self.loop.run()
コード例 #4
0
    async def start_pan_client(self,
                               access_token,
                               user,
                               user_id,
                               password,
                               device_id=None):
        client = ClientInfo(user_id, access_token)
        self.client_info[access_token] = client
        self.store.save_server_user(self.name, user_id)

        if user_id in self.pan_clients:
            logger.info(f"Background sync client already exists for {user_id},"
                        f" not starting new one")
            return

        pan_client = PanClient(
            self.name,
            self.store,
            self.conf,
            self.homeserver_url,
            self.send_queue,
            user_id,
            store_path=self.data_dir,
            ssl=self.ssl,
            proxy=self.proxy,
            store_class=self.client_store_class,
            media_info=self.media_info,
        )

        if password == "":
            if device_id is None:
                logger.warn(
                    "Empty password provided and device_id was also None, not "
                    "starting background sync client ")
                return
            # If password is blank, we cannot login normally and must
            # fall back to using the provided device_id.
            pan_client.restore_login(user_id, device_id, access_token)
        else:
            response = await pan_client.login(password, "pantalaimon")

            if not isinstance(response, LoginResponse):
                await pan_client.close()
                return

        logger.info(f"Succesfully started new background sync client for "
                    f"{user_id}")

        await self.send_ui_message(
            UpdateUsersMessage(self.name, user_id, pan_client.device_id))

        self.pan_clients[user_id] = pan_client

        if self.conf.keyring:
            try:
                keyring.set_password(
                    "pantalaimon",
                    f"{user_id}-{pan_client.device_id}-token",
                    pan_client.access_token,
                )
            except RuntimeError as e:
                logger.error(e)
        else:
            self.store.save_access_token(user_id, pan_client.device_id,
                                         pan_client.access_token)

        pan_client.start_loop()
コード例 #5
0
ファイル: daemon.py プロジェクト: aspacca/pantalaimon
    async def send_message(self, request):
        access_token = self.get_access_token(request)

        if not access_token:
            return self._missing_token

        client = await self._find_client(access_token)
        if not client:
            return self._unknown_token

        room_id = request.match_info["room_id"]

        try:
            room = client.rooms[room_id]
            encrypt = room.encrypted
        except KeyError:
            # The room is not in the joined rooms list, either the pan client
            # didn't manage to sync the state or we're not joined, in either
            # case send an error response.
            if client.has_been_synced:
                return web.json_response(
                    {
                        "errcode": "M_FORBIDDEN",
                        "error":
                        "You do not have permission to send the event."
                    },
                    headers=CORS_HEADERS,
                    status=403,
                )
            else:
                logger.error("The internal Pantalaimon client did not manage "
                             "to sync with the server.")
                return web.json_response(
                    {
                        "errcode":
                        "M_UNKNOWN",
                        "error":
                        "The pantalaimon client did not manage to sync with "
                        "the server",
                    },
                    headers=CORS_HEADERS,
                    status=500,
                )

        # Don't encrypt reactions for now - they are weird and clients
        # need to support them like this.
        # TODO: Fix when MSC1849 is fully supported by clients.
        if request.match_info["event_type"] == "m.reaction":
            encrypt = False

        msgtype = request.match_info["event_type"]

        try:
            content = await request.json()
        except (JSONDecodeError, ContentTypeError):
            return self._not_json

        # The room isn't encrypted just forward the message.
        if not encrypt:
            content_msgtype = content.get("msgtype")
            if (content_msgtype in ["m.image", "m.video", "m.audio", "m.file"]
                    or msgtype == "m.room.avatar"):
                try:
                    content["url"] = await self._decrypt_uri(
                        content["url"], client)
                    if ("info" in content
                            and "thumbnail_url" in content["info"]
                            and not content["info"]["thumbnail_url"] == None):
                        content["info"][
                            "thumbnail_url"] = await self._decrypt_uri(
                                content["info"]["thumbnail_url"], client)
                    return await self.forward_to_web(request,
                                                     data=json.dumps(content),
                                                     token=client.access_token)
                except ClientConnectionError as e:
                    return web.Response(status=500, text=str(e))
                except (KeyError, NotDecryptedAvailableError):
                    return await self.forward_to_web(request,
                                                     token=client.access_token)

            return await self.forward_to_web(request,
                                             token=client.access_token)

        txnid = request.match_info.get("txnid", uuid4())

        async def _send(ignore_unverified=False):
            try:
                content_msgtype = content.get("msgtype")
                if (content_msgtype
                        in ["m.image", "m.video", "m.audio", "m.file"]
                        or msgtype == "m.room.avatar"):
                    upload_info, media_info = self._get_upload_and_media_info(
                        content["url"])
                    if not upload_info or not media_info:
                        response = await client.room_send(
                            room_id, msgtype, content, txnid,
                            ignore_unverified)

                        return web.Response(
                            status=response.transport_response.status,
                            content_type=response.transport_response.
                            content_type,
                            headers=CORS_HEADERS,
                            body=await response.transport_response.read(),
                        )

                    media_info.to_content(content, upload_info.mimetype)
                    if content["info"].get("thumbnail_url", False):
                        (
                            thumb_upload_info,
                            thumb_media_info,
                        ) = self._get_upload_and_media_info(
                            content["info"]["thumbnail_url"])
                        if thumb_upload_info and thumb_media_info:
                            thumb_media_info.to_thumbnail(
                                content, thumb_upload_info.mimetype)

                    response = await client.room_send(room_id, msgtype,
                                                      content, txnid,
                                                      ignore_unverified)
                else:
                    response = await client.room_send(room_id, msgtype,
                                                      content, txnid,
                                                      ignore_unverified)

                return web.Response(
                    status=response.transport_response.status,
                    content_type=response.transport_response.content_type,
                    headers=CORS_HEADERS,
                    body=await response.transport_response.read(),
                )
            except ClientConnectionError as e:
                return web.Response(status=500, text=str(e))
            except SendRetryError as e:
                return web.Response(status=503, text=str(e))

        # Aquire a semaphore here so we only send out one
        # UnverifiedDevicesSignal
        sem = client.send_semaphores[room_id]

        async with sem:
            # Even though we request the full state we don't receive room
            # members since we're using lazy loading. The summary is for some
            # reason empty so nio can't know if room members are missing from
            # our state. Fetch the room members here instead.
            if not client.room_members_fetched[room_id]:
                try:
                    await client.joined_members(room_id)
                    client.room_members_fetched[room_id] = True
                except ClientConnectionError as e:
                    return web.Response(status=500, text=str(e))

            try:
                return await _send(self.conf.ignore_verification)
            except OlmTrustError as e:
                # There are unverified/unblocked devices in the room, notify
                # the UI thread about this and wait for a response.
                queue = asyncio.Queue()
                client.send_decision_queues[room_id] = queue

                message = UnverifiedDevicesSignal(client.user_id, room_id,
                                                  room.display_name)

                await self.send_ui_message(message)

                try:
                    response = await asyncio.wait_for(
                        queue.get(), self.unverified_send_timeout)

                    if isinstance(response, CancelSendingMessage):
                        # The send was canceled notify the client that sent the
                        # request about this.
                        info_msg = (f"Canceled message sending for room "
                                    f"{room.display_name} ({room_id}).")
                        logger.info(info_msg)
                        await self.send_response(response.message_id,
                                                 client.user_id, "m.ok",
                                                 info_msg)

                        return web.Response(status=503, text=str(e))

                    elif isinstance(response, SendAnywaysMessage):
                        # We are sending and ignoring devices along the way.
                        info_msg = (f"Ignoring unverified devices and sending "
                                    f"message to room "
                                    f"{room.display_name} ({room_id}).")
                        logger.info(info_msg)
                        await self.send_response(response.message_id,
                                                 client.user_id, "m.ok",
                                                 info_msg)

                        ret = await _send(True)
                        await client.send_update_devices(
                            client.room_devices(room_id))
                        return ret

                except asyncio.TimeoutError:
                    # We didn't get a response to our signal, send out an error
                    # response.

                    return web.Response(
                        status=503,
                        text=(f"Room contains unverified devices and no "
                              f"action was taken for "
                              f"{self.unverified_send_timeout} seconds, "
                              f"request timed out"),
                    )

                finally:
                    client.send_decision_queues.pop(room_id)