ole2_extractor.c (49964B)
1 /* 2 This file is part of libextractor. 3 Copyright (C) 2004--2026 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 "msoffice_biff.h" 35 #include "fulltext.h" 36 #include <glib-object.h> 37 #include <string.h> 38 #include <stdio.h> 39 #include <ctype.h> 40 #include <gsf/gsf-utils.h> 41 #include <gsf/gsf-input-impl.h> 42 #include <gsf/gsf-input-memory.h> 43 #include <gsf/gsf-impl-utils.h> 44 #include <gsf/gsf-infile.h> 45 #include <gsf/gsf-infile-msole.h> 46 #include <gsf/gsf-msole-utils.h> 47 48 49 /** 50 * Set to 1 to use our own GsfInput subclass which supports seeking 51 * and thus can handle very large files. Set to 0 to use the simple 52 * gsf in-memory buffer (which can only access the first ~16k) for 53 * debugging. 54 */ 55 #define USE_LE_INPUT 1 56 57 58 /** 59 * Give the given UTF8 string to LE by calling 'proc'. 60 * 61 * @param proc callback to invoke 62 * @param proc_cls closure for proc 63 * @param phrase metadata string to pass; may include spaces 64 * just double-quotes or just a space in a double quote; 65 * in those cases, nothing should be done 66 * @param type meta data type to use 67 * @return if 'proc' returned 1, otherwise 0 68 */ 69 static int 70 add_metadata (EXTRACTOR_MetaDataProcessor proc, 71 void *proc_cls, 72 const char *phrase, 73 enum EXTRACTOR_MetaType type) 74 { 75 char *tmp; 76 int ret; 77 78 if (0 == strlen (phrase)) 79 return 0; 80 if (0 == strcmp (phrase, "\"\"")) 81 return 0; 82 if (0 == strcmp (phrase, "\" \"")) 83 return 0; 84 if (0 == strcmp (phrase, " ")) 85 return 0; 86 if (NULL == (tmp = strdup (phrase))) 87 return 0; 88 89 while ( (strlen (tmp) > 0) && 90 (isblank ((unsigned char) tmp [strlen (tmp) - 1])) ) 91 tmp [strlen (tmp) - 1] = '\0'; 92 ret = proc (proc_cls, 93 "ole2", 94 type, 95 EXTRACTOR_METAFORMAT_UTF8, 96 "text/plain", 97 tmp, 98 strlen (tmp) + 1); 99 free (tmp); 100 return ret; 101 } 102 103 104 /** 105 * Entry in the map from OLE meta type strings 106 * to LE types. 107 */ 108 struct Matches 109 { 110 /** 111 * OLE description. 112 */ 113 const char *text; 114 115 /** 116 * Corresponding LE type. 117 */ 118 enum EXTRACTOR_MetaType type; 119 }; 120 121 122 static struct Matches tmap[] = { 123 { "Title", EXTRACTOR_METATYPE_TITLE }, 124 { "PresentationFormat", EXTRACTOR_METATYPE_FORMAT }, 125 { "Category", EXTRACTOR_METATYPE_SECTION }, 126 { "Manager", EXTRACTOR_METATYPE_MANAGER }, 127 { "Company", EXTRACTOR_METATYPE_COMPANY }, 128 { "Subject", EXTRACTOR_METATYPE_SUBJECT }, 129 { "Author", EXTRACTOR_METATYPE_AUTHOR_NAME }, 130 { "Keywords", EXTRACTOR_METATYPE_KEYWORDS }, 131 { "Comments", EXTRACTOR_METATYPE_COMMENT }, 132 { "Template", EXTRACTOR_METATYPE_TEMPLATE }, 133 { "NumPages", EXTRACTOR_METATYPE_PAGE_COUNT }, 134 { "AppName", EXTRACTOR_METATYPE_PRODUCED_BY_SOFTWARE }, 135 { "RevisionNumber", EXTRACTOR_METATYPE_REVISION_NUMBER }, 136 { "NumBytes", EXTRACTOR_METATYPE_EMBEDDED_FILE_SIZE }, 137 { "CreatedTime", EXTRACTOR_METATYPE_CREATION_DATE }, 138 { "LastSavedTime", EXTRACTOR_METATYPE_MODIFICATION_DATE }, 139 { "gsf:company", EXTRACTOR_METATYPE_COMPANY }, 140 { "gsf:character-count", EXTRACTOR_METATYPE_CHARACTER_COUNT }, 141 { "gsf:page-count", EXTRACTOR_METATYPE_PAGE_COUNT }, 142 { "gsf:line-count", EXTRACTOR_METATYPE_LINE_COUNT }, 143 { "gsf:word-count", EXTRACTOR_METATYPE_WORD_COUNT }, 144 { "gsf:paragraph-count", EXTRACTOR_METATYPE_PARAGRAPH_COUNT }, 145 { "gsf:last-saved-by", EXTRACTOR_METATYPE_LAST_SAVED_BY }, 146 { "gsf:manager", EXTRACTOR_METATYPE_MANAGER }, 147 { "dc:title", EXTRACTOR_METATYPE_TITLE }, 148 { "dc:creator", EXTRACTOR_METATYPE_CREATOR }, 149 { "dc:date", EXTRACTOR_METATYPE_UNKNOWN_DATE }, 150 { "dc:subject", EXTRACTOR_METATYPE_SUBJECT }, 151 { "dc:keywords", EXTRACTOR_METATYPE_KEYWORDS }, 152 { "dc:last-printed", EXTRACTOR_METATYPE_LAST_PRINTED }, 153 { "dc:description", EXTRACTOR_METATYPE_DESCRIPTION }, 154 { "meta:creation-date", EXTRACTOR_METATYPE_CREATION_DATE }, 155 { "meta:generator", EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE }, 156 { "meta:template", EXTRACTOR_METATYPE_TEMPLATE }, 157 { "meta:editing-cycles", EXTRACTOR_METATYPE_EDITING_CYCLES }, 158 /* { "Dictionary", EXTRACTOR_METATYPE_LANGUAGE }, */ 159 /* { "gsf:security", EXTRACTOR_SECURITY }, */ 160 /* { "gsf:scale", EXTRACTOR_SCALE }, // always "false"? */ 161 /* { "meta:editing-duration", EXTRACTOR_METATYPE_TOTAL_EDITING_TIME }, // encoding? */ 162 /* { "msole:codepage", EXTRACTOR_CHARACTER_SET }, */ 163 { NULL, 0 } 164 }; 165 166 167 /** 168 * Closure for 'process_metadata'. 169 */ 170 struct ProcContext 171 { 172 /** 173 * Function to call for meta data that was found. 174 */ 175 EXTRACTOR_MetaDataProcessor proc; 176 177 /** 178 * Closure for @e proc. 179 */ 180 void *proc_cls; 181 182 /** 183 * Return value; 0 to continue to extract, 1 if we are done 184 */ 185 int ret; 186 }; 187 188 189 /** 190 * Function invoked by 'gst_msole_metadata_read' with 191 * metadata found in the document. 192 * 193 * @param key 'const char *' describing the meta data 194 * @param value the UTF8 representation of the meta data 195 * @param user_data our 'struct ProcContext' (closure) 196 */ 197 static void 198 process_metadata (gpointer key, 199 gpointer value, 200 gpointer user_data) 201 { 202 const char *type = key; 203 const GsfDocProp *prop = value; 204 struct ProcContext *pc = user_data; 205 const GValue *gval; 206 char *contents; 207 int pos; 208 209 if ( (NULL == key) || 210 (NULL == value) ) 211 return; 212 if (0 != pc->ret) 213 return; 214 gval = gsf_doc_prop_get_val (prop); 215 216 if (G_VALUE_TYPE (gval) == G_TYPE_STRING) 217 { 218 const char *gvals; 219 220 gvals = g_value_get_string (gval); 221 if (NULL == gvals) 222 return; 223 contents = strdup (gvals); 224 } 225 else 226 { 227 /* convert other formats? */ 228 contents = g_strdup_value_contents (gval); 229 } 230 if (NULL == contents) 231 return; 232 if (0 == strcmp (type, 233 "meta:generator")) 234 { 235 const char *mimetype = "application/vnd.ms-files"; 236 struct 237 { 238 const char *v; 239 const char *m; 240 } mm[] = { 241 { "Microsoft Word", "application/msword" }, 242 { "Microsoft Office Word", "application/msword" }, 243 { "Microsoft Excel", "application/vnd.ms-excel" }, 244 { "Microsoft Office Excel", "application/vnd.ms-excel" }, 245 { "Microsoft PowerPoint", "application/vnd.ms-powerpoint" }, 246 { "Microsoft Office PowerPoint", "application/vnd.ms-powerpoint"}, 247 { "Microsoft Project", "application/vnd.ms-project" }, 248 { "Microsoft Visio", "application/vnd.visio" }, 249 { "Microsoft Office", "application/vnd.ms-office" }, 250 { NULL, NULL } 251 }; 252 int i; 253 254 for (i = 0; NULL != mm[i].v; i++) 255 if (0 == strncmp (value, 256 mm[i].v, 257 strlen (mm[i].v) + 1)) 258 { 259 mimetype = mm[i].m; 260 break; 261 } 262 if (0 != add_metadata (pc->proc, 263 pc->proc_cls, 264 mimetype, 265 EXTRACTOR_METATYPE_MIMETYPE)) 266 { 267 free (contents); 268 pc->ret = 1; 269 return; 270 } 271 } 272 for (pos = 0; NULL != tmap[pos].text; pos++) 273 if (0 == strcmp (tmap[pos].text, 274 type)) 275 break; 276 if ( (NULL != tmap[pos].text) && 277 (0 != add_metadata (pc->proc, pc->proc_cls, 278 contents, 279 tmap[pos].type)) ) 280 { 281 free (contents); 282 pc->ret = 1; 283 return; 284 } 285 free (contents); 286 } 287 288 289 /** 290 * Function called on (Document)SummaryInformation OLE 291 * streams. 292 * 293 * @param in the input OLE stream 294 * @param proc function to call on meta data found 295 * @param proc_cls closure for proc 296 * @return 0 to continue to extract, 1 if we are done 297 */ 298 static int 299 process (GsfInput *in, 300 EXTRACTOR_MetaDataProcessor proc, 301 void *proc_cls) 302 { 303 struct ProcContext pc; 304 GsfDocMetaData *sections; 305 GError *error; 306 307 pc.proc = proc; 308 pc.proc_cls = proc_cls; 309 pc.ret = 0; 310 sections = gsf_doc_meta_data_new (); 311 #ifdef HAVE_GSF_DOC_META_DATA_READ_FROM_MSOLE 312 error = gsf_doc_meta_data_read_from_msole (sections, in); 313 #else 314 error = gsf_msole_metadata_read (in, sections); 315 #endif 316 if (NULL == error) 317 { 318 gsf_doc_meta_data_foreach (sections, 319 &process_metadata, 320 &pc); 321 } 322 else 323 { 324 g_error_free (error); 325 } 326 g_object_unref (G_OBJECT (sections)); 327 return pc.ret; 328 } 329 330 331 /** 332 * Function called on SfxDocumentInfo OLE 333 * streams. 334 * 335 * @param in the input OLE stream 336 * @param proc function to call on meta data found 337 * @param proc_cls closure for proc 338 * @return 0 to continue to extract, 1 if we are done 339 */ 340 static int 341 process_star_office (GsfInput *src, 342 EXTRACTOR_MetaDataProcessor proc, 343 void *proc_cls) 344 { 345 off_t size = gsf_input_size (src); 346 char buf[0x374]; 347 348 if (size < 0x374) 349 return 0; 350 gsf_input_read (src, 351 sizeof (buf), 352 (unsigned char*) buf); 353 if ( (buf[0] != 0x0F) || 354 (buf[1] != 0x0) || 355 (0 != strncmp (&buf[2], 356 "SfxDocumentInfo", 357 strlen ("SfxDocumentInfo"))) || 358 (buf[0x11] != 0x0B) || 359 (buf[0x13] != 0x00) || /* pw protected! */ 360 (buf[0x12] != 0x00) ) 361 return 0; 362 buf[0xd3] = '\0'; 363 if ( (buf[0x94] + buf[0x93] > 0) && 364 (0 != add_metadata (proc, proc_cls, 365 &buf[0x95], 366 EXTRACTOR_METATYPE_TITLE)) ) 367 return 1; 368 buf[0x114] = '\0'; 369 if ( (buf[0xd5] + buf[0xd4] > 0) && 370 (0 != add_metadata (proc, proc_cls, 371 &buf[0xd6], 372 EXTRACTOR_METATYPE_SUBJECT)) ) 373 return 1; 374 buf[0x215] = '\0'; 375 if ( (buf[0x115] + buf[0x116] > 0) && 376 (0 != add_metadata (proc, proc_cls, 377 &buf[0x117], 378 EXTRACTOR_METATYPE_COMMENT)) ) 379 return 1; 380 buf[0x296] = '\0'; 381 if ( (buf[0x216] + buf[0x217] > 0) && 382 (0 != add_metadata (proc, proc_cls, 383 &buf[0x218], 384 EXTRACTOR_METATYPE_KEYWORDS)) ) 385 return 1; 386 /* fixme: do timestamps, 387 mime-type, user-defined info's */ 388 return 0; 389 } 390 391 392 /** 393 * Report the operating system recorded in the header of an OLE2 394 * property set stream. 395 * 396 * Every property set stream starts with a PropertySetStream header 397 * whose PropertySetSystemIdentifier field ([MS-OLEPS] section 2.21) 398 * names the operating system that wrote it. This is not exposed as a 399 * property and is therefore invisible to libgsf, but it is a reliable 400 * indicator of the system a document was authored on. 401 * 402 * @param src the property set stream, repositioned to the start on return 403 * @param reported set to 1 once we reported an operating system; a 404 * document usually has two property set streams that both 405 * carry the very same identifier 406 * @param proc function to call on meta data found 407 * @param proc_cls closure for @a proc 408 * @return 0 to continue to extract, 1 if we are done 409 */ 410 static int 411 process_system_identifier (GsfInput *src, 412 int *reported, 413 EXTRACTOR_MetaDataProcessor proc, 414 void *proc_cls) 415 { 416 guint8 hdr[8]; 417 uint32_t osver; 418 unsigned int os_type; 419 unsigned int major; 420 unsigned int minor; 421 char buf[128]; 422 int sret; 423 424 if ((gsf_off_t) sizeof (hdr) > gsf_input_size (src)) 425 return 0; 426 if (gsf_input_seek (src, 0, G_SEEK_SET)) 427 return 0; 428 /* Copy the header out before seeking back: the buffer handed to us 429 by gsf_input_read() only stays valid until the input is touched 430 again. */ 431 if (NULL == gsf_input_read (src, sizeof (hdr), hdr)) 432 return 0; 433 if (gsf_input_seek (src, 0, G_SEEK_SET)) 434 return 0; 435 if ( (0xFE != hdr[0]) || 436 (0xFF != hdr[1]) ) 437 return 0; /* not a property set stream after all */ 438 osver = ((uint32_t) hdr[4]) 439 | (((uint32_t) hdr[5]) << 8) 440 | (((uint32_t) hdr[6]) << 16) 441 | (((uint32_t) hdr[7]) << 24); 442 os_type = (unsigned int) (osver >> 16); 443 major = (unsigned int) (osver & 0xFF); 444 minor = (unsigned int) ((osver >> 8) & 0xFF); 445 switch (os_type) 446 { 447 case 0: 448 sret = snprintf (buf, sizeof (buf), 449 _ ("Windows (16 bit) %u.%u"), major, minor); 450 break; 451 case 1: 452 sret = snprintf (buf, sizeof (buf), 453 _ ("Macintosh %u.%u"), major, minor); 454 break; 455 case 2: 456 sret = snprintf (buf, sizeof (buf), 457 _ ("Windows %u.%u"), major, minor); 458 break; 459 default: 460 return 0; 461 } 462 if ( (0 >= sret) || 463 (sizeof (buf) <= (size_t) sret) ) 464 return 0; 465 *reported = 1; 466 return add_metadata (proc, proc_cls, 467 buf, 468 EXTRACTOR_METATYPE_AUTHORING_OS); 469 } 470 471 472 /** 473 * Function called on the "Workbook" (BIFF8) or "Book" (BIFF5) stream 474 * of an Excel document. Reports the user names from the File 475 * Protection Block, which the OLE2 property sets do not cover. 476 * 477 * @param src the workbook stream 478 * @param proc function to call on meta data found 479 * @param proc_cls closure for @a proc 480 * @return 0 to continue to extract, 1 if we are done 481 */ 482 static int 483 process_excel (GsfInput *src, 484 EXTRACTOR_MetaDataProcessor proc, 485 void *proc_cls) 486 { 487 const guint8 *data; 488 gsf_off_t size; 489 size_t want; 490 491 size = gsf_input_size (src); 492 if (8 > size) 493 return 0; 494 /* The File Protection Block is at the very beginning of the globals 495 substream, so we never need to look at the whole workbook. */ 496 want = (size > 64 * 1024) ? 64 * 1024 : (size_t) size; 497 if (gsf_input_seek (src, 0, G_SEEK_SET)) 498 return 0; 499 if (NULL == (data = gsf_input_read (src, want, NULL))) 500 return 0; 501 return (0 < EXTRACTOR_msoffice_biff_extract (data, 502 want, 503 "ole2", 504 proc, 505 proc_cls)) ? 1 : 0; 506 } 507 508 509 /** 510 * Function called on the "Current User" stream of a PowerPoint 511 * document. Its CurrentUserAtom ([MS-PPT] section 2.3.2) records the 512 * name of the user who last opened the presentation for editing -- 513 * which is not the same as, and frequently differs from, the author 514 * recorded in the summary information. 515 * 516 * @param src the "Current User" stream 517 * @param proc function to call on meta data found 518 * @param proc_cls closure for @a proc 519 * @return 0 to continue to extract, 1 if we are done 520 */ 521 static int 522 process_current_user (GsfInput *src, 523 EXTRACTOR_MetaDataProcessor proc, 524 void *proc_cls) 525 { 526 /** 527 * Size of the record header preceding the atom. 528 */ 529 static const size_t hdr_size = 8; 530 /** 531 * Size of the fixed part of the CurrentUserAtom. 532 */ 533 static const size_t atom_size = 20; 534 const guint8 *d; 535 gsf_off_t size; 536 size_t want; 537 size_t len_user; 538 char *name; 539 int ret; 540 541 size = gsf_input_size (src); 542 if ((gsf_off_t) (hdr_size + atom_size) > size) 543 return 0; 544 /* The stream holds nothing but this atom. */ 545 want = (size > 4096) ? 4096 : (size_t) size; 546 if (gsf_input_seek (src, 0, G_SEEK_SET)) 547 return 0; 548 if (NULL == (d = gsf_input_read (src, want, NULL))) 549 return 0; 550 if (0x0FF6 != (d[2] | (d[3] << 8))) 551 return 0; /* not an RT_CurrentUserAtom */ 552 len_user = d[hdr_size + 12] | (d[hdr_size + 13] << 8); 553 if ( (0 == len_user) || 554 (255 < len_user) || 555 (hdr_size + atom_size + len_user > want) ) 556 return 0; 557 /* The name is stored twice: once in the ANSI code page and, if the 558 stream is long enough, once more as UTF-16. Prefer the latter. */ 559 if (hdr_size + atom_size + 3 * len_user + 4 <= want) 560 name = EXTRACTOR_common_convert_to_utf8 ( 561 (const char *) &d[hdr_size + atom_size + len_user + 4], 562 2 * len_user, 563 "UTF-16LE"); 564 else 565 name = EXTRACTOR_common_convert_to_utf8 ( 566 (const char *) &d[hdr_size + atom_size], 567 len_user, 568 "CP1252"); 569 if (NULL == name) 570 return 0; 571 ret = add_metadata (proc, proc_cls, 572 name, 573 EXTRACTOR_METATYPE_LAST_SAVED_BY); 574 free (name); 575 return ret; 576 } 577 578 579 /** 580 * We use "__" to translate using iso-639. 581 * 582 * @param a string to translate 583 * @return translated string 584 */ 585 #define __(a) dgettext ("iso-639", a) 586 587 588 /** 589 * Get the language string for the given language ID (lid) 590 * value. 591 * 592 * @param lid language id value 593 * @return language string corresponding to the lid 594 */ 595 static const char * 596 lid_to_language (unsigned int lid) 597 { 598 switch (lid) 599 { 600 case 0x0400: 601 return _ ("No Proofing"); 602 case 0x0401: 603 return __ ("Arabic"); 604 case 0x0402: 605 return __ ("Bulgarian"); 606 case 0x0403: 607 return __ ("Catalan"); 608 case 0x0404: 609 return _ ("Traditional Chinese"); 610 case 0x0804: 611 return _ ("Simplified Chinese"); 612 case 0x0405: 613 return __ ("Chechen"); 614 case 0x0406: 615 return __ ("Danish"); 616 case 0x0407: 617 return __ ("German"); 618 case 0x0807: 619 return _ ("Swiss German"); 620 case 0x0408: 621 return __ ("Greek"); 622 case 0x0409: 623 return _ ("U.S. English"); 624 case 0x0809: 625 return _ ("U.K. English"); 626 case 0x0c09: 627 return _ ("Australian English"); 628 case 0x040a: 629 return _ ("Castilian Spanish"); 630 case 0x080a: 631 return _ ("Mexican Spanish"); 632 case 0x040b: 633 return __ ("Finnish"); 634 case 0x040c: 635 return __ ("French"); 636 case 0x080c: 637 return _ ("Belgian French"); 638 case 0x0c0c: 639 return _ ("Canadian French"); 640 case 0x100c: 641 return _ ("Swiss French"); 642 case 0x040d: 643 return __ ("Hebrew"); 644 case 0x040e: 645 return __ ("Hungarian"); 646 case 0x040f: 647 return __ ("Icelandic"); 648 case 0x0410: 649 return __ ("Italian"); 650 case 0x0810: 651 return _ ("Swiss Italian"); 652 case 0x0411: 653 return __ ("Japanese"); 654 case 0x0412: 655 return __ ("Korean"); 656 case 0x0413: 657 return __ ("Dutch"); 658 case 0x0813: 659 return _ ("Belgian Dutch"); 660 case 0x0414: 661 return _ ("Norwegian Bokmal"); 662 case 0x0814: 663 return __ ("Norwegian Nynorsk"); 664 case 0x0415: 665 return __ ("Polish"); 666 case 0x0416: 667 return __ ("Brazilian Portuguese"); 668 case 0x0816: 669 return __ ("Portuguese"); 670 case 0x0417: 671 return _ ("Rhaeto-Romanic"); 672 case 0x0418: 673 return __ ("Romanian"); 674 case 0x0419: 675 return __ ("Russian"); 676 case 0x041a: 677 return _ ("Croato-Serbian (Latin)"); 678 case 0x081a: 679 return _ ("Serbo-Croatian (Cyrillic)"); 680 case 0x041b: 681 return __ ("Slovak"); 682 case 0x041c: 683 return __ ("Albanian"); 684 case 0x041d: 685 return __ ("Swedish"); 686 case 0x041e: 687 return __ ("Thai"); 688 case 0x041f: 689 return __ ("Turkish"); 690 case 0x0420: 691 return __ ("Urdu"); 692 case 0x0421: 693 return __ ("Bahasa"); 694 case 0x0422: 695 return __ ("Ukrainian"); 696 case 0x0423: 697 return __ ("Byelorussian"); 698 case 0x0424: 699 return __ ("Slovenian"); 700 case 0x0425: 701 return __ ("Estonian"); 702 case 0x0426: 703 return __ ("Latvian"); 704 case 0x0427: 705 return __ ("Lithuanian"); 706 case 0x0429: 707 return _ ("Farsi"); 708 case 0x042D: 709 return __ ("Basque"); 710 case 0x042F: 711 return __ ("Macedonian"); 712 case 0x0436: 713 return __ ("Afrikaans"); 714 case 0x043E: 715 return __ ("Malayalam"); 716 default: 717 return NULL; 718 } 719 } 720 721 722 /** 723 * Extract editing history from XTable stream. 724 * 725 * @param stream OLE stream to process 726 * @param lcSttbSavedBy length of the revision history in bytes 727 * @param fcSttbSavedBy offset of the revision history in the stream 728 * @param proc function to call on meta data found 729 * @param proc_cls closure for proc 730 * @return 0 to continue to extract, 1 if we are done 731 */ 732 static int 733 history_extract (GsfInput *stream, 734 unsigned int lcbSttbSavedBy, 735 unsigned int fcSttbSavedBy, 736 EXTRACTOR_MetaDataProcessor proc, 737 void *proc_cls) 738 { 739 unsigned int where; 740 unsigned char *lbuffer; 741 unsigned int i; 742 unsigned int length; 743 char *author; 744 char *filename; 745 char *rbuf; 746 unsigned int nRev; 747 int ret; 748 749 /* goto offset of revision information */ 750 gsf_input_seek (stream, fcSttbSavedBy, G_SEEK_SET); 751 if (gsf_input_remaining (stream) < lcbSttbSavedBy) 752 return 0; 753 if (NULL == (lbuffer = malloc (lcbSttbSavedBy))) 754 return 0; 755 /* read all the revision history */ 756 gsf_input_read (stream, lcbSttbSavedBy, lbuffer); 757 /* there are n strings, so n/2 revisions (author & file) */ 758 nRev = (lbuffer[2] + (lbuffer[3] << 8)) / 2; 759 where = 6; 760 ret = 0; 761 for (i = 0; i < nRev; i++) 762 { 763 if (where >= lcbSttbSavedBy) 764 break; 765 length = lbuffer[where++]; 766 if ( (where + 2 * length + 2 >= lcbSttbSavedBy) || 767 (where + 2 * length + 2 <= where) ) 768 break; 769 author = EXTRACTOR_common_convert_to_utf8 ((const char*) &lbuffer[where], 770 length * 2, 771 "UTF-16BE"); 772 where += length * 2 + 1; 773 length = lbuffer[where++]; 774 if ( (where + 2 * length >= lcbSttbSavedBy) || 775 (where + 2 * length + 1 <= where) ) 776 { 777 if (NULL != author) 778 free (author); 779 break; 780 } 781 filename = EXTRACTOR_common_convert_to_utf8 ((const char*) &lbuffer[where], 782 length * 2, 783 "UTF-16BE"); 784 where += length * 2 + 1; 785 if ( (NULL != author) && 786 (NULL != filename) ) 787 { 788 size_t bsize; 789 790 bsize = strlen (author) + strlen (filename) + 512; 791 if (NULL != (rbuf = malloc (bsize))) 792 { 793 int snret; 794 795 snret = snprintf (rbuf, 796 bsize, 797 _ ("Revision #%u: Author `%s' worked on `%s'"), 798 i, 799 author, 800 filename); 801 if ( (-1 != snret) && 802 (bsize > (size_t) snret) ) 803 { 804 ret = add_metadata (proc, 805 proc_cls, 806 rbuf, 807 EXTRACTOR_METATYPE_REVISION_HISTORY); 808 } 809 free (rbuf); 810 } 811 } 812 if (NULL != author) 813 free (author); 814 if (NULL != filename) 815 free (filename); 816 if (0 != ret) 817 break; 818 } 819 free (lbuffer); 820 return ret; 821 } 822 823 824 /* ********************** Word 97-2003 full text ********************** */ 825 826 /** 827 * Largest CLX (piece table and property modifiers) we are willing to 828 * load. Real documents have a few kilobytes at most. 829 */ 830 #define WORD_MAX_CLX (16 * 1024 * 1024) 831 832 /** 833 * Maximum nesting depth of fields we keep track of; fields nested more 834 * deeply are balanced correctly, but all their text is dropped. 835 */ 836 #define WORD_MAX_FIELD_DEPTH 32 837 838 /** 839 * Number of characters of a field instruction we remember to decide 840 * which kind of field it is. 841 */ 842 #define WORD_FIELD_INSTR_LEN 32 843 844 /** 845 * Maximum number of characters of the main text we convert. Pieces 846 * may all point to the same bytes, so without this limit a small file 847 * could keep us busy for a long time (e.g. with text that is hidden 848 * inside a field instruction and thus never fills the output buffer). 849 */ 850 #define WORD_MAX_CHARS (32 * 1024 * 1024) 851 852 /** 853 * Size of the chunks in which we read the text of a piece. 854 */ 855 #define WORD_READ_CHUNK 4096 856 857 858 /** 859 * A field that is currently open in the text stream. 860 */ 861 struct WordField 862 { 863 /** 864 * The start of the field instruction (ASCII only). 865 */ 866 char instr[WORD_FIELD_INSTR_LEN]; 867 868 /** 869 * Number of valid bytes in @e instr. 870 */ 871 unsigned int instr_len; 872 873 /** 874 * 0 while we are in the field instruction, 1 once we got to the 875 * field result. 876 */ 877 int in_result; 878 879 /** 880 * 1 if the result of this field must be dropped (page numbers). 881 */ 882 int hide_result; 883 }; 884 885 886 /** 887 * State while converting the main text of a Word document. 888 */ 889 struct WordText 890 { 891 /** 892 * Where the text goes. 893 */ 894 struct EXTRACTOR_FullText ft; 895 896 /** 897 * Stack of open fields. 898 */ 899 struct WordField fields[WORD_MAX_FIELD_DEPTH]; 900 901 /** 902 * Number of entries in @e fields. 903 */ 904 unsigned int depth; 905 906 /** 907 * Number of open fields beyond #WORD_MAX_FIELD_DEPTH. 908 */ 909 unsigned int overflow; 910 911 /** 912 * 1 if the text at the current position is visible (not part of a 913 * field instruction or a dropped field result). 914 */ 915 int visible; 916 917 /** 918 * Pending UTF-16 high surrogate, 0 for none. 919 */ 920 uint16_t high_surrogate; 921 }; 922 923 924 /** 925 * Mapping of the bytes 0x80..0x9F in "compressed" (8-bit) text pieces 926 * to Unicode ([MS-DOC] 2.9.73, FcCompressed); 0 means that the byte 927 * maps to itself (and thus is a C1 control that we drop). 928 */ 929 static const uint16_t word_c1_map[32] = { 930 0, 0, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, 931 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0, 0, 0, 932 0, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, 933 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0, 0, 0x0178 934 }; 935 936 937 /** 938 * Read a little-endian 16-bit value. 939 * 940 * @param p where to read 941 * @return the value 942 */ 943 static uint16_t 944 word_le16 (const guint8 *p) 945 { 946 return (uint16_t) (p[0] | (p[1] << 8)); 947 } 948 949 950 /** 951 * Read a little-endian 32-bit value. 952 * 953 * @param p where to read 954 * @return the value 955 */ 956 static uint32_t 957 word_le32 (const guint8 *p) 958 { 959 return ((uint32_t) p[0]) 960 | (((uint32_t) p[1]) << 8) 961 | (((uint32_t) p[2]) << 16) 962 | (((uint32_t) p[3]) << 24); 963 } 964 965 966 /** 967 * Recompute whether text at the current position is visible. 968 * 969 * @param wt conversion state 970 */ 971 static void 972 word_update_visible (struct WordText *wt) 973 { 974 unsigned int i; 975 976 wt->visible = (0 == wt->overflow); 977 for (i = 0; i < wt->depth; i++) 978 if ( (! wt->fields[i].in_result) || 979 (wt->fields[i].hide_result) ) 980 wt->visible = 0; 981 } 982 983 984 /** 985 * Check whether the field instruction @a instr is one of the fields 986 * showing page numbers (PAGE, NUMPAGES, SECTIONPAGES), whose result we 987 * drop. 988 * 989 * @param f the field 990 * @return 1 if the field result must be dropped 991 */ 992 static int 993 word_is_page_field (const struct WordField *f) 994 { 995 static const char *const names[] = { 996 "PAGE", "NUMPAGES", "SECTIONPAGES", NULL 997 }; 998 unsigned int s = 0; 999 unsigned int e; 1000 unsigned int i; 1001 1002 while ( (s < f->instr_len) && 1003 (' ' == f->instr[s]) ) 1004 s++; 1005 e = s; 1006 while ( (e < f->instr_len) && 1007 (' ' != f->instr[e]) && 1008 ('\\' != f->instr[e]) ) 1009 e++; 1010 for (i = 0; NULL != names[i]; i++) 1011 if ( (strlen (names[i]) == e - s) && 1012 (0 == strncasecmp (names[i], &f->instr[s], e - s)) ) 1013 return 1; 1014 return 0; 1015 } 1016 1017 1018 /** 1019 * Process one character of the main document text. 1020 * 1021 * @param wt conversion state 1022 * @param cp the character (Unicode code point or Word control code) 1023 */ 1024 static void 1025 word_char (struct WordText *wt, 1026 uint32_t cp) 1027 { 1028 struct WordField *top; 1029 1030 switch (cp) 1031 { 1032 case 0x13: /* field begin */ 1033 if ( (0 < wt->overflow) || 1034 (WORD_MAX_FIELD_DEPTH == wt->depth) ) 1035 { 1036 wt->overflow++; 1037 } 1038 else 1039 { 1040 top = &wt->fields[wt->depth++]; 1041 top->instr_len = 0; 1042 top->in_result = 0; 1043 top->hide_result = 0; 1044 } 1045 word_update_visible (wt); 1046 return; 1047 case 0x14: /* field separator */ 1048 if ( (0 == wt->overflow) && 1049 (0 < wt->depth) ) 1050 { 1051 top = &wt->fields[wt->depth - 1]; 1052 if (! top->in_result) 1053 { 1054 top->in_result = 1; 1055 top->hide_result = word_is_page_field (top); 1056 word_update_visible (wt); 1057 } 1058 } 1059 return; 1060 case 0x15: /* field end */ 1061 if (0 < wt->overflow) 1062 wt->overflow--; 1063 else if (0 < wt->depth) 1064 wt->depth--; 1065 word_update_visible (wt); 1066 return; 1067 default: 1068 break; 1069 } 1070 if ( (0 == wt->overflow) && 1071 (0 < wt->depth) ) 1072 { 1073 top = &wt->fields[wt->depth - 1]; 1074 if ( (! top->in_result) && 1075 (top->instr_len < WORD_FIELD_INSTR_LEN) ) 1076 top->instr[top->instr_len++] = (cp >= 0x20) && (cp < 0x7F) 1077 ? (char) cp 1078 : ' '; 1079 } 1080 if (! wt->visible) 1081 return; 1082 switch (cp) 1083 { 1084 case 0x07: /* table cell / row end */ 1085 case 0x0C: /* page or section break */ 1086 case 0x0D: /* paragraph end */ 1087 EXTRACTOR_fulltext_paragraph_ (&wt->ft); 1088 return; 1089 case 0x09: /* tab */ 1090 case 0x0B: /* line break */ 1091 EXTRACTOR_fulltext_space_ (&wt->ft); 1092 return; 1093 case 0x1E: /* non-breaking hyphen */ 1094 EXTRACTOR_fulltext_append_cp_ (&wt->ft, '-'); 1095 return; 1096 case 0x1F: /* optional hyphen */ 1097 return; 1098 default: 1099 break; 1100 } 1101 if (cp < 0x20) 1102 return; /* anchors of pictures, notes, comments, ... */ 1103 EXTRACTOR_fulltext_append_cp_ (&wt->ft, cp); 1104 } 1105 1106 1107 /** 1108 * Process one UTF-16 code unit of the main document text. 1109 * 1110 * @param wt conversion state 1111 * @param cu the code unit 1112 */ 1113 static void 1114 word_utf16 (struct WordText *wt, 1115 uint16_t cu) 1116 { 1117 if ( (cu >= 0xDC00) && (cu <= 0xDFFF) && 1118 (0 != wt->high_surrogate) ) 1119 { 1120 uint32_t cp; 1121 1122 cp = 0x10000 1123 + (((uint32_t) (wt->high_surrogate - 0xD800)) << 10) 1124 + (cu - 0xDC00); 1125 wt->high_surrogate = 0; 1126 word_char (wt, cp); 1127 return; 1128 } 1129 if (0 != wt->high_surrogate) 1130 { 1131 wt->high_surrogate = 0; 1132 word_char (wt, 0xFFFD); 1133 } 1134 if ( (cu >= 0xD800) && (cu <= 0xDBFF) ) 1135 { 1136 wt->high_surrogate = cu; 1137 return; 1138 } 1139 if ( (cu >= 0xDC00) && (cu <= 0xDFFF) ) 1140 { 1141 word_char (wt, 0xFFFD); 1142 return; 1143 } 1144 word_char (wt, cu); 1145 } 1146 1147 1148 /** 1149 * Convert the text of one piece. 1150 * 1151 * @param wt conversion state 1152 * @param word the WordDocument stream 1153 * @param off offset of the text in @a word 1154 * @param nchars number of characters in the piece 1155 * @param compressed 1 for 8-bit text, 0 for UTF-16LE 1156 * @return 0 on success, -1 if the piece is outside of @a word 1157 */ 1158 static int 1159 word_piece (struct WordText *wt, 1160 GsfInput *word, 1161 uint64_t off, 1162 uint64_t nchars, 1163 int compressed) 1164 { 1165 guint8 buf[WORD_READ_CHUNK]; 1166 uint64_t nbytes; 1167 uint64_t size; 1168 1169 size = (uint64_t) gsf_input_size (word); 1170 nbytes = compressed ? nchars : 2 * nchars; 1171 if ( (off > size) || 1172 (nbytes > size - off) ) 1173 return -1; 1174 if (gsf_input_seek (word, (gsf_off_t) off, G_SEEK_SET)) 1175 return -1; 1176 while ( (0 < nbytes) && 1177 (! wt->ft.full) ) 1178 { 1179 size_t want = (nbytes > sizeof (buf)) ? sizeof (buf) : (size_t) nbytes; 1180 size_t i; 1181 1182 if (NULL == gsf_input_read (word, want, buf)) 1183 return -1; 1184 nbytes -= want; 1185 if (compressed) 1186 { 1187 for (i = 0; i < want; i++) 1188 { 1189 uint32_t cp = buf[i]; 1190 1191 if ( (cp >= 0x80) && 1192 (cp <= 0x9F) ) 1193 { 1194 if (0 == word_c1_map[cp - 0x80]) 1195 continue; 1196 cp = word_c1_map[cp - 0x80]; 1197 } 1198 word_char (wt, cp); 1199 } 1200 } 1201 else 1202 { 1203 for (i = 0; i + 1 < want; i += 2) 1204 word_utf16 (wt, word_le16 (&buf[i])); 1205 } 1206 } 1207 return 0; 1208 } 1209 1210 1211 /** 1212 * Read @a len bytes at @a off from @a in into a fresh buffer. 1213 * 1214 * @param in stream to read from 1215 * @param off where to start 1216 * @param len number of bytes to read 1217 * @return NULL on error 1218 */ 1219 static guint8 * 1220 word_read_range (GsfInput *in, 1221 uint32_t off, 1222 uint32_t len) 1223 { 1224 guint8 *buf; 1225 gsf_off_t size; 1226 1227 size = gsf_input_size (in); 1228 if ( (0 == len) || 1229 ((gsf_off_t) off > size) || 1230 ((gsf_off_t) len > size - (gsf_off_t) off) ) 1231 return NULL; 1232 if (NULL == (buf = malloc (len))) 1233 return NULL; 1234 if ( (gsf_input_seek (in, (gsf_off_t) off, G_SEEK_SET)) || 1235 (NULL == gsf_input_read (in, len, buf)) ) 1236 { 1237 free (buf); 1238 return NULL; 1239 } 1240 return buf; 1241 } 1242 1243 1244 /** 1245 * Extract the main text of a Word 97-2003 document and report it as 1246 * #EXTRACTOR_METATYPE_FULL_TEXT. 1247 * 1248 * We parse the File Information Block at the start of the 1249 * WordDocument stream, load the piece table (CLX) from the table 1250 * stream it points to and convert the character positions 1251 * [0, ccpText). That range is the main document only: headers and 1252 * footers, footnotes and endnotes, comments and text boxes all come 1253 * after it and are thus left out. Paragraph marks, table cell/row 1254 * marks and page/section breaks end a paragraph. Of fields, only the 1255 * result is kept (not the instruction), and the results of page 1256 * number fields (PAGE, NUMPAGES, SECTIONPAGES) are dropped. 1257 * 1258 * Limitations: deleted text of tracked changes and hidden text are 1259 * still included (dropping them requires parsing character 1260 * properties); documents older than Word 97 are ignored. 1261 * 1262 * @param infile the OLE2 container 1263 * @param ec extraction context 1264 * @return 0 to continue extracting, 1 to stop 1265 */ 1266 static int 1267 word_extract_fulltext (GsfInfile *infile, 1268 struct EXTRACTOR_ExtractContext *ec) 1269 { 1270 GsfInput *word; 1271 GsfInput *table = NULL; 1272 guint8 fib[34]; 1273 guint8 *hdr = NULL; 1274 guint8 *clx = NULL; 1275 struct WordText *wt = NULL; 1276 uint16_t flags; 1277 uint16_t csw; 1278 uint16_t cslw; 1279 uint16_t cb_fclcb; 1280 uint32_t fib_len; 1281 uint32_t ccp_text; 1282 uint32_t fc_clx; 1283 uint32_t lcb_clx; 1284 uint32_t pos; 1285 uint32_t lcb_plc; 1286 uint32_t npieces; 1287 uint32_t next_cp; 1288 const guint8 *plc; 1289 uint32_t i; 1290 int ret = 0; 1291 1292 if (NULL == (word = gsf_infile_child_by_name (infile, "WordDocument"))) 1293 return 0; 1294 if ( (gsf_input_seek (word, 0, G_SEEK_SET)) || 1295 (NULL == gsf_input_read (word, sizeof (fib), fib)) ) 1296 goto CLEANUP; 1297 if (0xA5EC != word_le16 (&fib[0])) 1298 goto CLEANUP; 1299 flags = word_le16 (&fib[0x0A]); 1300 if (0 != (flags & 0x0100)) 1301 goto CLEANUP; /* fEncrypted */ 1302 csw = word_le16 (&fib[32]); 1303 if (0x000E != csw) 1304 goto CLEANUP; /* not a Word 97 or later FIB */ 1305 /* FibBase, csw, fibRgW, cslw, fibRgLw, cbRgFcLcb, fibRgFcLcb up to 1306 and including fcClx/lcbClx (pair #33) */ 1307 fib_len = 34 + 2 * 14 + 2 + 4 * 22 + 2 + 8 * 34; 1308 if (NULL == (hdr = word_read_range (word, 0, fib_len))) 1309 goto CLEANUP; 1310 cslw = word_le16 (&hdr[34 + 2 * 14]); 1311 if (0x0016 != cslw) 1312 goto CLEANUP; 1313 ccp_text = word_le32 (&hdr[34 + 2 * 14 + 2 + 4 * 3]); 1314 cb_fclcb = word_le16 (&hdr[34 + 2 * 14 + 2 + 4 * 22]); 1315 if (cb_fclcb < 34) 1316 goto CLEANUP; 1317 fc_clx = word_le32 (&hdr[34 + 2 * 14 + 2 + 4 * 22 + 2 + 8 * 33]); 1318 lcb_clx = word_le32 (&hdr[34 + 2 * 14 + 2 + 4 * 22 + 2 + 8 * 33 + 4]); 1319 if (ccp_text > WORD_MAX_CHARS) 1320 ccp_text = WORD_MAX_CHARS; 1321 if ( (0 == ccp_text) || 1322 (lcb_clx > WORD_MAX_CLX) ) 1323 goto CLEANUP; 1324 table = gsf_infile_child_by_name (infile, 1325 (0 != (flags & 0x0200)) 1326 ? "1Table" 1327 : "0Table"); 1328 if (NULL == table) 1329 goto CLEANUP; 1330 if (NULL == (clx = word_read_range (table, fc_clx, lcb_clx))) 1331 goto CLEANUP; 1332 /* skip the Prc entries (property modifiers), find the Pcdt */ 1333 pos = 0; 1334 while ( (pos < lcb_clx) && 1335 (0x01 == clx[pos]) ) 1336 { 1337 int16_t cb_grpprl; 1338 1339 if (lcb_clx - pos < 3) 1340 goto CLEANUP; 1341 cb_grpprl = (int16_t) word_le16 (&clx[pos + 1]); 1342 if (cb_grpprl < 0) 1343 goto CLEANUP; 1344 pos += 3; 1345 if ((uint32_t) cb_grpprl > lcb_clx - pos) 1346 goto CLEANUP; 1347 pos += (uint32_t) cb_grpprl; 1348 } 1349 if ( (lcb_clx - pos < 5) || 1350 (0x02 != clx[pos]) ) 1351 goto CLEANUP; 1352 lcb_plc = word_le32 (&clx[pos + 1]); 1353 pos += 5; 1354 if ( (lcb_plc > lcb_clx - pos) || 1355 (lcb_plc < 4 + 12) || 1356 (0 != (lcb_plc - 4) % 12) ) 1357 goto CLEANUP; 1358 plc = &clx[pos]; 1359 npieces = (lcb_plc - 4) / 12; 1360 if (NULL == (wt = calloc (1, sizeof (*wt)))) 1361 goto CLEANUP; 1362 EXTRACTOR_fulltext_init_ (&wt->ft); 1363 EXTRACTOR_fulltext_set_context_ (&wt->ft, ec); 1364 wt->visible = 1; 1365 next_cp = 0; 1366 for (i = 0; i < npieces; i++) 1367 { 1368 uint32_t cp_start = word_le32 (&plc[4 * i]); 1369 uint32_t cp_end = word_le32 (&plc[4 * (i + 1)]); 1370 const guint8 *pcd = &plc[4 * (npieces + 1) + 8 * i]; 1371 uint32_t fc = word_le32 (&pcd[2]); 1372 int compressed = (0 != (fc & 0x40000000)); 1373 uint64_t off; 1374 1375 off = compressed 1376 ? (fc & 0x3FFFFFFF) / 2 1377 : (fc & 0x3FFFFFFF); 1378 1379 if (wt->ft.full) 1380 break; 1381 if (next_cp >= ccp_text) 1382 break; 1383 if (cp_end > ccp_text) 1384 cp_end = ccp_text; 1385 /* never convert a character position twice, pieces must be in 1386 order and must not overlap */ 1387 if (cp_start < next_cp) 1388 { 1389 if (cp_end <= next_cp) 1390 continue; 1391 off += (uint64_t) (next_cp - cp_start) * (compressed ? 1 : 2); 1392 cp_start = next_cp; 1393 } 1394 if (cp_end <= cp_start) 1395 continue; 1396 if (0 != word_piece (wt, 1397 word, 1398 off, 1399 cp_end - cp_start, 1400 compressed)) 1401 continue; 1402 next_cp = cp_end; 1403 } 1404 if (0 != wt->high_surrogate) 1405 word_char (wt, 0xFFFD); 1406 ret = (0 != EXTRACTOR_fulltext_emit_ (&wt->ft, ec, "ole2")) ? 1 : 0; 1407 CLEANUP: 1408 if (NULL != wt) 1409 { 1410 EXTRACTOR_fulltext_clear_ (&wt->ft); 1411 free (wt); 1412 } 1413 free (clx); 1414 free (hdr); 1415 if (NULL != table) 1416 g_object_unref (G_OBJECT (table)); 1417 g_object_unref (G_OBJECT (word)); 1418 return ret; 1419 } 1420 1421 1422 /* *************************** custom GSF input method ***************** */ 1423 1424 #define LE_TYPE_INPUT (le_input_get_type ()) 1425 #define LE_INPUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ 1426 LE_TYPE_INPUT, \ 1427 LeInput)) 1428 #define LE_INPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \ 1429 LE_TYPE_INPUT, \ 1430 LeInputClass)) 1431 #define IS_LE_INPUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ 1432 LE_TYPE_INPUT)) 1433 #define IS_LE_INPUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \ 1434 LE_TYPE_INPUT)) 1435 #define LE_INPUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ 1436 LE_TYPE_INPUT, \ 1437 LeInputClass) \ 1438 ) 1439 1440 /** 1441 * Internal state of an "LeInput" object. 1442 */ 1443 typedef struct _LeInputPrivate 1444 { 1445 /** 1446 * Our extraction context. 1447 */ 1448 struct EXTRACTOR_ExtractContext *ec; 1449 } LeInputPrivate; 1450 1451 1452 /** 1453 * Overall state of an "LeInput" object. 1454 */ 1455 typedef struct _LeInput 1456 { 1457 /** 1458 * Inherited state from parent (GsfInput). 1459 */ 1460 GsfInput input; 1461 1462 /*< private > */ 1463 /** 1464 * Private state of the LeInput. 1465 */ 1466 LeInputPrivate *priv; 1467 } LeInput; 1468 1469 1470 /** 1471 * LeInput's class state. 1472 */ 1473 typedef struct _LeInputClass 1474 { 1475 /** 1476 * GsfInput is our parent class. 1477 */ 1478 GsfInputClass parent_class; 1479 1480 /* Padding for future expansion */ 1481 void (*_gtk_reserved1)(void); 1482 void (*_gtk_reserved2)(void); 1483 void (*_gtk_reserved3)(void); 1484 void (*_gtk_reserved4)(void); 1485 } LeInputClass; 1486 1487 1488 /** 1489 * Constructor for LeInput objects. 1490 * 1491 * @param ec extraction context to use 1492 * @return the LeInput, NULL on error 1493 */ 1494 GsfInput * 1495 le_input_new (struct EXTRACTOR_ExtractContext *ec); 1496 1497 1498 /** 1499 * Class initializer for the "LeInput" class. 1500 * 1501 * @param class class object to initialize 1502 */ 1503 static void 1504 le_input_class_init (LeInputClass *class); 1505 1506 1507 /** 1508 * Initialize internal state of fresh input object. 1509 * 1510 * @param input object to initialize 1511 */ 1512 static void 1513 le_input_init (LeInput *input); 1514 1515 1516 /** 1517 * Macro to create LeInput type definition and register the class. 1518 */ 1519 GSF_CLASS (LeInput, le_input, le_input_class_init, le_input_init, 1520 GSF_INPUT_TYPE) 1521 1522 1523 /** 1524 * Duplicate input, leaving the new one at the same offset. 1525 * 1526 * @param input the input to duplicate 1527 * @param err location for error reporting, can be NULL 1528 * @return NULL on error (always) 1529 */ 1530 static GsfInput * 1531 le_input_dup (GsfInput * input, 1532 GError * *err) 1533 { 1534 if (NULL != err) 1535 *err = g_error_new (gsf_input_error_id (), 0, 1536 "dup not supported on LeInput"); 1537 return NULL; 1538 } 1539 1540 1541 /** 1542 * Read at least num_bytes. Does not change the current position if 1543 * there is an error. Will only read if the entire amount can be 1544 * read. Invalidates the buffer associated with previous calls to 1545 * gsf_input_read. 1546 * 1547 * @param input 1548 * @param num_bytes 1549 * @param optional_buffer 1550 * @return buffer where num_bytes data are available, or NULL on error 1551 */ 1552 static const guint8 * 1553 le_input_read (GsfInput *input, 1554 size_t num_bytes, 1555 guint8 *optional_buffer) 1556 { 1557 LeInput *li = LE_INPUT (input); 1558 struct EXTRACTOR_ExtractContext *ec; 1559 void *buf; 1560 uint64_t old_off; 1561 ssize_t ret; 1562 1563 ec = li->priv->ec; 1564 old_off = ec->seek (ec->cls, 1565 0, 1566 SEEK_CUR); 1567 if (num_bytes 1568 != (ret = ec->read (ec->cls, 1569 &buf, 1570 num_bytes))) 1571 { 1572 /* we don't support partial reads; 1573 most other GsfInput implementations in this case 1574 allocate some huge temporary buffer just to avoid 1575 the partial read; we might need to do that as well!? */ 1576 ec->seek (ec->cls, 1577 old_off, 1578 SEEK_SET); 1579 return NULL; 1580 } 1581 if (NULL != optional_buffer) 1582 { 1583 memcpy (optional_buffer, 1584 buf, 1585 num_bytes); 1586 return optional_buffer; 1587 } 1588 return buf; 1589 } 1590 1591 1592 /** 1593 * Move the current location in an input stream 1594 * 1595 * @param input stream to seek 1596 * @param offset target offset 1597 * @param whence determines to what the offset is relative to 1598 * @return TRUE on error 1599 */ 1600 static gboolean 1601 le_input_seek (GsfInput *input, 1602 gsf_off_t offset, 1603 GSeekType whence) 1604 { 1605 LeInput *li = LE_INPUT (input); 1606 struct EXTRACTOR_ExtractContext *ec; 1607 int w; 1608 int64_t ret; 1609 1610 ec = li->priv->ec; 1611 switch (whence) 1612 { 1613 case G_SEEK_SET: 1614 w = SEEK_SET; 1615 break; 1616 case G_SEEK_CUR: 1617 w = SEEK_CUR; 1618 break; 1619 case G_SEEK_END: 1620 w = SEEK_END; 1621 break; 1622 default: 1623 return TRUE; 1624 } 1625 if (-1 == 1626 (ret = ec->seek (ec->cls, 1627 offset, 1628 w))) 1629 return TRUE; 1630 return FALSE; 1631 } 1632 1633 1634 /** 1635 * Class initializer for the "LeInput" class. 1636 * 1637 * @param class class object to initialize 1638 */ 1639 static void 1640 le_input_class_init (LeInputClass *class) 1641 { 1642 GsfInputClass *input_class; 1643 1644 input_class = (GsfInputClass *) class; 1645 input_class->Dup = le_input_dup; 1646 input_class->Read = le_input_read; 1647 input_class->Seek = le_input_seek; 1648 g_type_class_add_private (class, 1649 sizeof (LeInputPrivate)); 1650 } 1651 1652 1653 /** 1654 * Initialize internal state of fresh input object. 1655 * 1656 * @param input object to initialize 1657 */ 1658 static void 1659 le_input_init (LeInput *input) 1660 { 1661 LeInputPrivate *priv; 1662 1663 input->priv = 1664 G_TYPE_INSTANCE_GET_PRIVATE (input, LE_TYPE_INPUT, 1665 LeInputPrivate); 1666 priv = input->priv; 1667 priv->ec = NULL; 1668 } 1669 1670 1671 /** 1672 * Creates a new LeInput object. 1673 * 1674 * @param ec extractor context to wrap 1675 * @return NULL on error 1676 */ 1677 GsfInput * 1678 le_input_new (struct EXTRACTOR_ExtractContext *ec) 1679 { 1680 LeInput *input; 1681 1682 input = g_object_new (LE_TYPE_INPUT, NULL); 1683 gsf_input_set_size (GSF_INPUT (input), 1684 ec->get_size (ec->cls)); 1685 gsf_input_seek_emulate (GSF_INPUT (input), 1686 0); 1687 input->input.name = NULL; 1688 input->input.container = NULL; 1689 input->priv->ec = ec; 1690 1691 return GSF_INPUT (input); 1692 } 1693 1694 1695 /* *********************** end of custom GSF input method ************* */ 1696 1697 1698 /** 1699 * Main entry method for the OLE2 extraction plugin. 1700 * 1701 * @param ec extraction context provided to the plugin 1702 */ 1703 void 1704 EXTRACTOR_ole2_extract_method (struct EXTRACTOR_ExtractContext *ec); 1705 1706 void 1707 EXTRACTOR_ole2_extract_method (struct EXTRACTOR_ExtractContext *ec) 1708 { 1709 GsfInput *input; 1710 GsfInfile *infile; 1711 GsfInput *src; 1712 const char *name; 1713 unsigned int i; 1714 unsigned int lcb; 1715 unsigned int fcb; 1716 const unsigned char *data512; 1717 unsigned int lid; 1718 const char *lang; 1719 int os_reported = 0; 1720 int ret; 1721 void *data; 1722 uint64_t fsize; 1723 ssize_t data_size; 1724 1725 fsize = ec->get_size (ec->cls); 1726 if (fsize < 512 + 898) 1727 { 1728 /* File too small for OLE2 */ 1729 return; /* can hardly be OLE2 */ 1730 } 1731 if (512 + 898 > (data_size = ec->read (ec->cls, &data, fsize))) 1732 { 1733 /* Failed to read minimum file size to buffer */ 1734 return; 1735 } 1736 data512 = (const unsigned char*) data + 512; 1737 lid = data512[6] + (data512[7] << 8); 1738 if ( (NULL != (lang = lid_to_language (lid))) && 1739 (0 != (ret = add_metadata (ec->proc, ec->cls, 1740 lang, 1741 EXTRACTOR_METATYPE_LANGUAGE))) ) 1742 return; 1743 /* The top byte must be widened before it is shifted: `unsigned char' 1744 promotes to `int', and 0x80 << 24 overflows a 32 bit int. */ 1745 lcb = ((uint32_t) data512[726]) 1746 + (((uint32_t) data512[727]) << 8) 1747 + (((uint32_t) data512[728]) << 16) 1748 + (((uint32_t) data512[729]) << 24); 1749 fcb = ((uint32_t) data512[722]) 1750 + (((uint32_t) data512[723]) << 8) 1751 + (((uint32_t) data512[724]) << 16) 1752 + (((uint32_t) data512[725]) << 24); 1753 if (0 != ec->seek (ec->cls, 1754 0, 1755 SEEK_SET)) 1756 { 1757 /* seek failed!? */ 1758 return; 1759 } 1760 #if USE_LE_INPUT 1761 if (NULL == (input = le_input_new (ec))) 1762 { 1763 fprintf (stderr, "le_input_new failed\n"); 1764 return; 1765 } 1766 #else 1767 input = gsf_input_memory_new ((const guint8 *) data, 1768 data_size, 1769 FALSE); 1770 #endif 1771 if (NULL == (infile = gsf_infile_msole_new (input, NULL))) 1772 { 1773 g_object_unref (G_OBJECT (input)); 1774 return; 1775 } 1776 ret = 0; 1777 for (i = 0; i<gsf_infile_num_children (infile); i++) 1778 { 1779 if (0 != ret) 1780 break; 1781 if (NULL == (name = gsf_infile_name_by_index (infile, i))) 1782 continue; 1783 src = NULL; 1784 if ( ( (0 == strcmp (name, "\005SummaryInformation")) || 1785 (0 == strcmp (name, "\005DocumentSummaryInformation")) ) && 1786 (NULL != (src = gsf_infile_child_by_index (infile, i))) ) 1787 { 1788 if (0 == os_reported) 1789 ret = process_system_identifier (src, 1790 &os_reported, 1791 ec->proc, 1792 ec->cls); 1793 if (0 == ret) 1794 ret = process (src, 1795 ec->proc, 1796 ec->cls); 1797 } 1798 if ( (0 == strcmp (name, "SfxDocumentInfo")) && 1799 (NULL != (src = gsf_infile_child_by_index (infile, i))) ) 1800 ret = process_star_office (src, 1801 ec->proc, 1802 ec->cls); 1803 if ( ( (0 == strcmp (name, "Workbook")) || 1804 (0 == strcmp (name, "Book")) ) && 1805 (NULL != (src = gsf_infile_child_by_index (infile, i))) ) 1806 ret = process_excel (src, 1807 ec->proc, 1808 ec->cls); 1809 if ( (0 == strcmp (name, "Current User")) && 1810 (NULL != (src = gsf_infile_child_by_index (infile, i))) ) 1811 ret = process_current_user (src, 1812 ec->proc, 1813 ec->cls); 1814 if (NULL != src) 1815 g_object_unref (G_OBJECT (src)); 1816 } 1817 if (0 != ret) 1818 goto CLEANUP; 1819 1820 for (i = 0; (lcb >= 6) && (i<gsf_infile_num_children (infile)); i++) 1821 { 1822 if (ret != 0) 1823 break; 1824 if (NULL == (name = gsf_infile_name_by_index (infile, i))) 1825 continue; 1826 if ( ( (0 == strcmp (name, "1Table")) || 1827 (0 == strcmp (name, "0Table")) ) && 1828 (NULL != (src = gsf_infile_child_by_index (infile, i))) ) 1829 { 1830 ret = history_extract (src, 1831 lcb, 1832 fcb, 1833 ec->proc, ec->cls); 1834 g_object_unref (G_OBJECT (src)); 1835 } 1836 } 1837 if ( (0 == ret) && 1838 (EXTRACTOR_fulltext_requested_ (ec->config)) ) 1839 ret = word_extract_fulltext (infile, 1840 ec); 1841 CLEANUP: 1842 g_object_unref (G_OBJECT (infile)); 1843 g_object_unref (G_OBJECT (input)); 1844 } 1845 1846 1847 /** 1848 * Custom log function we give to GSF to disable logging. 1849 * 1850 * @param log_domain unused 1851 * @param log_level unused 1852 * @param message unused 1853 * @param user_data unused 1854 */ 1855 static void 1856 nolog (const gchar *log_domain, 1857 GLogLevelFlags log_level, 1858 const gchar *message, 1859 gpointer user_data) 1860 { 1861 /* do nothing */ 1862 } 1863 1864 1865 /** 1866 * OLE2 plugin constructor. Initializes glib and gsf, in particular 1867 * gsf logging is disabled. 1868 */ 1869 void __attribute__ ((constructor)) 1870 ole2_ltdl_init () 1871 { 1872 #if ! GLIB_CHECK_VERSION (2, 35, 0) 1873 g_type_init (); 1874 #endif 1875 #ifdef HAVE_GSF_INIT 1876 gsf_init (); 1877 #endif 1878 /* disable logging -- thanks, Jody! */ 1879 g_log_set_handler ("libgsf:msole", 1880 G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING, 1881 &nolog, NULL); 1882 } 1883 1884 1885 /** 1886 * OLE2 plugin destructor. Shutdown of gsf. 1887 */ 1888 void __attribute__ ((destructor)) 1889 ole2_ltdl_fini () 1890 { 1891 #ifdef HAVE_GSF_INIT 1892 gsf_shutdown (); 1893 #endif 1894 } 1895 1896 1897 /* end of ole2_extractor.c */