Ejemplo n.º 1
0
    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"]

        # The room is not in the joined rooms list, just forward it.
        try:
            room = client.rooms[room_id]
            encrypt = room.encrypted
        except KeyError:
            return await self.forward_to_web(request,
                                             token=client.access_token)

        # 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

        # The room isn't encrypted just forward the message.
        if not encrypt:
            return await self.forward_to_web(request,
                                             token=client.access_token)

        msgtype = request.match_info["event_type"]
        txnid = request.match_info.get("txnid", uuid4())

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

        async def _send(ignore_unverified=False):
            try:
                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)
Ejemplo n.º 2
0
    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)