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 17 : int tl_skip_bool(TlReader *r) {
383 17 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
384 14 : tl_read_uint32(r);
385 14 : return 0;
386 : }
387 :
388 4186 : int tl_skip_string(TlReader *r) {
389 : /* tl_read_string already advances the cursor and handles padding. */
390 4186 : if (!tl_reader_ok(r)) return -1;
391 4182 : char *s = tl_read_string(r);
392 4182 : if (!s) return -1;
393 4182 : free(s);
394 4182 : return 0;
395 : }
396 :
397 2325 : int tl_skip_peer(TlReader *r) {
398 2325 : if (!tl_reader_ok(r) || r->len - r->pos < 12) return -1;
399 2322 : uint32_t crc = tl_read_uint32(r);
400 2322 : (void)tl_read_int64(r);
401 2322 : switch (crc) {
402 2315 : case TL_peerUser:
403 : case TL_peerChat:
404 : case TL_peerChannel:
405 2315 : return 0;
406 7 : default:
407 7 : logger_log(LOG_WARN, "tl_skip_peer: unknown Peer 0x%08x", crc);
408 7 : return -1;
409 : }
410 : }
411 :
412 21 : int tl_skip_notification_sound(TlReader *r) {
413 21 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
414 21 : uint32_t crc = tl_read_uint32(r);
415 21 : switch (crc) {
416 12 : case CRC_notificationSoundDefault:
417 : case CRC_notificationSoundNone:
418 12 : return 0; /* no payload */
419 3 : case CRC_notificationSoundRingtone:
420 3 : if (r->len - r->pos < 8) return -1;
421 3 : tl_read_int64(r); /* id */
422 3 : return 0;
423 3 : case CRC_notificationSoundLocal:
424 3 : if (tl_skip_string(r) != 0) return -1; /* title */
425 3 : if (tl_skip_string(r) != 0) return -1; /* data */
426 3 : return 0;
427 3 : default:
428 3 : logger_log(LOG_WARN, "tl_skip_notification_sound: unknown 0x%08x", crc);
429 3 : 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 853 : int tl_skip_peer_notify_settings(TlReader *r) {
444 853 : if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
445 853 : uint32_t crc = tl_read_uint32(r);
446 853 : if (crc != CRC_peerNotifySettings && crc != CRC_peerNotifySettings2) {
447 3 : logger_log(LOG_WARN,
448 : "tl_skip_peer_notify_settings: unexpected 0x%08x", crc);
449 3 : return -1;
450 : }
451 850 : uint32_t flags = tl_read_uint32(r);
452 :
453 850 : if (flags & (1u << 0)) if (tl_skip_bool(r) != 0) return -1;
454 850 : if (flags & (1u << 1)) if (tl_skip_bool(r) != 0) return -1;
455 850 : if (flags & (1u << 2)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
456 850 : if (flags & (1u << 3)) if (tl_skip_notification_sound(r) != 0) return -1;
457 850 : if (flags & (1u << 4)) if (tl_skip_notification_sound(r) != 0) return -1;
458 850 : if (flags & (1u << 5)) if (tl_skip_notification_sound(r) != 0) return -1;
459 850 : if (flags & (1u << 6)) if (tl_skip_bool(r) != 0) return -1;
460 850 : if (flags & (1u << 7)) if (tl_skip_bool(r) != 0) return -1;
461 850 : if (flags & (1u << 8)) if (tl_skip_notification_sound(r) != 0) return -1;
462 850 : if (flags & (1u << 9)) if (tl_skip_notification_sound(r) != 0) return -1;
463 850 : if (flags & (1u << 10)) if (tl_skip_notification_sound(r) != 0) return -1;
464 850 : 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 11 : int tl_skip_draft_message(TlReader *r) {
755 11 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
756 11 : uint32_t crc = tl_read_uint32(r);
757 11 : if (crc == CRC_draftMessageEmpty) {
758 5 : if (r->len - r->pos < 4) return -1;
759 5 : uint32_t flags = tl_read_uint32(r);
760 5 : if (flags & 1u) {
761 3 : if (r->len - r->pos < 4) return -1;
762 3 : tl_read_int32(r); /* date */
763 : }
764 5 : return 0;
765 : }
766 6 : if (crc == CRC_draftMessage) {
767 3 : if (r->len - r->pos < 4) return -1;
768 2 : uint32_t flags = tl_read_uint32(r);
769 2 : if (flags & (1u << 4)) {
770 0 : if (skip_input_reply_to(r) != 0) return -1;
771 : }
772 2 : 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 3 : logger_log(LOG_WARN, "tl_skip_draft_message: unknown 0x%08x", crc);
784 3 : return -1;
785 : }
786 :
787 27 : int tl_skip_message_entity(TlReader *r) {
788 27 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
789 27 : uint32_t crc = tl_read_uint32(r);
790 :
791 : /* All entities start with offset:int length:int (8 bytes). */
792 27 : switch (crc) {
793 15 : 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 15 : if (r->len - r->pos < 8) return -1;
809 15 : tl_read_int32(r); tl_read_int32(r);
810 15 : 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 3 : case CRC_messageEntityTextUrl:
818 3 : if (r->len - r->pos < 8) return -1;
819 3 : tl_read_int32(r); tl_read_int32(r);
820 3 : return tl_skip_string(r); /* url */
821 :
822 2 : case CRC_messageEntityMentionName:
823 2 : if (r->len - r->pos < 16) return -1;
824 2 : tl_read_int32(r); tl_read_int32(r);
825 2 : tl_read_int64(r); /* user_id */
826 2 : return 0;
827 :
828 2 : case CRC_messageEntityCustomEmoji:
829 2 : if (r->len - r->pos < 16) return -1;
830 2 : tl_read_int32(r); tl_read_int32(r);
831 2 : tl_read_int64(r); /* document_id */
832 2 : return 0;
833 :
834 2 : case CRC_messageEntityBlockquote:
835 : /* flags:# offset:int length:int — no string payload */
836 2 : if (r->len - r->pos < 12) return -1;
837 2 : tl_read_uint32(r); /* flags */
838 2 : tl_read_int32(r); tl_read_int32(r);
839 2 : return 0;
840 :
841 3 : default:
842 3 : logger_log(LOG_WARN, "tl_skip_message_entity: unknown 0x%08x", crc);
843 3 : return -1;
844 : }
845 : }
846 :
847 47 : int tl_skip_message_entities_vector(TlReader *r) {
848 47 : if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
849 47 : uint32_t vec_crc = tl_read_uint32(r);
850 47 : if (vec_crc != TL_vector) {
851 1 : logger_log(LOG_WARN,
852 : "tl_skip_message_entities_vector: expected vector 0x%08x",
853 : vec_crc);
854 1 : return -1;
855 : }
856 46 : uint32_t count = tl_read_uint32(r);
857 68 : for (uint32_t i = 0; i < count; i++) {
858 24 : if (tl_skip_message_entity(r) != 0) return -1;
859 : }
860 44 : 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 14 : static int skip_keyboard_button(TlReader *r) {
903 14 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
904 14 : uint32_t crc = tl_read_uint32(r);
905 14 : switch (crc) {
906 2 : case CRC_keyboardButton:
907 : case CRC_keyboardButtonRequestPhone:
908 : case CRC_keyboardButtonRequestGeoLoc:
909 : case CRC_keyboardButtonGame:
910 : case CRC_keyboardButtonBuy:
911 2 : return tl_skip_string(r); /* text */
912 4 : case CRC_keyboardButtonUrl: {
913 4 : if (tl_skip_string(r) != 0) return -1; /* text */
914 4 : return tl_skip_string(r); /* url */
915 : }
916 7 : case CRC_keyboardButtonCallback: {
917 7 : if (r->len - r->pos < 4) return -1;
918 7 : tl_read_uint32(r); /* flags */
919 7 : if (tl_skip_string(r) != 0) return -1; /* text */
920 7 : 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 1 : default:
964 1 : logger_log(LOG_WARN, "skip_keyboard_button: unknown 0x%08x", crc);
965 1 : return -1;
966 : }
967 : }
968 :
969 : /* Skip a KeyboardButtonRow = crc + Vector<KeyboardButton>. */
970 11 : static int skip_keyboard_button_row(TlReader *r) {
971 11 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
972 11 : uint32_t crc = tl_read_uint32(r);
973 11 : 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 11 : if (r->len - r->pos < 8) return -1;
980 11 : uint32_t vec_crc = tl_read_uint32(r);
981 11 : if (vec_crc != TL_vector) return -1;
982 11 : uint32_t n = tl_read_uint32(r);
983 24 : for (uint32_t i = 0; i < n; i++) {
984 14 : if (skip_keyboard_button(r) != 0) return -1;
985 : }
986 10 : return 0;
987 : }
988 :
989 : /* Skip a Vector<KeyboardButtonRow>. */
990 13 : static int skip_button_row_vector(TlReader *r) {
991 13 : if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
992 13 : uint32_t vec_crc = tl_read_uint32(r);
993 13 : if (vec_crc != TL_vector) return -1;
994 13 : uint32_t n = tl_read_uint32(r);
995 23 : for (uint32_t i = 0; i < n; i++) {
996 11 : if (skip_keyboard_button_row(r) != 0) return -1;
997 : }
998 12 : return 0;
999 : }
1000 :
1001 25 : int tl_skip_reply_markup(TlReader *r) {
1002 25 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1003 24 : uint32_t crc = tl_read_uint32(r);
1004 24 : switch (crc) {
1005 6 : case CRC_replyKeyboardHide:
1006 : case CRC_replyKeyboardForceReply: {
1007 : /* Body: flags:# (+ optional placeholder:flags.3?string for
1008 : * forceReply). */
1009 6 : if (r->len - r->pos < 4) return -1;
1010 6 : uint32_t flags = tl_read_uint32(r);
1011 6 : if (crc == CRC_replyKeyboardForceReply && (flags & (1u << 3))) {
1012 3 : if (tl_skip_string(r) != 0) return -1; /* placeholder */
1013 : }
1014 6 : 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 13 : case CRC_replyInlineMarkup:
1026 13 : return skip_button_row_vector(r);
1027 5 : default:
1028 5 : logger_log(LOG_WARN, "tl_skip_reply_markup: unknown 0x%08x", crc);
1029 5 : return -1;
1030 : }
1031 : }
1032 :
1033 : /* ---- MessageReactions skipper ---- */
1034 :
1035 : /* Skip a Reaction variant. */
1036 19 : static int skip_reaction(TlReader *r) {
1037 19 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1038 19 : uint32_t crc = tl_read_uint32(r);
1039 19 : switch (crc) {
1040 4 : case CRC_reactionEmpty:
1041 : case CRC_reactionPaid:
1042 4 : return 0;
1043 10 : case CRC_reactionEmoji:
1044 10 : return tl_skip_string(r);
1045 3 : case CRC_reactionCustomEmoji:
1046 3 : if (r->len - r->pos < 8) return -1;
1047 3 : tl_read_int64(r); /* document_id */
1048 3 : return 0;
1049 2 : default:
1050 2 : logger_log(LOG_WARN, "skip_reaction: unknown 0x%08x", crc);
1051 2 : return -1;
1052 : }
1053 : }
1054 :
1055 : /* Skip a ReactionCount#a3d1cb80: flags + (chosen_order) + Reaction + count. */
1056 17 : static int skip_reaction_count(TlReader *r) {
1057 17 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1058 17 : uint32_t crc = tl_read_uint32(r);
1059 17 : 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 17 : if (r->len - r->pos < 4) return -1;
1065 17 : uint32_t flags = tl_read_uint32(r);
1066 17 : if (flags & (1u << 0)) {
1067 3 : if (r->len - r->pos < 4) return -1;
1068 3 : tl_read_int32(r); /* chosen_order */
1069 : }
1070 17 : if (skip_reaction(r) != 0) return -1;
1071 15 : if (r->len - r->pos < 4) return -1;
1072 15 : tl_read_int32(r); /* count */
1073 15 : 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 16 : int tl_skip_message_reactions(TlReader *r) {
1114 16 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1115 16 : uint32_t crc = tl_read_uint32(r);
1116 16 : if (crc != CRC_messageReactions) {
1117 0 : logger_log(LOG_WARN, "tl_skip_message_reactions: unknown 0x%08x", crc);
1118 0 : return -1;
1119 : }
1120 16 : if (r->len - r->pos < 4) return -1;
1121 16 : uint32_t flags = tl_read_uint32(r);
1122 : /* results:Vector<ReactionCount> — required. */
1123 16 : if (r->len - r->pos < 8) return -1;
1124 16 : uint32_t vec_crc = tl_read_uint32(r);
1125 16 : if (vec_crc != TL_vector) return -1;
1126 16 : uint32_t n = tl_read_uint32(r);
1127 29 : for (uint32_t i = 0; i < n; i++) {
1128 15 : if (skip_reaction_count(r) != 0) return -1;
1129 : }
1130 14 : if (flags & (1u << 1)) {
1131 : /* recent_reactions:Vector<MessagePeerReaction> */
1132 3 : 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 11 : 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 11 : 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 16 : int tl_skip_message_replies(TlReader *r) {
1161 16 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1162 16 : uint32_t crc = tl_read_uint32(r);
1163 16 : if (crc != CRC_messageReplies) {
1164 3 : logger_log(LOG_WARN, "tl_skip_message_replies: unknown 0x%08x", crc);
1165 3 : return -1;
1166 : }
1167 13 : if (r->len - r->pos < 12) return -1;
1168 13 : uint32_t flags = tl_read_uint32(r);
1169 13 : tl_read_int32(r); /* replies */
1170 13 : tl_read_int32(r); /* replies_pts */
1171 13 : if (flags & (1u << 1)) {
1172 3 : if (r->len - r->pos < 8) return -1;
1173 3 : uint32_t vec_crc = tl_read_uint32(r);
1174 3 : if (vec_crc != TL_vector) return -1;
1175 3 : uint32_t n = tl_read_uint32(r);
1176 7 : for (uint32_t i = 0; i < n; i++) {
1177 4 : if (tl_skip_peer(r) != 0) return -1;
1178 : }
1179 : }
1180 13 : if (flags & (1u << 0)) {
1181 3 : if (r->len - r->pos < 8) return -1;
1182 3 : tl_read_int64(r); /* channel_id */
1183 : }
1184 13 : if (flags & (1u << 2)) {
1185 3 : if (r->len - r->pos < 4) return -1;
1186 3 : tl_read_int32(r); /* max_id */
1187 : }
1188 13 : if (flags & (1u << 3)) {
1189 2 : if (r->len - r->pos < 4) return -1;
1190 2 : tl_read_int32(r); /* read_max_id */
1191 : }
1192 13 : 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 15 : int tl_skip_factcheck(TlReader *r) {
1205 15 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1206 15 : uint32_t crc = tl_read_uint32(r);
1207 15 : if (crc != CRC_factCheck) {
1208 3 : logger_log(LOG_WARN, "tl_skip_factcheck: unknown 0x%08x", crc);
1209 3 : return -1;
1210 : }
1211 12 : if (r->len - r->pos < 4) return -1;
1212 12 : uint32_t flags = tl_read_uint32(r);
1213 12 : if (flags & (1u << 1)) {
1214 9 : if (tl_skip_string(r) != 0) return -1; /* country */
1215 : /* text:TextWithEntities */
1216 9 : if (r->len - r->pos < 4) return -1;
1217 9 : uint32_t twe_crc = tl_read_uint32(r);
1218 9 : 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 9 : if (tl_skip_string(r) != 0) return -1; /* text */
1225 9 : if (tl_skip_message_entities_vector(r) != 0) return -1;
1226 : }
1227 12 : if (r->len - r->pos < 8) return -1;
1228 12 : tl_read_int64(r); /* hash */
1229 12 : 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 13 : int tl_skip_message_fwd_header(TlReader *r) {
1248 13 : if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
1249 12 : uint32_t crc = tl_read_uint32(r);
1250 12 : if (crc != CRC_messageFwdHeader) {
1251 2 : logger_log(LOG_WARN,
1252 : "tl_skip_message_fwd_header: unexpected 0x%08x", crc);
1253 2 : return -1;
1254 : }
1255 10 : uint32_t flags = tl_read_uint32(r);
1256 :
1257 10 : if (flags & (1u << 0)) if (tl_skip_peer(r) != 0) return -1;
1258 10 : if (flags & (1u << 5)) if (tl_skip_string(r) != 0) return -1;
1259 10 : if (r->len - r->pos < 4) return -1;
1260 10 : tl_read_int32(r); /* date */
1261 10 : if (flags & (1u << 2)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
1262 10 : if (flags & (1u << 3)) if (tl_skip_string(r) != 0) return -1;
1263 10 : 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 10 : if (flags & (1u << 8)) if (tl_skip_peer(r) != 0) return -1;
1269 10 : if (flags & (1u << 9)) if (tl_skip_string(r) != 0) return -1;
1270 10 : if (flags & (1u << 10)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
1271 10 : if (flags & (1u << 6)) if (tl_skip_string(r) != 0) return -1;
1272 10 : 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 17 : int tl_skip_message_reply_header(TlReader *r) {
1289 17 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1290 17 : uint32_t crc = tl_read_uint32(r);
1291 17 : if (crc == CRC_messageReplyStoryHeader) {
1292 : /* peer_id:Peer story_id:int */
1293 2 : if (tl_skip_peer(r) != 0) return -1;
1294 2 : if (r->len - r->pos < 4) return -1;
1295 2 : tl_read_int32(r);
1296 2 : return 0;
1297 : }
1298 15 : if (crc != CRC_messageReplyHeader) {
1299 3 : logger_log(LOG_WARN,
1300 : "tl_skip_message_reply_header: unexpected 0x%08x", crc);
1301 3 : return -1;
1302 : }
1303 12 : if (r->len - r->pos < 4) return -1;
1304 12 : uint32_t flags = tl_read_uint32(r);
1305 :
1306 12 : if (flags & (1u << 4)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
1307 12 : if (flags & (1u << 0)) if (tl_skip_peer(r) != 0) return -1;
1308 12 : if (flags & (1u << 5)) if (tl_skip_message_fwd_header(r) != 0) return -1;
1309 12 : if (flags & (1u << 8)) {
1310 1 : if (tl_skip_message_media(r) != 0) return -1;
1311 : }
1312 11 : if (flags & (1u << 1)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
1313 11 : if (flags & (1u << 6)) if (tl_skip_string(r) != 0) return -1;
1314 11 : if (flags & (1u << 7)) if (tl_skip_message_entities_vector(r) != 0) return -1;
1315 11 : if (flags & (1u << 10)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
1316 11 : 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 14 : int tl_skip_photo_size(TlReader *r) {
1329 14 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1330 14 : uint32_t crc = tl_read_uint32(r);
1331 14 : switch (crc) {
1332 4 : case CRC_photoSizeEmpty:
1333 4 : return tl_skip_string(r);
1334 3 : case CRC_photoSize:
1335 3 : if (tl_skip_string(r) != 0) return -1;
1336 3 : if (r->len - r->pos < 12) return -1;
1337 3 : tl_read_int32(r); tl_read_int32(r); tl_read_int32(r);
1338 3 : 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 5 : case CRC_photoStrippedSize:
1345 : case CRC_photoPathSize:
1346 5 : if (tl_skip_string(r) != 0) return -1;
1347 5 : 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 2 : default:
1362 2 : logger_log(LOG_WARN, "tl_skip_photo_size: unknown 0x%08x", crc);
1363 2 : return -1;
1364 : }
1365 : }
1366 :
1367 3 : int tl_skip_photo_size_vector(TlReader *r) {
1368 3 : if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
1369 3 : uint32_t vec_crc = tl_read_uint32(r);
1370 3 : if (vec_crc != TL_vector) return -1;
1371 3 : uint32_t count = tl_read_uint32(r);
1372 6 : for (uint32_t i = 0; i < count; i++) {
1373 3 : if (tl_skip_photo_size(r) != 0) return -1;
1374 : }
1375 3 : 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 7 : static int walk_photo_size_vector(TlReader *r,
1400 : char *best_type, size_t best_type_cap) {
1401 7 : if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
1402 7 : uint32_t vec_crc = tl_read_uint32(r);
1403 7 : if (vec_crc != TL_vector) return -1;
1404 7 : uint32_t count = tl_read_uint32(r);
1405 :
1406 7 : long best_score = -1;
1407 7 : if (best_type && best_type_cap) best_type[0] = '\0';
1408 :
1409 18 : for (uint32_t i = 0; i < count; i++) {
1410 11 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1411 11 : uint32_t crc = tl_read_uint32(r);
1412 :
1413 : /* Capture type:string into a small buffer we may keep as best. */
1414 11 : char type_buf[8] = {0};
1415 11 : size_t type_n = 0;
1416 : {
1417 11 : size_t before = r->pos;
1418 11 : char *s = tl_read_string(r);
1419 11 : if (!s) return -1;
1420 11 : type_n = strlen(s);
1421 11 : if (type_n >= sizeof(type_buf)) type_n = sizeof(type_buf) - 1;
1422 11 : memcpy(type_buf, s, type_n);
1423 11 : type_buf[type_n] = '\0';
1424 11 : free(s);
1425 : (void)before;
1426 : }
1427 :
1428 11 : long score = 0;
1429 11 : switch (crc) {
1430 0 : case CRC_photoSizeEmpty:
1431 0 : score = -1; /* empty — never best */
1432 0 : break;
1433 7 : case CRC_photoSize: {
1434 7 : if (r->len - r->pos < 12) return -1;
1435 7 : int32_t w = tl_read_int32(r);
1436 7 : int32_t h = tl_read_int32(r);
1437 7 : int32_t sz = tl_read_int32(r); (void)sz;
1438 7 : score = (long)w * (long)h;
1439 7 : break;
1440 : }
1441 2 : case CRC_photoCachedSize: {
1442 2 : if (r->len - r->pos < 8) return -1;
1443 2 : int32_t w = tl_read_int32(r);
1444 2 : int32_t h = tl_read_int32(r);
1445 2 : if (tl_skip_string(r) != 0) return -1; /* bytes */
1446 2 : score = (long)w * (long)h;
1447 2 : 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 2 : case CRC_photoSizeProgressive: {
1455 2 : if (r->len - r->pos < 8) return -1;
1456 2 : int32_t w = tl_read_int32(r);
1457 2 : int32_t h = tl_read_int32(r);
1458 2 : if (r->len - r->pos < 8) return -1;
1459 2 : uint32_t vc = tl_read_uint32(r);
1460 2 : if (vc != TL_vector) return -1;
1461 2 : uint32_t nvals = tl_read_uint32(r);
1462 2 : if (r->len - r->pos < nvals * 4ULL) return -1;
1463 6 : for (uint32_t j = 0; j < nvals; j++) tl_read_int32(r);
1464 2 : score = (long)w * (long)h;
1465 2 : 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 11 : if (score > best_score && best_type && best_type_cap) {
1473 4 : best_score = score;
1474 4 : size_t n = type_n < best_type_cap - 1 ? type_n : best_type_cap - 1;
1475 4 : memcpy(best_type, type_buf, n);
1476 4 : best_type[n] = '\0';
1477 : }
1478 : }
1479 7 : return 0;
1480 : }
1481 :
1482 21 : static int photo_full(TlReader *r, MediaInfo *out) {
1483 21 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1484 21 : uint32_t crc = tl_read_uint32(r);
1485 21 : if (crc == CRC_photoEmpty) {
1486 14 : if (r->len - r->pos < 8) return -1;
1487 14 : int64_t id = tl_read_int64(r);
1488 14 : if (out) out->photo_id = id;
1489 14 : return 0;
1490 : }
1491 7 : if (crc != CRC_photo) {
1492 0 : logger_log(LOG_WARN, "tl_skip_photo: unknown 0x%08x", crc);
1493 0 : return -1;
1494 : }
1495 7 : if (r->len - r->pos < 4) return -1;
1496 7 : uint32_t flags = tl_read_uint32(r);
1497 7 : if (r->len - r->pos < 16) return -1;
1498 7 : int64_t id = tl_read_int64(r);
1499 7 : int64_t access = tl_read_int64(r);
1500 7 : 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 7 : size_t fr_len = 0;
1505 7 : uint8_t *fr = tl_read_bytes(r, &fr_len);
1506 7 : if (!fr && fr_len != 0) return -1;
1507 7 : if (out) {
1508 4 : size_t n = fr_len;
1509 4 : if (n > MEDIA_FILE_REF_MAX) n = MEDIA_FILE_REF_MAX;
1510 4 : if (fr) memcpy(out->file_reference, fr, n);
1511 4 : out->file_reference_len = n;
1512 : }
1513 7 : free(fr);
1514 :
1515 7 : if (r->len - r->pos < 4) return -1;
1516 7 : tl_read_int32(r); /* date */
1517 :
1518 7 : 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 7 : if (flags & (1u << 1)) {
1524 0 : if (skip_video_size_vector(r) != 0) return -1;
1525 : }
1526 7 : if (r->len - r->pos < 4) return -1;
1527 7 : int32_t dc = tl_read_int32(r);
1528 7 : if (out) out->dc_id = dc;
1529 7 : return 0;
1530 : }
1531 :
1532 12 : int tl_skip_photo(TlReader *r) {
1533 12 : 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 5 : static int skip_input_sticker_set(TlReader *r) {
1578 5 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1579 5 : uint32_t crc = tl_read_uint32(r);
1580 5 : switch (crc) {
1581 3 : 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 3 : return 0;
1590 1 : case CRC_inputStickerSetID:
1591 1 : if (r->len - r->pos < 16) return -1;
1592 1 : tl_read_int64(r); /* id */
1593 1 : tl_read_int64(r); /* access_hash */
1594 1 : return 0;
1595 1 : case CRC_inputStickerSetShortName:
1596 1 : 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 1 : static int skip_mask_coords(TlReader *r) {
1608 1 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1609 1 : uint32_t crc = tl_read_uint32(r);
1610 1 : if (crc != CRC_maskCoords) {
1611 0 : logger_log(LOG_WARN, "skip_mask_coords: unknown 0x%08x", crc);
1612 0 : return -1;
1613 : }
1614 1 : if (r->len - r->pos < 4 + 3 * 8) return -1;
1615 1 : tl_read_int32(r); /* n */
1616 1 : tl_read_double(r); tl_read_double(r); tl_read_double(r); /* x, y, zoom */
1617 1 : return 0;
1618 : }
1619 :
1620 : /* Skip a single VideoSize variant (document.video_thumbs element). */
1621 1 : static int skip_video_size(TlReader *r) {
1622 1 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1623 1 : uint32_t crc = tl_read_uint32(r);
1624 1 : switch (crc) {
1625 1 : case CRC_videoSize: {
1626 : /* flags:# type:string w:int h:int size:int video_start_ts:flags.0?double */
1627 1 : if (r->len - r->pos < 4) return -1;
1628 1 : uint32_t flags = tl_read_uint32(r);
1629 1 : if (tl_skip_string(r) != 0) return -1;
1630 1 : if (r->len - r->pos < 12) return -1;
1631 1 : tl_read_int32(r); tl_read_int32(r); tl_read_int32(r);
1632 1 : if (flags & (1u << 0)) {
1633 0 : if (r->len - r->pos < 8) return -1;
1634 0 : tl_read_double(r);
1635 : }
1636 1 : 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 1 : static int skip_video_size_vector(TlReader *r) {
1670 1 : if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
1671 1 : uint32_t vec = tl_read_uint32(r);
1672 1 : if (vec != TL_vector) return -1;
1673 1 : uint32_t n = tl_read_uint32(r);
1674 2 : for (uint32_t i = 0; i < n; i++) {
1675 1 : if (skip_video_size(r) != 0) return -1;
1676 : }
1677 1 : return 0;
1678 : }
1679 :
1680 : /* Skip a single DocumentAttribute, optionally extracting the filename. */
1681 33 : static int skip_document_attribute(TlReader *r,
1682 : char *filename_out, size_t fn_cap) {
1683 33 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1684 33 : uint32_t crc = tl_read_uint32(r);
1685 33 : switch (crc) {
1686 2 : case CRC_documentAttributeImageSize:
1687 2 : if (r->len - r->pos < 8) return -1;
1688 2 : tl_read_int32(r); tl_read_int32(r); /* w, h */
1689 2 : return 0;
1690 8 : case CRC_documentAttributeAnimated:
1691 : case CRC_documentAttributeHasStickers:
1692 8 : return 0;
1693 8 : case CRC_documentAttributeFilename: {
1694 8 : size_t before = r->pos;
1695 8 : char *s = tl_read_string(r);
1696 8 : if (!s) { r->pos = before; return -1; }
1697 8 : if (filename_out && fn_cap) {
1698 5 : size_t n = strlen(s);
1699 5 : if (n >= fn_cap) n = fn_cap - 1;
1700 5 : memcpy(filename_out, s, n);
1701 5 : filename_out[n] = '\0';
1702 : }
1703 8 : free(s);
1704 8 : return 0;
1705 : }
1706 4 : case CRC_documentAttributeVideo: {
1707 4 : if (r->len - r->pos < 4) return -1;
1708 4 : uint32_t flags = tl_read_uint32(r);
1709 4 : if (r->len - r->pos < 8 + 4 + 4) return -1;
1710 4 : tl_read_double(r); /* duration */
1711 4 : tl_read_int32(r); tl_read_int32(r); /* w, h */
1712 4 : 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 4 : 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 4 : if (flags & (1u << 5))
1721 0 : if (tl_skip_string(r) != 0) return -1; /* video_codec */
1722 4 : return 0;
1723 : }
1724 6 : case CRC_documentAttributeAudio: {
1725 6 : if (r->len - r->pos < 8) return -1;
1726 6 : uint32_t flags = tl_read_uint32(r);
1727 6 : tl_read_int32(r); /* duration */
1728 6 : if (flags & (1u << 0))
1729 0 : if (tl_skip_string(r) != 0) return -1; /* title */
1730 6 : if (flags & (1u << 1))
1731 0 : if (tl_skip_string(r) != 0) return -1; /* performer */
1732 6 : if (flags & (1u << 2))
1733 0 : if (tl_skip_string(r) != 0) return -1; /* waveform:bytes */
1734 6 : return 0;
1735 : }
1736 4 : case CRC_documentAttributeSticker: {
1737 : /* flags:# mask:flags.1?true alt:string stickerset:InputStickerSet
1738 : * mask_coords:flags.0?MaskCoords */
1739 4 : if (r->len - r->pos < 4) return -1;
1740 4 : uint32_t flags = tl_read_uint32(r);
1741 4 : if (tl_skip_string(r) != 0) return -1; /* alt */
1742 4 : if (skip_input_sticker_set(r) != 0) return -1;
1743 4 : if (flags & (1u << 0)) {
1744 1 : if (skip_mask_coords(r) != 0) return -1;
1745 : }
1746 4 : return 0;
1747 : }
1748 1 : case CRC_documentAttributeCustomEmoji: {
1749 : /* flags:# free:flags.0?true text_color:flags.1?true alt:string
1750 : * stickerset:InputStickerSet */
1751 1 : if (r->len - r->pos < 4) return -1;
1752 1 : (void)tl_read_uint32(r); /* flags */
1753 1 : if (tl_skip_string(r) != 0) return -1; /* alt */
1754 1 : 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 32 : static int document_inner(TlReader *r, MediaInfo *out) {
1765 32 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1766 32 : uint32_t crc = tl_read_uint32(r);
1767 32 : if (crc == CRC_documentEmpty) {
1768 8 : if (r->len - r->pos < 8) return -1;
1769 8 : int64_t id = tl_read_int64(r);
1770 8 : if (out) out->document_id = id;
1771 8 : return 0;
1772 : }
1773 24 : if (crc != CRC_document) {
1774 0 : logger_log(LOG_WARN, "tl_skip_document: unknown 0x%08x", crc);
1775 0 : return -1;
1776 : }
1777 24 : if (r->len - r->pos < 4) return -1;
1778 24 : uint32_t flags = tl_read_uint32(r);
1779 24 : if (r->len - r->pos < 16) return -1;
1780 24 : int64_t id = tl_read_int64(r);
1781 24 : int64_t access = tl_read_int64(r);
1782 24 : if (out) { out->document_id = id; out->access_hash = access; }
1783 :
1784 24 : size_t fr_len = 0;
1785 24 : uint8_t *fr = tl_read_bytes(r, &fr_len);
1786 24 : if (!fr && fr_len != 0) return -1;
1787 24 : if (out) {
1788 20 : size_t n = fr_len;
1789 20 : if (n > MEDIA_FILE_REF_MAX) n = MEDIA_FILE_REF_MAX;
1790 20 : if (fr) memcpy(out->file_reference, fr, n);
1791 20 : out->file_reference_len = n;
1792 : }
1793 24 : free(fr);
1794 :
1795 24 : if (r->len - r->pos < 4) return -1;
1796 24 : tl_read_int32(r); /* date */
1797 :
1798 24 : size_t mime_before = r->pos;
1799 24 : char *mime = tl_read_string(r);
1800 24 : if (!mime) { r->pos = mime_before; return -1; }
1801 24 : if (out) {
1802 20 : size_t n = strlen(mime);
1803 20 : if (n >= sizeof(out->document_mime)) n = sizeof(out->document_mime) - 1;
1804 20 : memcpy(out->document_mime, mime, n);
1805 20 : out->document_mime[n] = '\0';
1806 : }
1807 24 : free(mime);
1808 :
1809 24 : if (r->len - r->pos < 8) return -1;
1810 24 : int64_t size = tl_read_int64(r);
1811 24 : if (out) out->document_size = size;
1812 :
1813 24 : if (flags & (1u << 0)) {
1814 1 : if (tl_skip_photo_size_vector(r) != 0) return -1;
1815 : }
1816 24 : if (flags & (1u << 1)) {
1817 1 : if (skip_video_size_vector(r) != 0) return -1;
1818 : }
1819 :
1820 24 : if (r->len - r->pos < 4) return -1;
1821 24 : int32_t dc = tl_read_int32(r);
1822 24 : if (out) out->dc_id = dc;
1823 :
1824 : /* attributes:Vector<DocumentAttribute> */
1825 24 : if (r->len - r->pos < 8) return -1;
1826 24 : uint32_t vec_crc = tl_read_uint32(r);
1827 24 : if (vec_crc != TL_vector) return -1;
1828 24 : uint32_t n_attrs = tl_read_uint32(r);
1829 54 : for (uint32_t i = 0; i < n_attrs; i++) {
1830 30 : 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 24 : return 0;
1836 : }
1837 :
1838 9 : int tl_skip_document(TlReader *r) {
1839 9 : 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 19 : static int skip_geo_point(TlReader *r) {
1850 19 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1851 19 : uint32_t crc = tl_read_uint32(r);
1852 19 : if (crc == CRC_geoPointEmpty) return 0;
1853 5 : if (crc != CRC_geoPoint) {
1854 0 : logger_log(LOG_WARN, "skip_geo_point: unknown 0x%08x", crc);
1855 0 : return -1;
1856 : }
1857 5 : if (r->len - r->pos < 28) return -1;
1858 5 : uint32_t flags = tl_read_uint32(r);
1859 5 : tl_read_double(r); tl_read_double(r); tl_read_int64(r);
1860 5 : if (flags & 1u) {
1861 0 : if (r->len - r->pos < 4) return -1;
1862 0 : tl_read_int32(r);
1863 : }
1864 5 : 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 138 : static int skip_rich_text(TlReader *r) {
1880 138 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1881 138 : uint32_t crc = tl_read_uint32(r);
1882 138 : switch (crc) {
1883 92 : case CRC_textEmpty:
1884 92 : return 0;
1885 18 : case CRC_textPlain:
1886 18 : return tl_skip_string(r);
1887 13 : 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 13 : return skip_rich_text(r);
1896 2 : case CRC_textUrl: {
1897 2 : if (skip_rich_text(r) != 0) return -1;
1898 2 : if (tl_skip_string(r) != 0) return -1;
1899 2 : if (r->len - r->pos < 8) return -1;
1900 2 : tl_read_int64(r); /* webpage_id */
1901 2 : return 0;
1902 : }
1903 4 : case CRC_textEmail:
1904 : case CRC_textPhone: {
1905 4 : if (skip_rich_text(r) != 0) return -1;
1906 4 : return tl_skip_string(r);
1907 : }
1908 5 : case CRC_textConcat: {
1909 5 : if (r->len - r->pos < 8) return -1;
1910 5 : uint32_t vec = tl_read_uint32(r);
1911 5 : if (vec != TL_vector) return -1;
1912 5 : uint32_t n = tl_read_uint32(r);
1913 27 : for (uint32_t i = 0; i < n; i++) {
1914 22 : if (skip_rich_text(r) != 0) return -1;
1915 : }
1916 5 : return 0;
1917 : }
1918 2 : case CRC_textImage: {
1919 2 : if (r->len - r->pos < 16) return -1;
1920 2 : tl_read_int64(r); /* document_id */
1921 2 : tl_read_int32(r); /* w */
1922 2 : tl_read_int32(r); /* h */
1923 2 : return 0;
1924 : }
1925 2 : case CRC_textAnchor: {
1926 2 : if (skip_rich_text(r) != 0) return -1;
1927 2 : 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 24 : static int skip_page_caption(TlReader *r) {
1937 24 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1938 24 : uint32_t crc = tl_read_uint32(r);
1939 24 : if (crc != CRC_pageCaption) {
1940 0 : logger_log(LOG_WARN, "skip_page_caption: unknown 0x%08x", crc);
1941 0 : return -1;
1942 : }
1943 24 : if (skip_rich_text(r) != 0) return -1;
1944 24 : return skip_rich_text(r);
1945 : }
1946 :
1947 : /* Skip a Vector<PageBlock> — recursive into skip_page_block. */
1948 17 : static int skip_page_block_vector(TlReader *r) {
1949 17 : if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
1950 17 : uint32_t vec = tl_read_uint32(r);
1951 17 : if (vec != TL_vector) return -1;
1952 17 : uint32_t n = tl_read_uint32(r);
1953 26 : for (uint32_t i = 0; i < n; i++) {
1954 9 : if (skip_page_block(r) != 0) return -1;
1955 : }
1956 17 : return 0;
1957 : }
1958 :
1959 : /* Skip a single PageListItem. */
1960 6 : static int skip_page_list_item(TlReader *r) {
1961 6 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1962 6 : uint32_t crc = tl_read_uint32(r);
1963 6 : switch (crc) {
1964 3 : case CRC_pageListItemText:
1965 3 : return skip_rich_text(r);
1966 3 : case CRC_pageListItemBlocks:
1967 3 : 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 5 : static int skip_page_list_ordered_item(TlReader *r) {
1975 5 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1976 5 : uint32_t crc = tl_read_uint32(r);
1977 5 : switch (crc) {
1978 3 : case CRC_pageListOrderedItemText:
1979 3 : if (tl_skip_string(r) != 0) return -1; /* num */
1980 3 : return skip_rich_text(r);
1981 2 : case CRC_pageListOrderedItemBlocks:
1982 2 : if (tl_skip_string(r) != 0) return -1; /* num */
1983 2 : 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 3 : static int skip_page_table_cell(TlReader *r) {
1994 3 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
1995 3 : uint32_t crc = tl_read_uint32(r);
1996 3 : if (crc != CRC_pageTableCell) {
1997 0 : logger_log(LOG_WARN, "skip_page_table_cell: unknown 0x%08x", crc);
1998 0 : return -1;
1999 : }
2000 3 : if (r->len - r->pos < 4) return -1;
2001 3 : uint32_t flags = tl_read_uint32(r);
2002 3 : if (flags & (1u << 7)) {
2003 3 : if (skip_rich_text(r) != 0) return -1;
2004 : }
2005 3 : if (flags & (1u << 1)) {
2006 2 : if (r->len - r->pos < 4) return -1;
2007 2 : tl_read_int32(r); /* colspan */
2008 : }
2009 3 : if (flags & (1u << 2)) {
2010 2 : if (r->len - r->pos < 4) return -1;
2011 2 : tl_read_int32(r); /* rowspan */
2012 : }
2013 3 : return 0;
2014 : }
2015 :
2016 : /* pageTableRow#e0c0c5e5 cells:Vector<PageTableCell>. */
2017 3 : static int skip_page_table_row(TlReader *r) {
2018 3 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2019 3 : uint32_t crc = tl_read_uint32(r);
2020 3 : if (crc != CRC_pageTableRow) {
2021 0 : logger_log(LOG_WARN, "skip_page_table_row: unknown 0x%08x", crc);
2022 0 : return -1;
2023 : }
2024 3 : if (r->len - r->pos < 8) return -1;
2025 3 : uint32_t vec = tl_read_uint32(r);
2026 3 : if (vec != TL_vector) return -1;
2027 3 : uint32_t n = tl_read_uint32(r);
2028 6 : for (uint32_t i = 0; i < n; i++) {
2029 3 : if (skip_page_table_cell(r) != 0) return -1;
2030 : }
2031 3 : 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 3 : static int skip_page_related_article(TlReader *r) {
2039 3 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2040 3 : uint32_t crc = tl_read_uint32(r);
2041 3 : 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 3 : if (r->len - r->pos < 4) return -1;
2047 3 : uint32_t flags = tl_read_uint32(r);
2048 3 : if (tl_skip_string(r) != 0) return -1; /* url */
2049 3 : if (r->len - r->pos < 8) return -1;
2050 3 : tl_read_int64(r); /* webpage_id */
2051 3 : if (flags & (1u << 0)) if (tl_skip_string(r) != 0) return -1;
2052 3 : if (flags & (1u << 1)) if (tl_skip_string(r) != 0) return -1;
2053 3 : if (flags & (1u << 2)) {
2054 2 : if (r->len - r->pos < 8) return -1;
2055 2 : tl_read_int64(r); /* photo_id */
2056 : }
2057 3 : if (flags & (1u << 3)) if (tl_skip_string(r) != 0) return -1;
2058 3 : if (flags & (1u << 4)) {
2059 2 : if (r->len - r->pos < 4) return -1;
2060 2 : tl_read_int32(r); /* published_date */
2061 : }
2062 3 : return 0;
2063 : }
2064 :
2065 : /* Skip a single PageBlock — full coverage of the PageBlock tree. */
2066 90 : static int skip_page_block(TlReader *r) {
2067 90 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2068 90 : uint32_t crc = tl_read_uint32(r);
2069 90 : switch (crc) {
2070 13 : case CRC_pageBlockUnsupported:
2071 : case CRC_pageBlockDivider:
2072 13 : return 0;
2073 15 : 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 15 : return skip_rich_text(r);
2081 3 : case CRC_pageBlockAnchor:
2082 3 : return tl_skip_string(r);
2083 2 : case CRC_pageBlockPreformatted: {
2084 2 : if (skip_rich_text(r) != 0) return -1;
2085 2 : return tl_skip_string(r); /* language */
2086 : }
2087 2 : case CRC_pageBlockAuthorDate: {
2088 2 : if (skip_rich_text(r) != 0) return -1; /* author */
2089 2 : if (r->len - r->pos < 4) return -1;
2090 2 : tl_read_int32(r); /* published_date */
2091 2 : return 0;
2092 : }
2093 5 : case CRC_pageBlockBlockquote:
2094 : case CRC_pageBlockPullquote: {
2095 5 : if (skip_rich_text(r) != 0) return -1; /* text */
2096 5 : return skip_rich_text(r); /* caption */
2097 : }
2098 3 : case CRC_pageBlockPhoto: {
2099 3 : if (r->len - r->pos < 4) return -1;
2100 3 : uint32_t flags = tl_read_uint32(r);
2101 3 : if (r->len - r->pos < 8) return -1;
2102 3 : tl_read_int64(r); /* photo_id */
2103 3 : if (skip_page_caption(r) != 0) return -1;
2104 3 : 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 3 : return 0;
2110 : }
2111 3 : case CRC_pageBlockVideo: {
2112 3 : if (r->len - r->pos < 12) return -1;
2113 3 : tl_read_uint32(r); /* flags (autoplay/loop only) */
2114 3 : tl_read_int64(r); /* video_id */
2115 3 : return skip_page_caption(r);
2116 : }
2117 3 : case CRC_pageBlockAudio: {
2118 3 : if (r->len - r->pos < 8) return -1;
2119 3 : tl_read_int64(r); /* audio_id */
2120 3 : return skip_page_caption(r);
2121 : }
2122 3 : case CRC_pageBlockCover:
2123 3 : return skip_page_block(r);
2124 3 : case CRC_pageBlockChannel:
2125 3 : return tl_skip_chat(r);
2126 3 : case CRC_pageBlockMap: {
2127 3 : if (skip_geo_point(r) != 0) return -1;
2128 3 : if (r->len - r->pos < 12) return -1;
2129 3 : tl_read_int32(r); /* zoom */
2130 3 : tl_read_int32(r); tl_read_int32(r); /* w, h */
2131 3 : return skip_page_caption(r);
2132 : }
2133 5 : case CRC_pageBlockList: {
2134 5 : if (r->len - r->pos < 8) return -1;
2135 5 : uint32_t vec = tl_read_uint32(r);
2136 5 : if (vec != TL_vector) return -1;
2137 5 : uint32_t n = tl_read_uint32(r);
2138 11 : for (uint32_t i = 0; i < n; i++) {
2139 6 : if (skip_page_list_item(r) != 0) return -1;
2140 : }
2141 5 : return 0;
2142 : }
2143 5 : case CRC_pageBlockOrderedList: {
2144 5 : if (r->len - r->pos < 8) return -1;
2145 5 : uint32_t vec = tl_read_uint32(r);
2146 5 : if (vec != TL_vector) return -1;
2147 5 : uint32_t n = tl_read_uint32(r);
2148 10 : for (uint32_t i = 0; i < n; i++) {
2149 5 : if (skip_page_list_ordered_item(r) != 0) return -1;
2150 : }
2151 5 : return 0;
2152 : }
2153 6 : case CRC_pageBlockCollage:
2154 : case CRC_pageBlockSlideshow: {
2155 6 : if (skip_page_block_vector(r) != 0) return -1;
2156 6 : return skip_page_caption(r);
2157 : }
2158 3 : case CRC_pageBlockDetails: {
2159 3 : if (r->len - r->pos < 4) return -1;
2160 3 : tl_read_uint32(r); /* flags (open only) */
2161 3 : if (skip_page_block_vector(r) != 0) return -1;
2162 3 : return skip_rich_text(r); /* title */
2163 : }
2164 3 : case CRC_pageBlockRelatedArticles: {
2165 3 : if (skip_rich_text(r) != 0) return -1; /* title */
2166 3 : if (r->len - r->pos < 8) return -1;
2167 3 : uint32_t vec = tl_read_uint32(r);
2168 3 : if (vec != TL_vector) return -1;
2169 3 : uint32_t n = tl_read_uint32(r);
2170 6 : for (uint32_t i = 0; i < n; i++) {
2171 3 : if (skip_page_related_article(r) != 0) return -1;
2172 : }
2173 3 : return 0;
2174 : }
2175 3 : case CRC_pageBlockTable: {
2176 3 : if (r->len - r->pos < 4) return -1;
2177 3 : tl_read_uint32(r); /* flags (bordered/striped) */
2178 3 : if (skip_rich_text(r) != 0) return -1; /* title */
2179 3 : if (r->len - r->pos < 8) return -1;
2180 3 : uint32_t vec = tl_read_uint32(r);
2181 3 : if (vec != TL_vector) return -1;
2182 3 : uint32_t n = tl_read_uint32(r);
2183 6 : for (uint32_t i = 0; i < n; i++) {
2184 3 : if (skip_page_table_row(r) != 0) return -1;
2185 : }
2186 3 : return 0;
2187 : }
2188 3 : case CRC_pageBlockEmbed: {
2189 3 : if (r->len - r->pos < 4) return -1;
2190 3 : uint32_t flags = tl_read_uint32(r);
2191 3 : if (flags & (1u << 1)) if (tl_skip_string(r) != 0) return -1;
2192 3 : if (flags & (1u << 2)) if (tl_skip_string(r) != 0) return -1;
2193 3 : 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 3 : 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 3 : return skip_page_caption(r);
2202 : }
2203 3 : case CRC_pageBlockEmbedPost: {
2204 3 : if (tl_skip_string(r) != 0) return -1; /* url */
2205 3 : if (r->len - r->pos < 16) return -1;
2206 3 : tl_read_int64(r); /* webpage_id */
2207 3 : tl_read_int64(r); /* author_photo_id */
2208 3 : if (tl_skip_string(r) != 0) return -1; /* author */
2209 3 : if (r->len - r->pos < 4) return -1;
2210 3 : tl_read_int32(r); /* date */
2211 3 : if (skip_page_block_vector(r) != 0) return -1;
2212 3 : return skip_page_caption(r);
2213 : }
2214 1 : default:
2215 1 : logger_log(LOG_WARN, "skip_page_block: unsupported 0x%08x", crc);
2216 1 : 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 30 : static int skip_page(TlReader *r) {
2224 30 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2225 30 : uint32_t crc = tl_read_uint32(r);
2226 30 : if (crc != CRC_page) {
2227 0 : logger_log(LOG_WARN, "skip_page: unknown Page variant 0x%08x", crc);
2228 0 : return -1;
2229 : }
2230 30 : if (r->len - r->pos < 4) return -1;
2231 30 : uint32_t flags = tl_read_uint32(r);
2232 30 : if (tl_skip_string(r) != 0) return -1; /* url */
2233 :
2234 : /* blocks:Vector<PageBlock> */
2235 30 : if (r->len - r->pos < 8) return -1;
2236 30 : uint32_t vec = tl_read_uint32(r);
2237 30 : if (vec != TL_vector) return -1;
2238 30 : uint32_t n = tl_read_uint32(r);
2239 107 : for (uint32_t i = 0; i < n; i++) {
2240 78 : if (skip_page_block(r) != 0) return -1;
2241 : }
2242 :
2243 : /* photos:Vector<Photo> */
2244 29 : if (r->len - r->pos < 8) return -1;
2245 29 : vec = tl_read_uint32(r);
2246 29 : if (vec != TL_vector) return -1;
2247 29 : n = tl_read_uint32(r);
2248 29 : 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 29 : if (r->len - r->pos < 8) return -1;
2254 29 : vec = tl_read_uint32(r);
2255 29 : if (vec != TL_vector) return -1;
2256 29 : n = tl_read_uint32(r);
2257 29 : 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 29 : if (flags & (1u << 3)) {
2263 0 : if (r->len - r->pos < 4) return -1;
2264 0 : tl_read_int32(r);
2265 : }
2266 29 : 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 8 : static int skip_webpage_attributes_vector(TlReader *r) {
2380 8 : if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
2381 8 : uint32_t vec = tl_read_uint32(r);
2382 8 : if (vec != TL_vector) return -1;
2383 8 : uint32_t n = tl_read_uint32(r);
2384 17 : for (uint32_t i = 0; i < n; i++) {
2385 10 : if (r->len - r->pos < 4) return -1;
2386 10 : uint32_t crc = tl_read_uint32(r);
2387 10 : switch (crc) {
2388 3 : case CRC_webPageAttributeTheme: {
2389 : /* flags:# documents:flags.0?Vector<Document>
2390 : * settings:flags.1?ThemeSettings */
2391 3 : if (r->len - r->pos < 4) return -1;
2392 3 : uint32_t flags = tl_read_uint32(r);
2393 3 : if (flags & (1u << 0)) {
2394 1 : if (r->len - r->pos < 8) return -1;
2395 1 : uint32_t dvec = tl_read_uint32(r);
2396 1 : if (dvec != TL_vector) return -1;
2397 1 : uint32_t dn = tl_read_uint32(r);
2398 1 : for (uint32_t j = 0; j < dn; j++) {
2399 0 : if (tl_skip_document(r) != 0) return -1;
2400 : }
2401 : }
2402 3 : if (flags & (1u << 1)) {
2403 0 : if (skip_theme_settings(r) != 0) return -1;
2404 : }
2405 3 : break;
2406 : }
2407 2 : case CRC_webPageAttributeStory: {
2408 : /* flags:# peer:Peer id:int story:flags.0?StoryItem */
2409 2 : if (r->len - r->pos < 4) return -1;
2410 2 : uint32_t flags = tl_read_uint32(r);
2411 2 : if (tl_skip_peer(r) != 0) return -1;
2412 2 : if (r->len - r->pos < 4) return -1;
2413 2 : tl_read_int32(r); /* id */
2414 2 : if (flags & (1u << 0)) {
2415 1 : if (skip_story_item(r) != 0) return -1;
2416 : }
2417 2 : break;
2418 : }
2419 4 : case CRC_webPageAttributeStickerSet: {
2420 : /* flags:# emojis:flags.0?true text_color:flags.1?true
2421 : * stickers:Vector<Document> */
2422 4 : if (r->len - r->pos < 4) return -1;
2423 4 : (void)tl_read_uint32(r); /* flags */
2424 4 : if (r->len - r->pos < 8) return -1;
2425 4 : uint32_t svec = tl_read_uint32(r);
2426 4 : if (svec != TL_vector) return -1;
2427 4 : uint32_t sn = tl_read_uint32(r);
2428 6 : for (uint32_t j = 0; j < sn; j++) {
2429 2 : if (tl_skip_document(r) != 0) return -1;
2430 : }
2431 4 : break;
2432 : }
2433 1 : default:
2434 1 : logger_log(LOG_WARN,
2435 : "skip_webpage_attributes: unknown variant 0x%08x", crc);
2436 1 : return -1;
2437 : }
2438 : }
2439 7 : return 0;
2440 : }
2441 :
2442 : /* Skip a WebPage variant. */
2443 52 : static int skip_webpage(TlReader *r) {
2444 52 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2445 52 : uint32_t crc = tl_read_uint32(r);
2446 52 : switch (crc) {
2447 3 : case CRC_webPageEmpty: {
2448 3 : if (r->len - r->pos < 12) return -1;
2449 3 : uint32_t flags = tl_read_uint32(r);
2450 3 : tl_read_int64(r); /* id */
2451 3 : if (flags & (1u << 0))
2452 1 : if (tl_skip_string(r) != 0) return -1;
2453 3 : return 0;
2454 : }
2455 3 : case CRC_webPagePending: {
2456 3 : if (r->len - r->pos < 12) return -1;
2457 3 : uint32_t flags = tl_read_uint32(r);
2458 3 : tl_read_int64(r);
2459 3 : if (flags & (1u << 0))
2460 1 : if (tl_skip_string(r) != 0) return -1;
2461 3 : if (r->len - r->pos < 4) return -1;
2462 3 : tl_read_int32(r); /* date */
2463 3 : return 0;
2464 : }
2465 2 : case CRC_webPageNotModified: {
2466 2 : if (r->len - r->pos < 4) return -1;
2467 2 : uint32_t flags = tl_read_uint32(r);
2468 2 : if (flags & (1u << 0)) {
2469 2 : if (r->len - r->pos < 4) return -1;
2470 2 : tl_read_int32(r); /* cached_page_views */
2471 : }
2472 2 : return 0;
2473 : }
2474 42 : 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 42 : if (r->len - r->pos < 16) return -1;
2485 42 : uint32_t flags = tl_read_uint32(r);
2486 42 : tl_read_int64(r); /* id */
2487 42 : if (tl_skip_string(r) != 0) return -1; /* url */
2488 42 : if (tl_skip_string(r) != 0) return -1; /* display_url */
2489 42 : if (r->len - r->pos < 4) return -1;
2490 42 : tl_read_int32(r); /* hash */
2491 42 : if (flags & (1u << 0))
2492 2 : if (tl_skip_string(r) != 0) return -1;
2493 42 : if (flags & (1u << 1))
2494 3 : if (tl_skip_string(r) != 0) return -1;
2495 42 : if (flags & (1u << 2))
2496 3 : if (tl_skip_string(r) != 0) return -1;
2497 42 : if (flags & (1u << 3))
2498 2 : if (tl_skip_string(r) != 0) return -1;
2499 42 : if (flags & (1u << 4)) {
2500 0 : if (photo_full(r, NULL) != 0) return -1;
2501 : }
2502 42 : if (flags & (1u << 5)) {
2503 2 : if (tl_skip_string(r) != 0) return -1;
2504 2 : if (tl_skip_string(r) != 0) return -1;
2505 : }
2506 42 : if (flags & (1u << 6)) {
2507 2 : if (r->len - r->pos < 8) return -1;
2508 2 : tl_read_int32(r); tl_read_int32(r);
2509 : }
2510 42 : if (flags & (1u << 7)) {
2511 2 : if (r->len - r->pos < 4) return -1;
2512 2 : tl_read_int32(r);
2513 : }
2514 42 : if (flags & (1u << 8))
2515 2 : if (tl_skip_string(r) != 0) return -1;
2516 42 : if (flags & (1u << 9)) {
2517 : /* document:Document — reuse tl_skip_document. */
2518 0 : if (tl_skip_document(r) != 0) return -1;
2519 : }
2520 42 : if (flags & (1u << 10)) {
2521 30 : if (skip_page(r) != 0) return -1;
2522 : }
2523 41 : if (flags & (1u << 12)) {
2524 8 : if (skip_webpage_attributes_vector(r) != 0) return -1;
2525 : }
2526 40 : return 0;
2527 : }
2528 2 : default:
2529 2 : logger_log(LOG_WARN, "skip_webpage: unknown 0x%08x", crc);
2530 2 : return -1;
2531 : }
2532 : }
2533 :
2534 : /* Skip a textWithEntities#751f3146: text:string + Vector<MessageEntity>. */
2535 19 : static int skip_text_with_entities(TlReader *r) {
2536 19 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2537 19 : uint32_t crc = tl_read_uint32(r);
2538 19 : 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 19 : if (tl_skip_string(r) != 0) return -1;
2543 19 : return tl_skip_message_entities_vector(r);
2544 : }
2545 :
2546 : /* Skip a Poll#58747131 object. */
2547 10 : static int skip_poll(TlReader *r) {
2548 10 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2549 10 : uint32_t crc = tl_read_uint32(r);
2550 10 : if (crc != CRC_poll) {
2551 0 : logger_log(LOG_WARN, "skip_poll: unknown 0x%08x", crc);
2552 0 : return -1;
2553 : }
2554 10 : if (r->len - r->pos < 12) return -1;
2555 10 : uint32_t flags = tl_read_uint32(r);
2556 10 : tl_read_int64(r); /* id */
2557 10 : if (skip_text_with_entities(r) != 0) return -1; /* question */
2558 :
2559 : /* answers:Vector<PollAnswer> */
2560 10 : if (r->len - r->pos < 8) return -1;
2561 10 : uint32_t vec_crc = tl_read_uint32(r);
2562 10 : if (vec_crc != TL_vector) return -1;
2563 10 : uint32_t n_answers = tl_read_uint32(r);
2564 19 : for (uint32_t i = 0; i < n_answers; i++) {
2565 9 : if (r->len - r->pos < 4) return -1;
2566 9 : uint32_t pa_crc = tl_read_uint32(r);
2567 9 : 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 9 : if (skip_text_with_entities(r) != 0) return -1;
2572 9 : if (tl_skip_string(r) != 0) return -1; /* option:bytes */
2573 : }
2574 10 : if (flags & (1u << 4)) {
2575 0 : if (r->len - r->pos < 4) return -1;
2576 0 : tl_read_int32(r); /* close_period */
2577 : }
2578 10 : if (flags & (1u << 5)) {
2579 1 : if (r->len - r->pos < 4) return -1;
2580 1 : tl_read_int32(r); /* close_date */
2581 : }
2582 10 : return 0;
2583 : }
2584 :
2585 : /* Skip a PollAnswerVoters#3b6ddad2. */
2586 7 : static int skip_poll_answer_voters(TlReader *r) {
2587 7 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2588 7 : uint32_t crc = tl_read_uint32(r);
2589 7 : if (crc != CRC_pollAnswerVoters) {
2590 2 : logger_log(LOG_WARN, "skip_poll_answer_voters: 0x%08x", crc);
2591 2 : return -1;
2592 : }
2593 5 : if (r->len - r->pos < 4) return -1;
2594 5 : tl_read_uint32(r); /* flags */
2595 5 : if (tl_skip_string(r) != 0) return -1; /* option:bytes */
2596 5 : if (r->len - r->pos < 4) return -1;
2597 5 : tl_read_int32(r); /* voters */
2598 5 : return 0;
2599 : }
2600 :
2601 : /* Skip a PollResults#7adc669d object. */
2602 10 : static int skip_poll_results(TlReader *r) {
2603 10 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2604 10 : uint32_t crc = tl_read_uint32(r);
2605 10 : if (crc != CRC_pollResults) {
2606 0 : logger_log(LOG_WARN, "skip_poll_results: 0x%08x", crc);
2607 0 : return -1;
2608 : }
2609 10 : if (r->len - r->pos < 4) return -1;
2610 10 : uint32_t flags = tl_read_uint32(r);
2611 10 : if (flags & (1u << 1)) {
2612 5 : if (r->len - r->pos < 8) return -1;
2613 5 : uint32_t vc = tl_read_uint32(r);
2614 5 : if (vc != TL_vector) return -1;
2615 5 : uint32_t n = tl_read_uint32(r);
2616 10 : for (uint32_t i = 0; i < n; i++) {
2617 7 : if (skip_poll_answer_voters(r) != 0) return -1;
2618 : }
2619 : }
2620 8 : if (flags & (1u << 2)) {
2621 3 : if (r->len - r->pos < 4) return -1;
2622 3 : tl_read_int32(r); /* total_voters */
2623 : }
2624 8 : if (flags & (1u << 3)) {
2625 3 : if (r->len - r->pos < 8) return -1;
2626 3 : uint32_t vc = tl_read_uint32(r);
2627 3 : if (vc != TL_vector) return -1;
2628 3 : uint32_t n = tl_read_uint32(r);
2629 6 : for (uint32_t i = 0; i < n; i++) {
2630 3 : if (tl_skip_peer(r) != 0) return -1;
2631 : }
2632 : }
2633 8 : if (flags & (1u << 4)) {
2634 3 : if (tl_skip_string(r) != 0) return -1; /* solution */
2635 3 : if (tl_skip_message_entities_vector(r) != 0) return -1;
2636 : }
2637 8 : 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 9 : static int skip_game(TlReader *r) {
2645 9 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2646 9 : uint32_t crc = tl_read_uint32(r);
2647 9 : if (crc != CRC_game) {
2648 3 : logger_log(LOG_WARN, "skip_game: unexpected 0x%08x", crc);
2649 3 : return -1;
2650 : }
2651 6 : if (r->len - r->pos < 4 + 8 + 8) return -1;
2652 6 : uint32_t flags = tl_read_uint32(r);
2653 6 : tl_read_int64(r); /* id */
2654 6 : tl_read_int64(r); /* access_hash */
2655 6 : if (tl_skip_string(r) != 0) return -1; /* short_name */
2656 6 : if (tl_skip_string(r) != 0) return -1; /* title */
2657 6 : if (tl_skip_string(r) != 0) return -1; /* description */
2658 6 : if (tl_skip_photo(r) != 0) return -1; /* photo */
2659 6 : if (flags & (1u << 0)) {
2660 3 : if (tl_skip_document(r) != 0) return -1; /* document */
2661 : }
2662 6 : 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 14 : static int skip_message_extended_media(TlReader *r) {
2671 14 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2672 14 : uint32_t crc = tl_read_uint32(r);
2673 14 : switch (crc) {
2674 8 : case CRC_messageExtendedMediaPreview: {
2675 8 : if (r->len - r->pos < 4) return -1;
2676 8 : uint32_t flags = tl_read_uint32(r);
2677 8 : if (flags & (1u << 0)) {
2678 3 : if (r->len - r->pos < 8) return -1;
2679 3 : tl_read_int32(r); tl_read_int32(r); /* w, h */
2680 : }
2681 8 : if (flags & (1u << 1)) {
2682 3 : if (tl_skip_photo_size(r) != 0) return -1;
2683 : }
2684 8 : if (flags & (1u << 2)) {
2685 3 : if (r->len - r->pos < 4) return -1;
2686 3 : tl_read_int32(r); /* video_duration */
2687 : }
2688 8 : return 0;
2689 : }
2690 3 : case CRC_messageExtendedMedia:
2691 : /* Recurse into the wrapped MessageMedia. The inner metadata is
2692 : * discarded — paid-media iteration only needs the outer kind. */
2693 3 : return tl_skip_message_media_ex(r, NULL);
2694 3 : default:
2695 3 : logger_log(LOG_WARN,
2696 : "skip_message_extended_media: unknown 0x%08x", crc);
2697 3 : 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 9 : static int skip_web_document(TlReader *r) {
2708 9 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2709 8 : uint32_t crc = tl_read_uint32(r);
2710 : int has_access_hash;
2711 8 : switch (crc) {
2712 3 : case CRC_webDocument: has_access_hash = 1; break;
2713 3 : case CRC_webDocumentNoProxy: has_access_hash = 0; break;
2714 2 : default:
2715 2 : logger_log(LOG_WARN, "skip_web_document: unknown 0x%08x", crc);
2716 2 : return -1;
2717 : }
2718 6 : if (tl_skip_string(r) != 0) return -1; /* url */
2719 6 : if (has_access_hash) {
2720 3 : if (r->len - r->pos < 8) return -1;
2721 3 : tl_read_int64(r); /* access_hash */
2722 : }
2723 6 : if (r->len - r->pos < 4) return -1;
2724 6 : tl_read_int32(r); /* size */
2725 6 : if (tl_skip_string(r) != 0) return -1; /* mime_type */
2726 : /* attributes:Vector<DocumentAttribute> */
2727 6 : if (r->len - r->pos < 8) return -1;
2728 6 : uint32_t vc = tl_read_uint32(r);
2729 6 : if (vc != TL_vector) return -1;
2730 6 : uint32_t n = tl_read_uint32(r);
2731 9 : for (uint32_t i = 0; i < n; i++) {
2732 3 : if (skip_document_attribute(r, NULL, 0) != 0) return -1;
2733 : }
2734 6 : 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 4 : static int skip_story_fwd_header(TlReader *r) {
2742 4 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2743 4 : uint32_t crc = tl_read_uint32(r);
2744 4 : if (crc != CRC_storyFwdHeader) {
2745 0 : logger_log(LOG_WARN, "skip_story_fwd_header: unknown 0x%08x", crc);
2746 0 : return -1;
2747 : }
2748 4 : if (r->len - r->pos < 4) return -1;
2749 4 : uint32_t flags = tl_read_uint32(r);
2750 4 : if (flags & (1u << 0))
2751 2 : if (tl_skip_peer(r) != 0) return -1; /* from */
2752 4 : if (flags & (1u << 1))
2753 2 : if (tl_skip_string(r) != 0) return -1; /* from_name */
2754 4 : if (flags & (1u << 2)) {
2755 4 : if (r->len - r->pos < 4) return -1;
2756 4 : tl_read_int32(r); /* story_id */
2757 : }
2758 4 : 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 15 : static int skip_media_area_coordinates(TlReader *r) {
2766 15 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2767 15 : uint32_t crc = tl_read_uint32(r);
2768 15 : 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 15 : if (r->len - r->pos < 4 + 5 * 8) return -1;
2774 15 : uint32_t flags = tl_read_uint32(r);
2775 90 : for (int i = 0; i < 5; i++) tl_read_double(r); /* x, y, w, h, rot */
2776 15 : if (flags & (1u << 0)) {
2777 0 : if (r->len - r->pos < 8) return -1;
2778 0 : tl_read_double(r); /* radius */
2779 : }
2780 15 : 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 2 : static int skip_geo_point_address(TlReader *r) {
2788 2 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2789 2 : uint32_t crc = tl_read_uint32(r);
2790 2 : 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 2 : if (r->len - r->pos < 4) return -1;
2796 2 : uint32_t flags = tl_read_uint32(r);
2797 2 : if (tl_skip_string(r) != 0) return -1; /* country_iso2 */
2798 2 : if (flags & (1u << 0))
2799 2 : if (tl_skip_string(r) != 0) return -1; /* state */
2800 2 : if (flags & (1u << 1))
2801 2 : if (tl_skip_string(r) != 0) return -1; /* city */
2802 2 : if (flags & (1u << 2))
2803 2 : if (tl_skip_string(r) != 0) return -1; /* street */
2804 2 : return 0;
2805 : }
2806 :
2807 : /* ---- MediaArea ---- dispatch over 7 known variants. */
2808 17 : static int skip_media_area(TlReader *r) {
2809 17 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2810 17 : uint32_t crc = tl_read_uint32(r);
2811 17 : switch (crc) {
2812 2 : case CRC_mediaAreaVenue:
2813 2 : if (skip_media_area_coordinates(r) != 0) return -1;
2814 2 : if (skip_geo_point(r) != 0) return -1;
2815 2 : if (tl_skip_string(r) != 0) return -1; /* title */
2816 2 : if (tl_skip_string(r) != 0) return -1; /* address */
2817 2 : if (tl_skip_string(r) != 0) return -1; /* provider */
2818 2 : if (tl_skip_string(r) != 0) return -1; /* venue_id */
2819 2 : if (tl_skip_string(r) != 0) return -1; /* venue_type */
2820 2 : return 0;
2821 2 : case CRC_mediaAreaGeoPoint: {
2822 2 : if (r->len - r->pos < 4) return -1;
2823 2 : uint32_t flags = tl_read_uint32(r);
2824 2 : if (skip_media_area_coordinates(r) != 0) return -1;
2825 2 : if (skip_geo_point(r) != 0) return -1;
2826 2 : if (flags & (1u << 0))
2827 2 : if (skip_geo_point_address(r) != 0) return -1;
2828 2 : return 0;
2829 : }
2830 2 : case CRC_mediaAreaSuggestedReaction: {
2831 2 : if (r->len - r->pos < 4) return -1;
2832 2 : tl_read_uint32(r); /* flags */
2833 2 : if (skip_media_area_coordinates(r) != 0) return -1;
2834 2 : return skip_reaction(r);
2835 : }
2836 2 : case CRC_mediaAreaChannelPost:
2837 2 : if (skip_media_area_coordinates(r) != 0) return -1;
2838 2 : if (r->len - r->pos < 12) return -1;
2839 2 : tl_read_int64(r); /* channel_id */
2840 2 : tl_read_int32(r); /* msg_id */
2841 2 : return 0;
2842 3 : case CRC_mediaAreaUrl:
2843 3 : if (skip_media_area_coordinates(r) != 0) return -1;
2844 3 : return tl_skip_string(r); /* url */
2845 2 : case CRC_mediaAreaWeather:
2846 2 : if (skip_media_area_coordinates(r) != 0) return -1;
2847 2 : if (tl_skip_string(r) != 0) return -1; /* emoji */
2848 2 : if (r->len - r->pos < 8 + 4) return -1;
2849 2 : tl_read_double(r); /* temperature_c */
2850 2 : tl_read_int32(r); /* color */
2851 2 : return 0;
2852 2 : case CRC_mediaAreaStarGift:
2853 2 : if (skip_media_area_coordinates(r) != 0) return -1;
2854 2 : return tl_skip_string(r); /* slug */
2855 2 : default:
2856 2 : logger_log(LOG_WARN, "skip_media_area: unknown 0x%08x", crc);
2857 2 : return -1;
2858 : }
2859 : }
2860 :
2861 : /* ---- PrivacyRule ---- 12 variants; most are CRC-only. */
2862 8 : static int skip_privacy_rule(TlReader *r) {
2863 8 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2864 8 : uint32_t crc = tl_read_uint32(r);
2865 8 : switch (crc) {
2866 3 : 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 3 : return 0;
2875 3 : case CRC_privacyValueAllowUsers:
2876 : case CRC_privacyValueDisallowUsers:
2877 : case CRC_privacyValueAllowChatParticipants:
2878 : case CRC_privacyValueDisallowChatParticipants: {
2879 : /* Vector<long> */
2880 3 : if (r->len - r->pos < 8) return -1;
2881 3 : uint32_t vc = tl_read_uint32(r);
2882 3 : if (vc != TL_vector) return -1;
2883 3 : uint32_t n = tl_read_uint32(r);
2884 3 : if (r->len - r->pos < (size_t)n * 8) return -1;
2885 9 : for (uint32_t i = 0; i < n; i++) tl_read_int64(r);
2886 3 : return 0;
2887 : }
2888 2 : default:
2889 2 : logger_log(LOG_WARN, "skip_privacy_rule: unknown 0x%08x", crc);
2890 2 : 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 5 : static int skip_story_views(TlReader *r) {
2900 5 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2901 5 : uint32_t crc = tl_read_uint32(r);
2902 5 : if (crc != CRC_storyViews) {
2903 2 : logger_log(LOG_WARN, "skip_story_views: unknown 0x%08x", crc);
2904 2 : return -1;
2905 : }
2906 3 : if (r->len - r->pos < 4) return -1;
2907 3 : uint32_t flags = tl_read_uint32(r);
2908 3 : if (r->len - r->pos < 4) return -1;
2909 3 : tl_read_int32(r); /* views_count */
2910 3 : if (flags & (1u << 2)) {
2911 2 : if (r->len - r->pos < 4) return -1;
2912 2 : tl_read_int32(r); /* forwards_count */
2913 : }
2914 3 : if (flags & (1u << 3)) {
2915 2 : if (r->len - r->pos < 8) return -1;
2916 2 : uint32_t vc = tl_read_uint32(r);
2917 2 : if (vc != TL_vector) return -1;
2918 2 : uint32_t n = tl_read_uint32(r);
2919 4 : for (uint32_t i = 0; i < n; i++)
2920 2 : if (skip_reaction_count(r) != 0) return -1;
2921 : }
2922 3 : if (flags & (1u << 4)) {
2923 2 : if (r->len - r->pos < 4) return -1;
2924 2 : tl_read_int32(r); /* reactions_count */
2925 : }
2926 3 : if (flags & (1u << 0)) {
2927 2 : if (r->len - r->pos < 8) return -1;
2928 2 : uint32_t vc = tl_read_uint32(r);
2929 2 : if (vc != TL_vector) return -1;
2930 2 : uint32_t n = tl_read_uint32(r);
2931 2 : if (r->len - r->pos < (size_t)n * 8) return -1;
2932 4 : for (uint32_t i = 0; i < n; i++) tl_read_int64(r);
2933 : }
2934 3 : 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 32 : static int skip_story_item(TlReader *r) {
2948 32 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
2949 32 : uint32_t crc = tl_read_uint32(r);
2950 32 : switch (crc) {
2951 4 : case CRC_storyItemDeleted:
2952 4 : if (r->len - r->pos < 4) return -1;
2953 4 : tl_read_int32(r); /* id */
2954 4 : return 0;
2955 3 : case CRC_storyItemSkipped: {
2956 3 : if (r->len - r->pos < 4) return -1;
2957 3 : tl_read_uint32(r); /* flags */
2958 3 : if (r->len - r->pos < 12) return -1;
2959 3 : tl_read_int32(r); /* id */
2960 3 : tl_read_int32(r); /* date */
2961 3 : tl_read_int32(r); /* expire_date */
2962 3 : return 0;
2963 : }
2964 23 : case CRC_storyItem: {
2965 23 : if (r->len - r->pos < 4) return -1;
2966 22 : uint32_t flags = tl_read_uint32(r);
2967 22 : if (r->len - r->pos < 8) return -1;
2968 22 : tl_read_int32(r); /* id */
2969 22 : tl_read_int32(r); /* date */
2970 22 : if (flags & (1u << 18))
2971 0 : if (tl_skip_peer(r) != 0) return -1; /* from_id */
2972 22 : if (flags & (1u << 17))
2973 4 : if (skip_story_fwd_header(r) != 0) return -1; /* fwd_from */
2974 22 : if (r->len - r->pos < 4) return -1;
2975 22 : tl_read_int32(r); /* expire_date */
2976 22 : if (flags & (1u << 0))
2977 3 : if (tl_skip_string(r) != 0) return -1; /* caption */
2978 22 : if (flags & (1u << 1))
2979 3 : if (tl_skip_message_entities_vector(r) != 0) return -1;
2980 22 : if (tl_skip_message_media_ex(r, NULL) != 0) return -1;
2981 18 : if (flags & (1u << 14)) {
2982 9 : if (r->len - r->pos < 8) return -1;
2983 9 : uint32_t vc = tl_read_uint32(r);
2984 9 : if (vc != TL_vector) return -1;
2985 9 : uint32_t n = tl_read_uint32(r);
2986 24 : for (uint32_t i = 0; i < n; i++)
2987 17 : if (skip_media_area(r) != 0) return -1;
2988 : }
2989 16 : if (flags & (1u << 2)) {
2990 5 : if (r->len - r->pos < 8) return -1;
2991 5 : uint32_t vc = tl_read_uint32(r);
2992 5 : if (vc != TL_vector) return -1;
2993 5 : uint32_t n = tl_read_uint32(r);
2994 11 : for (uint32_t i = 0; i < n; i++)
2995 8 : if (skip_privacy_rule(r) != 0) return -1;
2996 : }
2997 14 : if (flags & (1u << 3))
2998 5 : if (skip_story_views(r) != 0) return -1;
2999 12 : if (flags & (1u << 15))
3000 0 : if (skip_reaction(r) != 0) return -1; /* sent_reaction */
3001 12 : return 0;
3002 : }
3003 2 : default:
3004 2 : logger_log(LOG_WARN, "skip_story_item: unknown 0x%08x", crc);
3005 2 : 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 218 : int tl_skip_message_media_ex(TlReader *r, MediaInfo *out) {
3025 : static int recursion_depth = 0;
3026 218 : if (recursion_depth >= MEDIA_RECURSION_MAX) {
3027 1 : logger_log(LOG_WARN,
3028 : "tl_skip_message_media_ex: recursion depth limit (%d) reached",
3029 : MEDIA_RECURSION_MAX);
3030 1 : return -1;
3031 : }
3032 217 : recursion_depth++;
3033 217 : int rc = skip_message_media_body(r, out);
3034 217 : recursion_depth--;
3035 217 : return rc;
3036 : }
3037 :
3038 217 : static int skip_message_media_body(TlReader *r, MediaInfo *out) {
3039 217 : if (out) memset(out, 0, sizeof(*out));
3040 217 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
3041 216 : uint32_t crc = tl_read_uint32(r);
3042 216 : switch (crc) {
3043 29 : case CRC_messageMediaEmpty:
3044 29 : if (out) out->kind = MEDIA_EMPTY;
3045 29 : return 0;
3046 3 : case CRC_messageMediaUnsupported:
3047 3 : if (out) out->kind = MEDIA_UNSUPPORTED;
3048 3 : return 0;
3049 :
3050 7 : case CRC_messageMediaGeo:
3051 7 : if (out) out->kind = MEDIA_GEO;
3052 7 : return skip_geo_point(r);
3053 :
3054 5 : case CRC_messageMediaContact:
3055 5 : if (out) out->kind = MEDIA_CONTACT;
3056 5 : if (tl_skip_string(r) != 0) return -1;
3057 5 : if (tl_skip_string(r) != 0) return -1;
3058 5 : if (tl_skip_string(r) != 0) return -1;
3059 5 : if (tl_skip_string(r) != 0) return -1;
3060 5 : if (r->len - r->pos < 8) return -1;
3061 5 : tl_read_int64(r);
3062 5 : return 0;
3063 :
3064 3 : case CRC_messageMediaVenue:
3065 3 : if (out) out->kind = MEDIA_VENUE;
3066 3 : if (skip_geo_point(r) != 0) return -1;
3067 3 : if (tl_skip_string(r) != 0) return -1;
3068 3 : if (tl_skip_string(r) != 0) return -1;
3069 3 : if (tl_skip_string(r) != 0) return -1;
3070 3 : if (tl_skip_string(r) != 0) return -1;
3071 3 : if (tl_skip_string(r) != 0) return -1;
3072 3 : return 0;
3073 :
3074 2 : case CRC_messageMediaGeoLive: {
3075 2 : if (out) out->kind = MEDIA_GEO_LIVE;
3076 2 : if (r->len - r->pos < 4) return -1;
3077 2 : uint32_t flags = tl_read_uint32(r);
3078 2 : if (skip_geo_point(r) != 0) return -1;
3079 2 : if (flags & 1u) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
3080 2 : if (r->len - r->pos < 4) return -1;
3081 2 : tl_read_int32(r);
3082 2 : if (flags & (1u << 1)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
3083 2 : return 0;
3084 : }
3085 :
3086 3 : case CRC_messageMediaDice:
3087 3 : if (out) out->kind = MEDIA_DICE;
3088 3 : if (r->len - r->pos < 4) return -1;
3089 3 : tl_read_int32(r);
3090 3 : return tl_skip_string(r);
3091 :
3092 9 : case CRC_messageMediaPhoto: {
3093 9 : if (out) out->kind = MEDIA_PHOTO;
3094 9 : if (r->len - r->pos < 4) return -1;
3095 9 : uint32_t flags = tl_read_uint32(r);
3096 9 : if (flags & (1u << 0)) {
3097 9 : if (photo_full(r, out) != 0) return -1;
3098 : }
3099 9 : if (flags & (1u << 2)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
3100 9 : return 0;
3101 : }
3102 :
3103 52 : case CRC_messageMediaWebPage: {
3104 52 : if (out) out->kind = MEDIA_WEBPAGE;
3105 52 : if (r->len - r->pos < 4) return -1;
3106 52 : uint32_t flags = tl_read_uint32(r);
3107 : (void)flags; /* only boolean previews */
3108 52 : return skip_webpage(r);
3109 : }
3110 :
3111 10 : case CRC_messageMediaPoll: {
3112 10 : if (out) out->kind = MEDIA_POLL;
3113 10 : if (skip_poll(r) != 0) return -1;
3114 10 : return skip_poll_results(r);
3115 : }
3116 :
3117 11 : case CRC_messageMediaInvoice: {
3118 11 : if (out) out->kind = MEDIA_INVOICE;
3119 11 : if (r->len - r->pos < 4) return -1;
3120 11 : uint32_t flags = tl_read_uint32(r);
3121 11 : if (tl_skip_string(r) != 0) return -1; /* title */
3122 11 : if (tl_skip_string(r) != 0) return -1; /* description */
3123 11 : if (flags & (1u << 0)) {
3124 9 : if (skip_web_document(r) != 0) return -1;
3125 : }
3126 8 : 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 8 : if (tl_skip_string(r) != 0) return -1; /* currency */
3131 8 : if (r->len - r->pos < 8) return -1;
3132 8 : tl_read_int64(r); /* total_amount */
3133 8 : if (tl_skip_string(r) != 0) return -1; /* start_param */
3134 8 : if (flags & (1u << 4)) {
3135 1 : if (skip_message_extended_media(r) != 0) return -1;
3136 : }
3137 8 : return 0;
3138 : }
3139 :
3140 32 : case CRC_messageMediaStory: {
3141 32 : if (out) out->kind = MEDIA_STORY;
3142 32 : if (r->len - r->pos < 4) return -1;
3143 32 : uint32_t flags = tl_read_uint32(r);
3144 32 : if (tl_skip_peer(r) != 0) return -1; /* peer */
3145 32 : if (r->len - r->pos < 4) return -1;
3146 32 : tl_read_int32(r); /* id */
3147 32 : if (flags & (1u << 0)) {
3148 31 : if (skip_story_item(r) != 0) return -1;
3149 : }
3150 19 : return 0;
3151 : }
3152 :
3153 2 : case CRC_messageMediaGiveaway: {
3154 2 : if (out) out->kind = MEDIA_GIVEAWAY;
3155 2 : if (r->len - r->pos < 4) return -1;
3156 2 : uint32_t flags = tl_read_uint32(r);
3157 : /* channels:Vector<long> */
3158 2 : if (r->len - r->pos < 8) return -1;
3159 2 : uint32_t vc = tl_read_uint32(r);
3160 2 : if (vc != TL_vector) return -1;
3161 2 : uint32_t n_ch = tl_read_uint32(r);
3162 2 : if (r->len - r->pos < (size_t)n_ch * 8) return -1;
3163 5 : for (uint32_t i = 0; i < n_ch; i++) tl_read_int64(r);
3164 2 : if (flags & (1u << 1)) {
3165 1 : if (r->len - r->pos < 8) return -1;
3166 1 : uint32_t cvc = tl_read_uint32(r);
3167 1 : if (cvc != TL_vector) return -1;
3168 1 : uint32_t n_c = tl_read_uint32(r);
3169 3 : for (uint32_t i = 0; i < n_c; i++)
3170 2 : if (tl_skip_string(r) != 0) return -1;
3171 : }
3172 2 : if (flags & (1u << 3))
3173 1 : if (tl_skip_string(r) != 0) return -1;
3174 2 : if (r->len - r->pos < 4) return -1;
3175 2 : tl_read_int32(r); /* quantity */
3176 2 : if (flags & (1u << 4)) {
3177 1 : if (r->len - r->pos < 4) return -1;
3178 1 : tl_read_int32(r); /* months */
3179 : }
3180 2 : if (flags & (1u << 5)) {
3181 0 : if (r->len - r->pos < 8) return -1;
3182 0 : tl_read_int64(r); /* stars */
3183 : }
3184 2 : if (r->len - r->pos < 4) return -1;
3185 2 : tl_read_int32(r); /* until_date */
3186 2 : return 0;
3187 : }
3188 :
3189 9 : case CRC_messageMediaGame: {
3190 9 : if (out) out->kind = MEDIA_GAME;
3191 9 : return skip_game(r);
3192 : }
3193 :
3194 12 : case CRC_messageMediaPaidMedia: {
3195 12 : if (out) out->kind = MEDIA_PAID;
3196 12 : if (r->len - r->pos < 8) return -1;
3197 12 : tl_read_int64(r); /* stars_amount */
3198 12 : if (r->len - r->pos < 8) return -1;
3199 12 : uint32_t vc = tl_read_uint32(r);
3200 12 : if (vc != TL_vector) return -1;
3201 12 : uint32_t n = tl_read_uint32(r);
3202 22 : for (uint32_t i = 0; i < n; i++) {
3203 13 : if (skip_message_extended_media(r) != 0) return -1;
3204 : }
3205 9 : return 0;
3206 : }
3207 :
3208 23 : case CRC_messageMediaDocument: {
3209 23 : if (out) out->kind = MEDIA_DOCUMENT;
3210 23 : if (r->len - r->pos < 4) return -1;
3211 23 : 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 23 : if (flags & (1u << 0)) {
3216 23 : if (document_inner(r, out) != 0) return -1;
3217 : }
3218 23 : if (flags & (1u << 2)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
3219 23 : 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 23 : return 0;
3230 : }
3231 :
3232 4 : default:
3233 4 : if (out) out->kind = MEDIA_OTHER;
3234 4 : logger_log(LOG_WARN, "tl_skip_message_media: unsupported 0x%08x", crc);
3235 4 : return -1;
3236 : }
3237 : }
3238 :
3239 29 : int tl_skip_message_media(TlReader *r) {
3240 29 : 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 33 : static int read_string_into(TlReader *r, char *out, size_t out_cap) {
3249 33 : char *s = tl_read_string(r);
3250 33 : if (!s) {
3251 0 : if (out_cap > 0) out[0] = '\0';
3252 0 : return -1;
3253 : }
3254 33 : if (out_cap > 0) {
3255 33 : size_t n = strlen(s);
3256 33 : if (n >= out_cap) n = out_cap - 1;
3257 33 : memcpy(out, s, n);
3258 33 : out[n] = '\0';
3259 : }
3260 33 : free(s);
3261 33 : return 0;
3262 : }
3263 :
3264 : /* Append `src` to `dst` (NUL-terminated), respecting dst_cap. */
3265 20 : static void str_append(char *dst, size_t dst_cap, const char *src) {
3266 20 : if (dst_cap == 0) return;
3267 20 : size_t cur = strlen(dst);
3268 20 : if (cur >= dst_cap - 1) return;
3269 20 : size_t room = dst_cap - 1 - cur;
3270 20 : size_t n = strlen(src);
3271 20 : if (n > room) n = room;
3272 20 : memcpy(dst + cur, src, n);
3273 20 : 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 21 : int tl_skip_chat_photo(TlReader *r) {
3282 21 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
3283 21 : uint32_t crc = tl_read_uint32(r);
3284 21 : if (crc == CRC_chatPhotoEmpty) return 0;
3285 6 : if (crc != CRC_chatPhoto) {
3286 2 : logger_log(LOG_WARN, "tl_skip_chat_photo: unknown 0x%08x", crc);
3287 2 : return -1;
3288 : }
3289 4 : if (r->len - r->pos < 4) return -1;
3290 4 : uint32_t flags = tl_read_uint32(r);
3291 : /* photo_id:long */
3292 4 : if (r->len - r->pos < 8) return -1;
3293 4 : tl_read_int64(r);
3294 4 : if (flags & (1u << 1)) {
3295 2 : if (tl_skip_string(r) != 0) return -1; /* stripped_thumb:bytes */
3296 : }
3297 : /* dc_id:int */
3298 4 : if (r->len - r->pos < 4) return -1;
3299 4 : tl_read_int32(r);
3300 4 : 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 6 : int tl_skip_user_profile_photo(TlReader *r) {
3310 6 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
3311 6 : uint32_t crc = tl_read_uint32(r);
3312 6 : if (crc == CRC_userProfilePhotoEmpty) return 0;
3313 4 : if (crc != CRC_userProfilePhoto) {
3314 2 : logger_log(LOG_WARN, "tl_skip_user_profile_photo: unknown 0x%08x", crc);
3315 2 : return -1;
3316 : }
3317 2 : if (r->len - r->pos < 4) return -1;
3318 2 : uint32_t flags = tl_read_uint32(r);
3319 : /* photo_id:long */
3320 2 : if (r->len - r->pos < 8) return -1;
3321 2 : tl_read_int64(r);
3322 2 : if (flags & (1u << 1)) {
3323 0 : if (tl_skip_string(r) != 0) return -1; /* stripped_thumb:bytes */
3324 : }
3325 : /* dc_id:int */
3326 2 : if (r->len - r->pos < 4) return -1;
3327 2 : tl_read_int32(r);
3328 2 : return 0;
3329 : }
3330 :
3331 : /* ---- UserStatus ---- */
3332 14 : int tl_skip_user_status(TlReader *r) {
3333 14 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
3334 14 : uint32_t crc = tl_read_uint32(r);
3335 14 : switch (crc) {
3336 2 : case CRC_userStatusEmpty:
3337 2 : return 0;
3338 10 : case CRC_userStatusOnline:
3339 : case CRC_userStatusOffline:
3340 : case CRC_userStatusRecently:
3341 : case CRC_userStatusLastWeek:
3342 : case CRC_userStatusLastMonth:
3343 10 : if (r->len - r->pos < 4) return -1;
3344 10 : tl_read_int32(r);
3345 10 : return 0;
3346 2 : default:
3347 2 : logger_log(LOG_WARN, "tl_skip_user_status: unknown 0x%08x", crc);
3348 2 : return -1;
3349 : }
3350 : }
3351 :
3352 : /* ---- Vector<RestrictionReason> ----
3353 : * restrictionReason#d072acb4 platform:string reason:string text:string
3354 : */
3355 11 : int tl_skip_restriction_reason_vector(TlReader *r) {
3356 11 : if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
3357 11 : uint32_t vec_crc = tl_read_uint32(r);
3358 11 : 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 11 : uint32_t count = tl_read_uint32(r);
3365 22 : for (uint32_t i = 0; i < count; i++) {
3366 13 : if (r->len - r->pos < 4) return -1;
3367 13 : uint32_t crc = tl_read_uint32(r);
3368 13 : if (crc != CRC_restrictionReason) {
3369 2 : logger_log(LOG_WARN,
3370 : "tl_skip_restriction_reason_vector: bad entry 0x%08x",
3371 : crc);
3372 2 : return -1;
3373 : }
3374 11 : if (tl_skip_string(r) != 0) return -1; /* platform */
3375 11 : if (tl_skip_string(r) != 0) return -1; /* reason */
3376 11 : if (tl_skip_string(r) != 0) return -1; /* text */
3377 : }
3378 9 : return 0;
3379 : }
3380 :
3381 : /* ---- Vector<Username> ----
3382 : * username#b4073647 flags:# editable:flags.0?true active:flags.1?true username:string
3383 : */
3384 4 : int tl_skip_username_vector(TlReader *r) {
3385 4 : if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
3386 4 : uint32_t vec_crc = tl_read_uint32(r);
3387 4 : 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 4 : uint32_t count = tl_read_uint32(r);
3393 8 : for (uint32_t i = 0; i < count; i++) {
3394 6 : if (r->len - r->pos < 8) return -1;
3395 6 : uint32_t crc = tl_read_uint32(r);
3396 6 : if (crc != CRC_username) {
3397 2 : logger_log(LOG_WARN,
3398 : "tl_skip_username_vector: bad entry 0x%08x", crc);
3399 2 : return -1;
3400 : }
3401 4 : tl_read_uint32(r); /* flags — only 'true' bits, no data */
3402 4 : if (tl_skip_string(r) != 0) return -1;
3403 : }
3404 2 : return 0;
3405 : }
3406 :
3407 : /* ---- PeerColor ----
3408 : * peerColor#b54b5acf flags:# color:flags.0?int background_emoji_id:flags.1?long
3409 : */
3410 4 : int tl_skip_peer_color(TlReader *r) {
3411 4 : if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
3412 4 : uint32_t crc = tl_read_uint32(r);
3413 4 : if (crc != CRC_peerColor) {
3414 2 : logger_log(LOG_WARN, "tl_skip_peer_color: unknown 0x%08x", crc);
3415 2 : return -1;
3416 : }
3417 2 : uint32_t flags = tl_read_uint32(r);
3418 2 : if (flags & (1u << 0)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
3419 2 : if (flags & (1u << 1)) { if (r->len - r->pos < 8) return -1; tl_read_int64(r); }
3420 2 : return 0;
3421 : }
3422 :
3423 : /* ---- EmojiStatus ---- */
3424 8 : int tl_skip_emoji_status(TlReader *r) {
3425 8 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
3426 8 : uint32_t crc = tl_read_uint32(r);
3427 8 : switch (crc) {
3428 2 : case CRC_emojiStatusEmpty:
3429 2 : return 0;
3430 2 : case CRC_emojiStatus:
3431 2 : if (r->len - r->pos < 8) return -1;
3432 2 : tl_read_int64(r);
3433 2 : return 0;
3434 2 : case CRC_emojiStatusUntil:
3435 2 : if (r->len - r->pos < 12) return -1;
3436 2 : tl_read_int64(r); tl_read_int32(r);
3437 2 : return 0;
3438 2 : default:
3439 2 : logger_log(LOG_WARN, "tl_skip_emoji_status: unknown 0x%08x", crc);
3440 2 : return -1;
3441 : }
3442 : }
3443 :
3444 : /* ChatAdminRights#5fb224d5 flags:# — single uint32. */
3445 6 : static int skip_chat_admin_rights(TlReader *r) {
3446 6 : if (!tl_reader_ok(r) || r->len - r->pos < 8) return -1;
3447 6 : uint32_t crc = tl_read_uint32(r);
3448 6 : if (crc != CRC_chatAdminRights) {
3449 2 : logger_log(LOG_WARN, "skip_chat_admin_rights: unknown 0x%08x", crc);
3450 2 : return -1;
3451 : }
3452 4 : tl_read_uint32(r); /* flags */
3453 4 : return 0;
3454 : }
3455 :
3456 : /* ChatBannedRights#9f120418 flags:# until_date:int. */
3457 8 : static int skip_chat_banned_rights(TlReader *r) {
3458 8 : if (!tl_reader_ok(r) || r->len - r->pos < 12) return -1;
3459 8 : uint32_t crc = tl_read_uint32(r);
3460 8 : if (crc != CRC_chatBannedRights) {
3461 2 : logger_log(LOG_WARN, "skip_chat_banned_rights: unknown 0x%08x", crc);
3462 2 : return -1;
3463 : }
3464 6 : tl_read_uint32(r); /* flags */
3465 6 : tl_read_int32(r); /* until_date */
3466 6 : 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 26 : static int extract_chat_inner(TlReader *r, ChatSummary *out) {
3481 26 : if (out) {
3482 13 : out->id = 0;
3483 13 : out->access_hash = 0;
3484 13 : out->have_access_hash = 0;
3485 13 : out->title[0] = '\0';
3486 : }
3487 :
3488 26 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
3489 26 : uint32_t crc = tl_read_uint32(r);
3490 :
3491 26 : if (crc == TL_chatEmpty) {
3492 5 : if (r->len - r->pos < 8) return -1;
3493 5 : int64_t id = tl_read_int64(r);
3494 5 : if (out) out->id = id;
3495 5 : return 0;
3496 : }
3497 :
3498 21 : if (crc == TL_chatForbidden) {
3499 4 : if (r->len - r->pos < 8) return -1;
3500 4 : int64_t id = tl_read_int64(r);
3501 4 : if (out) out->id = id;
3502 4 : if (out) {
3503 3 : if (read_string_into(r, out->title, sizeof(out->title)) != 0)
3504 0 : return -1;
3505 : } else {
3506 1 : if (tl_skip_string(r) != 0) return -1;
3507 : }
3508 4 : return 0;
3509 : }
3510 :
3511 17 : if (crc == TL_chat) {
3512 8 : if (r->len - r->pos < 4) return -1;
3513 8 : uint32_t flags = tl_read_uint32(r);
3514 8 : 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 8 : if (r->len - r->pos < 8) return -1;
3532 8 : int64_t id = tl_read_int64(r);
3533 8 : if (out) out->id = id;
3534 8 : if (out) {
3535 3 : if (read_string_into(r, out->title, sizeof(out->title)) != 0)
3536 0 : return -1;
3537 : } else {
3538 5 : if (tl_skip_string(r) != 0) return -1;
3539 : }
3540 8 : if (tl_skip_chat_photo(r) != 0) return -1;
3541 : /* participants_count:int date:int version:int */
3542 8 : if (r->len - r->pos < 12) return -1;
3543 8 : tl_read_int32(r); tl_read_int32(r); tl_read_int32(r);
3544 8 : if (flags & (1u << 14)) {
3545 4 : if (skip_chat_admin_rights(r) != 0) return -1;
3546 : }
3547 6 : if (flags & (1u << 18)) {
3548 4 : if (skip_chat_banned_rights(r) != 0) return -1;
3549 : }
3550 4 : return 0;
3551 : }
3552 :
3553 9 : if (crc == TL_channelForbidden) {
3554 2 : if (r->len - r->pos < 4) return -1;
3555 2 : uint32_t flags = tl_read_uint32(r);
3556 2 : if (r->len - r->pos < 16) return -1;
3557 2 : int64_t id = tl_read_int64(r);
3558 2 : if (out) out->id = id;
3559 2 : int64_t access_hash = tl_read_int64(r);
3560 2 : if (out) {
3561 1 : out->access_hash = access_hash;
3562 1 : out->have_access_hash = 1;
3563 : }
3564 2 : if (out) {
3565 1 : if (read_string_into(r, out->title, sizeof(out->title)) != 0)
3566 0 : return -1;
3567 : } else {
3568 1 : if (tl_skip_string(r) != 0) return -1;
3569 : }
3570 2 : if (flags & (1u << 16)) {
3571 0 : if (r->len - r->pos < 4) return -1;
3572 0 : tl_read_int32(r); /* until_date */
3573 : }
3574 2 : return 0;
3575 : }
3576 :
3577 7 : if (crc == TL_channel) {
3578 5 : if (r->len - r->pos < 8) return -1;
3579 5 : uint32_t flags = tl_read_uint32(r);
3580 5 : 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 5 : const uint32_t flags2_known =
3585 : (1u << 0) | (1u << 4) | (1u << 7) | (1u << 8) |
3586 : (1u << 9) | (1u << 10) | (1u << 11);
3587 5 : 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 5 : if (r->len - r->pos < 8) return -1;
3593 5 : int64_t id = tl_read_int64(r);
3594 5 : if (out) out->id = id;
3595 5 : if (flags & (1u << 13)) {
3596 2 : if (r->len - r->pos < 8) return -1;
3597 2 : int64_t access_hash = tl_read_int64(r);
3598 2 : if (out) {
3599 2 : out->access_hash = access_hash;
3600 2 : out->have_access_hash = 1;
3601 : }
3602 : }
3603 5 : if (out) {
3604 4 : if (read_string_into(r, out->title, sizeof(out->title)) != 0)
3605 0 : return -1;
3606 : } else {
3607 1 : if (tl_skip_string(r) != 0) return -1;
3608 : }
3609 5 : if (flags & (1u << 6)) {
3610 0 : if (tl_skip_string(r) != 0) return -1; /* username */
3611 : }
3612 5 : if (tl_skip_chat_photo(r) != 0) return -1;
3613 : /* date:int */
3614 5 : if (r->len - r->pos < 4) return -1;
3615 5 : tl_read_int32(r);
3616 5 : if (flags & (1u << 9)) {
3617 0 : if (tl_skip_restriction_reason_vector(r) != 0) return -1;
3618 : }
3619 5 : if (flags & (1u << 14)) {
3620 2 : if (skip_chat_admin_rights(r) != 0) return -1;
3621 : }
3622 5 : if (flags & (1u << 15)) {
3623 2 : if (skip_chat_banned_rights(r) != 0) return -1;
3624 : }
3625 5 : if (flags & (1u << 18)) {
3626 2 : if (skip_chat_banned_rights(r) != 0) return -1;
3627 : }
3628 5 : if (flags & (1u << 17)) {
3629 0 : if (r->len - r->pos < 4) return -1;
3630 0 : tl_read_int32(r); /* participants_count */
3631 : }
3632 5 : if (flags2 & (1u << 0)) {
3633 0 : if (tl_skip_username_vector(r) != 0) return -1;
3634 : }
3635 5 : 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 5 : if (flags2 & (1u << 7)) {
3640 0 : if (tl_skip_peer_color(r) != 0) return -1;
3641 : }
3642 5 : if (flags2 & (1u << 8)) {
3643 0 : if (tl_skip_peer_color(r) != 0) return -1;
3644 : }
3645 5 : if (flags2 & (1u << 9)) {
3646 0 : if (tl_skip_emoji_status(r) != 0) return -1;
3647 : }
3648 5 : if (flags2 & (1u << 10)) {
3649 0 : if (r->len - r->pos < 4) return -1;
3650 0 : tl_read_int32(r); /* level */
3651 : }
3652 5 : 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 5 : return 0;
3657 : }
3658 :
3659 2 : logger_log(LOG_WARN, "tl_skip_chat: unknown Chat variant 0x%08x", crc);
3660 2 : return -1;
3661 : }
3662 :
3663 9 : int tl_skip_chat(TlReader *r) {
3664 9 : return extract_chat_inner(r, NULL);
3665 : }
3666 :
3667 17 : int tl_extract_chat(TlReader *r, ChatSummary *out) {
3668 17 : if (!out) return extract_chat_inner(r, NULL);
3669 13 : 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 20 : static int extract_user_inner(TlReader *r, UserSummary *out) {
3683 20 : if (out) {
3684 16 : out->id = 0;
3685 16 : out->access_hash = 0;
3686 16 : out->have_access_hash = 0;
3687 16 : out->name[0] = '\0';
3688 16 : out->username[0] = '\0';
3689 : }
3690 :
3691 20 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
3692 20 : uint32_t crc = tl_read_uint32(r);
3693 :
3694 20 : if (crc == TL_userEmpty) {
3695 3 : if (r->len - r->pos < 8) return -1;
3696 3 : int64_t id = tl_read_int64(r);
3697 3 : if (out) out->id = id;
3698 3 : return 0;
3699 : }
3700 :
3701 17 : if (crc != TL_user && crc != TL_user2) {
3702 2 : logger_log(LOG_WARN, "tl_skip_user: unknown User variant 0x%08x", crc);
3703 2 : return -1;
3704 : }
3705 :
3706 15 : if (r->len - r->pos < 8) return -1;
3707 15 : uint32_t flags = tl_read_uint32(r);
3708 15 : 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 15 : 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 15 : if (r->len - r->pos < 8) return -1;
3720 15 : int64_t id = tl_read_int64(r);
3721 15 : if (out) out->id = id;
3722 :
3723 15 : if (flags & (1u << 0)) {
3724 12 : if (r->len - r->pos < 8) return -1;
3725 12 : int64_t access_hash = tl_read_int64(r);
3726 12 : if (out) {
3727 12 : out->access_hash = access_hash;
3728 12 : out->have_access_hash = 1;
3729 : }
3730 : }
3731 : /* first_name */
3732 15 : if (flags & (1u << 1)) {
3733 13 : if (out) {
3734 12 : char first[96] = {0};
3735 12 : if (read_string_into(r, first, sizeof(first)) != 0) return -1;
3736 12 : str_append(out->name, sizeof(out->name), first);
3737 : } else {
3738 1 : if (tl_skip_string(r) != 0) return -1;
3739 : }
3740 : }
3741 : /* last_name */
3742 15 : if (flags & (1u << 2)) {
3743 5 : if (out) {
3744 4 : char last[96] = {0};
3745 4 : if (read_string_into(r, last, sizeof(last)) != 0) return -1;
3746 4 : if (last[0] != '\0') {
3747 4 : if (out->name[0] != '\0') {
3748 4 : str_append(out->name, sizeof(out->name), " ");
3749 : }
3750 4 : str_append(out->name, sizeof(out->name), last);
3751 : }
3752 : } else {
3753 1 : if (tl_skip_string(r) != 0) return -1;
3754 : }
3755 : }
3756 : /* username */
3757 15 : if (flags & (1u << 3)) {
3758 7 : if (out) {
3759 6 : if (read_string_into(r, out->username, sizeof(out->username)) != 0)
3760 0 : return -1;
3761 : } else {
3762 1 : if (tl_skip_string(r) != 0) return -1;
3763 : }
3764 : }
3765 : /* phone */
3766 15 : if (flags & (1u << 4)) {
3767 2 : if (tl_skip_string(r) != 0) return -1;
3768 : }
3769 : /* photo */
3770 15 : if (flags & (1u << 5)) {
3771 0 : if (tl_skip_user_profile_photo(r) != 0) return -1;
3772 : }
3773 : /* status */
3774 15 : if (flags & (1u << 6)) {
3775 0 : if (tl_skip_user_status(r) != 0) return -1;
3776 : }
3777 : /* bot_info_version */
3778 15 : 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 15 : if (flags & (1u << 18)) {
3784 0 : if (tl_skip_restriction_reason_vector(r) != 0) return -1;
3785 : }
3786 : /* bot_inline_placeholder */
3787 15 : if (flags & (1u << 19)) {
3788 0 : if (tl_skip_string(r) != 0) return -1;
3789 : }
3790 : /* lang_code */
3791 15 : if (flags & (1u << 22)) {
3792 0 : if (tl_skip_string(r) != 0) return -1;
3793 : }
3794 : /* emoji_status */
3795 15 : if (flags & (1u << 30)) {
3796 0 : if (tl_skip_emoji_status(r) != 0) return -1;
3797 : }
3798 : /* usernames */
3799 15 : if (flags2 & (1u << 0)) {
3800 0 : if (tl_skip_username_vector(r) != 0) return -1;
3801 : }
3802 : /* stories_max_id */
3803 15 : if (flags2 & (1u << 5)) {
3804 0 : if (r->len - r->pos < 4) return -1;
3805 0 : tl_read_int32(r);
3806 : }
3807 : /* color */
3808 15 : if (flags2 & (1u << 8)) {
3809 0 : if (tl_skip_peer_color(r) != 0) return -1;
3810 : }
3811 : /* profile_color */
3812 15 : if (flags2 & (1u << 9)) {
3813 0 : if (tl_skip_peer_color(r) != 0) return -1;
3814 : }
3815 : /* bot_active_users */
3816 15 : if (flags2 & (1u << 12)) {
3817 0 : if (r->len - r->pos < 4) return -1;
3818 0 : tl_read_int32(r);
3819 : }
3820 15 : return 0;
3821 : }
3822 :
3823 4 : int tl_skip_user(TlReader *r) {
3824 4 : return extract_user_inner(r, NULL);
3825 : }
3826 :
3827 16 : int tl_extract_user(TlReader *r, UserSummary *out) {
3828 16 : if (!out) return extract_user_inner(r, NULL);
3829 16 : 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 29 : int tl_skip_message(TlReader *r) {
3947 29 : if (!tl_reader_ok(r) || r->len - r->pos < 4) return -1;
3948 27 : uint32_t crc = tl_read_uint32(r);
3949 :
3950 27 : if (crc == TL_messageEmpty) {
3951 : /* flags:# id:int peer_id:flags.0?Peer */
3952 4 : if (r->len - r->pos < 8) return -1;
3953 4 : uint32_t flags = tl_read_uint32(r);
3954 4 : tl_read_int32(r); /* id */
3955 4 : if (flags & 1u) {
3956 0 : if (tl_skip_peer(r) != 0) return -1;
3957 : }
3958 4 : return 0;
3959 : }
3960 :
3961 23 : if (crc == TL_messageService) {
3962 2 : if (r->len - r->pos < 8) return -1;
3963 2 : uint32_t flags = tl_read_uint32(r);
3964 2 : tl_read_int32(r); /* id — messageService has no flags2 */
3965 2 : if (flags & (1u << 8)) if (tl_skip_peer(r) != 0) return -1; /* from_id */
3966 2 : 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 21 : if (crc != TL_message) {
3977 2 : logger_log(LOG_WARN, "tl_skip_message: unknown 0x%08x", crc);
3978 2 : return -1;
3979 : }
3980 :
3981 19 : if (r->len - r->pos < 12) return -1;
3982 17 : uint32_t flags = tl_read_uint32(r);
3983 17 : uint32_t flags2 = tl_read_uint32(r);
3984 17 : tl_read_int32(r); /* id */
3985 :
3986 17 : if (flags & (1u << 8)) if (tl_skip_peer(r) != 0) return -1; /* from_id */
3987 17 : if (tl_skip_peer(r) != 0) return -1; /* peer_id */
3988 17 : if (flags & (1u << 28)) if (tl_skip_peer(r) != 0) return -1; /* saved_peer_id */
3989 17 : if (flags & (1u << 2)) if (tl_skip_message_fwd_header(r) != 0) return -1;
3990 17 : if (flags & (1u << 11)) { if (r->len - r->pos < 8) return -1; tl_read_int64(r); }
3991 17 : if (flags2 & (1u << 0)) { if (r->len - r->pos < 8) return -1; tl_read_int64(r); }
3992 17 : if (flags & (1u << 3)) if (tl_skip_message_reply_header(r) != 0) return -1;
3993 :
3994 17 : if (r->len - r->pos < 4) return -1;
3995 17 : tl_read_int32(r); /* date */
3996 17 : if (tl_skip_string(r) != 0) return -1; /* message */
3997 :
3998 17 : if (flags & (1u << 9)) if (tl_skip_message_media(r) != 0) return -1;
3999 :
4000 15 : if (flags & (1u << 6)) if (tl_skip_reply_markup(r) != 0) return -1;
4001 13 : if (flags & (1u << 7)) if (tl_skip_message_entities_vector(r) != 0) return -1;
4002 13 : if (flags & (1u << 10)) { if (r->len - r->pos < 8) return -1; tl_read_int32(r); tl_read_int32(r); }
4003 13 : if (flags & (1u << 23)) if (tl_skip_message_replies(r) != 0) return -1;
4004 13 : if (flags & (1u << 15)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
4005 13 : if (flags & (1u << 16)) if (tl_skip_string(r) != 0) return -1;
4006 13 : if (flags & (1u << 17)) { if (r->len - r->pos < 8) return -1; tl_read_int64(r); }
4007 13 : if (flags & (1u << 20)) if (tl_skip_message_reactions(r) != 0) return -1;
4008 13 : if (flags & (1u << 22)) if (tl_skip_restriction_reason_vector(r) != 0) return -1;
4009 13 : if (flags & (1u << 25)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
4010 13 : if (flags2 & (1u << 30)) { if (r->len - r->pos < 4) return -1; tl_read_int32(r); }
4011 13 : if (flags2 & (1u << 2)) { if (r->len - r->pos < 8) return -1; tl_read_int64(r); }
4012 13 : if (flags2 & (1u << 3)) if (tl_skip_factcheck(r) != 0) return -1;
4013 13 : return 0;
4014 : }
|