1 /*-
2 * Copyright (c) 2008, 2016 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 #include "file.h"
27
28 #ifndef lint
29 FILE_RCSID("@(#)$File: readcdf.c,v 1.74 2019/09/11 15:46:30 christos Exp $")
30 #endif
31
32 #include <assert.h>
33 #include <stdlib.h>
34 #ifdef PHP_WIN32
35 #include "win32/unistd.h"
36 #else
37 #include <unistd.h>
38 #endif
39 #include <string.h>
40 #include <time.h>
41 #include <ctype.h>
42
43 #include "cdf.h"
44 #include "magic.h"
45
46 #define NOTMIME(ms) (((ms)->flags & MAGIC_MIME) == 0)
47
48 static const struct nv {
49 const char *pattern;
50 const char *mime;
51 } app2mime[] = {
52 { "Word", "msword", },
53 { "Excel", "vnd.ms-excel", },
54 { "Powerpoint", "vnd.ms-powerpoint", },
55 { "Crystal Reports", "x-rpt", },
56 { "Advanced Installer", "vnd.ms-msi", },
57 { "InstallShield", "vnd.ms-msi", },
58 { "Microsoft Patch Compiler", "vnd.ms-msi", },
59 { "NAnt", "vnd.ms-msi", },
60 { "Windows Installer", "vnd.ms-msi", },
61 { NULL, NULL, },
62 }, name2mime[] = {
63 { "Book", "vnd.ms-excel", },
64 { "Workbook", "vnd.ms-excel", },
65 { "WordDocument", "msword", },
66 { "PowerPoint", "vnd.ms-powerpoint", },
67 { "DigitalSignature", "vnd.ms-msi", },
68 { NULL, NULL, },
69 }, name2desc[] = {
70 { "Book", "Microsoft Excel", },
71 { "Workbook", "Microsoft Excel", },
72 { "WordDocument", "Microsoft Word", },
73 { "PowerPoint", "Microsoft PowerPoint", },
74 { "DigitalSignature", "Microsoft Installer", },
75 { NULL, NULL, },
76 };
77
78 static const struct cv {
79 uint64_t clsid[2];
80 const char *mime;
81 } clsid2mime[] = {
82 {
83 { 0x00000000000c1084ULL, 0x46000000000000c0ULL },
84 "x-msi",
85 },
86 { { 0, 0 },
87 NULL,
88 },
89 }, clsid2desc[] = {
90 {
91 { 0x00000000000c1084ULL, 0x46000000000000c0ULL },
92 "MSI Installer",
93 },
94 { { 0, 0 },
95 NULL,
96 },
97 };
98
99 private const char *
cdf_clsid_to_mime(const uint64_t clsid[2],const struct cv * cv)100 cdf_clsid_to_mime(const uint64_t clsid[2], const struct cv *cv)
101 {
102 size_t i;
103 for (i = 0; cv[i].mime != NULL; i++) {
104 if (clsid[0] == cv[i].clsid[0] && clsid[1] == cv[i].clsid[1])
105 return cv[i].mime;
106 }
107 return NULL;
108 }
109
110 private const char *
cdf_app_to_mime(const char * vbuf,const struct nv * nv)111 cdf_app_to_mime(const char *vbuf, const struct nv *nv)
112 {
113 size_t i;
114 const char *rv = NULL;
115 char *vbuf_lower;
116
117 vbuf_lower = zend_str_tolower_dup(vbuf, strlen(vbuf));
118 for (i = 0; nv[i].pattern != NULL; i++) {
119 char *pattern_lower;
120 int found;
121
122 pattern_lower = zend_str_tolower_dup(nv[i].pattern, strlen(nv[i].pattern));
123 found = (strstr(vbuf_lower, pattern_lower) != NULL);
124 efree(pattern_lower);
125
126 if (found) {
127 rv = nv[i].mime;
128 break;
129 }
130 }
131
132 efree(vbuf_lower);
133 return rv;
134 }
135
136 private int
cdf_file_property_info(struct magic_set * ms,const cdf_property_info_t * info,size_t count,const cdf_directory_t * root_storage)137 cdf_file_property_info(struct magic_set *ms, const cdf_property_info_t *info,
138 size_t count, const cdf_directory_t *root_storage)
139 {
140 size_t i;
141 cdf_timestamp_t tp;
142 struct timespec ts;
143 char buf[64];
144 const char *str = NULL;
145 const char *s, *e;
146 int len;
147
148 memset(&ts, 0, sizeof(ts));
149
150 if (!NOTMIME(ms) && root_storage)
151 str = cdf_clsid_to_mime(root_storage->d_storage_uuid,
152 clsid2mime);
153
154 for (i = 0; i < count; i++) {
155 cdf_print_property_name(buf, sizeof(buf), info[i].pi_id);
156 switch (info[i].pi_type) {
157 case CDF_NULL:
158 break;
159 case CDF_SIGNED16:
160 if (NOTMIME(ms) && file_printf(ms, ", %s: %hd", buf,
161 info[i].pi_s16) == -1)
162 return -1;
163 break;
164 case CDF_SIGNED32:
165 if (NOTMIME(ms) && file_printf(ms, ", %s: %d", buf,
166 info[i].pi_s32) == -1)
167 return -1;
168 break;
169 case CDF_UNSIGNED32:
170 if (NOTMIME(ms) && file_printf(ms, ", %s: %u", buf,
171 info[i].pi_u32) == -1)
172 return -1;
173 break;
174 case CDF_FLOAT:
175 if (NOTMIME(ms) && file_printf(ms, ", %s: %g", buf,
176 info[i].pi_f) == -1)
177 return -1;
178 break;
179 case CDF_DOUBLE:
180 if (NOTMIME(ms) && file_printf(ms, ", %s: %g", buf,
181 info[i].pi_d) == -1)
182 return -1;
183 break;
184 case CDF_LENGTH32_STRING:
185 case CDF_LENGTH32_WSTRING:
186 len = info[i].pi_str.s_len;
187 if (len > 1) {
188 char vbuf[1024];
189 size_t j, k = 1;
190
191 if (info[i].pi_type == CDF_LENGTH32_WSTRING)
192 k++;
193 s = info[i].pi_str.s_buf;
194 e = info[i].pi_str.s_buf + len;
195 for (j = 0; s < e && j < sizeof(vbuf)
196 && len--; s += k) {
197 if (*s == '\0')
198 break;
199 if (isprint(CAST(unsigned char, *s)))
200 vbuf[j++] = *s;
201 }
202 if (j == sizeof(vbuf))
203 --j;
204 vbuf[j] = '\0';
205 if (NOTMIME(ms)) {
206 if (vbuf[0]) {
207 if (file_printf(ms, ", %s: %s",
208 buf, vbuf) == -1)
209 return -1;
210 }
211 } else if (str == NULL && info[i].pi_id ==
212 CDF_PROPERTY_NAME_OF_APPLICATION) {
213 str = cdf_app_to_mime(vbuf, app2mime);
214 }
215 }
216 break;
217 case CDF_FILETIME:
218 tp = info[i].pi_tp;
219 if (tp != 0) {
220 char tbuf[64];
221 if (tp < 1000000000000000LL) {
222 cdf_print_elapsed_time(tbuf,
223 sizeof(tbuf), tp);
224 if (NOTMIME(ms) && file_printf(ms,
225 ", %s: %s", buf, tbuf) == -1)
226 return -1;
227 } else {
228 char *c, *ec;
229 cdf_timestamp_to_timespec(&ts, tp);
230 c = cdf_ctime(&ts.tv_sec, tbuf);
231 if (c != NULL &&
232 (ec = strchr(c, '\n')) != NULL)
233 *ec = '\0';
234
235 if (NOTMIME(ms) && file_printf(ms,
236 ", %s: %s", buf, c) == -1)
237 return -1;
238 }
239 }
240 break;
241 case CDF_CLIPBOARD:
242 break;
243 default:
244 return -1;
245 }
246 }
247 if (ms->flags & MAGIC_MIME_TYPE) {
248 if (str == NULL)
249 return 0;
250 if (file_printf(ms, "application/%s", str) == -1)
251 return -1;
252 }
253 return 1;
254 }
255
256 private int
cdf_file_catalog(struct magic_set * ms,const cdf_header_t * h,const cdf_stream_t * sst)257 cdf_file_catalog(struct magic_set *ms, const cdf_header_t *h,
258 const cdf_stream_t *sst)
259 {
260 cdf_catalog_t *cat;
261 size_t i;
262 char buf[256];
263 cdf_catalog_entry_t *ce;
264
265 if (NOTMIME(ms)) {
266 if (file_printf(ms, "Microsoft Thumbs.db [") == -1)
267 return -1;
268 if (cdf_unpack_catalog(h, sst, &cat) == -1)
269 return -1;
270 ce = cat->cat_e;
271 /* skip first entry since it has a , or paren */
272 for (i = 1; i < cat->cat_num; i++)
273 if (file_printf(ms, "%s%s",
274 cdf_u16tos8(buf, ce[i].ce_namlen, ce[i].ce_name),
275 i == cat->cat_num - 1 ? "]" : ", ") == -1) {
276 efree(cat);
277 return -1;
278 }
279 efree(cat);
280 } else if (ms->flags & MAGIC_MIME_TYPE) {
281 if (file_printf(ms, "application/CDFV2") == -1)
282 return -1;
283 }
284 return 1;
285 }
286
287 private int
cdf_file_summary_info(struct magic_set * ms,const cdf_header_t * h,const cdf_stream_t * sst,const cdf_directory_t * root_storage)288 cdf_file_summary_info(struct magic_set *ms, const cdf_header_t *h,
289 const cdf_stream_t *sst, const cdf_directory_t *root_storage)
290 {
291 cdf_summary_info_header_t si;
292 cdf_property_info_t *info;
293 size_t count;
294 int m;
295
296 if (cdf_unpack_summary_info(sst, h, &si, &info, &count) == -1)
297 return -1;
298
299 if (NOTMIME(ms)) {
300 const char *str;
301
302 if (file_printf(ms, "Composite Document File V2 Document")
303 == -1)
304 return -1;
305
306 if (file_printf(ms, ", %s Endian",
307 si.si_byte_order == 0xfffe ? "Little" : "Big") == -1)
308 return -2;
309 switch (si.si_os) {
310 case 2:
311 if (file_printf(ms, ", Os: Windows, Version %d.%d",
312 si.si_os_version & 0xff,
313 CAST(uint32_t, si.si_os_version) >> 8) == -1)
314 return -2;
315 break;
316 case 1:
317 if (file_printf(ms, ", Os: MacOS, Version %d.%d",
318 CAST(uint32_t, si.si_os_version) >> 8,
319 si.si_os_version & 0xff) == -1)
320 return -2;
321 break;
322 default:
323 if (file_printf(ms, ", Os %d, Version: %d.%d", si.si_os,
324 si.si_os_version & 0xff,
325 CAST(uint32_t, si.si_os_version) >> 8) == -1)
326 return -2;
327 break;
328 }
329 if (root_storage) {
330 str = cdf_clsid_to_mime(root_storage->d_storage_uuid,
331 clsid2desc);
332 if (str) {
333 if (file_printf(ms, ", %s", str) == -1)
334 return -2;
335 }
336 }
337 }
338
339 m = cdf_file_property_info(ms, info, count, root_storage);
340 efree(info);
341
342 return m == -1 ? -2 : m;
343 }
344
345 #ifdef notdef
346 private char *
format_clsid(char * buf,size_t len,const uint64_t uuid[2])347 format_clsid(char *buf, size_t len, const uint64_t uuid[2]) {
348 snprintf(buf, len, "%.8" PRIx64 "-%.4" PRIx64 "-%.4" PRIx64 "-%.4"
349 PRIx64 "-%.12" PRIx64,
350 (uuid[0] >> 32) & (uint64_t)0x000000000ffffffffULL,
351 (uuid[0] >> 16) & (uint64_t)0x0000000000000ffffULL,
352 (uuid[0] >> 0) & (uint64_t)0x0000000000000ffffULL,
353 (uuid[1] >> 48) & (uint64_t)0x0000000000000ffffULL,
354 (uuid[1] >> 0) & (uint64_t)0x0000fffffffffffffULL);
355 return buf;
356 }
357 #endif
358
359 private int
cdf_file_catalog_info(struct magic_set * ms,const cdf_info_t * info,const cdf_header_t * h,const cdf_sat_t * sat,const cdf_sat_t * ssat,const cdf_stream_t * sst,const cdf_dir_t * dir,cdf_stream_t * scn)360 cdf_file_catalog_info(struct magic_set *ms, const cdf_info_t *info,
361 const cdf_header_t *h, const cdf_sat_t *sat, const cdf_sat_t *ssat,
362 const cdf_stream_t *sst, const cdf_dir_t *dir, cdf_stream_t *scn)
363 {
364 int i;
365
366 if ((i = cdf_read_user_stream(info, h, sat, ssat, sst,
367 dir, "Catalog", scn)) == -1)
368 return i;
369 #ifdef CDF_DEBUG
370 cdf_dump_catalog(h, scn);
371 #endif
372 if ((i = cdf_file_catalog(ms, h, scn)) == -1)
373 return -1;
374 return i;
375 }
376
377 private int
cdf_check_summary_info(struct magic_set * ms,const cdf_info_t * info,const cdf_header_t * h,const cdf_sat_t * sat,const cdf_sat_t * ssat,const cdf_stream_t * sst,const cdf_dir_t * dir,cdf_stream_t * scn,const cdf_directory_t * root_storage,const char ** expn)378 cdf_check_summary_info(struct magic_set *ms, const cdf_info_t *info,
379 const cdf_header_t *h, const cdf_sat_t *sat, const cdf_sat_t *ssat,
380 const cdf_stream_t *sst, const cdf_dir_t *dir, cdf_stream_t *scn,
381 const cdf_directory_t *root_storage, const char **expn)
382 {
383 int i;
384 const char *str = NULL;
385 cdf_directory_t *d;
386 char name[__arraycount(d->d_name)];
387 size_t j, k;
388
389 #ifdef CDF_DEBUG
390 cdf_dump_summary_info(h, scn);
391 #endif
392 if ((i = cdf_file_summary_info(ms, h, scn, root_storage)) < 0) {
393 *expn = "Can't expand summary_info";
394 return i;
395 }
396 if (i == 1)
397 return i;
398 for (j = 0; str == NULL && j < dir->dir_len; j++) {
399 d = &dir->dir_tab[j];
400 for (k = 0; k < sizeof(name); k++)
401 name[k] = CAST(char, cdf_tole2(d->d_name[k]));
402 str = cdf_app_to_mime(name,
403 NOTMIME(ms) ? name2desc : name2mime);
404 }
405 if (NOTMIME(ms)) {
406 if (str != NULL) {
407 if (file_printf(ms, "%s", str) == -1)
408 return -1;
409 i = 1;
410 }
411 } else if (ms->flags & MAGIC_MIME_TYPE) {
412 if (str == NULL)
413 str = "vnd.ms-office";
414 if (file_printf(ms, "application/%s", str) == -1)
415 return -1;
416 i = 1;
417 }
418 if (i <= 0) {
419 i = cdf_file_catalog_info(ms, info, h, sat, ssat, sst,
420 dir, scn);
421 }
422 return i;
423 }
424
425 private struct sinfo {
426 const char *name;
427 const char *mime;
428 const char *sections[5];
429 const int types[5];
430 } sectioninfo[] = {
431 { "Encrypted", "encrypted",
432 {
433 "EncryptedPackage", "EncryptedSummary",
434 NULL, NULL, NULL,
435 },
436 {
437 CDF_DIR_TYPE_USER_STREAM,
438 CDF_DIR_TYPE_USER_STREAM,
439 0, 0, 0,
440
441 },
442 },
443 { "QuickBooks", "quickbooks",
444 {
445 #if 0
446 "TaxForms", "PDFTaxForms", "modulesInBackup",
447 #endif
448 "mfbu_header", NULL, NULL, NULL, NULL,
449 },
450 {
451 #if 0
452 CDF_DIR_TYPE_USER_STORAGE,
453 CDF_DIR_TYPE_USER_STORAGE,
454 CDF_DIR_TYPE_USER_STREAM,
455 #endif
456 CDF_DIR_TYPE_USER_STREAM,
457 0, 0, 0, 0
458 },
459 },
460 { "Microsoft Excel", "vnd.ms-excel",
461 {
462 "Book", "Workbook", NULL, NULL, NULL,
463 },
464 {
465 CDF_DIR_TYPE_USER_STREAM,
466 CDF_DIR_TYPE_USER_STREAM,
467 0, 0, 0,
468 },
469 },
470 { "Microsoft Word", "msword",
471 {
472 "WordDocument", NULL, NULL, NULL, NULL,
473 },
474 {
475 CDF_DIR_TYPE_USER_STREAM,
476 0, 0, 0, 0,
477 },
478 },
479 { "Microsoft PowerPoint", "vnd.ms-powerpoint",
480 {
481 "PowerPoint", NULL, NULL, NULL, NULL,
482 },
483 {
484 CDF_DIR_TYPE_USER_STREAM,
485 0, 0, 0, 0,
486 },
487 },
488 { "Microsoft Outlook Message", "vnd.ms-outlook",
489 {
490 "__properties_version1.0",
491 "__recip_version1.0_#00000000",
492 NULL, NULL, NULL,
493 },
494 {
495 CDF_DIR_TYPE_USER_STREAM,
496 CDF_DIR_TYPE_USER_STORAGE,
497 0, 0, 0,
498 },
499 },
500 };
501
502 private int
cdf_file_dir_info(struct magic_set * ms,const cdf_dir_t * dir)503 cdf_file_dir_info(struct magic_set *ms, const cdf_dir_t *dir)
504 {
505 size_t sd, j;
506
507 for (sd = 0; sd < __arraycount(sectioninfo); sd++) {
508 const struct sinfo *si = §ioninfo[sd];
509 for (j = 0; si->sections[j]; j++) {
510 if (cdf_find_stream(dir, si->sections[j], si->types[j])
511 > 0)
512 break;
513 #ifdef CDF_DEBUG
514 fprintf(stderr, "Can't read %s\n", si->sections[j]);
515 #endif
516 }
517 if (si->sections[j] == NULL)
518 continue;
519 if (NOTMIME(ms)) {
520 if (file_printf(ms, "CDFV2 %s", si->name) == -1)
521 return -1;
522 } else if (ms->flags & MAGIC_MIME_TYPE) {
523 if (file_printf(ms, "application/%s", si->mime) == -1)
524 return -1;
525 }
526 return 1;
527 }
528 return -1;
529 }
530
531 protected int
file_trycdf(struct magic_set * ms,const struct buffer * b)532 file_trycdf(struct magic_set *ms, const struct buffer *b)
533 {
534 int fd = b->fd;
535 const unsigned char *buf = CAST(const unsigned char *, b->fbuf);
536 size_t nbytes = b->flen;
537 cdf_info_t info;
538 cdf_header_t h;
539 cdf_sat_t sat, ssat;
540 cdf_stream_t sst, scn;
541 cdf_dir_t dir;
542 int i;
543 const char *expn = "";
544 const cdf_directory_t *root_storage;
545
546 scn.sst_tab = NULL;
547 info.i_fd = fd;
548 info.i_buf = buf;
549 info.i_len = nbytes;
550 if (ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION))
551 return 0;
552 if (cdf_read_header(&info, &h) == -1)
553 return 0;
554 #ifdef CDF_DEBUG
555 cdf_dump_header(&h);
556 #endif
557
558 if ((i = cdf_read_sat(&info, &h, &sat)) == -1) {
559 expn = "Can't read SAT";
560 goto out0;
561 }
562 #ifdef CDF_DEBUG
563 cdf_dump_sat("SAT", &sat, CDF_SEC_SIZE(&h));
564 #endif
565
566 if ((i = cdf_read_ssat(&info, &h, &sat, &ssat)) == -1) {
567 expn = "Can't read SSAT";
568 goto out1;
569 }
570 #ifdef CDF_DEBUG
571 cdf_dump_sat("SSAT", &ssat, CDF_SHORT_SEC_SIZE(&h));
572 #endif
573
574 if ((i = cdf_read_dir(&info, &h, &sat, &dir)) == -1) {
575 expn = "Can't read directory";
576 goto out2;
577 }
578
579 if ((i = cdf_read_short_stream(&info, &h, &sat, &dir, &sst,
580 &root_storage)) == -1) {
581 expn = "Cannot read short stream";
582 goto out3;
583 }
584 #ifdef CDF_DEBUG
585 cdf_dump_dir(&info, &h, &sat, &ssat, &sst, &dir);
586 #endif
587 #ifdef notdef
588 if (root_storage) {
589 if (NOTMIME(ms)) {
590 char clsbuf[128];
591 if (file_printf(ms, "CLSID %s, ",
592 format_clsid(clsbuf, sizeof(clsbuf),
593 root_storage->d_storage_uuid)) == -1)
594 return -1;
595 }
596 }
597 #endif
598
599 if ((i = cdf_read_user_stream(&info, &h, &sat, &ssat, &sst, &dir,
600 "FileHeader", &scn)) != -1) {
601 #define HWP5_SIGNATURE "HWP Document File"
602 if (scn.sst_len * scn.sst_ss >= sizeof(HWP5_SIGNATURE) - 1
603 && memcmp(scn.sst_tab, HWP5_SIGNATURE,
604 sizeof(HWP5_SIGNATURE) - 1) == 0) {
605 if (NOTMIME(ms)) {
606 if (file_printf(ms,
607 "Hangul (Korean) Word Processor File 5.x") == -1)
608 return -1;
609 } else if (ms->flags & MAGIC_MIME_TYPE) {
610 if (file_printf(ms, "application/x-hwp") == -1)
611 return -1;
612 }
613 i = 1;
614 goto out5;
615 } else {
616 cdf_zero_stream(&scn);
617 }
618 }
619
620 if ((i = cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir,
621 &scn)) == -1) {
622 if (errno != ESRCH) {
623 expn = "Cannot read summary info";
624 }
625 } else {
626 i = cdf_check_summary_info(ms, &info, &h,
627 &sat, &ssat, &sst, &dir, &scn, root_storage, &expn);
628 cdf_zero_stream(&scn);
629 }
630 if (i <= 0) {
631 if ((i = cdf_read_doc_summary_info(&info, &h, &sat, &ssat,
632 &sst, &dir, &scn)) == -1) {
633 if (errno != ESRCH) {
634 expn = "Cannot read summary info";
635 }
636 } else {
637 i = cdf_check_summary_info(ms, &info, &h, &sat, &ssat,
638 &sst, &dir, &scn, root_storage, &expn);
639 }
640 }
641 if (i <= 0) {
642 i = cdf_file_dir_info(ms, &dir);
643 if (i < 0)
644 expn = "Cannot read section info";
645 }
646 out5:
647 cdf_zero_stream(&scn);
648 cdf_zero_stream(&sst);
649 out3:
650 efree(dir.dir_tab);
651 out2:
652 efree(ssat.sat_tab);
653 out1:
654 efree(sat.sat_tab);
655 out0:
656 /* If we handled it already, return */
657 if (i != -1)
658 return i;
659 /* Provide a default handler */
660 if (NOTMIME(ms)) {
661 if (file_printf(ms,
662 "Composite Document File V2 Document") == -1)
663 return -1;
664 if (*expn)
665 if (file_printf(ms, ", %s", expn) == -1)
666 return -1;
667 } else if (ms->flags & MAGIC_MIME_TYPE) {
668 if (file_printf(ms, "application/CDFV2") == -1)
669 return -1;
670 }
671 return 1;
672 }
673