Exemple #1
0
    async def _handle_signal_attachment(
            self, intent: IntentAPI,
            attachment: Attachment) -> MediaMessageEventContent:
        self.log.trace(f"Reuploading attachment {attachment}")
        if not attachment.content_type:
            attachment.content_type = (magic.from_file(
                attachment.incoming_filename, mime=True) if magic is not None
                                       else "application/octet-stream")

        content = self._make_media_content(attachment)

        with open(attachment.incoming_filename, "rb") as file:
            data = file.read()
        if self.config["signal.remove_file_after_handling"]:
            os.remove(attachment.incoming_filename)

        upload_mime_type = attachment.content_type
        if self.encrypted and encrypt_attachment:
            data, content.file = encrypt_attachment(data)
            upload_mime_type = "application/octet-stream"

        content.url = await intent.upload_media(data,
                                                mime_type=upload_mime_type,
                                                filename=attachment.id)
        if content.file:
            content.file.url = content.url
            content.url = None
        return content
Exemple #2
0
    async def _reupload_instagram_file(self, source: 'u.User', url: str, msgtype: MessageType,
                                       info: FileInfo, intent: IntentAPI
                                       ) -> Optional[ReuploadedMediaInfo]:
        async with await source.client.raw_http_get(url) as resp:
            data = await resp.read()
            info.mimetype = resp.headers["Content-Type"] or magic.from_buffer(data, mime=True)
        info.size = len(data)
        extension = {
            "image/webp": ".webp",
            "image/jpeg": ".jpg",
            "video/mp4": ".mp4",
            "audio/mp4": ".m4a",
        }.get(info.mimetype)
        extension = extension or mimetypes.guess_extension(info.mimetype) or ""
        file_name = f"{msgtype.value[2:]}{extension}"

        upload_mime_type = info.mimetype
        upload_file_name = file_name
        decryption_info = None
        if self.encrypted and encrypt_attachment:
            data, decryption_info = encrypt_attachment(data)
            upload_mime_type = "application/octet-stream"
            upload_file_name = None

        mxc = await call_with_net_retry(intent.upload_media, data, mime_type=upload_mime_type,
                                        filename=upload_file_name, _action="upload media")

        if decryption_info:
            decryption_info.url = mxc
            mxc = None

        return ReuploadedMediaInfo(mxc=mxc, url=url, decryption_info=decryption_info,
                                   file_name=file_name, msgtype=msgtype, info=info)
Exemple #3
0
    async def _reupload_twitter_media(
            self, source: 'u.User', url: str,
            intent: IntentAPI) -> ReuploadedMediaInfo:
        file_name = URL(url).name
        data, mime_type = await source.client.download_media(url)

        upload_mime_type = mime_type
        upload_file_name = file_name
        decryption_info = None
        if self.encrypted and encrypt_attachment:
            data, decryption_info = encrypt_attachment(data)
            upload_mime_type = "application/octet-stream"
            upload_file_name = None

        mxc = await call_with_net_retry(intent.upload_media,
                                        data,
                                        mime_type=upload_mime_type,
                                        filename=upload_file_name,
                                        _action="upload media")

        if decryption_info:
            decryption_info.url = mxc
            mxc = None

        return ReuploadedMediaInfo(mxc, decryption_info, mime_type, file_name,
                                   len(data))
async def transfer_thumbnail_to_matrix(
        client: MautrixTelegramClient, intent: IntentAPI,
        thumbnail_loc: TypeLocation, video: bytes, mime: str,
        encrypt: bool) -> Optional[DBTelegramFile]:
    if not Image or not VideoFileClip:
        return None

    loc_id = _location_to_id(thumbnail_loc)
    if not loc_id:
        return None

    db_file = DBTelegramFile.get(loc_id)
    if db_file:
        return db_file

    video_ext = sane_mimetypes.guess_extension(mime)
    if VideoFileClip and video_ext and video:
        try:
            file, width, height = _read_video_thumbnail(video,
                                                        video_ext,
                                                        frame_ext="png")
        except OSError:
            return None
        mime_type = "image/png"
    else:
        file = await client.download_file(thumbnail_loc)
        width, height = None, None
        mime_type = magic.from_buffer(file, mime=True)

    decryption_info = None
    upload_mime_type = mime_type
    if encrypt:
        file, decryption_info_dict = encrypt_attachment(file)
        decryption_info = EncryptedFile.deserialize(decryption_info_dict)
        upload_mime_type = "application/octet-stream"
    content_uri = await intent.upload_media(file, upload_mime_type)
    if decryption_info:
        decryption_info.url = content_uri

    db_file = DBTelegramFile(id=loc_id,
                             mxc=content_uri,
                             mime_type=mime_type,
                             was_converted=False,
                             timestamp=int(time.time()),
                             size=len(file),
                             width=width,
                             height=height,
                             decryption_info=decryption_info)
    try:
        db_file.insert()
    except (IntegrityError, InvalidRequestError) as e:
        log.exception(
            f"{e.__class__.__name__} while saving transferred file thumbnail data. "
            "This was probably caused by two simultaneous transfers of the same file, "
            "and might (but probably won't) cause problems with thumbnails or something."
        )
    return db_file
