1/*-
2 * Copyright (c) 2008 Christos Zoulas
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
15 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
16 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
18 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26/*
27 * Parse Composite Document Files, the format used in Microsoft Office
28 * document files before they switched to zipped XML.
29 * Info from: http://sc.openoffice.org/compdocfileformat.pdf
30 *
31 * N.B. This is the "Composite Document File" format, and not the
32 * "Compound Document Format", nor the "Channel Definition Format".
33 */
34
35#include "file.h"
36
37#ifndef lint
38FILE_RCSID("@(#)$File: cdf.c,v 1.73 2015/01/11 16:58:25 christos Exp $")
39#endif
40
41#include <assert.h>
42#ifdef CDF_DEBUG
43#include <err.h>
44#endif
45#include <stdlib.h>
46
47#ifdef PHP_WIN32
48#include "win32/unistd.h"
49#else
50#include <unistd.h>
51#endif
52
53#ifndef UINT32_MAX
54# define UINT32_MAX (0xffffffff)
55#endif
56
57#include <string.h>
58#include <time.h>
59#include <ctype.h>
60#ifdef HAVE_LIMITS_H
61#include <limits.h>
62#endif
63
64#ifndef EFTYPE
65#define EFTYPE EINVAL
66#endif
67
68#include "cdf.h"
69
70#ifdef CDF_DEBUG
71#define DPRINTF(a) printf a, fflush(stdout)
72#else
73#define DPRINTF(a)
74#endif
75
76static union {
77    char s[4];
78    uint32_t u;
79} cdf_bo;
80
81#define NEED_SWAP   (cdf_bo.u == (uint32_t)0x01020304)
82
83#define CDF_TOLE8(x)    ((uint64_t)(NEED_SWAP ? _cdf_tole8(x) : (uint64_t)(x)))
84#define CDF_TOLE4(x)    ((uint32_t)(NEED_SWAP ? _cdf_tole4(x) : (uint32_t)(x)))
85#define CDF_TOLE2(x)    ((uint16_t)(NEED_SWAP ? _cdf_tole2(x) : (uint16_t)(x)))
86#define CDF_TOLE(x) (/*CONSTCOND*/sizeof(x) == 2 ? \
87                CDF_TOLE2(CAST(uint16_t, x)) : \
88            (/*CONSTCOND*/sizeof(x) == 4 ? \
89                CDF_TOLE4(CAST(uint32_t, x)) : \
90                CDF_TOLE8(CAST(uint64_t, x))))
91#define CDF_GETUINT32(x, y) cdf_getuint32(x, y)
92
93
94/*
95 * swap a short
96 */
97static uint16_t
98_cdf_tole2(uint16_t sv)
99{
100    uint16_t rv;
101    uint8_t *s = (uint8_t *)(void *)&sv;
102    uint8_t *d = (uint8_t *)(void *)&rv;
103    d[0] = s[1];
104    d[1] = s[0];
105    return rv;
106}
107
108/*
109 * swap an int
110 */
111static uint32_t
112_cdf_tole4(uint32_t sv)
113{
114    uint32_t rv;
115    uint8_t *s = (uint8_t *)(void *)&sv;
116    uint8_t *d = (uint8_t *)(void *)&rv;
117    d[0] = s[3];
118    d[1] = s[2];
119    d[2] = s[1];
120    d[3] = s[0];
121    return rv;
122}
123
124/*
125 * swap a quad
126 */
127static uint64_t
128_cdf_tole8(uint64_t sv)
129{
130    uint64_t rv;
131    uint8_t *s = (uint8_t *)(void *)&sv;
132    uint8_t *d = (uint8_t *)(void *)&rv;
133    d[0] = s[7];
134    d[1] = s[6];
135    d[2] = s[5];
136    d[3] = s[4];
137    d[4] = s[3];
138    d[5] = s[2];
139    d[6] = s[1];
140    d[7] = s[0];
141    return rv;
142}
143
144/*
145 * grab a uint32_t from a possibly unaligned address, and return it in
146 * the native host order.
147 */
148static uint32_t
149cdf_getuint32(const uint8_t *p, size_t offs)
150{
151    uint32_t rv;
152    (void)memcpy(&rv, p + offs * sizeof(uint32_t), sizeof(rv));
153    return CDF_TOLE4(rv);
154}
155
156#define CDF_UNPACK(a)   \
157    (void)memcpy(&(a), &buf[len], sizeof(a)), len += sizeof(a)
158#define CDF_UNPACKA(a)  \
159    (void)memcpy((a), &buf[len], sizeof(a)), len += sizeof(a)
160
161uint16_t
162cdf_tole2(uint16_t sv)
163{
164    return CDF_TOLE2(sv);
165}
166
167uint32_t
168cdf_tole4(uint32_t sv)
169{
170    return CDF_TOLE4(sv);
171}
172
173uint64_t
174cdf_tole8(uint64_t sv)
175{
176    return CDF_TOLE8(sv);
177}
178
179void
180cdf_swap_header(cdf_header_t *h)
181{
182    size_t i;
183
184    h->h_magic = CDF_TOLE8(h->h_magic);
185    h->h_uuid[0] = CDF_TOLE8(h->h_uuid[0]);
186    h->h_uuid[1] = CDF_TOLE8(h->h_uuid[1]);
187    h->h_revision = CDF_TOLE2(h->h_revision);
188    h->h_version = CDF_TOLE2(h->h_version);
189    h->h_byte_order = CDF_TOLE2(h->h_byte_order);
190    h->h_sec_size_p2 = CDF_TOLE2(h->h_sec_size_p2);
191    h->h_short_sec_size_p2 = CDF_TOLE2(h->h_short_sec_size_p2);
192    h->h_num_sectors_in_sat = CDF_TOLE4(h->h_num_sectors_in_sat);
193    h->h_secid_first_directory = CDF_TOLE4(h->h_secid_first_directory);
194    h->h_min_size_standard_stream =
195        CDF_TOLE4(h->h_min_size_standard_stream);
196    h->h_secid_first_sector_in_short_sat =
197        CDF_TOLE4((uint32_t)h->h_secid_first_sector_in_short_sat);
198    h->h_num_sectors_in_short_sat =
199        CDF_TOLE4(h->h_num_sectors_in_short_sat);
200    h->h_secid_first_sector_in_master_sat =
201        CDF_TOLE4((uint32_t)h->h_secid_first_sector_in_master_sat);
202    h->h_num_sectors_in_master_sat =
203        CDF_TOLE4(h->h_num_sectors_in_master_sat);
204    for (i = 0; i < __arraycount(h->h_master_sat); i++)
205        h->h_master_sat[i] = CDF_TOLE4((uint32_t)h->h_master_sat[i]);
206}
207
208void
209cdf_unpack_header(cdf_header_t *h, char *buf)
210{
211    size_t i;
212    size_t len = 0;
213
214    CDF_UNPACK(h->h_magic);
215    CDF_UNPACKA(h->h_uuid);
216    CDF_UNPACK(h->h_revision);
217    CDF_UNPACK(h->h_version);
218    CDF_UNPACK(h->h_byte_order);
219    CDF_UNPACK(h->h_sec_size_p2);
220    CDF_UNPACK(h->h_short_sec_size_p2);
221    CDF_UNPACKA(h->h_unused0);
222    CDF_UNPACK(h->h_num_sectors_in_sat);
223    CDF_UNPACK(h->h_secid_first_directory);
224    CDF_UNPACKA(h->h_unused1);
225    CDF_UNPACK(h->h_min_size_standard_stream);
226    CDF_UNPACK(h->h_secid_first_sector_in_short_sat);
227    CDF_UNPACK(h->h_num_sectors_in_short_sat);
228    CDF_UNPACK(h->h_secid_first_sector_in_master_sat);
229    CDF_UNPACK(h->h_num_sectors_in_master_sat);
230    for (i = 0; i < __arraycount(h->h_master_sat); i++)
231        CDF_UNPACK(h->h_master_sat[i]);
232}
233
234void
235cdf_swap_dir(cdf_directory_t *d)
236{
237    d->d_namelen = CDF_TOLE2(d->d_namelen);
238    d->d_left_child = CDF_TOLE4((uint32_t)d->d_left_child);
239    d->d_right_child = CDF_TOLE4((uint32_t)d->d_right_child);
240    d->d_storage = CDF_TOLE4((uint32_t)d->d_storage);
241    d->d_storage_uuid[0] = CDF_TOLE8(d->d_storage_uuid[0]);
242    d->d_storage_uuid[1] = CDF_TOLE8(d->d_storage_uuid[1]);
243    d->d_flags = CDF_TOLE4(d->d_flags);
244    d->d_created = CDF_TOLE8((uint64_t)d->d_created);
245    d->d_modified = CDF_TOLE8((uint64_t)d->d_modified);
246    d->d_stream_first_sector = CDF_TOLE4((uint32_t)d->d_stream_first_sector);
247    d->d_size = CDF_TOLE4(d->d_size);
248}
249
250void
251cdf_swap_class(cdf_classid_t *d)
252{
253    d->cl_dword = CDF_TOLE4(d->cl_dword);
254    d->cl_word[0] = CDF_TOLE2(d->cl_word[0]);
255    d->cl_word[1] = CDF_TOLE2(d->cl_word[1]);
256}
257
258void
259cdf_unpack_dir(cdf_directory_t *d, char *buf)
260{
261    size_t len = 0;
262
263    CDF_UNPACKA(d->d_name);
264    CDF_UNPACK(d->d_namelen);
265    CDF_UNPACK(d->d_type);
266    CDF_UNPACK(d->d_color);
267    CDF_UNPACK(d->d_left_child);
268    CDF_UNPACK(d->d_right_child);
269    CDF_UNPACK(d->d_storage);
270    CDF_UNPACKA(d->d_storage_uuid);
271    CDF_UNPACK(d->d_flags);
272    CDF_UNPACK(d->d_created);
273    CDF_UNPACK(d->d_modified);
274    CDF_UNPACK(d->d_stream_first_sector);
275    CDF_UNPACK(d->d_size);
276    CDF_UNPACK(d->d_unused0);
277}
278
279static int
280cdf_check_stream_offset(const cdf_stream_t *sst, const cdf_header_t *h,
281    const void *p, size_t tail, int line)
282{
283    const char *b = (const char *)sst->sst_tab;
284    const char *e = ((const char *)p) + tail;
285    size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ?
286        CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h);
287    /*LINTED*/(void)&line;
288    if (e >= b && (size_t)(e - b) <= ss * sst->sst_len)
289        return 0;
290    DPRINTF(("%d: offset begin %p < end %p || %" SIZE_T_FORMAT "u"
291        " > %" SIZE_T_FORMAT "u [%" SIZE_T_FORMAT "u %"
292        SIZE_T_FORMAT "u]\n", line, b, e, (size_t)(e - b),
293        ss * sst->sst_len, ss, sst->sst_len));
294    errno = EFTYPE;
295    return -1;
296}
297
298static ssize_t
299cdf_read(const cdf_info_t *info, zend_off_t off, void *buf, size_t len)
300{
301    size_t siz = (size_t)off + len;
302
303    if ((zend_off_t)(off + len) != (zend_off_t)siz) {
304        errno = EINVAL;
305        return -1;
306    }
307
308    if (info->i_buf != NULL && info->i_len >= siz) {
309        (void)memcpy(buf, &info->i_buf[off], len);
310        return (ssize_t)len;
311    }
312
313    if (info->i_fd == -1)
314        return -1;
315
316    if (FINFO_LSEEK_FUNC(info->i_fd, off, SEEK_SET) == (zend_off_t)-1)
317        return -1;
318
319    if (FINFO_READ_FUNC(info->i_fd, buf, len) != (ssize_t)len)
320        return -1;
321
322    return (ssize_t)len;
323}
324
325int
326cdf_read_header(const cdf_info_t *info, cdf_header_t *h)
327{
328    char buf[512];
329
330    (void)memcpy(cdf_bo.s, "\01\02\03\04", 4);
331    if (cdf_read(info, (zend_off_t)0, buf, sizeof(buf)) == -1)
332        return -1;
333    cdf_unpack_header(h, buf);
334    cdf_swap_header(h);
335    if (h->h_magic != CDF_MAGIC) {
336        DPRINTF(("Bad magic 0x%" INT64_T_FORMAT "x != 0x%"
337            INT64_T_FORMAT "x\n",
338            (unsigned long long)h->h_magic,
339            (unsigned long long)CDF_MAGIC));
340        goto out;
341    }
342    if (h->h_sec_size_p2 > 20) {
343        DPRINTF(("Bad sector size 0x%u\n", h->h_sec_size_p2));
344        goto out;
345    }
346    if (h->h_short_sec_size_p2 > 20) {
347        DPRINTF(("Bad short sector size 0x%u\n",
348            h->h_short_sec_size_p2));
349        goto out;
350    }
351    return 0;
352out:
353    errno = EFTYPE;
354    return -1;
355}
356
357
358ssize_t
359cdf_read_sector(const cdf_info_t *info, void *buf, size_t offs, size_t len,
360    const cdf_header_t *h, cdf_secid_t id)
361{
362    size_t ss = CDF_SEC_SIZE(h);
363    size_t pos = CDF_SEC_POS(h, id);
364    assert(ss == len);
365    return cdf_read(info, (zend_off_t)pos, ((char *)buf) + offs, len);
366}
367
368ssize_t
369cdf_read_short_sector(const cdf_stream_t *sst, void *buf, size_t offs,
370    size_t len, const cdf_header_t *h, cdf_secid_t id)
371{
372    size_t ss = CDF_SHORT_SEC_SIZE(h);
373    size_t pos = CDF_SHORT_SEC_POS(h, id);
374    assert(ss == len);
375    if (pos + len > CDF_SEC_SIZE(h) * sst->sst_len) {
376        DPRINTF(("Out of bounds read %" SIZE_T_FORMAT "u > %"
377            SIZE_T_FORMAT "u\n",
378            pos + len, CDF_SEC_SIZE(h) * sst->sst_len));
379        return -1;
380    }
381    (void)memcpy(((char *)buf) + offs,
382        ((const char *)sst->sst_tab) + pos, len);
383    return len;
384}
385
386/*
387 * Read the sector allocation table.
388 */
389int
390cdf_read_sat(const cdf_info_t *info, cdf_header_t *h, cdf_sat_t *sat)
391{
392    size_t i, j, k;
393    size_t ss = CDF_SEC_SIZE(h);
394    cdf_secid_t *msa, mid, sec;
395    size_t nsatpersec = (ss / sizeof(mid)) - 1;
396
397    for (i = 0; i < __arraycount(h->h_master_sat); i++)
398        if (h->h_master_sat[i] == CDF_SECID_FREE)
399            break;
400
401#define CDF_SEC_LIMIT (UINT32_MAX / (4 * ss))
402    if ((nsatpersec > 0 &&
403        h->h_num_sectors_in_master_sat > CDF_SEC_LIMIT / nsatpersec) ||
404        i > CDF_SEC_LIMIT) {
405        DPRINTF(("Number of sectors in master SAT too big %u %"
406            SIZE_T_FORMAT "u\n", h->h_num_sectors_in_master_sat, i));
407        errno = EFTYPE;
408        return -1;
409    }
410
411    sat->sat_len = h->h_num_sectors_in_master_sat * nsatpersec + i;
412    DPRINTF(("sat_len = %" SIZE_T_FORMAT "u ss = %" SIZE_T_FORMAT "u\n",
413        sat->sat_len, ss));
414    if ((sat->sat_tab = CAST(cdf_secid_t *, calloc(sat->sat_len, ss)))
415        == NULL)
416        return -1;
417
418    for (i = 0; i < __arraycount(h->h_master_sat); i++) {
419        if (h->h_master_sat[i] < 0)
420            break;
421        if (cdf_read_sector(info, sat->sat_tab, ss * i, ss, h,
422            h->h_master_sat[i]) != (ssize_t)ss) {
423            DPRINTF(("Reading sector %d", h->h_master_sat[i]));
424            goto out1;
425        }
426    }
427
428    if ((msa = CAST(cdf_secid_t *, calloc(1, ss))) == NULL)
429        goto out1;
430
431    mid = h->h_secid_first_sector_in_master_sat;
432    for (j = 0; j < h->h_num_sectors_in_master_sat; j++) {
433        if (mid < 0)
434            goto out;
435        if (j >= CDF_LOOP_LIMIT) {
436            DPRINTF(("Reading master sector loop limit"));
437            errno = EFTYPE;
438            goto out2;
439        }
440        if (cdf_read_sector(info, msa, 0, ss, h, mid) != (ssize_t)ss) {
441            DPRINTF(("Reading master sector %d", mid));
442            goto out2;
443        }
444        for (k = 0; k < nsatpersec; k++, i++) {
445            sec = CDF_TOLE4((uint32_t)msa[k]);
446            if (sec < 0)
447                goto out;
448            if (i >= sat->sat_len) {
449                DPRINTF(("Out of bounds reading MSA %" SIZE_T_FORMAT
450                "u >= %" SIZE_T_FORMAT "u", i, sat->sat_len));
451                errno = EFTYPE;
452                goto out2;
453            }
454            if (cdf_read_sector(info, sat->sat_tab, ss * i, ss, h,
455                sec) != (ssize_t)ss) {
456                DPRINTF(("Reading sector %d",
457                    CDF_TOLE4(msa[k])));
458                goto out2;
459            }
460        }
461        mid = CDF_TOLE4((uint32_t)msa[nsatpersec]);
462    }
463out:
464    sat->sat_len = i;
465    free(msa);
466    return 0;
467out2:
468    free(msa);
469out1:
470    free(sat->sat_tab);
471    return -1;
472}
473
474size_t
475cdf_count_chain(const cdf_sat_t *sat, cdf_secid_t sid, size_t size)
476{
477    size_t i, j;
478    cdf_secid_t maxsector = (cdf_secid_t)((sat->sat_len * size)
479        / sizeof(maxsector));
480
481    DPRINTF(("Chain:"));
482    if (sid == CDF_SECID_END_OF_CHAIN) {
483        /* 0-length chain. */
484        DPRINTF((" empty\n"));
485        return 0;
486    }
487
488    for (j = i = 0; sid >= 0; i++, j++) {
489        DPRINTF((" %d", sid));
490        if (j >= CDF_LOOP_LIMIT) {
491            DPRINTF(("Counting chain loop limit"));
492            errno = EFTYPE;
493            return (size_t)-1;
494        }
495        if (sid >= maxsector) {
496            DPRINTF(("Sector %d >= %d\n", sid, maxsector));
497            errno = EFTYPE;
498            return (size_t)-1;
499        }
500        sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
501    }
502    if (i == 0) {
503        DPRINTF((" none, sid: %d\n", sid));
504        return (size_t)-1;
505
506    }
507    DPRINTF(("\n"));
508    return i;
509}
510
511int
512cdf_read_long_sector_chain(const cdf_info_t *info, const cdf_header_t *h,
513    const cdf_sat_t *sat, cdf_secid_t sid, size_t len, cdf_stream_t *scn)
514{
515    size_t ss = CDF_SEC_SIZE(h), i, j;
516    ssize_t nr;
517    scn->sst_len = cdf_count_chain(sat, sid, ss);
518    scn->sst_dirlen = len;
519
520    if (scn->sst_len == (size_t)-1)
521        return -1;
522
523    scn->sst_tab = calloc(scn->sst_len, ss);
524    if (scn->sst_tab == NULL)
525        return -1;
526
527    for (j = i = 0; sid >= 0; i++, j++) {
528        if (j >= CDF_LOOP_LIMIT) {
529            DPRINTF(("Read long sector chain loop limit"));
530            errno = EFTYPE;
531            goto out;
532        }
533        if (i >= scn->sst_len) {
534            DPRINTF(("Out of bounds reading long sector chain "
535                "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i,
536                scn->sst_len));
537            errno = EFTYPE;
538            goto out;
539        }
540        if ((nr = cdf_read_sector(info, scn->sst_tab, i * ss, ss, h,
541            sid)) != (ssize_t)ss) {
542            if (i == scn->sst_len - 1 && nr > 0) {
543                /* Last sector might be truncated */
544                return 0;
545            }
546            DPRINTF(("Reading long sector chain %d", sid));
547            goto out;
548        }
549        sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
550    }
551    return 0;
552out:
553    free(scn->sst_tab);
554    return -1;
555}
556
557int
558cdf_read_short_sector_chain(const cdf_header_t *h,
559    const cdf_sat_t *ssat, const cdf_stream_t *sst,
560    cdf_secid_t sid, size_t len, cdf_stream_t *scn)
561{
562    size_t ss = CDF_SHORT_SEC_SIZE(h), i, j;
563    scn->sst_len = cdf_count_chain(ssat, sid, CDF_SEC_SIZE(h));
564    scn->sst_dirlen = len;
565
566    if (sst->sst_tab == NULL || scn->sst_len == (size_t)-1)
567        return -1;
568
569    scn->sst_tab = calloc(scn->sst_len, ss);
570    if (scn->sst_tab == NULL)
571        return -1;
572
573    for (j = i = 0; sid >= 0; i++, j++) {
574        if (j >= CDF_LOOP_LIMIT) {
575            DPRINTF(("Read short sector chain loop limit"));
576            errno = EFTYPE;
577            goto out;
578        }
579        if (i >= scn->sst_len) {
580            DPRINTF(("Out of bounds reading short sector chain "
581                "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n",
582                i, scn->sst_len));
583            errno = EFTYPE;
584            goto out;
585        }
586        if (cdf_read_short_sector(sst, scn->sst_tab, i * ss, ss, h,
587            sid) != (ssize_t)ss) {
588            DPRINTF(("Reading short sector chain %d", sid));
589            goto out;
590        }
591        sid = CDF_TOLE4((uint32_t)ssat->sat_tab[sid]);
592    }
593    return 0;
594out:
595    free(scn->sst_tab);
596    return -1;
597}
598
599int
600cdf_read_sector_chain(const cdf_info_t *info, const cdf_header_t *h,
601    const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
602    cdf_secid_t sid, size_t len, cdf_stream_t *scn)
603{
604
605    if (len < h->h_min_size_standard_stream && sst->sst_tab != NULL)
606        return cdf_read_short_sector_chain(h, ssat, sst, sid, len,
607            scn);
608    else
609        return cdf_read_long_sector_chain(info, h, sat, sid, len, scn);
610}
611
612int
613cdf_read_dir(const cdf_info_t *info, const cdf_header_t *h,
614    const cdf_sat_t *sat, cdf_dir_t *dir)
615{
616    size_t i, j;
617    size_t ss = CDF_SEC_SIZE(h), ns, nd;
618    char *buf;
619    cdf_secid_t sid = h->h_secid_first_directory;
620
621    ns = cdf_count_chain(sat, sid, ss);
622    if (ns == (size_t)-1)
623        return -1;
624
625    nd = ss / CDF_DIRECTORY_SIZE;
626
627    dir->dir_len = ns * nd;
628    dir->dir_tab = CAST(cdf_directory_t *,
629        calloc(dir->dir_len, sizeof(dir->dir_tab[0])));
630    if (dir->dir_tab == NULL)
631        return -1;
632
633    if ((buf = CAST(char *, malloc(ss))) == NULL) {
634        free(dir->dir_tab);
635        return -1;
636    }
637
638    for (j = i = 0; i < ns; i++, j++) {
639        if (j >= CDF_LOOP_LIMIT) {
640            DPRINTF(("Read dir loop limit"));
641            errno = EFTYPE;
642            goto out;
643        }
644        if (cdf_read_sector(info, buf, 0, ss, h, sid) != (ssize_t)ss) {
645            DPRINTF(("Reading directory sector %d", sid));
646            goto out;
647        }
648        for (j = 0; j < nd; j++) {
649            cdf_unpack_dir(&dir->dir_tab[i * nd + j],
650                &buf[j * CDF_DIRECTORY_SIZE]);
651        }
652        sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
653    }
654    if (NEED_SWAP)
655        for (i = 0; i < dir->dir_len; i++)
656            cdf_swap_dir(&dir->dir_tab[i]);
657    free(buf);
658    return 0;
659out:
660    free(dir->dir_tab);
661    free(buf);
662    return -1;
663}
664
665
666int
667cdf_read_ssat(const cdf_info_t *info, const cdf_header_t *h,
668    const cdf_sat_t *sat, cdf_sat_t *ssat)
669{
670    size_t i, j;
671    size_t ss = CDF_SEC_SIZE(h);
672    cdf_secid_t sid = h->h_secid_first_sector_in_short_sat;
673
674    ssat->sat_len = cdf_count_chain(sat, sid, CDF_SEC_SIZE(h));
675    if (ssat->sat_len == (size_t)-1)
676        return -1;
677
678    ssat->sat_tab = CAST(cdf_secid_t *, calloc(ssat->sat_len, ss));
679    if (ssat->sat_tab == NULL)
680        return -1;
681
682    for (j = i = 0; sid >= 0; i++, j++) {
683        if (j >= CDF_LOOP_LIMIT) {
684            DPRINTF(("Read short sat sector loop limit"));
685            errno = EFTYPE;
686            goto out;
687        }
688        if (i >= ssat->sat_len) {
689            DPRINTF(("Out of bounds reading short sector chain "
690                "%" SIZE_T_FORMAT "u > %" SIZE_T_FORMAT "u\n", i,
691                ssat->sat_len));
692            errno = EFTYPE;
693            goto out;
694        }
695        if (cdf_read_sector(info, ssat->sat_tab, i * ss, ss, h, sid) !=
696            (ssize_t)ss) {
697            DPRINTF(("Reading short sat sector %d", sid));
698            goto out;
699        }
700        sid = CDF_TOLE4((uint32_t)sat->sat_tab[sid]);
701    }
702    return 0;
703out:
704    free(ssat->sat_tab);
705    return -1;
706}
707
708int
709cdf_read_short_stream(const cdf_info_t *info, const cdf_header_t *h,
710    const cdf_sat_t *sat, const cdf_dir_t *dir, cdf_stream_t *scn,
711    const cdf_directory_t **root)
712{
713    size_t i;
714    const cdf_directory_t *d;
715
716    *root = NULL;
717    for (i = 0; i < dir->dir_len; i++)
718        if (dir->dir_tab[i].d_type == CDF_DIR_TYPE_ROOT_STORAGE)
719            break;
720
721    /* If the it is not there, just fake it; some docs don't have it */
722    if (i == dir->dir_len)
723        goto out;
724    d = &dir->dir_tab[i];
725    *root = d;
726
727    /* If the it is not there, just fake it; some docs don't have it */
728    if (d->d_stream_first_sector < 0)
729        goto out;
730
731    return  cdf_read_long_sector_chain(info, h, sat,
732        d->d_stream_first_sector, d->d_size, scn);
733out:
734    scn->sst_tab = NULL;
735    scn->sst_len = 0;
736    scn->sst_dirlen = 0;
737    return 0;
738}
739
740static int
741cdf_namecmp(const char *d, const uint16_t *s, size_t l)
742{
743    for (; l--; d++, s++)
744        if (*d != CDF_TOLE2(*s))
745            return (unsigned char)*d - CDF_TOLE2(*s);
746    return 0;
747}
748
749int
750cdf_read_summary_info(const cdf_info_t *info, const cdf_header_t *h,
751    const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
752    const cdf_dir_t *dir, cdf_stream_t *scn)
753{
754    return cdf_read_user_stream(info, h, sat, ssat, sst, dir,
755        "\05SummaryInformation", scn);
756}
757
758int
759cdf_read_user_stream(const cdf_info_t *info, const cdf_header_t *h,
760    const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
761    const cdf_dir_t *dir, const char *name, cdf_stream_t *scn)
762{
763    const cdf_directory_t *d;
764    int i = cdf_find_stream(dir, name, CDF_DIR_TYPE_USER_STREAM);
765
766    if (i <= 0)
767        return -1;
768
769    d = &dir->dir_tab[i - 1];
770    return cdf_read_sector_chain(info, h, sat, ssat, sst,
771        d->d_stream_first_sector, d->d_size, scn);
772}
773
774int
775cdf_find_stream(const cdf_dir_t *dir, const char *name, int type)
776{
777    size_t i, name_len = strlen(name) + 1;
778
779    for (i = dir->dir_len; i > 0; i--)
780        if (dir->dir_tab[i - 1].d_type == type &&
781            cdf_namecmp(name, dir->dir_tab[i - 1].d_name, name_len)
782            == 0)
783            break;
784    if (i > 0)
785        return i;
786
787    DPRINTF(("Cannot find type %d `%s'\n", type, name));
788    errno = ESRCH;
789    return 0;
790}
791
792int
793cdf_read_property_info(const cdf_stream_t *sst, const cdf_header_t *h,
794    uint32_t offs, cdf_property_info_t **info, size_t *count, size_t *maxcount)
795{
796    const cdf_section_header_t *shp;
797    cdf_section_header_t sh;
798    const uint8_t *p, *q, *e;
799    int16_t s16;
800    int32_t s32;
801    uint32_t u32;
802    int64_t s64;
803    uint64_t u64;
804    cdf_timestamp_t tp;
805    size_t i, o, o4, nelements, j;
806    cdf_property_info_t *inp;
807
808    if (offs > UINT32_MAX / 4) {
809        errno = EFTYPE;
810        goto out;
811    }
812    shp = CAST(const cdf_section_header_t *, (const void *)
813        ((const char *)sst->sst_tab + offs));
814    if (cdf_check_stream_offset(sst, h, shp, sizeof(*shp), __LINE__) == -1)
815        goto out;
816    sh.sh_len = CDF_TOLE4(shp->sh_len);
817#define CDF_SHLEN_LIMIT (UINT32_MAX / 8)
818    if (sh.sh_len > CDF_SHLEN_LIMIT) {
819        errno = EFTYPE;
820        goto out;
821    }
822    sh.sh_properties = CDF_TOLE4(shp->sh_properties);
823#define CDF_PROP_LIMIT (UINT32_MAX / (4 * sizeof(*inp)))
824    if (sh.sh_properties > CDF_PROP_LIMIT)
825        goto out;
826    DPRINTF(("section len: %u properties %u\n", sh.sh_len,
827        sh.sh_properties));
828    if (*maxcount) {
829        if (*maxcount > CDF_PROP_LIMIT)
830            goto out;
831        *maxcount += sh.sh_properties;
832        inp = CAST(cdf_property_info_t *,
833            realloc(*info, *maxcount * sizeof(*inp)));
834    } else {
835        *maxcount = sh.sh_properties;
836        inp = CAST(cdf_property_info_t *,
837            malloc(*maxcount * sizeof(*inp)));
838    }
839    if (inp == NULL)
840        goto out;
841    *info = inp;
842    inp += *count;
843    *count += sh.sh_properties;
844    p = CAST(const uint8_t *, (const void *)
845        ((const char *)(const void *)sst->sst_tab +
846        offs + sizeof(sh)));
847    e = CAST(const uint8_t *, (const void *)
848        (((const char *)(const void *)shp) + sh.sh_len));
849    if (cdf_check_stream_offset(sst, h, e, 0, __LINE__) == -1)
850        goto out;
851    for (i = 0; i < sh.sh_properties; i++) {
852        size_t tail = (i << 1) + 1;
853        size_t ofs;
854        if (cdf_check_stream_offset(sst, h, p, tail * sizeof(uint32_t),
855            __LINE__) == -1)
856            goto out;
857        ofs = CDF_GETUINT32(p, tail);
858        q = (const uint8_t *)(const void *)
859            ((const char *)(const void *)p + ofs
860            - 2 * sizeof(uint32_t));
861        if (q < p) {
862            DPRINTF(("Wrapped around %p < %p\n", q, p));
863            goto out;
864        }
865        if (q > e) {
866            DPRINTF(("Ran of the end %p > %p\n", q, e));
867            goto out;
868        }
869        inp[i].pi_id = CDF_GETUINT32(p, i << 1);
870        inp[i].pi_type = CDF_GETUINT32(q, 0);
871        DPRINTF(("%" SIZE_T_FORMAT "u) id=%x type=%x offs=0x%tx,0x%x\n",
872            i, inp[i].pi_id, inp[i].pi_type, q - p, offs));
873        if (inp[i].pi_type & CDF_VECTOR) {
874            nelements = CDF_GETUINT32(q, 1);
875            if (nelements == 0) {
876                DPRINTF(("CDF_VECTOR with nelements == 0\n"));
877                goto out;
878            }
879            o = 2;
880        } else {
881            nelements = 1;
882            o = 1;
883        }
884        o4 = o * sizeof(uint32_t);
885        if (inp[i].pi_type & (CDF_ARRAY|CDF_BYREF|CDF_RESERVED))
886            goto unknown;
887        switch (inp[i].pi_type & CDF_TYPEMASK) {
888        case CDF_NULL:
889        case CDF_EMPTY:
890            break;
891        case CDF_SIGNED16:
892            if (inp[i].pi_type & CDF_VECTOR)
893                goto unknown;
894            (void)memcpy(&s16, &q[o4], sizeof(s16));
895            inp[i].pi_s16 = CDF_TOLE2(s16);
896            break;
897        case CDF_SIGNED32:
898            if (inp[i].pi_type & CDF_VECTOR)
899                goto unknown;
900            (void)memcpy(&s32, &q[o4], sizeof(s32));
901            inp[i].pi_s32 = CDF_TOLE4((uint32_t)s32);
902            break;
903        case CDF_BOOL:
904        case CDF_UNSIGNED32:
905            if (inp[i].pi_type & CDF_VECTOR)
906                goto unknown;
907            (void)memcpy(&u32, &q[o4], sizeof(u32));
908            inp[i].pi_u32 = CDF_TOLE4(u32);
909            break;
910        case CDF_SIGNED64:
911            if (inp[i].pi_type & CDF_VECTOR)
912                goto unknown;
913            (void)memcpy(&s64, &q[o4], sizeof(s64));
914            inp[i].pi_s64 = CDF_TOLE8((uint64_t)s64);
915            break;
916        case CDF_UNSIGNED64:
917            if (inp[i].pi_type & CDF_VECTOR)
918                goto unknown;
919            (void)memcpy(&u64, &q[o4], sizeof(u64));
920            inp[i].pi_u64 = CDF_TOLE8((uint64_t)u64);
921            break;
922        case CDF_FLOAT:
923            if (inp[i].pi_type & CDF_VECTOR)
924                goto unknown;
925            (void)memcpy(&u32, &q[o4], sizeof(u32));
926            u32 = CDF_TOLE4(u32);
927            memcpy(&inp[i].pi_f, &u32, sizeof(inp[i].pi_f));
928            break;
929        case CDF_DOUBLE:
930            if (inp[i].pi_type & CDF_VECTOR)
931                goto unknown;
932            (void)memcpy(&u64, &q[o4], sizeof(u64));
933            u64 = CDF_TOLE8((uint64_t)u64);
934            memcpy(&inp[i].pi_d, &u64, sizeof(inp[i].pi_d));
935            break;
936        case CDF_LENGTH32_STRING:
937        case CDF_LENGTH32_WSTRING:
938            if (nelements > 1) {
939                size_t nelem = inp - *info;
940                if (*maxcount > CDF_PROP_LIMIT
941                    || nelements > CDF_PROP_LIMIT)
942                    goto out;
943                *maxcount += nelements;
944                inp = CAST(cdf_property_info_t *,
945                    realloc(*info, *maxcount * sizeof(*inp)));
946                if (inp == NULL)
947                    goto out;
948                *info = inp;
949                inp = *info + nelem;
950            }
951            DPRINTF(("nelements = %" SIZE_T_FORMAT "u\n",
952                nelements));
953            for (j = 0; j < nelements && i < sh.sh_properties;
954                j++, i++)
955            {
956                uint32_t l = CDF_GETUINT32(q, o);
957                inp[i].pi_str.s_len = l;
958                inp[i].pi_str.s_buf = (const char *)
959                    (const void *)(&q[o4 + sizeof(l)]);
960                DPRINTF(("l = %d, r = %" SIZE_T_FORMAT
961                    "u, s = %s\n", l,
962                    CDF_ROUND(l, sizeof(l)),
963                    inp[i].pi_str.s_buf));
964                if (l & 1)
965                    l++;
966                o += l >> 1;
967                if (q + o >= e)
968                    goto out;
969                o4 = o * sizeof(uint32_t);
970            }
971            i--;
972            break;
973        case CDF_FILETIME:
974            if (inp[i].pi_type & CDF_VECTOR)
975                goto unknown;
976            (void)memcpy(&tp, &q[o4], sizeof(tp));
977            inp[i].pi_tp = CDF_TOLE8((uint64_t)tp);
978            break;
979        case CDF_CLIPBOARD:
980            if (inp[i].pi_type & CDF_VECTOR)
981                goto unknown;
982            break;
983        default:
984        unknown:
985            DPRINTF(("Don't know how to deal with %x\n",
986                inp[i].pi_type));
987            break;
988        }
989    }
990    return 0;
991out:
992    free(*info);
993    return -1;
994}
995
996int
997cdf_unpack_summary_info(const cdf_stream_t *sst, const cdf_header_t *h,
998    cdf_summary_info_header_t *ssi, cdf_property_info_t **info, size_t *count)
999{
1000    size_t maxcount;
1001    const cdf_summary_info_header_t *si =
1002        CAST(const cdf_summary_info_header_t *, sst->sst_tab);
1003    const cdf_section_declaration_t *sd =
1004        CAST(const cdf_section_declaration_t *, (const void *)
1005        ((const char *)sst->sst_tab + CDF_SECTION_DECLARATION_OFFSET));
1006
1007    if (cdf_check_stream_offset(sst, h, si, sizeof(*si), __LINE__) == -1 ||
1008        cdf_check_stream_offset(sst, h, sd, sizeof(*sd), __LINE__) == -1)
1009        return -1;
1010    ssi->si_byte_order = CDF_TOLE2(si->si_byte_order);
1011    ssi->si_os_version = CDF_TOLE2(si->si_os_version);
1012    ssi->si_os = CDF_TOLE2(si->si_os);
1013    ssi->si_class = si->si_class;
1014    cdf_swap_class(&ssi->si_class);
1015    ssi->si_count = CDF_TOLE4(si->si_count);
1016    *count = 0;
1017    maxcount = 0;
1018    *info = NULL;
1019    if (cdf_read_property_info(sst, h, CDF_TOLE4(sd->sd_offset), info,
1020        count, &maxcount) == -1)
1021        return -1;
1022    return 0;
1023}
1024
1025
1026#define extract_catalog_field(t, f, l) \
1027    if (b + l + sizeof(cep->f) > eb) { \
1028        cep->ce_namlen = 0; \
1029        break; \
1030    } \
1031    memcpy(&cep->f, b + (l), sizeof(cep->f)); \
1032    ce[i].f = CAST(t, CDF_TOLE(cep->f))
1033
1034int
1035cdf_unpack_catalog(const cdf_header_t *h, const cdf_stream_t *sst,
1036    cdf_catalog_t **cat)
1037{
1038    size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ?
1039        CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h);
1040    const char *b = CAST(const char *, sst->sst_tab);
1041    const char *eb = b + ss * sst->sst_len;
1042    size_t nr, i, k;
1043    cdf_catalog_entry_t *ce;
1044    uint16_t reclen;
1045    const uint16_t *np;
1046
1047    for (nr = 0; b < eb; nr++) {
1048        memcpy(&reclen, b, sizeof(reclen));
1049        reclen = CDF_TOLE2(reclen);
1050        if (reclen == 0)
1051            break;
1052        b += reclen;
1053    }
1054    *cat = CAST(cdf_catalog_t *,
1055        malloc(sizeof(cdf_catalog_t) + nr * sizeof(*ce)));
1056    (*cat)->cat_num = nr;
1057    ce = (*cat)->cat_e;
1058    memset(ce, 0, nr * sizeof(*ce));
1059    b = CAST(const char *, sst->sst_tab);
1060    for (i = 0; i < nr; i++, b += reclen) {
1061        cdf_catalog_entry_t *cep = &ce[i];
1062        uint16_t rlen;
1063
1064        extract_catalog_field(uint16_t, ce_namlen, 0);
1065        extract_catalog_field(uint16_t, ce_num, 2);
1066        extract_catalog_field(uint64_t, ce_timestamp, 6);
1067        reclen = cep->ce_namlen;
1068
1069        if (reclen < 14) {
1070            cep->ce_namlen = 0;
1071            continue;
1072        }
1073
1074        cep->ce_namlen = __arraycount(cep->ce_name) - 1;
1075        rlen = reclen - 14;
1076        if (cep->ce_namlen > rlen)
1077            cep->ce_namlen = rlen;
1078
1079        np = CAST(const uint16_t *, CAST(const void *, (b + 16)));
1080        if (CAST(const char *, np + cep->ce_namlen) > eb) {
1081            cep->ce_namlen = 0;
1082            break;
1083        }
1084
1085        for (k = 0; k < cep->ce_namlen; k++)
1086            cep->ce_name[k] = np[k]; /* XXX: CDF_TOLE2? */
1087        cep->ce_name[cep->ce_namlen] = 0;
1088    }
1089    return 0;
1090}
1091
1092int
1093cdf_print_classid(char *buf, size_t buflen, const cdf_classid_t *id)
1094{
1095    return snprintf(buf, buflen, "%.8x-%.4x-%.4x-%.2x%.2x-"
1096        "%.2x%.2x%.2x%.2x%.2x%.2x", id->cl_dword, id->cl_word[0],
1097        id->cl_word[1], id->cl_two[0], id->cl_two[1], id->cl_six[0],
1098        id->cl_six[1], id->cl_six[2], id->cl_six[3], id->cl_six[4],
1099        id->cl_six[5]);
1100}
1101
1102static const struct {
1103    uint32_t v;
1104    const char *n;
1105} vn[] = {
1106    { CDF_PROPERTY_CODE_PAGE, "Code page" },
1107    { CDF_PROPERTY_TITLE, "Title" },
1108    { CDF_PROPERTY_SUBJECT, "Subject" },
1109    { CDF_PROPERTY_AUTHOR, "Author" },
1110    { CDF_PROPERTY_KEYWORDS, "Keywords" },
1111    { CDF_PROPERTY_COMMENTS, "Comments" },
1112    { CDF_PROPERTY_TEMPLATE, "Template" },
1113    { CDF_PROPERTY_LAST_SAVED_BY, "Last Saved By" },
1114    { CDF_PROPERTY_REVISION_NUMBER, "Revision Number" },
1115    { CDF_PROPERTY_TOTAL_EDITING_TIME, "Total Editing Time" },
1116    { CDF_PROPERTY_LAST_PRINTED, "Last Printed" },
1117    { CDF_PROPERTY_CREATE_TIME, "Create Time/Date" },
1118    { CDF_PROPERTY_LAST_SAVED_TIME, "Last Saved Time/Date" },
1119    { CDF_PROPERTY_NUMBER_OF_PAGES, "Number of Pages" },
1120    { CDF_PROPERTY_NUMBER_OF_WORDS, "Number of Words" },
1121    { CDF_PROPERTY_NUMBER_OF_CHARACTERS, "Number of Characters" },
1122    { CDF_PROPERTY_THUMBNAIL, "Thumbnail" },
1123    { CDF_PROPERTY_NAME_OF_APPLICATION, "Name of Creating Application" },
1124    { CDF_PROPERTY_SECURITY, "Security" },
1125    { CDF_PROPERTY_LOCALE_ID, "Locale ID" },
1126};
1127
1128int
1129cdf_print_property_name(char *buf, size_t bufsiz, uint32_t p)
1130{
1131    size_t i;
1132
1133    for (i = 0; i < __arraycount(vn); i++)
1134        if (vn[i].v == p)
1135            return snprintf(buf, bufsiz, "%s", vn[i].n);
1136    return snprintf(buf, bufsiz, "0x%x", p);
1137}
1138
1139int
1140cdf_print_elapsed_time(char *buf, size_t bufsiz, cdf_timestamp_t ts)
1141{
1142    int len = 0;
1143    int days, hours, mins, secs;
1144
1145    ts /= CDF_TIME_PREC;
1146    secs = (int)(ts % 60);
1147    ts /= 60;
1148    mins = (int)(ts % 60);
1149    ts /= 60;
1150    hours = (int)(ts % 24);
1151    ts /= 24;
1152    days = (int)ts;
1153
1154    if (days) {
1155        len += snprintf(buf + len, bufsiz - len, "%dd+", days);
1156        if ((size_t)len >= bufsiz)
1157            return len;
1158    }
1159
1160    if (days || hours) {
1161        len += snprintf(buf + len, bufsiz - len, "%.2d:", hours);
1162        if ((size_t)len >= bufsiz)
1163            return len;
1164    }
1165
1166    len += snprintf(buf + len, bufsiz - len, "%.2d:", mins);
1167    if ((size_t)len >= bufsiz)
1168        return len;
1169
1170    len += snprintf(buf + len, bufsiz - len, "%.2d", secs);
1171    return len;
1172}
1173
1174char *
1175cdf_u16tos8(char *buf, size_t len, const uint16_t *p)
1176{
1177    size_t i;
1178    for (i = 0; i < len && p[i]; i++)
1179        buf[i] = (char)p[i];
1180    buf[i] = '\0';
1181    return buf;
1182}
1183
1184#ifdef CDF_DEBUG
1185void
1186cdf_dump_header(const cdf_header_t *h)
1187{
1188    size_t i;
1189
1190#define DUMP(a, b) (void)fprintf(stderr, "%40.40s = " a "\n", # b, h->h_ ## b)
1191#define DUMP2(a, b) (void)fprintf(stderr, "%40.40s = " a " (" a ")\n", # b, \
1192    h->h_ ## b, 1 << h->h_ ## b)
1193    DUMP("%d", revision);
1194    DUMP("%d", version);
1195    DUMP("0x%x", byte_order);
1196    DUMP2("%d", sec_size_p2);
1197    DUMP2("%d", short_sec_size_p2);
1198    DUMP("%d", num_sectors_in_sat);
1199    DUMP("%d", secid_first_directory);
1200    DUMP("%d", min_size_standard_stream);
1201    DUMP("%d", secid_first_sector_in_short_sat);
1202    DUMP("%d", num_sectors_in_short_sat);
1203    DUMP("%d", secid_first_sector_in_master_sat);
1204    DUMP("%d", num_sectors_in_master_sat);
1205    for (i = 0; i < __arraycount(h->h_master_sat); i++) {
1206        if (h->h_master_sat[i] == CDF_SECID_FREE)
1207            break;
1208        (void)fprintf(stderr, "%35.35s[%.3" SIZE_T_FORMAT "u] = %d\n",
1209            "master_sat", i, h->h_master_sat[i]);
1210    }
1211}
1212
1213void
1214cdf_dump_sat(const char *prefix, const cdf_sat_t *sat, size_t size)
1215{
1216    size_t i, j, s = size / sizeof(cdf_secid_t);
1217
1218    for (i = 0; i < sat->sat_len; i++) {
1219        (void)fprintf(stderr, "%s[%" SIZE_T_FORMAT "u]:\n%.6"
1220            SIZE_T_FORMAT "u: ", prefix, i, i * s);
1221        for (j = 0; j < s; j++) {
1222            (void)fprintf(stderr, "%5d, ",
1223                CDF_TOLE4(sat->sat_tab[s * i + j]));
1224            if ((j + 1) % 10 == 0)
1225                (void)fprintf(stderr, "\n%.6" SIZE_T_FORMAT
1226                    "u: ", i * s + j + 1);
1227        }
1228        (void)fprintf(stderr, "\n");
1229    }
1230}
1231
1232void
1233cdf_dump(const void *v, size_t len)
1234{
1235    size_t i, j;
1236    const unsigned char *p = v;
1237    char abuf[16];
1238
1239    (void)fprintf(stderr, "%.4x: ", 0);
1240    for (i = 0, j = 0; i < len; i++, p++) {
1241        (void)fprintf(stderr, "%.2x ", *p);
1242        abuf[j++] = isprint(*p) ? *p : '.';
1243        if (j == 16) {
1244            j = 0;
1245            abuf[15] = '\0';
1246            (void)fprintf(stderr, "%s\n%.4" SIZE_T_FORMAT "x: ",
1247                abuf, i + 1);
1248        }
1249    }
1250    (void)fprintf(stderr, "\n");
1251}
1252
1253void
1254cdf_dump_stream(const cdf_header_t *h, const cdf_stream_t *sst)
1255{
1256    size_t ss = sst->sst_dirlen < h->h_min_size_standard_stream ?
1257        CDF_SHORT_SEC_SIZE(h) : CDF_SEC_SIZE(h);
1258    cdf_dump(sst->sst_tab, ss * sst->sst_len);
1259}
1260
1261void
1262cdf_dump_dir(const cdf_info_t *info, const cdf_header_t *h,
1263    const cdf_sat_t *sat, const cdf_sat_t *ssat, const cdf_stream_t *sst,
1264    const cdf_dir_t *dir)
1265{
1266    size_t i, j;
1267    cdf_directory_t *d;
1268    char name[__arraycount(d->d_name)];
1269    cdf_stream_t scn;
1270    struct timeval ts;
1271
1272    static const char *types[] = { "empty", "user storage",
1273        "user stream", "lockbytes", "property", "root storage" };
1274
1275    for (i = 0; i < dir->dir_len; i++) {
1276        char buf[26];
1277        d = &dir->dir_tab[i];
1278        for (j = 0; j < sizeof(name); j++)
1279            name[j] = (char)CDF_TOLE2(d->d_name[j]);
1280        (void)fprintf(stderr, "Directory %" SIZE_T_FORMAT "u: %s\n",
1281            i, name);
1282        if (d->d_type < __arraycount(types))
1283            (void)fprintf(stderr, "Type: %s\n", types[d->d_type]);
1284        else
1285            (void)fprintf(stderr, "Type: %d\n", d->d_type);
1286        (void)fprintf(stderr, "Color: %s\n",
1287            d->d_color ? "black" : "red");
1288        (void)fprintf(stderr, "Left child: %d\n", d->d_left_child);
1289        (void)fprintf(stderr, "Right child: %d\n", d->d_right_child);
1290        (void)fprintf(stderr, "Flags: 0x%x\n", d->d_flags);
1291        cdf_timestamp_to_timespec(&ts, d->d_created);
1292        (void)fprintf(stderr, "Created %s", cdf_ctime(&ts.tv_sec, buf));
1293        cdf_timestamp_to_timespec(&ts, d->d_modified);
1294        (void)fprintf(stderr, "Modified %s",
1295            cdf_ctime(&ts.tv_sec, buf));
1296        (void)fprintf(stderr, "Stream %d\n", d->d_stream_first_sector);
1297        (void)fprintf(stderr, "Size %d\n", d->d_size);
1298        switch (d->d_type) {
1299        case CDF_DIR_TYPE_USER_STORAGE:
1300            (void)fprintf(stderr, "Storage: %d\n", d->d_storage);
1301            break;
1302        case CDF_DIR_TYPE_USER_STREAM:
1303            if (sst == NULL)
1304                break;
1305            if (cdf_read_sector_chain(info, h, sat, ssat, sst,
1306                d->d_stream_first_sector, d->d_size, &scn) == -1) {
1307                warn("Can't read stream for %s at %d len %d",
1308                    name, d->d_stream_first_sector, d->d_size);
1309                break;
1310            }
1311            cdf_dump_stream(h, &scn);
1312            free(scn.sst_tab);
1313            break;
1314        default:
1315            break;
1316        }
1317
1318    }
1319}
1320
1321void
1322cdf_dump_property_info(const cdf_property_info_t *info, size_t count)
1323{
1324    cdf_timestamp_t tp;
1325    struct timeval ts;
1326    char buf[64];
1327    size_t i, j;
1328
1329    for (i = 0; i < count; i++) {
1330        cdf_print_property_name(buf, sizeof(buf), info[i].pi_id);
1331        (void)fprintf(stderr, "%" SIZE_T_FORMAT "u) %s: ", i, buf);
1332        switch (info[i].pi_type) {
1333        case CDF_NULL:
1334            break;
1335        case CDF_SIGNED16:
1336            (void)fprintf(stderr, "signed 16 [%hd]\n",
1337                info[i].pi_s16);
1338            break;
1339        case CDF_SIGNED32:
1340            (void)fprintf(stderr, "signed 32 [%d]\n",
1341                info[i].pi_s32);
1342            break;
1343        case CDF_UNSIGNED32:
1344            (void)fprintf(stderr, "unsigned 32 [%u]\n",
1345                info[i].pi_u32);
1346            break;
1347        case CDF_FLOAT:
1348            (void)fprintf(stderr, "float [%g]\n",
1349                info[i].pi_f);
1350            break;
1351        case CDF_DOUBLE:
1352            (void)fprintf(stderr, "double [%g]\n",
1353                info[i].pi_d);
1354            break;
1355        case CDF_LENGTH32_STRING:
1356            (void)fprintf(stderr, "string %u [%.*s]\n",
1357                info[i].pi_str.s_len,
1358                info[i].pi_str.s_len, info[i].pi_str.s_buf);
1359            break;
1360        case CDF_LENGTH32_WSTRING:
1361            (void)fprintf(stderr, "string %u [",
1362                info[i].pi_str.s_len);
1363            for (j = 0; j < info[i].pi_str.s_len - 1; j++)
1364                (void)fputc(info[i].pi_str.s_buf[j << 1], stderr);
1365            (void)fprintf(stderr, "]\n");
1366            break;
1367        case CDF_FILETIME:
1368            tp = info[i].pi_tp;
1369            if (tp < 1000000000000000LL) {
1370                cdf_print_elapsed_time(buf, sizeof(buf), tp);
1371                (void)fprintf(stderr, "timestamp %s\n", buf);
1372            } else {
1373                char tbuf[26];
1374                cdf_timestamp_to_timespec(&ts, tp);
1375                (void)fprintf(stderr, "timestamp %s",
1376                    cdf_ctime(&ts.tv_sec, tbuf));
1377            }
1378            break;
1379        case CDF_CLIPBOARD:
1380            (void)fprintf(stderr, "CLIPBOARD %u\n", info[i].pi_u32);
1381            break;
1382        default:
1383            DPRINTF(("Don't know how to deal with %x\n",
1384                info[i].pi_type));
1385            break;
1386        }
1387    }
1388}
1389
1390
1391void
1392cdf_dump_summary_info(const cdf_header_t *h, const cdf_stream_t *sst)
1393{
1394    char buf[128];
1395    cdf_summary_info_header_t ssi;
1396    cdf_property_info_t *info;
1397    size_t count;
1398
1399    (void)&h;
1400    if (cdf_unpack_summary_info(sst, h, &ssi, &info, &count) == -1)
1401        return;
1402    (void)fprintf(stderr, "Endian: %x\n", ssi.si_byte_order);
1403    (void)fprintf(stderr, "Os Version %d.%d\n", ssi.si_os_version & 0xff,
1404        ssi.si_os_version >> 8);
1405    (void)fprintf(stderr, "Os %d\n", ssi.si_os);
1406    cdf_print_classid(buf, sizeof(buf), &ssi.si_class);
1407    (void)fprintf(stderr, "Class %s\n", buf);
1408    (void)fprintf(stderr, "Count %d\n", ssi.si_count);
1409    cdf_dump_property_info(info, count);
1410    free(info);
1411}
1412
1413
1414void
1415cdf_dump_catalog(const cdf_header_t *h, const cdf_stream_t *sst)
1416{
1417    cdf_catalog_t *cat;
1418    cdf_unpack_catalog(h, sst, &cat);
1419    const cdf_catalog_entry_t *ce = cat->cat_e;
1420    struct timespec ts;
1421    char tbuf[64], sbuf[256];
1422    size_t i;
1423
1424    printf("Catalog:\n");
1425    for (i = 0; i < cat->cat_num; i++) {
1426        cdf_timestamp_to_timespec(&ts, ce[i].ce_timestamp);
1427        printf("\t%d %s %s", ce[i].ce_num,
1428            cdf_u16tos8(sbuf, ce[i].ce_namlen, ce[i].ce_name),
1429            cdf_ctime(&ts.tv_sec, tbuf));
1430    }
1431    free(cat);
1432}
1433
1434#endif
1435
1436#ifdef TEST
1437int
1438main(int argc, char *argv[])
1439{
1440    int i;
1441    cdf_header_t h;
1442    cdf_sat_t sat, ssat;
1443    cdf_stream_t sst, scn;
1444    cdf_dir_t dir;
1445    cdf_info_t info;
1446    const cdf_directory_t *root;
1447
1448    if (argc < 2) {
1449        (void)fprintf(stderr, "Usage: %s <filename>\n", getprogname());
1450        return -1;
1451    }
1452
1453    info.i_buf = NULL;
1454    info.i_len = 0;
1455    for (i = 1; i < argc; i++) {
1456        if ((info.i_fd = open(argv[1], O_RDONLY)) == -1)
1457            err(1, "Cannot open `%s'", argv[1]);
1458
1459        if (cdf_read_header(&info, &h) == -1)
1460            err(1, "Cannot read header");
1461#ifdef CDF_DEBUG
1462        cdf_dump_header(&h);
1463#endif
1464
1465        if (cdf_read_sat(&info, &h, &sat) == -1)
1466            err(1, "Cannot read sat");
1467#ifdef CDF_DEBUG
1468        cdf_dump_sat("SAT", &sat, CDF_SEC_SIZE(&h));
1469#endif
1470
1471        if (cdf_read_ssat(&info, &h, &sat, &ssat) == -1)
1472            err(1, "Cannot read ssat");
1473#ifdef CDF_DEBUG
1474        cdf_dump_sat("SSAT", &ssat, CDF_SHORT_SEC_SIZE(&h));
1475#endif
1476
1477        if (cdf_read_dir(&info, &h, &sat, &dir) == -1)
1478            err(1, "Cannot read dir");
1479
1480        if (cdf_read_short_stream(&info, &h, &sat, &dir, &sst, &root)
1481            == -1)
1482            err(1, "Cannot read short stream");
1483#ifdef CDF_DEBUG
1484        cdf_dump_stream(&h, &sst);
1485#endif
1486
1487#ifdef CDF_DEBUG
1488        cdf_dump_dir(&info, &h, &sat, &ssat, &sst, &dir);
1489#endif
1490
1491
1492        if (cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir,
1493            &scn) == -1)
1494            warn("Cannot read summary info");
1495#ifdef CDF_DEBUG
1496        else
1497            cdf_dump_summary_info(&h, &scn);
1498#endif
1499        if (cdf_read_catalog(&info, &h, &sat, &ssat, &sst, &dir,
1500            &scn) == -1)
1501            warn("Cannot read catalog");
1502#ifdef CDF_DEBUG
1503        else
1504            cdf_dump_catalog(&h, &scn);
1505#endif
1506
1507        (void)close(info.i_fd);
1508    }
1509
1510    return 0;
1511}
1512#endif
1513