Coverage Report

Created: 2026-09-14 06:12

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
14.0k
static void *ole_malloc(size_t len) {
62
14.0k
    if (len > (1<<24) || len == 0) {
63
65
        return NULL;
64
65
    }
65
13.9k
    return calloc(1, len);
66
14.0k
}
67
68
/* Reallocates memory and zero-fills only the newly grown region (from old_len to new_len). Frees ptr on failure. */
69
1.48k
static void *ole_realloc_zero(void *ptr, size_t old_len, size_t new_len) {
70
1.48k
    if (new_len > (1<<24) || new_len == 0) {
71
17
        free(ptr);
72
17
        return NULL;
73
17
    }
74
1.47k
    void *new_ptr = realloc(ptr, new_len);
75
1.47k
    if (!new_ptr) {
76
0
        free(ptr);
77
0
        return NULL;
78
0
    }
79
1.47k
    if (new_len > old_len) {
80
1.40k
        memset((char *)new_ptr + old_len, 0, new_len - old_len);
81
1.40k
    }
82
1.47k
    return new_ptr;
83
1.47k
}
84
85
4.89k
static int ole2_validate_sector_chain(DWORD *chain, DWORD chain_count, DWORD chain_start) {
86
4.89k
    if (chain == NULL || chain_count == 0)
87
10
        return 0;
88
4.88k
    DWORD count = 0;
89
4.88k
    DWORD sector = chain_start;
90
27.5M
    while (sector != ENDOFCHAIN) {
91
27.5M
        if (sector >= chain_count)
92
308
            return 0;
93
        
94
27.5M
        if (++count >= chain_count)
95
37
            return 0;
96
97
27.5M
        sector = xlsIntVal(chain[sector]);
98
27.5M
    }
99
4.54k
    return 1;
100
4.88k
}
101
102
4.27M
static int ole2_validate_sector(DWORD sector, OLE2 *ole) {
103
4.27M
    if (sector >= ole->SecIDCount) {
104
2.81k
        if (xls_debug) fprintf(stderr, "Error: fatpos %d out-of-bounds for SecID[%d]\n",
105
0
                (int)sector, ole->SecIDCount);
106
2.81k
        return 0;
107
2.81k
    }
108
109
4.27M
    if (sector == xlsIntVal(ole->SecID[sector])) {
110
3
        if (xls_debug) fprintf(stderr, "Error: Sector loop detected, SecID[%d] = %d\n",
111
0
                (int)sector, (int)sector);
112
3
        return 0;
113
3
    }
114
115
4.27M
    return 1;
116
4.27M
}
117
118
// Read next sector of stream
119
static int ole2_bufread(OLE2Stream* olest)
120
679k
{
121
679k
  BYTE *ptr;
122
123
#ifdef OLE_DEBUG
124
    fprintf(stderr, "----------------------------------------------\n");
125
    fprintf(stderr, "ole2_bufread (start)\n");
126
#endif
127
128
679k
    if (olest == NULL || olest->ole == NULL)
129
0
        return -1;
130
131
679k
    if ((DWORD)olest->fatpos!=ENDOFCHAIN)
132
666k
    {
133
666k
    if(olest->sfat) {
134
229k
            if (olest->ole->SSAT == NULL || olest->buf == NULL || olest->ole->SSecID == NULL)
135
16
                return -1;
136
137
229k
            if (olest->fatpos*olest->ole->lssector + olest->bufsize > olest->ole->SSATCount) {
138
144
                if (xls_debug) fprintf(stderr, "Error: fatpos %d out-of-bounds for SSAT\n", (int)olest->fatpos);
139
144
                return -1;
140
144
            }
141
142
229k
      ptr = olest->ole->SSAT + olest->fatpos*olest->ole->lssector;
143
229k
      memcpy(olest->buf, ptr, olest->bufsize); 
144
145
229k
            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
229k
      olest->fatpos=xlsIntVal(olest->ole->SSecID[olest->fatpos]);
152
229k
      olest->pos=0;
153
229k
      olest->cfat++;
154
437k
    } else {
155
437k
      if ((int)olest->fatpos < 0 ||
156
437k
                sector_read(olest->ole, olest->buf, olest->bufsize, olest->fatpos) == -1) {
157
8.54k
                if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", (int)olest->fatpos);
158
8.54k
                return -1;
159
8.54k
            }
160
161
428k
            if (!ole2_validate_sector(olest->fatpos, olest->ole)) {
162
0
                return -1;
163
0
            }
164
165
428k
            olest->fatpos = xlsIntVal(olest->ole->SecID[olest->fatpos]);
166
428k
      olest->pos=0;
167
428k
      olest->cfat++;
168
428k
    }
169
666k
    }
170
#ifdef OLE_DEBUG
171
    fprintf(stderr, "----------------------------------------------\n");
172
    fprintf(stderr, "ole2_bufread (end)\n");
173
#endif
174
  // else printf("ENDOFCHAIN!!!\n");
175
670k
    return 0;
176
679k
}
177
178
// Read part of stream
179
ssize_t ole2_read(void* buf, size_t size, size_t count, OLE2Stream* olest)
180
4.32M
{
181
4.32M
    size_t didReadCount=0;
182
4.32M
    size_t totalReadCount;
183
184
4.32M
  totalReadCount=size*count;
185
4.32M
    if (buf && totalReadCount > 0) {
186
4.32M
        memset(buf, 0, totalReadCount);
187
4.32M
    }
188
189
  // olest->size inited to -1
190
  // printf("===== ole2_read(%ld bytes)\n", totalReadCount);
191
192
4.32M
    if ((long)olest->size>=0 && !olest->sfat)  // directory is -1
193
2.29M
    {
194
2.29M
    size_t rem;
195
2.29M
    rem = olest->size - (olest->cfat*olest->ole->lsector+olest->pos);   
196
2.29M
        totalReadCount = rem<totalReadCount?rem:totalReadCount;
197
2.29M
        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
2.29M
  }
202
203
9.19M
  while ((!olest->eof) && (didReadCount < totalReadCount))
204
4.88M
  {
205
4.88M
    unsigned long remainingBytes;
206
4.88M
        size_t needToReadCount;
207
208
4.88M
    needToReadCount = totalReadCount - didReadCount;
209
4.88M
    remainingBytes  = olest->bufsize - olest->pos;
210
211
4.88M
    if (needToReadCount < remainingBytes) { // does the current sector contain all the data I need?
212
4.23M
      memcpy((BYTE*)buf + didReadCount, olest->buf + olest->pos, needToReadCount);
213
4.23M
      olest->pos    += needToReadCount;
214
4.23M
      didReadCount  += needToReadCount;
215
4.23M
    } else {
216
642k
      memcpy((BYTE*)buf + didReadCount, olest->buf + olest->pos, remainingBytes);
217
642k
      olest->pos    += remainingBytes;
218
642k
      didReadCount  += remainingBytes;
219
642k
      if (ole2_bufread(olest) == -1)
220
8.02k
                return -1;
221
642k
    }
222
4.87M
    if (((DWORD)olest->fatpos == ENDOFCHAIN) && (olest->pos >= olest->bufsize)) {
223
12.2k
      olest->eof=1;
224
12.2k
    }
225
4.87M
  }
226
4.31M
    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
4.31M
    return didReadCount;
245
4.31M
}
246
247
// Open stream in logical ole file
248
OLE2Stream* ole2_sopen(OLE2* ole,DWORD start, size_t size)
249
4.89k
{
250
4.89k
    OLE2Stream* olest=NULL;
251
4.89k
    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.89k
    olest = calloc(1, sizeof(OLE2Stream));
259
4.89k
    if (olest == NULL)
260
0
        return NULL;
261
4.89k
    olest->ole=ole;
262
4.89k
    olest->size=size;
263
4.89k
    olest->fatpos=start;
264
4.89k
    olest->start=start;
265
4.89k
    olest->cfat=-1;
266
4.89k
    if((long)size > 0 && size < (size_t)ole->sectorcutoff) {
267
1.01k
        olest->bufsize=ole->lssector;
268
1.01k
        olest->sfat = 1;
269
3.87k
    } else {
270
3.87k
        olest->bufsize=ole->lsector;
271
3.87k
    }
272
4.89k
    if ((olest->buf = ole_malloc(olest->bufsize)) == NULL) {
273
0
        success = 0;
274
0
        goto cleanup;
275
0
    }
276
277
4.89k
    if (olest->sfat) {
278
1.01k
        if (!ole2_validate_sector_chain(ole->SSecID, ole->SSecIDCount, start)) {
279
134
            success = 0;
280
134
            goto cleanup;
281
134
        }
282
3.87k
    } else {
283
3.87k
        if (!ole2_validate_sector_chain(ole->SecID, ole->SecIDCount, start)) {
284
221
            success = 0;
285
221
            goto cleanup;
286
221
        }
287
3.87k
    }
288
289
4.54k
    if (ole2_bufread(olest) == -1) {
290
39
        success = 0;
291
39
        goto cleanup;
292
39
    }
293
294
4.89k
cleanup:
295
4.89k
    if (!success) {
296
394
        ole2_fclose(olest);
297
394
        olest = NULL;
298
394
    }
299
300
  // if(xls_debug) printf("sopen: sector=%d next=%d\n", start, olest->fatpos);
301
4.89k
    return olest;
302
4.54k
}
303
304
// Move in stream
305
int ole2_seek(OLE2Stream* olest,DWORD ofs)
306
36.2k
{
307
#ifdef OLE_DEBUG
308
    fprintf(stderr, "SEEK %x\n", ofs);
309
#endif
310
36.2k
  if(olest->sfat) {
311
10.2k
    ldiv_t div_rez=ldiv(ofs,olest->ole->lssector);
312
10.2k
    int i;
313
10.2k
    olest->fatpos=olest->start;
314
315
10.2k
        if (div_rez.quot!=0)
316
6.13k
        {
317
76.1k
            for (i=0;i<div_rez.quot;i++) {
318
71.1k
                if (olest->fatpos >= olest->ole->SSecIDCount)
319
1.12k
                    return -1;
320
70.0k
                olest->fatpos=xlsIntVal(olest->ole->SSecID[olest->fatpos]);
321
70.0k
            }
322
6.13k
        }
323
324
9.11k
        if (ole2_bufread(olest) == -1)
325
56
            return -1;
326
327
9.05k
        olest->pos=div_rez.rem;
328
9.05k
        olest->eof=0;
329
9.05k
        olest->cfat=div_rez.quot;
330
    //printf("%i=%i %i\n",ofs,div_rez.quot,div_rez.rem);
331
25.9k
  } else {
332
25.9k
    ldiv_t div_rez=ldiv(ofs,olest->ole->lsector);
333
25.9k
    int i;
334
#ifdef OLE_DEBUG
335
        fprintf(stderr, "seeking fatpos%lu start %u\n", olest->fatpos, olest->start);
336
#endif
337
25.9k
    olest->fatpos=olest->start;
338
339
25.9k
        if (div_rez.quot!=0)
340
4.76k
        {
341
129k
            for (i=0;i<div_rez.quot;i++) {
342
127k
                if (!ole2_validate_sector(olest->fatpos, olest->ole))
343
2.72k
                    return -1;
344
125k
                olest->fatpos=xlsIntVal(olest->ole->SecID[olest->fatpos]);
345
125k
            }
346
4.76k
        }
347
348
23.2k
        if (ole2_bufread(olest) == -1)
349
584
            return -1;
350
351
22.6k
    olest->pos=div_rez.rem;
352
22.6k
    olest->eof=0;
353
22.6k
    olest->cfat=div_rez.quot;
354
    //printf("%i=%i %i\n",ofs,div_rez.quot,div_rez.rem);
355
22.6k
  }
356
31.7k
    return 0;
357
36.2k
}
358
359
// Open logical file contained in physical OLE file
360
OLE2Stream*  ole2_fopen(OLE2* ole, const char *file)
361
7.22k
{
362
7.22k
    int i;
363
364
#ifdef OLE_DEBUG
365
    fprintf(stderr, "----------------------------------------------\n");
366
    fprintf(stderr, "ole2_fopen %s\n", file);
367
#endif
368
369
19.1k
    for (i=0;i<ole->files.count;i++) {
370
14.0k
    char *str = ole->files.file[i].name;
371
#ifdef OLE_DEBUG
372
    fprintf(stderr, "----------------------------------------------\n");
373
    fprintf(stderr, "ole2_fopen found %s\n", str);
374
#endif
375
14.0k
        if (str && strcmp(str,file)==0)  // newer versions of Excel don't write the "Root Entry" string for the first set of data
376
2.18k
        {
377
2.18k
            return ole2_sopen(ole,ole->files.file[i].start,ole->files.file[i].size);
378
2.18k
        }
379
14.0k
  }
380
5.04k
    return NULL;
381
7.22k
}
382
383
4.35M
static int ole2_fseek(OLE2 *ole2, size_t pos) {
384
4.35M
    if (ole2->file)
385
0
        return fseek(ole2->file, pos, SEEK_SET);
386
387
4.35M
    if (pos > ole2->buffer_len)
388
8.55k
        return -1;
389
390
4.34M
    ole2->buffer_pos = pos;
391
4.34M
    return 0;
392
4.35M
}
393
394
// Will read up to `size' bytes from the input, and pad the rest of `size' with
395
// zeros if the input file or buffer is short.
396
4.34M
static size_t ole2_fread(OLE2 *ole2, void *buffer, size_t buffer_len, size_t size) {
397
4.34M
    if (size > buffer_len)
398
10
        return 0;
399
400
4.34M
    memset(buffer, 0, size);
401
402
4.34M
    if (ole2->file)
403
0
        return fread(buffer, 1, size, ole2->file) > 0;
404
405
4.34M
    if (ole2->buffer_pos >= ole2->buffer_len)
406
462
        return 0;
407
408
4.34M
    if (ole2->buffer_pos + size > ole2->buffer_len)
409
1.87M
        size = ole2->buffer_len - ole2->buffer_pos;
410
411
4.34M
    memcpy(buffer, (const char *)ole2->buffer + ole2->buffer_pos, size);
412
4.34M
    ole2->buffer_pos += size;
413
414
4.34M
    return 1;
415
4.34M
}
416
417
// read header and check magic numbers
418
3.50k
static ssize_t ole2_read_header(OLE2 *ole) {
419
3.50k
    ssize_t bytes_read = 0, total_bytes_read = 0;
420
3.50k
    OLE2Header *oleh = calloc(1, sizeof(OLE2Header));
421
3.50k
    if (oleh == NULL)
422
0
        return -1;
423
3.50k
    if (ole2_fread(ole, oleh, sizeof(OLE2Header), sizeof(OLE2Header)) != 1) {
424
0
        total_bytes_read = -1;
425
0
        goto cleanup;
426
0
    }
427
3.50k
    total_bytes_read += sizeof(OLE2Header);
428
3.50k
    xlsConvertHeader(oleh);
429
430
  // make sure the file looks good. Note: this code only works on Little Endian machines
431
3.50k
  if(oleh->id[0] != 0xE011CFD0 || oleh->id[1] != 0xE11AB1A1 || oleh->byteorder != 0xFFFE) {
432
153
        if (xls_debug) fprintf(stderr, "Not an excel file\n");
433
153
        total_bytes_read = -1;
434
153
        goto cleanup;
435
153
  }
436
437
    //ole->lsector=(WORD)pow(2,oleh->lsector);
438
    //ole->lssector=(WORD)pow(2,oleh->lssector);
439
3.35k
  ole->lsector=512;
440
3.35k
    ole->lssector=64;
441
442
3.35k
  if (oleh->lsectorB != 9 || oleh->lssectorB != 6) { // 2**9 == 512, 2**6 == 64
443
36
        if (xls_debug) fprintf(stderr, "Unexpected sector size\n");
444
36
        total_bytes_read = -1;
445
36
        goto cleanup;
446
36
    }
447
  
448
3.31k
    ole->cfat=oleh->cfat;
449
3.31k
    ole->dirstart=oleh->dirstart;
450
3.31k
    ole->sectorcutoff=oleh->sectorcutoff;
451
3.31k
    ole->sfatstart=oleh->sfatstart;
452
3.31k
    ole->csfat=oleh->csfat;
453
3.31k
    ole->difstart=oleh->difstart;
454
3.31k
    ole->cdif=oleh->cdif;
455
3.31k
    ole->files.count=0;
456
457
#ifdef OLE_DEBUG
458
    fprintf(stderr, "==== OLE HEADER ====\n");
459
    //printf ("Header Size:   %i \n", sizeof(OLE2Header));
460
    //printf ("id[0]-id[1]:   %X-%X \n", oleh->id[0], oleh->id[1]);
461
    fprintf(stderr, "verminor:      %X \n",oleh->verminor);
462
    fprintf(stderr, "verdll:        %X \n",oleh->verdll);
463
    //printf ("Byte order:    %X \n",oleh->byteorder);
464
    fprintf(stderr, "sect len:      %X (%i)\n",ole->lsector,ole->lsector);    // ole
465
    fprintf(stderr, "mini len:      %X (%i)\n",ole->lssector,ole->lssector);  // ole
466
    fprintf(stderr, "Fat sect.:     %i \n",oleh->cfat);
467
    fprintf(stderr, "Dir Start:     %i \n",oleh->dirstart);
468
    
469
    fprintf(stderr, "Mini Cutoff:   %i \n",oleh->sectorcutoff);
470
    fprintf(stderr, "MiniFat Start: %X \n",oleh->sfatstart);
471
    fprintf(stderr, "Count MFat:    %i \n",oleh->csfat);
472
    fprintf(stderr, "Dif start:     %X \n",oleh->difstart);
473
    fprintf(stderr, "Count Dif:     %i \n",oleh->cdif);
474
    fprintf(stderr, "Fat Size:      %u (0x%X) \n",oleh->cfat*ole->lsector,oleh->cfat*ole->lsector);
475
#endif
476
    // read directory entries
477
3.31k
    if ((bytes_read = read_MSAT(ole, oleh)) == -1) {
478
598
        total_bytes_read = -1;
479
598
        goto cleanup;
480
598
    }
481
2.71k
    total_bytes_read += bytes_read;
482
483
3.50k
cleanup:
484
3.50k
    free(oleh);
485
486
3.50k
    return total_bytes_read;
487
2.71k
}
488
489
2.71k
static ssize_t ole2_read_body(OLE2 *ole) {
490
  // reuse this buffer
491
2.71k
    PSS *pss = NULL;
492
2.71k
    OLE2Stream *olest = NULL;
493
2.71k
    char* name = NULL;
494
2.71k
    ssize_t bytes_read = 0, total_bytes_read = 0;
495
496
2.71k
    if ((olest = ole2_sopen(ole,ole->dirstart, -1)) == NULL) {
497
176
        total_bytes_read = -1;
498
176
        goto cleanup;
499
176
    }
500
2.54k
    pss = calloc(1, sizeof(PSS));
501
2.54k
    if (pss == NULL) {
502
0
        total_bytes_read = -1;
503
0
        goto cleanup;
504
0
    }
505
12.0k
    do {
506
12.0k
        if ((bytes_read = ole2_read(pss,1,sizeof(PSS),olest)) == -1) {
507
16
            total_bytes_read = -1;
508
16
            goto cleanup;
509
16
        }
510
12.0k
        total_bytes_read += bytes_read;
511
12.0k
        xlsConvertPss(pss);
512
12.0k
        if (pss->bsize > sizeof(pss->name)) {
513
43
            total_bytes_read = -1;
514
43
            goto cleanup;
515
43
        }
516
11.9k
        name=transcode_utf16_to_utf8(pss->name, pss->bsize);
517
#ifdef OLE_DEBUG  
518
    fprintf(stderr, "OLE NAME: %s count=%d\n", name, (int)ole->files.count);
519
#endif
520
11.9k
        if (pss->type == PS_USER_ROOT || pss->type == PS_USER_STREAM) // (name!=NULL) // 
521
4.91k
        {
522
523
#ifdef OLE_DEBUG    
524
      fprintf(stderr, "OLE TYPE: %s file=%d size=%d\n",
525
                    pss->type == PS_USER_ROOT ? "root" : "user",
526
                    (int)ole->files.count, (int)pss->size);
527
#endif    
528
4.91k
            struct st_olefiles_data *new_files = realloc(ole->files.file,
529
4.91k
                    (ole->files.count+1)*sizeof(struct st_olefiles_data));
530
4.91k
            if (new_files == NULL) {
531
0
                free(name);
532
0
                total_bytes_read = -1;
533
0
                goto cleanup;
534
0
            }
535
4.91k
            ole->files.file = new_files;
536
4.91k
            ole->files.file[ole->files.count].name=name;
537
4.91k
            ole->files.file[ole->files.count].start=pss->sstart;
538
4.91k
            ole->files.file[ole->files.count].size=pss->size;
539
4.91k
            ole->files.count++;
540
      
541
#ifdef OLE_DEBUG
542
            fprintf(stderr, "----------------------------------------------\n");
543
            fprintf(stderr, "name: %s (size=%d [c=%c])\n", name, pss->bsize, name ? name[0]:' ');
544
            fprintf(stderr, "bsize %i\n",pss->bsize);
545
            fprintf(stderr, "type %i\n",pss->type);
546
            fprintf(stderr, "flag %i\n",pss->flag);
547
            fprintf(stderr, "left %X\n",pss->left);
548
            fprintf(stderr, "right %X\n",pss->right);
549
            fprintf(stderr, "child %X\n",pss->child);
550
            fprintf(stderr, "guid %.4X-%.4X-%.4X-%.4X %.4X-%.4X-%.4X-%.4X\n",
551
                    pss->guid[0],pss->guid[1],pss->guid[2],pss->guid[3],
552
                    pss->guid[4],pss->guid[5],pss->guid[6],pss->guid[7]);
553
            fprintf(stderr, "user flag %.4X\n",pss->userflags);
554
            fprintf(stderr, "sstart %.4d\n",pss->sstart);
555
            fprintf(stderr, "size %.4d\n",pss->size);
556
#endif
557
4.91k
      if(pss->sstart == ENDOFCHAIN) {
558
73
        if (xls_debug) verbose("END OF CHAIN\n");
559
4.84k
      } else if(pss->type == PS_USER_STREAM) {
560
3.32k
      } else if(pss->type == PS_USER_ROOT) {
561
1.52k
        DWORD sector, k, blocks;
562
1.52k
        BYTE *wptr;
563
1.52k
                size_t bytes_left;
564
        
565
1.52k
        blocks = (pss->size + (ole->lsector - 1)) / ole->lsector; // count partial
566
1.52k
        if (ole->lsector == 0 || blocks > (1u << 24) / ole->lsector) {
567
31
          total_bytes_read = -1;
568
31
          goto cleanup;
569
31
        }
570
#ifdef OLE_DEBUG
571
                fprintf(stderr, "OLE BLOCKS: %d = (%d + (%d - 1))/%d\n",
572
                        (int)blocks, (int)pss->size, (int)ole->lsector, (int)ole->lsector);
573
#endif
574
1.48k
        size_t old_ssat_bytes = ole->SSATCount;
575
1.48k
        if ((ole->SSAT = ole_realloc_zero(ole->SSAT, old_ssat_bytes, blocks*ole->lsector)) == NULL) {
576
17
                    total_bytes_read = -1;
577
17
                    goto cleanup;
578
17
                }
579
1.47k
                ole->SSATCount = blocks*ole->lsector;
580
        // printf("blocks %d\n", blocks);
581
582
1.47k
        sector = pss->sstart;
583
1.47k
        wptr = (BYTE*)ole->SSAT;
584
1.47k
                bytes_left = blocks*ole->lsector;
585
1.65M
        for(k=0; k<blocks; ++k) {
586
          // printf("block %d sector %d\n", k, sector);
587
1.65M
                    if (sector == ENDOFCHAIN || sector_read(ole, wptr, bytes_left, sector) == -1) {
588
103
                        if (xls_debug) fprintf(stderr, "Unable to read sector #%d\n", sector);
589
103
                        total_bytes_read = -1;
590
103
                        goto cleanup;
591
103
                    }
592
1.65M
                    if (!ole2_validate_sector(sector, ole)) {
593
46
                        total_bytes_read = -1;
594
46
                        goto cleanup;
595
46
                    }
596
1.65M
                    total_bytes_read += ole->lsector;
597
1.65M
          wptr += ole->lsector;
598
1.65M
                    bytes_left -= ole->lsector;
599
1.65M
          sector = xlsIntVal(ole->SecID[sector]);
600
1.65M
        }
601
1.47k
      }  
602
7.08k
    } else {
603
7.08k
      free(name);
604
7.08k
    }
605
11.9k
    } while (!olest->eof);
606
607
2.71k
cleanup:
608
2.71k
    if (olest)
609
2.54k
        ole2_fclose(olest);
610
2.71k
    if (pss)
611
2.54k
        free(pss);
612
613
#ifdef OLE_DEBUG
614
    fprintf(stderr, "----------------------------------------------\n");
615
    fprintf(stderr, "ole2_read_body: %d bytes\n", (int)total_bytes_read);
616
#endif
617
618
2.71k
    return total_bytes_read;
619
2.54k
}
620
621
3.50k
OLE2 *ole2_read_header_and_body(OLE2 *ole) {
622
3.50k
    if (ole2_read_header(ole) == -1) {
623
787
        ole2_close(ole);
624
787
        return NULL;
625
787
    }
626
627
2.71k
    if (ole2_read_body(ole) == -1) {
628
432
        ole2_close(ole);
629
432
        return NULL;
630
432
    }
631
632
2.28k
    return ole;
633
2.71k
}
634
635
// Open in-memory buffer
636
3.50k
OLE2 *ole2_open_buffer(const void *buffer, size_t len) {
637
3.50k
    OLE2 *ole = calloc(1, sizeof(OLE2));
638
3.50k
    if (ole == NULL)
639
0
        return NULL;
640
641
3.50k
    ole->buffer = buffer;
642
3.50k
    ole->buffer_len = len;
643
644
3.50k
    return ole2_read_header_and_body(ole);
645
3.50k
}
646
647
// Open physical file
648
#ifdef _WIN32
649
/* Convert a UTF-8 string to UTF-16 for _wfopen(). Returns NULL if the input
650
 * is not well-formed UTF-8 (the path is probably in the ANSI code page then).
651
 * Written by hand so that ole.c does not need <windows.h>, whose DWORD typedef
652
 * conflicts with the one in xlstypes.h. */
