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