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