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