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