libextractor

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

png_extractor.c (14332B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2002, 2003, 2004, 2005, 2009, 2012 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/png_extractor.c
     22  * @brief plugin to support PNG files
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include <zlib.h>
     27 #include "extractor.h"
     28 #include "convert.h"
     29 
     30 /**
     31  * Header that every PNG file must start with.
     32  */
     33 #define PNG_HEADER "\211PNG\r\n\032\n"
     34 
     35 
     36 /**
     37  * Function to create 0-terminated string from the
     38  * first n characters of the given input.
     39  *
     40  * @param str input string
     41  * @param n length of the input
     42  * @return n-bytes from str followed by 0-termination, NULL on error
     43  */
     44 static char *
     45 stndup (const char *str,
     46         size_t n)
     47 {
     48   char *tmp;
     49 
     50   if (n + 1 < n)
     51     return NULL;
     52   if (NULL == (tmp = malloc (n + 1)))
     53     return NULL;
     54   tmp[n] = '\0';
     55   memcpy (tmp, str, n);
     56   return tmp;
     57 }
     58 
     59 
     60 /**
     61  * strnlen is GNU specific, let's redo it here to be
     62  * POSIX compliant.
     63  *
     64  * @param str input string
     65  * @param maxlen maximum length of str
     66  * @return first position of 0-terminator in str, or maxlen
     67  */
     68 static size_t
     69 stnlen (const char *str,
     70         size_t maxlen)
     71 {
     72   size_t ret;
     73 
     74   ret = 0;
     75   while ( (ret < maxlen) &&
     76           ('\0' != str[ret]) )
     77     ret++;
     78   return ret;
     79 }
     80 
     81 
     82 /**
     83  * Interpret the 4 bytes in 'buf' as a big-endian
     84  * encoded 32-bit integer, convert and return.
     85  *
     86  * @param pos (unaligned) pointer to 4 byte integer
     87  * @return converted integer in host byte order
     88  */
     89 static uint32_t
     90 get_int_at (const void *pos)
     91 {
     92   uint32_t i;
     93 
     94   memcpy (&i, pos, sizeof (i));
     95   return htonl (i);
     96 }
     97 
     98 
     99 /**
    100  * Map from PNG meta data descriptor strings
    101  * to LE types.
    102  */
    103 static struct
    104 {
    105   /**
    106    * PNG name.
    107    */
    108   const char *name;
    109 
    110   /**
    111    * Corresponding LE type.
    112    */
    113   enum EXTRACTOR_MetaType type;
    114 } tagmap[] = {
    115   { "Author", EXTRACTOR_METATYPE_AUTHOR_NAME },
    116   { "Description", EXTRACTOR_METATYPE_DESCRIPTION },
    117   { "Comment", EXTRACTOR_METATYPE_COMMENT },
    118   { "Copyright", EXTRACTOR_METATYPE_COPYRIGHT },
    119   { "Source", EXTRACTOR_METATYPE_SOURCE_DEVICE },
    120   { "Creation Time", EXTRACTOR_METATYPE_CREATION_DATE },
    121   { "Title", EXTRACTOR_METATYPE_TITLE },
    122   { "Software", EXTRACTOR_METATYPE_PRODUCED_BY_SOFTWARE },
    123   { "Disclaimer", EXTRACTOR_METATYPE_DISCLAIMER },
    124   { "Warning", EXTRACTOR_METATYPE_WARNING },
    125   { "Signature", EXTRACTOR_METATYPE_UNKNOWN },
    126   { NULL, EXTRACTOR_METATYPE_RESERVED }
    127 };
    128 
    129 
    130 /**
    131  * Give the given metadata to LE.  Set "ret" to 1 and
    132  * goto 'FINISH' if LE says we are done.
    133  *
    134  * @param t type of the metadata
    135  * @param s utf8 string with the metadata
    136  */
    137 #define ADD(t,s) do { if (0 != (ret = ec->proc (ec->cls, "png", t, \
    138                                                 EXTRACTOR_METAFORMAT_UTF8, \
    139                                                 "text/plain", s, strlen (s) \
    140                                                 + 1))) goto FINISH; \
    141 } while (0)
    142 
    143 
    144 /**
    145  * Give the given metadata to LE and free the memory.  Set "ret" to 1 and
    146  * goto 'FINISH' if LE says we are done.
    147  *
    148  * @param t type of the metadata
    149  * @param s utf8 string with the metadata, to be freed afterwards
    150  */
    151 #define ADDF(t,s) do { if ( (NULL != s) && (0 != (ret = ec->proc (ec->cls, \
    152                                                                   "png", t, \
    153                                                                   EXTRACTOR_METAFORMAT_UTF8, \
    154                                                                   "text/plain", \
    155                                                                   s, strlen (s) \
    156                                                                   + 1))) ) { \
    157                          free (s); goto FINISH; } if (NULL != s) free (s); \
    158 } while (0)
    159 
    160 
    161 /**
    162  * Process EXt tag.
    163  *
    164  * @param ec extraction context
    165  * @param length length of the tag
    166  * @return 0 to continue extracting, 1 if we are done
    167  */
    168 static int
    169 processtEXt (struct EXTRACTOR_ExtractContext *ec,
    170              uint32_t length)
    171 {
    172   void *ptr;
    173   unsigned char *data;
    174   char *keyword;
    175   size_t off;
    176   unsigned int i;
    177   int ret;
    178 
    179   if (length != ec->read (ec->cls, &ptr, length))
    180     return 1;
    181   data = ptr;
    182   off = stnlen ((char*) data, length) + 1;
    183   if (off >= length)
    184     return 0;                /* failed to find '\0' */
    185   if (NULL == (keyword = EXTRACTOR_common_convert_to_utf8 ((char*) &data[off],
    186                                                            length - off,
    187                                                            "ISO-8859-1")))
    188     return 0;
    189   ret = 0;
    190   for (i = 0; NULL != tagmap[i].name; i++)
    191     if (0 == strcmp (tagmap[i].name, (char*) data))
    192     {
    193       ADDF (tagmap[i].type, keyword);
    194       return 0;
    195     }
    196   ADDF (EXTRACTOR_METATYPE_KEYWORDS, keyword);
    197 FINISH:
    198   return ret;
    199 }
    200 
    201 
    202 /**
    203  * Process iTXt tag.
    204  *
    205  * @param ec extraction context
    206  * @param length length of the tag
    207  * @return 0 to continue extracting, 1 if we are done
    208  */
    209 static int
    210 processiTXt (struct EXTRACTOR_ExtractContext *ec,
    211              uint32_t length)
    212 {
    213   void *ptr;
    214   unsigned char *data;
    215   size_t pos;
    216   char *keyword;
    217   const char *language;
    218   const char *translated;
    219   unsigned int i;
    220   int compressed;
    221   char *buf;
    222   char *lan;
    223   uLongf bufLen;
    224   int ret;
    225   int zret;
    226 
    227   if (length != ec->read (ec->cls,
    228                           &ptr,
    229                           length))
    230     return 1;
    231   data = ptr;
    232   pos = stnlen ((char *) data,
    233                 length) + 1;
    234   if (pos >= length)
    235     return 0;
    236   compressed = data[pos++];
    237   /* `pos > length' can never hold here -- the check above left pos at
    238      most length - 1 and it was incremented once -- so it let pos ==
    239      length through and the read below went one byte past the buffer
    240      ec->read() handed us.  Only `length' bytes are ours; at a window
    241      boundary the next byte is not even mapped. */
    242   if (pos >= length)
    243     return 0;
    244   if (compressed && (0 != data[pos++]))
    245     return 0;                /* bad compression method */
    246   if (pos >= length)
    247     return 0;
    248   language = (char *) &data[pos];
    249   ret = 0;
    250   if ( (stnlen (language, length - pos) > 0) &&
    251        (NULL != (lan = stndup (language, length - pos))) )
    252     ADDF (EXTRACTOR_METATYPE_LANGUAGE, lan);
    253   pos += stnlen (language, length - pos) + 1;
    254   if (pos + 1 >= length)
    255     return 0;
    256   translated = (char*) &data[pos];      /* already in utf-8! */
    257   if ( (stnlen (translated, length - pos) > 0) &&
    258        (NULL != (lan = stndup (translated, length - pos))) )
    259     ADDF (EXTRACTOR_METATYPE_KEYWORDS, lan);
    260   pos += stnlen (translated, length - pos) + 1;
    261   if (pos >= length)
    262     return 0;
    263 
    264   if (compressed)
    265   {
    266     bufLen = 1024 + 2 * (length - pos);
    267     while (1)
    268     {
    269       if (bufLen * 2 < bufLen)
    270         return 0;
    271       bufLen *= 2;
    272       if (bufLen > 50 * (length - pos))
    273       {
    274         /* printf("zlib problem"); */
    275         return 0;
    276       }
    277       if (NULL == (buf = malloc (bufLen)))
    278       {
    279         /* printf("out of memory"); */
    280         return 0;            /* out of memory */
    281       }
    282       if (Z_OK ==
    283           (zret = uncompress ((Bytef *) buf,
    284                               &bufLen,
    285                               (const Bytef *) &data[pos], length - pos)))
    286       {
    287         /* printf("zlib ok"); */
    288         break;
    289       }
    290       free (buf);
    291       if (Z_BUF_ERROR != zret)
    292         return 0;            /* unknown error, abort */
    293     }
    294     keyword = stndup (buf, bufLen);
    295     free (buf);
    296   }
    297   else
    298   {
    299     keyword = stndup ((char *) &data[pos], length - pos);
    300   }
    301   if (NULL == keyword)
    302     return ret;
    303   for (i = 0; NULL != tagmap[i].name; i++)
    304     if (0 == strcmp (tagmap[i].name, (char*) data))
    305     {
    306       ADDF (tagmap[i].type, keyword /* already in utf8 */);
    307       return 0;
    308     }
    309   ADDF (EXTRACTOR_METATYPE_COMMENT, keyword);
    310 FINISH:
    311   return ret;
    312 }
    313 
    314 
    315 /**
    316  * Process IHDR tag.
    317  *
    318  * @param ec extraction context
    319  * @param length length of the tag
    320  * @return 0 to continue extracting, 1 if we are done
    321  */
    322 static int
    323 processIHDR (struct EXTRACTOR_ExtractContext *ec,
    324              uint32_t length)
    325 {
    326   void *ptr;
    327   unsigned char *data;
    328   char tmp[128];
    329   int ret;
    330 
    331   if (length < 12)
    332     return 0;
    333   if (length != ec->read (ec->cls, &ptr, length))
    334     return 1;
    335   data = ptr;
    336   ret = 0;
    337   snprintf (tmp,
    338             sizeof (tmp),
    339             "%ux%u",
    340             get_int_at (data), get_int_at (&data[4]));
    341   ADD (EXTRACTOR_METATYPE_IMAGE_DIMENSIONS, tmp);
    342 FINISH:
    343   return ret;
    344 }
    345 
    346 
    347 /**
    348  * Process zTXt tag.
    349  *
    350  * @param ec extraction context
    351  * @param length length of the tag
    352  * @return 0 to continue extracting, 1 if we are done
    353  */
    354 static int
    355 processzTXt (struct EXTRACTOR_ExtractContext *ec,
    356              uint32_t length)
    357 {
    358   void *ptr;
    359   unsigned char *data;
    360   char *keyword;
    361   size_t off;
    362   unsigned int i;
    363   char *buf;
    364   uLongf bufLen;
    365   int zret;
    366   int ret;
    367 
    368   if (length != ec->read (ec->cls, &ptr, length))
    369     return 1;
    370   data = ptr;
    371   off = stnlen ((char *) data, length) + 1;
    372   if (off >= length)
    373     return 0;                /* failed to find '\0' */
    374   if (0 != data[off])
    375     return 0;                /* compression method must be 0 */
    376   off++;
    377   ret = 0;
    378   bufLen = 1024 + 2 * (length - off);
    379   while (1)
    380   {
    381     if (bufLen * 2 < bufLen)
    382       return 0;
    383     bufLen *= 2;
    384     if (bufLen > 50 * (length - off))
    385     {
    386       /* printf("zlib problem"); */
    387       return 0;
    388     }
    389     if (NULL == (buf = malloc (bufLen)))
    390     {
    391       /* printf("out of memory"); */
    392       return 0;              /* out of memory */
    393     }
    394     if (Z_OK ==
    395         (zret = uncompress ((Bytef *) buf,
    396                             &bufLen,
    397                             (const Bytef *) &data[off],
    398                             length - off)))
    399     {
    400       /* printf("zlib ok"); */
    401       break;
    402     }
    403     free (buf);
    404     if (Z_BUF_ERROR != zret)
    405       return 0;              /* unknown error, abort */
    406   }
    407   keyword = EXTRACTOR_common_convert_to_utf8 (buf,
    408                                               bufLen,
    409                                               "ISO-8859-1");
    410   free (buf);
    411   for (i = 0; NULL != tagmap[i].name; i++)
    412     if (0 == strcmp (tagmap[i].name, (char*)  data))
    413     {
    414       ADDF (tagmap[i].type, keyword);
    415       return 0;
    416     }
    417   ADDF (EXTRACTOR_METATYPE_COMMENT, keyword);
    418 FINISH:
    419   return ret;
    420 }
    421 
    422 
    423 /**
    424  * Process IME tag.
    425  *
    426  * @param ec extraction context
    427  * @param length length of the tag
    428  * @return 0 to continue extracting, 1 if we are done
    429  */
    430 static int
    431 processtIME (struct EXTRACTOR_ExtractContext *ec,
    432              uint32_t length)
    433 {
    434   void *ptr;
    435   unsigned char *data;
    436   unsigned short y;
    437   unsigned int year;
    438   unsigned int mo;
    439   unsigned int day;
    440   unsigned int h;
    441   unsigned int m;
    442   unsigned int s;
    443   char val[256];
    444   int ret;
    445 
    446   if (length != 7)
    447     return 0;
    448   if (length != ec->read (ec->cls, &ptr, length))
    449     return 1;
    450   data = ptr;
    451   ret = 0;
    452   memcpy (&y, data, sizeof (uint16_t));
    453   year = ntohs (y);
    454   mo = (unsigned char) data[2];
    455   day = (unsigned char) data[3];
    456   h = (unsigned char) data[4];
    457   m = (unsigned char) data[5];
    458   s = (unsigned char) data[6];
    459   snprintf (val,
    460             sizeof (val),
    461             "%04u-%02u-%02u %02d:%02d:%02d",
    462             year, mo, day, h, m, s);
    463   ADD (EXTRACTOR_METATYPE_MODIFICATION_DATE, val);
    464 FINISH:
    465   return ret;
    466 }
    467 
    468 
    469 /**
    470  * Main entry method for the 'image/png' extraction plugin.
    471  *
    472  * @param ec extraction context provided to the plugin
    473  */
    474 void
    475 EXTRACTOR_png_extract_method (struct EXTRACTOR_ExtractContext *ec)
    476 {
    477   void *data;
    478   uint32_t length;
    479   int64_t pos;
    480   int ret;
    481   ssize_t len;
    482 
    483   len = strlen (PNG_HEADER);
    484   if (len != ec->read (ec->cls,
    485                        &data,
    486                        len))
    487     return;
    488   if (0 != strncmp ((const char*) data,
    489                     PNG_HEADER,
    490                     len))
    491     return;
    492   ADD (EXTRACTOR_METATYPE_MIMETYPE,
    493        "image/png");
    494   ret = 0;
    495   while (0 == ret)
    496   {
    497     char chunk[sizeof (uint32_t) + 4];
    498 
    499     if (sizeof (chunk) !=
    500         ec->read (ec->cls,
    501                   &data,
    502                   sizeof (chunk)))
    503       break;
    504     /* The pointer read() returns addresses the shared memory window and
    505        is only valid until the next read() or seek() slides it.  Both the
    506        ec->seek() below and the process*() calls do that, so take a copy
    507        of the chunk header before any of them runs. */
    508     memcpy (chunk,
    509             data,
    510             sizeof (chunk));
    511     length = get_int_at (chunk);
    512     if (0 > (pos = ec->seek (ec->cls,
    513                              0,
    514                              SEEK_CUR)))
    515       break;
    516     pos += ((int64_t) length) + 4;   /* Chunk type, data, crc */
    517     if (0 == strncmp (&chunk[sizeof (uint32_t)],
    518                       "IHDR",
    519                       4))
    520       ret = processIHDR (ec,
    521                          length);
    522     else if (0 == strncmp (&chunk[sizeof (uint32_t)],
    523                            "iTXt",
    524                            4))
    525       ret = processiTXt (ec,
    526                          length);
    527     else if (0 == strncmp (&chunk[sizeof (uint32_t)],
    528                            "tEXt",
    529                            4))
    530       ret = processtEXt (ec,
    531                          length);
    532     else if (0 == strncmp (&chunk[sizeof (uint32_t)],
    533                            "zTXt",
    534                            4))
    535       ret = processzTXt (ec,
    536                          length);
    537     else if (0 == strncmp (&chunk[sizeof (uint32_t)],
    538                            "tIME",
    539                            4))
    540       ret = processtIME (ec,
    541                          length);
    542     if (ret != 0)
    543       break;
    544     if (pos != ec->seek (ec->cls,
    545                          pos,
    546                          SEEK_SET))
    547       break;
    548   }
    549 FINISH:
    550   return;
    551 }
    552 
    553 
    554 /* end of png_extractor.c */