libextractor

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

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