From 8481b5c66bc798be2280f0a25092e5d704f87961 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Mon, 1 Apr 2024 17:51:11 +0200 Subject: [PATCH] Finalize --- composer.json | 5 +- src/FileId.php | 102 ++++++++++----------- src/Tools.php | 205 +++++++++++++++++++++++++++++++++++++++++++ src/UniqueFileId.php | 28 +++--- src/functions.php | 203 ------------------------------------------ 5 files changed, 271 insertions(+), 272 deletions(-) create mode 100644 src/Tools.php delete mode 100644 src/functions.php diff --git a/composer.json b/composer.json index bbdeac8..eb2c95c 100644 --- a/composer.json +++ b/composer.json @@ -22,10 +22,7 @@ "autoload": { "psr-4": { "danog\\Decoder\\": "src/" - }, - "files": [ - "src/functions.php" - ] + } }, "autoload-dev": { "psr-4": { diff --git a/src/FileId.php b/src/FileId.php index dd2d84c..8e127f4 100644 --- a/src/FileId.php +++ b/src/FileId.php @@ -111,7 +111,7 @@ final class FileId public static function fromBotAPI(string $fileId): self { $orig = $fileId; - $fileId = rleDecode(base64urlDecode($fileId)); + $fileId = Tools::rleDecode(Tools::base64urlDecode($fileId)); $version = \ord($fileId[\strlen($fileId) - 1]); $subVersion = $version === 4 ? \ord($fileId[\strlen($fileId) - 2]) : 0; @@ -126,16 +126,16 @@ final class FileId return $res; }; - $typeId = unpackInt($read(4)); - $dc_id = unpackInt($read(4)); - $fileReference = $typeId & FILE_REFERENCE_FLAG ? readTLString($fileId) : null; - $hasWebLocation = (bool) ($typeId & WEB_LOCATION_FLAG); - $typeId &= ~FILE_REFERENCE_FLAG; - $typeId &= ~WEB_LOCATION_FLAG; + $typeId = Tools::unpackInt($read(4)); + $dc_id = Tools::unpackInt($read(4)); + $fileReference = $typeId & Tools::FILE_REFERENCE_FLAG ? Tools::readTLString($fileId) : null; + $hasWebLocation = (bool) ($typeId & Tools::WEB_LOCATION_FLAG); + $typeId &= ~Tools::FILE_REFERENCE_FLAG; + $typeId &= ~Tools::WEB_LOCATION_FLAG; if ($hasWebLocation) { - $url = readTLString($fileId); - $access_hash = unpackLong($read(8)); + $url = Tools::readTLString($fileId); + $access_hash = Tools::unpackLong($read(8)); return new self( $dc_id, FileIdType::from($typeId), @@ -147,28 +147,28 @@ final class FileId subVersion: $subVersion ); } - $id = unpackLong($read(8)); - $access_hash = unpackLong($read(8)); + $id = Tools::unpackLong($read(8)); + $access_hash = Tools::unpackLong($read(8)); $volume_id = null; $local_id = null; $photoSizeSource = null; if ($typeId <= FileIdType::PHOTO->value) { if ($subVersion < 32) { - $volume_id = unpackLong($read(8)); - $local_id = unpackInt($read(4)); + $volume_id = Tools::unpackLong($read(8)); + $local_id = Tools::unpackInt($read(4)); } /** @psalm-suppress MixedArgument */ $photosize_source = PhotoSizeSourceType::from($subVersion >= 4 ? \unpack('V', $read(4))[1] : 0); switch ($photosize_source) { case PhotoSizeSourceType::LEGACY: - $photoSizeSource = new PhotoSizeSourceLegacy(unpackLong($read(8))); + $photoSizeSource = new PhotoSizeSourceLegacy(Tools::unpackLong($read(8))); break; case PhotoSizeSourceType::FULL_LEGACY: - $volume_id = unpackLong($read(8)); - $photoSizeSource = new PhotoSizeSourceLegacy(unpackLong($read(8))); - $local_id = unpackInt($read(4)); + $volume_id = Tools::unpackLong($read(8)); + $photoSizeSource = new PhotoSizeSourceLegacy(Tools::unpackLong($read(8))); + $local_id = Tools::unpackInt($read(4)); break; case PhotoSizeSourceType::THUMBNAIL: /** @var array{file_type: int, thumbnail_type: string} */ @@ -184,14 +184,14 @@ final class FileId ? PhotoSizeSourceDialogPhotoSmall::class : PhotoSizeSourceDialogPhotoBig::class; $photoSizeSource = new $clazz( - unpackLong($read(8)), - unpackLong($read(8)), + Tools::unpackLong($read(8)), + Tools::unpackLong($read(8)), ); break; case PhotoSizeSourceType::STICKERSET_THUMBNAIL: $photoSizeSource = new PhotoSizeSourceStickersetThumbnail( - unpackLong($read(8)), - unpackLong($read(8)) + Tools::unpackLong($read(8)), + Tools::unpackLong($read(8)) ); break; case PhotoSizeSourceType::DIALOGPHOTO_BIG_LEGACY: @@ -200,27 +200,27 @@ final class FileId ? PhotoSizeSourceDialogPhotoSmall::class : PhotoSizeSourceDialogPhotoBig::class; $photoSizeSource = new $clazz( - unpackLong($read(8)), - unpackLong($read(8)) + Tools::unpackLong($read(8)), + Tools::unpackLong($read(8)) ); - $volume_id = unpackLong($read(8)); - $local_id = unpackInt($read(4)); + $volume_id = Tools::unpackLong($read(8)); + $local_id = Tools::unpackInt($read(4)); break; case PhotoSizeSourceType::STICKERSET_THUMBNAIL_LEGACY: $photoSizeSource = new PhotoSizeSourceStickersetThumbnail( - unpackLong($read(8)), - unpackLong($read(8)), + Tools::unpackLong($read(8)), + Tools::unpackLong($read(8)), ); - $volume_id = unpackLong($read(8)); - $local_id = unpackInt($read(4)); + $volume_id = Tools::unpackLong($read(8)); + $local_id = Tools::unpackInt($read(4)); break; case PhotoSizeSourceType::STICKERSET_THUMBNAIL_VERSION: $photoSizeSource = new PhotoSizeSourceStickersetThumbnailVersion( - unpackLong($read(8)), - unpackLong($read(8)), - unpackInt($read(4)) + Tools::unpackLong($read(8)), + Tools::unpackLong($read(8)), + Tools::unpackInt($read(4)) ); break; } @@ -253,25 +253,25 @@ final class FileId { $type = $this->type->value; if ($this->fileReference !== null) { - $type |= FILE_REFERENCE_FLAG; + $type |= Tools::FILE_REFERENCE_FLAG; } if ($this->url !== null) { - $type |= WEB_LOCATION_FLAG; + $type |= Tools::WEB_LOCATION_FLAG; } $fileId = \pack('VV', $type, $this->dcId); if ($this->fileReference !== null) { - $fileId .= packTLString($this->fileReference); + $fileId .= Tools::packTLString($this->fileReference); } if ($this->url !== null) { - $fileId .= packTLString($this->url); - $fileId .= packLong($this->accessHash); - return base64urlEncode(rleEncode($fileId)); + $fileId .= Tools::packTLString($this->url); + $fileId .= Tools::packLong($this->accessHash); + return Tools::base64urlEncode(Tools::rleEncode($fileId)); } \assert($this->id !== null); - $fileId .= packLong($this->id); - $fileId .= packLong($this->accessHash); + $fileId .= Tools::packLong($this->id); + $fileId .= Tools::packLong($this->accessHash); if ($this->photoSizeSource !== null) { $photoSize = $this->photoSizeSource; @@ -281,11 +281,11 @@ final class FileId if ($this->volumeId === null) { $writeExtra = true; $fileId .= \pack('V', PhotoSizeSourceType::LEGACY->value); - $fileId .= packLong($photoSize->secret); + $fileId .= Tools::packLong($photoSize->secret); } else { $fileId .= \pack('V', PhotoSizeSourceType::FULL_LEGACY->value); - $fileId .= packLong($this->volumeId); - $fileId .= packLong($photoSize->secret); + $fileId .= Tools::packLong($this->volumeId); + $fileId .= Tools::packLong($photoSize->secret); $fileId .= \pack('l', $this->localId); } break; @@ -307,22 +307,22 @@ final class FileId : PhotoSizeSourceType::DIALOGPHOTO_BIG->value ) ); - $fileId .= packLong($photoSize->dialogId); - $fileId .= packLong($photoSize->dialogAccessHash); + $fileId .= Tools::packLong($photoSize->dialogId); + $fileId .= Tools::packLong($photoSize->dialogAccessHash); break; case $photoSize instanceof PhotoSizeSourceStickersetThumbnail: $writeExtra = $this->volumeId !== null; - $fileId .= packLong($photoSize->stickerSetId); - $fileId .= packLong($photoSize->stickerSetAccessHash); + $fileId .= Tools::packLong($photoSize->stickerSetId); + $fileId .= Tools::packLong($photoSize->stickerSetAccessHash); break; case $photoSize instanceof PhotoSizeSourceStickersetThumbnailVersion: - $fileId .= packLong($photoSize->stickerSetId); - $fileId .= packLong($photoSize->stickerSetAccessHash); + $fileId .= Tools::packLong($photoSize->stickerSetId); + $fileId .= Tools::packLong($photoSize->stickerSetAccessHash); $fileId .= \pack('l', $photoSize->stickerSetVersion); break; } if ($writeExtra && $this->volumeId !== null && $this->localId !== null) { - $fileId .= packLong($this->volumeId); + $fileId .= Tools::packLong($this->volumeId); $fileId .= \pack('l', $this->localId); } } @@ -332,7 +332,7 @@ final class FileId } $fileId .= \chr($this->version); - return base64urlEncode(rleEncode($fileId)); + return Tools::base64urlEncode(Tools::rleEncode($fileId)); } /** diff --git a/src/Tools.php b/src/Tools.php new file mode 100644 index 0000000..fdc555e --- /dev/null +++ b/src/Tools.php @@ -0,0 +1,205 @@ + 0) { + $new .= $null.\chr($count); + $count = 0; + } + $new .= $cur; + } + } + if ($count > 0) { + $new .= $null.\chr($count); + } + + return $new; + } + + /** + * Positive modulo + * Works just like the % (modulus) operator, only returns always a postive number. + * + * @param int $a A + * @param int $b B + * + * @internal + * + * @return int Modulo + */ + public static function posmod(int $a, int $b): int + { + $resto = $a % $b; + + return $resto < 0 ? $resto + \abs($b) : $resto; + } + + /** + * Read TL string. + * + * @param resource $stream Byte stream + * + * @internal + * + */ + public static function readTLString(mixed $stream): string + { + $l = \ord(\stream_get_contents($stream, 1)); + if ($l > 254) { + throw new \InvalidArgumentException("Length too big!"); + } + if ($l === 254) { + /** @var int */ + $long_len = \unpack('V', \stream_get_contents($stream, 3).\chr(0))[1]; + $x = \stream_get_contents($stream, $long_len); + $resto = self::posmod(-$long_len, 4); + if ($resto > 0) { + \fseek($stream, $resto, SEEK_CUR); + } + } else { + $x = $l ? \stream_get_contents($stream, $l) : ''; + $resto = self::posmod(-($l + 1), 4); + if ($resto > 0) { + \fseek($stream, $resto, SEEK_CUR); + } + } + \assert($x !== false); + return $x; + } + + /** + * Pack TL string. + * + * @param string $string String + * + * @internal + */ + public static function packTLString(string $string): string + { + $l = \strlen($string); + $concat = ''; + if ($l <= 253) { + $concat .= \chr($l); + $concat .= $string; + $concat .= \pack('@'.self::posmod(-$l - 1, 4)); + } else { + $concat .= \chr(254); + $concat .= \substr(\pack('V', $l), 0, 3); + $concat .= $string; + $concat .= \pack('@'.self::posmod(-$l, 4)); + } + return $concat; + } + +} diff --git a/src/UniqueFileId.php b/src/UniqueFileId.php index 086071c..f85ddb9 100644 --- a/src/UniqueFileId.php +++ b/src/UniqueFileId.php @@ -93,26 +93,26 @@ final class UniqueFileId { $fileId = \pack('V', $this->type->value); if ($this->url !== null) { - $fileId .= packTLString($this->url); + $fileId .= Tools::packTLString($this->url); } elseif ($this->type === UniqueFileIdType::PHOTO) { if ($this->volumeId !== null) { - $fileId .= packLong($this->volumeId); + $fileId .= Tools::packLong($this->volumeId); $fileId .= \pack('l', $this->localId); } elseif ($this->stickerSetId !== null) { \assert($this->subType !== null); $fileId .= \chr($this->subType); - $fileId .= packLong($this->stickerSetId); + $fileId .= Tools::packLong($this->stickerSetId); $fileId .= \pack('l', $this->stickerSetVersion); } else { \assert($this->subType !== null && $this->id !== null); - $fileId .= packLong($this->id); + $fileId .= Tools::packLong($this->id); $fileId .= \chr($this->subType); } } elseif ($this->id !== null) { - $fileId .= packLong($this->id); + $fileId .= Tools::packLong($this->id); } - return base64urlEncode(rleEncode($fileId)); + return Tools::base64urlEncode(Tools::rleEncode($fileId)); } /** @@ -124,7 +124,7 @@ final class UniqueFileId public static function fromUniqueBotAPI(string $fileId): self { $orig = $fileId; - $fileId = rleDecode(base64urlDecode($fileId)); + $fileId = Tools::rleDecode(Tools::base64urlDecode($fileId)); /** @var int */ $typeId = \unpack('V', $fileId)[1]; @@ -144,26 +144,26 @@ final class UniqueFileId \fwrite($res, $fileId); \fseek($res, 0); $fileId = $res; - $url = readTLString($fileId); + $url = Tools::readTLString($fileId); $l = \fstat($fileId)['size'] - \ftell($fileId); \trigger_error("Unique file ID $orig has $l bytes of leftover data"); } elseif (\strlen($fileId) === 12) { // Legacy photos - $volume_id = unpackLong(\substr($fileId, 0, 8)); - $local_id = unpackInt(\substr($fileId, 8)); + $volume_id = Tools::unpackLong(\substr($fileId, 0, 8)); + $local_id = Tools::unpackInt(\substr($fileId, 8)); } elseif (\strlen($fileId) === 9) { // Dialog photos/thumbnails - $id = unpackLong($fileId); + $id = Tools::unpackLong($fileId); $subType = \ord($fileId[8]); } elseif (\strlen($fileId) === 13) { // Stickerset ID/version $subType = \ord($fileId[0]); - $sticker_set_id = unpackLong(\substr($fileId, 1, 8)); - $sticker_set_version = unpackInt(\substr($fileId, 9)); + $sticker_set_id = Tools::unpackLong(\substr($fileId, 1, 8)); + $sticker_set_version = Tools::unpackInt(\substr($fileId, 9)); } elseif (\strlen($fileId) === 8) { // Any other document - $id = unpackLong($fileId); + $id = Tools::unpackLong($fileId); } else { $l = \strlen($fileId); \trigger_error("Unique file ID $orig has $l bytes of leftover data"); diff --git a/src/functions.php b/src/functions.php deleted file mode 100644 index 6faecf8..0000000 --- a/src/functions.php +++ /dev/null @@ -1,203 +0,0 @@ - 0) { - $new .= $null.\chr($count); - $count = 0; - } - $new .= $cur; - } - } - if ($count > 0) { - $new .= $null.\chr($count); - } - - return $new; -} - -/** - * Positive modulo - * Works just like the % (modulus) operator, only returns always a postive number. - * - * @param int $a A - * @param int $b B - * - * @internal - * - * @return int Modulo - */ -function posmod(int $a, int $b): int -{ - $resto = $a % $b; - - return $resto < 0 ? $resto + \abs($b) : $resto; -} - -/** - * Read TL string. - * - * @param resource $stream Byte stream - * - * @internal - * - */ -function readTLString(mixed $stream): string -{ - $l = \ord(\stream_get_contents($stream, 1)); - if ($l > 254) { - throw new \InvalidArgumentException("Length too big!"); - } - if ($l === 254) { - /** @var int */ - $long_len = \unpack('V', \stream_get_contents($stream, 3).\chr(0))[1]; - $x = \stream_get_contents($stream, $long_len); - $resto = posmod(-$long_len, 4); - if ($resto > 0) { - \fseek($stream, $resto, SEEK_CUR); - } - } else { - $x = $l ? \stream_get_contents($stream, $l) : ''; - $resto = posmod(-($l + 1), 4); - if ($resto > 0) { - \fseek($stream, $resto, SEEK_CUR); - } - } - \assert($x !== false); - return $x; -} - -/** - * Pack TL string. - * - * @param string $string String - * - * @internal - */ -function packTLString(string $string): string -{ - $l = \strlen($string); - $concat = ''; - if ($l <= 253) { - $concat .= \chr($l); - $concat .= $string; - $concat .= \pack('@'.posmod(-$l - 1, 4)); - } else { - $concat .= \chr(254); - $concat .= \substr(\pack('V', $l), 0, 3); - $concat .= $string; - $concat .= \pack('@'.posmod(-$l, 4)); - } - return $concat; -}