libextractor

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

png_extractor.c (13607B)


      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   if (pos > length)
    238     return 0;
    239   if (compressed && (0 != data[pos++]))
    240     return 0;                /* bad compression method */
    241   if (pos > length)
    242     return 0;
    243   language = (char *) &data[pos];
    244   ret = 0;
    245   if ( (stnlen (language, length - pos) > 0) &&
    246        (NULL != (lan = stndup (language, length - pos))) )
    247     ADDF (EXTRACTOR_METATYPE_LANGUAGE, lan);
    248   pos += stnlen (language, length - pos) + 1;
    249   if (pos + 1 >= length)
    250     return 0;
    251   translated = (char*) &data[pos];      /* already in utf-8! */
    252   if ( (stnlen (translated, length - pos) > 0) &&
    253        (NULL != (lan = stndup (translated, length - pos))) )
    254     ADDF (EXTRACTOR_METATYPE_KEYWORDS, lan);
    255   pos += stnlen (translated, length - pos) + 1;
    256   if (pos >= length)
    257     return 0;
    258 
    259   if (compressed)
    260   {
    261     bufLen = 1024 + 2 * (length - pos);
    262     while (1)
    263     {
    264       if (bufLen * 2 < bufLen)
    265         return 0;
    266       bufLen *= 2;
    267       if (bufLen > 50 * (length - pos))
    268       {
    269         /* printf("zlib problem"); */
    270         return 0;
    271       }
    272       if (NULL == (buf = malloc (bufLen)))
    273       {
    274         /* printf("out of memory"); */
    275         return 0;            /* out of memory */
    276       }
    277       if (Z_OK ==
    278           (zret = uncompress ((Bytef *) buf,
    279                               &bufLen,
    280                               (const Bytef *) &data[pos], length - pos)))
    281       {
    282         /* printf("zlib ok"); */
    283         break;
    284       }
    285       free (buf);
    286       if (Z_BUF_ERROR != zret)
    287         return 0;            /* unknown error, abort */
    288     }
    289     keyword = stndup (buf, bufLen);
    290     free (buf);
    291   }
    292   else
    293   {
    294     keyword = stndup ((char *) &data[pos], length - pos);
    295   }
    296   if (NULL == keyword)
    297     return ret;
    298   for (i = 0; NULL != tagmap[i].name; i++)
    299     if (0 == strcmp (tagmap[i].name, (char*) data))
    300     {
    301       ADDF (tagmap[i].type, keyword /* already in utf8 */);
    302       return 0;
    303     }
    304   ADDF (EXTRACTOR_METATYPE_COMMENT, keyword);
    305 FINISH:
    306   return ret;
    307 }
    308 
    309 
    310 /**
    311  * Process IHDR tag.
    312  *
    313  * @param ec extraction context
    314  * @param length length of the tag
    315  * @return 0 to continue extracting, 1 if we are done
    316  */
    317 static int
    318 processIHDR (struct EXTRACTOR_ExtractContext *ec,
    319              uint32_t length)
    320 {
    321   void *ptr;
    322   unsigned char *data;
    323   char tmp[128];
    324   int ret;
    325 
    326   if (length < 12)
    327     return 0;
    328   if (length != ec->read (ec->cls, &ptr, length))
    329     return 1;
    330   data = ptr;
    331   ret = 0;
    332   snprintf (tmp,
    333             sizeof (tmp),
    334             "%ux%u",
    335             get_int_at (data), get_int_at (&data[4]));
    336   ADD (EXTRACTOR_METATYPE_IMAGE_DIMENSIONS, tmp);
    337 FINISH:
    338   return ret;
    339 }
    340 
    341 
    342 /**
    343  * Process zTXt tag.
    344  *
    345  * @param ec extraction context
    346  * @param length length of the tag
    347  * @return 0 to continue extracting, 1 if we are done
    348  */
    349 static int
    350 processzTXt (struct EXTRACTOR_ExtractContext *ec,
    351              uint32_t length)
    352 {
    353   void *ptr;
    354   unsigned char *data;
    355   char *keyword;
    356   size_t off;
    357   unsigned int i;
    358   char *buf;
    359   uLongf bufLen;
    360   int zret;
    361   int ret;
    362 
    363   if (length != ec->read (ec->cls, &ptr, length))
    364     return 1;
    365   data = ptr;
    366   off = stnlen ((char *) data, length) + 1;
    367   if (off >= length)
    368     return 0;                /* failed to find '\0' */
    369   if (0 != data[off])
    370     return 0;                /* compression method must be 0 */
    371   off++;
    372   ret = 0;
    373   bufLen = 1024 + 2 * (length - off);
    374   while (1)
    375   {
    376     if (bufLen * 2 < bufLen)
    377       return 0;
    378     bufLen *= 2;
    379     if (bufLen > 50 * (length - off))
    380     {
    381       /* printf("zlib problem"); */
    382       return 0;
    383     }
    384     if (NULL == (buf = malloc (bufLen)))
    385     {
    386       /* printf("out of memory"); */
    387       return 0;              /* out of memory */
    388     }
    389     if (Z_OK ==
    390         (zret = uncompress ((Bytef *) buf,
    391                             &bufLen,
    392                             (const Bytef *) &data[off],
    393                             length - off)))
    394     {
    395       /* printf("zlib ok"); */
    396       break;
    397     }
    398     free (buf);
    399     if (Z_BUF_ERROR != zret)
    400       return 0;              /* unknown error, abort */
    401   }
    402   keyword = EXTRACTOR_common_convert_to_utf8 (buf,
    403                                               bufLen,
    404                                               "ISO-8859-1");
    405   free (buf);
    406   for (i = 0; NULL != tagmap[i].name; i++)
    407     if (0 == strcmp (tagmap[i].name, (char*)  data))
    408     {
    409       ADDF (tagmap[i].type, keyword);
    410       return 0;
    411     }
    412   ADDF (EXTRACTOR_METATYPE_COMMENT, keyword);
    413 FINISH:
    414   return ret;
    415 }
    416 
    417 
    418 /**
    419  * Process IME tag.
    420  *
    421  * @param ec extraction context
    422  * @param length length of the tag
    423  * @return 0 to continue extracting, 1 if we are done
    424  */
    425 static int
    426 processtIME (struct EXTRACTOR_ExtractContext *ec,
    427              uint32_t length)
    428 {
    429   void *ptr;
    430   unsigned char *data;
    431   unsigned short y;
    432   unsigned int year;
    433   unsigned int mo;
    434   unsigned int day;
    435   unsigned int h;
    436   unsigned int m;
    437   unsigned int s;
    438   char val[256];
    439   int ret;
    440 
    441   if (length != 7)
    442     return 0;
    443   if (length != ec->read (ec->cls, &ptr, length))
    444     return 1;
    445   data = ptr;
    446   ret = 0;
    447   memcpy (&y, data, sizeof (uint16_t));
    448   year = ntohs (y);
    449   mo = (unsigned char) data[2];
    450   day = (unsigned char) data[3];
    451   h = (unsigned char) data[4];
    452   m = (unsigned char) data[5];
    453   s = (unsigned char) data[6];
    454   snprintf (val,
    455             sizeof (val),
    456             "%04u-%02u-%02u %02d:%02d:%02d",
    457             year, mo, day, h, m, s);
    458   ADD (EXTRACTOR_METATYPE_MODIFICATION_DATE, val);
    459 FINISH:
    460   return ret;
    461 }
    462 
    463 
    464 /**
    465  * Main entry method for the 'image/png' extraction plugin.
    466  *
    467  * @param ec extraction context provided to the plugin
    468  */
    469 void
    470 EXTRACTOR_png_extract_method (struct EXTRACTOR_ExtractContext *ec)
    471 {
    472   void *data;
    473   uint32_t length;
    474   int64_t pos;
    475   int ret;
    476   ssize_t len;
    477 
    478   len = strlen (PNG_HEADER);
    479   if (len != ec->read (ec->cls,
    480                        &data,
    481                        len))
    482     return;
    483   if (0 != strncmp ((const char*) data,
    484                     PNG_HEADER,
    485                     len))
    486     return;
    487   ADD (EXTRACTOR_METATYPE_MIMETYPE,
    488        "image/png");
    489   ret = 0;
    490   while (0 == ret)
    491   {
    492     if (sizeof (uint32_t) + 4 !=
    493         ec->read (ec->cls,
    494                   &data,
    495                   sizeof (uint32_t) + 4))
    496       break;
    497     length = get_int_at (data);
    498     if (0 > (pos = ec->seek (ec->cls,
    499                              0,
    500                              SEEK_CUR)))
    501       break;
    502     pos += ((int64_t) length) + 4;   /* Chunk type, data, crc */
    503     if (0 == strncmp ((char*) data + sizeof (uint32_t),
    504                       "IHDR",
    505                       4))
    506       ret = processIHDR (ec,
    507                          length);
    508     if (0 == strncmp ((char*) data + sizeof (uint32_t),
    509                       "iTXt",
    510                       4))
    511       ret = processiTXt (ec,
    512                          length);
    513     if (0 == strncmp ((char*) data + sizeof (uint32_t),
    514                       "tEXt",
    515                       4))
    516       ret = processtEXt (ec,
    517                          length);
    518     if (0 == strncmp ((char*) data + sizeof (uint32_t),
    519                       "zTXt",
    520                       4))
    521       ret = processzTXt (ec,
    522                          length);
    523     if (0 == strncmp ((char*) data + sizeof (uint32_t),
    524                       "tIME",
    525                       4))
    526       ret = processtIME (ec,
    527                          length);
    528     if (ret != 0)
    529       break;
    530     if (pos != ec->seek (ec->cls,
    531                          pos,
    532                          SEEK_SET))
    533       break;
    534   }
    535 FINISH:
    536   return;
    537 }
    538 
    539 
    540 /* end of png_extractor.c */