libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

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 */