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