ole2_extractor.c (28013B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2004, 2005, 2006, 2007, 2009, 2012, 2018 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 This code makes extensive use of libgsf 21 -- the Gnome Structured File Library 22 Copyright Copyright (C) 2002-2004 Jody Goldberg (jody@gnome.org) 23 24 Part of this code was adapted from wordleaker. 25 */ 26 /** 27 * @file plugins/ole2_extractor.c 28 * @brief plugin to support OLE2 (DOC, XLS, etc.) files 29 * @author Christian Grothoff 30 */ 31 #include "platform.h" 32 #include "extractor.h" 33 #include "convert.h" 34 #include <glib-object.h> 35 #include <string.h> 36 #include <stdio.h> 37 #include <ctype.h> 38 #include <gsf/gsf-utils.h> 39 #include <gsf/gsf-input-impl.h> 40 #include <gsf/gsf-input-memory.h> 41 #include <gsf/gsf-impl-utils.h> 42 #include <gsf/gsf-infile.h> 43 #include <gsf/gsf-infile-msole.h> 44 #include <gsf/gsf-msole-utils.h> 45 46 47 /** 48 * Set to 1 to use our own GsfInput subclass which supports seeking 49 * and thus can handle very large files. Set to 0 to use the simple 50 * gsf in-memory buffer (which can only access the first ~16k) for 51 * debugging. 52 */ 53 #define USE_LE_INPUT 1 54 55 56 /** 57 * Give the given UTF8 string to LE by calling 'proc'. 58 * 59 * @param proc callback to invoke 60 * @param proc_cls closure for proc 61 * @param phrase metadata string to pass; may include spaces 62 * just double-quotes or just a space in a double quote; 63 * in those cases, nothing should be done 64 * @param type meta data type to use 65 * @return if 'proc' returned 1, otherwise 0 66 */ 67 static int 68 add_metadata (EXTRACTOR_MetaDataProcessor proc, 69 void *proc_cls, 70 const char *phrase, 71 enum EXTRACTOR_MetaType type) 72 { 73 char *tmp; 74 int ret; 75 76 if (0 == strlen (phrase)) 77 return 0; 78 if (0 == strcmp (phrase, "\"\"")) 79 return 0; 80 if (0 == strcmp (phrase, "\" \"")) 81 return 0; 82 if (0 == strcmp (phrase, " ")) 83 return 0; 84 if (NULL == (tmp = strdup (phrase))) 85 return 0; 86 87 while ( (strlen (tmp) > 0) && 88 (isblank ((unsigned char) tmp [strlen (tmp) - 1])) ) 89 tmp [strlen (tmp) - 1] = '\0'; 90 ret = proc (proc_cls, 91 "ole2", 92 type, 93 EXTRACTOR_METAFORMAT_UTF8, 94 "text/plain", 95 tmp, 96 strlen (tmp) + 1); 97 free (tmp); 98 return ret; 99 } 100 101 102 /** 103 * Entry in the map from OLE meta type strings 104 * to LE types. 105 */ 106 struct Matches 107 { 108 /** 109 * OLE description. 110 */ 111 const char *text; 112 113 /** 114 * Corresponding LE type. 115 */ 116 enum EXTRACTOR_MetaType type; 117 }; 118 119 120 static struct Matches tmap[] = { 121 { "Title", EXTRACTOR_METATYPE_TITLE }, 122 { "PresentationFormat", EXTRACTOR_METATYPE_FORMAT }, 123 { "Category", EXTRACTOR_METATYPE_SECTION }, 124 { "Manager", EXTRACTOR_METATYPE_MANAGER }, 125 { "Company", EXTRACTOR_METATYPE_COMPANY }, 126 { "Subject", EXTRACTOR_METATYPE_SUBJECT }, 127 { "Author", EXTRACTOR_METATYPE_AUTHOR_NAME }, 128 { "Keywords", EXTRACTOR_METATYPE_KEYWORDS }, 129 { "Comments", EXTRACTOR_METATYPE_COMMENT }, 130 { "Template", EXTRACTOR_METATYPE_TEMPLATE }, 131 { "NumPages", EXTRACTOR_METATYPE_PAGE_COUNT }, 132 { "AppName", EXTRACTOR_METATYPE_PRODUCED_BY_SOFTWARE }, 133 { "RevisionNumber", EXTRACTOR_METATYPE_REVISION_NUMBER }, 134 { "NumBytes", EXTRACTOR_METATYPE_EMBEDDED_FILE_SIZE }, 135 { "CreatedTime", EXTRACTOR_METATYPE_CREATION_DATE }, 136 { "LastSavedTime", EXTRACTOR_METATYPE_MODIFICATION_DATE }, 137 { "gsf:company", EXTRACTOR_METATYPE_COMPANY }, 138 { "gsf:character-count", EXTRACTOR_METATYPE_CHARACTER_COUNT }, 139 { "gsf:page-count", EXTRACTOR_METATYPE_PAGE_COUNT }, 140 { "gsf:line-count", EXTRACTOR_METATYPE_LINE_COUNT }, 141 { "gsf:word-count", EXTRACTOR_METATYPE_WORD_COUNT }, 142 { "gsf:paragraph-count", EXTRACTOR_METATYPE_PARAGRAPH_COUNT }, 143 { "gsf:last-saved-by", EXTRACTOR_METATYPE_LAST_SAVED_BY }, 144 { "gsf:manager", EXTRACTOR_METATYPE_MANAGER }, 145 { "dc:title", EXTRACTOR_METATYPE_TITLE }, 146 { "dc:creator", EXTRACTOR_METATYPE_CREATOR }, 147 { "dc:date", EXTRACTOR_METATYPE_UNKNOWN_DATE }, 148 { "dc:subject", EXTRACTOR_METATYPE_SUBJECT }, 149 { "dc:keywords", EXTRACTOR_METATYPE_KEYWORDS }, 150 { "dc:last-printed", EXTRACTOR_METATYPE_LAST_PRINTED }, 151 { "dc:description", EXTRACTOR_METATYPE_DESCRIPTION }, 152 { "meta:creation-date", EXTRACTOR_METATYPE_CREATION_DATE }, 153 { "meta:generator", EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE }, 154 { "meta:template", EXTRACTOR_METATYPE_TEMPLATE }, 155 { "meta:editing-cycles", EXTRACTOR_METATYPE_EDITING_CYCLES }, 156 /* { "Dictionary", EXTRACTOR_METATYPE_LANGUAGE }, */ 157 /* { "gsf:security", EXTRACTOR_SECURITY }, */ 158 /* { "gsf:scale", EXTRACTOR_SCALE }, // always "false"? */ 159 /* { "meta:editing-duration", EXTRACTOR_METATYPE_TOTAL_EDITING_TIME }, // encoding? */ 160 /* { "msole:codepage", EXTRACTOR_CHARACTER_SET }, */ 161 { NULL, 0 } 162 }; 163 164 165 /** 166 * Closure for 'process_metadata'. 167 */ 168 struct ProcContext 169 { 170 /** 171 * Function to call for meta data that was found. 172 */ 173 EXTRACTOR_MetaDataProcessor proc; 174 175 /** 176 * Closure for @e proc. 177 */ 178 void *proc_cls; 179 180 /** 181 * Return value; 0 to continue to extract, 1 if we are done 182 */ 183 int ret; 184 }; 185 186 187 /** 188 * Function invoked by 'gst_msole_metadata_read' with 189 * metadata found in the document. 190 * 191 * @param key 'const char *' describing the meta data 192 * @param value the UTF8 representation of the meta data 193 * @param user_data our 'struct ProcContext' (closure) 194 */ 195 static void 196 process_metadata (gpointer key, 197 gpointer value, 198 gpointer user_data) 199 { 200 const char *type = key; 201 const GsfDocProp *prop = value; 202 struct ProcContext *pc = user_data; 203 const GValue *gval; 204 char *contents; 205 int pos; 206 207 if ( (NULL == key) || 208 (NULL == value) ) 209 return; 210 if (0 != pc->ret) 211 return; 212 gval = gsf_doc_prop_get_val (prop); 213 214 if (G_VALUE_TYPE (gval) == G_TYPE_STRING) 215 { 216 const char *gvals; 217 218 gvals = g_value_get_string (gval); 219 if (NULL == gvals) 220 return; 221 contents = strdup (gvals); 222 } 223 else 224 { 225 /* convert other formats? */ 226 contents = g_strdup_value_contents (gval); 227 } 228 if (NULL == contents) 229 return; 230 if (0 == strcmp (type, 231 "meta:generator")) 232 { 233 const char *mimetype = "application/vnd.ms-files"; 234 struct 235 { 236 const char *v; 237 const char *m; 238 } mm[] = { 239 { "Microsoft Word", "application/msword" }, 240 { "Microsoft Office Word", "application/msword" }, 241 { "Microsoft Excel", "application/vnd.ms-excel" }, 242 { "Microsoft Office Excel", "application/vnd.ms-excel" }, 243 { "Microsoft PowerPoint", "application/vnd.ms-powerpoint" }, 244 { "Microsoft Office PowerPoint", "application/vnd.ms-powerpoint"}, 245 { "Microsoft Project", "application/vnd.ms-project" }, 246 { "Microsoft Visio", "application/vnd.visio" }, 247 { "Microsoft Office", "application/vnd.ms-office" }, 248 { NULL, NULL } 249 }; 250 int i; 251 252 for (i = 0; NULL != mm[i].v; i++) 253 if (0 == strncmp (value, 254 mm[i].v, 255 strlen (mm[i].v) + 1)) 256 { 257 mimetype = mm[i].m; 258 break; 259 } 260 if (0 != add_metadata (pc->proc, 261 pc->proc_cls, 262 mimetype, 263 EXTRACTOR_METATYPE_MIMETYPE)) 264 { 265 free (contents); 266 pc->ret = 1; 267 return; 268 } 269 } 270 for (pos = 0; NULL != tmap[pos].text; pos++) 271 if (0 == strcmp (tmap[pos].text, 272 type)) 273 break; 274 if ( (NULL != tmap[pos].text) && 275 (0 != add_metadata (pc->proc, pc->proc_cls, 276 contents, 277 tmap[pos].type)) ) 278 { 279 free (contents); 280 pc->ret = 1; 281 return; 282 } 283 free (contents); 284 } 285 286 287 /** 288 * Function called on (Document)SummaryInformation OLE 289 * streams. 290 * 291 * @param in the input OLE stream 292 * @param proc function to call on meta data found 293 * @param proc_cls closure for proc 294 * @return 0 to continue to extract, 1 if we are done 295 */ 296 static int 297 process (GsfInput *in, 298 EXTRACTOR_MetaDataProcessor proc, 299 void *proc_cls) 300 { 301 struct ProcContext pc; 302 GsfDocMetaData *sections; 303 GError *error; 304 305 pc.proc = proc; 306 pc.proc_cls = proc_cls; 307 pc.ret = 0; 308 sections = gsf_doc_meta_data_new (); 309 #ifdef HAVE_GSF_DOC_META_DATA_READ_FROM_MSOLE 310 error = gsf_doc_meta_data_read_from_msole (sections, in); 311 #else 312 error = gsf_msole_metadata_read (in, sections); 313 #endif 314 if (NULL == error) 315 { 316 gsf_doc_meta_data_foreach (sections, 317 &process_metadata, 318 &pc); 319 } 320 else 321 { 322 g_error_free (error); 323 } 324 g_object_unref (G_OBJECT (sections)); 325 return pc.ret; 326 } 327 328 329 /** 330 * Function called on SfxDocumentInfo OLE 331 * streams. 332 * 333 * @param in the input OLE stream 334 * @param proc function to call on meta data found 335 * @param proc_cls closure for proc 336 * @return 0 to continue to extract, 1 if we are done 337 */ 338 static int 339 process_star_office (GsfInput *src, 340 EXTRACTOR_MetaDataProcessor proc, 341 void *proc_cls) 342 { 343 off_t size = gsf_input_size (src); 344 char buf[0x374]; 345 346 if (size < 0x374) 347 return 0; 348 gsf_input_read (src, 349 sizeof (buf), 350 (unsigned char*) buf); 351 if ( (buf[0] != 0x0F) || 352 (buf[1] != 0x0) || 353 (0 != strncmp (&buf[2], 354 "SfxDocumentInfo", 355 strlen ("SfxDocumentInfo"))) || 356 (buf[0x11] != 0x0B) || 357 (buf[0x13] != 0x00) || /* pw protected! */ 358 (buf[0x12] != 0x00) ) 359 return 0; 360 buf[0xd3] = '\0'; 361 if ( (buf[0x94] + buf[0x93] > 0) && 362 (0 != add_metadata (proc, proc_cls, 363 &buf[0x95], 364 EXTRACTOR_METATYPE_TITLE)) ) 365 return 1; 366 buf[0x114] = '\0'; 367 if ( (buf[0xd5] + buf[0xd4] > 0) && 368 (0 != add_metadata (proc, proc_cls, 369 &buf[0xd6], 370 EXTRACTOR_METATYPE_SUBJECT)) ) 371 return 1; 372 buf[0x215] = '\0'; 373 if ( (buf[0x115] + buf[0x116] > 0) && 374 (0 != add_metadata (proc, proc_cls, 375 &buf[0x117], 376 EXTRACTOR_METATYPE_COMMENT)) ) 377 return 1; 378 buf[0x296] = '\0'; 379 if ( (buf[0x216] + buf[0x217] > 0) && 380 (0 != add_metadata (proc, proc_cls, 381 &buf[0x218], 382 EXTRACTOR_METATYPE_KEYWORDS)) ) 383 return 1; 384 /* fixme: do timestamps, 385 mime-type, user-defined info's */ 386 return 0; 387 } 388 389 390 /** 391 * We use "__" to translate using iso-639. 392 * 393 * @param a string to translate 394 * @return translated string 395 */ 396 #define __(a) dgettext ("iso-639", a) 397 398 399 /** 400 * Get the language string for the given language ID (lid) 401 * value. 402 * 403 * @param lid language id value 404 * @return language string corresponding to the lid 405 */ 406 static const char * 407 lid_to_language (unsigned int lid) 408 { 409 switch (lid) 410 { 411 case 0x0400: 412 return _ ("No Proofing"); 413 case 0x0401: 414 return __ ("Arabic"); 415 case 0x0402: 416 return __ ("Bulgarian"); 417 case 0x0403: 418 return __ ("Catalan"); 419 case 0x0404: 420 return _ ("Traditional Chinese"); 421 case 0x0804: 422 return _ ("Simplified Chinese"); 423 case 0x0405: 424 return __ ("Chechen"); 425 case 0x0406: 426 return __ ("Danish"); 427 case 0x0407: 428 return __ ("German"); 429 case 0x0807: 430 return _ ("Swiss German"); 431 case 0x0408: 432 return __ ("Greek"); 433 case 0x0409: 434 return _ ("U.S. English"); 435 case 0x0809: 436 return _ ("U.K. English"); 437 case 0x0c09: 438 return _ ("Australian English"); 439 case 0x040a: 440 return _ ("Castilian Spanish"); 441 case 0x080a: 442 return _ ("Mexican Spanish"); 443 case 0x040b: 444 return __ ("Finnish"); 445 case 0x040c: 446 return __ ("French"); 447 case 0x080c: 448 return _ ("Belgian French"); 449 case 0x0c0c: 450 return _ ("Canadian French"); 451 case 0x100c: 452 return _ ("Swiss French"); 453 case 0x040d: 454 return __ ("Hebrew"); 455 case 0x040e: 456 return __ ("Hungarian"); 457 case 0x040f: 458 return __ ("Icelandic"); 459 case 0x0410: 460 return __ ("Italian"); 461 case 0x0810: 462 return _ ("Swiss Italian"); 463 case 0x0411: 464 return __ ("Japanese"); 465 case 0x0412: 466 return __ ("Korean"); 467 case 0x0413: 468 return __ ("Dutch"); 469 case 0x0813: 470 return _ ("Belgian Dutch"); 471 case 0x0414: 472 return _ ("Norwegian Bokmal"); 473 case 0x0814: 474 return __ ("Norwegian Nynorsk"); 475 case 0x0415: 476 return __ ("Polish"); 477 case 0x0416: 478 return __ ("Brazilian Portuguese"); 479 case 0x0816: 480 return __ ("Portuguese"); 481 case 0x0417: 482 return _ ("Rhaeto-Romanic"); 483 case 0x0418: 484 return __ ("Romanian"); 485 case 0x0419: 486 return __ ("Russian"); 487 case 0x041a: 488 return _ ("Croato-Serbian (Latin)"); 489 case 0x081a: 490 return _ ("Serbo-Croatian (Cyrillic)"); 491 case 0x041b: 492 return __ ("Slovak"); 493 case 0x041c: 494 return __ ("Albanian"); 495 case 0x041d: 496 return __ ("Swedish"); 497 case 0x041e: 498 return __ ("Thai"); 499 case 0x041f: 500 return __ ("Turkish"); 501 case 0x0420: 502 return __ ("Urdu"); 503 case 0x0421: 504 return __ ("Bahasa"); 505 case 0x0422: 506 return __ ("Ukrainian"); 507 case 0x0423: 508 return __ ("Byelorussian"); 509 case 0x0424: 510 return __ ("Slovenian"); 511 case 0x0425: 512 return __ ("Estonian"); 513 case 0x0426: 514 return __ ("Latvian"); 515 case 0x0427: 516 return __ ("Lithuanian"); 517 case 0x0429: 518 return _ ("Farsi"); 519 case 0x042D: 520 return __ ("Basque"); 521 case 0x042F: 522 return __ ("Macedonian"); 523 case 0x0436: 524 return __ ("Afrikaans"); 525 case 0x043E: 526 return __ ("Malayalam"); 527 default: 528 return NULL; 529 } 530 } 531 532 533 /** 534 * Extract editing history from XTable stream. 535 * 536 * @param stream OLE stream to process 537 * @param lcSttbSavedBy length of the revision history in bytes 538 * @param fcSttbSavedBy offset of the revision history in the stream 539 * @param proc function to call on meta data found 540 * @param proc_cls closure for proc 541 * @return 0 to continue to extract, 1 if we are done 542 */ 543 static int 544 history_extract (GsfInput *stream, 545 unsigned int lcbSttbSavedBy, 546 unsigned int fcSttbSavedBy, 547 EXTRACTOR_MetaDataProcessor proc, 548 void *proc_cls) 549 { 550 unsigned int where; 551 unsigned char *lbuffer; 552 unsigned int i; 553 unsigned int length; 554 char *author; 555 char *filename; 556 char *rbuf; 557 unsigned int nRev; 558 int ret; 559 560 /* goto offset of revision information */ 561 gsf_input_seek (stream, fcSttbSavedBy, G_SEEK_SET); 562 if (gsf_input_remaining (stream) < lcbSttbSavedBy) 563 return 0; 564 if (NULL == (lbuffer = malloc (lcbSttbSavedBy))) 565 return 0; 566 /* read all the revision history */ 567 gsf_input_read (stream, lcbSttbSavedBy, lbuffer); 568 /* there are n strings, so n/2 revisions (author & file) */ 569 nRev = (lbuffer[2] + (lbuffer[3] << 8)) / 2; 570 where = 6; 571 ret = 0; 572 for (i = 0; i < nRev; i++) 573 { 574 if (where >= lcbSttbSavedBy) 575 break; 576 length = lbuffer[where++]; 577 if ( (where + 2 * length + 2 >= lcbSttbSavedBy) || 578 (where + 2 * length + 2 <= where) ) 579 break; 580 author = EXTRACTOR_common_convert_to_utf8 ((const char*) &lbuffer[where], 581 length * 2, 582 "UTF-16BE"); 583 where += length * 2 + 1; 584 length = lbuffer[where++]; 585 if ( (where + 2 * length >= lcbSttbSavedBy) || 586 (where + 2 * length + 1 <= where) ) 587 { 588 if (NULL != author) 589 free (author); 590 break; 591 } 592 filename = EXTRACTOR_common_convert_to_utf8 ((const char*) &lbuffer[where], 593 length * 2, 594 "UTF-16BE"); 595 where += length * 2 + 1; 596 if ( (NULL != author) && 597 (NULL != filename) ) 598 { 599 size_t bsize; 600 601 bsize = strlen (author) + strlen (filename) + 512; 602 if (NULL != (rbuf = malloc (bsize))) 603 { 604 int snret; 605 606 snret = snprintf (rbuf, 607 bsize, 608 _ ("Revision #%u: Author `%s' worked on `%s'"), 609 i, 610 author, 611 filename); 612 if ( (-1 != snret) && 613 (bsize > (size_t) snret) ) 614 { 615 ret = add_metadata (proc, 616 proc_cls, 617 rbuf, 618 EXTRACTOR_METATYPE_REVISION_HISTORY); 619 } 620 free (rbuf); 621 } 622 } 623 if (NULL != author) 624 free (author); 625 if (NULL != filename) 626 free (filename); 627 if (0 != ret) 628 break; 629 } 630 free (lbuffer); 631 return ret; 632 } 633 634 635 /* *************************** custom GSF input method ***************** */ 636 637 #define LE_TYPE_INPUT (le_input_get_type ()) 638 #define LE_INPUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ 639 LE_TYPE_INPUT, \ 640 LeInput)) 641 #define LE_INPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ 642 LE_TYPE_INPUT, \ 643 LeInputClass)) 644 #define IS_LE_INPUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ 645 LE_TYPE_INPUT)) 646 #define IS_LE_INPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ 647 LE_TYPE_INPUT)) 648 #define LE_INPUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ 649 LE_TYPE_INPUT, \ 650 LeInputClass) \ 651 ) 652 653 /** 654 * Internal state of an "LeInput" object. 655 */ 656 typedef struct _LeInputPrivate 657 { 658 /** 659 * Our extraction context. 660 */ 661 struct EXTRACTOR_ExtractContext *ec; 662 } LeInputPrivate; 663 664 665 /** 666 * Overall state of an "LeInput" object. 667 */ 668 typedef struct _LeInput 669 { 670 /** 671 * Inherited state from parent (GsfInput). 672 */ 673 GsfInput input; 674 675 /*< private > */ 676 /** 677 * Private state of the LeInput. 678 */ 679 LeInputPrivate *priv; 680 } LeInput; 681 682 683 /** 684 * LeInput's class state. 685 */ 686 typedef struct _LeInputClass 687 { 688 /** 689 * GsfInput is our parent class. 690 */ 691 GsfInputClass parent_class; 692 693 /* Padding for future expansion */ 694 void (*_gtk_reserved1)(void); 695 void (*_gtk_reserved2)(void); 696 void (*_gtk_reserved3)(void); 697 void (*_gtk_reserved4)(void); 698 } LeInputClass; 699 700 701 /** 702 * Constructor for LeInput objects. 703 * 704 * @param ec extraction context to use 705 * @return the LeInput, NULL on error 706 */ 707 GsfInput * 708 le_input_new (struct EXTRACTOR_ExtractContext *ec); 709 710 711 /** 712 * Class initializer for the "LeInput" class. 713 * 714 * @param class class object to initialize 715 */ 716 static void 717 le_input_class_init (LeInputClass *class); 718 719 720 /** 721 * Initialize internal state of fresh input object. 722 * 723 * @param input object to initialize 724 */ 725 static void 726 le_input_init (LeInput *input); 727 728 729 /** 730 * Macro to create LeInput type definition and register the class. 731 */ 732 GSF_CLASS (LeInput, le_input, le_input_class_init, le_input_init, 733 GSF_INPUT_TYPE) 734 735 736 /** 737 * Duplicate input, leaving the new one at the same offset. 738 * 739 * @param input the input to duplicate 740 * @param err location for error reporting, can be NULL 741 * @return NULL on error (always) 742 */ 743 static GsfInput * 744 le_input_dup (GsfInput * input, 745 GError * *err) 746 { 747 if (NULL != err) 748 *err = g_error_new (gsf_input_error_id (), 0, 749 "dup not supported on LeInput"); 750 return NULL; 751 } 752 753 754 /** 755 * Read at least num_bytes. Does not change the current position if 756 * there is an error. Will only read if the entire amount can be 757 * read. Invalidates the buffer associated with previous calls to 758 * gsf_input_read. 759 * 760 * @param input 761 * @param num_bytes 762 * @param optional_buffer 763 * @return buffer where num_bytes data are available, or NULL on error 764 */ 765 static const guint8 * 766 le_input_read (GsfInput *input, 767 size_t num_bytes, 768 guint8 *optional_buffer) 769 { 770 LeInput *li = LE_INPUT (input); 771 struct EXTRACTOR_ExtractContext *ec; 772 void *buf; 773 uint64_t old_off; 774 ssize_t ret; 775 776 ec = li->priv->ec; 777 old_off = ec->seek (ec->cls, 778 0, 779 SEEK_CUR); 780 if (num_bytes 781 != (ret = ec->read (ec->cls, 782 &buf, 783 num_bytes))) 784 { 785 /* we don't support partial reads; 786 most other GsfInput implementations in this case 787 allocate some huge temporary buffer just to avoid 788 the partial read; we might need to do that as well!? */ 789 ec->seek (ec->cls, 790 old_off, 791 SEEK_SET); 792 return NULL; 793 } 794 if (NULL != optional_buffer) 795 { 796 memcpy (optional_buffer, 797 buf, 798 num_bytes); 799 return optional_buffer; 800 } 801 return buf; 802 } 803 804 805 /** 806 * Move the current location in an input stream 807 * 808 * @param input stream to seek 809 * @param offset target offset 810 * @param whence determines to what the offset is relative to 811 * @return TRUE on error 812 */ 813 static gboolean 814 le_input_seek (GsfInput *input, 815 gsf_off_t offset, 816 GSeekType whence) 817 { 818 LeInput *li = LE_INPUT (input); 819 struct EXTRACTOR_ExtractContext *ec; 820 int w; 821 int64_t ret; 822 823 ec = li->priv->ec; 824 switch (whence) 825 { 826 case G_SEEK_SET: 827 w = SEEK_SET; 828 break; 829 case G_SEEK_CUR: 830 w = SEEK_CUR; 831 break; 832 case G_SEEK_END: 833 w = SEEK_END; 834 break; 835 default: 836 return TRUE; 837 } 838 if (-1 == 839 (ret = ec->seek (ec->cls, 840 offset, 841 w))) 842 return TRUE; 843 return FALSE; 844 } 845 846 847 /** 848 * Class initializer for the "LeInput" class. 849 * 850 * @param class class object to initialize 851 */ 852 static void 853 le_input_class_init (LeInputClass *class) 854 { 855 GsfInputClass *input_class; 856 857 input_class = (GsfInputClass *) class; 858 input_class->Dup = le_input_dup; 859 input_class->Read = le_input_read; 860 input_class->Seek = le_input_seek; 861 g_type_class_add_private (class, 862 sizeof (LeInputPrivate)); 863 } 864 865 866 /** 867 * Initialize internal state of fresh input object. 868 * 869 * @param input object to initialize 870 */ 871 static void 872 le_input_init (LeInput *input) 873 { 874 LeInputPrivate *priv; 875 876 input->priv = 877 G_TYPE_INSTANCE_GET_PRIVATE (input, LE_TYPE_INPUT, 878 LeInputPrivate); 879 priv = input->priv; 880 priv->ec = NULL; 881 } 882 883 884 /** 885 * Creates a new LeInput object. 886 * 887 * @param ec extractor context to wrap 888 * @return NULL on error 889 */ 890 GsfInput * 891 le_input_new (struct EXTRACTOR_ExtractContext *ec) 892 { 893 LeInput *input; 894 895 input = g_object_new (LE_TYPE_INPUT, NULL); 896 gsf_input_set_size (GSF_INPUT (input), 897 ec->get_size (ec->cls)); 898 gsf_input_seek_emulate (GSF_INPUT (input), 899 0); 900 input->input.name = NULL; 901 input->input.container = NULL; 902 input->priv->ec = ec; 903 904 return GSF_INPUT (input); 905 } 906 907 908 /* *********************** end of custom GSF input method ************* */ 909 910 911 /** 912 * Main entry method for the OLE2 extraction plugin. 913 * 914 * @param ec extraction context provided to the plugin 915 */ 916 void 917 EXTRACTOR_ole2_extract_method (struct EXTRACTOR_ExtractContext *ec); 918 919 void 920 EXTRACTOR_ole2_extract_method (struct EXTRACTOR_ExtractContext *ec) 921 { 922 GsfInput *input; 923 GsfInfile *infile; 924 GsfInput *src; 925 const char *name; 926 unsigned int i; 927 unsigned int lcb; 928 unsigned int fcb; 929 const unsigned char *data512; 930 unsigned int lid; 931 const char *lang; 932 int ret; 933 void *data; 934 uint64_t fsize; 935 ssize_t data_size; 936 937 fsize = ec->get_size (ec->cls); 938 if (fsize < 512 + 898) 939 { 940 /* File too small for OLE2 */ 941 return; /* can hardly be OLE2 */ 942 } 943 if (512 + 898 > (data_size = ec->read (ec->cls, &data, fsize))) 944 { 945 /* Failed to read minimum file size to buffer */ 946 return; 947 } 948 data512 = (const unsigned char*) data + 512; 949 lid = data512[6] + (data512[7] << 8); 950 if ( (NULL != (lang = lid_to_language (lid))) && 951 (0 != (ret = add_metadata (ec->proc, ec->cls, 952 lang, 953 EXTRACTOR_METATYPE_LANGUAGE))) ) 954 return; 955 lcb = data512[726] + (data512[727] << 8) + (data512[728] << 16) 956 + (data512[729] << 24); 957 fcb = data512[722] + (data512[723] << 8) + (data512[724] << 16) 958 + (data512[725] << 24); 959 if (0 != ec->seek (ec->cls, 960 0, 961 SEEK_SET)) 962 { 963 /* seek failed!? */ 964 return; 965 } 966 #if USE_LE_INPUT 967 if (NULL == (input = le_input_new (ec))) 968 { 969 fprintf (stderr, "le_input_new failed\n"); 970 return; 971 } 972 #else 973 input = gsf_input_memory_new ((const guint8 *) data, 974 data_size, 975 FALSE); 976 #endif 977 if (NULL == (infile = gsf_infile_msole_new (input, NULL))) 978 { 979 g_object_unref (G_OBJECT (input)); 980 return; 981 } 982 ret = 0; 983 for (i = 0; i<gsf_infile_num_children (infile); i++) 984 { 985 if (0 != ret) 986 break; 987 if (NULL == (name = gsf_infile_name_by_index (infile, i))) 988 continue; 989 src = NULL; 990 if ( ( (0 == strcmp (name, "\005SummaryInformation")) || 991 (0 == strcmp (name, "\005DocumentSummaryInformation")) ) && 992 (NULL != (src = gsf_infile_child_by_index (infile, i))) ) 993 ret = process (src, 994 ec->proc, 995 ec->cls); 996 if ( (0 == strcmp (name, "SfxDocumentInfo")) && 997 (NULL != (src = gsf_infile_child_by_index (infile, i))) ) 998 ret = process_star_office (src, 999 ec->proc, 1000 ec->cls); 1001 if (NULL != src) 1002 g_object_unref (G_OBJECT (src)); 1003 } 1004 if (0 != ret) 1005 goto CLEANUP; 1006 1007 if (lcb < 6) 1008 goto CLEANUP; 1009 for (i = 0; i<gsf_infile_num_children (infile); i++) 1010 { 1011 if (ret != 0) 1012 break; 1013 if (NULL == (name = gsf_infile_name_by_index (infile, i))) 1014 continue; 1015 if ( ( (0 == strcmp (name, "1Table")) || 1016 (0 == strcmp (name, "0Table")) ) && 1017 (NULL != (src = gsf_infile_child_by_index (infile, i))) ) 1018 { 1019 ret = history_extract (src, 1020 lcb, 1021 fcb, 1022 ec->proc, ec->cls); 1023 g_object_unref (G_OBJECT (src)); 1024 } 1025 } 1026 CLEANUP: 1027 g_object_unref (G_OBJECT (infile)); 1028 g_object_unref (G_OBJECT (input)); 1029 } 1030 1031 1032 /** 1033 * Custom log function we give to GSF to disable logging. 1034 * 1035 * @param log_domain unused 1036 * @param log_level unused 1037 * @param message unused 1038 * @param user_data unused 1039 */ 1040 static void 1041 nolog (const gchar *log_domain, 1042 GLogLevelFlags log_level, 1043 const gchar *message, 1044 gpointer user_data) 1045 { 1046 /* do nothing */ 1047 } 1048 1049 1050 /** 1051 * OLE2 plugin constructor. Initializes glib and gsf, in particular 1052 * gsf logging is disabled. 1053 */ 1054 void __attribute__ ((constructor)) 1055 ole2_ltdl_init () 1056 { 1057 #if ! GLIB_CHECK_VERSION (2, 35, 0) 1058 g_type_init (); 1059 #endif 1060 #ifdef HAVE_GSF_INIT 1061 gsf_init (); 1062 #endif 1063 /* disable logging -- thanks, Jody! */ 1064 g_log_set_handler ("libgsf:msole", 1065 G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, 1066 &nolog, NULL); 1067 } 1068 1069 1070 /** 1071 * OLE2 plugin destructor. Shutdown of gsf. 1072 */ 1073 void __attribute__ ((destructor)) 1074 ole2_ltdl_fini () 1075 { 1076 #ifdef HAVE_GSF_INIT 1077 gsf_shutdown (); 1078 #endif 1079 } 1080 1081 1082 /* end of ole2_extractor.c */