libextractor

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

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