qt_extractor.c (42006B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2002, 2003, 2006, 2012, 2026 Vidyut Samanta and Christian Grothoff 4 5 libextractor is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 libextractor is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with libextractor; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file plugins/qt_extractor.c 22 * @brief plugin to support QuickTime, MP4, M4A and 3GPP files 23 * @author Vidyut Samanta 24 * @author Christian Grothoff 25 * 26 * This plugin parses the ISO base media / QuickTime "atom" (box) tree. 27 * It does not link against any third-party demuxer: the metadata-bearing 28 * atoms ('ftyp', 'moov' and the boxes nested inside it) are tiny compared 29 * to the media payload ('mdat'), so the plugin streams the top-level 30 * boxes via the extraction context and only ever pulls the small 31 * metadata containers into memory before walking them recursively. 32 */ 33 #include "platform.h" 34 #include "extractor.h" 35 #include <zlib.h> 36 #include <stdint.h> 37 #include <stdbool.h> 38 #include "le_architecture.h" 39 40 /** 41 * Maximum size (in bytes) of a single top-level atom that we are willing 42 * to pull into memory. 'moov' is always far smaller than this in 43 * practice; the cap merely protects us against hostile or corrupt files. 44 */ 45 #define MAX_ATOM_SIZE (64 * 1024 * 1024) 46 47 /** 48 * Maximum size of a (decompressed) compressed-movie 'cmov' atom. 49 */ 50 #define MAX_CMOV_SIZE (16 * 1024 * 1024) 51 52 /** 53 * Maximum atom nesting depth we are willing to recurse into. Real files 54 * stay well below ten; the limit guards against stack exhaustion from 55 * maliciously deeply nested boxes. 56 */ 57 #define MAX_ATOM_DEPTH 32 58 59 60 /* verbatim from mp3extractor */ 61 static const char *const genre_names[] = { 62 gettext_noop ("Blues"), 63 gettext_noop ("Classic Rock"), 64 gettext_noop ("Country"), 65 gettext_noop ("Dance"), 66 gettext_noop ("Disco"), 67 gettext_noop ("Funk"), 68 gettext_noop ("Grunge"), 69 gettext_noop ("Hip-Hop"), 70 gettext_noop ("Jazz"), 71 gettext_noop ("Metal"), 72 gettext_noop ("New Age"), 73 gettext_noop ("Oldies"), 74 gettext_noop ("Other"), 75 gettext_noop ("Pop"), 76 gettext_noop ("R&B"), 77 gettext_noop ("Rap"), 78 gettext_noop ("Reggae"), 79 gettext_noop ("Rock"), 80 gettext_noop ("Techno"), 81 gettext_noop ("Industrial"), 82 gettext_noop ("Alternative"), 83 gettext_noop ("Ska"), 84 gettext_noop ("Death Metal"), 85 gettext_noop ("Pranks"), 86 gettext_noop ("Soundtrack"), 87 gettext_noop ("Euro-Techno"), 88 gettext_noop ("Ambient"), 89 gettext_noop ("Trip-Hop"), 90 gettext_noop ("Vocal"), 91 gettext_noop ("Jazz+Funk"), 92 gettext_noop ("Fusion"), 93 gettext_noop ("Trance"), 94 gettext_noop ("Classical"), 95 gettext_noop ("Instrumental"), 96 gettext_noop ("Acid"), 97 gettext_noop ("House"), 98 gettext_noop ("Game"), 99 gettext_noop ("Sound Clip"), 100 gettext_noop ("Gospel"), 101 gettext_noop ("Noise"), 102 gettext_noop ("Alt. Rock"), 103 gettext_noop ("Bass"), 104 gettext_noop ("Soul"), 105 gettext_noop ("Punk"), 106 gettext_noop ("Space"), 107 gettext_noop ("Meditative"), 108 gettext_noop ("Instrumental Pop"), 109 gettext_noop ("Instrumental Rock"), 110 gettext_noop ("Ethnic"), 111 gettext_noop ("Gothic"), 112 gettext_noop ("Darkwave"), 113 gettext_noop ("Techno-Industrial"), 114 gettext_noop ("Electronic"), 115 gettext_noop ("Pop-Folk"), 116 gettext_noop ("Eurodance"), 117 gettext_noop ("Dream"), 118 gettext_noop ("Southern Rock"), 119 gettext_noop ("Comedy"), 120 gettext_noop ("Cult"), 121 gettext_noop ("Gangsta Rap"), 122 gettext_noop ("Top 40"), 123 gettext_noop ("Christian Rap"), 124 gettext_noop ("Pop/Funk"), 125 gettext_noop ("Jungle"), 126 gettext_noop ("Native American"), 127 gettext_noop ("Cabaret"), 128 gettext_noop ("New Wave"), 129 gettext_noop ("Psychedelic"), 130 gettext_noop ("Rave"), 131 gettext_noop ("Showtunes"), 132 gettext_noop ("Trailer"), 133 gettext_noop ("Lo-Fi"), 134 gettext_noop ("Tribal"), 135 gettext_noop ("Acid Punk"), 136 gettext_noop ("Acid Jazz"), 137 gettext_noop ("Polka"), 138 gettext_noop ("Retro"), 139 gettext_noop ("Musical"), 140 gettext_noop ("Rock & Roll"), 141 gettext_noop ("Hard Rock"), 142 gettext_noop ("Folk"), 143 gettext_noop ("Folk/Rock"), 144 gettext_noop ("National Folk"), 145 gettext_noop ("Swing"), 146 gettext_noop ("Fast-Fusion"), 147 gettext_noop ("Bebob"), 148 gettext_noop ("Latin"), 149 gettext_noop ("Revival"), 150 gettext_noop ("Celtic"), 151 gettext_noop ("Bluegrass"), 152 gettext_noop ("Avantgarde"), 153 gettext_noop ("Gothic Rock"), 154 gettext_noop ("Progressive Rock"), 155 gettext_noop ("Psychedelic Rock"), 156 gettext_noop ("Symphonic Rock"), 157 gettext_noop ("Slow Rock"), 158 gettext_noop ("Big Band"), 159 gettext_noop ("Chorus"), 160 gettext_noop ("Easy Listening"), 161 gettext_noop ("Acoustic"), 162 gettext_noop ("Humour"), 163 gettext_noop ("Speech"), 164 gettext_noop ("Chanson"), 165 gettext_noop ("Opera"), 166 gettext_noop ("Chamber Music"), 167 gettext_noop ("Sonata"), 168 gettext_noop ("Symphony"), 169 gettext_noop ("Booty Bass"), 170 gettext_noop ("Primus"), 171 gettext_noop ("Porn Groove"), 172 gettext_noop ("Satire"), 173 gettext_noop ("Slow Jam"), 174 gettext_noop ("Club"), 175 gettext_noop ("Tango"), 176 gettext_noop ("Samba"), 177 gettext_noop ("Folklore"), 178 gettext_noop ("Ballad"), 179 gettext_noop ("Power Ballad"), 180 gettext_noop ("Rhythmic Soul"), 181 gettext_noop ("Freestyle"), 182 gettext_noop ("Duet"), 183 gettext_noop ("Punk Rock"), 184 gettext_noop ("Drum Solo"), 185 gettext_noop ("A Cappella"), 186 gettext_noop ("Euro-House"), 187 gettext_noop ("Dance Hall"), 188 gettext_noop ("Goa"), 189 gettext_noop ("Drum & Bass"), 190 gettext_noop ("Club-House"), 191 gettext_noop ("Hardcore"), 192 gettext_noop ("Terror"), 193 gettext_noop ("Indie"), 194 gettext_noop ("BritPop"), 195 gettext_noop ("Negerpunk"), 196 gettext_noop ("Polsk Punk"), 197 gettext_noop ("Beat"), 198 gettext_noop ("Christian Gangsta Rap"), 199 gettext_noop ("Heavy Metal"), 200 gettext_noop ("Black Metal"), 201 gettext_noop ("Crossover"), 202 gettext_noop ("Contemporary Christian"), 203 gettext_noop ("Christian Rock"), 204 gettext_noop ("Merengue"), 205 gettext_noop ("Salsa"), 206 gettext_noop ("Thrash Metal"), 207 gettext_noop ("Anime"), 208 gettext_noop ("JPop"), 209 gettext_noop ("Synthpop"), 210 }; 211 212 #define GENRE_NAME_COUNT \ 213 ((unsigned int) (sizeof genre_names / sizeof (const char *const))) 214 215 216 static const char *languages[] = { 217 "English", 218 "French", 219 "German", 220 "Italian", 221 "Dutch", 222 "Swedish", 223 "Spanish", 224 "Danish", 225 "Portuguese", 226 "Norwegian", 227 "Hebrew", 228 "Japanese", 229 "Arabic", 230 "Finnish", 231 "Greek", 232 "Icelandic", 233 "Maltese", 234 "Turkish", 235 "Croatian", 236 "Traditional Chinese", 237 "Urdu", 238 "Hindi", 239 "Thai", 240 "Korean", 241 "Lithuanian", 242 "Polish", 243 "Hungarian", 244 "Estonian", 245 "Lettish", 246 "Saamisk", 247 "Lappish", 248 "Faeroese", 249 "Farsi", 250 "Russian", 251 "Simplified Chinese", 252 "Flemish", 253 "Irish", 254 "Albanian", 255 "Romanian", 256 "Czech", 257 "Slovak", 258 "Slovenian", 259 "Yiddish", 260 "Serbian", 261 "Macedonian", 262 "Bulgarian", 263 "Ukrainian", 264 "Byelorussian", 265 "Uzbek", 266 "Kazakh", 267 "Azerbaijani", 268 "AzerbaijanAr", 269 "Armenian", 270 "Georgian", 271 "Moldavian", 272 "Kirghiz", 273 "Tajiki", 274 "Turkmen", 275 "Mongolian", 276 "MongolianCyr", 277 "Pashto", 278 "Kurdish", 279 "Kashmiri", 280 "Sindhi", 281 "Tibetan", 282 "Nepali", 283 "Sanskrit", 284 "Marathi", 285 "Bengali", 286 "Assamese", 287 "Gujarati", 288 "Punjabi", 289 "Oriya", 290 "Malayalam", 291 "Kannada", 292 "Tamil", 293 "Telugu", 294 "Sinhalese", 295 "Burmese", 296 "Khmer", 297 "Lao", 298 "Vietnamese", 299 "Indonesian", 300 "Tagalog", 301 "MalayRoman", 302 "MalayArabic", 303 "Amharic", 304 "Tigrinya", 305 "Galla", 306 "Oromo", 307 "Somali", 308 "Swahili", 309 "Ruanda", 310 "Rundi", 311 "Chewa", 312 "Malagasy", 313 "Esperanto", 314 "Welsh", 315 "Basque", 316 "Catalan", 317 "Latin", 318 "Quechua", 319 "Guarani", 320 "Aymara", 321 "Tatar", 322 "Uighur", 323 "Dzongkha", 324 "JavaneseRom", 325 }; 326 327 328 typedef struct 329 { 330 const char *ext; 331 const char *mime; 332 } C2M; 333 334 /* see http://www.mp4ra.org/filetype.html 335 * http://www.ftyps.com/ */ 336 static C2M ftMap[] = { 337 {"qt ", "video/quicktime"}, 338 {"isom", "video/mp4"}, /* ISO Base Media files */ 339 {"iso2", "video/mp4"}, 340 {"iso4", "video/mp4"}, 341 {"iso5", "video/mp4"}, 342 {"iso6", "video/mp4"}, 343 {"avc1", "video/mp4"}, 344 {"mp41", "video/mp4"}, /* MPEG-4 (ISO/IEC 14491-1) version 1 */ 345 {"mp42", "video/mp4"}, /* MPEG-4 (ISO/IEC 14491-1) version 2 */ 346 {"mp71", "video/mp4"}, /* MPEG-4 with MPEG-7 metadata */ 347 {"dash", "video/mp4"}, /* MPEG-DASH */ 348 {"3gp1", "video/3gpp"}, 349 {"3gp2", "video/3gpp"}, 350 {"3gp3", "video/3gpp"}, 351 {"3gp4", "video/3gpp"}, 352 {"3gp5", "video/3gpp"}, 353 {"3gp6", "video/3gpp"}, 354 {"3gp7", "video/3gpp"}, 355 {"3g2a", "video/3gpp2"}, 356 {"3g2b", "video/3gpp2"}, 357 {"3g2c", "video/3gpp2"}, 358 {"mmp4", "video/mp4"}, /* Mobile MPEG-4 */ 359 {"M4A ", "audio/mp4"}, 360 {"M4B ", "audio/mp4"}, /* Apple audio book */ 361 {"M4P ", "audio/mp4"}, 362 {"M4V ", "video/mp4"}, 363 {"M4VH", "video/mp4"}, 364 {"M4VP", "video/mp4"}, 365 {"f4v ", "video/mp4"}, /* Adobe Flash MP4 video */ 366 {"f4a ", "audio/mp4"}, 367 {"f4b ", "audio/mp4"}, 368 {"qt ", "video/quicktime"}, 369 {"mj2s", "video/mj2"}, /* Motion JPEG 2000 */ 370 {"mjp2", "video/mj2"}, 371 {NULL, NULL}, 372 }; 373 374 typedef struct CHE 375 { 376 const char *pfx; 377 enum EXTRACTOR_MetaType type; 378 } CHE; 379 380 static CHE cHm[] = { 381 {"aut", EXTRACTOR_METATYPE_AUTHOR_NAME}, 382 {"cpy", EXTRACTOR_METATYPE_COPYRIGHT}, 383 {"day", EXTRACTOR_METATYPE_CREATION_DATE}, 384 {"ed1", EXTRACTOR_METATYPE_MODIFICATION_DATE}, 385 {"ed2", EXTRACTOR_METATYPE_MODIFICATION_DATE}, 386 {"ed3", EXTRACTOR_METATYPE_MODIFICATION_DATE}, 387 {"ed4", EXTRACTOR_METATYPE_MODIFICATION_DATE}, 388 {"ed5", EXTRACTOR_METATYPE_MODIFICATION_DATE}, 389 {"ed6", EXTRACTOR_METATYPE_MODIFICATION_DATE}, 390 {"ed7", EXTRACTOR_METATYPE_MODIFICATION_DATE}, 391 {"ed8", EXTRACTOR_METATYPE_MODIFICATION_DATE}, 392 {"ed9", EXTRACTOR_METATYPE_MODIFICATION_DATE}, 393 {"cmt", EXTRACTOR_METATYPE_COMMENT}, 394 {"url", EXTRACTOR_METATYPE_URL}, 395 {"enc", EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE}, 396 {"hst", EXTRACTOR_METATYPE_BUILDHOST}, 397 {"nam", EXTRACTOR_METATYPE_TITLE}, 398 {"gen", EXTRACTOR_METATYPE_GENRE}, 399 {"mak", EXTRACTOR_METATYPE_CAMERA_MAKE}, 400 {"mod", EXTRACTOR_METATYPE_CAMERA_MODEL}, 401 {"des", EXTRACTOR_METATYPE_DESCRIPTION}, 402 {"dis", EXTRACTOR_METATYPE_DISCLAIMER}, 403 {"dir", EXTRACTOR_METATYPE_MOVIE_DIRECTOR}, 404 {"src", EXTRACTOR_METATYPE_CONTRIBUTOR_NAME}, 405 {"prf", EXTRACTOR_METATYPE_PERFORMER }, 406 {"prd", EXTRACTOR_METATYPE_PRODUCER}, 407 {"PRD", EXTRACTOR_METATYPE_PRODUCT_VERSION}, 408 {"swr", EXTRACTOR_METATYPE_PRODUCED_BY_SOFTWARE}, 409 {"isr", EXTRACTOR_METATYPE_ISRC}, 410 {"wrt", EXTRACTOR_METATYPE_WRITER}, 411 {"wrn", EXTRACTOR_METATYPE_WARNING}, 412 {"chp", EXTRACTOR_METATYPE_CHAPTER_NAME}, 413 {"inf", EXTRACTOR_METATYPE_DESCRIPTION}, 414 {"req", EXTRACTOR_METATYPE_TARGET_PLATFORM}, /* hardware requirements */ 415 {"fmt", EXTRACTOR_METATYPE_FORMAT}, 416 {"alb", EXTRACTOR_METATYPE_ALBUM}, 417 {"ART", EXTRACTOR_METATYPE_ARTIST}, 418 {"art", EXTRACTOR_METATYPE_ARTIST}, 419 {"too", EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE}, 420 {"grp", EXTRACTOR_METATYPE_GROUP}, 421 {"lyr", EXTRACTOR_METATYPE_LYRICS}, 422 {"st3", EXTRACTOR_METATYPE_SUBTITLE}, 423 {NULL, EXTRACTOR_METATYPE_RESERVED }, 424 }; 425 426 427 typedef struct 428 { 429 const char *atom_type; 430 enum EXTRACTOR_MetaType type; 431 } ITTagConversionEntry; 432 433 /* iTunes / "ilst" tags: 434 * see http://atomicparsley.sourceforge.net/mpeg-4files.html 435 * 436 * The first byte of the four-character key is the (C) / 0xa9 sign for 437 * the "user" tags; we keep it spelled out here so the table can be 438 * memcmp()ed against the raw atom name. */ 439 static ITTagConversionEntry it_to_extr_table[] = { 440 {"\xa9" "alb", EXTRACTOR_METATYPE_ALBUM}, 441 {"\xa9" "ART", EXTRACTOR_METATYPE_ARTIST}, 442 {"\xa9" "art", EXTRACTOR_METATYPE_ARTIST}, 443 {"aART", EXTRACTOR_METATYPE_ARTIST}, /* album artist */ 444 {"\xa9" "cmt", EXTRACTOR_METATYPE_COMMENT}, 445 {"\xa9" "day", EXTRACTOR_METATYPE_UNKNOWN_DATE}, 446 {"\xa9" "nam", EXTRACTOR_METATYPE_TITLE}, 447 {"\xa9" "trk", EXTRACTOR_METATYPE_TRACK_NUMBER}, 448 {"trkn", EXTRACTOR_METATYPE_TRACK_NUMBER}, 449 {"\xa9" "dis", EXTRACTOR_METATYPE_DISC_NUMBER}, 450 {"disk", EXTRACTOR_METATYPE_DISC_NUMBER}, 451 {"\xa9" "gen", EXTRACTOR_METATYPE_GENRE}, 452 {"gnre", EXTRACTOR_METATYPE_GENRE}, 453 {"\xa9" "wrt", EXTRACTOR_METATYPE_COMPOSER}, 454 {"\xa9" "com", EXTRACTOR_METATYPE_COMPOSER}, 455 {"\xa9" "too", EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE}, 456 {"\xa9" "enc", EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE}, 457 {"cprt", EXTRACTOR_METATYPE_COPYRIGHT}, 458 {"\xa9" "cpy", EXTRACTOR_METATYPE_COPYRIGHT}, 459 {"\xa9" "grp", EXTRACTOR_METATYPE_GROUP}, 460 {"\xa9" "lyr", EXTRACTOR_METATYPE_LYRICS}, 461 {"\xa9" "st3", EXTRACTOR_METATYPE_SUBTITLE}, 462 {"\xa9" "url", EXTRACTOR_METATYPE_URL}, 463 {"\xa9" "prd", EXTRACTOR_METATYPE_PRODUCER}, 464 {"\xa9" "dir", EXTRACTOR_METATYPE_MOVIE_DIRECTOR}, 465 {"\xa9" "prf", EXTRACTOR_METATYPE_PERFORMER}, 466 {"\xa9" "swr", EXTRACTOR_METATYPE_PRODUCED_BY_SOFTWARE}, 467 {"\xa9" "fmt", EXTRACTOR_METATYPE_FORMAT}, 468 {"\xa9" "inf", EXTRACTOR_METATYPE_DESCRIPTION}, 469 {"tmpo", EXTRACTOR_METATYPE_BEATS_PER_MINUTE}, 470 {"catg", EXTRACTOR_METATYPE_SECTION}, 471 {"keyw", EXTRACTOR_METATYPE_KEYWORDS}, 472 {"desc", EXTRACTOR_METATYPE_DESCRIPTION}, 473 {"ldes", EXTRACTOR_METATYPE_DESCRIPTION}, /* long description */ 474 {"tvnn", EXTRACTOR_METATYPE_NETWORK_NAME}, 475 {"tvsh", EXTRACTOR_METATYPE_SHOW_NAME}, 476 {"tvsn", EXTRACTOR_METATYPE_SHOW_SEASON_NUMBER}, 477 {"tves", EXTRACTOR_METATYPE_SHOW_EPISODE_NUMBER}, 478 {"purd", EXTRACTOR_METATYPE_UNKNOWN_DATE}, /* purchase date */ 479 {"covr", EXTRACTOR_METATYPE_COVER_PICTURE}, 480 {NULL, EXTRACTOR_METATYPE_RESERVED} 481 }; 482 483 484 /* The structs below describe on-disk layout and are overlaid directly 485 on the (unaligned) read buffer, so they must be packed: without that 486 the compiler is entitled to assume natural alignment and emit loads 487 that are undefined for the addresses they are actually used at -- and 488 fatal on a strict-alignment target. The attribute goes on the struct 489 rather than on each member because gcc ignores it on aggregate and 490 array members. */ 491 LE_NETWORK_STRUCT_BEGIN 492 struct Atom 493 { 494 uint32_t size; 495 uint32_t type; 496 } LE_PACKED; 497 498 499 struct LongAtom 500 { 501 uint32_t one; 502 uint32_t type; 503 uint64_t size; 504 } LE_PACKED; 505 LE_NETWORK_STRUCT_END 506 507 508 static uint64_t 509 ntohll (uint64_t n) 510 { 511 #if __BYTE_ORDER == __BIG_ENDIAN 512 return n; 513 #else 514 return (((uint64_t) ntohl (n)) << 32) + ntohl (n >> 32); 515 #endif 516 } 517 518 519 /** 520 * Read a big-endian 32 bit value from @a p, which need not be aligned. 521 * Casting the read buffer to `uint32_t *' has the same alignment problem 522 * as an unpacked struct does and is not fixed by packing. 523 * 524 * @param p (unaligned) source 525 * @return the value in host byte order 526 */ 527 static uint32_t 528 read_be32 (const void *p) 529 { 530 uint32_t v; 531 532 memcpy (&v, p, sizeof (v)); 533 return ntohl (v); 534 } 535 536 537 /** 538 * Read a big-endian 64 bit value from @a p, which need not be aligned. 539 * 540 * @param p (unaligned) source 541 * @return the value in host byte order 542 */ 543 static uint64_t 544 read_be64 (const void *p) 545 { 546 uint64_t v; 547 548 memcpy (&v, p, sizeof (v)); 549 return ntohll (v); 550 } 551 552 553 /** 554 * Check if at position pos there is a valid atom. 555 * @return false if the atom is invalid, true if it is valid 556 */ 557 static bool 558 checkAtomValid (const char *buffer, 559 size_t size, 560 size_t pos) 561 { 562 unsigned long long atomSize; 563 const struct Atom *atom; 564 const struct LongAtom *latom; 565 566 if ( (pos >= size) || 567 (pos + sizeof (struct Atom) > size) || 568 (pos + sizeof (struct Atom) < pos) ) 569 return false; 570 atom = (const struct Atom *) &buffer[pos]; 571 if (ntohl (atom->size) == 1) 572 { 573 if ( (pos + sizeof (struct LongAtom) > size) || 574 (pos + sizeof (struct LongAtom) < pos) ) 575 return false; 576 latom = (const struct LongAtom *) &buffer[pos]; 577 atomSize = ntohll (latom->size); 578 if ((atomSize < sizeof (struct LongAtom)) || 579 (atomSize + pos > size) || (atomSize + pos < atomSize)) 580 return false; 581 } 582 else 583 { 584 atomSize = ntohl (atom->size); 585 if ((atomSize < sizeof (struct Atom)) || 586 (atomSize + pos > size) || (atomSize + pos < atomSize)) 587 return false; 588 } 589 return true; 590 } 591 592 593 /** 594 * Assumes that checkAtomValid has already been called. 595 */ 596 static uint64_t 597 getAtomSize (const char *buf) 598 { 599 const struct Atom *atom; 600 const struct LongAtom *latom; 601 602 atom = (const struct Atom *) buf; 603 if (ntohl (atom->size) == 1) 604 { 605 latom = (const struct LongAtom *) buf; 606 return ntohll (latom->size); 607 } 608 return ntohl (atom->size); 609 } 610 611 612 /** 613 * Assumes that checkAtomValid has already been called. 614 */ 615 static size_t 616 getAtomHeaderSize (const char *buf) 617 { 618 const struct Atom *atom; 619 620 atom = (const struct Atom *) buf; 621 if (ntohl (atom->size) == 1) 622 return sizeof (const struct LongAtom); 623 return sizeof (struct Atom); 624 } 625 626 627 /** 628 * State carried through the recursive atom walk. 629 */ 630 struct ExtractContext 631 { 632 /** 633 * The libextractor processing callback. 634 */ 635 EXTRACTOR_MetaDataProcessor proc; 636 637 /** 638 * Closure for @e proc. 639 */ 640 void *proc_cls; 641 642 /** 643 * Set to non-zero once @e proc asked us to stop. 644 */ 645 int ret; 646 647 /** 648 * Current atom nesting depth (for recursion limiting). 649 */ 650 unsigned int depth; 651 }; 652 653 654 static void 655 addKeyword (enum EXTRACTOR_MetaType type, 656 const char *str, 657 struct ExtractContext *ec) 658 { 659 if (ec->ret != 0) 660 return; 661 ec->ret = ec->proc (ec->proc_cls, 662 "qt", 663 type, 664 EXTRACTOR_METAFORMAT_UTF8, 665 "text/plain", 666 str, 667 strlen (str) + 1); 668 } 669 670 671 static void 672 addBinary (enum EXTRACTOR_MetaType type, 673 const char *mime, 674 const void *data, 675 size_t data_len, 676 struct ExtractContext *ec) 677 { 678 if (ec->ret != 0) 679 return; 680 ec->ret = ec->proc (ec->proc_cls, 681 "qt", 682 type, 683 EXTRACTOR_METAFORMAT_BINARY, 684 mime, 685 data, 686 data_len); 687 } 688 689 690 /** 691 * Assumes that checkAtomValid has already been called. 692 * 693 * @return 0 on a fatal error (stop the current level), 694 * 1 for success, -1 for "atom not understood, skip it" 695 */ 696 typedef int 697 (*AtomHandler) (const char *input, 698 size_t size, 699 size_t pos, 700 struct ExtractContext *ec); 701 702 struct HandlerEntry 703 { 704 const char *name; 705 AtomHandler handler; 706 }; 707 708 709 /** 710 * Call the handler for the atom at the given position. 711 * Will check validity of the given atom. 712 * 713 * @return 0 on error, 1 for success, -1 for unknown atom type 714 */ 715 static int 716 handleAtom (struct HandlerEntry *handlers, 717 const char *input, 718 size_t size, 719 size_t pos, 720 struct ExtractContext *ec); 721 722 static struct HandlerEntry all_handlers[]; 723 724 /** 725 * Process atoms. 726 * @return 0 on error, 1 for success, -1 for unknown atom type 727 */ 728 static int 729 processAtoms (struct HandlerEntry *handlers, 730 const char *input, 731 size_t size, 732 struct ExtractContext *ec) 733 { 734 size_t pos; 735 736 if (size < sizeof (struct Atom)) 737 return 1; 738 if (ec->depth >= MAX_ATOM_DEPTH) 739 return 1; 740 ec->depth++; 741 pos = 0; 742 while (pos < size - sizeof (struct Atom)) 743 { 744 if (0 == handleAtom (handlers, 745 input, 746 size, 747 pos, 748 ec)) 749 { 750 ec->depth--; 751 return 0; 752 } 753 if (0 != ec->ret) 754 break; /* processor asked us to stop */ 755 pos += getAtomSize (&input[pos]); 756 } 757 ec->depth--; 758 return 1; 759 } 760 761 762 /** 763 * Process all atoms. 764 * @return 0 on error, 1 for success, -1 for unknown atom type 765 */ 766 static int 767 processAllAtoms (const char *input, 768 size_t size, 769 struct ExtractContext *ec) 770 { 771 return processAtoms (all_handlers, 772 input, 773 size, 774 ec); 775 } 776 777 778 /** 779 * Handle the moov atom. 780 * @return 0 on error, 1 for success, -1 for unknown atom type 781 */ 782 static int 783 moovHandler (const char *input, 784 size_t size, 785 size_t pos, 786 struct ExtractContext *ec) 787 { 788 uint32_t hdr = getAtomHeaderSize (&input[pos]); 789 790 return processAllAtoms (&input[pos + hdr], 791 getAtomSize (&input[pos]) - hdr, 792 ec); 793 } 794 795 796 /* see http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap1/chapter_2_section_5.html */ 797 LE_NETWORK_STRUCT_BEGIN 798 struct FileType 799 { 800 struct Atom header; 801 /* major brand */ 802 char type[4]; 803 /* minor version */ 804 unsigned int version; 805 /* compatible brands */ 806 char compatibility[4]; 807 } LE_PACKED; 808 LE_NETWORK_STRUCT_END 809 810 811 static int 812 ftypHandler (const char *input, 813 size_t size, 814 size_t pos, 815 struct ExtractContext *ec) 816 { 817 const struct FileType *ft; 818 819 if (getAtomSize (&input[pos]) < sizeof (struct FileType)) 820 return -1; 821 ft = (const struct FileType *) &input[pos]; 822 823 for (unsigned i = 0; 824 NULL != ftMap[i].ext; 825 i++) 826 { 827 if (0 == 828 memcmp (ft->type, 829 ftMap[i].ext, 830 4)) 831 { 832 addKeyword (EXTRACTOR_METATYPE_MIMETYPE, 833 ftMap[i].mime, 834 ec); 835 break; 836 } 837 } 838 return 1; 839 } 840 841 842 /** 843 * Handle the movie header ('mvhd') atom, reporting the movie duration. 844 * Supports both the 32-bit (version 0) and 64-bit (version 1) layouts. 845 * 846 * @return 1 for success, -1 if the atom could not be parsed 847 */ 848 static int 849 mvhdHandler (const char *input, 850 size_t size, 851 size_t pos, 852 struct ExtractContext *ec) 853 { 854 uint64_t asize = getAtomSize (&input[pos]); 855 uint32_t hdr = getAtomHeaderSize (&input[pos]); 856 const unsigned char *body; 857 unsigned char version; 858 uint64_t timeScale; 859 uint64_t duration; 860 char dur[24]; 861 862 if (asize < hdr + 4) 863 return -1; 864 body = (const unsigned char *) &input[pos + hdr]; 865 version = body[0]; 866 if (0 == version) 867 { 868 /* version(1) flags(3) creation(4) modification(4) 869 timeScale(4) duration(4) ... */ 870 if (asize < hdr + 20) 871 return -1; 872 timeScale = read_be32 (&body[12]); 873 duration = read_be32 (&body[16]); 874 } 875 else if (1 == version) 876 { 877 /* version(1) flags(3) creation(8) modification(8) 878 timeScale(4) duration(8) ... */ 879 if (asize < hdr + 32) 880 return -1; 881 timeScale = read_be32 (&body[20]); 882 duration = read_be64 (&body[24]); 883 } 884 else 885 { 886 return -1; 887 } 888 if (0 == timeScale) 889 return -1; 890 snprintf (dur, 891 sizeof (dur), 892 "%llus", 893 (unsigned long long) (duration / timeScale)); 894 addKeyword (EXTRACTOR_METATYPE_DURATION, 895 dur, 896 ec); 897 return 1; 898 } 899 900 901 LE_NETWORK_STRUCT_BEGIN 902 struct CompressedMovieHeaderAtom 903 { 904 struct Atom cmovAtom; 905 struct Atom dcomAtom; 906 char compressor[4]; 907 struct Atom cmvdAtom; 908 uint32_t decompressedSize; 909 } LE_PACKED; 910 LE_NETWORK_STRUCT_END 911 912 913 static int 914 cmovHandler (const char *input, 915 size_t size, 916 size_t pos, 917 struct ExtractContext *ec) 918 { 919 const struct CompressedMovieHeaderAtom *c; 920 unsigned int s; 921 char *buf; 922 int ret; 923 z_stream z_state; 924 int z_ret_code; 925 926 if (getAtomSize (&input[pos]) < sizeof (struct CompressedMovieHeaderAtom)) 927 return -1; 928 c = (const struct CompressedMovieHeaderAtom *) &input[pos]; 929 if ((ntohl (c->dcomAtom.size) != 12) || 930 (0 != memcmp (&c->dcomAtom.type, "dcom", 4)) || 931 (0 != memcmp (c->compressor, "zlib", 4)) || 932 (0 != memcmp (&c->cmvdAtom.type, "cmvd", 4)) || 933 (ntohl (c->cmvdAtom.size) != 934 getAtomSize (&input[pos]) - sizeof (struct Atom) * 2 - 4)) 935 { 936 return -1; /* dcom must be 12 bytes */ 937 } 938 s = ntohl (c->decompressedSize); 939 if (s > MAX_CMOV_SIZE) 940 return -1; /* ignore, too big! */ 941 buf = malloc (s); 942 if (NULL == buf) 943 return -1; /* out of memory, handle gracefully */ 944 945 memset (&z_state, 0, sizeof (z_state)); 946 z_state.next_in = (unsigned char *) &c[1]; /* data starts after 'c' */ 947 z_state.avail_in = ntohl (c->cmvdAtom.size) 948 - sizeof (struct Atom) /* cmvdAtom itself */ 949 - sizeof (uint32_t); /* decompressedSize field */ 950 z_state.avail_out = s; 951 z_state.next_out = (unsigned char *) buf; 952 z_state.zalloc = (alloc_func) 0; 953 z_state.zfree = (free_func) 0; 954 z_state.opaque = (voidpf) 0; 955 z_ret_code = inflateInit (&z_state); 956 if (Z_OK != z_ret_code) 957 { 958 free (buf); 959 return -1; /* crc error? */ 960 } 961 z_ret_code = inflate (&z_state, 962 Z_NO_FLUSH); 963 if ( (z_ret_code != Z_OK) && 964 (z_ret_code != Z_STREAM_END) ) 965 { 966 inflateEnd (&z_state); 967 free (buf); 968 return -1; /* decode error? */ 969 } 970 z_ret_code = inflateEnd (&z_state); 971 if (Z_OK != z_ret_code) 972 { 973 free (buf); 974 return -1; /* decode error? */ 975 } 976 if (ec->depth >= MAX_ATOM_DEPTH) 977 { 978 free (buf); 979 return -1; 980 } 981 ec->depth++; 982 ret = handleAtom (all_handlers, 983 buf, 984 s, 985 0, 986 ec); 987 ec->depth--; 988 free (buf); 989 return ret; 990 } 991 992 993 /** 994 * Handle the track header ('tkhd') atom. The (fixed-point) track 995 * width and height are the final eight bytes of the atom regardless of 996 * the atom's version, so we read them relative to the end of the box. 997 * 998 * @return 1 for success, -1 if the atom could not be parsed 999 */ 1000 static int 1001 tkhdHandler (const char *input, 1002 size_t size, 1003 size_t pos, 1004 struct ExtractContext *ec) 1005 { 1006 uint64_t asize = getAtomSize (&input[pos]); 1007 uint32_t hdr = getAtomHeaderSize (&input[pos]); 1008 const unsigned char *p; 1009 unsigned int width; 1010 unsigned int height; 1011 char dimensions[40]; 1012 1013 if (asize < hdr + 8) 1014 return -1; 1015 p = (const unsigned char *) &input[pos + asize - 8]; 1016 /* 16.16 fixed point; the integer part is the high 16 bits */ 1017 width = (p[0] << 8) | p[1]; 1018 height = (p[4] << 8) | p[5]; 1019 if (0 != width) 1020 { 1021 /* if actually a/the video track */ 1022 snprintf (dimensions, 1023 sizeof (dimensions), 1024 "%ux%u", 1025 width, 1026 height); 1027 addKeyword (EXTRACTOR_METATYPE_IMAGE_DIMENSIONS, 1028 dimensions, 1029 ec); 1030 } 1031 return 1; 1032 } 1033 1034 1035 static int 1036 trakHandler (const char *input, 1037 size_t size, 1038 size_t pos, 1039 struct ExtractContext *ec) 1040 { 1041 uint32_t hdr = getAtomHeaderSize (&input[pos]); 1042 1043 return processAllAtoms (&input[pos + hdr], 1044 getAtomSize (&input[pos]) - hdr, 1045 ec); 1046 } 1047 1048 1049 static int 1050 metaHandler (const char *input, 1051 size_t size, 1052 size_t pos, 1053 struct ExtractContext *ec) 1054 { 1055 uint32_t hdr = getAtomHeaderSize (&input[pos]); 1056 1057 if (getAtomSize (&input[pos]) < hdr + 4) 1058 return -1; 1059 return processAllAtoms (&input[pos + hdr + 4], 1060 getAtomSize (&input[pos]) - hdr - 4, 1061 ec); 1062 } 1063 1064 1065 LE_NETWORK_STRUCT_BEGIN 1066 struct InternationalText 1067 { 1068 struct Atom header; 1069 uint16_t length; 1070 uint16_t language; 1071 } LE_PACKED; 1072 LE_NETWORK_STRUCT_END 1073 1074 1075 /* 1076 * see http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap2/chapter_3_section_2.html 1077 * "User Data Text Strings and Language Codes" 1078 */ 1079 static int 1080 processTextTag (const char *input, 1081 size_t size, 1082 size_t pos, 1083 enum EXTRACTOR_MetaType type, struct ExtractContext *ec) 1084 { 1085 uint64_t as; 1086 uint16_t len; 1087 uint16_t lang; 1088 const struct InternationalText *txt; 1089 char *meta; 1090 1091 /* contains "international text": 1092 16-bit size + 16 bit language code */ 1093 as = getAtomSize (&input[pos]); 1094 if (as < sizeof (struct InternationalText)) 1095 return -1; /* invalid */ 1096 txt = (const struct InternationalText *) &input[pos]; 1097 len = ntohs (txt->length); 1098 if (len + sizeof (struct InternationalText) > as) 1099 return -1; /* invalid */ 1100 lang = ntohs (txt->language); 1101 if (lang < sizeof (languages) / sizeof (char *)) 1102 addKeyword (EXTRACTOR_METATYPE_LANGUAGE, 1103 languages[lang], 1104 ec); 1105 1106 meta = malloc (len + 1); 1107 if (NULL == meta) 1108 return -1; 1109 memcpy (meta, 1110 &txt[1], 1111 len); 1112 meta[len] = '\0'; 1113 for (unsigned int i = 0; i < len; i++) 1114 if (meta[i] == '\r') 1115 meta[i] = '\n'; 1116 addKeyword (type, 1117 meta, 1118 ec); 1119 free (meta); 1120 return 1; 1121 } 1122 1123 1124 static int 1125 c_Handler (const char *input, 1126 size_t size, 1127 size_t pos, 1128 struct ExtractContext *ec) 1129 { 1130 for (unsigned int i = 0; 1131 NULL != cHm[i].pfx; 1132 i++) 1133 if (0 == memcmp (&input[pos + 5], 1134 cHm[i].pfx, 1135 3)) 1136 return processTextTag (input, 1137 size, 1138 pos, 1139 cHm[i].type, 1140 ec); 1141 return -1; /* not found */ 1142 } 1143 1144 1145 /** 1146 * Process the 'data' atom nested inside an iTunes-style 'ilst' entry. 1147 * 1148 * @param input start of the buffer 1149 * @param size size of the parent (ilst entry) atom 1150 * @param pos offset of the 'data' atom within @a input 1151 * @param patom pointer to the parent (ilst entry) atom 1152 * @param type metadata type to report the value as 1153 * @return 1 for success, -1 if the atom could not be handled 1154 */ 1155 static int 1156 processDataAtom (const char *input, 1157 size_t size, /* parent atom size */ 1158 size_t pos, 1159 const char *patom, 1160 enum EXTRACTOR_MetaType type, 1161 struct ExtractContext *ec) 1162 { 1163 char *meta; 1164 unsigned char version; 1165 unsigned int wellknown; 1166 uint64_t asize; 1167 unsigned int len; 1168 uint32_t hdr; 1169 int i; 1170 1171 hdr = getAtomHeaderSize (&input[pos]); 1172 asize = getAtomSize (&input[pos]); 1173 if (0 != 1174 memcmp (&input[pos + 4], 1175 "data", 1176 4)) 1177 return -1; 1178 1179 if ((asize < hdr + 8) || /* header + u32 type + u32 locale */ 1180 (asize > (getAtomSize (&patom[0]) - 8))) 1181 return -1; 1182 1183 len = (unsigned int) (asize - (hdr + 8)); 1184 1185 version = input[pos + 8]; 1186 /* "well known type" indicator (the low 24 bits of the type field) */ 1187 wellknown = ((unsigned char) input[pos + 9] << 16) 1188 | ((unsigned char) input[pos + 10] << 8) 1189 | (unsigned char) input[pos + 11]; 1190 1191 if (0 != version) 1192 return -1; 1193 1194 /* cover art: well-known type 13 = JPEG, 14 = PNG, 27 = BMP */ 1195 if ( (EXTRACTOR_METATYPE_COVER_PICTURE == type) && 1196 ( (13 == wellknown) || 1197 (14 == wellknown) || 1198 (27 == wellknown) ) ) 1199 { 1200 const char *mime; 1201 1202 if (0 == len) 1203 return -1; 1204 switch (wellknown) 1205 { 1206 case 13: 1207 mime = "image/jpeg"; 1208 break; 1209 case 14: 1210 mime = "image/png"; 1211 break; 1212 default: 1213 mime = "image/bmp"; 1214 break; 1215 } 1216 addBinary (type, 1217 mime, 1218 &input[pos + 16], 1219 len, 1220 ec); 1221 return 1; 1222 } 1223 1224 if (0x0 == wellknown) /* binary data */ 1225 { 1226 if (0 == 1227 memcmp (&patom[4], 1228 "gnre", 1229 4)) 1230 { 1231 if (len >= 2) 1232 { 1233 uint16_t genre = ((uint8_t) input[pos + 16] << 8) 1234 | (uint8_t) input[pos + 17]; 1235 1236 if ((genre > 0) && (genre <= GENRE_NAME_COUNT)) 1237 addKeyword (type, 1238 genre_names[genre - 1], 1239 ec); 1240 } 1241 return 1; 1242 } 1243 else if ( (0 == 1244 memcmp (&patom[4], 1245 "trkn", 1246 4)) || 1247 (0 == 1248 memcmp (&patom[4], 1249 "disk", 1250 4))) 1251 { 1252 if (len >= 4) 1253 { 1254 unsigned short n = ((unsigned char) input[pos + 18] << 8) 1255 | (unsigned char) input[pos + 19]; 1256 char s[8]; 1257 1258 snprintf (s, 1259 sizeof (s), 1260 "%d", 1261 n); 1262 addKeyword (type, 1263 s, 1264 ec); 1265 } 1266 return 1; 1267 } 1268 else if (0 == 1269 memcmp (&patom[4], 1270 "tmpo", 1271 4)) 1272 { 1273 if (len >= 2) 1274 { 1275 unsigned short n = ((unsigned char) input[pos + 16] << 8) 1276 | (unsigned char) input[pos + 17]; 1277 char s[8]; 1278 1279 snprintf (s, 1280 sizeof (s), 1281 "%u", 1282 n); 1283 addKeyword (type, 1284 s, 1285 ec); 1286 } 1287 return 1; 1288 } 1289 else 1290 { 1291 return -1; 1292 } 1293 } 1294 else if (0x15 == wellknown) /* signed/unsigned big-endian integer */ 1295 { 1296 unsigned long long n = 0; 1297 char s[24]; 1298 unsigned int j; 1299 1300 if ((len < 1) || (len > 8)) 1301 return -1; 1302 for (j = 0; j < len; j++) 1303 n = (n << 8) | (unsigned char) input[pos + 16 + j]; 1304 snprintf (s, 1305 sizeof (s), 1306 "%llu", 1307 n); 1308 addKeyword (type, 1309 s, 1310 ec); 1311 return 1; 1312 } 1313 else if (wellknown == 0x1) /* UTF-8 text data */ 1314 { 1315 meta = malloc (len + 1); 1316 if (meta == NULL) 1317 return -1; 1318 memcpy (meta, 1319 &input[pos + 16], 1320 len); 1321 meta[len] = '\0'; 1322 for (i = 0; i < len; i++) 1323 if (meta[i] == '\r') 1324 meta[i] = '\n'; 1325 addKeyword (type, 1326 meta, 1327 ec); 1328 free (meta); 1329 return 1; 1330 } 1331 1332 return -1; 1333 } 1334 1335 1336 /* NOTE: iTunes tag processing should, in theory, be limited to iTunes 1337 * file types (from ftyp), but, in reality, it seems that there are other 1338 * files, like 3gpp, out in the wild with iTunes tags. */ 1339 static int 1340 iTunesTagHandler (const char *input, 1341 size_t size, 1342 size_t pos, 1343 struct ExtractContext *ec) 1344 { 1345 uint64_t asize; 1346 uint32_t hdr; 1347 1348 hdr = getAtomHeaderSize (&input[pos]); 1349 asize = getAtomSize (&input[pos]); 1350 1351 if (asize < hdr + 8) /* header + at least one atom */ 1352 return -1; 1353 /* #processDataAtom immediately reads the child atom's own header, and 1354 both #getAtomSize and #getAtomHeaderSize require #checkAtomValid to 1355 have run first. The test above only says that the *declared* size 1356 of this entry leaves room for eight more bytes; a child whose size 1357 field is the 64-bit escape value 1 is a `struct LongAtom' and is 1358 read sixteen bytes wide, which is eight past that guarantee. */ 1359 if (! checkAtomValid (input, 1360 size, 1361 pos + hdr)) 1362 return -1; 1363 1364 for (unsigned int i = 0; 1365 NULL != it_to_extr_table[i].atom_type; 1366 i++) 1367 if (0 == memcmp (&input[pos + 4], 1368 it_to_extr_table[i].atom_type, 1369 4)) 1370 return processDataAtom (input, 1371 asize, 1372 pos + hdr, 1373 &input[pos], 1374 it_to_extr_table[i].type, 1375 ec); 1376 return -1; 1377 } 1378 1379 1380 /** 1381 * Handle the iTunes metadata list ('ilst'). Its children have 1382 * arbitrary four-character keys, so rather than a name table we simply 1383 * iterate them and let #iTunesTagHandler decide what is interesting. 1384 * 1385 * @return 0 on a fatal error, 1 otherwise 1386 */ 1387 static int 1388 ilstHandler (const char *input, 1389 size_t size, 1390 size_t pos, 1391 struct ExtractContext *ec) 1392 { 1393 uint32_t hdr = getAtomHeaderSize (&input[pos]); 1394 size_t end = pos + getAtomSize (&input[pos]); 1395 size_t cpos = pos + hdr; 1396 1397 if (ec->depth >= MAX_ATOM_DEPTH) 1398 return 1; 1399 ec->depth++; 1400 while ((cpos + sizeof (struct Atom) <= end) && 1401 (checkAtomValid (input, end, cpos))) 1402 { 1403 iTunesTagHandler (input, end, cpos, ec); 1404 if (0 != ec->ret) 1405 break; 1406 cpos += getAtomSize (&input[cpos]); 1407 } 1408 ec->depth--; 1409 return 1; 1410 } 1411 1412 1413 /** 1414 * Handle the user-data ('udta') atom. It mixes classic QuickTime 1415 * '(C)xyz' international-text tags with structural sub-atoms such as 1416 * 'meta'/'ilst', so we iterate the children and dispatch accordingly. 1417 * 1418 * @return 0 on a fatal error, 1 otherwise 1419 */ 1420 static int 1421 udtaHandler (const char *input, 1422 size_t size, 1423 size_t pos, 1424 struct ExtractContext *ec) 1425 { 1426 uint32_t hdr = getAtomHeaderSize (&input[pos]); 1427 size_t end = pos + getAtomSize (&input[pos]); 1428 size_t cpos = pos + hdr; 1429 1430 if (ec->depth >= MAX_ATOM_DEPTH) 1431 return 1; 1432 ec->depth++; 1433 while ((cpos + sizeof (struct Atom) <= end) && 1434 (checkAtomValid (input, end, cpos))) 1435 { 1436 if (0xA9 == (unsigned char) input[cpos + 4]) 1437 c_Handler (input, 1438 end, 1439 cpos, 1440 ec); 1441 else 1442 handleAtom (all_handlers, 1443 input, 1444 end, 1445 cpos, 1446 ec); 1447 if (0 != ec->ret) 1448 break; 1449 cpos += getAtomSize (&input[cpos]); 1450 } 1451 ec->depth--; 1452 return 1; 1453 } 1454 1455 1456 static struct HandlerEntry all_handlers[] = { 1457 {"moov", &moovHandler}, 1458 {"cmov", &cmovHandler}, 1459 {"mvhd", &mvhdHandler}, 1460 {"trak", &trakHandler}, 1461 {"tkhd", &tkhdHandler}, 1462 {"ilst", &ilstHandler}, 1463 {"meta", &metaHandler}, 1464 {"udta", &udtaHandler}, 1465 {"ftyp", &ftypHandler}, 1466 {NULL, NULL}, 1467 }; 1468 1469 1470 /** 1471 * Call the handler for the atom at the given position. 1472 * @return 0 on error, 1 for success, -1 for unknown atom type 1473 */ 1474 static int 1475 handleAtom (struct HandlerEntry *handlers, 1476 const char *input, 1477 size_t size, 1478 size_t pos, 1479 struct ExtractContext *ec) 1480 { 1481 if (! checkAtomValid (input, 1482 size, 1483 pos)) 1484 return 0; 1485 for (unsigned i = 0; 1486 handlers[i].name != NULL; 1487 i++) 1488 { 1489 if (0 == 1490 memcmp (&input[pos + 4], 1491 handlers[i].name, 1492 4)) 1493 { 1494 return handlers[i].handler (input, 1495 size, 1496 pos, 1497 ec); 1498 } 1499 } 1500 return -1; 1501 } 1502 1503 1504 /** 1505 * Read exactly @a len bytes from absolute offset @a off into @a dst. 1506 * 1507 * The extraction context exposes the file through a sliding shared 1508 * memory window, so a single read may return fewer bytes than 1509 * requested; we seek once and then loop until the request is satisfied. 1510 * 1511 * @return 0 on success, -1 on error / short file 1512 */ 1513 static int 1514 qt_pread (struct EXTRACTOR_ExtractContext *ec, 1515 uint64_t off, 1516 void *dst, 1517 size_t len) 1518 { 1519 unsigned char *out = dst; 1520 1521 if ((int64_t) off != ec->seek (ec->cls, (int64_t) off, SEEK_SET)) 1522 return -1; 1523 while (len > 0) 1524 { 1525 void *buf; 1526 ssize_t got; 1527 1528 got = ec->read (ec->cls, 1529 &buf, 1530 len); 1531 if (got <= 0) 1532 return -1; 1533 memcpy (out, 1534 buf, 1535 (size_t) got); 1536 out += got; 1537 len -= (size_t) got; 1538 } 1539 return 0; 1540 } 1541 1542 1543 /** 1544 * Top-level atom types worth pulling into memory. Everything else 1545 * (notably the huge 'mdat' payload, plus 'free'/'skip'/'wide') is 1546 * skipped without ever being read. 1547 */ 1548 static bool 1549 is_interesting_top_atom (const unsigned char *type) 1550 { 1551 static const char *const interesting[] = { 1552 "moov", "ftyp", "meta", "udta", "uuid", "pnot", NULL 1553 }; 1554 1555 for (unsigned int i = 0; 1556 NULL != interesting[i]; 1557 i++) 1558 if (0 == memcmp (type, 1559 interesting[i], 1560 4)) 1561 return true; 1562 return false; 1563 } 1564 1565 1566 /** 1567 * Main entry method for the QuickTime/MP4 extraction plugin. 1568 * 1569 * @param ec extraction context provided to the plugin 1570 */ 1571 void 1572 EXTRACTOR_qt_extract_method (struct EXTRACTOR_ExtractContext *ec); 1573 1574 void 1575 EXTRACTOR_qt_extract_method (struct EXTRACTOR_ExtractContext *ec) 1576 { 1577 struct ExtractContext xc; 1578 uint64_t fsize; 1579 uint64_t pos; 1580 1581 fsize = ec->get_size (ec->cls); 1582 if ((UINT64_MAX == fsize) || (fsize < sizeof (struct Atom))) 1583 return; 1584 1585 xc.proc = ec->proc; 1586 xc.proc_cls = ec->cls; 1587 xc.ret = 0; 1588 xc.depth = 0; 1589 1590 pos = 0; 1591 while ( (0 == xc.ret) && 1592 (pos + sizeof (struct Atom) <= fsize) ) 1593 { 1594 unsigned char hdr[16]; 1595 uint64_t asize; 1596 unsigned int hsize; 1597 1598 if (0 != qt_pread (ec, pos, hdr, 8)) 1599 break; 1600 asize = ((uint64_t) hdr[0] << 24) | ((uint64_t) hdr[1] << 16) 1601 | ((uint64_t) hdr[2] << 8) | (uint64_t) hdr[3]; 1602 if (1 == asize) 1603 { 1604 if ((pos + 16 > fsize) || 1605 (0 != qt_pread (ec, pos + 8, &hdr[8], 8))) 1606 break; 1607 asize = ((uint64_t) hdr[8] << 56) | ((uint64_t) hdr[9] << 48) 1608 | ((uint64_t) hdr[10] << 40) | ((uint64_t) hdr[11] << 32) 1609 | ((uint64_t) hdr[12] << 24) | ((uint64_t) hdr[13] << 16) 1610 | ((uint64_t) hdr[14] << 8) | (uint64_t) hdr[15]; 1611 hsize = 16; 1612 } 1613 else if (0 == asize) 1614 { 1615 /* atom extends to end of file */ 1616 asize = fsize - pos; 1617 hsize = 8; 1618 } 1619 else 1620 { 1621 hsize = 8; 1622 } 1623 if ((asize < hsize) || (pos + asize > fsize) || (pos + asize < pos)) 1624 break; 1625 1626 if (is_interesting_top_atom (&hdr[4]) && 1627 (asize <= MAX_ATOM_SIZE)) 1628 { 1629 char *buf = malloc ((size_t) asize); 1630 1631 if (NULL != buf) 1632 { 1633 if (0 == qt_pread (ec, 1634 pos, 1635 buf, 1636 (size_t) asize)) 1637 handleAtom (all_handlers, 1638 buf, 1639 (size_t) asize, 1640 0, 1641 &xc); 1642 free (buf); 1643 } 1644 } 1645 pos += asize; 1646 } 1647 } 1648 1649 1650 /* end of qt_extractor.c */