653
static wchar_t *ole2_utf8_to_utf16(const char *str) {
654
    const unsigned char *in = (const unsigned char *)str;
655
    size_t n = strlen(str);
656
    wchar_t *out = malloc((n + 1) * sizeof(wchar_t));
657
    size_t i = 0, o = 0;
658
659
    if (out == NULL)
660
        return NULL;
661
662
    while (i < n) {
663
        uint32_t cp;
664
        size_t extra;
665
        if (in[i] < 0x80) {
666
            cp = in[i]; extra = 0;
667
        } else if ((in[i] & 0xE0) == 0xC0 && in[i] >= 0xC2) {
668
            cp = in[i] & 0x1F; extra = 1;
669
        } else if ((in[i] & 0xF0) == 0xE0) {
670
            cp = in[i] & 0x0F; extra = 2;
671
        } else if ((in[i] & 0xF8) == 0xF0 && in[i] <= 0xF4) {
672
            cp = in[i] & 0x07; extra = 3;
673
        } else {
674
            goto invalid;
675
        }
676
        for (size_t k = 1; k <= extra; k++) {
677
            if (i + k >= n || (in[i + k] & 0xC0) != 0x80)
678
                goto invalid;
679
            cp = (cp << 6) | (in[i + k] & 0x3F);
680
        }
681
        if ((extra == 2 && cp < 0x800) || (extra == 3 && cp < 0x10000) ||
682
                cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF))
683
            goto invalid;
684
        i += extra + 1;
685
        if (cp >= 0x10000) {
686
            cp -= 0x10000;
687
            out[o++] = (wchar_t)(0xD800 | (cp >> 10));
688
            out[o++] = (wchar_t)(0xDC00 | (cp & 0x3FF));
689
        } else {
690
            out[o++] = (wchar_t)cp;
691
        }
692
    }
