libextractor

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

ole2_extractor.c (28013B)


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