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