693
    out[o] = L'\0';
694
    return out;
695
696
invalid:
697
    free(out);
698
    return NULL;
699
}
700
#endif
701
702
/* Open a file by path. On Windows fopen() interprets the path in the ANSI code
703
 * page, so a UTF-8 path with non-ASCII characters fails; retry it as UTF-16.
704
 * See https://github.com/libxls/libxls/issues/146 */
705
static FILE *ole2_fopen_path(const char *file)
706
0
{
707
0
    FILE *fp = fopen(file, "rb");
708
#ifdef _WIN32
709
    if (fp == NULL) {
710
        wchar_t *wfile = ole2_utf8_to_utf16(file);
711
        if (wfile) {
712
            fp = _wfopen(wfile, L"rb");
713
            free(wfile);
714
        }
715
    }
716
#endif
717
0
    return fp;
718
0
}
719
720
OLE2* ole2_open_file(const char *file)
721
0
{
722
0
    OLE2* ole = NULL;
723
724
#ifdef OLE_DEBUG
725
    fprintf(stderr, "----------------------------------------------\n");
726
    fprintf(stderr, "ole2_open_file %s\n", file);
727
#endif
728
729
0
  if(xls_debug) printf("ole2_open: %s\n", file);
730
0
    ole = calloc(1, sizeof(OLE2));
731
0
    if (ole == NULL)
732
0
        return NULL;
733
734
0
    if (!(ole->file=ole2_fopen_path(file))) {
735
0
        if(xls_debug) fprintf(stderr, "File not found\n");
736
0
        free(ole);
737
0
        return NULL;
738
0
    }
739
740
0
    return ole2_read_header_and_body(ole);
741
0
}
742
743
void ole2_close(OLE2* ole2)
744
3.50k
{
745
3.50k
    int i;
746
3.50k
    if (ole2->file)
747
0
        fclose(ole2->file);
748
749
8.42k
    for(i=0; i<ole2->files.count; ++i) {
750
4.91k
        free(ole2->files.file[i].name);
751
4.91k
    }
752
3.50k
    free(ole2->files.file);
753
3.50k
    free(ole2->SecID);
754
3.50k
    free(ole2->SSecID);
755
3.50k
    free(ole2->SSAT);
756
3.50k
    free(ole2);
757
3.50k
}
758
759
void ole2_fclose(OLE2Stream* ole2st)
760
4.89k
{
761
4.89k
  free(ole2st->buf);
762
4.89k
  free(ole2st);
763
4.89k
}
764
765
// Return offset in bytes of a sector from its sid
766
static size_t sector_pos(OLE2* ole2, DWORD sid)
767
4.35M
{
768
4.35M
    return 512 + sid * ole2->lsector;
769
4.35M
}
770
// Read one sector from its sid
771
static ssize_t sector_read(OLE2* ole2, void *buffer, size_t buffer_len, DWORD sid)
772
4.35M
{
773
4.35M
  size_t num;
774
4.35M
  size_t seeked;
775
776
4.35M
  if ((seeked = ole2_fseek(ole2, sector_pos(ole2, sid))) != 0) {
777
8.55k
    if (xls_debug) fprintf(stderr, "Error: wanted to seek to sector %u (0x%x) loc=%u\n", sid, sid,
778
0
                (unsigned int)sector_pos(ole2, sid));
779
8.55k
        return -1;
780
8.55k
    }
781
782
4.34M
    if ((num = ole2_fread(ole2, buffer, buffer_len, ole2->lsector)) != 1) {
783
472
        if (xls_debug) fprintf(stderr, "Error: fread wanted 1 got %lu loc=%u\n", (unsigned long)num,
784
0
                (unsigned int)sector_pos(ole2, sid));
785
472
        return -1;
786
472
    }
787
788
4.34M
    return ole2->lsector;
789
4.34M
}
790
791
// read first 109 sectors of MSAT from header
792
3.24k
static ssize_t read_MSAT_header(OLE2* ole2, OLE2Header* oleh, DWORD sectorCount) {
793
3.24k
    BYTE *sector = (BYTE*)ole2->SecID;
794
3.24k
    ssize_t bytes_read = 0, total_bytes_read = 0;
795
3.24k
    size_t bytes_left = ole2->SecIDCount * sizeof(DWORD);
796
3.24k
    DWORD sectorNum;
797
798
8.71k
    for (sectorNum = 0; sectorNum < sectorCount && sectorNum < 109; sectorNum++)
799
6.05k
    {
800
6.05k
        DWORD s = oleh->MSAT[sectorNum];
801
6.05k
        if (s == ENDOFCHAIN || s == FREESECT)
802
455
            break;
803
5.59k
        if ((bytes_read = sector_read(ole2, sector, bytes_left, s)) == -1) {
804
134
            if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", s);
805
134
            return -1;
806
134
        }
807
5.46k
        sector += ole2->lsector;
808
5.46k
        bytes_left -= ole2->lsector;
809
5.46k
        total_bytes_read += bytes_read;
810
5.46k
    }
811
3.11k
    return total_bytes_read;
812
3.24k
}
813
814
// Add additional sectors of the MSAT
815
3.11k
static ssize_t read_MSAT_body(OLE2 *ole2, DWORD sectorOffset, DWORD sectorCount) {
816
3.11k
    DWORD sid = ole2->difstart;
817
3.11k
    ssize_t bytes_read = 0, total_bytes_read = 0;
818
3.11k
    DWORD sectorNum = sectorOffset;
819
820
3.11k
    DWORD *sector = ole_malloc(ole2->lsector);
821
3.11k
    if (sector == NULL)
822
0
        return -1;
823
    //printf("sid=%u (0x%x) sector=%u\n", sid, sid, ole2->lsector);
824
5.06k
    while (sid != ENDOFCHAIN && sid != FREESECT) // FREESECT only here due to an actual file that requires it (old Apple Numbers bug)
825
2.18k
    {
826
2.18k
        int posInSector;
827
        // read MSAT sector
828
2.18k
        if ((bytes_read = sector_read(ole2, sector, ole2->lsector, sid)) == -1) {
829
101
            total_bytes_read = -1;
830
101
            if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", sid);
831
101
            goto cleanup;
832
101
        }
833
2.08k
        total_bytes_read += bytes_read;
834
835
        // read content
836
254k
        for (posInSector = 0; posInSector < (ole2->lsector-4)/4; posInSector++)
837
252k
        {
838
252k
            DWORD s = sector[posInSector];
839
            //printf("   s[%d]=%d (0x%x)\n", posInSector, s, s);
840
841
252k
            if (s != ENDOFCHAIN && s != FREESECT) // see patch in Bug 31. For very large files
842
190k
            {
843
190k
                if (sectorNum == sectorCount) {
844
37
                    if (xls_debug) fprintf(stderr, "Error: Unable to seek to sector #%d\n", s);
845
37
                    total_bytes_read = -1;
846
37
                    goto cleanup;
847
37
                }
848
190k
                if ((bytes_read = sector_read(ole2, (BYTE*)(ole2->SecID)+sectorNum*ole2->lsector,
849
190k
                                (ole2->SecIDCount * sizeof(DWORD) - sectorNum*ole2->lsector), s)) == -1) {
850
72
                    if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", s);
851
72
                    total_bytes_read = -1;
852
72
                    goto cleanup;
853
72
                }
854
190k
                total_bytes_read += bytes_read;
855
190k
                sectorNum++;
856
190k
            }
857
252k
        }
858
1.97k
        if (sid == sector[posInSector]) {
859
30
            if (xls_debug) fprintf(stderr, "Error: Loop detected in sector #%d\n", sid);
860
30
            total_bytes_read = -1;
861
30
            goto cleanup;
862
30
        }
863
1.94k
        sid = sector[posInSector];
864
        //printf("   s[%d]=%d (0x%x)\n", posInSector, sid, sid);
865
1.94k
    }
866
#ifdef OLE_DEBUG
867
    if(xls_debug) {
868
        //printf("==== READ IN SECTORS FOR MSAT TABLE====\n");
869
        int i;
870
        for(i=0; i<512/4; ++i) {  // just the first block
871
            if(ole2->SecID[i] != FREESECT) printf("SecID[%d]=%d\n", i, ole2->SecID[i]);
872
        }
873
    }
874
    //exit(0);
875
#endif
876
877
3.11k
cleanup:
878
3.11k
    free(sector);
879
3.11k
    return total_bytes_read;
880
3.11k
}
881
882
// read in short table
883
2.87k
static ssize_t read_MSAT_trailer(OLE2 *ole2) {
884
2.87k
    ssize_t total_bytes_read = 0;
885
2.87k
    DWORD sector, k;
886
2.87k
    BYTE *wptr;
887
2.87k
    size_t bytes_left;
888
889
2.87k
    if(ole2->sfatstart == ENDOFCHAIN)
890
137
        return 0;
891
892
2.73k
    if ((ole2->SSecID = ole_malloc(ole2->csfat*(size_t)ole2->lsector)) == NULL) {
893
34
        return -1;
894
34
    }
895
2.70k
    ole2->SSecIDCount = ole2->csfat*(size_t)ole2->lsector/4;
896
2.70k
    memset(ole2->SSecID, 0xFF, ole2->SSecIDCount * sizeof(DWORD));
897
2.70k
    sector = ole2->sfatstart;
898
2.70k
    wptr=(BYTE*)ole2->SSecID;
899
2.70k
    bytes_left = ole2->SSecIDCount * sizeof(DWORD);
900
2.06M
    for(k=0; k<ole2->csfat; ++k) {
901
2.06M
        if (sector == ENDOFCHAIN || sector_read(ole2, wptr, bytes_left, sector) == -1) {
902
77
            total_bytes_read = -1;
903
77
            goto cleanup;
904
77
        }
905
2.06M
        if (!ole2_validate_sector(sector, ole2)) {
906
47
            total_bytes_read = -1;
907
47
            goto cleanup;
908
47
        }
909
2.06M
        wptr += ole2->lsector;
910
2.06M
        bytes_left -= ole2->lsector;
911
2.06M
        total_bytes_read += ole2->lsector;
912
2.06M
        sector = xlsIntVal(ole2->SecID[sector]);
913
2.06M
    }
914
#ifdef OLE_DEBUG
915
    if(xls_debug) {
916
        int i;
917
        for(i=0; i<ole2->csfat; ++i) {
918
            if(ole2->SSecID[i] != FREESECT) fprintf(stderr, "SSecID[%d]=%d\n", i, ole2->SSecID[i]);
919
        }
920
    }
921
#endif
922
923
2.70k
cleanup:
924
2.70k
    return total_bytes_read;
925
2.70k
}
926
927
928
// Read MSAT
929
static ssize_t read_MSAT(OLE2* ole2, OLE2Header* oleh)
930
3.31k
{
931
    // reconstitution of the MSAT
932
3.31k
    DWORD count = ole2->cfat;
933
3.31k
    if(count == 0 || count > (1 << 24)) {
934
35
        if (xls_debug) fprintf(stderr, "Error: MSAT count %u out-of-bounds\n", count);
935
35
        return -1;
936
35
    }
937
938
3.27k
    ssize_t total_bytes_read = 0;
939
3.27k
    ssize_t bytes_read = 0;
940
941
3.27k
    ole2->SecIDCount = count*ole2->lsector/4;
942
3.27k
    if ((ole2->SecID = ole_malloc(ole2->SecIDCount * sizeof(DWORD))) == NULL) {
943
31
        total_bytes_read = -1;
944
31
        goto cleanup;
945
31
    }
946
3.24k
    memset(ole2->SecID, 0xFF, ole2->SecIDCount * sizeof(DWORD));
947
948
3.24k
    if ((bytes_read = read_MSAT_header(ole2, oleh, count)) == -1) {
949
134
        total_bytes_read = -1;
950
134
        goto cleanup;
951
134
    }
952
3.11k
    total_bytes_read += bytes_read;
953
954
3.11k
    if ((bytes_read = read_MSAT_body(ole2, total_bytes_read / ole2->lsector, count)) == -1) {
955
240
        total_bytes_read = -1;
956
240
        goto cleanup;
957
240
    }
958
2.87k
    total_bytes_read += bytes_read;
959
960
2.87k
    if ((bytes_read = read_MSAT_trailer(ole2)) == -1) {
961
158
        total_bytes_read = -1;
962
158
        goto cleanup;
963
158
    }
964
2.71k
    total_bytes_read += bytes_read;
965
966
3.27k
cleanup:
967
3.27k
    if (total_bytes_read == -1) {
968
563
        if (ole2->SecID) {
969
532
            free(ole2->SecID);
970
532
            ole2->SecID = NULL;
971
532
        }
972
563
        if (ole2->SSecID) {
973
124
            free(ole2->SSecID);
974
124
            ole2->SSecID = NULL;
975
124
        }
976
563
    }
977
978
3.27k
    return total_bytes_read;
979
2.71k
}