Exemple #5
0
    async def process_hangouts_attachments(
            self, event: ChatMessageEvent,
            intent: IntentAPI) -> Optional[EventID]:
        attachments_pb = event._event.chat_message.message_content.attachment

        if len(event.attachments) > 1:
            self.log.warning("Can't handle more that one attachment")
            return None

        attachment = event.attachments[0]
        attachment_pb = attachments_pb[0]

        embed_item = attachment_pb.embed_item

        # Get the filename from the headers
        async with self.az.http_session.request("GET", attachment) as resp:
            value, params = cgi.parse_header(
                resp.headers["Content-Disposition"])
            mime = resp.headers["Content-Type"]
            filename = params.get('filename', attachment.split("/")[-1])

        # TODO: This also catches movies, but I can't work out how they present
        #       differently to images
        if embed_item.type[0] == hangouts.ITEM_TYPE_PLUS_PHOTO:
            data = await self._get_remote_bytes(attachment)
            upload_mime = mime
            decryption_info = None
            if self.encrypted and encrypt_attachment:
                data, decryption_info_dict = encrypt_attachment(data)
                decryption_info = EncryptedFile.deserialize(
                    decryption_info_dict)
                upload_mime = "application/octet-stream"
            mxc_url = await intent.upload_media(data,
                                                mime_type=upload_mime,
                                                filename=filename)
            if decryption_info:
                decryption_info.url = mxc_url
            content = MediaMessageEventContent(url=mxc_url,
                                               file=decryption_info,
                                               body=filename,
                                               info=ImageInfo(size=len(data),
                                                              mimetype=mime),
                                               msgtype=MessageType.IMAGE)
            return await self._send_message(intent,
                                            content,
                                            timestamp=event.timestamp)
        return None
Exemple #6
0
    async def _handle_signal_attachment(
            self, intent: IntentAPI,
            attachment: Attachment) -> MediaMessageEventContent:
        self.log.trace(f"Reuploading attachment {attachment}")
        if not attachment.content_type:
            attachment.content_type = (magic.from_file(
                attachment.incoming_filename, mime=True) if magic is not None
                                       else "application/octet-stream")

        content = self._make_media_content(attachment)

        # The bridge and signald can share files but have different filepaths. This can happen in
        # a Docker deployment when signald and this bridge are in different containers. In this
        # case, convert the file path from one context to another
        incoming_attachment_dir = self.config["signal.incoming_attachment_dir"]
        if incoming_attachment_dir is None:
            path = attachment.incoming_filename
        else:
            filename = os.path.basename(attachment.incoming_filename)
            path = os.path.join(self.config["signal.outgoing_attachment_dir"],
                                filename)
            self.log.debug(
                f"Changing attachment from {attachment.incoming_filename} to {path}"
            )

        with open(path, "rb") as file:
            data = file.read()
        if self.config["signal.remove_file_after_handling"]:
            os.remove(path)

        upload_mime_type = attachment.content_type
        if self.encrypted and encrypt_attachment:
            data, content.file = encrypt_attachment(data)
            upload_mime_type = "application/octet-stream"

        content.url = await intent.upload_media(data,
                                                mime_type=upload_mime_type,
                                                filename=attachment.id)
        if content.file:
            content.file.url = content.url
            content.url = None
        return content
