LCOV - code coverage report
Current view: top level - src/core - tl_skip.c (source / functions) Coverage Total Hit
Test: coverage-functional.info Lines: 67.0 % 2411 1615
Test Date: 2026-05-06 13:17:08 Functions: 80.9 % 94 76

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-3.0-or-later */
       2              : /* Copyright 2026 Peter Csaszar */
       3              : 
       4              : /**
       5              :  * @file tl_skip.c
       6              :  * @brief TL object skippers for vector iteration.
       7              :  */
       8              : 
       9              : #include "tl_skip.h"
      10              : #include "tl_registry.h"
      11              : #include "logger.h"
      12              : 
      13              : #include <stdlib.h>
      14              : #include <string.h>
      15              : 
      16              : /* ---- Layer-specific CRC constants (intentionally local to tl_skip.c) ----
      17              :  * Not exported via tl_registry.h: some names (e.g. CRC_inputUser) have
      18              :  * different values in domain/ files that target a different API layer.  */
      19              : 
      20              : #define CRC_peerNotifySettings          0xa83b0426u  /* layer ≤ 175 */
      21              : #define CRC_peerNotifySettings2         0x99622c0cu  /* layer 176+ */
      22              : #define CRC_notificationSoundDefault    0x97e8bebeu
      23              : #define CRC_notificationSoundNone       0x6f0c34dfu
      24              : #define CRC_notificationSoundLocal      0x830b9ae4u
      25              : #define CRC_notificationSoundRingtone   0xff6c8049u
      26              : #define CRC_draftMessage                0x3fccf7efu
      27              : #define CRC_draftMessageEmpty           0x1b0c841au
      28              : 
      29              : /* MessageEntity variants (layer 170+). */
      30              : #define CRC_messageEntityUnknown        0xbb92ba95u
      31              : #define CRC_messageEntityMention        0xfa04579du
      32              : #define CRC_messageEntityHashtag        0x6f635b0du
      33              : #define CRC_messageEntityBotCommand     0x6cef8ac7u
      34              : #define CRC_messageEntityUrl            0x6ed02538u
      35              : #define CRC_messageEntityEmail          0x64e475c2u
      36              : #define CRC_messageEntityBold           0xbd610bc9u
      37              : #define CRC_messageEntityItalic         0x826f8b60u
      38              : #define CRC_messageEntityCode           0x28a20571u
      39              : #define CRC_messageEntityPre            0x73924be0u
      40              : #define CRC_messageEntityTextUrl        0x76a6d327u
      41              : #define CRC_messageEntityMentionName    0xdc7b1140u
      42              : #define CRC_messageEntityPhone          0x9b69e34bu
      43              : #define CRC_messageEntityCashtag        0x4c4e743fu
      44              : #define CRC_messageEntityUnderline      0x9c4e7e8bu
      45              : #define CRC_messageEntityStrike         0xbf0693d4u
      46              : #define CRC_messageEntityBlockquote     0xf1ccaaacu
      47              : #define CRC_messageEntityBankCard       0x761e6af4u
      48              : #define CRC_messageEntitySpoiler        0x32ca960fu
      49              : #define CRC_messageEntityCustomEmoji    0xc8cf05f8u
      50              : 
      51              : /* MessageFwdHeader (layer 170+). */
      52              : #define CRC_messageFwdHeader            0x4e4df4bbu
      53              : 
      54              : /* MessageReplyHeader variants. */
      55              : #define CRC_messageReplyHeader          0xafbc09dbu
      56              : #define CRC_messageReplyStoryHeader     0xe5af939u
      57              : 
      58              : /* PhotoSize variants (layer 170+). */
      59              : #define CRC_photoSizeEmpty              0x0e17e23cu
      60              : #define CRC_photoSize                   0x75c78e60u
      61              : #define CRC_photoCachedSize             0x021e1ad6u
      62              : #define CRC_photoStrippedSize           0xe0b0bc2eu
      63              : #define CRC_photoSizeProgressive        0xfa3efb95u
      64              : #define CRC_photoPathSize               0xd8214d41u
      65              : 
      66              : /* Photo variants. */
      67              : #define CRC_photo                       0xfb197a65u
      68              : #define CRC_photoEmpty                  0x2331b22du
      69              : 
      70              : /* Document variants. */
      71              : #define CRC_document                    0x8fd4c4d8u
      72              : #define CRC_documentEmpty               0x36f8c871u
      73              : 
      74              : /* DocumentAttribute — present inside document.attributes. We don't walk
      75              :  * them; we just skip via Vector count by bailing when document is set. */
      76              : 
      77              : /* GeoPoint. */
      78              : #define CRC_geoPointEmpty               0x1117dd5fu
      79              : #define CRC_geoPoint                    0xb2a2f663u
      80              : 
      81              : /* MessageMedia variants. */
      82              : #define CRC_messageMediaEmpty           0x3ded6320u
      83              : #define CRC_messageMediaPhoto           0x695150d7u
      84              : #define CRC_messageMediaDocument        0x4cf4d72du
      85              : #define CRC_messageMediaGeo             0x56e0d474u
      86              : #define CRC_messageMediaContact         0x70322949u
      87              : #define CRC_messageMediaUnsupported     0x9f84f49eu
      88              : #define CRC_messageMediaVenue           0x2ec0533fu
      89              : #define CRC_messageMediaGeoLive         0xb940c666u
      90              : #define CRC_messageMediaDice            0x3f7ee58bu
      91              : #define CRC_messageMediaWebPage         0xddf8c26eu
      92              : #define CRC_messageMediaPoll            0x4bd6e798u
      93              : #define CRC_messageMediaInvoice         0xf6a548d3u
      94              : #define CRC_messageMediaStory           0x68cb6283u
      95              : #define CRC_messageMediaGiveaway        0xaa073beeu
      96              : #define CRC_messageMediaGame            0xfdb19008u
      97              : #define CRC_messageMediaPaidMedia       0xa8852491u
      98              : 
      99              : /* Game (inside messageMediaGame). */
     100              : #define CRC_game                        0xbdf9653bu
     101              : 
     102              : /* MessageExtendedMedia (inside messageMediaPaidMedia). */
     103              : #define CRC_messageExtendedMediaPreview 0xad628cc8u
     104              : #define CRC_messageExtendedMedia        0xee479c64u
     105              : 
     106              : /* WebDocument (inside messageMediaInvoice.photo). */
     107              : #define CRC_webDocument                 0x1c570ed1u
     108              : #define CRC_webDocumentNoProxy          0xf9c8bcc6u
     109              : 
     110              : /* StoryItem variants (inside messageMediaStory when flags.0). */
     111              : #define CRC_storyItemDeleted            0x51e6ee4fu
     112              : #define CRC_storyItemSkipped            0xffadc913u
     113              : #define CRC_storyItem                   0x79b26a24u
     114              : 
     115              : /* StoryFwdHeader (inside full storyItem.fwd_from). */
     116              : #define CRC_storyFwdHeader              0xb826e150u
     117              : 
     118              : /* MediaAreaCoordinates (inner of every MediaArea). */
     119              : #define CRC_mediaAreaCoordinates        0x03d1ea4eu
     120              : 
     121              : /* GeoPointAddress (inside mediaAreaGeoPoint when flags.0). */
     122              : #define CRC_geoPointAddress             0xde4c5d93u
     123              : 
     124              : /* MediaArea variants. */
     125              : #define CRC_mediaAreaVenue              0xbe82db9cu
     126              : #define CRC_mediaAreaGeoPoint           0xdf8b3b22u
     127              : #define CRC_mediaAreaSuggestedReaction  0x14455871u
     128              : #define CRC_mediaAreaChannelPost        0x770416afu
     129              : #define CRC_mediaAreaUrl                0x37381085u
     130              : #define CRC_mediaAreaWeather            0x49a6549cu
     131              : #define CRC_mediaAreaStarGift           0x5787686du
     132              : 
     133              : /* PrivacyRule variants. */
     134              : #define CRC_privacyValueAllowContacts          0xfffe1bacu
     135              : #define CRC_privacyValueAllowAll               0x65427b82u
     136              : #define CRC_privacyValueAllowUsers             0xb8905fb2u
     137              : #define CRC_privacyValueDisallowContacts       0xf888fa1au
     138              : #define CRC_privacyValueDisallowAll            0x8b73e763u
     139              : #define CRC_privacyValueDisallowUsers          0xe4621141u
     140              : #define CRC_privacyValueAllowChatParticipants  0x6b134e8eu
     141              : #define CRC_privacyValueDisallowChatParticipants 0x41c87565u
     142              : #define CRC_privacyValueAllowCloseFriends      0xf7e8d89bu
     143              : #define CRC_privacyValueAllowPremium           0xece9814bu
     144              : #define CRC_privacyValueAllowBots              0x21461b5du
     145              : #define CRC_privacyValueDisallowBots           0xf6a5f82fu
     146              : 
     147              : /* StoryViews. */
     148              : #define CRC_storyViews                  0x8d595cd6u
     149              : 
     150              : /* WebPage variants (inside messageMediaWebPage). */
     151              : #define CRC_webPage                     0xe89c45b2u
     152              : #define CRC_webPageEmpty                0xeb1477e8u
     153              : #define CRC_webPagePending              0xb0d13e47u
     154              : #define CRC_webPageNotModified          0x7311ca11u
     155              : 
     156              : /* WebPageAttribute variants (webPage.attributes, flags.12). */
     157              : #define CRC_webPageAttributeTheme       0x54b56617u
     158              : #define CRC_webPageAttributeStory       0x2e94c3e7u
     159              : #define CRC_webPageAttributeStickerSet  0x50cc03d3u
     160              : 
     161              : /* Page (webPage.cached_page, flags.10). */
     162              : #define CRC_page                        0x98657f0du
     163              : 
     164              : /* PageBlock variants — full set for cached_page iteration. */
     165              : #define CRC_pageBlockUnsupported        0x13567e8au
     166              : #define CRC_pageBlockTitle              0x70abc3fdu
     167              : #define CRC_pageBlockSubtitle           0x8ffa9a1fu
     168              : #define CRC_pageBlockHeader             0xbfd064ecu
     169              : #define CRC_pageBlockSubheader          0xf12bb6e1u
     170              : #define CRC_pageBlockKicker             0x1e148390u
     171              : #define CRC_pageBlockParagraph          0x467a0766u
     172              : #define CRC_pageBlockPreformatted       0xc070d93eu
     173              : #define CRC_pageBlockFooter             0x48870999u
     174              : #define CRC_pageBlockDivider            0xdb20b188u
     175              : #define CRC_pageBlockAnchor             0xce0d37b0u
     176              : #define CRC_pageBlockAuthorDate         0xbaafe5e0u
     177              : #define CRC_pageBlockBlockquote         0x263d7c26u
     178              : #define CRC_pageBlockPullquote          0x4f4456d5u
     179              : #define CRC_pageBlockPhoto              0x1759c560u
     180              : #define CRC_pageBlockVideo              0x7c8fe7b6u
     181              : #define CRC_pageBlockAudio              0x804361eau
     182              : #define CRC_pageBlockCover              0x39f23300u
     183              : #define CRC_pageBlockChannel            0xef1751b5u
     184              : #define CRC_pageBlockMap                0xa44f3ef6u
     185              : #define CRC_pageBlockList               0xe4e88011u
     186              : #define CRC_pageBlockOrderedList        0x9a8ae1e1u
     187              : #define CRC_pageBlockCollage            0x65a0fa4du
     188              : #define CRC_pageBlockSlideshow          0x031f9590u
     189              : #define CRC_pageBlockDetails            0x76768bedu
     190              : #define CRC_pageBlockRelatedArticles    0x16115a96u
     191              : #define CRC_pageBlockTable              0xbf4dea82u
     192              : #define CRC_pageBlockEmbed              0xa8718dc5u
     193              : #define CRC_pageBlockEmbedPost          0xf259a80bu
     194              : 
     195              : /* PageCaption, PageListItem, PageListOrderedItem, PageTableRow/Cell,
     196              :  * PageRelatedArticle — all reached from PageBlock variants. */
     197              : #define CRC_pageCaption                 0x6f747657u
     198              : #define CRC_pageListItemText            0xb92fb6cdu
     199              : #define CRC_pageListItemBlocks          0x25e073fcu
     200              : #define CRC_pageListOrderedItemText     0x5e068047u
     201              : #define CRC_pageListOrderedItemBlocks   0x98dd8936u
     202              : #define CRC_pageTableRow                0xe0c0c5e5u
     203              : #define CRC_pageTableCell               0x34566b6au
     204              : #define CRC_pageRelatedArticle          0xb390dc08u
     205              : 
     206              : /* RichText variants — full tree so every supported PageBlock can walk
     207              :  * its RichText payload. */
     208              : #define CRC_textEmpty                   0xdc3d824fu
     209              : #define CRC_textPlain                   0x744694e0u
     210              : #define CRC_textBold                    0x6724abc4u
     211              : #define CRC_textItalic                  0xd912a59cu
     212              : #define CRC_textUnderline               0xc12622c4u
     213              : #define CRC_textStrike                  0x9bf8bb95u
     214              : #define CRC_textFixed                   0x6c3f19b9u
     215              : #define CRC_textUrl                     0x3c2884c1u
     216              : #define CRC_textEmail                   0xde5a0dd6u
     217              : #define CRC_textConcat                  0x7e6260d7u
     218              : #define CRC_textSubscript               0xed6a8504u
     219              : #define CRC_textSuperscript             0xc7fb5e01u
     220              : #define CRC_textMarked                  0x034b27f6u
     221              : #define CRC_textPhone                   0x1ccb966au
     222              : #define CRC_textImage                   0x081ccf4fu
     223              : #define CRC_textAnchor                  0x35553762u
     224              : 
     225              : /* Poll + PollAnswer + PollResults + PollAnswerVoters. */
     226              : #define CRC_poll                        0x58747131u
     227              : #define CRC_pollAnswer                  0x6ca9c2e9u
     228              : #define CRC_pollResults                 0x7adc669du
     229              : #define CRC_pollAnswerVoters            0x3b6ddad2u
     230              : #define CRC_textWithEntities_poll       0x751f3146u
     231              : 
     232              : /* ChatPhoto. */
     233              : #define CRC_chatPhotoEmpty              0x37c1011cu
     234              : #define CRC_chatPhoto                   0x1c6e1c11u
     235              : 
     236              : /* UserProfilePhoto. */
     237              : #define CRC_userProfilePhotoEmpty       0x4f11bae1u
     238              : #define CRC_userProfilePhoto            0x82d1f706u
     239              : 
     240              : /* UserStatus. */
     241              : #define CRC_userStatusEmpty             0x09d05049u
     242              : #define CRC_userStatusOnline            0xedb93949u
     243              : #define CRC_userStatusOffline           0x008c703fu
     244              : #define CRC_userStatusRecently          0x7b197dc8u
     245              : #define CRC_userStatusLastWeek          0x541a1d1au
     246              : #define CRC_userStatusLastMonth         0x65899e67u
     247              : 
     248              : /* RestrictionReason. */
     249              : #define CRC_restrictionReason           0xd072acb4u
     250              : 
     251              : /* Username. */
     252              : #define CRC_username                    0xb4073647u
     253              : 
     254              : /* PeerColor. */
     255              : #define CRC_peerColor                   0xb54b5acfu
     256              : 
     257              : /* EmojiStatus. */
     258              : #define CRC_emojiStatusEmpty            0x2de11aaeu
     259              : #define CRC_emojiStatus                 0x929b619du
     260              : #define CRC_emojiStatusUntil            0xfa30a8c7u
     261              : 
     262              : /* ChatAdminRights / ChatBannedRights. */
     263              : #define CRC_chatAdminRights             0x5fb224d5u
     264              : #define CRC_chatBannedRights            0x9f120418u
     265              : 
     266              : /* ReplyMarkup variants. */
     267              : #define CRC_replyKeyboardHide           0xa03e5b85u
     268              : #define CRC_replyKeyboardForceReply     0x86b40b08u
     269              : #define CRC_replyKeyboardMarkup         0x85dd99d1u
     270              : #define CRC_replyInlineMarkup           0x48a30254u
     271              : 
     272              : /* KeyboardButtonRow. */
     273              : #define CRC_keyboardButtonRow           0x77608b83u
     274              : 
     275              : /* KeyboardButton variants we handle inline. */
     276              : #define CRC_keyboardButton              0xa2fa4880u
     277              : #define CRC_keyboardButtonUrl           0x258aff05u
     278              : #define CRC_keyboardButtonCallback      0x35bbdb6bu
     279              : #define CRC_keyboardButtonRequestPhone  0xb16a6c29u
     280              : #define CRC_keyboardButtonRequestGeoLoc 0xfc796b3fu
     281              : #define CRC_keyboardButtonSwitchInline  0x93b9fbb5u
     282              : #define CRC_keyboardButtonGame          0x50f41ccfu
     283              : #define CRC_keyboardButtonBuy           0xafd93fbbu
     284              : #define CRC_keyboardButtonUrlAuth       0x10b78d29u
     285              : #define CRC_keyboardButtonRequestPoll   0xbbc7515du
     286              : #define CRC_keyboardButtonUserProfile   0x308660c1u
     287              : #define CRC_keyboardButtonWebView       0x13767230u
     288              : #define CRC_keyboardButtonSimpleWebView 0xa0c0505cu
     289              : 
     290              : /* MessageReplies. */
     291              : #define CRC_messageReplies              0x83d60fc2u
     292              : 
     293              : /* FactCheck + TextWithEntities. */
     294              : #define CRC_factCheck                   0xb89bfccfu
     295              : #define CRC_textWithEntities            0x751f3146u
     296              : 
     297              : /* MessageReactions + Reaction variants. */
     298              : #define CRC_messageReactions            0x4f2b9479u
     299              : #define CRC_reactionCount               0xa3d1cb80u
     300              : #define CRC_reactionEmpty               0x79f5d419u
     301              : #define CRC_reactionEmoji               0x1b2286b8u
     302              : #define CRC_reactionCustomEmoji         0x8935fc73u
     303              : #define CRC_reactionPaid                0x523da4ebu
     304              : 
     305              : /* MessagePeerReaction + MessageReactor (recent/top reactors). */
     306              : #define CRC_messagePeerReaction         0xb156fe9du
     307              : #define CRC_messageReactor              0xedd1b4adu
     308              : 
     309              : /* InputChannel variants (extract_chat migrated_to). */
     310              : #define CRC_inputChannelEmpty           0xee8c1e86u
     311              : #define CRC_inputChannel                0xf35aec28u
     312              : #define CRC_inputChannelFromMessage     0x5b934f9du
     313              : 
     314              : /* InputPeer variants (draft reply_to, inputMediaStory). */
     315              : #define CRC_inputPeerEmpty              0x7f3b18eau
     316              : #define CRC_inputPeerSelf               0x7da07ec9u
     317              : #define CRC_inputPeerUser               0x7b8e7de6u
     318              : #define CRC_inputPeerChat               0x35a95cb9u
     319              : #define CRC_inputPeerChannel            0x27bcbbfcu
     320              : #define CRC_inputPeerUserFromMessage    0xa87b0a1cu
     321              : #define CRC_inputPeerChannelFromMessage 0xbd2a0840u
     322              : 
     323              : /* InputReplyTo variants (draftMessage.reply_to). */
     324              : #define CRC_inputReplyToMessage         0x3faad5f0u
     325              : #define CRC_inputReplyToStory           0x7e2a9a7bu
     326              : 
     327              : /* InputPhoto / InputDocument / InputGeoPoint (draftMessage.media). */
     328              : #define CRC_inputPhotoEmpty             0x1cd7bf0du
     329              : #define CRC_inputPhoto                  0x3bb3b94au
     330              : #define CRC_inputDocumentEmpty          0x72f0eafau
     331              : #define CRC_inputDocument               0x1abfb575u
     332              : #define CRC_inputGeoPointEmpty          0xe4c123d6u
     333              : #define CRC_inputGeoPoint               0xf3b7acc9u
     334              : 
     335              : /* InputMedia variants (draftMessage.media). */
     336              : #define CRC_inputMediaEmpty             0x9664f57fu
     337              : #define CRC_inputMediaUploadedPhoto     0x1e287d04u
     338              : #define CRC_inputMediaPhoto             0xb3ba0635u
     339              : #define CRC_inputMediaGeoPoint          0xf9c44144u
     340              : #define CRC_inputMediaContact           0xf8ab7dfbu
     341              : #define CRC_inputMediaUploadedDocument  0x5b38c6c1u
     342              : #define CRC_inputMediaDocument          0x33473058u
     343              : #define CRC_inputMediaVenue             0xc13d1c11u
     344              : #define CRC_inputMediaGeoLive           0x971fa843u
     345              : #define CRC_inputMediaGame              0xd33f43f3u
     346              : #define CRC_inputMediaInvoice           0x8eb5a6d5u
     347              : #define CRC_inputMediaDice              0xe66fbf7bu
     348              : #define CRC_inputMediaStory             0x89fdd778u
     349              : #define CRC_inputMediaWebPage           0xc21b8849u
     350              : #define CRC_inputMediaPoll              0x261e868fu
     351              : #define CRC_inputMediaPaidMedia         0x6519cb78u
     352              : #define CRC_inputMediaPhotoExternal     0xe5bbfe1au
     353              : #define CRC_inputMediaDocumentExternal  0xfb52dc99u
     354              : 
     355              : /* InputFile variants (inputMediaUploaded*). */
     356              : #define CRC_inputFile                   0xf52ff27fu
     357              : #define CRC_inputFileBig                0xfa4f0bb5u
     358              : 
     359              : /* InputGame variants (inputMediaGame). */
     360              : #define CRC_inputGameID                 0x032c3e77u
     361              : #define CRC_inputGameShortName          0xc331e80au
     362              : 
     363              : /* InputUser variants (inputGameShortName.bot_id). */
     364              : #define CRC_inputUserEmpty              0xb98886cfu
     365              : #define CRC_inputUserSelf               0xf7c1b13fu
     366              : #define CRC_inputUser                   0xf21158c6u
     367              : #define CRC_inputUserFromMessage        0x1da448e2u
     368              : 
     369              : /* ThemeSettings (webPageAttributeTheme.settings). */
     370              : #define CRC_themeSettings               0xfa58b6d4u
     371              : #define CRC_baseThemeClassic            0xc3a12462u
     372              : #define CRC_baseThemeDay                0xfbd81688u
     373              : #define CRC_baseThemeDark               0xb7b31ea8u
     374              : #define CRC_baseThemeTinted             0x6d5f77eeu
     375              : #define CRC_baseThemeArctic             0x5b11125au
     376              : 
     377              : /* WallPaper + WallPaperSettings (themeSettings.wallpaper). */
     378              : #define CRC_wallPaper                   0xa437c3edu
     379              : #define CRC_wallPaperNoFile             0xe0804116u
     380              : #define CRC_wallPaperSettings           0x372efcd0u
     381              : 
     382            6 : int tl_skip_bool(TlReader *r) {
     383            6 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     384            5 :     tl_read_uint32(r);
     385            5 :     return 0;
     386              : }
     387              : 
     388         2002 : int tl_skip_string(TlReader *r) {
     389              :     /* tl_read_string already advances the cursor and handles padding. */
     390         2002 :     if (!tl_reader_ok(r)) return -1;
     391         2000 :     char *s = tl_read_string(r);
     392         2000 :     if (!s) return -1;
     393         2000 :     free(s);
     394         2000 :     return 0;
     395              : }
     396              : 
     397         1143 : int tl_skip_peer(TlReader *r) {
     398         1143 :     if (!tl_reader_ok(r) || r->len - r->pos < 12) return -1;
     399         1142 :     uint32_t crc = tl_read_uint32(r);
     400         1142 :     (void)tl_read_int64(r);
     401         1142 :     switch (crc) {
     402         1139 :     case TL_peerUser:
     403              :     case TL_peerChat:
     404              :     case TL_peerChannel:
     405         1139 :         return 0;
     406            3 :     default:
     407            3 :         logger_log(LOG_WARN, "tl_skip_peer: unknown Peer 0x%08x", crc);
     408            3 :         return -1;
     409              :     }
     410              : }
     411              : 
     412            8 : int tl_skip_notification_sound(TlReader *r) {
     413            8 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     414            8 :     uint32_t crc = tl_read_uint32(r);
     415            8 :     switch (crc) {
     416            5 :     case CRC_notificationSoundDefault:
     417              :     case CRC_notificationSoundNone:
     418            5 :         return 0;                              /* no payload */
     419            1 :     case CRC_notificationSoundRingtone:
     420            1 :         if (r->len - r->pos < 8) return -1;
     421            1 :         tl_read_int64(r);                      /* id */
     422            1 :         return 0;
     423            1 :     case CRC_notificationSoundLocal:
     424            1 :         if (tl_skip_string(r) != 0) return -1; /* title */
     425            1 :         if (tl_skip_string(r) != 0) return -1; /* data */
     426            1 :         return 0;
     427            1 :     default:
     428            1 :         logger_log(LOG_WARN, "tl_skip_notification_sound: unknown 0x%08x", crc);
     429            1 :         return -1;
     430              :     }
     431              : }
     432              : 
     433              : /* peerNotifySettings#a83b0426 flags:#
     434              :  *   show_previews:flags.0?Bool  silent:flags.1?Bool  mute_until:flags.2?int
     435              :  *   ios_sound:flags.3?NotificationSound
     436              :  *   android_sound:flags.4?NotificationSound
     437              :  *   other_sound:flags.5?NotificationSound
     438              :  *   stories_muted:flags.6?Bool  stories_hide_sender:flags.7?Bool
     439              :  *   stories_ios_sound:flags.8?NotificationSound
     440              :  *   stories_android_sound:flags.9?NotificationSound
     441              :  *   stories_other_sound:flags.10?NotificationSound
     442              :  */
     443          420 : int tl_skip_peer_notify_settings(TlReader *r) {
     444          420 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
     445          420 :     uint32_t crc = tl_read_uint32(r);
     446          420 :     if (crc != CRC_peerNotifySettings && crc != CRC_peerNotifySettings2) {
     447            1 :         logger_log(LOG_WARN,
     448              :                    "tl_skip_peer_notify_settings: unexpected 0x%08x", crc);
     449            1 :         return -1;
     450              :     }
     451          419 :     uint32_t flags = tl_read_uint32(r);
     452              : 
     453          419 :     if (flags & (1u << 0)) if (tl_skip_bool(r) != 0) return -1;
     454          419 :     if (flags & (1u << 1)) if (tl_skip_bool(r) != 0) return -1;
     455          419 :     if (flags & (1u << 2)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
     456          419 :     if (flags & (1u << 3)) if (tl_skip_notification_sound(r) != 0) return -1;
     457          419 :     if (flags & (1u << 4)) if (tl_skip_notification_sound(r) != 0) return -1;
     458          419 :     if (flags & (1u << 5)) if (tl_skip_notification_sound(r) != 0) return -1;
     459          419 :     if (flags & (1u << 6)) if (tl_skip_bool(r) != 0) return -1;
     460          419 :     if (flags & (1u << 7)) if (tl_skip_bool(r) != 0) return -1;
     461          419 :     if (flags & (1u << 8)) if (tl_skip_notification_sound(r) != 0) return -1;
     462          419 :     if (flags & (1u << 9)) if (tl_skip_notification_sound(r) != 0) return -1;
     463          419 :     if (flags & (1u << 10)) if (tl_skip_notification_sound(r) != 0) return -1;
     464          419 :     return 0;
     465              : }
     466              : 
     467              : /* ---- InputPeer / InputMedia / InputReplyTo helpers (draft parsing) ---- */
     468              : 
     469              : static int skip_input_peer(TlReader *r);  /* forward decl for self-recursion */
     470              : static int skip_input_media(TlReader *r); /* forward decl for inputMediaPaidMedia */
     471              : 
     472            0 : static int skip_input_peer(TlReader *r) {
     473            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     474            0 :     uint32_t crc = tl_read_uint32(r);
     475            0 :     switch (crc) {
     476            0 :     case CRC_inputPeerEmpty:
     477              :     case CRC_inputPeerSelf:
     478            0 :         return 0;
     479            0 :     case CRC_inputPeerUser:
     480              :     case CRC_inputPeerChannel:
     481            0 :         if (r->len - r->pos < 16) return -1;
     482            0 :         tl_read_int64(r); tl_read_int64(r);
     483            0 :         return 0;
     484            0 :     case CRC_inputPeerChat:
     485            0 :         if (r->len - r->pos < 8) return -1;
     486            0 :         tl_read_int64(r);
     487            0 :         return 0;
     488            0 :     case CRC_inputPeerUserFromMessage:
     489              :     case CRC_inputPeerChannelFromMessage:
     490            0 :         if (skip_input_peer(r) != 0) return -1;
     491            0 :         if (r->len - r->pos < 12) return -1;
     492            0 :         tl_read_int32(r);                               /* msg_id */
     493            0 :         tl_read_int64(r);                               /* user_id / channel_id */
     494            0 :         return 0;
     495            0 :     default:
     496            0 :         logger_log(LOG_WARN, "skip_input_peer: unknown 0x%08x", crc);
     497            0 :         return -1;
     498              :     }
     499              : }
     500              : 
     501            0 : static int skip_input_geo_point(TlReader *r) {
     502            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     503            0 :     uint32_t crc = tl_read_uint32(r);
     504            0 :     if (crc == CRC_inputGeoPointEmpty) return 0;
     505            0 :     if (crc != CRC_inputGeoPoint) {
     506            0 :         logger_log(LOG_WARN, "skip_input_geo_point: unknown 0x%08x", crc);
     507            0 :         return -1;
     508              :     }
     509              :     /* flags:# lat:double long:double accuracy_radius:flags.0?int */
     510            0 :     if (r->len - r->pos < 4) return -1;
     511            0 :     uint32_t flags = tl_read_uint32(r);
     512            0 :     if (r->len - r->pos < 16) return -1;
     513            0 :     tl_read_double(r); tl_read_double(r);
     514            0 :     if (flags & 1u) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
     515            0 :     return 0;
     516              : }
     517              : 
     518            0 : static int skip_input_photo(TlReader *r) {
     519            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     520            0 :     uint32_t crc = tl_read_uint32(r);
     521            0 :     if (crc == CRC_inputPhotoEmpty) {
     522            0 :         if (r->len - r->pos < 8) return -1;
     523            0 :         tl_read_int64(r);
     524            0 :         return 0;
     525              :     }
     526            0 :     if (crc != CRC_inputPhoto) {
     527            0 :         logger_log(LOG_WARN, "skip_input_photo: unknown 0x%08x", crc);
     528            0 :         return -1;
     529              :     }
     530            0 :     if (r->len - r->pos < 16) return -1;
     531            0 :     tl_read_int64(r); tl_read_int64(r);            /* id, access_hash */
     532            0 :     return tl_skip_string(r);                       /* file_reference:bytes */
     533              : }
     534              : 
     535            0 : static int skip_input_document(TlReader *r) {
     536            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     537            0 :     uint32_t crc = tl_read_uint32(r);
     538            0 :     if (crc == CRC_inputDocumentEmpty) {
     539            0 :         if (r->len - r->pos < 8) return -1;
     540            0 :         tl_read_int64(r);
     541            0 :         return 0;
     542              :     }
     543            0 :     if (crc != CRC_inputDocument) {
     544            0 :         logger_log(LOG_WARN, "skip_input_document: unknown 0x%08x", crc);
     545            0 :         return -1;
     546              :     }
     547            0 :     if (r->len - r->pos < 16) return -1;
     548            0 :     tl_read_int64(r); tl_read_int64(r);            /* id, access_hash */
     549            0 :     return tl_skip_string(r);                       /* file_reference:bytes */
     550              : }
     551              : 
     552            0 : static int skip_input_user(TlReader *r) {
     553            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     554            0 :     uint32_t crc = tl_read_uint32(r);
     555            0 :     switch (crc) {
     556            0 :     case CRC_inputUserEmpty:
     557              :     case CRC_inputUserSelf:
     558            0 :         return 0;
     559            0 :     case CRC_inputUser:
     560            0 :         if (r->len - r->pos < 16) return -1;
     561            0 :         tl_read_int64(r); tl_read_int64(r);
     562            0 :         return 0;
     563            0 :     case CRC_inputUserFromMessage:
     564            0 :         if (skip_input_peer(r) != 0) return -1;
     565            0 :         if (r->len - r->pos < 12) return -1;
     566            0 :         tl_read_int32(r); tl_read_int64(r);        /* msg_id, user_id */
     567            0 :         return 0;
     568            0 :     default:
     569            0 :         logger_log(LOG_WARN, "skip_input_user: unknown 0x%08x", crc);
     570            0 :         return -1;
     571              :     }
     572              : }
     573              : 
     574            0 : static int skip_input_game(TlReader *r) {
     575            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     576            0 :     uint32_t crc = tl_read_uint32(r);
     577            0 :     if (crc == CRC_inputGameID) {
     578            0 :         if (r->len - r->pos < 16) return -1;
     579            0 :         tl_read_int64(r); tl_read_int64(r);
     580            0 :         return 0;
     581              :     }
     582            0 :     if (crc == CRC_inputGameShortName) {
     583            0 :         if (skip_input_user(r) != 0) return -1;
     584            0 :         return tl_skip_string(r);                   /* short_name */
     585              :     }
     586            0 :     logger_log(LOG_WARN, "skip_input_game: unknown 0x%08x", crc);
     587            0 :     return -1;
     588              : }
     589              : 
     590              : static int skip_text_with_entities(TlReader *r); /* fwd — defined in poll section */
     591              : static int skip_poll(TlReader *r);               /* fwd — defined in poll section */
     592              : 
     593            0 : static int skip_input_media(TlReader *r) {
     594            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     595            0 :     uint32_t crc = tl_read_uint32(r);
     596            0 :     switch (crc) {
     597            0 :     case CRC_inputMediaEmpty:
     598            0 :         return 0;
     599            0 :     case CRC_inputMediaPhoto: {
     600              :         /* flags:# id:InputPhoto ttl_seconds:flags.0?int */
     601            0 :         if (r->len - r->pos < 4) return -1;
     602            0 :         uint32_t flags = tl_read_uint32(r);
     603            0 :         if (skip_input_photo(r) != 0) return -1;
     604            0 :         if (flags & 1u) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
     605            0 :         return 0;
     606              :     }
     607            0 :     case CRC_inputMediaGeoPoint:
     608            0 :         return skip_input_geo_point(r);
     609            0 :     case CRC_inputMediaContact:
     610              :         /* phone_number first_name last_name vcard */
     611            0 :         for (int i = 0; i < 4; i++) { if (tl_skip_string(r) != 0) return -1; }
     612            0 :         return 0;
     613            0 :     case CRC_inputMediaDocument: {
     614              :         /* flags:# id:InputDocument ttl_seconds:flags.0?int query:flags.1?string */
     615            0 :         if (r->len - r->pos < 4) return -1;
     616            0 :         uint32_t flags = tl_read_uint32(r);
     617            0 :         if (skip_input_document(r) != 0) return -1;
     618            0 :         if (flags & 1u) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
     619            0 :         if (flags & (1u << 1)) { if (tl_skip_string(r) != 0) return -1; }
     620            0 :         return 0;
     621              :     }
     622            0 :     case CRC_inputMediaVenue:
     623              :         /* geo_point title address provider venue_id venue_type */
     624            0 :         if (skip_input_geo_point(r) != 0) return -1;
     625            0 :         for (int i = 0; i < 5; i++) { if (tl_skip_string(r) != 0) return -1; }
     626            0 :         return 0;
     627            0 :     case CRC_inputMediaGeoLive: {
     628              :         /* flags:# geo_point:InputGeoPoint heading:flags.2?int period:int
     629              :          *   proximity_notification_radius:flags.3?int */
     630            0 :         if (r->len - r->pos < 4) return -1;
     631            0 :         uint32_t flags = tl_read_uint32(r);
     632            0 :         if (skip_input_geo_point(r) != 0) return -1;
     633            0 :         if (flags & (1u << 2)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
     634            0 :         if (r->len - r->pos < 4) return -1;
     635            0 :         tl_read_int32(r);                               /* period */
     636            0 :         if (flags & (1u << 3)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
     637            0 :         return 0;
     638              :     }
     639            0 :     case CRC_inputMediaGame:
     640            0 :         return skip_input_game(r);
     641            0 :     case CRC_inputMediaDice:
     642            0 :         return tl_skip_string(r);                       /* emoticon */
     643            0 :     case CRC_inputMediaStory:
     644              :         /* peer:InputPeer id:int */
     645            0 :         if (skip_input_peer(r) != 0) return -1;
     646            0 :         if (r->len - r->pos < 4) return -1;
     647            0 :         tl_read_int32(r);
     648            0 :         return 0;
     649            0 :     case CRC_inputMediaWebPage: {
     650              :         /* flags:# url:string query:flags.4?string */
     651            0 :         if (r->len - r->pos < 4) return -1;
     652            0 :         uint32_t flags = tl_read_uint32(r);
     653            0 :         if (tl_skip_string(r) != 0) return -1;         /* url */
     654            0 :         if (flags & (1u << 4)) { if (tl_skip_string(r) != 0) return -1; }
     655            0 :         return 0;
     656              :     }
     657            0 :     case CRC_inputMediaPoll: {
     658              :         /* flags:# poll:Poll correct_answers:flags.0?Vector<bytes>
     659              :          *   solution:flags.1?string solution_entities:flags.2?Vector<MessageEntity> */
     660            0 :         if (r->len - r->pos < 4) return -1;
     661            0 :         uint32_t flags = tl_read_uint32(r);
     662            0 :         if (skip_poll(r) != 0) return -1;
     663            0 :         if (flags & 1u) {
     664            0 :             if (r->len - r->pos < 8) return -1;
     665            0 :             uint32_t bvec = tl_read_uint32(r);
     666            0 :             if (bvec != TL_vector) return -1;
     667            0 :             uint32_t bn = tl_read_uint32(r);
     668            0 :             for (uint32_t i = 0; i < bn; i++) {
     669            0 :                 if (tl_skip_string(r) != 0) return -1;
     670              :             }
     671              :         }
     672            0 :         if (flags & (1u << 1)) { if (tl_skip_string(r) != 0) return -1; }
     673            0 :         if (flags & (1u << 2)) {
     674            0 :             if (tl_skip_message_entities_vector(r) != 0) return -1;
     675              :         }
     676            0 :         return 0;
     677              :     }
     678            0 :     case CRC_inputMediaPaidMedia: {
     679              :         /* stars_amount:long extended_media:Vector<InputMedia> */
     680            0 :         if (r->len - r->pos < 8) return -1;
     681            0 :         tl_read_int64(r);
     682            0 :         if (r->len - r->pos < 8) return -1;
     683            0 :         uint32_t pvec = tl_read_uint32(r);
     684            0 :         if (pvec != TL_vector) return -1;
     685            0 :         uint32_t pn = tl_read_uint32(r);
     686            0 :         for (uint32_t i = 0; i < pn; i++) {
     687            0 :             if (skip_input_media(r) != 0) return -1;
     688              :         }
     689            0 :         return 0;
     690              :     }
     691            0 :     case CRC_inputMediaPhotoExternal: {
     692              :         /* flags:# url:string ttl_seconds:flags.0?int */
     693            0 :         if (r->len - r->pos < 4) return -1;
     694            0 :         uint32_t flags = tl_read_uint32(r);
     695            0 :         if (tl_skip_string(r) != 0) return -1;
     696            0 :         if (flags & 1u) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
     697            0 :         return 0;
     698              :     }
     699            0 :     case CRC_inputMediaDocumentExternal: {
     700              :         /* flags:# url:string ttl_seconds:flags.0?int */
     701            0 :         if (r->len - r->pos < 4) return -1;
     702            0 :         uint32_t flags = tl_read_uint32(r);
     703            0 :         if (tl_skip_string(r) != 0) return -1;
     704            0 :         if (flags & 1u) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
     705            0 :         return 0;
     706              :     }
     707            0 :     case CRC_inputMediaUploadedPhoto:
     708              :     case CRC_inputMediaUploadedDocument:
     709              :     case CRC_inputMediaInvoice:
     710            0 :         logger_log(LOG_DEBUG,
     711              :                    "skip_input_media: transient/complex variant 0x%08x in draft",
     712              :                    crc);
     713            0 :         return -1;
     714            0 :     default:
     715            0 :         logger_log(LOG_WARN, "skip_input_media: unknown 0x%08x", crc);
     716            0 :         return -1;
     717              :     }
     718              : }
     719              : 
     720              : /* inputReplyToMessage#3faad5f0 flags:# reply_to_msg_id:int top_msg_id:flags.0?int
     721              :  *   reply_to_peer_id:flags.1?InputPeer quote_text:flags.2?string
     722              :  *   quote_entities:flags.3?Vector<MessageEntity> quote_offset:flags.4?int
     723              :  * inputReplyToStory#7e2a9a7b peer:InputPeer story_id:int */
     724            0 : static int skip_input_reply_to(TlReader *r) {
     725            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     726            0 :     uint32_t crc = tl_read_uint32(r);
     727            0 :     if (crc == CRC_inputReplyToMessage) {
     728            0 :         if (r->len - r->pos < 8) return -1;
     729            0 :         uint32_t flags = tl_read_uint32(r);
     730            0 :         tl_read_int32(r);                               /* reply_to_msg_id */
     731            0 :         if (flags & 1u) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
     732            0 :         if (flags & (1u << 1)) { if (skip_input_peer(r) != 0) return -1; }
     733            0 :         if (flags & (1u << 2)) { if (tl_skip_string(r) != 0) return -1; }
     734            0 :         if (flags & (1u << 3)) {
     735            0 :             if (tl_skip_message_entities_vector(r) != 0) return -1;
     736              :         }
     737            0 :         if (flags & (1u << 4)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
     738            0 :         return 0;
     739              :     }
     740            0 :     if (crc == CRC_inputReplyToStory) {
     741            0 :         if (skip_input_peer(r) != 0) return -1;
     742            0 :         if (r->len - r->pos < 4) return -1;
     743            0 :         tl_read_int32(r);                               /* story_id */
     744            0 :         return 0;
     745              :     }
     746            0 :     logger_log(LOG_WARN, "skip_input_reply_to: unknown 0x%08x", crc);
     747            0 :     return -1;
     748              : }
     749              : 
     750              : /* draftMessageEmpty#1b0c841a flags:# date:flags.0?int
     751              :  * draftMessage#3fccf7ef flags:# no_webpage:flags.1?true invert_media:flags.6?true
     752              :  *   reply_to:flags.4?InputReplyTo message:string
     753              :  *   entities:flags.3?Vector<MessageEntity> media:flags.5?InputMedia date:int */
     754            4 : int tl_skip_draft_message(TlReader *r) {
     755            4 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     756            4 :     uint32_t crc = tl_read_uint32(r);
     757            4 :     if (crc == CRC_draftMessageEmpty) {
     758            2 :         if (r->len - r->pos < 4) return -1;
     759            2 :         uint32_t flags = tl_read_uint32(r);
     760            2 :         if (flags & 1u) {
     761            1 :             if (r->len - r->pos < 4) return -1;
     762            1 :             tl_read_int32(r); /* date */
     763              :         }
     764            2 :         return 0;
     765              :     }
     766            2 :     if (crc == CRC_draftMessage) {
     767            1 :         if (r->len - r->pos < 4) return -1;
     768            1 :         uint32_t flags = tl_read_uint32(r);
     769            1 :         if (flags & (1u << 4)) {
     770            0 :             if (skip_input_reply_to(r) != 0) return -1;
     771              :         }
     772            1 :         if (tl_skip_string(r) != 0) return -1;         /* message */
     773            0 :         if (flags & (1u << 3)) {
     774            0 :             if (tl_skip_message_entities_vector(r) != 0) return -1;
     775              :         }
     776            0 :         if (flags & (1u << 5)) {
     777            0 :             if (skip_input_media(r) != 0) return -1;
     778              :         }
     779            0 :         if (r->len - r->pos < 4) return -1;
     780            0 :         tl_read_int32(r);                               /* date */
     781            0 :         return 0;
     782              :     }
     783            1 :     logger_log(LOG_WARN, "tl_skip_draft_message: unknown 0x%08x", crc);
     784            1 :     return -1;
     785              : }
     786              : 
     787           10 : int tl_skip_message_entity(TlReader *r) {
     788           10 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     789           10 :     uint32_t crc = tl_read_uint32(r);
     790              : 
     791              :     /* All entities start with offset:int length:int (8 bytes). */
     792           10 :     switch (crc) {
     793            5 :     case CRC_messageEntityUnknown:
     794              :     case CRC_messageEntityMention:
     795              :     case CRC_messageEntityHashtag:
     796              :     case CRC_messageEntityBotCommand:
     797              :     case CRC_messageEntityUrl:
     798              :     case CRC_messageEntityEmail:
     799              :     case CRC_messageEntityBold:
     800              :     case CRC_messageEntityItalic:
     801              :     case CRC_messageEntityCode:
     802              :     case CRC_messageEntityPhone:
     803              :     case CRC_messageEntityCashtag:
     804              :     case CRC_messageEntityUnderline:
     805              :     case CRC_messageEntityStrike:
     806              :     case CRC_messageEntityBankCard:
     807              :     case CRC_messageEntitySpoiler:
     808            5 :         if (r->len - r->pos < 8) return -1;
     809            5 :         tl_read_int32(r); tl_read_int32(r);
     810            5 :         return 0;
     811              : 
     812            0 :     case CRC_messageEntityPre:
     813            0 :         if (r->len - r->pos < 8) return -1;
     814            0 :         tl_read_int32(r); tl_read_int32(r);
     815            0 :         return tl_skip_string(r); /* language */
     816              : 
     817            1 :     case CRC_messageEntityTextUrl:
     818            1 :         if (r->len - r->pos < 8) return -1;
     819            1 :         tl_read_int32(r); tl_read_int32(r);
     820            1 :         return tl_skip_string(r); /* url */
     821              : 
     822            1 :     case CRC_messageEntityMentionName:
     823            1 :         if (r->len - r->pos < 16) return -1;
     824            1 :         tl_read_int32(r); tl_read_int32(r);
     825            1 :         tl_read_int64(r); /* user_id */
     826            1 :         return 0;
     827              : 
     828            1 :     case CRC_messageEntityCustomEmoji:
     829            1 :         if (r->len - r->pos < 16) return -1;
     830            1 :         tl_read_int32(r); tl_read_int32(r);
     831            1 :         tl_read_int64(r); /* document_id */
     832            1 :         return 0;
     833              : 
     834            1 :     case CRC_messageEntityBlockquote:
     835              :         /* flags:# offset:int length:int — no string payload */
     836            1 :         if (r->len - r->pos < 12) return -1;
     837            1 :         tl_read_uint32(r); /* flags */
     838            1 :         tl_read_int32(r); tl_read_int32(r);
     839            1 :         return 0;
     840              : 
     841            1 :     default:
     842            1 :         logger_log(LOG_WARN, "tl_skip_message_entity: unknown 0x%08x", crc);
     843            1 :         return -1;
     844              :     }
     845              : }
     846              : 
     847           18 : int tl_skip_message_entities_vector(TlReader *r) {
     848           18 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
     849           18 :     uint32_t vec_crc = tl_read_uint32(r);
     850           18 :     if (vec_crc != TL_vector) {
     851            0 :         logger_log(LOG_WARN,
     852              :                    "tl_skip_message_entities_vector: expected vector 0x%08x",
     853              :                    vec_crc);
     854            0 :         return -1;
     855              :     }
     856           18 :     uint32_t count = tl_read_uint32(r);
     857           27 :     for (uint32_t i = 0; i < count; i++) {
     858           10 :         if (tl_skip_message_entity(r) != 0) return -1;
     859              :     }
     860           17 :     return 0;
     861              : }
     862              : 
     863              : /* ---- ReplyMarkup skipper ---- */
     864              : 
     865              : /* InlineQueryPeerType variants — all singleton objects (CRC only, no payload).
     866              :  * inlineQueryPeerTypeSameBotPM#3081ed9d  inlineQueryPeerTypePM#833c0fac
     867              :  * inlineQueryPeerTypeChat#d766c50a        inlineQueryPeerTypeMegagroup#5bc52d1a
     868              :  * inlineQueryPeerTypeBroadcast#6d1ded88   inlineQueryPeerTypeBotPM#e3b2a56a   */
     869              : #define CRC_inlineQueryPeerTypeSameBotPM  0x3081ed9du
     870              : #define CRC_inlineQueryPeerTypePM         0x833c0facu
     871              : #define CRC_inlineQueryPeerTypeChat       0xd766c50au
     872              : #define CRC_inlineQueryPeerTypeMegagroup  0x5bc52d1au
     873              : #define CRC_inlineQueryPeerTypeBroadcast  0x6d1ded88u
     874              : #define CRC_inlineQueryPeerTypeBotPM      0xe3b2a56au
     875              : 
     876            0 : static int skip_inline_query_peer_type_vector(TlReader *r) {
     877            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
     878            0 :     uint32_t vec = tl_read_uint32(r);
     879            0 :     if (vec != TL_vector) return -1;
     880            0 :     uint32_t n = tl_read_uint32(r);
     881            0 :     for (uint32_t i = 0; i < n; i++) {
     882            0 :         if (r->len - r->pos < 4) return -1;
     883            0 :         uint32_t crc = tl_read_uint32(r);
     884            0 :         switch (crc) {
     885            0 :         case CRC_inlineQueryPeerTypeSameBotPM:
     886              :         case CRC_inlineQueryPeerTypePM:
     887              :         case CRC_inlineQueryPeerTypeChat:
     888              :         case CRC_inlineQueryPeerTypeMegagroup:
     889              :         case CRC_inlineQueryPeerTypeBroadcast:
     890              :         case CRC_inlineQueryPeerTypeBotPM:
     891            0 :             break; /* singleton — no payload */
     892            0 :         default:
     893            0 :             logger_log(LOG_WARN,
     894              :                        "skip_inline_query_peer_type: unknown 0x%08x", crc);
     895            0 :             return -1;
     896              :         }
     897              :     }
     898            0 :     return 0;
     899              : }
     900              : 
     901              : /* Skip a KeyboardButton. Returns -1 on unknown variant. */
     902            5 : static int skip_keyboard_button(TlReader *r) {
     903            5 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     904            5 :     uint32_t crc = tl_read_uint32(r);
     905            5 :     switch (crc) {
     906            1 :     case CRC_keyboardButton:
     907              :     case CRC_keyboardButtonRequestPhone:
     908              :     case CRC_keyboardButtonRequestGeoLoc:
     909              :     case CRC_keyboardButtonGame:
     910              :     case CRC_keyboardButtonBuy:
     911            1 :         return tl_skip_string(r);                 /* text */
     912            1 :     case CRC_keyboardButtonUrl: {
     913            1 :         if (tl_skip_string(r) != 0) return -1;    /* text */
     914            1 :         return tl_skip_string(r);                 /* url */
     915              :     }
     916            3 :     case CRC_keyboardButtonCallback: {
     917            3 :         if (r->len - r->pos < 4) return -1;
     918            3 :         tl_read_uint32(r);                        /* flags */
     919            3 :         if (tl_skip_string(r) != 0) return -1;    /* text */
     920            3 :         return tl_skip_string(r);                 /* data:bytes */
     921              :     }
     922            0 :     case CRC_keyboardButtonSwitchInline: {
     923            0 :         if (r->len - r->pos < 4) return -1;
     924            0 :         uint32_t flags = tl_read_uint32(r);
     925            0 :         if (tl_skip_string(r) != 0) return -1;    /* text */
     926            0 :         if (tl_skip_string(r) != 0) return -1;    /* query */
     927            0 :         if (flags & (1u << 1)) {
     928            0 :             if (skip_inline_query_peer_type_vector(r) != 0) return -1;
     929              :         }
     930            0 :         return 0;
     931              :     }
     932            0 :     case CRC_keyboardButtonUrlAuth: {
     933            0 :         if (r->len - r->pos < 4) return -1;
     934            0 :         uint32_t flags = tl_read_uint32(r);
     935            0 :         if (tl_skip_string(r) != 0) return -1;    /* text */
     936            0 :         if (flags & (1u << 0))
     937            0 :             if (tl_skip_string(r) != 0) return -1;/* fwd_text */
     938            0 :         if (tl_skip_string(r) != 0) return -1;    /* url */
     939            0 :         if (r->len - r->pos < 4) return -1;
     940            0 :         tl_read_int32(r);                         /* button_id */
     941            0 :         return 0;
     942              :     }
     943            0 :     case CRC_keyboardButtonRequestPoll: {
     944            0 :         if (r->len - r->pos < 4) return -1;
     945            0 :         uint32_t flags = tl_read_uint32(r);
     946            0 :         if (flags & (1u << 0)) {
     947            0 :             if (r->len - r->pos < 4) return -1;
     948            0 :             tl_read_uint32(r);                    /* Bool (crc only) */
     949              :         }
     950            0 :         return tl_skip_string(r);                 /* text */
     951              :     }
     952            0 :     case CRC_keyboardButtonUserProfile: {
     953            0 :         if (tl_skip_string(r) != 0) return -1;    /* text */
     954            0 :         if (r->len - r->pos < 8) return -1;
     955            0 :         tl_read_int64(r);                         /* user_id */
     956            0 :         return 0;
     957              :     }
     958            0 :     case CRC_keyboardButtonWebView:
     959              :     case CRC_keyboardButtonSimpleWebView: {
     960            0 :         if (tl_skip_string(r) != 0) return -1;    /* text */
     961            0 :         return tl_skip_string(r);                 /* url */
     962              :     }
     963            0 :     default:
     964            0 :         logger_log(LOG_WARN, "skip_keyboard_button: unknown 0x%08x", crc);
     965            0 :         return -1;
     966              :     }
     967              : }
     968              : 
     969              : /* Skip a KeyboardButtonRow = crc + Vector<KeyboardButton>. */
     970            4 : static int skip_keyboard_button_row(TlReader *r) {
     971            4 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
     972            4 :     uint32_t crc = tl_read_uint32(r);
     973            4 :     if (crc != CRC_keyboardButtonRow) {
     974            0 :         logger_log(LOG_WARN,
     975              :                    "skip_keyboard_button_row: expected 0x77608b83 got 0x%08x",
     976              :                    crc);
     977            0 :         return -1;
     978              :     }
     979            4 :     if (r->len - r->pos < 8) return -1;
     980            4 :     uint32_t vec_crc = tl_read_uint32(r);
     981            4 :     if (vec_crc != TL_vector) return -1;
     982            4 :     uint32_t n = tl_read_uint32(r);
     983            9 :     for (uint32_t i = 0; i < n; i++) {
     984            5 :         if (skip_keyboard_button(r) != 0) return -1;
     985              :     }
     986            4 :     return 0;
     987              : }
     988              : 
     989              : /* Skip a Vector<KeyboardButtonRow>. */
     990            5 : static int skip_button_row_vector(TlReader *r) {
     991            5 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
     992            5 :     uint32_t vec_crc = tl_read_uint32(r);
     993            5 :     if (vec_crc != TL_vector) return -1;
     994            5 :     uint32_t n = tl_read_uint32(r);
     995            9 :     for (uint32_t i = 0; i < n; i++) {
     996            4 :         if (skip_keyboard_button_row(r) != 0) return -1;
     997              :     }
     998            5 :     return 0;
     999              : }
    1000              : 
    1001            9 : int tl_skip_reply_markup(TlReader *r) {
    1002            9 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1003            9 :     uint32_t crc = tl_read_uint32(r);
    1004            9 :     switch (crc) {
    1005            2 :     case CRC_replyKeyboardHide:
    1006              :     case CRC_replyKeyboardForceReply: {
    1007              :         /* Body: flags:# (+ optional placeholder:flags.3?string for
    1008              :          * forceReply). */
    1009            2 :         if (r->len - r->pos < 4) return -1;
    1010            2 :         uint32_t flags = tl_read_uint32(r);
    1011            2 :         if (crc == CRC_replyKeyboardForceReply && (flags & (1u << 3))) {
    1012            1 :             if (tl_skip_string(r) != 0) return -1; /* placeholder */
    1013              :         }
    1014            2 :         return 0;
    1015              :     }
    1016            0 :     case CRC_replyKeyboardMarkup: {
    1017            0 :         if (r->len - r->pos < 4) return -1;
    1018            0 :         uint32_t flags = tl_read_uint32(r);
    1019            0 :         if (skip_button_row_vector(r) != 0) return -1;
    1020            0 :         if (flags & (1u << 3)) {
    1021            0 :             if (tl_skip_string(r) != 0) return -1; /* placeholder */
    1022              :         }
    1023            0 :         return 0;
    1024              :     }
    1025            5 :     case CRC_replyInlineMarkup:
    1026            5 :         return skip_button_row_vector(r);
    1027            2 :     default:
    1028            2 :         logger_log(LOG_WARN, "tl_skip_reply_markup: unknown 0x%08x", crc);
    1029            2 :         return -1;
    1030              :     }
    1031              : }
    1032              : 
    1033              : /* ---- MessageReactions skipper ---- */
    1034              : 
    1035              : /* Skip a Reaction variant. */
    1036            8 : static int skip_reaction(TlReader *r) {
    1037            8 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1038            8 :     uint32_t crc = tl_read_uint32(r);
    1039            8 :     switch (crc) {
    1040            2 :     case CRC_reactionEmpty:
    1041              :     case CRC_reactionPaid:
    1042            2 :         return 0;
    1043            4 :     case CRC_reactionEmoji:
    1044            4 :         return tl_skip_string(r);
    1045            1 :     case CRC_reactionCustomEmoji:
    1046            1 :         if (r->len - r->pos < 8) return -1;
    1047            1 :         tl_read_int64(r);                       /* document_id */
    1048            1 :         return 0;
    1049            1 :     default:
    1050            1 :         logger_log(LOG_WARN, "skip_reaction: unknown 0x%08x", crc);
    1051            1 :         return -1;
    1052              :     }
    1053              : }
    1054              : 
    1055              : /* Skip a ReactionCount#a3d1cb80: flags + (chosen_order) + Reaction + count. */
    1056            7 : static int skip_reaction_count(TlReader *r) {
    1057            7 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1058            7 :     uint32_t crc = tl_read_uint32(r);
    1059            7 :     if (crc != CRC_reactionCount) {
    1060            0 :         logger_log(LOG_WARN, "skip_reaction_count: expected 0xa3d1cb80 got 0x%08x",
    1061              :                    crc);
    1062            0 :         return -1;
    1063              :     }
    1064            7 :     if (r->len - r->pos < 4) return -1;
    1065            7 :     uint32_t flags = tl_read_uint32(r);
    1066            7 :     if (flags & (1u << 0)) {
    1067            1 :         if (r->len - r->pos < 4) return -1;
    1068            1 :         tl_read_int32(r);                       /* chosen_order */
    1069              :     }
    1070            7 :     if (skip_reaction(r) != 0) return -1;
    1071            6 :     if (r->len - r->pos < 4) return -1;
    1072            6 :     tl_read_int32(r);                           /* count */
    1073            6 :     return 0;
    1074              : }
    1075              : 
    1076              : /* messagePeerReaction#b156fe9d flags:# big:flags.0?true unread:flags.1?true
    1077              :  *   reaction:Reaction date:int peer_id:Peer */
    1078            0 : static int skip_message_peer_reaction(TlReader *r) {
    1079            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1080            0 :     uint32_t crc = tl_read_uint32(r);
    1081            0 :     if (crc != CRC_messagePeerReaction) {
    1082            0 :         logger_log(LOG_WARN, "skip_message_peer_reaction: unknown 0x%08x", crc);
    1083            0 :         return -1;
    1084              :     }
    1085            0 :     if (r->len - r->pos < 4) return -1;
    1086            0 :     (void)tl_read_uint32(r);                        /* flags — big/unread are bools */
    1087            0 :     if (skip_reaction(r) != 0) return -1;
    1088            0 :     if (r->len - r->pos < 4) return -1;
    1089            0 :     tl_read_int32(r);                               /* date */
    1090            0 :     return tl_skip_peer(r);                         /* peer_id */
    1091              : }
    1092              : 
    1093              : /* messageReactor#edd1b4ad flags:# top:flags.0?true my:flags.1?true
    1094              :  *   anonymous:flags.2?true peer_id:flags.3?Peer count:int date:int */
    1095            0 : static int skip_message_reactor(TlReader *r) {
    1096            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1097            0 :     uint32_t crc = tl_read_uint32(r);
    1098            0 :     if (crc != CRC_messageReactor) {
    1099            0 :         logger_log(LOG_WARN, "skip_message_reactor: unknown 0x%08x", crc);
    1100            0 :         return -1;
    1101              :     }
    1102            0 :     if (r->len - r->pos < 4) return -1;
    1103            0 :     uint32_t flags = tl_read_uint32(r);
    1104            0 :     if (flags & (1u << 3)) {
    1105            0 :         if (tl_skip_peer(r) != 0) return -1;        /* peer_id */
    1106              :     }
    1107            0 :     if (r->len - r->pos < 8) return -1;
    1108            0 :     tl_read_int32(r);                               /* count */
    1109            0 :     tl_read_int32(r);                               /* date */
    1110            0 :     return 0;
    1111              : }
    1112              : 
    1113            6 : int tl_skip_message_reactions(TlReader *r) {
    1114            6 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1115            6 :     uint32_t crc = tl_read_uint32(r);
    1116            6 :     if (crc != CRC_messageReactions) {
    1117            0 :         logger_log(LOG_WARN, "tl_skip_message_reactions: unknown 0x%08x", crc);
    1118            0 :         return -1;
    1119              :     }
    1120            6 :     if (r->len - r->pos < 4) return -1;
    1121            6 :     uint32_t flags = tl_read_uint32(r);
    1122              :     /* results:Vector<ReactionCount> — required. */
    1123            6 :     if (r->len - r->pos < 8) return -1;
    1124            6 :     uint32_t vec_crc = tl_read_uint32(r);
    1125            6 :     if (vec_crc != TL_vector) return -1;
    1126            6 :     uint32_t n = tl_read_uint32(r);
    1127           11 :     for (uint32_t i = 0; i < n; i++) {
    1128            6 :         if (skip_reaction_count(r) != 0) return -1;
    1129              :     }
    1130            5 :     if (flags & (1u << 1)) {
    1131              :         /* recent_reactions:Vector<MessagePeerReaction> */
    1132            1 :         if (r->len - r->pos < 8) return -1;
    1133            0 :         uint32_t rv = tl_read_uint32(r);
    1134            0 :         if (rv != TL_vector) return -1;
    1135            0 :         uint32_t rn = tl_read_uint32(r);
    1136            0 :         for (uint32_t i = 0; i < rn; i++) {
    1137            0 :             if (skip_message_peer_reaction(r) != 0) return -1;
    1138              :         }
    1139              :     }
    1140            4 :     if (flags & (1u << 2)) {
    1141              :         /* top_reactors:Vector<MessageReactor> */
    1142            0 :         if (r->len - r->pos < 8) return -1;
    1143            0 :         uint32_t tv = tl_read_uint32(r);
    1144            0 :         if (tv != TL_vector) return -1;
    1145            0 :         uint32_t tn = tl_read_uint32(r);
    1146            0 :         for (uint32_t i = 0; i < tn; i++) {
    1147            0 :             if (skip_message_reactor(r) != 0) return -1;
    1148              :         }
    1149              :     }
    1150            4 :     return 0;
    1151              : }
    1152              : 
    1153              : /* ---- MessageReplies skipper ----
    1154              :  * messageReplies#83d60fc2 flags:# comments:flags.0?true
    1155              :  *   replies:int replies_pts:int
    1156              :  *   recent_repliers:flags.1?Vector<Peer>
    1157              :  *   channel_id:flags.0?long
    1158              :  *   max_id:flags.2?int  read_max_id:flags.3?int
    1159              :  */
    1160            6 : int tl_skip_message_replies(TlReader *r) {
    1161            6 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1162            6 :     uint32_t crc = tl_read_uint32(r);
    1163            6 :     if (crc != CRC_messageReplies) {
    1164            1 :         logger_log(LOG_WARN, "tl_skip_message_replies: unknown 0x%08x", crc);
    1165            1 :         return -1;
    1166              :     }
    1167            5 :     if (r->len - r->pos < 12) return -1;
    1168            5 :     uint32_t flags = tl_read_uint32(r);
    1169            5 :     tl_read_int32(r);                                 /* replies */
    1170            5 :     tl_read_int32(r);                                 /* replies_pts */
    1171            5 :     if (flags & (1u << 1)) {
    1172            1 :         if (r->len - r->pos < 8) return -1;
    1173            1 :         uint32_t vec_crc = tl_read_uint32(r);
    1174            1 :         if (vec_crc != TL_vector) return -1;
    1175            1 :         uint32_t n = tl_read_uint32(r);
    1176            2 :         for (uint32_t i = 0; i < n; i++) {
    1177            1 :             if (tl_skip_peer(r) != 0) return -1;
    1178              :         }
    1179              :     }
    1180            5 :     if (flags & (1u << 0)) {
    1181            1 :         if (r->len - r->pos < 8) return -1;
    1182            1 :         tl_read_int64(r);                             /* channel_id */
    1183              :     }
    1184            5 :     if (flags & (1u << 2)) {
    1185            1 :         if (r->len - r->pos < 4) return -1;
    1186            1 :         tl_read_int32(r);                             /* max_id */
    1187              :     }
    1188            5 :     if (flags & (1u << 3)) {
    1189            1 :         if (r->len - r->pos < 4) return -1;
    1190            1 :         tl_read_int32(r);                             /* read_max_id */
    1191              :     }
    1192            5 :     return 0;
    1193              : }
    1194              : 
    1195              : /* ---- FactCheck skipper ----
    1196              :  * factCheck#b89bfccf flags:#
    1197              :  *   need_check:flags.0?true
    1198              :  *   country:flags.1?string
    1199              :  *   text:flags.1?TextWithEntities
    1200              :  *   hash:long
    1201              :  *
    1202              :  * textWithEntities#751f3146 text:string entities:Vector<MessageEntity>
    1203              :  */
    1204            6 : int tl_skip_factcheck(TlReader *r) {
    1205            6 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1206            6 :     uint32_t crc = tl_read_uint32(r);
    1207            6 :     if (crc != CRC_factCheck) {
    1208            1 :         logger_log(LOG_WARN, "tl_skip_factcheck: unknown 0x%08x", crc);
    1209            1 :         return -1;
    1210              :     }
    1211            5 :     if (r->len - r->pos < 4) return -1;
    1212            5 :     uint32_t flags = tl_read_uint32(r);
    1213            5 :     if (flags & (1u << 1)) {
    1214            4 :         if (tl_skip_string(r) != 0) return -1;  /* country */
    1215              :         /* text:TextWithEntities */
    1216            4 :         if (r->len - r->pos < 4) return -1;
    1217            4 :         uint32_t twe_crc = tl_read_uint32(r);
    1218            4 :         if (twe_crc != CRC_textWithEntities) {
    1219            0 :             logger_log(LOG_WARN,
    1220              :                        "tl_skip_factcheck: expected textWithEntities got 0x%08x",
    1221              :                        twe_crc);
    1222            0 :             return -1;
    1223              :         }
    1224            4 :         if (tl_skip_string(r) != 0) return -1;  /* text */
    1225            4 :         if (tl_skip_message_entities_vector(r) != 0) return -1;
    1226              :     }
    1227            5 :     if (r->len - r->pos < 8) return -1;
    1228            5 :     tl_read_int64(r);                            /* hash */
    1229            5 :     return 0;
    1230              : }
    1231              : 
    1232              : /* messageFwdHeader#4e4df4bb flags:#
    1233              :  *   imported:flags.7?true           (no data)
    1234              :  *   saved_out:flags.11?true         (no data)
    1235              :  *   from_id:flags.0?Peer
    1236              :  *   from_name:flags.5?string
    1237              :  *   date:int                        (always present)
    1238              :  *   channel_post:flags.2?int
    1239              :  *   post_author:flags.3?string
    1240              :  *   saved_from_peer:flags.4?Peer
    1241              :  *   saved_from_msg_id:flags.4?int
    1242              :  *   saved_from_id:flags.8?Peer
    1243              :  *   saved_from_name:flags.9?string
    1244              :  *   saved_date:flags.10?int
    1245              :  *   psa_type:flags.6?string
    1246              :  */
    1247            5 : int tl_skip_message_fwd_header(TlReader *r) {
    1248            5 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
    1249            5 :     uint32_t crc = tl_read_uint32(r);
    1250            5 :     if (crc != CRC_messageFwdHeader) {
    1251            1 :         logger_log(LOG_WARN,
    1252              :                    "tl_skip_message_fwd_header: unexpected 0x%08x", crc);
    1253            1 :         return -1;
    1254              :     }
    1255            4 :     uint32_t flags = tl_read_uint32(r);
    1256              : 
    1257            4 :     if (flags & (1u << 0)) if (tl_skip_peer(r) != 0) return -1;
    1258            4 :     if (flags & (1u << 5)) if (tl_skip_string(r) != 0) return -1;
    1259            4 :     if (r->len - r->pos < 4) return -1;
    1260            4 :     tl_read_int32(r); /* date */
    1261            4 :     if (flags & (1u << 2)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    1262            4 :     if (flags & (1u << 3)) if (tl_skip_string(r) != 0) return -1;
    1263            4 :     if (flags & (1u << 4)) {
    1264            0 :         if (tl_skip_peer(r) != 0) return -1;
    1265            0 :         if (r->len - r->pos < 4) return -1;
    1266            0 :         tl_read_int32(r);
    1267              :     }
    1268            4 :     if (flags & (1u << 8)) if (tl_skip_peer(r) != 0) return -1;
    1269            4 :     if (flags & (1u << 9)) if (tl_skip_string(r) != 0) return -1;
    1270            4 :     if (flags & (1u << 10)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    1271            4 :     if (flags & (1u << 6)) if (tl_skip_string(r) != 0) return -1;
    1272            4 :     return 0;
    1273              : }
    1274              : 
    1275              : /* messageReplyHeader#afbc09db flags:#
    1276              :  *   reply_to_scheduled:flags.2?true   (no data)
    1277              :  *   forum_topic:flags.3?true          (no data)
    1278              :  *   quote:flags.9?true                (no data)
    1279              :  *   reply_to_msg_id:flags.4?int
    1280              :  *   reply_to_peer_id:flags.0?Peer
    1281              :  *   reply_from:flags.5?MessageFwdHeader
    1282              :  *   reply_media:flags.8?MessageMedia
    1283              :  *   reply_to_top_id:flags.1?int
    1284              :  *   quote_text:flags.6?string
    1285              :  *   quote_entities:flags.7?Vector<MessageEntity>
    1286              :  *   quote_offset:flags.10?int
    1287              :  */
    1288            7 : int tl_skip_message_reply_header(TlReader *r) {
    1289            7 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1290            7 :     uint32_t crc = tl_read_uint32(r);
    1291            7 :     if (crc == CRC_messageReplyStoryHeader) {
    1292              :         /* peer_id:Peer story_id:int */
    1293            1 :         if (tl_skip_peer(r) != 0) return -1;
    1294            1 :         if (r->len - r->pos < 4) return -1;
    1295            1 :         tl_read_int32(r);
    1296            1 :         return 0;
    1297              :     }
    1298            6 :     if (crc != CRC_messageReplyHeader) {
    1299            1 :         logger_log(LOG_WARN,
    1300              :                    "tl_skip_message_reply_header: unexpected 0x%08x", crc);
    1301            1 :         return -1;
    1302              :     }
    1303            5 :     if (r->len - r->pos < 4) return -1;
    1304            5 :     uint32_t flags = tl_read_uint32(r);
    1305              : 
    1306            5 :     if (flags & (1u << 4)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    1307            5 :     if (flags & (1u << 0)) if (tl_skip_peer(r) != 0) return -1;
    1308            5 :     if (flags & (1u << 5)) if (tl_skip_message_fwd_header(r) != 0) return -1;
    1309            5 :     if (flags & (1u << 8)) {
    1310            0 :         if (tl_skip_message_media(r) != 0) return -1;
    1311              :     }
    1312            5 :     if (flags & (1u << 1)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    1313            5 :     if (flags & (1u << 6)) if (tl_skip_string(r) != 0) return -1;
    1314            5 :     if (flags & (1u << 7)) if (tl_skip_message_entities_vector(r) != 0) return -1;
    1315            5 :     if (flags & (1u << 10)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    1316            5 :     return 0;
    1317              : }
    1318              : 
    1319              : /* ---- PhotoSize skipper ----
    1320              :  * Variants:
    1321              :  *   photoSizeEmpty#0e17e23c type:string
    1322              :  *   photoSize#75c78e60 type:string w:int h:int size:int
    1323              :  *   photoCachedSize#021e1ad6 type:string w:int h:int bytes:bytes
    1324              :  *   photoStrippedSize#e0b0bc2e type:string bytes:bytes
    1325              :  *   photoSizeProgressive#fa3efb95 type:string w:int h:int sizes:Vector<int>
    1326              :  *   photoPathSize#d8214d41 type:string bytes:bytes
    1327              :  */
    1328            6 : int tl_skip_photo_size(TlReader *r) {
    1329            6 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1330            6 :     uint32_t crc = tl_read_uint32(r);
    1331            6 :     switch (crc) {
    1332            2 :     case CRC_photoSizeEmpty:
    1333            2 :         return tl_skip_string(r);
    1334            1 :     case CRC_photoSize:
    1335            1 :         if (tl_skip_string(r) != 0) return -1;
    1336            1 :         if (r->len - r->pos < 12) return -1;
    1337            1 :         tl_read_int32(r); tl_read_int32(r); tl_read_int32(r);
    1338            1 :         return 0;
    1339            0 :     case CRC_photoCachedSize:
    1340            0 :         if (tl_skip_string(r) != 0) return -1;
    1341            0 :         if (r->len - r->pos < 8) return -1;
    1342            0 :         tl_read_int32(r); tl_read_int32(r);
    1343            0 :         return tl_skip_string(r); /* bytes */
    1344            2 :     case CRC_photoStrippedSize:
    1345              :     case CRC_photoPathSize:
    1346            2 :         if (tl_skip_string(r) != 0) return -1;
    1347            2 :         return tl_skip_string(r); /* bytes */
    1348            0 :     case CRC_photoSizeProgressive: {
    1349            0 :         if (tl_skip_string(r) != 0) return -1;
    1350            0 :         if (r->len - r->pos < 8) return -1;
    1351            0 :         tl_read_int32(r); tl_read_int32(r);
    1352              :         /* sizes:Vector<int> */
    1353            0 :         if (r->len - r->pos < 8) return -1;
    1354            0 :         uint32_t vec_crc = tl_read_uint32(r);
    1355            0 :         if (vec_crc != TL_vector) return -1;
    1356            0 :         uint32_t count = tl_read_uint32(r);
    1357            0 :         if (r->len - r->pos < count * 4ULL) return -1;
    1358            0 :         for (uint32_t i = 0; i < count; i++) tl_read_int32(r);
    1359            0 :         return 0;
    1360              :     }
    1361            1 :     default:
    1362            1 :         logger_log(LOG_WARN, "tl_skip_photo_size: unknown 0x%08x", crc);
    1363            1 :         return -1;
    1364              :     }
    1365              : }
    1366              : 
    1367            1 : int tl_skip_photo_size_vector(TlReader *r) {
    1368            1 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
    1369            1 :     uint32_t vec_crc = tl_read_uint32(r);
    1370            1 :     if (vec_crc != TL_vector) return -1;
    1371            1 :     uint32_t count = tl_read_uint32(r);
    1372            2 :     for (uint32_t i = 0; i < count; i++) {
    1373            1 :         if (tl_skip_photo_size(r) != 0) return -1;
    1374              :     }
    1375            1 :     return 0;
    1376              : }
    1377              : 
    1378              : /* ---- Photo skipper / extractor ----
    1379              :  * photoEmpty#2331b22d id:long
    1380              :  * photo#fb197a65 flags:# has_stickers:flags.0?true id:long access_hash:long
    1381              :  *                file_reference:bytes date:int
    1382              :  *                sizes:Vector<PhotoSize>
    1383              :  *                video_sizes:flags.1?Vector<VideoSize>
    1384              :  *                dc_id:int
    1385              :  *
    1386              :  * photo_full walks the full object and, when @p out is non-NULL, fills
    1387              :  * access_hash, file_reference, dc_id and the largest PhotoSize.type.
    1388              :  * Used by tl_skip_message_media_ex for MEDIA_PHOTO + file-download
    1389              :  * support. tl_skip_photo is a thin wrapper that passes NULL.
    1390              :  */
    1391              : 
    1392              : /* Forward declaration — defined later after skip_video_size. */
    1393              : static int skip_video_size_vector(TlReader *r);
    1394              : 
    1395              : /* Walk a Vector<PhotoSize> and record the `type` of the largest entry
    1396              :  * (by pixel dimensions for photoSize / photoSizeProgressive, by byte
    1397              :  * count as a tie-breaker for cached/stripped forms). Leaves the cursor
    1398              :  * past the vector. */
    1399            3 : static int walk_photo_size_vector(TlReader *r,
    1400              :                                     char *best_type, size_t best_type_cap) {
    1401            3 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
    1402            3 :     uint32_t vec_crc = tl_read_uint32(r);
    1403            3 :     if (vec_crc != TL_vector) return -1;
    1404            3 :     uint32_t count = tl_read_uint32(r);
    1405              : 
    1406            3 :     long best_score = -1;
    1407            3 :     if (best_type && best_type_cap) best_type[0] = '\0';
    1408              : 
    1409            8 :     for (uint32_t i = 0; i < count; i++) {
    1410            5 :         if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1411            5 :         uint32_t crc = tl_read_uint32(r);
    1412              : 
    1413              :         /* Capture type:string into a small buffer we may keep as best. */
    1414            5 :         char type_buf[8] = {0};
    1415            5 :         size_t type_n = 0;
    1416              :         {
    1417            5 :             size_t before = r->pos;
    1418            5 :             char *s = tl_read_string(r);
    1419            5 :             if (!s) return -1;
    1420            5 :             type_n = strlen(s);
    1421            5 :             if (type_n >= sizeof(type_buf)) type_n = sizeof(type_buf) - 1;
    1422            5 :             memcpy(type_buf, s, type_n);
    1423            5 :             type_buf[type_n] = '\0';
    1424            5 :             free(s);
    1425              :             (void)before;
    1426              :         }
    1427              : 
    1428            5 :         long score = 0;
    1429            5 :         switch (crc) {
    1430            0 :         case CRC_photoSizeEmpty:
    1431            0 :             score = -1; /* empty — never best */
    1432            0 :             break;
    1433            3 :         case CRC_photoSize: {
    1434            3 :             if (r->len - r->pos < 12) return -1;
    1435            3 :             int32_t w = tl_read_int32(r);
    1436            3 :             int32_t h = tl_read_int32(r);
    1437            3 :             int32_t sz = tl_read_int32(r); (void)sz;
    1438            3 :             score = (long)w * (long)h;
    1439            3 :             break;
    1440              :         }
    1441            1 :         case CRC_photoCachedSize: {
    1442            1 :             if (r->len - r->pos < 8) return -1;
    1443            1 :             int32_t w = tl_read_int32(r);
    1444            1 :             int32_t h = tl_read_int32(r);
    1445            1 :             if (tl_skip_string(r) != 0) return -1; /* bytes */
    1446            1 :             score = (long)w * (long)h;
    1447            1 :             break;
    1448              :         }
    1449            0 :         case CRC_photoStrippedSize:
    1450              :         case CRC_photoPathSize:
    1451            0 :             if (tl_skip_string(r) != 0) return -1; /* bytes */
    1452            0 :             score = 0;
    1453            0 :             break;
    1454            1 :         case CRC_photoSizeProgressive: {
    1455            1 :             if (r->len - r->pos < 8) return -1;
    1456            1 :             int32_t w = tl_read_int32(r);
    1457            1 :             int32_t h = tl_read_int32(r);
    1458            1 :             if (r->len - r->pos < 8) return -1;
    1459            1 :             uint32_t vc = tl_read_uint32(r);
    1460            1 :             if (vc != TL_vector) return -1;
    1461            1 :             uint32_t nvals = tl_read_uint32(r);
    1462            1 :             if (r->len - r->pos < nvals * 4ULL) return -1;
    1463            3 :             for (uint32_t j = 0; j < nvals; j++) tl_read_int32(r);
    1464            1 :             score = (long)w * (long)h;
    1465            1 :             break;
    1466              :         }
    1467            0 :         default:
    1468            0 :             logger_log(LOG_WARN, "walk_photo_size_vector: unknown 0x%08x", crc);
    1469            0 :             return -1;
    1470              :         }
    1471              : 
    1472            5 :         if (score > best_score && best_type && best_type_cap) {
    1473            2 :             best_score = score;
    1474            2 :             size_t n = type_n < best_type_cap - 1 ? type_n : best_type_cap - 1;
    1475            2 :             memcpy(best_type, type_buf, n);
    1476            2 :             best_type[n] = '\0';
    1477              :         }
    1478              :     }
    1479            3 :     return 0;
    1480              : }
    1481              : 
    1482            7 : static int photo_full(TlReader *r, MediaInfo *out) {
    1483            7 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1484            7 :     uint32_t crc = tl_read_uint32(r);
    1485            7 :     if (crc == CRC_photoEmpty) {
    1486            4 :         if (r->len - r->pos < 8) return -1;
    1487            4 :         int64_t id = tl_read_int64(r);
    1488            4 :         if (out) out->photo_id = id;
    1489            4 :         return 0;
    1490              :     }
    1491            3 :     if (crc != CRC_photo) {
    1492            0 :         logger_log(LOG_WARN, "tl_skip_photo: unknown 0x%08x", crc);
    1493            0 :         return -1;
    1494              :     }
    1495            3 :     if (r->len - r->pos < 4) return -1;
    1496            3 :     uint32_t flags = tl_read_uint32(r);
    1497            3 :     if (r->len - r->pos < 16) return -1;
    1498            3 :     int64_t id = tl_read_int64(r);
    1499            3 :     int64_t access = tl_read_int64(r);
    1500            3 :     if (out) { out->photo_id = id; out->access_hash = access; }
    1501              : 
    1502              :     /* file_reference is a TL bytes field. Capture it into MediaInfo
    1503              :      * (truncating if it exceeds MEDIA_FILE_REF_MAX) while advancing. */
    1504            3 :     size_t fr_len = 0;
    1505            3 :     uint8_t *fr = tl_read_bytes(r, &fr_len);
    1506            3 :     if (!fr && fr_len != 0) return -1;
    1507            3 :     if (out) {
    1508            2 :         size_t n = fr_len;
    1509            2 :         if (n > MEDIA_FILE_REF_MAX) n = MEDIA_FILE_REF_MAX;
    1510            2 :         if (fr) memcpy(out->file_reference, fr, n);
    1511            2 :         out->file_reference_len = n;
    1512              :     }
    1513            3 :     free(fr);
    1514              : 
    1515            3 :     if (r->len - r->pos < 4) return -1;
    1516            3 :     tl_read_int32(r); /* date */
    1517              : 
    1518            3 :     if (walk_photo_size_vector(r,
    1519              :                                 out ? out->thumb_type : NULL,
    1520              :                                 out ? sizeof(out->thumb_type) : 0) != 0)
    1521            0 :         return -1;
    1522              : 
    1523            3 :     if (flags & (1u << 1)) {
    1524            0 :         if (skip_video_size_vector(r) != 0) return -1;
    1525              :     }
    1526            3 :     if (r->len - r->pos < 4) return -1;
    1527            3 :     int32_t dc = tl_read_int32(r);
    1528            3 :     if (out) out->dc_id = dc;
    1529            3 :     return 0;
    1530              : }
    1531              : 
    1532            5 : int tl_skip_photo(TlReader *r) {
    1533            5 :     return photo_full(r, NULL);
    1534              : }
    1535              : 
    1536              : /* ---- Document skipper ----
    1537              :  * documentEmpty#36f8c871 id:long
    1538              :  * document#8fd4c4d8 flags:# id:long access_hash:long file_reference:bytes
    1539              :  *           date:int mime_type:string size:long
    1540              :  *           thumbs:flags.0?Vector<PhotoSize>
    1541              :  *           video_thumbs:flags.1?Vector<VideoSize>
    1542              :  *           dc_id:int attributes:Vector<DocumentAttribute>
    1543              :  */
    1544              : /* DocumentAttribute CRCs we can skip without bailing. */
    1545              : #define CRC_documentAttributeImageSize   0x6c37c15cu
    1546              : #define CRC_documentAttributeAnimated    0x11b58939u
    1547              : #define CRC_documentAttributeHasStickers 0x9801d2f7u
    1548              : #define CRC_documentAttributeFilename    0x15590068u
    1549              : #define CRC_documentAttributeVideo       0x43c57c48u
    1550              : #define CRC_documentAttributeAudio       0x9852f9c6u
    1551              : #define CRC_documentAttributeSticker     0x6319d612u
    1552              : #define CRC_documentAttributeCustomEmoji 0xfd149899u
    1553              : 
    1554              : /* InputStickerSet variants (documentAttributeSticker / CustomEmoji). */
    1555              : #define CRC_inputStickerSetEmpty                    0xffb62b95u
    1556              : #define CRC_inputStickerSetID                       0x9de7a269u
    1557              : #define CRC_inputStickerSetShortName                0x861cc8a0u
    1558              : #define CRC_inputStickerSetAnimatedEmoji            0x028703c8u
    1559              : #define CRC_inputStickerSetDice                     0xe67f520eu
    1560              : #define CRC_inputStickerSetAnimatedEmojiAnimations  0x0cde3739u
    1561              : #define CRC_inputStickerSetPremiumGifts             0xc88b3b02u
    1562              : #define CRC_inputStickerSetEmojiGenericAnimations   0x04c4d4ceu
    1563              : #define CRC_inputStickerSetEmojiDefaultStatuses     0x29d0f5eeu
    1564              : #define CRC_inputStickerSetEmojiDefaultTopicIcons   0x44c1f8e9u
    1565              : #define CRC_inputStickerSetEmojiChannelDefaultStatuses 0x49748553u
    1566              : 
    1567              : /* MaskCoords (documentAttributeSticker.mask_coords). */
    1568              : #define CRC_maskCoords                   0xaed6dbb2u
    1569              : 
    1570              : /* VideoSize variants (document.video_thumbs, flags.1). */
    1571              : #define CRC_videoSize                    0xde33b094u
    1572              : #define CRC_videoSizeEmojiMarkup         0xf85c413cu
    1573              : #define CRC_videoSizeStickerMarkup       0x0da082feu
    1574              : 
    1575              : /* Skip an InputStickerSet variant. Used by documentAttributeSticker /
    1576              :  * CustomEmoji. Handles every declared variant; on unknown ones we bail. */
    1577            1 : static int skip_input_sticker_set(TlReader *r) {
    1578            1 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1579            1 :     uint32_t crc = tl_read_uint32(r);
    1580            1 :     switch (crc) {
    1581            1 :     case CRC_inputStickerSetEmpty:
    1582              :     case CRC_inputStickerSetAnimatedEmoji:
    1583              :     case CRC_inputStickerSetAnimatedEmojiAnimations:
    1584              :     case CRC_inputStickerSetPremiumGifts:
    1585              :     case CRC_inputStickerSetEmojiGenericAnimations:
    1586              :     case CRC_inputStickerSetEmojiDefaultStatuses:
    1587              :     case CRC_inputStickerSetEmojiDefaultTopicIcons:
    1588              :     case CRC_inputStickerSetEmojiChannelDefaultStatuses:
    1589            1 :         return 0;
    1590            0 :     case CRC_inputStickerSetID:
    1591            0 :         if (r->len - r->pos < 16) return -1;
    1592            0 :         tl_read_int64(r);                            /* id */
    1593            0 :         tl_read_int64(r);                            /* access_hash */
    1594            0 :         return 0;
    1595            0 :     case CRC_inputStickerSetShortName:
    1596            0 :         return tl_skip_string(r);
    1597            0 :     case CRC_inputStickerSetDice:
    1598            0 :         return tl_skip_string(r);                    /* emoticon */
    1599            0 :     default:
    1600            0 :         logger_log(LOG_WARN,
    1601              :                    "skip_input_sticker_set: unknown 0x%08x", crc);
    1602            0 :         return -1;
    1603              :     }
    1604              : }
    1605              : 
    1606              : /* Skip a MaskCoords#aed6dbb2: n:int + 3 doubles. */
    1607            0 : static int skip_mask_coords(TlReader *r) {
    1608            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1609            0 :     uint32_t crc = tl_read_uint32(r);
    1610            0 :     if (crc != CRC_maskCoords) {
    1611            0 :         logger_log(LOG_WARN, "skip_mask_coords: unknown 0x%08x", crc);
    1612            0 :         return -1;
    1613              :     }
    1614            0 :     if (r->len - r->pos < 4 + 3 * 8) return -1;
    1615            0 :     tl_read_int32(r);                                /* n */
    1616            0 :     tl_read_double(r); tl_read_double(r); tl_read_double(r); /* x, y, zoom */
    1617            0 :     return 0;
    1618              : }
    1619              : 
    1620              : /* Skip a single VideoSize variant (document.video_thumbs element). */
    1621            0 : static int skip_video_size(TlReader *r) {
    1622            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1623            0 :     uint32_t crc = tl_read_uint32(r);
    1624            0 :     switch (crc) {
    1625            0 :     case CRC_videoSize: {
    1626              :         /* flags:# type:string w:int h:int size:int video_start_ts:flags.0?double */
    1627            0 :         if (r->len - r->pos < 4) return -1;
    1628            0 :         uint32_t flags = tl_read_uint32(r);
    1629            0 :         if (tl_skip_string(r) != 0) return -1;
    1630            0 :         if (r->len - r->pos < 12) return -1;
    1631            0 :         tl_read_int32(r); tl_read_int32(r); tl_read_int32(r);
    1632            0 :         if (flags & (1u << 0)) {
    1633            0 :             if (r->len - r->pos < 8) return -1;
    1634            0 :             tl_read_double(r);
    1635              :         }
    1636            0 :         return 0;
    1637              :     }
    1638            0 :     case CRC_videoSizeEmojiMarkup: {
    1639            0 :         if (r->len - r->pos < 8) return -1;
    1640            0 :         tl_read_int64(r);                            /* emoji_id */
    1641              :         /* background_colors:Vector<int> */
    1642            0 :         if (r->len - r->pos < 8) return -1;
    1643            0 :         uint32_t vec = tl_read_uint32(r);
    1644            0 :         if (vec != TL_vector) return -1;
    1645            0 :         uint32_t n = tl_read_uint32(r);
    1646            0 :         if (r->len - r->pos < (size_t)n * 4) return -1;
    1647            0 :         for (uint32_t i = 0; i < n; i++) tl_read_int32(r);
    1648            0 :         return 0;
    1649              :     }
    1650            0 :     case CRC_videoSizeStickerMarkup: {
    1651            0 :         if (skip_input_sticker_set(r) != 0) return -1;
    1652            0 :         if (r->len - r->pos < 8) return -1;
    1653            0 :         tl_read_int64(r);                            /* sticker_id */
    1654            0 :         if (r->len - r->pos < 8) return -1;
    1655            0 :         uint32_t vec = tl_read_uint32(r);
    1656            0 :         if (vec != TL_vector) return -1;
    1657            0 :         uint32_t n = tl_read_uint32(r);
    1658            0 :         if (r->len - r->pos < (size_t)n * 4) return -1;
    1659            0 :         for (uint32_t i = 0; i < n; i++) tl_read_int32(r);
    1660            0 :         return 0;
    1661              :     }
    1662            0 :     default:
    1663            0 :         logger_log(LOG_WARN, "skip_video_size: unknown 0x%08x", crc);
    1664            0 :         return -1;
    1665              :     }
    1666              : }
    1667              : 
    1668              : /* Skip a Vector<VideoSize>. */
    1669            0 : static int skip_video_size_vector(TlReader *r) {
    1670            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
    1671            0 :     uint32_t vec = tl_read_uint32(r);
    1672            0 :     if (vec != TL_vector) return -1;
    1673            0 :     uint32_t n = tl_read_uint32(r);
    1674            0 :     for (uint32_t i = 0; i < n; i++) {
    1675            0 :         if (skip_video_size(r) != 0) return -1;
    1676              :     }
    1677            0 :     return 0;
    1678              : }
    1679              : 
    1680              : /* Skip a single DocumentAttribute, optionally extracting the filename. */
    1681           14 : static int skip_document_attribute(TlReader *r,
    1682              :                                     char *filename_out, size_t fn_cap) {
    1683           14 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1684           14 :     uint32_t crc = tl_read_uint32(r);
    1685           14 :     switch (crc) {
    1686            1 :     case CRC_documentAttributeImageSize:
    1687            1 :         if (r->len - r->pos < 8) return -1;
    1688            1 :         tl_read_int32(r); tl_read_int32(r);              /* w, h */
    1689            1 :         return 0;
    1690            4 :     case CRC_documentAttributeAnimated:
    1691              :     case CRC_documentAttributeHasStickers:
    1692            4 :         return 0;
    1693            3 :     case CRC_documentAttributeFilename: {
    1694            3 :         size_t before = r->pos;
    1695            3 :         char *s = tl_read_string(r);
    1696            3 :         if (!s) { r->pos = before; return -1; }
    1697            3 :         if (filename_out && fn_cap) {
    1698            2 :             size_t n = strlen(s);
    1699            2 :             if (n >= fn_cap) n = fn_cap - 1;
    1700            2 :             memcpy(filename_out, s, n);
    1701            2 :             filename_out[n] = '\0';
    1702              :         }
    1703            3 :         free(s);
    1704            3 :         return 0;
    1705              :     }
    1706            2 :     case CRC_documentAttributeVideo: {
    1707            2 :         if (r->len - r->pos < 4) return -1;
    1708            2 :         uint32_t flags = tl_read_uint32(r);
    1709            2 :         if (r->len - r->pos < 8 + 4 + 4) return -1;
    1710            2 :         tl_read_double(r);                               /* duration */
    1711            2 :         tl_read_int32(r); tl_read_int32(r);              /* w, h */
    1712            2 :         if (flags & (1u << 2)) {
    1713            0 :             if (r->len - r->pos < 4) return -1;
    1714            0 :             tl_read_int32(r);                            /* preload_prefix_size */
    1715              :         }
    1716            2 :         if (flags & (1u << 4)) {
    1717            0 :             if (r->len - r->pos < 8) return -1;
    1718            0 :             tl_read_double(r);                           /* video_start_ts */
    1719              :         }
    1720            2 :         if (flags & (1u << 5))
    1721            0 :             if (tl_skip_string(r) != 0) return -1;       /* video_codec */
    1722            2 :         return 0;
    1723              :     }
    1724            3 :     case CRC_documentAttributeAudio: {
    1725            3 :         if (r->len - r->pos < 8) return -1;
    1726            3 :         uint32_t flags = tl_read_uint32(r);
    1727            3 :         tl_read_int32(r);                                /* duration */
    1728            3 :         if (flags & (1u << 0))
    1729            0 :             if (tl_skip_string(r) != 0) return -1;       /* title */
    1730            3 :         if (flags & (1u << 1))
    1731            0 :             if (tl_skip_string(r) != 0) return -1;       /* performer */
    1732            3 :         if (flags & (1u << 2))
    1733            0 :             if (tl_skip_string(r) != 0) return -1;       /* waveform:bytes */
    1734            3 :         return 0;
    1735              :     }
    1736            1 :     case CRC_documentAttributeSticker: {
    1737              :         /* flags:# mask:flags.1?true alt:string stickerset:InputStickerSet
    1738              :          *        mask_coords:flags.0?MaskCoords */
    1739            1 :         if (r->len - r->pos < 4) return -1;
    1740            1 :         uint32_t flags = tl_read_uint32(r);
    1741            1 :         if (tl_skip_string(r) != 0) return -1;           /* alt */
    1742            1 :         if (skip_input_sticker_set(r) != 0) return -1;
    1743            1 :         if (flags & (1u << 0)) {
    1744            0 :             if (skip_mask_coords(r) != 0) return -1;
    1745              :         }
    1746            1 :         return 0;
    1747              :     }
    1748            0 :     case CRC_documentAttributeCustomEmoji: {
    1749              :         /* flags:# free:flags.0?true text_color:flags.1?true alt:string
    1750              :          *        stickerset:InputStickerSet */
    1751            0 :         if (r->len - r->pos < 4) return -1;
    1752            0 :         (void)tl_read_uint32(r);                         /* flags */
    1753            0 :         if (tl_skip_string(r) != 0) return -1;           /* alt */
    1754            0 :         return skip_input_sticker_set(r);
    1755              :     }
    1756            0 :     default:
    1757            0 :         logger_log(LOG_WARN, "skip_document_attribute: unknown 0x%08x", crc);
    1758            0 :         return -1;
    1759              :     }
    1760              : }
    1761              : 
    1762              : /* Walk a Document. When @p out is non-NULL, fills id + access_hash +
    1763              :  * file_reference + dc_id + size + mime_type + filename. */
    1764           11 : static int document_inner(TlReader *r, MediaInfo *out) {
    1765           11 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1766           11 :     uint32_t crc = tl_read_uint32(r);
    1767           11 :     if (crc == CRC_documentEmpty) {
    1768            2 :         if (r->len - r->pos < 8) return -1;
    1769            2 :         int64_t id = tl_read_int64(r);
    1770            2 :         if (out) out->document_id = id;
    1771            2 :         return 0;
    1772              :     }
    1773            9 :     if (crc != CRC_document) {
    1774            0 :         logger_log(LOG_WARN, "tl_skip_document: unknown 0x%08x", crc);
    1775            0 :         return -1;
    1776              :     }
    1777            9 :     if (r->len - r->pos < 4) return -1;
    1778            9 :     uint32_t flags = tl_read_uint32(r);
    1779            9 :     if (r->len - r->pos < 16) return -1;
    1780            9 :     int64_t id     = tl_read_int64(r);
    1781            9 :     int64_t access = tl_read_int64(r);
    1782            9 :     if (out) { out->document_id = id; out->access_hash = access; }
    1783              : 
    1784            9 :     size_t fr_len = 0;
    1785            9 :     uint8_t *fr = tl_read_bytes(r, &fr_len);
    1786            9 :     if (!fr && fr_len != 0) return -1;
    1787            9 :     if (out) {
    1788            8 :         size_t n = fr_len;
    1789            8 :         if (n > MEDIA_FILE_REF_MAX) n = MEDIA_FILE_REF_MAX;
    1790            8 :         if (fr) memcpy(out->file_reference, fr, n);
    1791            8 :         out->file_reference_len = n;
    1792              :     }
    1793            9 :     free(fr);
    1794              : 
    1795            9 :     if (r->len - r->pos < 4) return -1;
    1796            9 :     tl_read_int32(r);                                    /* date */
    1797              : 
    1798            9 :     size_t mime_before = r->pos;
    1799            9 :     char *mime = tl_read_string(r);
    1800            9 :     if (!mime) { r->pos = mime_before; return -1; }
    1801            9 :     if (out) {
    1802            8 :         size_t n = strlen(mime);
    1803            8 :         if (n >= sizeof(out->document_mime)) n = sizeof(out->document_mime) - 1;
    1804            8 :         memcpy(out->document_mime, mime, n);
    1805            8 :         out->document_mime[n] = '\0';
    1806              :     }
    1807            9 :     free(mime);
    1808              : 
    1809            9 :     if (r->len - r->pos < 8) return -1;
    1810            9 :     int64_t size = tl_read_int64(r);
    1811            9 :     if (out) out->document_size = size;
    1812              : 
    1813            9 :     if (flags & (1u << 0)) {
    1814            0 :         if (tl_skip_photo_size_vector(r) != 0) return -1;
    1815              :     }
    1816            9 :     if (flags & (1u << 1)) {
    1817            0 :         if (skip_video_size_vector(r) != 0) return -1;
    1818              :     }
    1819              : 
    1820            9 :     if (r->len - r->pos < 4) return -1;
    1821            9 :     int32_t dc = tl_read_int32(r);
    1822            9 :     if (out) out->dc_id = dc;
    1823              : 
    1824              :     /* attributes:Vector<DocumentAttribute> */
    1825            9 :     if (r->len - r->pos < 8) return -1;
    1826            9 :     uint32_t vec_crc = tl_read_uint32(r);
    1827            9 :     if (vec_crc != TL_vector) return -1;
    1828            9 :     uint32_t n_attrs = tl_read_uint32(r);
    1829           22 :     for (uint32_t i = 0; i < n_attrs; i++) {
    1830           13 :         if (skip_document_attribute(r,
    1831              :                                      out ? out->document_filename : NULL,
    1832              :                                      out ? sizeof(out->document_filename) : 0) != 0)
    1833            0 :             return -1;
    1834              :     }
    1835            9 :     return 0;
    1836              : }
    1837              : 
    1838            3 : int tl_skip_document(TlReader *r) {
    1839            3 :     return document_inner(r, NULL);
    1840              : }
    1841              : 
    1842              : /* Skip GeoPoint: empty → CRC-only; geoPoint → CRC + long lat + long long +
    1843              :  * long access_hash + flags:# + optional accuracy_radius:flags.0?int.
    1844              :  * Actually:
    1845              :  *   geoPointEmpty#1117dd5f
    1846              :  *   geoPoint#b2a2f663 flags:# long:double lat:double access_hash:long
    1847              :  *                    accuracy_radius:flags.0?int
    1848              :  */
    1849            7 : static int skip_geo_point(TlReader *r) {
    1850            7 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1851            7 :     uint32_t crc = tl_read_uint32(r);
    1852            7 :     if (crc == CRC_geoPointEmpty) return 0;
    1853            2 :     if (crc != CRC_geoPoint) {
    1854            0 :         logger_log(LOG_WARN, "skip_geo_point: unknown 0x%08x", crc);
    1855            0 :         return -1;
    1856              :     }
    1857            2 :     if (r->len - r->pos < 28) return -1;
    1858            2 :     uint32_t flags = tl_read_uint32(r);
    1859            2 :     tl_read_double(r); tl_read_double(r); tl_read_int64(r);
    1860            2 :     if (flags & 1u) {
    1861            0 :         if (r->len - r->pos < 4) return -1;
    1862            0 :         tl_read_int32(r);
    1863              :     }
    1864            2 :     return 0;
    1865              : }
    1866              : 
    1867              : /* Forward decl: skip_story_item is defined later in the file (next to
    1868              :  * the MessageMedia switch) and is re-used by webPageAttributeStory. */
    1869              : static int skip_story_item(TlReader *r);
    1870              : 
    1871              : /* Forward decls: PageBlock recursion (Cover → PageBlock, Details →
    1872              :  * Vector<PageBlock>, EmbedPost → Vector<PageBlock>, List item blocks,
    1873              :  * etc.) and reliance on skip_geo_point for pageBlockMap. */
    1874              : static int skip_page_block(TlReader *r);
    1875              : static int skip_geo_point(TlReader *r);
    1876              : 
    1877              : /* RichText tree (used by cached_page's PageBlock variants). Recursive:
    1878              :  * wrap-type variants contain a nested RichText. textConcat is a vector. */
    1879           52 : static int skip_rich_text(TlReader *r) {
    1880           52 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1881           52 :     uint32_t crc = tl_read_uint32(r);
    1882           52 :     switch (crc) {
    1883           36 :     case CRC_textEmpty:
    1884           36 :         return 0;
    1885            3 :     case CRC_textPlain:
    1886            3 :         return tl_skip_string(r);
    1887            6 :     case CRC_textBold:
    1888              :     case CRC_textItalic:
    1889              :     case CRC_textUnderline:
    1890              :     case CRC_textStrike:
    1891              :     case CRC_textFixed:
    1892              :     case CRC_textSubscript:
    1893              :     case CRC_textSuperscript:
    1894              :     case CRC_textMarked:
    1895            6 :         return skip_rich_text(r);
    1896            1 :     case CRC_textUrl: {
    1897            1 :         if (skip_rich_text(r) != 0) return -1;
    1898            1 :         if (tl_skip_string(r) != 0) return -1;
    1899            1 :         if (r->len - r->pos < 8) return -1;
    1900            1 :         tl_read_int64(r);                            /* webpage_id */
    1901            1 :         return 0;
    1902              :     }
    1903            2 :     case CRC_textEmail:
    1904              :     case CRC_textPhone: {
    1905            2 :         if (skip_rich_text(r) != 0) return -1;
    1906            2 :         return tl_skip_string(r);
    1907              :     }
    1908            2 :     case CRC_textConcat: {
    1909            2 :         if (r->len - r->pos < 8) return -1;
    1910            2 :         uint32_t vec = tl_read_uint32(r);
    1911            2 :         if (vec != TL_vector) return -1;
    1912            2 :         uint32_t n = tl_read_uint32(r);
    1913           12 :         for (uint32_t i = 0; i < n; i++) {
    1914           10 :             if (skip_rich_text(r) != 0) return -1;
    1915              :         }
    1916            2 :         return 0;
    1917              :     }
    1918            1 :     case CRC_textImage: {
    1919            1 :         if (r->len - r->pos < 16) return -1;
    1920            1 :         tl_read_int64(r);                            /* document_id */
    1921            1 :         tl_read_int32(r);                            /* w */
    1922            1 :         tl_read_int32(r);                            /* h */
    1923            1 :         return 0;
    1924              :     }
    1925            1 :     case CRC_textAnchor: {
    1926            1 :         if (skip_rich_text(r) != 0) return -1;
    1927            1 :         return tl_skip_string(r);
    1928              :     }
    1929            0 :     default:
    1930            0 :         logger_log(LOG_WARN, "skip_rich_text: unknown 0x%08x", crc);
    1931            0 :         return -1;
    1932              :     }
    1933              : }
    1934              : 
    1935              : /* Skip a pageCaption#6f747657 text:RichText credit:RichText. */
    1936            8 : static int skip_page_caption(TlReader *r) {
    1937            8 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1938            8 :     uint32_t crc = tl_read_uint32(r);
    1939            8 :     if (crc != CRC_pageCaption) {
    1940            0 :         logger_log(LOG_WARN, "skip_page_caption: unknown 0x%08x", crc);
    1941            0 :         return -1;
    1942              :     }
    1943            8 :     if (skip_rich_text(r) != 0) return -1;
    1944            8 :     return skip_rich_text(r);
    1945              : }
    1946              : 
    1947              : /* Skip a Vector<PageBlock> — recursive into skip_page_block. */
    1948            6 : static int skip_page_block_vector(TlReader *r) {
    1949            6 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
    1950            6 :     uint32_t vec = tl_read_uint32(r);
    1951            6 :     if (vec != TL_vector) return -1;
    1952            6 :     uint32_t n = tl_read_uint32(r);
    1953            9 :     for (uint32_t i = 0; i < n; i++) {
    1954            3 :         if (skip_page_block(r) != 0) return -1;
    1955              :     }
    1956            6 :     return 0;
    1957              : }
    1958              : 
    1959              : /* Skip a single PageListItem. */
    1960            2 : static int skip_page_list_item(TlReader *r) {
    1961            2 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1962            2 :     uint32_t crc = tl_read_uint32(r);
    1963            2 :     switch (crc) {
    1964            1 :     case CRC_pageListItemText:
    1965            1 :         return skip_rich_text(r);
    1966            1 :     case CRC_pageListItemBlocks:
    1967            1 :         return skip_page_block_vector(r);
    1968            0 :     default:
    1969            0 :         logger_log(LOG_WARN, "skip_page_list_item: unknown 0x%08x", crc);
    1970            0 :         return -1;
    1971              :     }
    1972              : }
    1973              : 
    1974            2 : static int skip_page_list_ordered_item(TlReader *r) {
    1975            2 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1976            2 :     uint32_t crc = tl_read_uint32(r);
    1977            2 :     switch (crc) {
    1978            1 :     case CRC_pageListOrderedItemText:
    1979            1 :         if (tl_skip_string(r) != 0) return -1;       /* num */
    1980            1 :         return skip_rich_text(r);
    1981            1 :     case CRC_pageListOrderedItemBlocks:
    1982            1 :         if (tl_skip_string(r) != 0) return -1;       /* num */
    1983            1 :         return skip_page_block_vector(r);
    1984            0 :     default:
    1985            0 :         logger_log(LOG_WARN,
    1986              :                    "skip_page_list_ordered_item: unknown 0x%08x", crc);
    1987            0 :         return -1;
    1988              :     }
    1989              : }
    1990              : 
    1991              : /* pageTableCell#34566b6a flags:# header/align/valign:flags
    1992              :  *   text:flags.7?RichText colspan:flags.1?int rowspan:flags.2?int */
    1993            1 : static int skip_page_table_cell(TlReader *r) {
    1994            1 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    1995            1 :     uint32_t crc = tl_read_uint32(r);
    1996            1 :     if (crc != CRC_pageTableCell) {
    1997            0 :         logger_log(LOG_WARN, "skip_page_table_cell: unknown 0x%08x", crc);
    1998            0 :         return -1;
    1999              :     }
    2000            1 :     if (r->len - r->pos < 4) return -1;
    2001            1 :     uint32_t flags = tl_read_uint32(r);
    2002            1 :     if (flags & (1u << 7)) {
    2003            1 :         if (skip_rich_text(r) != 0) return -1;
    2004              :     }
    2005            1 :     if (flags & (1u << 1)) {
    2006            1 :         if (r->len - r->pos < 4) return -1;
    2007            1 :         tl_read_int32(r);                            /* colspan */
    2008              :     }
    2009            1 :     if (flags & (1u << 2)) {
    2010            1 :         if (r->len - r->pos < 4) return -1;
    2011            1 :         tl_read_int32(r);                            /* rowspan */
    2012              :     }
    2013            1 :     return 0;
    2014              : }
    2015              : 
    2016              : /* pageTableRow#e0c0c5e5 cells:Vector<PageTableCell>. */
    2017            1 : static int skip_page_table_row(TlReader *r) {
    2018            1 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2019            1 :     uint32_t crc = tl_read_uint32(r);
    2020            1 :     if (crc != CRC_pageTableRow) {
    2021            0 :         logger_log(LOG_WARN, "skip_page_table_row: unknown 0x%08x", crc);
    2022            0 :         return -1;
    2023              :     }
    2024            1 :     if (r->len - r->pos < 8) return -1;
    2025            1 :     uint32_t vec = tl_read_uint32(r);
    2026            1 :     if (vec != TL_vector) return -1;
    2027            1 :     uint32_t n = tl_read_uint32(r);
    2028            2 :     for (uint32_t i = 0; i < n; i++) {
    2029            1 :         if (skip_page_table_cell(r) != 0) return -1;
    2030              :     }
    2031            1 :     return 0;
    2032              : }
    2033              : 
    2034              : /* pageRelatedArticle#b390dc08 flags:# url:string webpage_id:long
    2035              :  *   title:flags.0?string description:flags.1?string
    2036              :  *   photo_id:flags.2?long author:flags.3?string
    2037              :  *   published_date:flags.4?int */
    2038            1 : static int skip_page_related_article(TlReader *r) {
    2039            1 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2040            1 :     uint32_t crc = tl_read_uint32(r);
    2041            1 :     if (crc != CRC_pageRelatedArticle) {
    2042            0 :         logger_log(LOG_WARN,
    2043              :                    "skip_page_related_article: unknown 0x%08x", crc);
    2044            0 :         return -1;
    2045              :     }
    2046            1 :     if (r->len - r->pos < 4) return -1;
    2047            1 :     uint32_t flags = tl_read_uint32(r);
    2048            1 :     if (tl_skip_string(r) != 0) return -1;           /* url */
    2049            1 :     if (r->len - r->pos < 8) return -1;
    2050            1 :     tl_read_int64(r);                                /* webpage_id */
    2051            1 :     if (flags & (1u << 0)) if (tl_skip_string(r) != 0) return -1;
    2052            1 :     if (flags & (1u << 1)) if (tl_skip_string(r) != 0) return -1;
    2053            1 :     if (flags & (1u << 2)) {
    2054            1 :         if (r->len - r->pos < 8) return -1;
    2055            1 :         tl_read_int64(r);                            /* photo_id */
    2056              :     }
    2057            1 :     if (flags & (1u << 3)) if (tl_skip_string(r) != 0) return -1;
    2058            1 :     if (flags & (1u << 4)) {
    2059            1 :         if (r->len - r->pos < 4) return -1;
    2060            1 :         tl_read_int32(r);                            /* published_date */
    2061              :     }
    2062            1 :     return 0;
    2063              : }
    2064              : 
    2065              : /* Skip a single PageBlock — full coverage of the PageBlock tree. */
    2066           32 : static int skip_page_block(TlReader *r) {
    2067           32 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2068           32 :     uint32_t crc = tl_read_uint32(r);
    2069           32 :     switch (crc) {
    2070            6 :     case CRC_pageBlockUnsupported:
    2071              :     case CRC_pageBlockDivider:
    2072            6 :         return 0;
    2073            4 :     case CRC_pageBlockTitle:
    2074              :     case CRC_pageBlockSubtitle:
    2075              :     case CRC_pageBlockHeader:
    2076              :     case CRC_pageBlockSubheader:
    2077              :     case CRC_pageBlockKicker:
    2078              :     case CRC_pageBlockParagraph:
    2079              :     case CRC_pageBlockFooter:
    2080            4 :         return skip_rich_text(r);
    2081            1 :     case CRC_pageBlockAnchor:
    2082            1 :         return tl_skip_string(r);
    2083            1 :     case CRC_pageBlockPreformatted: {
    2084            1 :         if (skip_rich_text(r) != 0) return -1;
    2085            1 :         return tl_skip_string(r);                    /* language */
    2086              :     }
    2087            1 :     case CRC_pageBlockAuthorDate: {
    2088            1 :         if (skip_rich_text(r) != 0) return -1;       /* author */
    2089            1 :         if (r->len - r->pos < 4) return -1;
    2090            1 :         tl_read_int32(r);                            /* published_date */
    2091            1 :         return 0;
    2092              :     }
    2093            2 :     case CRC_pageBlockBlockquote:
    2094              :     case CRC_pageBlockPullquote: {
    2095            2 :         if (skip_rich_text(r) != 0) return -1;       /* text */
    2096            2 :         return skip_rich_text(r);                    /* caption */
    2097              :     }
    2098            1 :     case CRC_pageBlockPhoto: {
    2099            1 :         if (r->len - r->pos < 4) return -1;
    2100            1 :         uint32_t flags = tl_read_uint32(r);
    2101            1 :         if (r->len - r->pos < 8) return -1;
    2102            1 :         tl_read_int64(r);                            /* photo_id */
    2103            1 :         if (skip_page_caption(r) != 0) return -1;
    2104            1 :         if (flags & (1u << 0)) {
    2105            0 :             if (tl_skip_string(r) != 0) return -1;   /* url */
    2106            0 :             if (r->len - r->pos < 8) return -1;
    2107            0 :             tl_read_int64(r);                        /* webpage_id */
    2108              :         }
    2109            1 :         return 0;
    2110              :     }
    2111            1 :     case CRC_pageBlockVideo: {
    2112            1 :         if (r->len - r->pos < 12) return -1;
    2113            1 :         tl_read_uint32(r);                           /* flags (autoplay/loop only) */
    2114            1 :         tl_read_int64(r);                            /* video_id */
    2115            1 :         return skip_page_caption(r);
    2116              :     }
    2117            1 :     case CRC_pageBlockAudio: {
    2118            1 :         if (r->len - r->pos < 8) return -1;
    2119            1 :         tl_read_int64(r);                            /* audio_id */
    2120            1 :         return skip_page_caption(r);
    2121              :     }
    2122            1 :     case CRC_pageBlockCover:
    2123            1 :         return skip_page_block(r);
    2124            1 :     case CRC_pageBlockChannel:
    2125            1 :         return tl_skip_chat(r);
    2126            1 :     case CRC_pageBlockMap: {
    2127            1 :         if (skip_geo_point(r) != 0) return -1;
    2128            1 :         if (r->len - r->pos < 12) return -1;
    2129            1 :         tl_read_int32(r);                            /* zoom */
    2130            1 :         tl_read_int32(r); tl_read_int32(r);          /* w, h */
    2131            1 :         return skip_page_caption(r);
    2132              :     }
    2133            2 :     case CRC_pageBlockList: {
    2134            2 :         if (r->len - r->pos < 8) return -1;
    2135            2 :         uint32_t vec = tl_read_uint32(r);
    2136            2 :         if (vec != TL_vector) return -1;
    2137            2 :         uint32_t n = tl_read_uint32(r);
    2138            4 :         for (uint32_t i = 0; i < n; i++) {
    2139            2 :             if (skip_page_list_item(r) != 0) return -1;
    2140              :         }
    2141            2 :         return 0;
    2142              :     }
    2143            2 :     case CRC_pageBlockOrderedList: {
    2144            2 :         if (r->len - r->pos < 8) return -1;
    2145            2 :         uint32_t vec = tl_read_uint32(r);
    2146            2 :         if (vec != TL_vector) return -1;
    2147            2 :         uint32_t n = tl_read_uint32(r);
    2148            4 :         for (uint32_t i = 0; i < n; i++) {
    2149            2 :             if (skip_page_list_ordered_item(r) != 0) return -1;
    2150              :         }
    2151            2 :         return 0;
    2152              :     }
    2153            2 :     case CRC_pageBlockCollage:
    2154              :     case CRC_pageBlockSlideshow: {
    2155            2 :         if (skip_page_block_vector(r) != 0) return -1;
    2156            2 :         return skip_page_caption(r);
    2157              :     }
    2158            1 :     case CRC_pageBlockDetails: {
    2159            1 :         if (r->len - r->pos < 4) return -1;
    2160            1 :         tl_read_uint32(r);                           /* flags (open only) */
    2161            1 :         if (skip_page_block_vector(r) != 0) return -1;
    2162            1 :         return skip_rich_text(r);                    /* title */
    2163              :     }
    2164            1 :     case CRC_pageBlockRelatedArticles: {
    2165            1 :         if (skip_rich_text(r) != 0) return -1;       /* title */
    2166            1 :         if (r->len - r->pos < 8) return -1;
    2167            1 :         uint32_t vec = tl_read_uint32(r);
    2168            1 :         if (vec != TL_vector) return -1;
    2169            1 :         uint32_t n = tl_read_uint32(r);
    2170            2 :         for (uint32_t i = 0; i < n; i++) {
    2171            1 :             if (skip_page_related_article(r) != 0) return -1;
    2172              :         }
    2173            1 :         return 0;
    2174              :     }
    2175            1 :     case CRC_pageBlockTable: {
    2176            1 :         if (r->len - r->pos < 4) return -1;
    2177            1 :         tl_read_uint32(r);                           /* flags (bordered/striped) */
    2178            1 :         if (skip_rich_text(r) != 0) return -1;       /* title */
    2179            1 :         if (r->len - r->pos < 8) return -1;
    2180            1 :         uint32_t vec = tl_read_uint32(r);
    2181            1 :         if (vec != TL_vector) return -1;
    2182            1 :         uint32_t n = tl_read_uint32(r);
    2183            2 :         for (uint32_t i = 0; i < n; i++) {
    2184            1 :             if (skip_page_table_row(r) != 0) return -1;
    2185              :         }
    2186            1 :         return 0;
    2187              :     }
    2188            1 :     case CRC_pageBlockEmbed: {
    2189            1 :         if (r->len - r->pos < 4) return -1;
    2190            1 :         uint32_t flags = tl_read_uint32(r);
    2191            1 :         if (flags & (1u << 1)) if (tl_skip_string(r) != 0) return -1;
    2192            1 :         if (flags & (1u << 2)) if (tl_skip_string(r) != 0) return -1;
    2193            1 :         if (flags & (1u << 4)) {
    2194            0 :             if (r->len - r->pos < 8) return -1;
    2195            0 :             tl_read_int64(r);                        /* poster_photo_id */
    2196              :         }
    2197            1 :         if (flags & (1u << 5)) {
    2198            0 :             if (r->len - r->pos < 8) return -1;
    2199            0 :             tl_read_int32(r); tl_read_int32(r);      /* w, h */
    2200              :         }
    2201            1 :         return skip_page_caption(r);
    2202              :     }
    2203            1 :     case CRC_pageBlockEmbedPost: {
    2204            1 :         if (tl_skip_string(r) != 0) return -1;       /* url */
    2205            1 :         if (r->len - r->pos < 16) return -1;
    2206            1 :         tl_read_int64(r);                            /* webpage_id */
    2207            1 :         tl_read_int64(r);                            /* author_photo_id */
    2208            1 :         if (tl_skip_string(r) != 0) return -1;       /* author */
    2209            1 :         if (r->len - r->pos < 4) return -1;
    2210            1 :         tl_read_int32(r);                            /* date */
    2211            1 :         if (skip_page_block_vector(r) != 0) return -1;
    2212            1 :         return skip_page_caption(r);
    2213              :     }
    2214            0 :     default:
    2215            0 :         logger_log(LOG_WARN, "skip_page_block: unsupported 0x%08x", crc);
    2216            0 :         return -1;
    2217              :     }
    2218              : }
    2219              : 
    2220              : /* Skip a Page object (webPage.cached_page). Best-effort: simple
    2221              :  * Instant View article bodies iterate; anything that contains a
    2222              :  * complex PageBlock variant bails so the caller stops walking. */
    2223            5 : static int skip_page(TlReader *r) {
    2224            5 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2225            5 :     uint32_t crc = tl_read_uint32(r);
    2226            5 :     if (crc != CRC_page) {
    2227            0 :         logger_log(LOG_WARN, "skip_page: unknown Page variant 0x%08x", crc);
    2228            0 :         return -1;
    2229              :     }
    2230            5 :     if (r->len - r->pos < 4) return -1;
    2231            5 :     uint32_t flags = tl_read_uint32(r);
    2232            5 :     if (tl_skip_string(r) != 0) return -1;           /* url */
    2233              : 
    2234              :     /* blocks:Vector<PageBlock> */
    2235            5 :     if (r->len - r->pos < 8) return -1;
    2236            5 :     uint32_t vec = tl_read_uint32(r);
    2237            5 :     if (vec != TL_vector) return -1;
    2238            5 :     uint32_t n = tl_read_uint32(r);
    2239           33 :     for (uint32_t i = 0; i < n; i++) {
    2240           28 :         if (skip_page_block(r) != 0) return -1;
    2241              :     }
    2242              : 
    2243              :     /* photos:Vector<Photo> */
    2244            5 :     if (r->len - r->pos < 8) return -1;
    2245            5 :     vec = tl_read_uint32(r);
    2246            5 :     if (vec != TL_vector) return -1;
    2247            5 :     n = tl_read_uint32(r);
    2248            5 :     for (uint32_t i = 0; i < n; i++) {
    2249            0 :         if (tl_skip_photo(r) != 0) return -1;
    2250              :     }
    2251              : 
    2252              :     /* documents:Vector<Document> */
    2253            5 :     if (r->len - r->pos < 8) return -1;
    2254            5 :     vec = tl_read_uint32(r);
    2255            5 :     if (vec != TL_vector) return -1;
    2256            5 :     n = tl_read_uint32(r);
    2257            5 :     for (uint32_t i = 0; i < n; i++) {
    2258            0 :         if (tl_skip_document(r) != 0) return -1;
    2259              :     }
    2260              : 
    2261              :     /* views:flags.3?int */
    2262            5 :     if (flags & (1u << 3)) {
    2263            0 :         if (r->len - r->pos < 4) return -1;
    2264            0 :         tl_read_int32(r);
    2265              :     }
    2266            5 :     return 0;
    2267              : }
    2268              : 
    2269              : /* wallPaperSettings#372efcd0 flags:# blur:flags.1?true motion:flags.2?true
    2270              :  *   background_color:flags.0?int second_background_color:flags.4?int
    2271              :  *   third_background_color:flags.5?int fourth_background_color:flags.6?int
    2272              :  *   intensity:flags.3?int rotation:flags.7?int emoticon:flags.8?string */
    2273            0 : static int skip_wallpaper_settings(TlReader *r) {
    2274            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2275            0 :     uint32_t crc = tl_read_uint32(r);
    2276            0 :     if (crc != CRC_wallPaperSettings) {
    2277            0 :         logger_log(LOG_WARN, "skip_wallpaper_settings: unknown 0x%08x", crc);
    2278            0 :         return -1;
    2279              :     }
    2280            0 :     if (r->len - r->pos < 4) return -1;
    2281            0 :     uint32_t flags = tl_read_uint32(r);
    2282            0 :     if (flags & (1u << 0)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    2283            0 :     if (flags & (1u << 4)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    2284            0 :     if (flags & (1u << 5)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    2285            0 :     if (flags & (1u << 6)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    2286            0 :     if (flags & (1u << 3)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    2287            0 :     if (flags & (1u << 7)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    2288            0 :     if (flags & (1u << 8)) { if (tl_skip_string(r) != 0) return -1; }
    2289            0 :     return 0;
    2290              : }
    2291              : 
    2292              : /* wallPaper#a437c3ed id:long access_hash:long file_reference:bytes slug:string
    2293              :  *   type:WallPaperSettings document:flags.0?Document
    2294              :  * wallPaperNoFile#e0804116 id:long flags:# default:flags.2?true dark:flags.4?true
    2295              :  *   settings:flags.2?WallPaperSettings (note: flag bit 2 on flags, not a separate field) */
    2296            0 : static int skip_wallpaper(TlReader *r) {
    2297            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2298            0 :     uint32_t crc = tl_read_uint32(r);
    2299            0 :     if (crc == CRC_wallPaper) {
    2300            0 :         if (r->len - r->pos < 16) return -1;
    2301            0 :         tl_read_int64(r);                           /* id */
    2302            0 :         tl_read_int64(r);                           /* access_hash */
    2303            0 :         if (tl_skip_string(r) != 0) return -1;     /* file_reference:bytes */
    2304            0 :         if (tl_skip_string(r) != 0) return -1;     /* slug */
    2305            0 :         if (skip_wallpaper_settings(r) != 0) return -1; /* type */
    2306            0 :         if (r->len - r->pos < 4) return -1;
    2307            0 :         uint32_t doc_flags = tl_read_uint32(r);
    2308              :         /* doc_flags is actually a separate flags field for wallPaper:
    2309              :          * creator:flags.0?true default:flags.1?true pattern:flags.3?true dark:flags.4?true
    2310              :          * document:flags.2?Document */
    2311            0 :         if (doc_flags & (1u << 2)) {
    2312            0 :             if (tl_skip_document(r) != 0) return -1;
    2313              :         }
    2314            0 :         return 0;
    2315              :     }
    2316            0 :     if (crc == CRC_wallPaperNoFile) {
    2317            0 :         if (r->len - r->pos < 12) return -1;
    2318            0 :         tl_read_int64(r);                           /* id */
    2319            0 :         uint32_t flags = tl_read_uint32(r);
    2320            0 :         if (flags & (1u << 2)) {
    2321            0 :             if (skip_wallpaper_settings(r) != 0) return -1;
    2322              :         }
    2323            0 :         return 0;
    2324              :     }
    2325            0 :     logger_log(LOG_WARN, "skip_wallpaper: unknown 0x%08x", crc);
    2326            0 :     return -1;
    2327              : }
    2328              : 
    2329              : /* themeSettings#fa58b6d4 flags:# message_colors_animated:flags.2?true
    2330              :  *   base_theme:BaseTheme accent_color:int
    2331              :  *   background_icon_id:flags.0?long outbox_accent_color:flags.3?int
    2332              :  *   message_colors:flags.1?Vector<int> wallpaper:flags.2?WallPaper */
    2333            0 : static int skip_theme_settings(TlReader *r) {
    2334            0 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2335            0 :     uint32_t crc = tl_read_uint32(r);
    2336            0 :     if (crc != CRC_themeSettings) {
    2337            0 :         logger_log(LOG_WARN, "skip_theme_settings: unknown 0x%08x", crc);
    2338            0 :         return -1;
    2339              :     }
    2340            0 :     if (r->len - r->pos < 4) return -1;
    2341            0 :     uint32_t flags = tl_read_uint32(r);
    2342              :     /* base_theme — one of the 5 base-theme CRCs, no payload */
    2343            0 :     if (r->len - r->pos < 4) return -1;
    2344            0 :     uint32_t bt = tl_read_uint32(r);
    2345            0 :     if (bt != CRC_baseThemeClassic && bt != CRC_baseThemeDay &&
    2346            0 :         bt != CRC_baseThemeDark    && bt != CRC_baseThemeTinted &&
    2347              :         bt != CRC_baseThemeArctic) {
    2348            0 :         logger_log(LOG_WARN, "skip_theme_settings: unknown BaseTheme 0x%08x", bt);
    2349            0 :         return -1;
    2350              :     }
    2351            0 :     if (r->len - r->pos < 4) return -1;
    2352            0 :     tl_read_int32(r);                               /* accent_color */
    2353            0 :     if (flags & (1u << 0)) {
    2354            0 :         if (r->len - r->pos < 8) return -1;
    2355            0 :         tl_read_int64(r);                           /* background_icon_id */
    2356              :     }
    2357            0 :     if (flags & (1u << 3)) {
    2358            0 :         if (r->len - r->pos < 4) return -1;
    2359            0 :         tl_read_int32(r);                           /* outbox_accent_color */
    2360              :     }
    2361            0 :     if (flags & (1u << 1)) {
    2362              :         /* message_colors:Vector<int> */
    2363            0 :         if (r->len - r->pos < 8) return -1;
    2364            0 :         uint32_t vc = tl_read_uint32(r);
    2365            0 :         if (vc != TL_vector) return -1;
    2366            0 :         uint32_t n = tl_read_uint32(r);
    2367            0 :         if (r->len - r->pos < (size_t)n * 4) return -1;
    2368            0 :         for (uint32_t i = 0; i < n; i++) tl_read_int32(r);
    2369              :     }
    2370            0 :     if (flags & (1u << 2)) {
    2371            0 :         if (skip_wallpaper(r) != 0) return -1;     /* wallpaper */
    2372              :     }
    2373            0 :     return 0;
    2374              : }
    2375              : 
    2376              : /* Skip a Vector<WebPageAttribute> (webPage.attributes, flags.12). Only
    2377              :  * the three known WebPageAttribute variants are supported; anything
    2378              :  * else makes the caller bail. */
    2379            1 : static int skip_webpage_attributes_vector(TlReader *r) {
    2380            1 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
    2381            1 :     uint32_t vec = tl_read_uint32(r);
    2382            1 :     if (vec != TL_vector) return -1;
    2383            1 :     uint32_t n = tl_read_uint32(r);
    2384            3 :     for (uint32_t i = 0; i < n; i++) {
    2385            2 :         if (r->len - r->pos < 4) return -1;
    2386            2 :         uint32_t crc = tl_read_uint32(r);
    2387            2 :         switch (crc) {
    2388            1 :         case CRC_webPageAttributeTheme: {
    2389              :             /* flags:# documents:flags.0?Vector<Document>
    2390              :              *        settings:flags.1?ThemeSettings */
    2391            1 :             if (r->len - r->pos < 4) return -1;
    2392            1 :             uint32_t flags = tl_read_uint32(r);
    2393            1 :             if (flags & (1u << 0)) {
    2394            0 :                 if (r->len - r->pos < 8) return -1;
    2395            0 :                 uint32_t dvec = tl_read_uint32(r);
    2396            0 :                 if (dvec != TL_vector) return -1;
    2397            0 :                 uint32_t dn = tl_read_uint32(r);
    2398            0 :                 for (uint32_t j = 0; j < dn; j++) {
    2399            0 :                     if (tl_skip_document(r) != 0) return -1;
    2400              :                 }
    2401              :             }
    2402            1 :             if (flags & (1u << 1)) {
    2403            0 :                 if (skip_theme_settings(r) != 0) return -1;
    2404              :             }
    2405            1 :             break;
    2406              :         }
    2407            0 :         case CRC_webPageAttributeStory: {
    2408              :             /* flags:# peer:Peer id:int story:flags.0?StoryItem */
    2409            0 :             if (r->len - r->pos < 4) return -1;
    2410            0 :             uint32_t flags = tl_read_uint32(r);
    2411            0 :             if (tl_skip_peer(r) != 0) return -1;
    2412            0 :             if (r->len - r->pos < 4) return -1;
    2413            0 :             tl_read_int32(r);                        /* id */
    2414            0 :             if (flags & (1u << 0)) {
    2415            0 :                 if (skip_story_item(r) != 0) return -1;
    2416              :             }
    2417            0 :             break;
    2418              :         }
    2419            1 :         case CRC_webPageAttributeStickerSet: {
    2420              :             /* flags:# emojis:flags.0?true text_color:flags.1?true
    2421              :              *        stickers:Vector<Document> */
    2422            1 :             if (r->len - r->pos < 4) return -1;
    2423            1 :             (void)tl_read_uint32(r);                 /* flags */
    2424            1 :             if (r->len - r->pos < 8) return -1;
    2425            1 :             uint32_t svec = tl_read_uint32(r);
    2426            1 :             if (svec != TL_vector) return -1;
    2427            1 :             uint32_t sn = tl_read_uint32(r);
    2428            1 :             for (uint32_t j = 0; j < sn; j++) {
    2429            0 :                 if (tl_skip_document(r) != 0) return -1;
    2430              :             }
    2431            1 :             break;
    2432              :         }
    2433            0 :         default:
    2434            0 :             logger_log(LOG_WARN,
    2435              :                 "skip_webpage_attributes: unknown variant 0x%08x", crc);
    2436            0 :             return -1;
    2437              :         }
    2438              :     }
    2439            1 :     return 0;
    2440              : }
    2441              : 
    2442              : /* Skip a WebPage variant. */
    2443           12 : static int skip_webpage(TlReader *r) {
    2444           12 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2445           12 :     uint32_t crc = tl_read_uint32(r);
    2446           12 :     switch (crc) {
    2447            1 :     case CRC_webPageEmpty: {
    2448            1 :         if (r->len - r->pos < 12) return -1;
    2449            1 :         uint32_t flags = tl_read_uint32(r);
    2450            1 :         tl_read_int64(r);                        /* id */
    2451            1 :         if (flags & (1u << 0))
    2452            0 :             if (tl_skip_string(r) != 0) return -1;
    2453            1 :         return 0;
    2454              :     }
    2455            1 :     case CRC_webPagePending: {
    2456            1 :         if (r->len - r->pos < 12) return -1;
    2457            1 :         uint32_t flags = tl_read_uint32(r);
    2458            1 :         tl_read_int64(r);
    2459            1 :         if (flags & (1u << 0))
    2460            0 :             if (tl_skip_string(r) != 0) return -1;
    2461            1 :         if (r->len - r->pos < 4) return -1;
    2462            1 :         tl_read_int32(r);                        /* date */
    2463            1 :         return 0;
    2464              :     }
    2465            1 :     case CRC_webPageNotModified: {
    2466            1 :         if (r->len - r->pos < 4) return -1;
    2467            1 :         uint32_t flags = tl_read_uint32(r);
    2468            1 :         if (flags & (1u << 0)) {
    2469            1 :             if (r->len - r->pos < 4) return -1;
    2470            1 :             tl_read_int32(r);                    /* cached_page_views */
    2471              :         }
    2472            1 :         return 0;
    2473              :     }
    2474            8 :     case CRC_webPage: {
    2475              :         /* webPage#e89c45b2 flags:# id:long url:string display_url:string
    2476              :          *   hash:int type:flags.0?string site_name:flags.1?string
    2477              :          *   title:flags.2?string description:flags.3?string
    2478              :          *   photo:flags.4?Photo embed_url:flags.5?string
    2479              :          *   embed_type:flags.5?string embed_width/height:flags.6?int
    2480              :          *   duration:flags.7?int author:flags.8?string
    2481              :          *   document:flags.9?Document cached_page:flags.10?Page
    2482              :          *   attributes:flags.12?Vector<WebPageAttribute>
    2483              :          */
    2484            8 :         if (r->len - r->pos < 16) return -1;
    2485            8 :         uint32_t flags = tl_read_uint32(r);
    2486            8 :         tl_read_int64(r);                        /* id */
    2487            8 :         if (tl_skip_string(r) != 0) return -1;   /* url */
    2488            8 :         if (tl_skip_string(r) != 0) return -1;   /* display_url */
    2489            8 :         if (r->len - r->pos < 4) return -1;
    2490            8 :         tl_read_int32(r);                        /* hash */
    2491            8 :         if (flags & (1u << 0))
    2492            1 :             if (tl_skip_string(r) != 0) return -1;
    2493            8 :         if (flags & (1u << 1))
    2494            1 :             if (tl_skip_string(r) != 0) return -1;
    2495            8 :         if (flags & (1u << 2))
    2496            1 :             if (tl_skip_string(r) != 0) return -1;
    2497            8 :         if (flags & (1u << 3))
    2498            1 :             if (tl_skip_string(r) != 0) return -1;
    2499            8 :         if (flags & (1u << 4)) {
    2500            0 :             if (photo_full(r, NULL) != 0) return -1;
    2501              :         }
    2502            8 :         if (flags & (1u << 5)) {
    2503            1 :             if (tl_skip_string(r) != 0) return -1;
    2504            1 :             if (tl_skip_string(r) != 0) return -1;
    2505              :         }
    2506            8 :         if (flags & (1u << 6)) {
    2507            1 :             if (r->len - r->pos < 8) return -1;
    2508            1 :             tl_read_int32(r); tl_read_int32(r);
    2509              :         }
    2510            8 :         if (flags & (1u << 7)) {
    2511            1 :             if (r->len - r->pos < 4) return -1;
    2512            1 :             tl_read_int32(r);
    2513              :         }
    2514            8 :         if (flags & (1u << 8))
    2515            1 :             if (tl_skip_string(r) != 0) return -1;
    2516            8 :         if (flags & (1u << 9)) {
    2517              :             /* document:Document — reuse tl_skip_document. */
    2518            0 :             if (tl_skip_document(r) != 0) return -1;
    2519              :         }
    2520            8 :         if (flags & (1u << 10)) {
    2521            5 :             if (skip_page(r) != 0) return -1;
    2522              :         }
    2523            8 :         if (flags & (1u << 12)) {
    2524            1 :             if (skip_webpage_attributes_vector(r) != 0) return -1;
    2525              :         }
    2526            8 :         return 0;
    2527              :     }
    2528            1 :     default:
    2529            1 :         logger_log(LOG_WARN, "skip_webpage: unknown 0x%08x", crc);
    2530            1 :         return -1;
    2531              :     }
    2532              : }
    2533              : 
    2534              : /* Skip a textWithEntities#751f3146: text:string + Vector<MessageEntity>. */
    2535            7 : static int skip_text_with_entities(TlReader *r) {
    2536            7 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2537            7 :     uint32_t crc = tl_read_uint32(r);
    2538            7 :     if (crc != CRC_textWithEntities_poll) {
    2539            0 :         logger_log(LOG_WARN, "skip_text_with_entities: unknown 0x%08x", crc);
    2540            0 :         return -1;
    2541              :     }
    2542            7 :     if (tl_skip_string(r) != 0) return -1;
    2543            7 :     return tl_skip_message_entities_vector(r);
    2544              : }
    2545              : 
    2546              : /* Skip a Poll#58747131 object. */
    2547            4 : static int skip_poll(TlReader *r) {
    2548            4 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2549            4 :     uint32_t crc = tl_read_uint32(r);
    2550            4 :     if (crc != CRC_poll) {
    2551            0 :         logger_log(LOG_WARN, "skip_poll: unknown 0x%08x", crc);
    2552            0 :         return -1;
    2553              :     }
    2554            4 :     if (r->len - r->pos < 12) return -1;
    2555            4 :     uint32_t flags = tl_read_uint32(r);
    2556            4 :     tl_read_int64(r);                                /* id */
    2557            4 :     if (skip_text_with_entities(r) != 0) return -1;  /* question */
    2558              : 
    2559              :     /* answers:Vector<PollAnswer> */
    2560            4 :     if (r->len - r->pos < 8) return -1;
    2561            4 :     uint32_t vec_crc = tl_read_uint32(r);
    2562            4 :     if (vec_crc != TL_vector) return -1;
    2563            4 :     uint32_t n_answers = tl_read_uint32(r);
    2564            7 :     for (uint32_t i = 0; i < n_answers; i++) {
    2565            3 :         if (r->len - r->pos < 4) return -1;
    2566            3 :         uint32_t pa_crc = tl_read_uint32(r);
    2567            3 :         if (pa_crc != CRC_pollAnswer) {
    2568            0 :             logger_log(LOG_WARN, "skip_poll: bad PollAnswer 0x%08x", pa_crc);
    2569            0 :             return -1;
    2570              :         }
    2571            3 :         if (skip_text_with_entities(r) != 0) return -1;
    2572            3 :         if (tl_skip_string(r) != 0) return -1;       /* option:bytes */
    2573              :     }
    2574            4 :     if (flags & (1u << 4)) {
    2575            0 :         if (r->len - r->pos < 4) return -1;
    2576            0 :         tl_read_int32(r);                            /* close_period */
    2577              :     }
    2578            4 :     if (flags & (1u << 5)) {
    2579            0 :         if (r->len - r->pos < 4) return -1;
    2580            0 :         tl_read_int32(r);                            /* close_date */
    2581              :     }
    2582            4 :     return 0;
    2583              : }
    2584              : 
    2585              : /* Skip a PollAnswerVoters#3b6ddad2. */
    2586            3 : static int skip_poll_answer_voters(TlReader *r) {
    2587            3 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2588            3 :     uint32_t crc = tl_read_uint32(r);
    2589            3 :     if (crc != CRC_pollAnswerVoters) {
    2590            1 :         logger_log(LOG_WARN, "skip_poll_answer_voters: 0x%08x", crc);
    2591            1 :         return -1;
    2592              :     }
    2593            2 :     if (r->len - r->pos < 4) return -1;
    2594            2 :     tl_read_uint32(r);                               /* flags */
    2595            2 :     if (tl_skip_string(r) != 0) return -1;           /* option:bytes */
    2596            2 :     if (r->len - r->pos < 4) return -1;
    2597            2 :     tl_read_int32(r);                                /* voters */
    2598            2 :     return 0;
    2599              : }
    2600              : 
    2601              : /* Skip a PollResults#7adc669d object. */
    2602            4 : static int skip_poll_results(TlReader *r) {
    2603            4 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2604            4 :     uint32_t crc = tl_read_uint32(r);
    2605            4 :     if (crc != CRC_pollResults) {
    2606            0 :         logger_log(LOG_WARN, "skip_poll_results: 0x%08x", crc);
    2607            0 :         return -1;
    2608              :     }
    2609            4 :     if (r->len - r->pos < 4) return -1;
    2610            4 :     uint32_t flags = tl_read_uint32(r);
    2611            4 :     if (flags & (1u << 1)) {
    2612            2 :         if (r->len - r->pos < 8) return -1;
    2613            2 :         uint32_t vc = tl_read_uint32(r);
    2614            2 :         if (vc != TL_vector) return -1;
    2615            2 :         uint32_t n = tl_read_uint32(r);
    2616            4 :         for (uint32_t i = 0; i < n; i++) {
    2617            3 :             if (skip_poll_answer_voters(r) != 0) return -1;
    2618              :         }
    2619              :     }
    2620            3 :     if (flags & (1u << 2)) {
    2621            1 :         if (r->len - r->pos < 4) return -1;
    2622            1 :         tl_read_int32(r);                            /* total_voters */
    2623              :     }
    2624            3 :     if (flags & (1u << 3)) {
    2625            1 :         if (r->len - r->pos < 8) return -1;
    2626            1 :         uint32_t vc = tl_read_uint32(r);
    2627            1 :         if (vc != TL_vector) return -1;
    2628            1 :         uint32_t n = tl_read_uint32(r);
    2629            2 :         for (uint32_t i = 0; i < n; i++) {
    2630            1 :             if (tl_skip_peer(r) != 0) return -1;
    2631              :         }
    2632              :     }
    2633            3 :     if (flags & (1u << 4)) {
    2634            1 :         if (tl_skip_string(r) != 0) return -1;       /* solution */
    2635            1 :         if (tl_skip_message_entities_vector(r) != 0) return -1;
    2636              :     }
    2637            3 :     return 0;
    2638              : }
    2639              : 
    2640              : /* ---- Game ----
    2641              :  * game#bdf9653b flags:# id:long access_hash:long short_name:string
    2642              :  *   title:string description:string photo:Photo document:flags.0?Document
    2643              :  */
    2644            3 : static int skip_game(TlReader *r) {
    2645            3 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2646            3 :     uint32_t crc = tl_read_uint32(r);
    2647            3 :     if (crc != CRC_game) {
    2648            1 :         logger_log(LOG_WARN, "skip_game: unexpected 0x%08x", crc);
    2649            1 :         return -1;
    2650              :     }
    2651            2 :     if (r->len - r->pos < 4 + 8 + 8) return -1;
    2652            2 :     uint32_t flags = tl_read_uint32(r);
    2653            2 :     tl_read_int64(r);                                /* id */
    2654            2 :     tl_read_int64(r);                                /* access_hash */
    2655            2 :     if (tl_skip_string(r) != 0) return -1;           /* short_name */
    2656            2 :     if (tl_skip_string(r) != 0) return -1;           /* title */
    2657            2 :     if (tl_skip_string(r) != 0) return -1;           /* description */
    2658            2 :     if (tl_skip_photo(r) != 0) return -1;            /* photo */
    2659            2 :     if (flags & (1u << 0)) {
    2660            1 :         if (tl_skip_document(r) != 0) return -1;     /* document */
    2661              :     }
    2662            2 :     return 0;
    2663              : }
    2664              : 
    2665              : /* ---- MessageExtendedMedia ----
    2666              :  * messageExtendedMediaPreview#ad628cc8 flags:# w:flags.0?int h:flags.0?int
    2667              :  *   thumb:flags.1?PhotoSize video_duration:flags.2?int
    2668              :  * messageExtendedMedia#ee479c64 media:MessageMedia
    2669              :  */
    2670            4 : static int skip_message_extended_media(TlReader *r) {
    2671            4 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2672            4 :     uint32_t crc = tl_read_uint32(r);
    2673            4 :     switch (crc) {
    2674            2 :     case CRC_messageExtendedMediaPreview: {
    2675            2 :         if (r->len - r->pos < 4) return -1;
    2676            2 :         uint32_t flags = tl_read_uint32(r);
    2677            2 :         if (flags & (1u << 0)) {
    2678            1 :             if (r->len - r->pos < 8) return -1;
    2679            1 :             tl_read_int32(r); tl_read_int32(r);       /* w, h */
    2680              :         }
    2681            2 :         if (flags & (1u << 1)) {
    2682            1 :             if (tl_skip_photo_size(r) != 0) return -1;
    2683              :         }
    2684            2 :         if (flags & (1u << 2)) {
    2685            1 :             if (r->len - r->pos < 4) return -1;
    2686            1 :             tl_read_int32(r);                         /* video_duration */
    2687              :         }
    2688            2 :         return 0;
    2689              :     }
    2690            1 :     case CRC_messageExtendedMedia:
    2691              :         /* Recurse into the wrapped MessageMedia. The inner metadata is
    2692              :          * discarded — paid-media iteration only needs the outer kind. */
    2693            1 :         return tl_skip_message_media_ex(r, NULL);
    2694            1 :     default:
    2695            1 :         logger_log(LOG_WARN,
    2696              :                    "skip_message_extended_media: unknown 0x%08x", crc);
    2697            1 :         return -1;
    2698              :     }
    2699              : }
    2700              : 
    2701              : /* ---- WebDocument ----
    2702              :  * webDocument#1c570ed1        url:string access_hash:long size:int
    2703              :  *                             mime_type:string attributes:Vector<DocumentAttribute>
    2704              :  * webDocumentNoProxy#f9c8bcc6 url:string size:int mime_type:string
    2705              :  *                             attributes:Vector<DocumentAttribute>
    2706              :  */
    2707            3 : static int skip_web_document(TlReader *r) {
    2708            3 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2709            3 :     uint32_t crc = tl_read_uint32(r);
    2710              :     int has_access_hash;
    2711            3 :     switch (crc) {
    2712            1 :     case CRC_webDocument:        has_access_hash = 1; break;
    2713            1 :     case CRC_webDocumentNoProxy: has_access_hash = 0; break;
    2714            1 :     default:
    2715            1 :         logger_log(LOG_WARN, "skip_web_document: unknown 0x%08x", crc);
    2716            1 :         return -1;
    2717              :     }
    2718            2 :     if (tl_skip_string(r) != 0) return -1;                /* url */
    2719            2 :     if (has_access_hash) {
    2720            1 :         if (r->len - r->pos < 8) return -1;
    2721            1 :         tl_read_int64(r);                                 /* access_hash */
    2722              :     }
    2723            2 :     if (r->len - r->pos < 4) return -1;
    2724            2 :     tl_read_int32(r);                                     /* size */
    2725            2 :     if (tl_skip_string(r) != 0) return -1;                /* mime_type */
    2726              :     /* attributes:Vector<DocumentAttribute> */
    2727            2 :     if (r->len - r->pos < 8) return -1;
    2728            2 :     uint32_t vc = tl_read_uint32(r);
    2729            2 :     if (vc != TL_vector) return -1;
    2730            2 :     uint32_t n = tl_read_uint32(r);
    2731            3 :     for (uint32_t i = 0; i < n; i++) {
    2732            1 :         if (skip_document_attribute(r, NULL, 0) != 0) return -1;
    2733              :     }
    2734            2 :     return 0;
    2735              : }
    2736              : 
    2737              : /* ---- StoryFwdHeader ----
    2738              :  * storyFwdHeader#b826e150 flags:# modified:flags.3?true
    2739              :  *   from:flags.0?Peer from_name:flags.1?string story_id:flags.2?int
    2740              :  */
    2741            2 : static int skip_story_fwd_header(TlReader *r) {
    2742            2 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2743            2 :     uint32_t crc = tl_read_uint32(r);
    2744            2 :     if (crc != CRC_storyFwdHeader) {
    2745            0 :         logger_log(LOG_WARN, "skip_story_fwd_header: unknown 0x%08x", crc);
    2746            0 :         return -1;
    2747              :     }
    2748            2 :     if (r->len - r->pos < 4) return -1;
    2749            2 :     uint32_t flags = tl_read_uint32(r);
    2750            2 :     if (flags & (1u << 0))
    2751            1 :         if (tl_skip_peer(r) != 0) return -1;              /* from */
    2752            2 :     if (flags & (1u << 1))
    2753            1 :         if (tl_skip_string(r) != 0) return -1;            /* from_name */
    2754            2 :     if (flags & (1u << 2)) {
    2755            2 :         if (r->len - r->pos < 4) return -1;
    2756            2 :         tl_read_int32(r);                                 /* story_id */
    2757              :     }
    2758            2 :     return 0;
    2759              : }
    2760              : 
    2761              : /* ---- MediaAreaCoordinates ----
    2762              :  * mediaAreaCoordinates#03d1ea4e flags:# x:double y:double w:double h:double
    2763              :  *                                rotation:double radius:flags.0?double
    2764              :  */
    2765            7 : static int skip_media_area_coordinates(TlReader *r) {
    2766            7 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2767            7 :     uint32_t crc = tl_read_uint32(r);
    2768            7 :     if (crc != CRC_mediaAreaCoordinates) {
    2769            0 :         logger_log(LOG_WARN,
    2770              :                    "skip_media_area_coordinates: unknown 0x%08x", crc);
    2771            0 :         return -1;
    2772              :     }
    2773            7 :     if (r->len - r->pos < 4 + 5 * 8) return -1;
    2774            7 :     uint32_t flags = tl_read_uint32(r);
    2775           42 :     for (int i = 0; i < 5; i++) tl_read_double(r);        /* x, y, w, h, rot */
    2776            7 :     if (flags & (1u << 0)) {
    2777            0 :         if (r->len - r->pos < 8) return -1;
    2778            0 :         tl_read_double(r);                                /* radius */
    2779              :     }
    2780            7 :     return 0;
    2781              : }
    2782              : 
    2783              : /* ---- GeoPointAddress ----
    2784              :  * geoPointAddress#de4c5d93 flags:# country_iso2:string
    2785              :  *   state:flags.0?string city:flags.1?string street:flags.2?string
    2786              :  */
    2787            1 : static int skip_geo_point_address(TlReader *r) {
    2788            1 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2789            1 :     uint32_t crc = tl_read_uint32(r);
    2790            1 :     if (crc != CRC_geoPointAddress) {
    2791            0 :         logger_log(LOG_WARN,
    2792              :                    "skip_geo_point_address: unknown 0x%08x", crc);
    2793            0 :         return -1;
    2794              :     }
    2795            1 :     if (r->len - r->pos < 4) return -1;
    2796            1 :     uint32_t flags = tl_read_uint32(r);
    2797            1 :     if (tl_skip_string(r) != 0) return -1;                /* country_iso2 */
    2798            1 :     if (flags & (1u << 0))
    2799            1 :         if (tl_skip_string(r) != 0) return -1;            /* state */
    2800            1 :     if (flags & (1u << 1))
    2801            1 :         if (tl_skip_string(r) != 0) return -1;            /* city */
    2802            1 :     if (flags & (1u << 2))
    2803            1 :         if (tl_skip_string(r) != 0) return -1;            /* street */
    2804            1 :     return 0;
    2805              : }
    2806              : 
    2807              : /* ---- MediaArea ---- dispatch over 7 known variants. */
    2808            8 : static int skip_media_area(TlReader *r) {
    2809            8 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2810            8 :     uint32_t crc = tl_read_uint32(r);
    2811            8 :     switch (crc) {
    2812            1 :     case CRC_mediaAreaVenue:
    2813            1 :         if (skip_media_area_coordinates(r) != 0) return -1;
    2814            1 :         if (skip_geo_point(r) != 0) return -1;
    2815            1 :         if (tl_skip_string(r) != 0) return -1;            /* title */
    2816            1 :         if (tl_skip_string(r) != 0) return -1;            /* address */
    2817            1 :         if (tl_skip_string(r) != 0) return -1;            /* provider */
    2818            1 :         if (tl_skip_string(r) != 0) return -1;            /* venue_id */
    2819            1 :         if (tl_skip_string(r) != 0) return -1;            /* venue_type */
    2820            1 :         return 0;
    2821            1 :     case CRC_mediaAreaGeoPoint: {
    2822            1 :         if (r->len - r->pos < 4) return -1;
    2823            1 :         uint32_t flags = tl_read_uint32(r);
    2824            1 :         if (skip_media_area_coordinates(r) != 0) return -1;
    2825            1 :         if (skip_geo_point(r) != 0) return -1;
    2826            1 :         if (flags & (1u << 0))
    2827            1 :             if (skip_geo_point_address(r) != 0) return -1;
    2828            1 :         return 0;
    2829              :     }
    2830            1 :     case CRC_mediaAreaSuggestedReaction: {
    2831            1 :         if (r->len - r->pos < 4) return -1;
    2832            1 :         tl_read_uint32(r);                                /* flags */
    2833            1 :         if (skip_media_area_coordinates(r) != 0) return -1;
    2834            1 :         return skip_reaction(r);
    2835              :     }
    2836            1 :     case CRC_mediaAreaChannelPost:
    2837            1 :         if (skip_media_area_coordinates(r) != 0) return -1;
    2838            1 :         if (r->len - r->pos < 12) return -1;
    2839            1 :         tl_read_int64(r);                                 /* channel_id */
    2840            1 :         tl_read_int32(r);                                 /* msg_id */
    2841            1 :         return 0;
    2842            1 :     case CRC_mediaAreaUrl:
    2843            1 :         if (skip_media_area_coordinates(r) != 0) return -1;
    2844            1 :         return tl_skip_string(r);                         /* url */
    2845            1 :     case CRC_mediaAreaWeather:
    2846            1 :         if (skip_media_area_coordinates(r) != 0) return -1;
    2847            1 :         if (tl_skip_string(r) != 0) return -1;            /* emoji */
    2848            1 :         if (r->len - r->pos < 8 + 4) return -1;
    2849            1 :         tl_read_double(r);                                /* temperature_c */
    2850            1 :         tl_read_int32(r);                                 /* color */
    2851            1 :         return 0;
    2852            1 :     case CRC_mediaAreaStarGift:
    2853            1 :         if (skip_media_area_coordinates(r) != 0) return -1;
    2854            1 :         return tl_skip_string(r);                         /* slug */
    2855            1 :     default:
    2856            1 :         logger_log(LOG_WARN, "skip_media_area: unknown 0x%08x", crc);
    2857            1 :         return -1;
    2858              :     }
    2859              : }
    2860              : 
    2861              : /* ---- PrivacyRule ---- 12 variants; most are CRC-only. */
    2862            3 : static int skip_privacy_rule(TlReader *r) {
    2863            3 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2864            3 :     uint32_t crc = tl_read_uint32(r);
    2865            3 :     switch (crc) {
    2866            1 :     case CRC_privacyValueAllowContacts:
    2867              :     case CRC_privacyValueAllowAll:
    2868              :     case CRC_privacyValueDisallowContacts:
    2869              :     case CRC_privacyValueDisallowAll:
    2870              :     case CRC_privacyValueAllowCloseFriends:
    2871              :     case CRC_privacyValueAllowPremium:
    2872              :     case CRC_privacyValueAllowBots:
    2873              :     case CRC_privacyValueDisallowBots:
    2874            1 :         return 0;
    2875            1 :     case CRC_privacyValueAllowUsers:
    2876              :     case CRC_privacyValueDisallowUsers:
    2877              :     case CRC_privacyValueAllowChatParticipants:
    2878              :     case CRC_privacyValueDisallowChatParticipants: {
    2879              :         /* Vector<long> */
    2880            1 :         if (r->len - r->pos < 8) return -1;
    2881            1 :         uint32_t vc = tl_read_uint32(r);
    2882            1 :         if (vc != TL_vector) return -1;
    2883            1 :         uint32_t n = tl_read_uint32(r);
    2884            1 :         if (r->len - r->pos < (size_t)n * 8) return -1;
    2885            3 :         for (uint32_t i = 0; i < n; i++) tl_read_int64(r);
    2886            1 :         return 0;
    2887              :     }
    2888            1 :     default:
    2889            1 :         logger_log(LOG_WARN, "skip_privacy_rule: unknown 0x%08x", crc);
    2890            1 :         return -1;
    2891              :     }
    2892              : }
    2893              : 
    2894              : /* ---- StoryViews ----
    2895              :  * storyViews#8d595cd6 flags:# has_viewers:flags.1?true views_count:int
    2896              :  *   forwards_count:flags.2?int reactions:flags.3?Vector<ReactionCount>
    2897              :  *   reactions_count:flags.4?int recent_viewers:flags.0?Vector<long>
    2898              :  */
    2899            2 : static int skip_story_views(TlReader *r) {
    2900            2 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2901            2 :     uint32_t crc = tl_read_uint32(r);
    2902            2 :     if (crc != CRC_storyViews) {
    2903            1 :         logger_log(LOG_WARN, "skip_story_views: unknown 0x%08x", crc);
    2904            1 :         return -1;
    2905              :     }
    2906            1 :     if (r->len - r->pos < 4) return -1;
    2907            1 :     uint32_t flags = tl_read_uint32(r);
    2908            1 :     if (r->len - r->pos < 4) return -1;
    2909            1 :     tl_read_int32(r);                                     /* views_count */
    2910            1 :     if (flags & (1u << 2)) {
    2911            1 :         if (r->len - r->pos < 4) return -1;
    2912            1 :         tl_read_int32(r);                                 /* forwards_count */
    2913              :     }
    2914            1 :     if (flags & (1u << 3)) {
    2915            1 :         if (r->len - r->pos < 8) return -1;
    2916            1 :         uint32_t vc = tl_read_uint32(r);
    2917            1 :         if (vc != TL_vector) return -1;
    2918            1 :         uint32_t n = tl_read_uint32(r);
    2919            2 :         for (uint32_t i = 0; i < n; i++)
    2920            1 :             if (skip_reaction_count(r) != 0) return -1;
    2921              :     }
    2922            1 :     if (flags & (1u << 4)) {
    2923            1 :         if (r->len - r->pos < 4) return -1;
    2924            1 :         tl_read_int32(r);                                 /* reactions_count */
    2925              :     }
    2926            1 :     if (flags & (1u << 0)) {
    2927            1 :         if (r->len - r->pos < 8) return -1;
    2928            1 :         uint32_t vc = tl_read_uint32(r);
    2929            1 :         if (vc != TL_vector) return -1;
    2930            1 :         uint32_t n = tl_read_uint32(r);
    2931            1 :         if (r->len - r->pos < (size_t)n * 8) return -1;
    2932            2 :         for (uint32_t i = 0; i < n; i++) tl_read_int64(r);
    2933              :     }
    2934            1 :     return 0;
    2935              : }
    2936              : 
    2937              : /* ---- StoryItem ----
    2938              :  * storyItemDeleted#51e6ee4f id:int
    2939              :  * storyItemSkipped#ffadc913 flags:# close_friends:flags.8?true
    2940              :  *                           id:int date:int expire_date:int
    2941              :  * storyItem#79b26a24 — full layout. The inner `media:MessageMedia`
    2942              :  * dispatches recursively through tl_skip_message_media_ex, which
    2943              :  * handles the common sub-cases (empty / photo / document / webpage /
    2944              :  * …). Deeply nested stories-inside-stories would bail by the inner
    2945              :  * dispatcher's own bail rules.
    2946              :  */
    2947           10 : static int skip_story_item(TlReader *r) {
    2948           10 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    2949           10 :     uint32_t crc = tl_read_uint32(r);
    2950           10 :     switch (crc) {
    2951            1 :     case CRC_storyItemDeleted:
    2952            1 :         if (r->len - r->pos < 4) return -1;
    2953            1 :         tl_read_int32(r);                                 /* id */
    2954            1 :         return 0;
    2955            1 :     case CRC_storyItemSkipped: {
    2956            1 :         if (r->len - r->pos < 4) return -1;
    2957            1 :         tl_read_uint32(r);                                /* flags */
    2958            1 :         if (r->len - r->pos < 12) return -1;
    2959            1 :         tl_read_int32(r);                                 /* id */
    2960            1 :         tl_read_int32(r);                                 /* date */
    2961            1 :         tl_read_int32(r);                                 /* expire_date */
    2962            1 :         return 0;
    2963              :     }
    2964            7 :     case CRC_storyItem: {
    2965            7 :         if (r->len - r->pos < 4) return -1;
    2966            7 :         uint32_t flags = tl_read_uint32(r);
    2967            7 :         if (r->len - r->pos < 8) return -1;
    2968            7 :         tl_read_int32(r);                                 /* id */
    2969            7 :         tl_read_int32(r);                                 /* date */
    2970            7 :         if (flags & (1u << 18))
    2971            0 :             if (tl_skip_peer(r) != 0) return -1;          /* from_id */
    2972            7 :         if (flags & (1u << 17))
    2973            2 :             if (skip_story_fwd_header(r) != 0) return -1; /* fwd_from */
    2974            7 :         if (r->len - r->pos < 4) return -1;
    2975            7 :         tl_read_int32(r);                                 /* expire_date */
    2976            7 :         if (flags & (1u << 0))
    2977            1 :             if (tl_skip_string(r) != 0) return -1;        /* caption */
    2978            7 :         if (flags & (1u << 1))
    2979            1 :             if (tl_skip_message_entities_vector(r) != 0) return -1;
    2980            7 :         if (tl_skip_message_media_ex(r, NULL) != 0) return -1;
    2981            7 :         if (flags & (1u << 14)) {
    2982            4 :             if (r->len - r->pos < 8) return -1;
    2983            4 :             uint32_t vc = tl_read_uint32(r);
    2984            4 :             if (vc != TL_vector) return -1;
    2985            4 :             uint32_t n = tl_read_uint32(r);
    2986           11 :             for (uint32_t i = 0; i < n; i++)
    2987            8 :                 if (skip_media_area(r) != 0) return -1;
    2988              :         }
    2989            6 :         if (flags & (1u << 2)) {
    2990            2 :             if (r->len - r->pos < 8) return -1;
    2991            2 :             uint32_t vc = tl_read_uint32(r);
    2992            2 :             if (vc != TL_vector) return -1;
    2993            2 :             uint32_t n = tl_read_uint32(r);
    2994            4 :             for (uint32_t i = 0; i < n; i++)
    2995            3 :                 if (skip_privacy_rule(r) != 0) return -1;
    2996              :         }
    2997            5 :         if (flags & (1u << 3))
    2998            2 :             if (skip_story_views(r) != 0) return -1;
    2999            4 :         if (flags & (1u << 15))
    3000            0 :             if (skip_reaction(r) != 0) return -1;         /* sent_reaction */
    3001            4 :         return 0;
    3002              :     }
    3003            1 :     default:
    3004            1 :         logger_log(LOG_WARN, "skip_story_item: unknown 0x%08x", crc);
    3005            1 :         return -1;
    3006              :     }
    3007              : }
    3008              : 
    3009              : /* ---- MessageMedia skipper ----
    3010              :  * Covers all normal variants (photo, document, geo, contact, venue,
    3011              :  * poll, invoice, story-deleted/skipped, giveaway, game, paid, webpage).
    3012              :  *
    3013              :  * The dispatcher is re-entrant via messageMediaStory → storyItem →
    3014              :  * inner media:MessageMedia. An adversarial server could attempt
    3015              :  * unbounded recursion by chaining messageMediaStory inside itself.
    3016              :  * A depth counter caps the recursion at MEDIA_RECURSION_MAX; beyond
    3017              :  * that we bail with -1 and the caller stops iterating the enclosing
    3018              :  * Message. Realistic payloads never exceed depth 2.
    3019              :  */
    3020              : #define MEDIA_RECURSION_MAX 4
    3021              : 
    3022              : static int skip_message_media_body(TlReader *r, MediaInfo *out);
    3023              : 
    3024           68 : int tl_skip_message_media_ex(TlReader *r, MediaInfo *out) {
    3025              :     static int recursion_depth = 0;
    3026           68 :     if (recursion_depth >= MEDIA_RECURSION_MAX) {
    3027            0 :         logger_log(LOG_WARN,
    3028              :             "tl_skip_message_media_ex: recursion depth limit (%d) reached",
    3029              :             MEDIA_RECURSION_MAX);
    3030            0 :         return -1;
    3031              :     }
    3032           68 :     recursion_depth++;
    3033           68 :     int rc = skip_message_media_body(r, out);
    3034           68 :     recursion_depth--;
    3035           68 :     return rc;
    3036              : }
    3037              : 
    3038           68 : static int skip_message_media_body(TlReader *r, MediaInfo *out) {
    3039           68 :     if (out) memset(out, 0, sizeof(*out));
    3040           68 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    3041           68 :     uint32_t crc = tl_read_uint32(r);
    3042           68 :     switch (crc) {
    3043           12 :     case CRC_messageMediaEmpty:
    3044           12 :         if (out) out->kind = MEDIA_EMPTY;
    3045           12 :         return 0;
    3046            1 :     case CRC_messageMediaUnsupported:
    3047            1 :         if (out) out->kind = MEDIA_UNSUPPORTED;
    3048            1 :         return 0;
    3049              : 
    3050            2 :     case CRC_messageMediaGeo:
    3051            2 :         if (out) out->kind = MEDIA_GEO;
    3052            2 :         return skip_geo_point(r);
    3053              : 
    3054            2 :     case CRC_messageMediaContact:
    3055            2 :         if (out) out->kind = MEDIA_CONTACT;
    3056            2 :         if (tl_skip_string(r) != 0) return -1;
    3057            2 :         if (tl_skip_string(r) != 0) return -1;
    3058            2 :         if (tl_skip_string(r) != 0) return -1;
    3059            2 :         if (tl_skip_string(r) != 0) return -1;
    3060            2 :         if (r->len - r->pos < 8) return -1;
    3061            2 :         tl_read_int64(r);
    3062            2 :         return 0;
    3063              : 
    3064            1 :     case CRC_messageMediaVenue:
    3065            1 :         if (out) out->kind = MEDIA_VENUE;
    3066            1 :         if (skip_geo_point(r) != 0) return -1;
    3067            1 :         if (tl_skip_string(r) != 0) return -1;
    3068            1 :         if (tl_skip_string(r) != 0) return -1;
    3069            1 :         if (tl_skip_string(r) != 0) return -1;
    3070            1 :         if (tl_skip_string(r) != 0) return -1;
    3071            1 :         if (tl_skip_string(r) != 0) return -1;
    3072            1 :         return 0;
    3073              : 
    3074            1 :     case CRC_messageMediaGeoLive: {
    3075            1 :         if (out) out->kind = MEDIA_GEO_LIVE;
    3076            1 :         if (r->len - r->pos < 4) return -1;
    3077            1 :         uint32_t flags = tl_read_uint32(r);
    3078            1 :         if (skip_geo_point(r) != 0) return -1;
    3079            1 :         if (flags & 1u) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    3080            1 :         if (r->len - r->pos < 4) return -1;
    3081            1 :         tl_read_int32(r);
    3082            1 :         if (flags & (1u << 1)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    3083            1 :         return 0;
    3084              :     }
    3085              : 
    3086            1 :     case CRC_messageMediaDice:
    3087            1 :         if (out) out->kind = MEDIA_DICE;
    3088            1 :         if (r->len - r->pos < 4) return -1;
    3089            1 :         tl_read_int32(r);
    3090            1 :         return tl_skip_string(r);
    3091              : 
    3092            2 :     case CRC_messageMediaPhoto: {
    3093            2 :         if (out) out->kind = MEDIA_PHOTO;
    3094            2 :         if (r->len - r->pos < 4) return -1;
    3095            2 :         uint32_t flags = tl_read_uint32(r);
    3096            2 :         if (flags & (1u << 0)) {
    3097            2 :             if (photo_full(r, out) != 0) return -1;
    3098              :         }
    3099            2 :         if (flags & (1u << 2)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    3100            2 :         return 0;
    3101              :     }
    3102              : 
    3103           12 :     case CRC_messageMediaWebPage: {
    3104           12 :         if (out) out->kind = MEDIA_WEBPAGE;
    3105           12 :         if (r->len - r->pos < 4) return -1;
    3106           12 :         uint32_t flags = tl_read_uint32(r);
    3107              :         (void)flags;                              /* only boolean previews */
    3108           12 :         return skip_webpage(r);
    3109              :     }
    3110              : 
    3111            4 :     case CRC_messageMediaPoll: {
    3112            4 :         if (out) out->kind = MEDIA_POLL;
    3113            4 :         if (skip_poll(r) != 0) return -1;
    3114            4 :         return skip_poll_results(r);
    3115              :     }
    3116              : 
    3117            3 :     case CRC_messageMediaInvoice: {
    3118            3 :         if (out) out->kind = MEDIA_INVOICE;
    3119            3 :         if (r->len - r->pos < 4) return -1;
    3120            3 :         uint32_t flags = tl_read_uint32(r);
    3121            3 :         if (tl_skip_string(r) != 0) return -1;    /* title */
    3122            3 :         if (tl_skip_string(r) != 0) return -1;    /* description */
    3123            3 :         if (flags & (1u << 0)) {
    3124            3 :             if (skip_web_document(r) != 0) return -1;
    3125              :         }
    3126            2 :         if (flags & (1u << 2)) {
    3127            0 :             if (r->len - r->pos < 4) return -1;
    3128            0 :             tl_read_int32(r);                     /* receipt_msg_id */
    3129              :         }
    3130            2 :         if (tl_skip_string(r) != 0) return -1;    /* currency */
    3131            2 :         if (r->len - r->pos < 8) return -1;
    3132            2 :         tl_read_int64(r);                         /* total_amount */
    3133            2 :         if (tl_skip_string(r) != 0) return -1;    /* start_param */
    3134            2 :         if (flags & (1u << 4)) {
    3135            0 :             if (skip_message_extended_media(r) != 0) return -1;
    3136              :         }
    3137            2 :         return 0;
    3138              :     }
    3139              : 
    3140           10 :     case CRC_messageMediaStory: {
    3141           10 :         if (out) out->kind = MEDIA_STORY;
    3142           10 :         if (r->len - r->pos < 4) return -1;
    3143           10 :         uint32_t flags = tl_read_uint32(r);
    3144           10 :         if (tl_skip_peer(r) != 0) return -1;      /* peer */
    3145           10 :         if (r->len - r->pos < 4) return -1;
    3146           10 :         tl_read_int32(r);                         /* id */
    3147           10 :         if (flags & (1u << 0)) {
    3148           10 :             if (skip_story_item(r) != 0) return -1;
    3149              :         }
    3150            6 :         return 0;
    3151              :     }
    3152              : 
    3153            0 :     case CRC_messageMediaGiveaway: {
    3154            0 :         if (out) out->kind = MEDIA_GIVEAWAY;
    3155            0 :         if (r->len - r->pos < 4) return -1;
    3156            0 :         uint32_t flags = tl_read_uint32(r);
    3157              :         /* channels:Vector<long> */
    3158            0 :         if (r->len - r->pos < 8) return -1;
    3159            0 :         uint32_t vc = tl_read_uint32(r);
    3160            0 :         if (vc != TL_vector) return -1;
    3161            0 :         uint32_t n_ch = tl_read_uint32(r);
    3162            0 :         if (r->len - r->pos < (size_t)n_ch * 8) return -1;
    3163            0 :         for (uint32_t i = 0; i < n_ch; i++) tl_read_int64(r);
    3164            0 :         if (flags & (1u << 1)) {
    3165            0 :             if (r->len - r->pos < 8) return -1;
    3166            0 :             uint32_t cvc = tl_read_uint32(r);
    3167            0 :             if (cvc != TL_vector) return -1;
    3168            0 :             uint32_t n_c = tl_read_uint32(r);
    3169            0 :             for (uint32_t i = 0; i < n_c; i++)
    3170            0 :                 if (tl_skip_string(r) != 0) return -1;
    3171              :         }
    3172            0 :         if (flags & (1u << 3))
    3173            0 :             if (tl_skip_string(r) != 0) return -1;
    3174            0 :         if (r->len - r->pos < 4) return -1;
    3175            0 :         tl_read_int32(r);                         /* quantity */
    3176            0 :         if (flags & (1u << 4)) {
    3177            0 :             if (r->len - r->pos < 4) return -1;
    3178            0 :             tl_read_int32(r);                     /* months */
    3179              :         }
    3180            0 :         if (flags & (1u << 5)) {
    3181            0 :             if (r->len - r->pos < 8) return -1;
    3182            0 :             tl_read_int64(r);                     /* stars */
    3183              :         }
    3184            0 :         if (r->len - r->pos < 4) return -1;
    3185            0 :         tl_read_int32(r);                         /* until_date */
    3186            0 :         return 0;
    3187              :     }
    3188              : 
    3189            3 :     case CRC_messageMediaGame: {
    3190            3 :         if (out) out->kind = MEDIA_GAME;
    3191            3 :         return skip_game(r);
    3192              :     }
    3193              : 
    3194            4 :     case CRC_messageMediaPaidMedia: {
    3195            4 :         if (out) out->kind = MEDIA_PAID;
    3196            4 :         if (r->len - r->pos < 8) return -1;
    3197            4 :         tl_read_int64(r);                         /* stars_amount */
    3198            4 :         if (r->len - r->pos < 8) return -1;
    3199            4 :         uint32_t vc = tl_read_uint32(r);
    3200            4 :         if (vc != TL_vector) return -1;
    3201            4 :         uint32_t n = tl_read_uint32(r);
    3202            7 :         for (uint32_t i = 0; i < n; i++) {
    3203            4 :             if (skip_message_extended_media(r) != 0) return -1;
    3204              :         }
    3205            3 :         return 0;
    3206              :     }
    3207              : 
    3208            8 :     case CRC_messageMediaDocument: {
    3209            8 :         if (out) out->kind = MEDIA_DOCUMENT;
    3210            8 :         if (r->len - r->pos < 4) return -1;
    3211            8 :         uint32_t flags = tl_read_uint32(r);
    3212              :         /* flags.3=nopremium flags.4=spoiler flags.6=video flags.7=round
    3213              :          * flags.8=voice — all boolean markers, no wire data; safe to ignore.
    3214              :          * flags.5/9/11 are undefined in the current schema; treat as boolean. */
    3215            8 :         if (flags & (1u << 0)) {
    3216            8 :             if (document_inner(r, out) != 0) return -1;
    3217              :         }
    3218            8 :         if (flags & (1u << 2)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    3219            8 :         if (flags & (1u << 10)) {
    3220              :             /* alt_documents:flags.10?Vector<Document> */
    3221            0 :             if (r->len - r->pos < 8) return -1;
    3222            0 :             uint32_t vc = tl_read_uint32(r);
    3223            0 :             if (vc != TL_vector) return -1;
    3224            0 :             uint32_t n = tl_read_uint32(r);
    3225            0 :             for (uint32_t i = 0; i < n; i++) {
    3226            0 :                 if (tl_skip_document(r) != 0) return -1;
    3227              :             }
    3228              :         }
    3229            8 :         return 0;
    3230              :     }
    3231              : 
    3232            2 :     default:
    3233            2 :         if (out) out->kind = MEDIA_OTHER;
    3234            2 :         logger_log(LOG_WARN, "tl_skip_message_media: unsupported 0x%08x", crc);
    3235            2 :         return -1;
    3236              :     }
    3237              : }
    3238              : 
    3239            9 : int tl_skip_message_media(TlReader *r) {
    3240            9 :     return tl_skip_message_media_ex(r, NULL);
    3241              : }
    3242              : 
    3243              : /* ---- Chat / User helpers ---- */
    3244              : 
    3245              : /* Read a TL string into a fixed-size buffer, truncating if necessary.
    3246              :  * Always NUL-terminates `out` unless out_cap == 0. Advances the cursor.
    3247              :  * Returns 0 on success, -1 on reader error. */
    3248            9 : static int read_string_into(TlReader *r, char *out, size_t out_cap) {
    3249            9 :     char *s = tl_read_string(r);
    3250            9 :     if (!s) {
    3251            0 :         if (out_cap > 0) out[0] = '\0';
    3252            0 :         return -1;
    3253              :     }
    3254            9 :     if (out_cap > 0) {
    3255            9 :         size_t n = strlen(s);
    3256            9 :         if (n >= out_cap) n = out_cap - 1;
    3257            9 :         memcpy(out, s, n);
    3258            9 :         out[n] = '\0';
    3259              :     }
    3260            9 :     free(s);
    3261            9 :     return 0;
    3262              : }
    3263              : 
    3264              : /* Append `src` to `dst` (NUL-terminated), respecting dst_cap. */
    3265            6 : static void str_append(char *dst, size_t dst_cap, const char *src) {
    3266            6 :     if (dst_cap == 0) return;
    3267            6 :     size_t cur = strlen(dst);
    3268            6 :     if (cur >= dst_cap - 1) return;
    3269            6 :     size_t room = dst_cap - 1 - cur;
    3270            6 :     size_t n = strlen(src);
    3271            6 :     if (n > room) n = room;
    3272            6 :     memcpy(dst + cur, src, n);
    3273            6 :     dst[cur + n] = '\0';
    3274              : }
    3275              : 
    3276              : /* ---- ChatPhoto ----
    3277              :  * chatPhotoEmpty#37c1011c
    3278              :  * chatPhoto#1c6e1c11 flags:# has_video:flags.0?true photo_id:long
    3279              :  *                    stripped_thumb:flags.1?bytes dc_id:int
    3280              :  */
    3281            8 : int tl_skip_chat_photo(TlReader *r) {
    3282            8 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    3283            8 :     uint32_t crc = tl_read_uint32(r);
    3284            8 :     if (crc == CRC_chatPhotoEmpty) return 0;
    3285            3 :     if (crc != CRC_chatPhoto) {
    3286            1 :         logger_log(LOG_WARN, "tl_skip_chat_photo: unknown 0x%08x", crc);
    3287            1 :         return -1;
    3288              :     }
    3289            2 :     if (r->len - r->pos < 4) return -1;
    3290            2 :     uint32_t flags = tl_read_uint32(r);
    3291              :     /* photo_id:long */
    3292            2 :     if (r->len - r->pos < 8) return -1;
    3293            2 :     tl_read_int64(r);
    3294            2 :     if (flags & (1u << 1)) {
    3295            1 :         if (tl_skip_string(r) != 0) return -1; /* stripped_thumb:bytes */
    3296              :     }
    3297              :     /* dc_id:int */
    3298            2 :     if (r->len - r->pos < 4) return -1;
    3299            2 :     tl_read_int32(r);
    3300            2 :     return 0;
    3301              : }
    3302              : 
    3303              : /* ---- UserProfilePhoto ----
    3304              :  * userProfilePhotoEmpty#4f11bae1
    3305              :  * userProfilePhoto#82d1f706 flags:# has_video:flags.0?true
    3306              :  *                           personal:flags.2?true photo_id:long
    3307              :  *                           stripped_thumb:flags.1?bytes dc_id:int
    3308              :  */
    3309            3 : int tl_skip_user_profile_photo(TlReader *r) {
    3310            3 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    3311            3 :     uint32_t crc = tl_read_uint32(r);
    3312            3 :     if (crc == CRC_userProfilePhotoEmpty) return 0;
    3313            2 :     if (crc != CRC_userProfilePhoto) {
    3314            1 :         logger_log(LOG_WARN, "tl_skip_user_profile_photo: unknown 0x%08x", crc);
    3315            1 :         return -1;
    3316              :     }
    3317            1 :     if (r->len - r->pos < 4) return -1;
    3318            1 :     uint32_t flags = tl_read_uint32(r);
    3319              :     /* photo_id:long */
    3320            1 :     if (r->len - r->pos < 8) return -1;
    3321            1 :     tl_read_int64(r);
    3322            1 :     if (flags & (1u << 1)) {
    3323            0 :         if (tl_skip_string(r) != 0) return -1; /* stripped_thumb:bytes */
    3324              :     }
    3325              :     /* dc_id:int */
    3326            1 :     if (r->len - r->pos < 4) return -1;
    3327            1 :     tl_read_int32(r);
    3328            1 :     return 0;
    3329              : }
    3330              : 
    3331              : /* ---- UserStatus ---- */
    3332            7 : int tl_skip_user_status(TlReader *r) {
    3333            7 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    3334            7 :     uint32_t crc = tl_read_uint32(r);
    3335            7 :     switch (crc) {
    3336            1 :     case CRC_userStatusEmpty:
    3337            1 :         return 0;
    3338            5 :     case CRC_userStatusOnline:
    3339              :     case CRC_userStatusOffline:
    3340              :     case CRC_userStatusRecently:
    3341              :     case CRC_userStatusLastWeek:
    3342              :     case CRC_userStatusLastMonth:
    3343            5 :         if (r->len - r->pos < 4) return -1;
    3344            5 :         tl_read_int32(r);
    3345            5 :         return 0;
    3346            1 :     default:
    3347            1 :         logger_log(LOG_WARN, "tl_skip_user_status: unknown 0x%08x", crc);
    3348            1 :         return -1;
    3349              :     }
    3350              : }
    3351              : 
    3352              : /* ---- Vector<RestrictionReason> ----
    3353              :  * restrictionReason#d072acb4 platform:string reason:string text:string
    3354              :  */
    3355            5 : int tl_skip_restriction_reason_vector(TlReader *r) {
    3356            5 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
    3357            5 :     uint32_t vec_crc = tl_read_uint32(r);
    3358            5 :     if (vec_crc != TL_vector) {
    3359            0 :         logger_log(LOG_WARN,
    3360              :                    "tl_skip_restriction_reason_vector: expected vector 0x%08x",
    3361              :                    vec_crc);
    3362            0 :         return -1;
    3363              :     }
    3364            5 :     uint32_t count = tl_read_uint32(r);
    3365           10 :     for (uint32_t i = 0; i < count; i++) {
    3366            6 :         if (r->len - r->pos < 4) return -1;
    3367            6 :         uint32_t crc = tl_read_uint32(r);
    3368            6 :         if (crc != CRC_restrictionReason) {
    3369            1 :             logger_log(LOG_WARN,
    3370              :                        "tl_skip_restriction_reason_vector: bad entry 0x%08x",
    3371              :                        crc);
    3372            1 :             return -1;
    3373              :         }
    3374            5 :         if (tl_skip_string(r) != 0) return -1; /* platform */
    3375            5 :         if (tl_skip_string(r) != 0) return -1; /* reason */
    3376            5 :         if (tl_skip_string(r) != 0) return -1; /* text */
    3377              :     }
    3378            4 :     return 0;
    3379              : }
    3380              : 
    3381              : /* ---- Vector<Username> ----
    3382              :  * username#b4073647 flags:# editable:flags.0?true active:flags.1?true username:string
    3383              :  */
    3384            2 : int tl_skip_username_vector(TlReader *r) {
    3385            2 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
    3386            2 :     uint32_t vec_crc = tl_read_uint32(r);
    3387            2 :     if (vec_crc != TL_vector) {
    3388            0 :         logger_log(LOG_WARN,
    3389              :                    "tl_skip_username_vector: expected vector 0x%08x", vec_crc);
    3390            0 :         return -1;
    3391              :     }
    3392            2 :     uint32_t count = tl_read_uint32(r);
    3393            4 :     for (uint32_t i = 0; i < count; i++) {
    3394            3 :         if (r->len - r->pos < 8) return -1;
    3395            3 :         uint32_t crc = tl_read_uint32(r);
    3396            3 :         if (crc != CRC_username) {
    3397            1 :             logger_log(LOG_WARN,
    3398              :                        "tl_skip_username_vector: bad entry 0x%08x", crc);
    3399            1 :             return -1;
    3400              :         }
    3401            2 :         tl_read_uint32(r); /* flags — only 'true' bits, no data */
    3402            2 :         if (tl_skip_string(r) != 0) return -1;
    3403              :     }
    3404            1 :     return 0;
    3405              : }
    3406              : 
    3407              : /* ---- PeerColor ----
    3408              :  * peerColor#b54b5acf flags:# color:flags.0?int background_emoji_id:flags.1?long
    3409              :  */
    3410            2 : int tl_skip_peer_color(TlReader *r) {
    3411            2 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
    3412            2 :     uint32_t crc = tl_read_uint32(r);
    3413            2 :     if (crc != CRC_peerColor) {
    3414            1 :         logger_log(LOG_WARN, "tl_skip_peer_color: unknown 0x%08x", crc);
    3415            1 :         return -1;
    3416              :     }
    3417            1 :     uint32_t flags = tl_read_uint32(r);
    3418            1 :     if (flags & (1u << 0)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    3419            1 :     if (flags & (1u << 1)) { if (r->len - r->pos < 8) return -1; tl_read_int64(r); }
    3420            1 :     return 0;
    3421              : }
    3422              : 
    3423              : /* ---- EmojiStatus ---- */
    3424            4 : int tl_skip_emoji_status(TlReader *r) {
    3425            4 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    3426            4 :     uint32_t crc = tl_read_uint32(r);
    3427            4 :     switch (crc) {
    3428            1 :     case CRC_emojiStatusEmpty:
    3429            1 :         return 0;
    3430            1 :     case CRC_emojiStatus:
    3431            1 :         if (r->len - r->pos < 8) return -1;
    3432            1 :         tl_read_int64(r);
    3433            1 :         return 0;
    3434            1 :     case CRC_emojiStatusUntil:
    3435            1 :         if (r->len - r->pos < 12) return -1;
    3436            1 :         tl_read_int64(r); tl_read_int32(r);
    3437            1 :         return 0;
    3438            1 :     default:
    3439            1 :         logger_log(LOG_WARN, "tl_skip_emoji_status: unknown 0x%08x", crc);
    3440            1 :         return -1;
    3441              :     }
    3442              : }
    3443              : 
    3444              : /* ChatAdminRights#5fb224d5 flags:# — single uint32. */
    3445            3 : static int skip_chat_admin_rights(TlReader *r) {
    3446            3 :     if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
    3447            3 :     uint32_t crc = tl_read_uint32(r);
    3448            3 :     if (crc != CRC_chatAdminRights) {
    3449            1 :         logger_log(LOG_WARN, "skip_chat_admin_rights: unknown 0x%08x", crc);
    3450            1 :         return -1;
    3451              :     }
    3452            2 :     tl_read_uint32(r); /* flags */
    3453            2 :     return 0;
    3454              : }
    3455              : 
    3456              : /* ChatBannedRights#9f120418 flags:# until_date:int. */
    3457            4 : static int skip_chat_banned_rights(TlReader *r) {
    3458            4 :     if (!tl_reader_ok(r) || r->len - r->pos < 12) return -1;
    3459            4 :     uint32_t crc = tl_read_uint32(r);
    3460            4 :     if (crc != CRC_chatBannedRights) {
    3461            1 :         logger_log(LOG_WARN, "skip_chat_banned_rights: unknown 0x%08x", crc);
    3462            1 :         return -1;
    3463              :     }
    3464            3 :     tl_read_uint32(r); /* flags */
    3465            3 :     tl_read_int32(r);  /* until_date */
    3466            3 :     return 0;
    3467              : }
    3468              : 
    3469              : /* ---- Core Chat extractor ----
    3470              :  *
    3471              :  * Covers:
    3472              :  *   chatEmpty#29562865 id:long
    3473              :  *   chat#41cbf256                       (see header comment)
    3474              :  *   chatForbidden#6592a1a7 id:long title:string
    3475              :  *   channel#0aadfc8f                    (layer 170+)
    3476              :  *   channelForbidden#17d493d5
    3477              :  *
    3478              :  * If `out` is non-NULL, populates (id, title). chatEmpty leaves title empty.
    3479              :  */
    3480            8 : static int extract_chat_inner(TlReader *r, ChatSummary *out) {
    3481            8 :     if (out) {
    3482            4 :         out->id = 0;
    3483            4 :         out->access_hash = 0;
    3484            4 :         out->have_access_hash = 0;
    3485            4 :         out->title[0] = '\0';
    3486              :     }
    3487              : 
    3488            8 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    3489            8 :     uint32_t crc = tl_read_uint32(r);
    3490              : 
    3491            8 :     if (crc == TL_chatEmpty) {
    3492            2 :         if (r->len - r->pos < 8) return -1;
    3493            2 :         int64_t id = tl_read_int64(r);
    3494            2 :         if (out) out->id = id;
    3495            2 :         return 0;
    3496              :     }
    3497              : 
    3498            6 :     if (crc == TL_chatForbidden) {
    3499            1 :         if (r->len - r->pos < 8) return -1;
    3500            1 :         int64_t id = tl_read_int64(r);
    3501            1 :         if (out) out->id = id;
    3502            1 :         if (out) {
    3503            1 :             if (read_string_into(r, out->title, sizeof(out->title)) != 0)
    3504            0 :                 return -1;
    3505              :         } else {
    3506            0 :             if (tl_skip_string(r) != 0) return -1;
    3507              :         }
    3508            1 :         return 0;
    3509              :     }
    3510              : 
    3511            5 :     if (crc == TL_chat) {
    3512            3 :         if (r->len - r->pos < 4) return -1;
    3513            3 :         uint32_t flags = tl_read_uint32(r);
    3514            3 :         if (flags & (1u << 6)) {
    3515              :             /* migrated_to:flags.6?InputChannel — read and discard */
    3516            0 :             if (r->len - r->pos < 4) return -1;
    3517            0 :             uint32_t ic_crc = tl_read_uint32(r);
    3518            0 :             if (ic_crc == CRC_inputChannelEmpty) {
    3519              :                 /* no payload */
    3520            0 :             } else if (ic_crc == CRC_inputChannel ||
    3521              :                        ic_crc == CRC_inputChannelFromMessage) {
    3522            0 :                 if (r->len - r->pos < 16) return -1;
    3523            0 :                 tl_read_int64(r); tl_read_int64(r);
    3524              :             } else {
    3525            0 :                 logger_log(LOG_WARN,
    3526              :                            "extract_chat: unknown InputChannel 0x%08x", ic_crc);
    3527            0 :                 return -1;
    3528              :             }
    3529              :         }
    3530              :         /* id:long title:string photo:ChatPhoto */
    3531            3 :         if (r->len - r->pos < 8) return -1;
    3532            3 :         int64_t id = tl_read_int64(r);
    3533            3 :         if (out) out->id = id;
    3534            3 :         if (out) {
    3535            1 :             if (read_string_into(r, out->title, sizeof(out->title)) != 0)
    3536            0 :                 return -1;
    3537              :         } else {
    3538            2 :             if (tl_skip_string(r) != 0) return -1;
    3539              :         }
    3540            3 :         if (tl_skip_chat_photo(r) != 0) return -1;
    3541              :         /* participants_count:int date:int version:int */
    3542            3 :         if (r->len - r->pos < 12) return -1;
    3543            3 :         tl_read_int32(r); tl_read_int32(r); tl_read_int32(r);
    3544            3 :         if (flags & (1u << 14)) {
    3545            2 :             if (skip_chat_admin_rights(r) != 0) return -1;
    3546              :         }
    3547            2 :         if (flags & (1u << 18)) {
    3548            2 :             if (skip_chat_banned_rights(r) != 0) return -1;
    3549              :         }
    3550            1 :         return 0;
    3551              :     }
    3552              : 
    3553            2 :     if (crc == TL_channelForbidden) {
    3554            0 :         if (r->len - r->pos < 4) return -1;
    3555            0 :         uint32_t flags = tl_read_uint32(r);
    3556            0 :         if (r->len - r->pos < 16) return -1;
    3557            0 :         int64_t id = tl_read_int64(r);
    3558            0 :         if (out) out->id = id;
    3559            0 :         int64_t access_hash = tl_read_int64(r);
    3560            0 :         if (out) {
    3561            0 :             out->access_hash = access_hash;
    3562            0 :             out->have_access_hash = 1;
    3563              :         }
    3564            0 :         if (out) {
    3565            0 :             if (read_string_into(r, out->title, sizeof(out->title)) != 0)
    3566            0 :                 return -1;
    3567              :         } else {
    3568            0 :             if (tl_skip_string(r) != 0) return -1;
    3569              :         }
    3570            0 :         if (flags & (1u << 16)) {
    3571            0 :             if (r->len - r->pos < 4) return -1;
    3572            0 :             tl_read_int32(r); /* until_date */
    3573              :         }
    3574            0 :         return 0;
    3575              :     }
    3576              : 
    3577            2 :     if (crc == TL_channel) {
    3578            1 :         if (r->len - r->pos < 8) return -1;
    3579            1 :         uint32_t flags  = tl_read_uint32(r);
    3580            1 :         uint32_t flags2 = tl_read_uint32(r);
    3581              :         /* Known flags2 bits: 0,4,7,8,9,10,11.  Unknown bits may be boolean
    3582              :          * markers with no wire payload (same pattern as tl_skip_user). Log
    3583              :          * at DEBUG and continue; a desync will surface as a parse error below. */
    3584            1 :         const uint32_t flags2_known =
    3585              :             (1u << 0) | (1u << 4) | (1u << 7) | (1u << 8) |
    3586              :             (1u << 9) | (1u << 10) | (1u << 11);
    3587            1 :         if (flags2 & ~flags2_known) {
    3588            0 :             logger_log(LOG_DEBUG,
    3589              :                        "extract_chat: channel unknown flags2 bits 0x%x (continuing)",
    3590            0 :                        flags2 & ~flags2_known);
    3591              :         }
    3592            1 :         if (r->len - r->pos < 8) return -1;
    3593            1 :         int64_t id = tl_read_int64(r);
    3594            1 :         if (out) out->id = id;
    3595            1 :         if (flags & (1u << 13)) {
    3596            0 :             if (r->len - r->pos < 8) return -1;
    3597            0 :             int64_t access_hash = tl_read_int64(r);
    3598            0 :             if (out) {
    3599            0 :                 out->access_hash = access_hash;
    3600            0 :                 out->have_access_hash = 1;
    3601              :             }
    3602              :         }
    3603            1 :         if (out) {
    3604            1 :             if (read_string_into(r, out->title, sizeof(out->title)) != 0)
    3605            0 :                 return -1;
    3606              :         } else {
    3607            0 :             if (tl_skip_string(r) != 0) return -1;
    3608              :         }
    3609            1 :         if (flags & (1u << 6)) {
    3610            0 :             if (tl_skip_string(r) != 0) return -1; /* username */
    3611              :         }
    3612            1 :         if (tl_skip_chat_photo(r) != 0) return -1;
    3613              :         /* date:int */
    3614            1 :         if (r->len - r->pos < 4) return -1;
    3615            1 :         tl_read_int32(r);
    3616            1 :         if (flags & (1u << 9)) {
    3617            0 :             if (tl_skip_restriction_reason_vector(r) != 0) return -1;
    3618              :         }
    3619            1 :         if (flags & (1u << 14)) {
    3620            1 :             if (skip_chat_admin_rights(r) != 0) return -1;
    3621              :         }
    3622            1 :         if (flags & (1u << 15)) {
    3623            1 :             if (skip_chat_banned_rights(r) != 0) return -1;
    3624              :         }
    3625            1 :         if (flags & (1u << 18)) {
    3626            1 :             if (skip_chat_banned_rights(r) != 0) return -1;
    3627              :         }
    3628            1 :         if (flags & (1u << 17)) {
    3629            0 :             if (r->len - r->pos < 4) return -1;
    3630            0 :             tl_read_int32(r); /* participants_count */
    3631              :         }
    3632            1 :         if (flags2 & (1u << 0)) {
    3633            0 :             if (tl_skip_username_vector(r) != 0) return -1;
    3634              :         }
    3635            1 :         if (flags2 & (1u << 4)) {
    3636            0 :             if (r->len - r->pos < 4) return -1;
    3637            0 :             tl_read_int32(r); /* stories_max_id */
    3638              :         }
    3639            1 :         if (flags2 & (1u << 7)) {
    3640            0 :             if (tl_skip_peer_color(r) != 0) return -1;
    3641              :         }
    3642            1 :         if (flags2 & (1u << 8)) {
    3643            0 :             if (tl_skip_peer_color(r) != 0) return -1;
    3644              :         }
    3645            1 :         if (flags2 & (1u << 9)) {
    3646            0 :             if (tl_skip_emoji_status(r) != 0) return -1;
    3647              :         }
    3648            1 :         if (flags2 & (1u << 10)) {
    3649            0 :             if (r->len - r->pos < 4) return -1;
    3650            0 :             tl_read_int32(r); /* level */
    3651              :         }
    3652            1 :         if (flags2 & (1u << 11)) {
    3653            0 :             if (r->len - r->pos < 4) return -1;
    3654            0 :             tl_read_int32(r); /* subscription_until_date */
    3655              :         }
    3656            1 :         return 0;
    3657              :     }
    3658              : 
    3659            1 :     logger_log(LOG_WARN, "tl_skip_chat: unknown Chat variant 0x%08x", crc);
    3660            1 :     return -1;
    3661              : }
    3662              : 
    3663            2 : int tl_skip_chat(TlReader *r) {
    3664            2 :     return extract_chat_inner(r, NULL);
    3665              : }
    3666              : 
    3667            6 : int tl_extract_chat(TlReader *r, ChatSummary *out) {
    3668            6 :     if (!out) return extract_chat_inner(r, NULL);
    3669            4 :     return extract_chat_inner(r, out);
    3670              : }
    3671              : 
    3672              : /* ---- Core User extractor ----
    3673              :  *
    3674              :  * Covers:
    3675              :  *   userEmpty#d3bc4b7a id:long
    3676              :  *   user#3ff6ecb0 (layer 170+)
    3677              :  *
    3678              :  * If `out` is non-NULL, populates (id, name, username). `name` is
    3679              :  * `first_name` and `last_name` joined with a single space where both are
    3680              :  * present.
    3681              :  */
    3682            7 : static int extract_user_inner(TlReader *r, UserSummary *out) {
    3683            7 :     if (out) {
    3684            6 :         out->id = 0;
    3685            6 :         out->access_hash = 0;
    3686            6 :         out->have_access_hash = 0;
    3687            6 :         out->name[0] = '\0';
    3688            6 :         out->username[0] = '\0';
    3689              :     }
    3690              : 
    3691            7 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    3692            7 :     uint32_t crc = tl_read_uint32(r);
    3693              : 
    3694            7 :     if (crc == TL_userEmpty) {
    3695            1 :         if (r->len - r->pos < 8) return -1;
    3696            1 :         int64_t id = tl_read_int64(r);
    3697            1 :         if (out) out->id = id;
    3698            1 :         return 0;
    3699              :     }
    3700              : 
    3701            6 :     if (crc != TL_user && crc != TL_user2) {
    3702            1 :         logger_log(LOG_WARN, "tl_skip_user: unknown User variant 0x%08x", crc);
    3703            1 :         return -1;
    3704              :     }
    3705              : 
    3706            5 :     if (r->len - r->pos < 8) return -1;
    3707            5 :     uint32_t flags  = tl_read_uint32(r);
    3708            5 :     uint32_t flags2 = tl_read_uint32(r);
    3709              :     /* Unknown flags2 bits beyond what we handle may be pure boolean markers
    3710              :      * (no wire value). Log at DEBUG but do not reject — if they carry an
    3711              :      * unknown payload the stream will desync and a parse error will surface
    3712              :      * below.  Rejecting here would break the title join for every user that
    3713              :      * has a newer feature flag set. */
    3714            5 :     if (flags2 & ~((1u << 14) - 1u)) {
    3715            0 :         logger_log(LOG_DEBUG,
    3716              :                    "tl_skip_user: unknown flags2 bits 0x%x (continuing)",
    3717              :                    flags2 & ~((1u << 14) - 1u));
    3718              :     }
    3719            5 :     if (r->len - r->pos < 8) return -1;
    3720            5 :     int64_t id = tl_read_int64(r);
    3721            5 :     if (out) out->id = id;
    3722              : 
    3723            5 :     if (flags & (1u << 0)) {
    3724            5 :         if (r->len - r->pos < 8) return -1;
    3725            5 :         int64_t access_hash = tl_read_int64(r);
    3726            5 :         if (out) {
    3727            5 :             out->access_hash = access_hash;
    3728            5 :             out->have_access_hash = 1;
    3729              :         }
    3730              :     }
    3731              :     /* first_name */
    3732            5 :     if (flags & (1u << 1)) {
    3733            4 :         if (out) {
    3734            4 :             char first[96] = {0};
    3735            4 :             if (read_string_into(r, first, sizeof(first)) != 0) return -1;
    3736            4 :             str_append(out->name, sizeof(out->name), first);
    3737              :         } else {
    3738            0 :             if (tl_skip_string(r) != 0) return -1;
    3739              :         }
    3740              :     }
    3741              :     /* last_name */
    3742            5 :     if (flags & (1u << 2)) {
    3743            1 :         if (out) {
    3744            1 :             char last[96] = {0};
    3745            1 :             if (read_string_into(r, last, sizeof(last)) != 0) return -1;
    3746            1 :             if (last[0] != '\0') {
    3747            1 :                 if (out->name[0] != '\0') {
    3748            1 :                     str_append(out->name, sizeof(out->name), " ");
    3749              :                 }
    3750            1 :                 str_append(out->name, sizeof(out->name), last);
    3751              :             }
    3752              :         } else {
    3753            0 :             if (tl_skip_string(r) != 0) return -1;
    3754              :         }
    3755              :     }
    3756              :     /* username */
    3757            5 :     if (flags & (1u << 3)) {
    3758            1 :         if (out) {
    3759            1 :             if (read_string_into(r, out->username, sizeof(out->username)) != 0)
    3760            0 :                 return -1;
    3761              :         } else {
    3762            0 :             if (tl_skip_string(r) != 0) return -1;
    3763              :         }
    3764              :     }
    3765              :     /* phone */
    3766            5 :     if (flags & (1u << 4)) {
    3767            1 :         if (tl_skip_string(r) != 0) return -1;
    3768              :     }
    3769              :     /* photo */
    3770            5 :     if (flags & (1u << 5)) {
    3771            0 :         if (tl_skip_user_profile_photo(r) != 0) return -1;
    3772              :     }
    3773              :     /* status */
    3774            5 :     if (flags & (1u << 6)) {
    3775            0 :         if (tl_skip_user_status(r) != 0) return -1;
    3776              :     }
    3777              :     /* bot_info_version */
    3778            5 :     if (flags & (1u << 14)) {
    3779            0 :         if (r->len - r->pos < 4) return -1;
    3780            0 :         tl_read_int32(r);
    3781              :     }
    3782              :     /* restriction_reason */
    3783            5 :     if (flags & (1u << 18)) {
    3784            0 :         if (tl_skip_restriction_reason_vector(r) != 0) return -1;
    3785              :     }
    3786              :     /* bot_inline_placeholder */
    3787            5 :     if (flags & (1u << 19)) {
    3788            0 :         if (tl_skip_string(r) != 0) return -1;
    3789              :     }
    3790              :     /* lang_code */
    3791            5 :     if (flags & (1u << 22)) {
    3792            0 :         if (tl_skip_string(r) != 0) return -1;
    3793              :     }
    3794              :     /* emoji_status */
    3795            5 :     if (flags & (1u << 30)) {
    3796            0 :         if (tl_skip_emoji_status(r) != 0) return -1;
    3797              :     }
    3798              :     /* usernames */
    3799            5 :     if (flags2 & (1u << 0)) {
    3800            0 :         if (tl_skip_username_vector(r) != 0) return -1;
    3801              :     }
    3802              :     /* stories_max_id */
    3803            5 :     if (flags2 & (1u << 5)) {
    3804            0 :         if (r->len - r->pos < 4) return -1;
    3805            0 :         tl_read_int32(r);
    3806              :     }
    3807              :     /* color */
    3808            5 :     if (flags2 & (1u << 8)) {
    3809            0 :         if (tl_skip_peer_color(r) != 0) return -1;
    3810              :     }
    3811              :     /* profile_color */
    3812            5 :     if (flags2 & (1u << 9)) {
    3813            0 :         if (tl_skip_peer_color(r) != 0) return -1;
    3814              :     }
    3815              :     /* bot_active_users */
    3816            5 :     if (flags2 & (1u << 12)) {
    3817            0 :         if (r->len - r->pos < 4) return -1;
    3818            0 :         tl_read_int32(r);
    3819              :     }
    3820            5 :     return 0;
    3821              : }
    3822              : 
    3823            1 : int tl_skip_user(TlReader *r) {
    3824            1 :     return extract_user_inner(r, NULL);
    3825              : }
    3826              : 
    3827            6 : int tl_extract_user(TlReader *r, UserSummary *out) {
    3828            6 :     if (!out) return extract_user_inner(r, NULL);
    3829            6 :     return extract_user_inner(r, out);
    3830              : }
    3831              : 
    3832              : /* Skip a MessageAction (cursor positioned after the action CRC).
    3833              :  * Returns 0 on success, -1 on unknown action or truncation. */
    3834            0 : static int skip_message_action(TlReader *r) {
    3835            0 :     if (r->len - r->pos < 4) return -1;
    3836            0 :     uint32_t acrc = tl_read_uint32(r);
    3837              : 
    3838              :     /* CRC constants mirror those in domain/read/history.c */
    3839            0 :     switch (acrc) {
    3840              :     /* No-payload actions */
    3841            0 :     case 0xb6aef7b0u: /* messageActionEmpty */
    3842              :     case 0x95e3fbefu: /* messageActionChatDeletePhoto */
    3843              :     case 0x94bd38edu: /* messageActionPinMessage */
    3844              :     case 0x9fbab604u: /* messageActionHistoryClear */
    3845              :     case 0x4792929bu: /* messageActionScreenshotTaken */
    3846              :     case 0x9244c2adu: /* messageActionContactSignUp */
    3847              :     case 0xb4c38cb5u: /* messageActionChatJoinedByRequest */
    3848            0 :         return 0;
    3849              : 
    3850              :     /* string-only actions */
    3851            0 :     case 0xb5a1ce5au: /* messageActionChatEditTitle */
    3852              :     case 0x95d2ac92u: /* messageActionChannelCreate */
    3853              :     case 0xfae69f56u: /* messageActionCustomAction */
    3854            0 :         return tl_skip_string(r);
    3855              : 
    3856              :     /* long-only actions */
    3857            0 :     case 0xa43f30ccu: /* messageActionChatDeleteUser */
    3858              :     case 0xe1037f92u: /* messageActionChatMigrateTo */
    3859              :     case 0x031224c3u: /* messageActionChatJoinedByLink */
    3860            0 :         if (r->len - r->pos < 8) return -1;
    3861            0 :         tl_read_int64(r);
    3862            0 :         return 0;
    3863              : 
    3864              :     /* messageActionChatCreate: title:string + users:Vector<long> */
    3865            0 :     case 0xbd47cbadu:
    3866            0 :         if (tl_skip_string(r) != 0) return -1;
    3867              :         /* FALLTHROUGH */
    3868              :     /* messageActionChatAddUser: users:Vector<long> */
    3869              :     /* cppcheck-suppress missingBreak */
    3870              :     case 0x15cefd00u: {
    3871            0 :         if (r->len - r->pos < 8) return -1;
    3872            0 :         uint32_t vcrc = tl_read_uint32(r);
    3873            0 :         if (vcrc != TL_vector) return -1;
    3874            0 :         uint32_t n = tl_read_uint32(r);
    3875            0 :         if (n > 4096) return -1;
    3876            0 :         if (r->len - r->pos < (size_t)n * 8) return -1;
    3877            0 :         for (uint32_t i = 0; i < n; i++) tl_read_int64(r);
    3878            0 :         return 0;
    3879              :     }
    3880              : 
    3881              :     /* messageActionChannelMigrateFrom: title:string + chat_id:long */
    3882            0 :     case 0xea3948e9u: {
    3883            0 :         if (tl_skip_string(r) != 0) return -1;
    3884            0 :         if (r->len - r->pos < 8) return -1;
    3885            0 :         tl_read_int64(r);
    3886            0 :         return 0;
    3887              :     }
    3888              : 
    3889              :     /* Photo */
    3890            0 :     case 0x7fcb13a8u: /* messageActionChatEditPhoto */
    3891            0 :         return tl_skip_photo(r);
    3892              : 
    3893              :     /* flags + call_id:long + optional reason(uint32) + optional duration(int32) */
    3894            0 :     case 0x80e11a7fu: /* messageActionPhoneCall */ {
    3895            0 :         if (r->len - r->pos < 4 + 8) return -1;
    3896            0 :         uint32_t pflags = tl_read_uint32(r);
    3897            0 :         tl_read_int64(r); /* call_id */
    3898            0 :         if (pflags & 1u) {
    3899            0 :             if (r->len - r->pos < 4) return -1;
    3900            0 :             tl_read_uint32(r); /* PhoneCallDiscardReason */
    3901              :         }
    3902            0 :         if (pflags & (1u << 1)) {
    3903            0 :             if (r->len - r->pos < 4) return -1;
    3904            0 :             tl_read_int32(r); /* duration */
    3905              :         }
    3906            0 :         return 0;
    3907              :     }
    3908              : 
    3909              :     /* InputGroupCall#d8aa840f (crc+id+access_hash) + optional flags */
    3910            0 :     case 0x7a0d7f42u: /* messageActionGroupCall */ {
    3911            0 :         if (r->len - r->pos < 4) return -1;
    3912            0 :         uint32_t gflags = tl_read_uint32(r);
    3913            0 :         if (r->len - r->pos < 4 + 16) return -1;
    3914            0 :         tl_read_uint32(r); tl_read_int64(r); tl_read_int64(r);
    3915            0 :         if (gflags & 1u) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    3916            0 :         return 0;
    3917              :     }
    3918            0 :     case 0xb3a07661u: /* messageActionGroupCallScheduled */
    3919            0 :         if (r->len - r->pos < 4 + 16 + 4) return -1;
    3920            0 :         tl_read_uint32(r); tl_read_int64(r); tl_read_int64(r);
    3921            0 :         tl_read_int32(r);
    3922            0 :         return 0;
    3923            0 :     case 0x502f92f7u: /* messageActionInviteToGroupCall */ {
    3924            0 :         if (r->len - r->pos < 4 + 16) return -1;
    3925            0 :         tl_read_uint32(r); tl_read_int64(r); tl_read_int64(r);
    3926            0 :         if (r->len - r->pos < 8) return -1;
    3927            0 :         uint32_t vcrc2 = tl_read_uint32(r);
    3928            0 :         if (vcrc2 != TL_vector) return -1;
    3929            0 :         uint32_t n2 = tl_read_uint32(r);
    3930            0 :         if (n2 > 4096) return -1;
    3931            0 :         if (r->len - r->pos < (size_t)n2 * 8) return -1;
    3932            0 :         for (uint32_t i = 0; i < n2; i++) tl_read_int64(r);
    3933            0 :         return 0;
    3934              :     }
    3935              : 
    3936            0 :     default:
    3937            0 :         return -1; /* unknown action — cannot advance cursor safely */
    3938              :     }
    3939              : }
    3940              : 
    3941              : /* ---- tl_skip_message ----
    3942              :  * Mirrors the parser in domain/read/history.c but discards all output.
    3943              :  * Returns 0 if the cursor is safely past the whole Message, -1 if we
    3944              :  * had to bail (cursor possibly mid-object).
    3945              :  */
    3946           14 : int tl_skip_message(TlReader *r) {
    3947           14 :     if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
    3948           13 :     uint32_t crc = tl_read_uint32(r);
    3949              : 
    3950           13 :     if (crc == TL_messageEmpty) {
    3951              :         /* flags:# id:int peer_id:flags.0?Peer */
    3952            2 :         if (r->len - r->pos < 8) return -1;
    3953            2 :         uint32_t flags = tl_read_uint32(r);
    3954            2 :         tl_read_int32(r); /* id */
    3955            2 :         if (flags & 1u) {
    3956            0 :             if (tl_skip_peer(r) != 0) return -1;
    3957              :         }
    3958            2 :         return 0;
    3959              :     }
    3960              : 
    3961           11 :     if (crc == TL_messageService) {
    3962            1 :         if (r->len - r->pos < 8) return -1;
    3963            1 :         uint32_t flags = tl_read_uint32(r);
    3964            1 :         tl_read_int32(r); /* id — messageService has no flags2 */
    3965            1 :         if (flags & (1u << 8)) if (tl_skip_peer(r) != 0) return -1; /* from_id */
    3966            1 :         if (tl_skip_peer(r) != 0) return -1;                          /* peer_id */
    3967            0 :         if (flags & (1u << 3)) if (tl_skip_message_reply_header(r) != 0) return -1;
    3968            0 :         if (r->len - r->pos < 4) return -1;
    3969            0 :         tl_read_int32(r); /* date */
    3970            0 :         if (skip_message_action(r) != 0) return -1;
    3971            0 :         if (flags & (1u << 20)) if (tl_skip_message_reactions(r) != 0) return -1;
    3972            0 :         if (flags & (1u << 25)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    3973            0 :         return 0;
    3974              :     }
    3975              : 
    3976           10 :     if (crc != TL_message) {
    3977            1 :         logger_log(LOG_WARN, "tl_skip_message: unknown 0x%08x", crc);
    3978            1 :         return -1;
    3979              :     }
    3980              : 
    3981            9 :     if (r->len - r->pos < 12) return -1;
    3982            8 :     uint32_t flags  = tl_read_uint32(r);
    3983            8 :     uint32_t flags2 = tl_read_uint32(r);
    3984            8 :     tl_read_int32(r); /* id */
    3985              : 
    3986            8 :     if (flags & (1u << 8)) if (tl_skip_peer(r) != 0) return -1; /* from_id */
    3987            8 :     if (tl_skip_peer(r) != 0) return -1;                         /* peer_id */
    3988            8 :     if (flags & (1u << 28)) if (tl_skip_peer(r) != 0) return -1; /* saved_peer_id */
    3989            8 :     if (flags & (1u << 2))  if (tl_skip_message_fwd_header(r) != 0) return -1;
    3990            8 :     if (flags & (1u << 11)) { if (r->len - r->pos < 8) return -1; tl_read_int64(r); }
    3991            8 :     if (flags2 & (1u << 0)) { if (r->len - r->pos < 8) return -1; tl_read_int64(r); }
    3992            8 :     if (flags & (1u << 3))  if (tl_skip_message_reply_header(r) != 0) return -1;
    3993              : 
    3994            8 :     if (r->len - r->pos < 4) return -1;
    3995            8 :     tl_read_int32(r); /* date */
    3996            8 :     if (tl_skip_string(r) != 0) return -1; /* message */
    3997              : 
    3998            8 :     if (flags & (1u << 9)) if (tl_skip_message_media(r) != 0) return -1;
    3999              : 
    4000            7 :     if (flags & (1u << 6))  if (tl_skip_reply_markup(r) != 0) return -1;
    4001            6 :     if (flags & (1u << 7))  if (tl_skip_message_entities_vector(r) != 0) return -1;
    4002            6 :     if (flags & (1u << 10)) { if (r->len - r->pos < 8) return -1; tl_read_int32(r); tl_read_int32(r); }
    4003            6 :     if (flags & (1u << 23)) if (tl_skip_message_replies(r) != 0) return -1;
    4004            6 :     if (flags & (1u << 15)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    4005            6 :     if (flags & (1u << 16)) if (tl_skip_string(r) != 0) return -1;
    4006            6 :     if (flags & (1u << 17)) { if (r->len - r->pos < 8) return -1; tl_read_int64(r); }
    4007            6 :     if (flags & (1u << 20)) if (tl_skip_message_reactions(r) != 0) return -1;
    4008            6 :     if (flags & (1u << 22)) if (tl_skip_restriction_reason_vector(r) != 0) return -1;
    4009            6 :     if (flags & (1u << 25)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    4010            6 :     if (flags2 & (1u << 30)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
    4011            6 :     if (flags2 & (1u << 2))  { if (r->len - r->pos < 8) return -1; tl_read_int64(r); }
    4012            6 :     if (flags2 & (1u << 3))  if (tl_skip_factcheck(r) != 0) return -1;
    4013            6 :     return 0;
    4014              : }
        

Generated by: LCOV version 2.0-1