"Initial commit to Gerrit"
[profile/ivi/libgsf.git] / gsf / gsf-infile-msole.c
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * gsf-infile-msole.c :
4  *
5  * Copyright (C) 2002-2004 Jody Goldberg (jody@gnome.org)
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2.1 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
19  * USA
20  */
21
22 /* Lots of useful information in
23  *   http://www.aafassociation.org/html/specs/aafcontainerspec-v1.0.1.pdf
24  */
25
26 #include <gsf-config.h>
27 #include <gsf/gsf-infile-impl.h>
28 #include <gsf/gsf-infile-msole.h>
29 #include <gsf/gsf-impl-utils.h>
30 #include <gsf/gsf-utils.h>
31 #include <gsf/gsf-msole-impl.h>
32 #include <gsf/gsf-input-proxy.h>
33
34 #include <string.h>
35 #include <stdio.h>
36
37 #undef G_LOG_DOMAIN
38 #define G_LOG_DOMAIN "libgsf:msole"
39
40 static GObjectClass *parent_class;
41
42 typedef struct {
43         guint32 *block;
44         guint32  num_blocks;
45 } MSOleBAT;
46
47 typedef struct {
48         char     *name;
49         char     *collation_name;
50         int       index;
51         size_t    size;
52         gboolean  use_sb;
53         guint32   first_block;
54         gboolean  is_directory;
55         GList    *children;
56         unsigned char clsid[16];        /* 16 byte GUID used by some apps */
57 } MSOleDirent;
58
59 typedef struct {
60         struct {
61                 MSOleBAT bat;
62                 unsigned shift;
63                 unsigned filter;
64                 size_t   size;
65         } bb, sb;
66         gsf_off_t max_block;
67         guint32 threshold; /* transition between small and big blocks */
68         guint32 sbat_start, num_sbat;
69
70         MSOleDirent *root_dir;
71         GsfInput *sb_file;
72
73         int ref_count;
74 } MSOleInfo;
75
76 struct _GsfInfileMSOle {
77         GsfInfile parent;
78
79         GsfInput    *input;
80         MSOleInfo   *info;
81         MSOleDirent *dirent;
82         MSOleBAT     bat;
83         gsf_off_t    cur_block;
84
85         struct {
86                 guint8  *buf;
87                 size_t  buf_size;
88         } stream;
89 };
90
91 typedef struct {
92         GsfInfileClass  parent_class;
93 } GsfInfileMSOleClass;
94
95 #define GSF_INFILE_MSOLE_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST ((k), GSF_INFILE_MSOLE_TYPE, GsfInfileMSOleClass))
96 #define GSF_IS_INFILE_MSOLE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GSF_INFILE_MSOLE_TYPE))
97
98 /* utility macros */
99 #define OLE_BIG_BLOCK(index, ole)       ((index) >> ole->info->bb.shift)
100
101 static GsfInput *gsf_infile_msole_new_child (GsfInfileMSOle *parent,
102                                              MSOleDirent *dirent, GError **err);
103 static void ole_info_unref (MSOleInfo *info);
104
105 /**
106  * ole_get_block :
107  * @ole: the infile
108  * @block: block number
109  * @buffer: optionally NULL
110  *
111  * Read a block of data from the underlying input.
112  * Be really anal.
113  *
114  * Returns: pointer to the buffer or NULL if there is an error or 0 bytes are
115  * requested.
116  **/
117 static guint8 const *
118 ole_get_block (GsfInfileMSOle const *ole, guint32 block, guint8 *buffer)
119 {
120         g_return_val_if_fail (block < ole->info->max_block, NULL);
121
122         /* OLE_HEADER_SIZE is fixed at 512, but the sector containing the
123          * header is padded out to bb.size (sector size) when bb.size > 512. */
124         if (gsf_input_seek (ole->input,
125                 (gsf_off_t)(MAX (OLE_HEADER_SIZE, ole->info->bb.size) + (block << ole->info->bb.shift)),
126                 G_SEEK_SET) < 0)
127                 return NULL;
128
129         return gsf_input_read (ole->input, ole->info->bb.size, buffer);
130 }
131
132 /**
133  * ole_make_bat :
134  * @metabat: a meta bat to connect to the raw blocks (small or large)
135  * @size_guess: An optional guess as to how many blocks are in the file
136  * @block: The first block in the list.
137  * @res: where to store the result.
138  *
139  * Walk the linked list of the supplied block allocation table and build up a
140  * table for the list starting in @block.
141  *
142  * Returns: TRUE on error.
143  **/
144 static gboolean
145 ole_make_bat (MSOleBAT const *metabat, size_t size_guess, guint32 block,
146               MSOleBAT *res)
147 {
148         /* NOTE : Only use size as a suggestion, sometimes it is wrong */
149         GArray *bat = g_array_sized_new (FALSE, FALSE,
150                 sizeof (guint32), size_guess);
151
152         guint8 *used = (guint8*)g_alloca (1 + metabat->num_blocks / 8);
153         memset (used, 0, 1 + metabat->num_blocks / 8);
154
155         if (block < metabat->num_blocks)
156                 do {
157                         /* Catch cycles in the bat list */
158                         if (used[block/8] & (1 << (block & 0x7)))
159                                 break;
160                         used[block/8] |= 1 << (block & 0x7);
161
162                         g_array_append_val (bat, block);
163                         block = metabat->block [block];
164                 } while (block < metabat->num_blocks);
165
166         res->block = NULL;
167
168         res->num_blocks = bat->len;
169         res->block = (guint32 *) (gpointer) g_array_free (bat, FALSE);
170
171         if (block != BAT_MAGIC_END_OF_CHAIN) {
172                 g_warning ("This OLE2 file is invalid.\n"
173                            "The Block Allocation Table for one of the streams had %x instead of a terminator (%x).\n"
174                            "We might still be able to extract some data, but you'll want to check the file.",
175                            block, BAT_MAGIC_END_OF_CHAIN);
176         }
177
178         return FALSE;
179 }
180
181 static void
182 ols_bat_release (MSOleBAT *bat)
183 {
184         if (bat->block != NULL) {
185                 bat->num_blocks = 0;
186                 g_free (bat->block);
187                 bat->block = NULL;
188         }
189 }
190
191 /* A small utility routine to read a set of references to bat blocks
192  * either from the OLE header, or a meta-bat block.
193  * Returns: a pointer to the element after the last position filled. */
194 static guint32 *
195 ole_info_read_metabat (GsfInfileMSOle *ole, guint32 *bats, guint32 max_bat,
196                        guint32 const *metabat, guint32 const *metabat_end)
197 {
198         guint8 const *bat, *end;
199
200         for (; metabat < metabat_end; metabat++) {
201                 if (*metabat != BAT_MAGIC_UNUSED) {
202                         bat = ole_get_block (ole, *metabat, NULL);
203                         if (bat == NULL)
204                                 return NULL;
205                         end = bat + ole->info->bb.size;
206                         for ( ; bat < end ; bat += BAT_INDEX_SIZE, bats++) {
207                                 *bats = GSF_LE_GET_GUINT32 (bat);
208                                 if (*bats >= max_bat && *bats < BAT_MAGIC_METABAT) {
209                                         g_warning ("Invalid metabat item %08x",
210                                                    *bats);
211                                         return NULL;
212                                 }
213                         }
214                 } else {
215                         /* Looks like something in the wild sometimes creates
216                          * 'unused' entries in the metabat.  Let's assume that
217                          * corresponds to lots of unused blocks
218                          * http://bugzilla.gnome.org/show_bug.cgi?id=336858 */
219                         unsigned i = ole->info->bb.size / BAT_INDEX_SIZE;
220                         while (i-- > 0)
221                                 *bats++ = BAT_MAGIC_UNUSED;
222                 }
223         }
224         return bats;
225 }
226
227 /* Copy some some raw data into an array of guint32. */
228 static void
229 gsf_ole_get_guint32s (guint32 *dst, guint8 const *src, int num_bytes)
230 {
231         for (; (num_bytes -= BAT_INDEX_SIZE) >= 0 ; src += BAT_INDEX_SIZE)
232                 *dst++ = GSF_LE_GET_GUINT32 (src);
233 }
234
235 static GsfInput *
236 ole_info_get_sb_file (GsfInfileMSOle *parent)
237 {
238         MSOleBAT meta_sbat;
239
240         if (parent->info->sb_file != NULL)
241                 return parent->info->sb_file;
242
243         parent->info->sb_file = gsf_infile_msole_new_child (parent,
244                 parent->info->root_dir, NULL);
245         if (!parent->info->sb_file)
246                 return NULL;
247
248         /* avoid creating a circular reference */
249         ole_info_unref (((GsfInfileMSOle *)parent->info->sb_file)->info);
250
251         g_return_val_if_fail (parent->info->sb.bat.block == NULL, NULL);
252
253         if (ole_make_bat (&parent->info->bb.bat,
254                           parent->info->num_sbat, parent->info->sbat_start, &meta_sbat))
255                 return NULL;
256
257         parent->info->sb.bat.num_blocks = meta_sbat.num_blocks * (parent->info->bb.size / BAT_INDEX_SIZE);
258         parent->info->sb.bat.block      = g_new0 (guint32, parent->info->sb.bat.num_blocks);
259         ole_info_read_metabat (parent, parent->info->sb.bat.block,
260                 parent->info->sb.bat.num_blocks,
261                 meta_sbat.block, meta_sbat.block + meta_sbat.num_blocks);
262         ols_bat_release (&meta_sbat);
263
264         return parent->info->sb_file;
265 }
266
267 static gint
268 ole_dirent_cmp (MSOleDirent const *a, MSOleDirent const *b)
269 {
270         g_return_val_if_fail (a, 0);
271         g_return_val_if_fail (b, 0);
272
273         g_return_val_if_fail (a->collation_name, 0);
274         g_return_val_if_fail (b->collation_name, 0);
275
276         return strcmp (b->collation_name, a->collation_name);
277 }
278
279 /* Parse dirent number @entry and recursively handle its siblings and children.
280  * parent is optional. */
281 static MSOleDirent *
282 ole_dirent_new (GsfInfileMSOle *ole, guint32 entry, MSOleDirent *parent,
283                 guint8 *seen_before)
284 {
285         MSOleDirent *dirent;
286         guint32 block, next, prev, child, size;
287         guint8 const *data;
288         guint8 type;
289         guint16 name_len;
290
291         if (entry >= DIRENT_MAGIC_END)
292                 return NULL;
293
294         g_return_val_if_fail (entry <= G_MAXUINT / DIRENT_SIZE, NULL);
295
296         block = OLE_BIG_BLOCK (entry * DIRENT_SIZE, ole);
297         g_return_val_if_fail (block < ole->bat.num_blocks, NULL);
298
299         g_return_val_if_fail (!seen_before[entry], NULL);
300         seen_before[entry] = TRUE;
301
302         data = ole_get_block (ole, ole->bat.block [block], NULL);
303         if (data == NULL)
304                 return NULL;
305         data += (DIRENT_SIZE * entry) % ole->info->bb.size;
306
307         type = GSF_LE_GET_GUINT8 (data + DIRENT_TYPE);
308         if (type != DIRENT_TYPE_DIR &&
309             type != DIRENT_TYPE_FILE &&
310             type != DIRENT_TYPE_ROOTDIR) {
311                 g_warning ("Unknown stream type 0x%x", type);
312                 return NULL;
313         }
314         if (!parent && type != DIRENT_TYPE_ROOTDIR) {
315                 /* See bug 346118.  */
316                 g_warning ("Root directory is not marked as such.");
317                 type = DIRENT_TYPE_ROOTDIR;
318         }
319
320         /* It looks like directory (and root directory) sizes are sometimes bogus */
321         size = GSF_LE_GET_GUINT32 (data + DIRENT_FILE_SIZE);
322         g_return_val_if_fail (type == DIRENT_TYPE_DIR || type == DIRENT_TYPE_ROOTDIR ||
323                               size <= (guint32)ole->input->size, NULL);
324
325         dirent = g_new0 (MSOleDirent, 1);
326         dirent->index        = entry;
327         dirent->size         = size;
328         /* Store the class id which is 16 byte identifier used by some apps */
329         memcpy(dirent->clsid, data + DIRENT_CLSID, sizeof(dirent->clsid));
330
331         /* root dir is always big block */
332         dirent->use_sb       = parent && (size < ole->info->threshold);
333         dirent->first_block  = (GSF_LE_GET_GUINT32 (data + DIRENT_FIRSTBLOCK));
334         dirent->is_directory = (type != DIRENT_TYPE_FILE);
335         dirent->children     = NULL;
336         prev  = GSF_LE_GET_GUINT32 (data + DIRENT_PREV);
337         next  = GSF_LE_GET_GUINT32 (data + DIRENT_NEXT);
338         child = GSF_LE_GET_GUINT32 (data + DIRENT_CHILD);
339         name_len = GSF_LE_GET_GUINT16 (data + DIRENT_NAME_LEN);
340         dirent->name = NULL;
341         if (0 < name_len && name_len <= DIRENT_MAX_NAME_SIZE) {
342                 gunichar2 uni_name [DIRENT_MAX_NAME_SIZE+1];
343                 gchar const *end;
344                 int i;
345
346                 /* !#%!@$#^
347                  * Sometimes, rarely, people store the stream name as ascii
348                  * rather than utf16.  Do a validation first just in case.
349                  */
350                 if (!g_utf8_validate (data, -1, &end) ||
351                     ((guint8 const *)end - data + 1) != name_len) {
352                         /* be wary about endianness */
353                         for (i = 0 ; i < name_len ; i += 2)
354                                 uni_name [i/2] = GSF_LE_GET_GUINT16 (data + i);
355                         uni_name [i/2] = 0;
356
357                         dirent->name = g_utf16_to_utf8 (uni_name, -1, NULL, NULL, NULL);
358                 } else
359                         dirent->name = g_strndup ((gchar *)data, (gsize)((guint8 const *)end - data + 1));
360         }
361         /* be really anal in the face of screwups */
362         if (dirent->name == NULL)
363                 dirent->name = g_strdup ("");
364         dirent->collation_name = g_utf8_collate_key (dirent->name, -1);
365
366 #if 0
367         printf ("%c '%s' :\tsize = %d\tfirst_block = 0x%x\n",
368                 dirent->is_directory ? 'd' : ' ',
369                 dirent->name, dirent->size, dirent->first_block);
370 #endif
371
372         if (parent != NULL)
373                 parent->children = g_list_insert_sorted (parent->children,
374                         dirent, (GCompareFunc)ole_dirent_cmp);
375
376         /* NOTE : These links are a tree, not a linked list */
377         ole_dirent_new (ole, prev, parent, seen_before); 
378         ole_dirent_new (ole, next, parent, seen_before); 
379
380         if (dirent->is_directory)
381                 ole_dirent_new (ole, child, dirent, seen_before);
382         else if (child != DIRENT_MAGIC_END)
383                 g_warning ("A non directory stream with children ?");
384
385         return dirent;
386 }
387
388 static void
389 ole_dirent_free (MSOleDirent *dirent)
390 {
391         GList *tmp;
392         g_return_if_fail (dirent != NULL);
393
394         g_free (dirent->name);
395         g_free (dirent->collation_name);
396
397         for (tmp = dirent->children; tmp; tmp = tmp->next)
398                 ole_dirent_free ((MSOleDirent *)tmp->data);
399         g_list_free (dirent->children);
400         g_free (dirent);
401 }
402
403 /*****************************************************************************/
404
405 static void
406 ole_info_unref (MSOleInfo *info)
407 {
408         if (info->ref_count-- != 1)
409                 return;
410
411         ols_bat_release (&info->bb.bat);
412         ols_bat_release (&info->sb.bat);
413         if (info->root_dir != NULL) {
414                 ole_dirent_free (info->root_dir);
415                 info->root_dir = NULL;
416         }
417         if (info->sb_file != NULL)  {
418                 g_object_unref (G_OBJECT (info->sb_file));
419                 info->sb_file = NULL;
420         }
421         g_free (info);
422 }
423
424 static MSOleInfo *
425 ole_info_ref (MSOleInfo *info)
426 {
427         info->ref_count++;
428         return info;
429 }
430
431 /* Utility routine to _partially_ replicate a file.  It does NOT copy the bat
432  * blocks, or init the dirent. */
433 static GsfInfileMSOle *
434 ole_dup (GsfInfileMSOle const *src, GError **err)
435 {
436         GsfInfileMSOle  *dst;
437         GsfInput *input;
438
439         g_return_val_if_fail (src != NULL, NULL);
440
441         input = gsf_input_dup (src->input, err);
442         if (input == NULL) {
443                 if (err != NULL)
444                         *err = g_error_new (gsf_input_error_id (), 0,
445                                             "Failed to duplicate input stream");
446                 return NULL;
447         }
448
449         dst = (GsfInfileMSOle *)g_object_new (GSF_INFILE_MSOLE_TYPE, NULL);
450         if (G_UNLIKELY (NULL == dst)) return NULL;
451
452         dst->input = input;
453         dst->info  = ole_info_ref (src->info);
454         /* buf and buf_size are initialized to NULL */
455
456         return dst;
457 }
458
459 /* Read an OLE header and do some sanity checking
460  * along the way.
461  * Returns: %TRUE on error setting @err if it is supplied. */
462 static gboolean
463 ole_init_info (GsfInfileMSOle *ole, GError **err)
464 {
465         static guint8 const signature[] =
466                 { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
467         guint8 *seen_before;
468         guint8 const *header, *tmp;
469         guint32 *metabat = NULL;
470         MSOleInfo *info;
471         guint32 bb_shift, sb_shift, num_bat, num_metabat, last, dirent_start;
472         guint32 metabat_block, *ptr;
473         gboolean fail;
474
475         /* check the header */
476         if (gsf_input_seek (ole->input, 0, G_SEEK_SET) ||
477             NULL == (header = gsf_input_read (ole->input, OLE_HEADER_SIZE, NULL)) ||
478             0 != memcmp (header, signature, sizeof (signature))) {
479                 if (err != NULL)
480                         *err = g_error_new (gsf_input_error_id (), 0,
481                                 "No OLE2 signature");
482                 return TRUE;
483         }
484
485         bb_shift      = GSF_LE_GET_GUINT16 (header + OLE_HEADER_BB_SHIFT);
486         sb_shift      = GSF_LE_GET_GUINT16 (header + OLE_HEADER_SB_SHIFT);
487         num_bat       = GSF_LE_GET_GUINT32 (header + OLE_HEADER_NUM_BAT);
488         dirent_start  = GSF_LE_GET_GUINT32 (header + OLE_HEADER_DIRENT_START);
489         metabat_block = GSF_LE_GET_GUINT32 (header + OLE_HEADER_METABAT_BLOCK);
490         num_metabat   = GSF_LE_GET_GUINT32 (header + OLE_HEADER_NUM_METABAT);
491
492         /* Some sanity checks
493          * 1) There should always be at least 1 BAT block
494          * 2) It makes no sense to have a block larger than 2^31 for now.
495          *    Maybe relax this later, but not much.
496          */
497         if (6 > bb_shift || bb_shift >= 31 || sb_shift > bb_shift ||
498             (gsf_input_size (ole->input) >> bb_shift) < 1) {
499                 if (err != NULL)
500                         *err = g_error_new (gsf_input_error_id (), 0,
501                                 "Unreasonable block sizes");
502                 return TRUE;
503         }
504
505         info = g_new0 (MSOleInfo, 1);
506         ole->info = info;
507
508         info->ref_count      = 1;
509         info->bb.shift       = bb_shift;
510         info->bb.size        = 1 << info->bb.shift;
511         info->bb.filter      = info->bb.size - 1;
512         info->sb.shift       = sb_shift;
513         info->sb.size        = 1 << info->sb.shift;
514         info->sb.filter      = info->sb.size - 1;
515         info->threshold      = GSF_LE_GET_GUINT32 (header + OLE_HEADER_THRESHOLD);
516         info->sbat_start     = GSF_LE_GET_GUINT32 (header + OLE_HEADER_SBAT_START);
517         info->num_sbat       = GSF_LE_GET_GUINT32 (header + OLE_HEADER_NUM_SBAT);
518         info->max_block      = (gsf_input_size (ole->input) - OLE_HEADER_SIZE) / info->bb.size;
519         info->sb_file        = NULL;
520
521         if (info->num_sbat == 0 &&
522             info->sbat_start != BAT_MAGIC_END_OF_CHAIN &&
523             info->sbat_start != BAT_MAGIC_UNUSED) {
524                 g_warning ("There are not supposed to be any blocks in the small block allocation table, yet there is a link to some.  Ignoring it.");
525         }
526
527         /* very rough heuristic, just in case */
528         if (num_bat < info->max_block && info->num_sbat < info->max_block) {
529                 info->bb.bat.num_blocks = num_bat * (info->bb.size / BAT_INDEX_SIZE);
530                 info->bb.bat.block      = g_new0 (guint32, info->bb.bat.num_blocks);
531
532                 metabat = g_try_new (guint32, MAX (info->bb.size, OLE_HEADER_SIZE));
533                 if (!metabat) {
534                         g_free (info);
535                         if (err != NULL)
536                                 *err = g_error_new (gsf_input_error_id (), 0,
537                                                     "Insufficient memory");
538                         return TRUE;
539                 }
540
541                 /* Reading the elements invalidates this memory, make copy */
542                 gsf_ole_get_guint32s (metabat, header + OLE_HEADER_START_BAT,
543                         OLE_HEADER_SIZE - OLE_HEADER_START_BAT);
544                 last = num_bat;
545                 if (last > OLE_HEADER_METABAT_SIZE)
546                         last = OLE_HEADER_METABAT_SIZE;
547
548                 ptr = ole_info_read_metabat (ole, info->bb.bat.block,
549                         info->bb.bat.num_blocks, metabat, metabat + last);
550                 num_bat -= last;
551         } else
552                 ptr = NULL;
553
554         last = (info->bb.size - BAT_INDEX_SIZE) / BAT_INDEX_SIZE;
555         while (ptr != NULL && num_metabat-- > 0) {
556                 tmp = ole_get_block (ole, metabat_block, NULL);
557                 if (tmp == NULL) {
558                         ptr = NULL;
559                         break;
560                 }
561
562                 /* Reading the elements invalidates this memory, make copy */
563                 gsf_ole_get_guint32s (metabat, tmp, (int)info->bb.size);
564
565                 if (num_metabat == 0) {
566                         if (last < num_bat) {
567                                 /* there should be less that a full metabat block
568                                  * remaining */
569                                 ptr = NULL;
570                                 break;
571                         }
572                         last = num_bat;
573                 } else if (num_metabat > 0) {
574                         metabat_block = metabat[last];
575                         if (num_bat < last) {
576                                 /* ::num_bat and ::num_metabat are
577                                  * inconsistent.  There are too many metabats
578                                  * for the bat count in the header. */
579                                 ptr = NULL;
580                                 break;
581                         }
582                         num_bat -= last;
583                 }
584
585                 ptr = ole_info_read_metabat (ole, ptr,
586                         info->bb.bat.num_blocks, metabat, metabat + last);
587         }
588         fail = (ptr == NULL);
589
590         g_free (metabat);
591         metabat = ptr = NULL;
592
593         if (fail) {
594                 if (err != NULL)
595                         *err = g_error_new (gsf_input_error_id (), 0,
596                                 "Inconsistent block allocation table");
597                 return TRUE;
598         }
599
600         /* Read the directory's bat, we do not know the size */
601         if (ole_make_bat (&info->bb.bat, 0, dirent_start, &ole->bat)) {
602                 if (err != NULL)
603                         *err = g_error_new (gsf_input_error_id (), 0,
604                                 "Problems making block allocation table");
605                 return TRUE;
606         }
607
608         /* Read the directory */
609         seen_before = g_malloc0 ((ole->bat.num_blocks << info->bb.shift) * DIRENT_SIZE + 1);
610         ole->dirent = info->root_dir =
611                 ole_dirent_new (ole, 0, NULL, seen_before);
612         g_free (seen_before);
613         if (ole->dirent == NULL) {
614                 if (err != NULL)
615                         *err = g_error_new (gsf_input_error_id (), 0,
616                                 "Problems reading directory");
617                 return TRUE;
618         }
619
620         return FALSE;
621 }
622
623 static void
624 gsf_infile_msole_finalize (GObject *obj)
625 {
626         GsfInfileMSOle *ole = GSF_INFILE_MSOLE (obj);
627
628         if (ole->input != NULL) {
629                 g_object_unref (G_OBJECT (ole->input));
630                 ole->input = NULL;
631         }
632         if (ole->info != NULL &&
633             ole->info->sb_file != (GsfInput *)ole) {
634                 ole_info_unref (ole->info);
635                 ole->info = NULL;
636         }
637         ols_bat_release (&ole->bat);
638
639         g_free (ole->stream.buf);
640
641         parent_class->finalize (obj);
642 }
643
644 static GsfInput *
645 gsf_infile_msole_dup (GsfInput *src_input, GError **err)
646 {
647         GsfInfileMSOle const *src = GSF_INFILE_MSOLE (src_input);
648         GsfInfileMSOle *dst = ole_dup (src, err);
649
650         if (dst == NULL)
651                 return NULL;
652
653         if (src->bat.block != NULL) {
654                 dst->bat.block = g_new (guint32, src->bat.num_blocks),
655                 memcpy (dst->bat.block, src->bat.block,
656                         sizeof (guint32) * src->bat.num_blocks);
657         }
658         dst->bat.num_blocks = src->bat.num_blocks;
659         dst->dirent = src->dirent;
660
661         return GSF_INPUT (dst);
662 }
663
664 static guint8 const *
665 gsf_infile_msole_read (GsfInput *input, size_t num_bytes, guint8 *buffer)
666 {
667         GsfInfileMSOle *ole = GSF_INFILE_MSOLE (input);
668         gsf_off_t first_block, last_block, raw_block, offset, i;
669         guint8 const *data;
670         guint8 *ptr;
671         size_t count;
672
673         /* small block files are preload */
674         if (ole->dirent != NULL && ole->dirent->use_sb) {
675                 if (buffer != NULL) {
676                         memcpy (buffer, ole->stream.buf + input->cur_offset, num_bytes);
677                         return buffer;
678                 }
679                 return ole->stream.buf + input->cur_offset;
680         }
681
682         /* GsfInput guarantees that num_bytes > 0 */
683         first_block = OLE_BIG_BLOCK (input->cur_offset, ole);
684         last_block = OLE_BIG_BLOCK (input->cur_offset + num_bytes - 1, ole);
685         offset = input->cur_offset & ole->info->bb.filter;
686
687         /* optimization : are all the raw blocks contiguous */
688         i = first_block;
689         raw_block = ole->bat.block [i];
690         while (++i <= last_block && ++raw_block == ole->bat.block [i])
691                 ;
692         if (i > last_block) {
693                 /* optimization don't seek if we don't need to */
694                 if (ole->cur_block != first_block) {
695                         if (gsf_input_seek (ole->input,
696                                 (gsf_off_t)(MAX (OLE_HEADER_SIZE, ole->info->bb.size) + (ole->bat.block [first_block] << ole->info->bb.shift) + offset),
697                                 G_SEEK_SET) < 0)
698                                 return NULL;
699                 }
700                 ole->cur_block = last_block;
701                 return gsf_input_read (ole->input, num_bytes, buffer);
702         }
703
704         /* damn, we need to copy it block by block */
705         if (buffer == NULL) {
706                 if (ole->stream.buf_size < num_bytes) {
707                         g_free (ole->stream.buf);
708                         ole->stream.buf_size = num_bytes;
709                         ole->stream.buf = g_new (guint8, num_bytes);
710                 }
711                 buffer = ole->stream.buf;
712         }
713
714         ptr = buffer;
715         for (i = first_block ; i <= last_block ; i++ , ptr += count, num_bytes -= count) {
716                 count = ole->info->bb.size - offset;
717                 if (count > num_bytes)
718                         count = num_bytes;
719                 data = ole_get_block (ole, ole->bat.block [i], NULL);
720                 if (data == NULL)
721                         return NULL;
722
723                 /* TODO : this could be optimized to avoid the copy */
724                 memcpy (ptr, data + offset, count);
725                 offset = 0;
726         }
727         ole->cur_block = BAT_MAGIC_UNUSED;
728
729         return buffer;
730 }
731
732 static gboolean
733 gsf_infile_msole_seek (GsfInput *input, gsf_off_t offset, GSeekType whence)
734 {
735         GsfInfileMSOle *ole = GSF_INFILE_MSOLE (input);
736
737         (void) offset; 
738         (void) whence;
739
740         ole->cur_block = BAT_MAGIC_UNUSED;
741         return FALSE;
742 }
743
744 static GsfInput *
745 gsf_infile_msole_new_child (GsfInfileMSOle *parent,
746                             MSOleDirent *dirent, GError **err)
747 {
748         GsfInfileMSOle *child;
749         MSOleInfo *info;
750         MSOleBAT const *metabat;
751         GsfInput *sb_file = NULL;
752         size_t size_guess;
753
754         child = ole_dup (parent, err);
755         if (!child)
756                 return NULL;
757
758         child->dirent = dirent;
759         gsf_input_set_size (GSF_INPUT (child), (gsf_off_t) dirent->size);
760
761         /* The root dirent defines the small block file */
762         if (dirent->index != 0) {
763                 gsf_input_set_name (GSF_INPUT (child), dirent->name);
764                 gsf_input_set_container (GSF_INPUT (child), GSF_INFILE (parent));
765
766                 if (dirent->is_directory) {
767                         /* be wary.  It seems as if some implementations pretend that the
768                          * directories contain data */
769                         gsf_input_set_size (GSF_INPUT (child), 0);
770                         return GSF_INPUT (child);
771                 }
772         }
773
774         info = parent->info;
775
776         /* build the bat */
777         if (dirent->use_sb) {
778                 metabat = &info->sb.bat;
779                 size_guess = dirent->size >> info->sb.shift;
780                 sb_file = ole_info_get_sb_file (parent);
781                 if (!sb_file) {
782                         if (err != NULL)
783                                 *err = g_error_new (gsf_input_error_id (), 0,
784                                                     "Failed to access child");
785                         g_object_unref (G_OBJECT (child));
786                         return NULL;
787                 }
788         } else {
789                 metabat = &info->bb.bat;
790                 size_guess = dirent->size >> info->bb.shift;
791         }
792         if (ole_make_bat (metabat, size_guess + 1, dirent->first_block, &child->bat)) {
793                 g_object_unref (G_OBJECT (child));
794                 return NULL;
795         }
796
797         if (dirent->use_sb) {
798                 unsigned int i;
799                 int remaining;
800                 guint8 const *data;
801
802                 g_return_val_if_fail (sb_file != NULL, NULL);
803
804                 child->stream.buf_size = remaining = dirent->size;
805                 child->stream.buf = g_new (guint8, child->stream.buf_size);
806
807                 for (i = 0 ; remaining > 0 && i < child->bat.num_blocks; i++, remaining -= info->sb.size)
808                         if (gsf_input_seek (GSF_INPUT (sb_file),
809                                 (gsf_off_t)(child->bat.block [i] << info->sb.shift), G_SEEK_SET) < 0 ||
810                             (data = gsf_input_read (GSF_INPUT (sb_file),
811                                                     MIN (remaining, (int)info->sb.size),
812                                                     child->stream.buf + (i << info->sb.shift))) == NULL) {
813
814                                 g_warning ("failure reading block %d for '%s'", i, dirent->name);
815                                 if (err) *err = g_error_new (gsf_input_error_id (), 0, "failure reading block");
816                                 g_object_unref (G_OBJECT (child));
817                                 return NULL;
818                         }
819
820                 if (remaining > 0) {
821                         if (err) *err = g_error_new (gsf_input_error_id (), 0, "insufficient blocks");
822                         g_warning ("Small-block file '%s' has insufficient blocks (%u) for the stated size (%lu)", dirent->name, child->bat.num_blocks, (long)dirent->size);
823                         g_object_unref (G_OBJECT (child));
824                         return NULL;
825                 }
826         }
827
828         return GSF_INPUT (child);
829 }
830
831 static GsfInput *
832 gsf_infile_msole_child_by_index (GsfInfile *infile, int target, GError **err)
833 {
834         GsfInfileMSOle *ole = GSF_INFILE_MSOLE (infile);
835         GList *p;
836
837         for (p = ole->dirent->children; p != NULL ; p = p->next)
838                 if (target-- <= 0)
839                         return gsf_infile_msole_new_child (ole,
840                                 (MSOleDirent *)p->data, err);
841         return NULL;
842 }
843
844 static char const *
845 gsf_infile_msole_name_by_index (GsfInfile *infile, int target)
846 {
847         GsfInfileMSOle *ole = GSF_INFILE_MSOLE (infile);
848         GList *p;
849
850         for (p = ole->dirent->children; p != NULL ; p = p->next)
851                 if (target-- <= 0)
852                         return ((MSOleDirent *)p->data)->name;
853         return NULL;
854 }
855
856 static GsfInput *
857 gsf_infile_msole_child_by_name (GsfInfile *infile, char const *name, GError **err)
858 {
859         GsfInfileMSOle *ole = GSF_INFILE_MSOLE (infile);
860         GList *p;
861
862         for (p = ole->dirent->children; p != NULL ; p = p->next) {
863                 MSOleDirent *dirent = p->data;
864                 if (dirent->name != NULL && !strcmp (name, dirent->name))
865                         return gsf_infile_msole_new_child (ole, dirent, err);
866         }
867         return NULL;
868 }
869
870 static int
871 gsf_infile_msole_num_children (GsfInfile *infile)
872 {
873         GsfInfileMSOle *ole = GSF_INFILE_MSOLE (infile);
874
875         g_return_val_if_fail (ole->dirent != NULL, -1);
876
877         if (!ole->dirent->is_directory)
878                 return -1;
879         return g_list_length (ole->dirent->children);
880 }
881
882 static void
883 gsf_infile_msole_init (GObject *obj)
884 {
885         GsfInfileMSOle *ole = GSF_INFILE_MSOLE (obj);
886
887         ole->input              = NULL;
888         ole->info               = NULL;
889         ole->bat.block          = NULL;
890         ole->bat.num_blocks     = 0;
891         ole->cur_block          = BAT_MAGIC_UNUSED;
892         ole->stream.buf         = NULL;
893         ole->stream.buf_size    = 0; 
894 }
895
896 static void
897 gsf_infile_msole_class_init (GObjectClass *gobject_class)
898 {
899         GsfInputClass  *input_class  = GSF_INPUT_CLASS (gobject_class);
900         GsfInfileClass *infile_class = GSF_INFILE_CLASS (gobject_class);
901
902         gobject_class->finalize         = gsf_infile_msole_finalize;
903         input_class->Dup                = gsf_infile_msole_dup;
904         input_class->Read               = gsf_infile_msole_read;
905         input_class->Seek               = gsf_infile_msole_seek;
906         infile_class->num_children      = gsf_infile_msole_num_children;
907         infile_class->name_by_index     = gsf_infile_msole_name_by_index;
908         infile_class->child_by_index    = gsf_infile_msole_child_by_index;
909         infile_class->child_by_name     = gsf_infile_msole_child_by_name;
910
911         parent_class = g_type_class_peek_parent (gobject_class);
912 }
913
914 GSF_CLASS (GsfInfileMSOle, gsf_infile_msole,
915            gsf_infile_msole_class_init, gsf_infile_msole_init,
916            GSF_INFILE_TYPE)
917
918 /**
919  * gsf_infile_msole_new :
920  * @source: #GsfInput
921  * @err: optional place to store an error
922  *
923  * Opens the root directory of an MS OLE file.
924  * <note>This adds a reference to @source.</note>
925  *
926  * Returns: the new ole file handler
927  **/
928 GsfInfile *
929 gsf_infile_msole_new (GsfInput *source, GError **err)
930 {
931         GsfInfileMSOle *ole;
932         gsf_off_t calling_pos;
933
934         g_return_val_if_fail (GSF_IS_INPUT (source), NULL);
935
936         ole = (GsfInfileMSOle *)g_object_new (GSF_INFILE_MSOLE_TYPE, NULL);
937         if (G_UNLIKELY (NULL == ole)) return NULL;
938
939         ole->input = gsf_input_proxy_new (source);
940         gsf_input_set_size (GSF_INPUT (ole), 0);
941
942         calling_pos = gsf_input_tell (source);
943         if (ole_init_info (ole, err)) {
944                 /* We do this so other kinds of archives can be tried.  */
945                 (void)gsf_input_seek (source, calling_pos, G_SEEK_SET);
946
947                 g_object_unref (G_OBJECT (ole));
948                 return NULL;
949         }
950
951         return GSF_INFILE (ole);
952 }
953
954 /**
955  * gsf_infile_msole_get_class_id :
956  * @ole: a #GsfInfileMSOle
957  * @res: 16 byte identifier (often a GUID in MS Windows apps)
958  *
959  * Retrieves the 16 byte indentifier (often a GUID in MS Windows apps)
960  * stored within the directory associated with @ole and stores it in @res.
961  *
962  * Returns: TRUE on success
963  **/
964 gboolean
965 gsf_infile_msole_get_class_id (GsfInfileMSOle const *ole, guint8 *res)
966 {
967         g_return_val_if_fail (ole != NULL && ole->dirent != NULL, FALSE);
968
969         memcpy (res, ole->dirent->clsid,
970                 sizeof(ole->dirent->clsid));
971         return TRUE;
972 }