Exemple #7
0
    async def _reupload_twitter_media(
            self, source: 'u.User', attachment: MessageAttachmentMedia,
            intent: IntentAPI) -> ReuploadedMediaInfo:
        file_name = path.basename(attachment.media_url_https)
        data, mime_type = await source.client.download_media(attachment)

        upload_mime_type = mime_type
        upload_file_name = file_name
        decryption_info = None
        if self.encrypted and encrypt_attachment:
            data, decryption_info = encrypt_attachment(data)
            upload_mime_type = "application/octet-stream"
            upload_file_name = None

        mxc = await intent.upload_media(data,
                                        mime_type=upload_mime_type,
                                        filename=upload_file_name)

        if decryption_info:
            decryption_info.url = mxc
            mxc = None

        return ReuploadedMediaInfo(mxc, decryption_info, mime_type, file_name,
                                   len(data))
Exemple #8
0
async def _unlocked_transfer_file_to_matrix(
        client: MautrixTelegramClient, intent: IntentAPI, loc_id: str,
        location: TypeLocation, thumbnail: TypeThumbnail, is_sticker: bool,
        tgs_convert: Optional[dict], filename: Optional[str], encrypt: bool,
        parallel_id: Optional[int]) -> Optional[DBTelegramFile]:
    db_file = DBTelegramFile.get(loc_id)
    if db_file:
        return db_file

    converted_anim = None

    if parallel_id and isinstance(location, Document) and (not is_sticker
                                                           or not tgs_convert):
        db_file = await parallel_transfer_to_matrix(client, intent, loc_id,
                                                    location, filename,
                                                    encrypt, parallel_id)
        mime_type = location.mime_type
        file = None
    else:
        try:
            file = await client.download_file(location)
        except (LocationInvalidError, FileIdInvalidError):
            return None
        except (AuthBytesInvalidError, AuthKeyInvalidError,
                SecurityError) as e:
            log.exception(f"{e.__class__.__name__} while downloading a file.")
            return None

        width, height = None, None
        mime_type = magic.from_buffer(file, mime=True)

        image_converted = False
        # A weird bug in alpine/magic makes it return application/octet-stream for gzips...
        is_tgs = (mime_type == "application/gzip"
                  or (mime_type == "application/octet-stream"
                      and magic.from_buffer(file).startswith("gzip")))
        if is_sticker and tgs_convert and is_tgs:
            converted_anim = await convert_tgs_to(file, tgs_convert["target"],
                                                  **tgs_convert["args"])
            mime_type = converted_anim.mime
            file = converted_anim.data
            width, height = converted_anim.width, converted_anim.height
            image_converted = mime_type != "application/gzip"
            thumbnail = None

        if mime_type == "image/webp":
            new_mime_type, file, width, height = convert_image(
                file,
                source_mime="image/webp",
                target_type="png",
                thumbnail_to=(256, 256) if is_sticker else None)
            image_converted = new_mime_type != mime_type
            mime_type = new_mime_type
            thumbnail = None

        decryption_info = None
        upload_mime_type = mime_type
        if encrypt and encrypt_attachment:
            file, decryption_info = encrypt_attachment(file)
            upload_mime_type = "application/octet-stream"
        content_uri = await intent.upload_media(file, upload_mime_type)
        if decryption_info:
            decryption_info.url = content_uri

        db_file = DBTelegramFile(id=loc_id,
                                 mxc=content_uri,
                                 decryption_info=decryption_info,
                                 mime_type=mime_type,
                                 was_converted=image_converted,
                                 timestamp=int(time.time()),
                                 size=len(file),
                                 width=width,
                                 height=height)
    if thumbnail and (mime_type.startswith("video/")
                      or mime_type == "image/gif"):
        if isinstance(thumbnail, (PhotoSize, PhotoCachedSize)):
            thumbnail = thumbnail.location
        try:
            db_file.thumbnail = await transfer_thumbnail_to_matrix(
                client,
                intent,
                thumbnail,
                video=file,
                mime_type=mime_type,
                encrypt=encrypt)
        except FileIdInvalidError:
            log.warning(f"Failed to transfer thumbnail for {thumbnail!s}",
                        exc_info=True)
    elif converted_anim and converted_anim.thumbnail_data:
        db_file.thumbnail = await transfer_thumbnail_to_matrix(
            client,
            intent,
            location,
            video=None,
            encrypt=encrypt,
            custom_data=converted_anim.thumbnail_data,
            mime_type=converted_anim.thumbnail_mime,
            width=converted_anim.width,
            height=converted_anim.height)

    try:
        db_file.insert()
    except (IntegrityError, InvalidRequestError) as e:
        log.exception(
            f"{e.__class__.__name__} while saving transferred file data. "
            "This was probably caused by two simultaneous transfers of the same file, "
            "and should not cause any problems.")
    return db_file