Coverage Report

Created: 2026-08-13 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxls/src/ole.c
Line
Count
Source
1
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
 *
3
 * Copyright 2004 Komarov Valery
4
 * Copyright 2006 Christophe Leitienne
5
 * Copyright 2008-2017 David Hoerl
6
 * Copyright 2013 Bob Colbert
7
 * Copyright 2013-2018 Evan Miller
8
 *
9
 * This file is part of libxls -- A multiplatform, C/C++ library for parsing
10
 * Excel(TM) files.
11
 *
12
 * Redistribution and use in source and binary forms, with or without
13
 * modification, are permitted provided that the following conditions are met:
14
 *
15
 *    1. Redistributions of source code must retain the above copyright notice,
16
 *    this list of conditions and the following disclaimer.
17
 *
18
 *    2. Redistributions in binary form must reproduce the above copyright
19
 *    notice, this list of conditions and the following disclaimer in the
20
 *    documentation and/or other materials provided with the distribution.
21
 *
22
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS
23
 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
26
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
 *
34
 */
35
36
#include "config.h" 
37
38
#include <memory.h>
39
#include <string.h>
40
#include <stdio.h>
41
#include <stdlib.h>
42
43
#include "../include/libxls/ole.h"
44
#include "../include/libxls/xlstool.h"
45
#include "../include/libxls/endian.h"
46
47
extern int xls_debug;
48
49
//#define OLE_DEBUG
50
51
//static const DWORD MSATSECT   = 0xFFFFFFFC; // -4
52
//static const DWORD FATSECT    = 0xFFFFFFFD; // -3
53
static const DWORD ENDOFCHAIN = 0xFFFFFFFE; // -2
54
static const DWORD FREESECT   = 0xFFFFFFFF; // -1
55
56
static size_t sector_pos(OLE2* ole2, DWORD sid);
57
static ssize_t sector_read(OLE2* ole2, void *buffer, size_t buffer_len, DWORD sid);
58
static ssize_t read_MSAT(OLE2* ole2, OLE2Header *oleh);
59
static void *ole_malloc(size_t len);
60
61
12.1k
static void *ole_malloc(size_t len) {
62
12.1k
    if (len > (1<<24) || len == 0) {
63
50
        return NULL;
64
50
    }
65
12.1k
    return calloc(1, len);
66
12.1k
}
67
68
/* Reallocates memory and zero-fills only the newly grown region (from old_len to new_len). Frees ptr on failure. */
69
1.28k
static void *ole_realloc_zero(void *ptr, size_t old_len, size_t new_len) {
70
1.28k
    if (new_len > (1<<24) || new_len == 0) {
71
11
        free(ptr);
72
11
        return NULL;
73
11
    }
74
1.27k
    void *new_ptr = realloc(ptr, new_len);
75
1.27k
    if (!new_ptr) {
76
0
        free(ptr);
77
0
        return NULL;
78
0
    }
79
1.27k
    if (new_len > old_len) {
80
1.22k
        memset((char *)new_ptr + old_len, 0, new_len - old_len);
81
1.22k
    }
82
1.27k
    return new_ptr;
83
1.27k
}
84
85
4.32k
static int ole2_validate_sector_chain(DWORD *chain, DWORD chain_count, DWORD chain_start) {
86
4.32k
    if (chain == NULL || chain_count == 0)
87
13
        return 0;
88
4.30k
    DWORD count = 0;
89
4.30k
    DWORD sector = chain_start;
90
8.31M
    while (sector != ENDOFCHAIN) {
91
8.31M
        if (sector >= chain_count)
92
243
            return 0;
93
        
94
8.31M
        if (++count >= chain_count)
95
30
            return 0;
96
97
8.31M
        sector = xlsIntVal(chain[sector]);
98
8.31M
    }
99
4.03k
    return 1;
100
4.30k
}
101
102
4.25M
static int ole2_validate_sector(DWORD sector, OLE2 *ole) {
103
4.25M
    if (sector >= ole->SecIDCount) {
104
2.92k
        if (xls_debug) fprintf(stderr, "Error: fatpos %d out-of-bounds for SecID[%d]\n",
105
0
                (int)sector, ole->SecIDCount);
106
2.92k
        return 0;
107
2.92k
    }
108
109
4.24M
    if (sector == xlsIntVal(ole->SecID[sector])) {
110
4
        if (xls_debug) fprintf(stderr, "Error: Sector loop detected, SecID[%d] = %d\n",
111
0
                (int)sector, (int)sector);
112
4
        return 0;
113
4
    }
114
115
4.24M
    return 1;
116
4.24M
}
117
118
// Read next sector of stream
119
static int ole2_bufread(OLE2Stream* olest)
120
887k
{
121
887k
  BYTE *ptr;
122
123
#ifdef OLE_DEBUG
124
    fprintf(stderr, "----------------------------------------------\n");
125
    fprintf(stderr, "ole2_bufread (start)\n");
126
#endif
127
128
887k
    if (olest == NULL || olest->ole == NULL)
129
0
        return -1;
130
131
887k
    if ((DWORD)olest->fatpos!=ENDOFCHAIN)
132
875k
    {
133
875k
    if(olest->sfat) {
134
250k
            if (olest->ole->SSAT == NULL || olest->buf == NULL || olest->ole->SSecID == NULL)
135
19
                return -1;
136
137
250k
            if (olest->fatpos*olest->ole->lssector + olest->bufsize > olest->ole->SSATCount) {
138
138
                if (xls_debug) fprintf(stderr, "Error: fatpos %d out-of-bounds for SSAT\n", (int)olest->fatpos);
139
138
                return -1;
140
138
            }
141
142
250k
      ptr = olest->ole->SSAT + olest->fatpos*olest->ole->lssector;
143
250k
      memcpy(olest->buf, ptr, olest->bufsize); 
144
145
250k
            if (olest->fatpos >= olest->ole->SSecIDCount) {
146
0
                if (xls_debug) fprintf(stderr, "Error: fatpos %d out-of-bounds for SSecID[%d]\n",
147
0
                        (int)olest->fatpos, olest->ole->SSecIDCount);
148
0
                return -1;
149
0
            }
150
151
250k
      olest->fatpos=xlsIntVal(olest->ole->SSecID[olest->fatpos]);
152
250k
      olest->pos=0;
153
250k
      olest->cfat++;
154
625k
    } else {
155
625k
      if ((int)olest->fatpos < 0 ||
156
625k
                sector_read(olest->ole, olest->buf, olest->bufsize, olest->fatpos) == -1) {
157
9.03k
                if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", (int)olest->fatpos);
158
9.03k
                return -1;
159
9.03k
            }
160
161
616k
            if (!ole2_validate_sector(olest->fatpos, olest->ole)) {
162
0
                return -1;
163
0
            }
164
165
616k
            olest->fatpos = xlsIntVal(olest->ole->SecID[olest->fatpos]);
166
616k
      olest->pos=0;
167
616k
      olest->cfat++;
168
616k
    }
169
875k
    }
170
#ifdef OLE_DEBUG
171
    fprintf(stderr, "----------------------------------------------\n");
172
    fprintf(stderr, "ole2_bufread (end)\n");
173
#endif
174
  // else printf("ENDOFCHAIN!!!\n");
175
878k
    return 0;
176
887k
}
177
178
// Read part of stream
179
ssize_t ole2_read(void* buf, size_t size, size_t count, OLE2Stream* olest)
180
6.60M
{
181
6.60M
    size_t didReadCount=0;
182
6.60M
    size_t totalReadCount;
183
184
6.60M
  totalReadCount=size*count;
185
6.60M
    if (buf && totalReadCount > 0) {
186
6.60M
        memset(buf, 0, totalReadCount);
187
6.60M
    }
188
189
  // olest->size inited to -1
190
  // printf("===== ole2_read(%ld bytes)\n", totalReadCount);
191
192
6.60M
    if ((long)olest->size>=0 && !olest->sfat)  // directory is -1
193
4.35M
    {
194
4.35M
    size_t rem;
195
4.35M
    rem = olest->size - (olest->cfat*olest->ole->lsector+olest->pos);   
196
4.35M
        totalReadCount = rem<totalReadCount?rem:totalReadCount;
197
4.35M
        if (rem<=0) olest->eof=1;
198
199
    // printf("  rem=%ld olest->size=%d - subfunc=%d\n", rem, olest->size, (olest->cfat*olest->ole->lsector+olest->pos) );
200
    //printf("  totalReadCount=%d (rem=%d size*count=%ld)\n", totalReadCount, rem, size*count);
201
4.35M
  }
202
203
13.9M
  while ((!olest->eof) && (didReadCount < totalReadCount))
204
7.37M
  {
205
7.37M
    unsigned long remainingBytes;
206
7.37M
        size_t needToReadCount;
207
208
7.37M
    needToReadCount = totalReadCount - didReadCount;
209
7.37M
    remainingBytes  = olest->bufsize - olest->pos;
210
211
7.37M
    if (needToReadCount < remainingBytes) { // does the current sector contain all the data I need?
212
6.52M
      memcpy((BYTE*)buf + didReadCount, olest->buf + olest->pos, needToReadCount);
213
6.52M
      olest->pos    += needToReadCount;
214
6.52M
      didReadCount  += needToReadCount;
215
6.52M
    } else {
216
848k
      memcpy((BYTE*)buf + didReadCount, olest->buf + olest->pos, remainingBytes);
217
848k
      olest->pos    += remainingBytes;
218
848k
      didReadCount  += remainingBytes;
219
848k
      if (ole2_bufread(olest) == -1)
220
8.51k
                return -1;
221
848k
    }
222
7.36M
    if (((DWORD)olest->fatpos == ENDOFCHAIN) && (olest->pos >= olest->bufsize)) {
223
11.6k
      olest->eof=1;
224
11.6k
    }
225
7.36M
  }
226
6.59M
    if (didReadCount > totalReadCount)
227
0
        return -1;
228
229
  // printf("  didReadCount=%ld EOF=%d\n", didReadCount, olest->eof);
230
  // printf("=====\n");
231
232
#ifdef OLE_DEBUG
233
    fprintf(stderr, "----------------------------------------------\n");
234
    fprintf(stderr, "ole2_read (end)\n");
235
    fprintf(stderr, "start:   %d \n",olest->start);
236
    fprintf(stderr, "pos:   %d \n",(int)olest->pos);
237
    fprintf(stderr, "cfat:    %d \n",(int)olest->cfat);
238
    fprintf(stderr, "size:    %d \n",(int)olest->size);
239
    fprintf(stderr, "fatpos:    %d \n",(int)olest->fatpos);
240
    fprintf(stderr, "bufsize:   %d \n",(int)olest->bufsize);
241
    fprintf(stderr, "eof:   %d \n",olest->eof);
242
#endif
243
244
6.59M
    return didReadCount;
245
6.59M
}
246
247
// Open stream in logical ole file
248
OLE2Stream* ole2_sopen(OLE2* ole,DWORD start, size_t size)
249
4.32k
{
250
4.32k
    OLE2Stream* olest=NULL;
251
4.32k
    int success = 1;
252
253
#ifdef OLE_DEBUG
254
    fprintf(stderr, "----------------------------------------------\n");
255
    fprintf(stderr, "ole2_sopen start=%Xh\n", start);
256
#endif
257
258
4.32k
    olest = calloc(1, sizeof(OLE2Stream));
259
4.32k
    olest->ole=ole;
260
4.32k
    olest->size=size;
261
4.32k
    olest->fatpos=start;
262
4.32k
    olest->start=start;
263
4.32k
    olest->cfat=-1;
264
4.32k
    if((long)size > 0 && size < (size_t)ole->sectorcutoff) {
265
925
        olest->bufsize=ole->lssector;
266
925
        olest->sfat = 1;
267
3.39k
    } else {
268
3.39k
        olest->bufsize=ole->lsector;
269
3.39k
    }
270
4.32k
    if ((olest->buf = ole_malloc(olest->bufsize)) == NULL) {
271
0
        success = 0;
272
0
        goto cleanup;
273
0
    }
274
275
4.32k
    if (olest->sfat) {
276
925
        if (!ole2_validate_sector_chain(ole->SSecID, ole->SSecIDCount, start)) {
277
121
            success = 0;
278
121
            goto cleanup;
279
121
        }
280
3.39k
    } else {
281
3.39k
        if (!ole2_validate_sector_chain(ole->SecID, ole->SecIDCount, start)) {
282
165
            success = 0;
283
165
            goto cleanup;
284
165
        }
285
3.39k
    }
286
287
4.03k
    if (ole2_bufread(olest) == -1) {
288
33
        success = 0;
289
33
        goto cleanup;
290
33
    }
291
292
4.32k
cleanup:
293
4.32k
    if (!success) {
294
319
        ole2_fclose(olest);
295
319
        olest = NULL;
296
319
    }
297
298
  // if(xls_debug) printf("sopen: sector=%d next=%d\n", start, olest->fatpos);
299
4.32k
    return olest;
300
4.03k
}
301
302
// Move in stream
303
int ole2_seek(OLE2Stream* olest,DWORD ofs)
304
38.7k
{
305
#ifdef OLE_DEBUG
306
    fprintf(stderr, "SEEK %x\n", ofs);
307
#endif
308
38.7k
  if(olest->sfat) {
309
10.5k
    ldiv_t div_rez=ldiv(ofs,olest->ole->lssector);
310
10.5k
    int i;
311
10.5k
    olest->fatpos=olest->start;
312
313
10.5k
        if (div_rez.quot!=0)
314
6.49k
        {
315
79.6k
            for (i=0;i<div_rez.quot;i++) {
316
74.2k
                if (olest->fatpos >= olest->ole->SSecIDCount)
317
1.11k
                    return -1;
318
73.1k
                olest->fatpos=xlsIntVal(olest->ole->SSecID[olest->fatpos]);
319
73.1k
            }
320
6.49k
        }
321
322
9.39k
        if (ole2_bufread(olest) == -1)
323
53
            return -1;
324
325
9.34k
        olest->pos=div_rez.rem;
326
9.34k
        olest->eof=0;
327
9.34k
        olest->cfat=div_rez.quot;
328
    //printf("%i=%i %i\n",ofs,div_rez.quot,div_rez.rem);
329
28.2k
  } else {
330
28.2k
    ldiv_t div_rez=ldiv(ofs,olest->ole->lsector);
331
28.2k
    int i;
332
#ifdef OLE_DEBUG
333
        fprintf(stderr, "seeking fatpos%lu start %u\n", olest->fatpos, olest->start);
334
#endif
335
28.2k
    olest->fatpos=olest->start;
336
337
28.2k
        if (div_rez.quot!=0)
338
4.98k
        {
339
152k
            for (i=0;i<div_rez.quot;i++) {
340
150k
                if (!ole2_validate_sector(olest->fatpos, olest->ole))
341
2.85k
                    return -1;
342
147k
                olest->fatpos=xlsIntVal(olest->ole->SecID[olest->fatpos]);
343
147k
            }
344
4.98k
        }
345
346
25.4k
        if (ole2_bufread(olest) == -1)
347
584
            return -1;
348
349
24.8k
    olest->pos=div_rez.rem;
350
24.8k
    olest->eof=0;
351
24.8k
    olest->cfat=div_rez.quot;
352
    //printf("%i=%i %i\n",ofs,div_rez.quot,div_rez.rem);
353
24.8k
  }
354
34.1k
    return 0;
355
38.7k
}
356
357
// Open logical file contained in physical OLE file
358
OLE2Stream*  ole2_fopen(OLE2* ole, const char *file)
359
6.45k
{
360
6.45k
    int i;
361
362
#ifdef OLE_DEBUG
363
    fprintf(stderr, "----------------------------------------------\n");
364
    fprintf(stderr, "ole2_fopen %s\n", file);
365
#endif
366
367
17.6k
    for (i=0;i<ole->files.count;i++) {
368
13.1k
    char *str = ole->files.file[i].name;
369
#ifdef OLE_DEBUG
370
    fprintf(stderr, "----------------------------------------------\n");
371
    fprintf(stderr, "ole2_fopen found %s\n", str);
372
#endif
373
13.1k
        if (str && strcmp(str,file)==0)  // newer versions of Excel don't write the "Root Entry" string for the first set of data
374
1.97k
        {
375
1.97k
            return ole2_sopen(ole,ole->files.file[i].start,ole->files.file[i].size);
376
1.97k
        }
377
13.1k
  }
378
4.48k
    return NULL;
379
6.45k
}
380
381
4.33M
static int ole2_fseek(OLE2 *ole2, size_t pos) {
382
4.33M
    if (ole2->file)
383
0
        return fseek(ole2->file, pos, SEEK_SET);
384
385
4.33M
    if (pos > ole2->buffer_len)
386
9.07k
        return -1;
387
388
4.32M
    ole2->buffer_pos = pos;
389
4.32M
    return 0;
390
4.33M
}
391
392
// Will read up to `size' bytes from the input, and pad the rest of `size' with
393
// zeros if the input file or buffer is short.
394
4.32M
static size_t ole2_fread(OLE2 *ole2, void *buffer, size_t buffer_len, size_t size) {
395
4.32M
    if (size > buffer_len)
396
6
        return 0;
397
398
4.32M
    memset(buffer, 0, size);
399
400
4.32M
    if (ole2->file)
401
0
        return fread(buffer, 1, size, ole2->file) > 0;
402
403
4.32M
    if (ole2->buffer_pos >= ole2->buffer_len)
404
293
        return 0;
405
406
4.32M
    if (ole2->buffer_pos + size > ole2->buffer_len)
407
1.75M
        size = ole2->buffer_len - ole2->buffer_pos;
408
409
4.32M
    memcpy(buffer, (const char *)ole2->buffer + ole2->buffer_pos, size);
410
4.32M
    ole2->buffer_pos += size;
411
412
4.32M
    return 1;
413
4.32M
}
414
415
// read header and check magic numbers
416
2.95k
static ssize_t ole2_read_header(OLE2 *ole) {
417
2.95k
    ssize_t bytes_read = 0, total_bytes_read = 0;
418
2.95k
    OLE2Header *oleh = calloc(1, sizeof(OLE2Header));
419
2.95k
    if (ole2_fread(ole, oleh, sizeof(OLE2Header), sizeof(OLE2Header)) != 1) {
420
0
        total_bytes_read = -1;
421
0
        goto cleanup;
422
0
    }
423
2.95k
    total_bytes_read += sizeof(OLE2Header);
424
2.95k
    xlsConvertHeader(oleh);
425
426
  // make sure the file looks good. Note: this code only works on Little Endian machines
427
2.95k
  if(oleh->id[0] != 0xE011CFD0 || oleh->id[1] != 0xE11AB1A1 || oleh->byteorder != 0xFFFE) {
428
127
        if (xls_debug) fprintf(stderr, "Not an excel file\n");
429
127
        total_bytes_read = -1;
430
127
        goto cleanup;
431
127
  }
432
433
    //ole->lsector=(WORD)pow(2,oleh->lsector);
434
    //ole->lssector=(WORD)pow(2,oleh->lssector);
435
2.83k
  ole->lsector=512;
436
2.83k
    ole->lssector=64;
437
438
2.83k
  if (oleh->lsectorB != 9 || oleh->lssectorB != 6) { // 2**9 == 512, 2**6 == 64
439
25
        if (xls_debug) fprintf(stderr, "Unexpected sector size\n");
440
25
        total_bytes_read = -1;
441
25
        goto cleanup;
442
25
    }
443
  
444
2.80k
    ole->cfat=oleh->cfat;
445
2.80k
    ole->dirstart=oleh->dirstart;
446
2.80k
    ole->sectorcutoff=oleh->sectorcutoff;
447
2.80k
    ole->sfatstart=oleh->sfatstart;
448
2.80k
    ole->csfat=oleh->csfat;
449
2.80k
    ole->difstart=oleh->difstart;
450
2.80k
    ole->cdif=oleh->cdif;
451
2.80k
    ole->files.count=0;
452
453
#ifdef OLE_DEBUG
454
    fprintf(stderr, "==== OLE HEADER ====\n");
455
    //printf ("Header Size:   %i \n", sizeof(OLE2Header));
456
    //printf ("id[0]-id[1]:   %X-%X \n", oleh->id[0], oleh->id[1]);
457
    fprintf(stderr, "verminor:      %X \n",oleh->verminor);
458
    fprintf(stderr, "verdll:        %X \n",oleh->verdll);
459
    //printf ("Byte order:    %X \n",oleh->byteorder);
460
    fprintf(stderr, "sect len:      %X (%i)\n",ole->lsector,ole->lsector);    // ole
461
    fprintf(stderr, "mini len:      %X (%i)\n",ole->lssector,ole->lssector);  // ole
462
    fprintf(stderr, "Fat sect.:     %i \n",oleh->cfat);
463
    fprintf(stderr, "Dir Start:     %i \n",oleh->dirstart);
464
    
465
    fprintf(stderr, "Mini Cutoff:   %i \n",oleh->sectorcutoff);
466
    fprintf(stderr, "MiniFat Start: %X \n",oleh->sfatstart);
467
    fprintf(stderr, "Count MFat:    %i \n",oleh->csfat);
468
    fprintf(stderr, "Dif start:     %X \n",oleh->difstart);
469
    fprintf(stderr, "Count Dif:     %i \n",oleh->cdif);
470
    fprintf(stderr, "Fat Size:      %u (0x%X) \n",oleh->cfat*ole->lsector,oleh->cfat*ole->lsector);
471
#endif
472
    // read directory entries
473
2.80k
    if ((bytes_read = read_MSAT(ole, oleh)) == -1) {
474
459
        total_bytes_read = -1;
475
459
        goto cleanup;
476
459
    }
477
2.34k
    total_bytes_read += bytes_read;
478
479
2.95k
cleanup:
480
2.95k
    free(oleh);
481
482
2.95k
    return total_bytes_read;
483
2.34k
}
484
485
2.34k
static ssize_t ole2_read_body(OLE2 *ole) {
486
  // reuse this buffer
487
2.34k
    PSS *pss = NULL;
488
2.34k
    OLE2Stream *olest = NULL;
489
2.34k
    char* name = NULL;
490
2.34k
    ssize_t bytes_read = 0, total_bytes_read = 0;
491
492
2.34k
    if ((olest = ole2_sopen(ole,ole->dirstart, -1)) == NULL) {
493
114
        total_bytes_read = -1;
494
114
        goto cleanup;
495
114
    }
496
2.23k
    pss = calloc(1, sizeof(PSS));
497
10.8k
    do {
498
10.8k
        if ((bytes_read = ole2_read(pss,1,sizeof(PSS),olest)) == -1) {
499
18
            total_bytes_read = -1;
500
18
            goto cleanup;
501
18
        }
502
10.8k
        total_bytes_read += bytes_read;
503
10.8k
        xlsConvertPss(pss);
504
10.8k
        if (pss->bsize > sizeof(pss->name)) {
505
24
            total_bytes_read = -1;
506
24
            goto cleanup;
507
24
        }
508
10.8k
        name=transcode_utf16_to_utf8(pss->name, pss->bsize);
509
#ifdef OLE_DEBUG  
510
    fprintf(stderr, "OLE NAME: %s count=%d\n", name, (int)ole->files.count);
511
#endif
512
10.8k
        if (pss->type == PS_USER_ROOT || pss->type == PS_USER_STREAM) // (name!=NULL) // 
513
4.65k
        {
514
515
#ifdef OLE_DEBUG    
516
      fprintf(stderr, "OLE TYPE: %s file=%d size=%d\n",
517
                    pss->type == PS_USER_ROOT ? "root" : "user",
518
                    (int)ole->files.count, (int)pss->size);
519
#endif    
520
4.65k
            ole->files.file = realloc(ole->files.file,(ole->files.count+1)*sizeof(struct st_olefiles_data));
521
4.65k
            ole->files.file[ole->files.count].name=name;
522
4.65k
            ole->files.file[ole->files.count].start=pss->sstart;
523
4.65k
            ole->files.file[ole->files.count].size=pss->size;
524
4.65k
            ole->files.count++;
525
      
526
#ifdef OLE_DEBUG
527
            fprintf(stderr, "----------------------------------------------\n");
528
            fprintf(stderr, "name: %s (size=%d [c=%c])\n", name, pss->bsize, name ? name[0]:' ');
529
            fprintf(stderr, "bsize %i\n",pss->bsize);
530
            fprintf(stderr, "type %i\n",pss->type);
531
            fprintf(stderr, "flag %i\n",pss->flag);
532
            fprintf(stderr, "left %X\n",pss->left);
533
            fprintf(stderr, "right %X\n",pss->right);
534
            fprintf(stderr, "child %X\n",pss->child);
535
            fprintf(stderr, "guid %.4X-%.4X-%.4X-%.4X %.4X-%.4X-%.4X-%.4X\n",
536
                    pss->guid[0],pss->guid[1],pss->guid[2],pss->guid[3],
537
                    pss->guid[4],pss->guid[5],pss->guid[6],pss->guid[7]);
538
            fprintf(stderr, "user flag %.4X\n",pss->userflags);
539
            fprintf(stderr, "sstart %.4d\n",pss->sstart);
540
            fprintf(stderr, "size %.4d\n",pss->size);
541
#endif
542
4.65k
      if(pss->sstart == ENDOFCHAIN) {
543
56
        if (xls_debug) verbose("END OF CHAIN\n");
544
4.59k
      } else if(pss->type == PS_USER_STREAM) {
545
3.29k
      } else if(pss->type == PS_USER_ROOT) {
546
1.30k
        DWORD sector, k, blocks;
547
1.30k
        BYTE *wptr;
548
1.30k
                size_t bytes_left;
549
        
550
1.30k
        blocks = (pss->size + (ole->lsector - 1)) / ole->lsector; // count partial
551
1.30k
        if (ole->lsector == 0 || blocks > (1u << 24) / ole->lsector) {
552
21
          total_bytes_read = -1;
553
21
          goto cleanup;
554
21
        }
555
#ifdef OLE_DEBUG
556
                fprintf(stderr, "OLE BLOCKS: %d = (%d + (%d - 1))/%d\n",
557
                        (int)blocks, (int)pss->size, (int)ole->lsector, (int)ole->lsector);
558
#endif
559
1.28k
        size_t old_ssat_bytes = ole->SSATCount;
560
1.28k
        if ((ole->SSAT = ole_realloc_zero(ole->SSAT, old_ssat_bytes, blocks*ole->lsector)) == NULL) {
561
11
                    total_bytes_read = -1;
562
11
                    goto cleanup;
563
11
                }
564
1.27k
                ole->SSATCount = blocks*ole->lsector;
565
        // printf("blocks %d\n", blocks);
566
567
1.27k
        sector = pss->sstart;
568
1.27k
        wptr = (BYTE*)ole->SSAT;
569
1.27k
                bytes_left = blocks*ole->lsector;
570
1.43M
        for(k=0; k<blocks; ++k) {
571
          // printf("block %d sector %d\n", k, sector);
572
1.43M
                    if (sector == ENDOFCHAIN || sector_read(ole, wptr, bytes_left, sector) == -1) {
573
81
                        if (xls_debug) fprintf(stderr, "Unable to read sector #%d\n", sector);
574
81
                        total_bytes_read = -1;
575
81
                        goto cleanup;
576
81
                    }
577
1.43M
                    if (!ole2_validate_sector(sector, ole)) {
578
33
                        total_bytes_read = -1;
579
33
                        goto cleanup;
580
33
                    }
581
1.43M
                    total_bytes_read += ole->lsector;
582
1.43M
          wptr += ole->lsector;
583
1.43M
                    bytes_left -= ole->lsector;
584
1.43M
          sector = xlsIntVal(ole->SecID[sector]);
585
1.43M
        }
586
1.27k
      }  
587
6.17k
    } else {
588
6.17k
      free(name);
589
6.17k
    }
590
10.8k
    } while (!olest->eof);
591
592
2.34k
cleanup:
593
2.34k
    if (olest)
594
2.23k
        ole2_fclose(olest);
595
2.34k
    if (pss)
596
2.23k
        free(pss);
597
598
#ifdef OLE_DEBUG
599
    fprintf(stderr, "----------------------------------------------\n");
600
    fprintf(stderr, "ole2_read_body: %d bytes\n", (int)total_bytes_read);
601
#endif
602
603
2.34k
    return total_bytes_read;
604
2.23k
}
605
606
2.95k
OLE2 *ole2_read_header_and_body(OLE2 *ole) {
607
2.95k
    if (ole2_read_header(ole) == -1) {
608
611
        ole2_close(ole);
609
611
        return NULL;
610
611
    }
611
612
2.34k
    if (ole2_read_body(ole) == -1) {
613
302
        ole2_close(ole);
614
302
        return NULL;
615
302
    }
616
617
2.04k
    return ole;
618
2.34k
}
619
620
// Open in-memory buffer
621
2.95k
OLE2 *ole2_open_buffer(const void *buffer, size_t len) {
622
2.95k
    OLE2 *ole = calloc(1, sizeof(OLE2));
623
624
2.95k
    ole->buffer = buffer;
625
2.95k
    ole->buffer_len = len;
626
627
2.95k
    return ole2_read_header_and_body(ole);
628
2.95k
}
629
630
// Open physical file
631
OLE2* ole2_open_file(const char *file)
632
0
{
633
0
    OLE2* ole = NULL;
634
635
#ifdef OLE_DEBUG
636
    fprintf(stderr, "----------------------------------------------\n");
637
    fprintf(stderr, "ole2_open_file %s\n", file);
638
#endif
639
640
0
  if(xls_debug) printf("ole2_open: %s\n", file);
641
0
    ole = calloc(1, sizeof(OLE2));
642
643
0
    if (!(ole->file=fopen(file, "rb"))) {
644
0
        if(xls_debug) fprintf(stderr, "File not found\n");
645
0
        free(ole);
646
0
        return NULL;
647
0
    }
648
649
0
    return ole2_read_header_and_body(ole);
650
0
}
651
652
void ole2_close(OLE2* ole2)
653
2.95k
{
654
2.95k
    int i;
655
2.95k
    if (ole2->file)
656
0
        fclose(ole2->file);
657
658
7.61k
    for(i=0; i<ole2->files.count; ++i) {
659
4.65k
        free(ole2->files.file[i].name);
660
4.65k
    }
661
2.95k
    free(ole2->files.file);
662
2.95k
    free(ole2->SecID);
663
2.95k
    free(ole2->SSecID);
664
2.95k
    free(ole2->SSAT);
665
2.95k
    free(ole2);
666
2.95k
}
667
668
void ole2_fclose(OLE2Stream* ole2st)
669
4.32k
{
670
4.32k
  free(ole2st->buf);
671
4.32k
  free(ole2st);
672
4.32k
}
673
674
// Return offset in bytes of a sector from its sid
675
static size_t sector_pos(OLE2* ole2, DWORD sid)
676
4.33M
{
677
4.33M
    return 512 + sid * ole2->lsector;
678
4.33M
}
679
// Read one sector from its sid
680
static ssize_t sector_read(OLE2* ole2, void *buffer, size_t buffer_len, DWORD sid)
681
4.33M
{
682
4.33M
  size_t num;
683
4.33M
  size_t seeked;
684
685
4.33M
  if ((seeked = ole2_fseek(ole2, sector_pos(ole2, sid))) != 0) {
686
9.07k
    if (xls_debug) fprintf(stderr, "Error: wanted to seek to sector %u (0x%x) loc=%u\n", sid, sid,
687
0
                (unsigned int)sector_pos(ole2, sid));
688
9.07k
        return -1;
689
9.07k
    }
690
691
4.32M
    if ((num = ole2_fread(ole2, buffer, buffer_len, ole2->lsector)) != 1) {
692
299
        if (xls_debug) fprintf(stderr, "Error: fread wanted 1 got %lu loc=%u\n", (unsigned long)num,
693
0
                (unsigned int)sector_pos(ole2, sid));
694
299
        return -1;
695
299
    }
696
697
4.32M
    return ole2->lsector;
698
4.32M
}
699
700
// read first 109 sectors of MSAT from header
701
2.74k
static ssize_t read_MSAT_header(OLE2* ole2, OLE2Header* oleh, DWORD sectorCount) {
702
2.74k
    BYTE *sector = (BYTE*)ole2->SecID;
703
2.74k
    ssize_t bytes_read = 0, total_bytes_read = 0;
704
2.74k
    size_t bytes_left = ole2->SecIDCount * sizeof(DWORD);
705
2.74k
    DWORD sectorNum;
706
707
7.69k
    for (sectorNum = 0; sectorNum < sectorCount && sectorNum < 109; sectorNum++)
708
5.34k
    {
709
5.34k
        DWORD s = oleh->MSAT[sectorNum];
710
5.34k
        if (s == ENDOFCHAIN || s == FREESECT)
711
319
            break;
712
5.02k
        if ((bytes_read = sector_read(ole2, sector, bytes_left, s)) == -1) {
713
74
            if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", s);
714
74
            return -1;
715
74
        }
716
4.94k
        sector += ole2->lsector;
717
4.94k
        bytes_left -= ole2->lsector;
718
4.94k
        total_bytes_read += bytes_read;
719
4.94k
    }
720
2.67k
    return total_bytes_read;
721
2.74k
}
722
723
// Add additional sectors of the MSAT
724
2.67k
static ssize_t read_MSAT_body(OLE2 *ole2, DWORD sectorOffset, DWORD sectorCount) {
725
2.67k
    DWORD sid = ole2->difstart;
726
2.67k
    ssize_t bytes_read = 0, total_bytes_read = 0;
727
2.67k
    DWORD sectorNum = sectorOffset;
728
729
2.67k
    DWORD *sector = ole_malloc(ole2->lsector);
730
    //printf("sid=%u (0x%x) sector=%u\n", sid, sid, ole2->lsector);
731
4.88k
    while (sid != ENDOFCHAIN && sid != FREESECT) // FREESECT only here due to an actual file that requires it (old Apple Numbers bug)
732
2.40k
    {
733
2.40k
        int posInSector;
734
        // read MSAT sector
735
2.40k
        if ((bytes_read = sector_read(ole2, sector, ole2->lsector, sid)) == -1) {
736
85
            total_bytes_read = -1;
737
85
            if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", sid);
738
85
            goto cleanup;
739
85
        }
740
2.31k
        total_bytes_read += bytes_read;
741
742
        // read content
743
287k
        for (posInSector = 0; posInSector < (ole2->lsector-4)/4; posInSector++)
744
284k
        {
745
284k
            DWORD s = sector[posInSector];
746
            //printf("   s[%d]=%d (0x%x)\n", posInSector, s, s);
747
748
284k
            if (s != ENDOFCHAIN && s != FREESECT) // see patch in Bug 31. For very large files
749
214k
            {
750
214k
                if (sectorNum == sectorCount) {
751
32
                    if (xls_debug) fprintf(stderr, "Error: Unable to seek to sector #%d\n", s);
752
32
                    total_bytes_read = -1;
753
32
                    goto cleanup;
754
32
                }
755
214k
                if ((bytes_read = sector_read(ole2, (BYTE*)(ole2->SecID)+sectorNum*ole2->lsector,
756
214k
                                (ole2->SecIDCount * sizeof(DWORD) - sectorNum*ole2->lsector), s)) == -1) {
757
50
                    if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", s);
758
50
                    total_bytes_read = -1;
759
50
                    goto cleanup;
760
50
                }
761
214k
                total_bytes_read += bytes_read;
762
214k
                sectorNum++;
763
214k
            }
764
284k
        }
765
2.23k
        if (sid == sector[posInSector]) {
766
24
            if (xls_debug) fprintf(stderr, "Error: Loop detected in sector #%d\n", sid);
767
24
            total_bytes_read = -1;
768
24
            goto cleanup;
769
24
        }
770
2.21k
        sid = sector[posInSector];
771
        //printf("   s[%d]=%d (0x%x)\n", posInSector, sid, sid);
772
2.21k
    }
773
#ifdef OLE_DEBUG
774
    if(xls_debug) {
775
        //printf("==== READ IN SECTORS FOR MSAT TABLE====\n");
776
        int i;
777
        for(i=0; i<512/4; ++i) {  // just the first block
778
            if(ole2->SecID[i] != FREESECT) printf("SecID[%d]=%d\n", i, ole2->SecID[i]);
779
        }
780
    }
781
    //exit(0);
782
#endif
783
784
2.67k
cleanup:
785
2.67k
    free(sector);
786
2.67k
    return total_bytes_read;
787
2.67k
}
788
789
// read in short table
790
2.48k
static ssize_t read_MSAT_trailer(OLE2 *ole2) {
791
2.48k
    ssize_t total_bytes_read = 0;
792
2.48k
    DWORD sector, k;
793
2.48k
    BYTE *wptr;
794
2.48k
    size_t bytes_left;
795
796
2.48k
    if(ole2->sfatstart == ENDOFCHAIN)
797
83
        return 0;
798
799
2.39k
    if ((ole2->SSecID = ole_malloc(ole2->csfat*(size_t)ole2->lsector)) == NULL) {
800
25
        return -1;
801
25
    }
802
2.37k
    ole2->SSecIDCount = ole2->csfat*(size_t)ole2->lsector/4;
803
2.37k
    memset(ole2->SSecID, 0xFF, ole2->SSecIDCount * sizeof(DWORD));
804
2.37k
    sector = ole2->sfatstart;
805
2.37k
    wptr=(BYTE*)ole2->SSecID;
806
2.37k
    bytes_left = ole2->SSecIDCount * sizeof(DWORD);
807
2.05M
    for(k=0; k<ole2->csfat; ++k) {
808
2.05M
        if (sector == ENDOFCHAIN || sector_read(ole2, wptr, bytes_left, sector) == -1) {
809
69
            total_bytes_read = -1;
810
69
            goto cleanup;
811
69
        }
812
2.05M
        if (!ole2_validate_sector(sector, ole2)) {
813
42
            total_bytes_read = -1;
814
42
            goto cleanup;
815
42
        }
816
2.05M
        wptr += ole2->lsector;
817
2.05M
        bytes_left -= ole2->lsector;
818
2.05M
        total_bytes_read += ole2->lsector;
819
2.05M
        sector = xlsIntVal(ole2->SecID[sector]);
820
2.05M
    }
821
#ifdef OLE_DEBUG
822
    if(xls_debug) {
823
        int i;
824
        for(i=0; i<ole2->csfat; ++i) {
825
            if(ole2->SSecID[i] != FREESECT) fprintf(stderr, "SSecID[%d]=%d\n", i, ole2->SSecID[i]);
826
        }
827
    }
828
#endif
829
830
2.37k
cleanup:
831
2.37k
    return total_bytes_read;
832
2.37k
}
833
834
835
// Read MSAT
836
static ssize_t read_MSAT(OLE2* ole2, OLE2Header* oleh)
837
2.80k
{
838
    // reconstitution of the MSAT
839
2.80k
    DWORD count = ole2->cfat;
840
2.80k
    if(count == 0 || count > (1 << 24)) {
841
33
        if (xls_debug) fprintf(stderr, "Error: MSAT count %u out-of-bounds\n", count);
842
33
        return -1;
843
33
    }
844
845
2.77k
    ssize_t total_bytes_read = 0;
846
2.77k
    ssize_t bytes_read = 0;
847
848
2.77k
    ole2->SecIDCount = count*ole2->lsector/4;
849
2.77k
    if ((ole2->SecID = ole_malloc(ole2->SecIDCount * sizeof(DWORD))) == NULL) {
850
25
        total_bytes_read = -1;
851
25
        goto cleanup;
852
25
    }
853
2.74k
    memset(ole2->SecID, 0xFF, ole2->SecIDCount * sizeof(DWORD));
854
855
2.74k
    if ((bytes_read = read_MSAT_header(ole2, oleh, count)) == -1) {
856
74
        total_bytes_read = -1;
857
74
        goto cleanup;
858
74
    }
859
2.67k
    total_bytes_read += bytes_read;
860
861
2.67k
    if ((bytes_read = read_MSAT_body(ole2, total_bytes_read / ole2->lsector, count)) == -1) {
862
191
        total_bytes_read = -1;
863
191
        goto cleanup;
864
191
    }
865
2.48k
    total_bytes_read += bytes_read;
866
867
2.48k
    if ((bytes_read = read_MSAT_trailer(ole2)) == -1) {
868
136
        total_bytes_read = -1;
869
136
        goto cleanup;
870
136
    }
871
2.34k
    total_bytes_read += bytes_read;
872
873
2.77k
cleanup:
874
2.77k
    if (total_bytes_read == -1) {
875
426
        if (ole2->SecID) {
876
401
            free(ole2->SecID);
877
401
            ole2->SecID = NULL;
878
401
        }
879
426
        if (ole2->SSecID) {
880
111
            free(ole2->SSecID);
881
111
            ole2->SSecID = NULL;
882
111
        }
883
426
    }
884
885
2.77k
    return total_bytes_read;
886
2.34k
}