xref: /netbsd-src/external/bsd/file/dist/src/readcdf.c (revision ddb176824c39fb0db5ceef3e9e40dcaa273aec38)
1*ddb17682Schristos /*	$NetBSD: readcdf.c,v 1.20 2023/08/18 19:00:11 christos Exp $	*/
2fa9ee498Schristos 
31b108b8bSchristos /*-
474db5203Schristos  * Copyright (c) 2008, 2016 Christos Zoulas
51b108b8bSchristos  * All rights reserved.
61b108b8bSchristos  *
71b108b8bSchristos  * Redistribution and use in source and binary forms, with or without
81b108b8bSchristos  * modification, are permitted provided that the following conditions
91b108b8bSchristos  * are met:
101b108b8bSchristos  * 1. Redistributions of source code must retain the above copyright
111b108b8bSchristos  *    notice, this list of conditions and the following disclaimer.
121b108b8bSchristos  * 2. Redistributions in binary form must reproduce the above copyright
131b108b8bSchristos  *    notice, this list of conditions and the following disclaimer in the
141b108b8bSchristos  *    documentation and/or other materials provided with the distribution.
151b108b8bSchristos  *
161b108b8bSchristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
171b108b8bSchristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
181b108b8bSchristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
191b108b8bSchristos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
201b108b8bSchristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
211b108b8bSchristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
221b108b8bSchristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
231b108b8bSchristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
241b108b8bSchristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
251b108b8bSchristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
261b108b8bSchristos  * POSSIBILITY OF SUCH DAMAGE.
271b108b8bSchristos  */
281b108b8bSchristos #include "file.h"
291b108b8bSchristos 
301b108b8bSchristos #ifndef lint
311b108b8bSchristos #if 0
32*ddb17682Schristos FILE_RCSID("@(#)$File: readcdf.c,v 1.80 2023/01/24 20:13:40 christos Exp $")
331b108b8bSchristos #else
34*ddb17682Schristos __RCSID("$NetBSD: readcdf.c,v 1.20 2023/08/18 19:00:11 christos Exp $");
351b108b8bSchristos #endif
361b108b8bSchristos #endif
371b108b8bSchristos 
38819e6405Schristos #include <assert.h>
391b108b8bSchristos #include <stdlib.h>
401b108b8bSchristos #include <unistd.h>
411b108b8bSchristos #include <string.h>
421b108b8bSchristos #include <time.h>
431b108b8bSchristos #include <ctype.h>
441b108b8bSchristos 
451b108b8bSchristos #include "cdf.h"
461b108b8bSchristos #include "magic.h"
471b108b8bSchristos 
481b108b8bSchristos #define NOTMIME(ms) (((ms)->flags & MAGIC_MIME) == 0)
491b108b8bSchristos 
508dd459ccSchristos static const struct nv {
518dd459ccSchristos 	const char *pattern;
528dd459ccSchristos 	const char *mime;
538dd459ccSchristos } app2mime[] =  {
548dd459ccSchristos 	{ "Word",			"msword",		},
558dd459ccSchristos 	{ "Excel",			"vnd.ms-excel",		},
568dd459ccSchristos 	{ "Powerpoint",			"vnd.ms-powerpoint",	},
578dd459ccSchristos 	{ "Crystal Reports",		"x-rpt",		},
588dd459ccSchristos 	{ "Advanced Installer",		"vnd.ms-msi",		},
598dd459ccSchristos 	{ "InstallShield",		"vnd.ms-msi",		},
608dd459ccSchristos 	{ "Microsoft Patch Compiler",	"vnd.ms-msi",		},
618dd459ccSchristos 	{ "NAnt",			"vnd.ms-msi",		},
628dd459ccSchristos 	{ "Windows Installer",		"vnd.ms-msi",		},
638dd459ccSchristos 	{ NULL,				NULL,			},
648dd459ccSchristos }, name2mime[] = {
6574db5203Schristos 	{ "Book",			"vnd.ms-excel",		},
6674db5203Schristos 	{ "Workbook",			"vnd.ms-excel",		},
678dd459ccSchristos 	{ "WordDocument",		"msword",		},
688dd459ccSchristos 	{ "PowerPoint",			"vnd.ms-powerpoint",	},
698dd459ccSchristos 	{ "DigitalSignature",		"vnd.ms-msi",		},
708dd459ccSchristos 	{ NULL,				NULL,			},
718dd459ccSchristos }, name2desc[] = {
7274db5203Schristos 	{ "Book",			"Microsoft Excel",	},
7374db5203Schristos 	{ "Workbook",			"Microsoft Excel",	},
7474db5203Schristos 	{ "WordDocument",		"Microsoft Word",	},
758dd459ccSchristos 	{ "PowerPoint",			"Microsoft PowerPoint",	},
768dd459ccSchristos 	{ "DigitalSignature",		"Microsoft Installer",	},
778dd459ccSchristos 	{ NULL,				NULL,			},
788dd459ccSchristos };
798dd459ccSchristos 
80819e6405Schristos static const struct cv {
81819e6405Schristos 	uint64_t clsid[2];
82819e6405Schristos 	const char *mime;
83819e6405Schristos } clsid2mime[] = {
84819e6405Schristos 	{
85fa9ee498Schristos 		{ 0x00000000000c1084ULL, 0x46000000000000c0ULL  },
86819e6405Schristos 		"x-msi",
87819e6405Schristos 	},
88819e6405Schristos 	{	{ 0,			 0			},
89819e6405Schristos 		NULL,
90819e6405Schristos 	},
91819e6405Schristos }, clsid2desc[] = {
92819e6405Schristos 	{
93fa9ee498Schristos 		{ 0x00000000000c1084ULL, 0x46000000000000c0ULL  },
94819e6405Schristos 		"MSI Installer",
95819e6405Schristos 	},
96819e6405Schristos 	{	{ 0,			 0			},
97819e6405Schristos 		NULL,
98819e6405Schristos 	},
99819e6405Schristos };
100819e6405Schristos 
101*ddb17682Schristos file_private const char *
cdf_clsid_to_mime(const uint64_t clsid[2],const struct cv * cv)102819e6405Schristos cdf_clsid_to_mime(const uint64_t clsid[2], const struct cv *cv)
103819e6405Schristos {
104819e6405Schristos 	size_t i;
105819e6405Schristos 	for (i = 0; cv[i].mime != NULL; i++) {
106819e6405Schristos 		if (clsid[0] == cv[i].clsid[0] && clsid[1] == cv[i].clsid[1])
107819e6405Schristos 			return cv[i].mime;
108819e6405Schristos 	}
10974db5203Schristos #ifdef CDF_DEBUG
11074db5203Schristos 	fprintf(stderr, "unknown mime %" PRIx64 ", %" PRIx64 "\n", clsid[0],
11174db5203Schristos 	    clsid[1]);
11274db5203Schristos #endif
113819e6405Schristos 	return NULL;
114819e6405Schristos }
115819e6405Schristos 
116*ddb17682Schristos file_private const char *
cdf_app_to_mime(const char * vbuf,const struct nv * nv)1178dd459ccSchristos cdf_app_to_mime(const char *vbuf, const struct nv *nv)
1188dd459ccSchristos {
1198dd459ccSchristos 	size_t i;
120819e6405Schristos 	const char *rv = NULL;
12158b7f199Schristos #ifdef USE_C_LOCALE
12258b7f199Schristos 	locale_t old_lc_ctype, c_lc_ctype;
1238dd459ccSchristos 
12458b7f199Schristos 	c_lc_ctype = newlocale(LC_CTYPE_MASK, "C", 0);
12558b7f199Schristos 	assert(c_lc_ctype != NULL);
12658b7f199Schristos 	old_lc_ctype = uselocale(c_lc_ctype);
127819e6405Schristos 	assert(old_lc_ctype != NULL);
12874db5203Schristos #else
12978a23c3aSchristos 	char *old_lc_ctype = setlocale(LC_CTYPE, NULL);
13078a23c3aSchristos 	assert(old_lc_ctype != NULL);
13178a23c3aSchristos 	old_lc_ctype = strdup(old_lc_ctype);
13278a23c3aSchristos 	assert(old_lc_ctype != NULL);
13378a23c3aSchristos 	(void)setlocale(LC_CTYPE, "C");
13458b7f199Schristos #endif
1358dd459ccSchristos 	for (i = 0; nv[i].pattern != NULL; i++)
136819e6405Schristos 		if (strcasestr(vbuf, nv[i].pattern) != NULL) {
137819e6405Schristos 			rv = nv[i].mime;
138819e6405Schristos 			break;
139819e6405Schristos 		}
14074db5203Schristos #ifdef CDF_DEBUG
14174db5203Schristos 	fprintf(stderr, "unknown app %s\n", vbuf);
14274db5203Schristos #endif
14358b7f199Schristos #ifdef USE_C_LOCALE
14458b7f199Schristos 	(void)uselocale(old_lc_ctype);
14558b7f199Schristos 	freelocale(c_lc_ctype);
14674db5203Schristos #else
14778a23c3aSchristos 	(void)setlocale(LC_CTYPE, old_lc_ctype);
14878a23c3aSchristos 	free(old_lc_ctype);
14958b7f199Schristos #endif
150819e6405Schristos 	return rv;
1518dd459ccSchristos }
1528dd459ccSchristos 
153*ddb17682Schristos file_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)1541b108b8bSchristos cdf_file_property_info(struct magic_set *ms, const cdf_property_info_t *info,
155819e6405Schristos     size_t count, const cdf_directory_t *root_storage)
1561b108b8bSchristos {
1571b108b8bSchristos 	size_t i;
1581b108b8bSchristos 	cdf_timestamp_t tp;
1591b108b8bSchristos 	struct timespec ts;
1601b108b8bSchristos 	char buf[64];
16179bb278aSchristos 	const char *str = NULL;
162e2725312Schristos 	const char *s, *e;
1631b108b8bSchristos 	int len;
1641b108b8bSchristos 
165819e6405Schristos 	if (!NOTMIME(ms) && root_storage)
166819e6405Schristos 		str = cdf_clsid_to_mime(root_storage->d_storage_uuid,
167819e6405Schristos 		    clsid2mime);
168819e6405Schristos 
1691b108b8bSchristos 	for (i = 0; i < count; i++) {
1701b108b8bSchristos 		cdf_print_property_name(buf, sizeof(buf), info[i].pi_id);
1711b108b8bSchristos 		switch (info[i].pi_type) {
1722344ff98Schristos 		case CDF_NULL:
1732344ff98Schristos 			break;
1741b108b8bSchristos 		case CDF_SIGNED16:
1751b108b8bSchristos 			if (NOTMIME(ms) && file_printf(ms, ", %s: %hd", buf,
1761b108b8bSchristos 			    info[i].pi_s16) == -1)
1771b108b8bSchristos 				return -1;
1781b108b8bSchristos 			break;
1791b108b8bSchristos 		case CDF_SIGNED32:
1801b108b8bSchristos 			if (NOTMIME(ms) && file_printf(ms, ", %s: %d", buf,
1811b108b8bSchristos 			    info[i].pi_s32) == -1)
1821b108b8bSchristos 				return -1;
1831b108b8bSchristos 			break;
1841b108b8bSchristos 		case CDF_UNSIGNED32:
1851b108b8bSchristos 			if (NOTMIME(ms) && file_printf(ms, ", %s: %u", buf,
1861b108b8bSchristos 			    info[i].pi_u32) == -1)
1871b108b8bSchristos 				return -1;
1881b108b8bSchristos 			break;
189046a38ddSchristos 		case CDF_FLOAT:
190046a38ddSchristos 			if (NOTMIME(ms) && file_printf(ms, ", %s: %g", buf,
191046a38ddSchristos 			    info[i].pi_f) == -1)
192046a38ddSchristos 				return -1;
193046a38ddSchristos 			break;
194046a38ddSchristos 		case CDF_DOUBLE:
195046a38ddSchristos 			if (NOTMIME(ms) && file_printf(ms, ", %s: %g", buf,
196046a38ddSchristos 			    info[i].pi_d) == -1)
197046a38ddSchristos 				return -1;
198046a38ddSchristos 			break;
1991b108b8bSchristos 		case CDF_LENGTH32_STRING:
2002344ff98Schristos 		case CDF_LENGTH32_WSTRING:
2011b108b8bSchristos 			len = info[i].pi_str.s_len;
2021b108b8bSchristos 			if (len > 1) {
2031b108b8bSchristos 				char vbuf[1024];
2042344ff98Schristos 				size_t j, k = 1;
2052344ff98Schristos 
2062344ff98Schristos 				if (info[i].pi_type == CDF_LENGTH32_WSTRING)
2072344ff98Schristos 				    k++;
2082344ff98Schristos 				s = info[i].pi_str.s_buf;
209e2725312Schristos 				e = info[i].pi_str.s_buf + len;
210e2725312Schristos 				for (j = 0; s < e && j < sizeof(vbuf)
211e2725312Schristos 				    && len--; s += k) {
2121b108b8bSchristos 					if (*s == '\0')
2131b108b8bSchristos 						break;
214d0c65b7bSchristos 					if (isprint(CAST(unsigned char, *s)))
215819e6405Schristos 						vbuf[j++] = *s;
2161b108b8bSchristos 				}
2171b108b8bSchristos 				if (j == sizeof(vbuf))
2181b108b8bSchristos 					--j;
2191b108b8bSchristos 				vbuf[j] = '\0';
2202344ff98Schristos 				if (NOTMIME(ms)) {
2211b108b8bSchristos 					if (vbuf[0]) {
2221b108b8bSchristos 						if (file_printf(ms, ", %s: %s",
2231b108b8bSchristos 						    buf, vbuf) == -1)
2241b108b8bSchristos 							return -1;
2251b108b8bSchristos 					}
226819e6405Schristos 				} else if (str == NULL && info[i].pi_id ==
2271b108b8bSchristos 				    CDF_PROPERTY_NAME_OF_APPLICATION) {
2288dd459ccSchristos 					str = cdf_app_to_mime(vbuf, app2mime);
2291b108b8bSchristos 				}
2301b108b8bSchristos 			}
2311b108b8bSchristos 			break;
2321b108b8bSchristos 		case CDF_FILETIME:
2331b108b8bSchristos 			tp = info[i].pi_tp;
2341b108b8bSchristos 			if (tp != 0) {
2351b108b8bSchristos 				char tbuf[64];
23620d96732Schristos 				if (tp < 1000000000000000LL) {
2371b108b8bSchristos 					cdf_print_elapsed_time(tbuf,
2381b108b8bSchristos 					    sizeof(tbuf), tp);
2391b108b8bSchristos 					if (NOTMIME(ms) && file_printf(ms,
2401b108b8bSchristos 					    ", %s: %s", buf, tbuf) == -1)
2411b108b8bSchristos 						return -1;
2421b108b8bSchristos 				} else {
2431b108b8bSchristos 					char *c, *ec;
2441b108b8bSchristos 					cdf_timestamp_to_timespec(&ts, tp);
24520d96732Schristos 					c = cdf_ctime(&ts.tv_sec, tbuf);
2468dd459ccSchristos 					if (c != NULL &&
2478dd459ccSchristos 					    (ec = strchr(c, '\n')) != NULL)
2481b108b8bSchristos 						*ec = '\0';
2491b108b8bSchristos 
2501b108b8bSchristos 					if (NOTMIME(ms) && file_printf(ms,
2511b108b8bSchristos 					    ", %s: %s", buf, c) == -1)
2521b108b8bSchristos 						return -1;
2531b108b8bSchristos 				}
2541b108b8bSchristos 			}
2551b108b8bSchristos 			break;
2561b108b8bSchristos 		case CDF_CLIPBOARD:
2571b108b8bSchristos 			break;
2581b108b8bSchristos 		default:
2591b108b8bSchristos 			return -1;
2601b108b8bSchristos 		}
2611b108b8bSchristos 	}
262c02f7f97Schristos 	if (ms->flags & MAGIC_MIME_TYPE) {
26379bb278aSchristos 		if (str == NULL)
26479bb278aSchristos 			return 0;
2652203d253Schristos 		if (file_printf(ms, "application/%s", str) == -1)
2662203d253Schristos 			return -1;
2671b108b8bSchristos 	}
2681b108b8bSchristos 	return 1;
2691b108b8bSchristos }
2701b108b8bSchristos 
271*ddb17682Schristos file_private int
cdf_file_catalog(struct magic_set * ms,const cdf_header_t * h,const cdf_stream_t * sst)27258b7f199Schristos cdf_file_catalog(struct magic_set *ms, const cdf_header_t *h,
27358b7f199Schristos     const cdf_stream_t *sst)
27458b7f199Schristos {
27558b7f199Schristos 	cdf_catalog_t *cat;
27658b7f199Schristos 	size_t i;
27758b7f199Schristos 	char buf[256];
27858b7f199Schristos 	cdf_catalog_entry_t *ce;
27958b7f199Schristos 
28058b7f199Schristos 	if (NOTMIME(ms)) {
28158b7f199Schristos 		if (file_printf(ms, "Microsoft Thumbs.db [") == -1)
28258b7f199Schristos 			return -1;
28358b7f199Schristos 		if (cdf_unpack_catalog(h, sst, &cat) == -1)
28458b7f199Schristos 			return -1;
28558b7f199Schristos 		ce = cat->cat_e;
28658b7f199Schristos 		/* skip first entry since it has a , or paren */
28758b7f199Schristos 		for (i = 1; i < cat->cat_num; i++)
28858b7f199Schristos 			if (file_printf(ms, "%s%s",
28958b7f199Schristos 			    cdf_u16tos8(buf, ce[i].ce_namlen, ce[i].ce_name),
29058b7f199Schristos 			    i == cat->cat_num - 1 ? "]" : ", ") == -1) {
29158b7f199Schristos 				free(cat);
29258b7f199Schristos 				return -1;
29358b7f199Schristos 			}
29458b7f199Schristos 		free(cat);
295c02f7f97Schristos 	} else if (ms->flags & MAGIC_MIME_TYPE) {
29658b7f199Schristos 		if (file_printf(ms, "application/CDFV2") == -1)
29758b7f199Schristos 			return -1;
29858b7f199Schristos 	}
29958b7f199Schristos 	return 1;
30058b7f199Schristos }
30158b7f199Schristos 
302*ddb17682Schristos file_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)3032344ff98Schristos cdf_file_summary_info(struct magic_set *ms, const cdf_header_t *h,
304819e6405Schristos     const cdf_stream_t *sst, const cdf_directory_t *root_storage)
3051b108b8bSchristos {
3061b108b8bSchristos 	cdf_summary_info_header_t si;
3071b108b8bSchristos 	cdf_property_info_t *info;
3081b108b8bSchristos 	size_t count;
3091b108b8bSchristos 	int m;
3101b108b8bSchristos 
3112344ff98Schristos 	if (cdf_unpack_summary_info(sst, h, &si, &info, &count) == -1)
3121b108b8bSchristos 		return -1;
3131b108b8bSchristos 
3141b108b8bSchristos 	if (NOTMIME(ms)) {
315819e6405Schristos 		const char *str;
316819e6405Schristos 
317046a38ddSchristos 		if (file_printf(ms, "Composite Document File V2 Document")
318046a38ddSchristos 		    == -1)
3191b108b8bSchristos 			return -1;
3201b108b8bSchristos 
3211b108b8bSchristos 		if (file_printf(ms, ", %s Endian",
3221b108b8bSchristos 		    si.si_byte_order == 0xfffe ?  "Little" : "Big") == -1)
323046a38ddSchristos 			return -2;
3241b108b8bSchristos 		switch (si.si_os) {
3251b108b8bSchristos 		case 2:
3261b108b8bSchristos 			if (file_printf(ms, ", Os: Windows, Version %d.%d",
3277c25ef23Schristos 			    si.si_os_version & 0xff,
328d0c65b7bSchristos 			    CAST(uint32_t, si.si_os_version) >> 8) == -1)
329046a38ddSchristos 				return -2;
3301b108b8bSchristos 			break;
3311b108b8bSchristos 		case 1:
3321b108b8bSchristos 			if (file_printf(ms, ", Os: MacOS, Version %d.%d",
333d0c65b7bSchristos 			    CAST(uint32_t, si.si_os_version) >> 8,
3347c25ef23Schristos 			    si.si_os_version & 0xff) == -1)
335046a38ddSchristos 				return -2;
3361b108b8bSchristos 			break;
3371b108b8bSchristos 		default:
3381b108b8bSchristos 			if (file_printf(ms, ", Os %d, Version: %d.%d", si.si_os,
3397c25ef23Schristos 			    si.si_os_version & 0xff,
340d0c65b7bSchristos 			    CAST(uint32_t, si.si_os_version) >> 8) == -1)
341046a38ddSchristos 				return -2;
3421b108b8bSchristos 			break;
3431b108b8bSchristos 		}
344819e6405Schristos 		if (root_storage) {
345819e6405Schristos 			str = cdf_clsid_to_mime(root_storage->d_storage_uuid,
346819e6405Schristos 			    clsid2desc);
34758b7f199Schristos 			if (str) {
348819e6405Schristos 				if (file_printf(ms, ", %s", str) == -1)
349819e6405Schristos 					return -2;
350819e6405Schristos 			}
3511b108b8bSchristos 		}
35258b7f199Schristos 	}
3531b108b8bSchristos 
354819e6405Schristos 	m = cdf_file_property_info(ms, info, count, root_storage);
3551b108b8bSchristos 	free(info);
3561b108b8bSchristos 
357046a38ddSchristos 	return m == -1 ? -2 : m;
3581b108b8bSchristos }
3591b108b8bSchristos 
360819e6405Schristos #ifdef notdef
361*ddb17682Schristos file_private char *
format_clsid(char * buf,size_t len,const uint64_t uuid[2])362819e6405Schristos format_clsid(char *buf, size_t len, const uint64_t uuid[2]) {
363819e6405Schristos 	snprintf(buf, len, "%.8" PRIx64 "-%.4" PRIx64 "-%.4" PRIx64 "-%.4"
364819e6405Schristos 	    PRIx64 "-%.12" PRIx64,
365fa9ee498Schristos 	    (uuid[0] >> 32) & (uint64_t)0x000000000ffffffffULL,
366fa9ee498Schristos 	    (uuid[0] >> 16) & (uint64_t)0x0000000000000ffffULL,
367fa9ee498Schristos 	    (uuid[0] >>  0) & (uint64_t)0x0000000000000ffffULL,
368fa9ee498Schristos 	    (uuid[1] >> 48) & (uint64_t)0x0000000000000ffffULL,
369fa9ee498Schristos 	    (uuid[1] >>  0) & (uint64_t)0x0000fffffffffffffULL);
370819e6405Schristos 	return buf;
371819e6405Schristos }
372819e6405Schristos #endif
373819e6405Schristos 
374*ddb17682Schristos file_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)37574db5203Schristos cdf_file_catalog_info(struct magic_set *ms, const cdf_info_t *info,
37674db5203Schristos     const cdf_header_t *h, const cdf_sat_t *sat, const cdf_sat_t *ssat,
37774db5203Schristos     const cdf_stream_t *sst, const cdf_dir_t *dir, cdf_stream_t *scn)
37874db5203Schristos {
37974db5203Schristos 	int i;
38074db5203Schristos 
38174db5203Schristos 	if ((i = cdf_read_user_stream(info, h, sat, ssat, sst,
38274db5203Schristos 	    dir, "Catalog", scn)) == -1)
38374db5203Schristos 		return i;
38474db5203Schristos #ifdef CDF_DEBUG
38574db5203Schristos 	cdf_dump_catalog(h, scn);
38674db5203Schristos #endif
38774db5203Schristos 	if ((i = cdf_file_catalog(ms, h, scn)) == -1)
38874db5203Schristos 		return -1;
38974db5203Schristos 	return i;
39074db5203Schristos }
39174db5203Schristos 
392*ddb17682Schristos file_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)39374db5203Schristos cdf_check_summary_info(struct magic_set *ms, const cdf_info_t *info,
39474db5203Schristos     const cdf_header_t *h, const cdf_sat_t *sat, const cdf_sat_t *ssat,
39574db5203Schristos     const cdf_stream_t *sst, const cdf_dir_t *dir, cdf_stream_t *scn,
39674db5203Schristos     const cdf_directory_t *root_storage, const char **expn)
39774db5203Schristos {
39874db5203Schristos 	int i;
39974db5203Schristos 	const char *str = NULL;
40074db5203Schristos 	cdf_directory_t *d;
40174db5203Schristos 	char name[__arraycount(d->d_name)];
40274db5203Schristos 	size_t j, k;
40374db5203Schristos 
40474db5203Schristos #ifdef CDF_DEBUG
40574db5203Schristos 	cdf_dump_summary_info(h, scn);
40674db5203Schristos #endif
40774db5203Schristos 	if ((i = cdf_file_summary_info(ms, h, scn, root_storage)) < 0) {
40874db5203Schristos 	    *expn = "Can't expand summary_info";
40974db5203Schristos 	    return i;
41074db5203Schristos 	}
41174db5203Schristos 	if (i == 1)
41274db5203Schristos 		return i;
41374db5203Schristos 	for (j = 0; str == NULL && j < dir->dir_len; j++) {
41474db5203Schristos 		d = &dir->dir_tab[j];
41574db5203Schristos 		for (k = 0; k < sizeof(name); k++)
416d0c65b7bSchristos 			name[k] = CAST(char, cdf_tole2(d->d_name[k]));
41774db5203Schristos 		str = cdf_app_to_mime(name,
41874db5203Schristos 				      NOTMIME(ms) ? name2desc : name2mime);
41974db5203Schristos 	}
42074db5203Schristos 	if (NOTMIME(ms)) {
42174db5203Schristos 		if (str != NULL) {
42274db5203Schristos 			if (file_printf(ms, "%s", str) == -1)
42374db5203Schristos 				return -1;
42474db5203Schristos 			i = 1;
42574db5203Schristos 		}
426c02f7f97Schristos 	} else if (ms->flags & MAGIC_MIME_TYPE) {
42774db5203Schristos 		if (str == NULL)
42874db5203Schristos 			str = "vnd.ms-office";
42974db5203Schristos 		if (file_printf(ms, "application/%s", str) == -1)
43074db5203Schristos 			return -1;
43174db5203Schristos 		i = 1;
43274db5203Schristos 	}
43374db5203Schristos 	if (i <= 0) {
43474db5203Schristos 		i = cdf_file_catalog_info(ms, info, h, sat, ssat, sst,
43574db5203Schristos 					  dir, scn);
43674db5203Schristos 	}
43774db5203Schristos 	return i;
43874db5203Schristos }
43974db5203Schristos 
440*ddb17682Schristos file_private struct sinfo {
44174db5203Schristos 	const char *name;
44274db5203Schristos 	const char *mime;
44374db5203Schristos 	const char *sections[5];
44474db5203Schristos 	const int  types[5];
44574db5203Schristos } sectioninfo[] = {
44674db5203Schristos 	{ "Encrypted", "encrypted",
44774db5203Schristos 		{
44874db5203Schristos 			"EncryptedPackage", "EncryptedSummary",
44974db5203Schristos 			NULL, NULL, NULL,
45074db5203Schristos 		},
45174db5203Schristos 		{
45274db5203Schristos 			CDF_DIR_TYPE_USER_STREAM,
45374db5203Schristos 			CDF_DIR_TYPE_USER_STREAM,
45474db5203Schristos 			0, 0, 0,
45574db5203Schristos 
45674db5203Schristos 		},
45774db5203Schristos 	},
45874db5203Schristos 	{ "QuickBooks", "quickbooks",
45974db5203Schristos 		{
46074db5203Schristos #if 0
46174db5203Schristos 			"TaxForms", "PDFTaxForms", "modulesInBackup",
46274db5203Schristos #endif
46374db5203Schristos 			"mfbu_header", NULL, NULL, NULL, NULL,
46474db5203Schristos 		},
46574db5203Schristos 		{
46674db5203Schristos #if 0
46774db5203Schristos 			CDF_DIR_TYPE_USER_STORAGE,
46874db5203Schristos 			CDF_DIR_TYPE_USER_STORAGE,
46974db5203Schristos 			CDF_DIR_TYPE_USER_STREAM,
47074db5203Schristos #endif
47174db5203Schristos 			CDF_DIR_TYPE_USER_STREAM,
47274db5203Schristos 			0, 0, 0, 0
47374db5203Schristos 		},
47474db5203Schristos 	},
47574db5203Schristos 	{ "Microsoft Excel", "vnd.ms-excel",
47674db5203Schristos 		{
47774db5203Schristos 			"Book", "Workbook", NULL, NULL, NULL,
47874db5203Schristos 		},
47974db5203Schristos 		{
48074db5203Schristos 			CDF_DIR_TYPE_USER_STREAM,
48174db5203Schristos 			CDF_DIR_TYPE_USER_STREAM,
48274db5203Schristos 			0, 0, 0,
48374db5203Schristos 		},
48474db5203Schristos 	},
48574db5203Schristos 	{ "Microsoft Word", "msword",
48674db5203Schristos 		{
48774db5203Schristos 			"WordDocument", NULL, NULL, NULL, NULL,
48874db5203Schristos 		},
48974db5203Schristos 		{
49074db5203Schristos 			CDF_DIR_TYPE_USER_STREAM,
49174db5203Schristos 			0, 0, 0, 0,
49274db5203Schristos 		},
49374db5203Schristos 	},
49474db5203Schristos 	{ "Microsoft PowerPoint", "vnd.ms-powerpoint",
49574db5203Schristos 		{
49674db5203Schristos 			"PowerPoint", NULL, NULL, NULL, NULL,
49774db5203Schristos 		},
49874db5203Schristos 		{
49974db5203Schristos 			CDF_DIR_TYPE_USER_STREAM,
50074db5203Schristos 			0, 0, 0, 0,
50174db5203Schristos 		},
50274db5203Schristos 	},
50374db5203Schristos 	{ "Microsoft Outlook Message", "vnd.ms-outlook",
50474db5203Schristos 		{
50574db5203Schristos 			"__properties_version1.0",
50674db5203Schristos 			"__recip_version1.0_#00000000",
50774db5203Schristos 			NULL, NULL, NULL,
50874db5203Schristos 		},
50974db5203Schristos 		{
51074db5203Schristos 			CDF_DIR_TYPE_USER_STREAM,
51174db5203Schristos 			CDF_DIR_TYPE_USER_STORAGE,
51274db5203Schristos 			0, 0, 0,
51374db5203Schristos 		},
51474db5203Schristos 	},
51574db5203Schristos };
51674db5203Schristos 
517*ddb17682Schristos file_private int
cdf_file_dir_info(struct magic_set * ms,const cdf_dir_t * dir)51874db5203Schristos cdf_file_dir_info(struct magic_set *ms, const cdf_dir_t *dir)
51974db5203Schristos {
52074db5203Schristos 	size_t sd, j;
52174db5203Schristos 
52274db5203Schristos 	for (sd = 0; sd < __arraycount(sectioninfo); sd++) {
52374db5203Schristos 		const struct sinfo *si = &sectioninfo[sd];
52474db5203Schristos 		for (j = 0; si->sections[j]; j++) {
52574db5203Schristos 			if (cdf_find_stream(dir, si->sections[j], si->types[j])
52674db5203Schristos 			    > 0)
52774db5203Schristos 				break;
52874db5203Schristos #ifdef CDF_DEBUG
52974db5203Schristos 			fprintf(stderr, "Can't read %s\n", si->sections[j]);
53074db5203Schristos #endif
53174db5203Schristos 		}
53274db5203Schristos 		if (si->sections[j] == NULL)
53374db5203Schristos 			continue;
53474db5203Schristos 		if (NOTMIME(ms)) {
53574db5203Schristos 			if (file_printf(ms, "CDFV2 %s", si->name) == -1)
53674db5203Schristos 				return -1;
537c02f7f97Schristos 		} else if (ms->flags & MAGIC_MIME_TYPE) {
53874db5203Schristos 			if (file_printf(ms, "application/%s", si->mime) == -1)
53974db5203Schristos 				return -1;
54074db5203Schristos 		}
54174db5203Schristos 		return 1;
54274db5203Schristos 	}
54374db5203Schristos 	return -1;
54474db5203Schristos }
54574db5203Schristos 
546*ddb17682Schristos file_protected int
file_trycdf(struct magic_set * ms,const struct buffer * b)5475efe63deSchristos file_trycdf(struct magic_set *ms, const struct buffer *b)
5481b108b8bSchristos {
5495efe63deSchristos 	int fd = b->fd;
550c02f7f97Schristos 	const unsigned char *buf = CAST(const unsigned char *, b->fbuf);
5515efe63deSchristos 	size_t nbytes = b->flen;
5521b108b8bSchristos 	cdf_info_t info;
5531b108b8bSchristos 	cdf_header_t h;
5541b108b8bSchristos 	cdf_sat_t sat, ssat;
5551b108b8bSchristos 	cdf_stream_t sst, scn;
5561b108b8bSchristos 	cdf_dir_t dir;
5571b108b8bSchristos 	int i;
5581b108b8bSchristos 	const char *expn = "";
559fa9ee498Schristos 	const cdf_directory_t *root_storage;
5601b108b8bSchristos 
56174db5203Schristos 	scn.sst_tab = NULL;
5621b108b8bSchristos 	info.i_fd = fd;
5631b108b8bSchristos 	info.i_buf = buf;
5641b108b8bSchristos 	info.i_len = nbytes;
56574db5203Schristos 	if (ms->flags & (MAGIC_APPLE|MAGIC_EXTENSION))
5661b108b8bSchristos 		return 0;
5671b108b8bSchristos 	if (cdf_read_header(&info, &h) == -1)
5681b108b8bSchristos 		return 0;
5691b108b8bSchristos #ifdef CDF_DEBUG
5701b108b8bSchristos 	cdf_dump_header(&h);
5711b108b8bSchristos #endif
5721b108b8bSchristos 
5731b108b8bSchristos 	if ((i = cdf_read_sat(&info, &h, &sat)) == -1) {
5741b108b8bSchristos 		expn = "Can't read SAT";
5751b108b8bSchristos 		goto out0;
5761b108b8bSchristos 	}
5771b108b8bSchristos #ifdef CDF_DEBUG
5781b108b8bSchristos 	cdf_dump_sat("SAT", &sat, CDF_SEC_SIZE(&h));
5791b108b8bSchristos #endif
5801b108b8bSchristos 
5811b108b8bSchristos 	if ((i = cdf_read_ssat(&info, &h, &sat, &ssat)) == -1) {
5821b108b8bSchristos 		expn = "Can't read SSAT";
5831b108b8bSchristos 		goto out1;
5841b108b8bSchristos 	}
5851b108b8bSchristos #ifdef CDF_DEBUG
5861b108b8bSchristos 	cdf_dump_sat("SSAT", &ssat, CDF_SHORT_SEC_SIZE(&h));
5871b108b8bSchristos #endif
5881b108b8bSchristos 
5891b108b8bSchristos 	if ((i = cdf_read_dir(&info, &h, &sat, &dir)) == -1) {
5901b108b8bSchristos 		expn = "Can't read directory";
5911b108b8bSchristos 		goto out2;
5921b108b8bSchristos 	}
5931b108b8bSchristos 
594819e6405Schristos 	if ((i = cdf_read_short_stream(&info, &h, &sat, &dir, &sst,
595819e6405Schristos 	    &root_storage)) == -1) {
5961b108b8bSchristos 		expn = "Cannot read short stream";
5971b108b8bSchristos 		goto out3;
5981b108b8bSchristos 	}
5991b108b8bSchristos #ifdef CDF_DEBUG
6001b108b8bSchristos 	cdf_dump_dir(&info, &h, &sat, &ssat, &sst, &dir);
6011b108b8bSchristos #endif
602819e6405Schristos #ifdef notdef
603819e6405Schristos 	if (root_storage) {
604819e6405Schristos 		if (NOTMIME(ms)) {
605819e6405Schristos 			char clsbuf[128];
606819e6405Schristos 			if (file_printf(ms, "CLSID %s, ",
607819e6405Schristos 			    format_clsid(clsbuf, sizeof(clsbuf),
608819e6405Schristos 			    root_storage->d_storage_uuid)) == -1)
609819e6405Schristos 				return -1;
610819e6405Schristos 		}
611819e6405Schristos 	}
612819e6405Schristos #endif
613819e6405Schristos 
6141d4cb158Schristos 	if (cdf_read_user_stream(&info, &h, &sat, &ssat, &sst, &dir,
6151d4cb158Schristos 	    "FileHeader", &scn) != -1) {
616819e6405Schristos #define HWP5_SIGNATURE "HWP Document File"
617e2725312Schristos 		if (scn.sst_len * scn.sst_ss >= sizeof(HWP5_SIGNATURE) - 1
618819e6405Schristos 		    && memcmp(scn.sst_tab, HWP5_SIGNATURE,
619819e6405Schristos 		    sizeof(HWP5_SIGNATURE) - 1) == 0) {
620819e6405Schristos 		    if (NOTMIME(ms)) {
621819e6405Schristos 			if (file_printf(ms,
622*ddb17682Schristos 			    "Hancom HWP (Hangul Word Processor) file, version 5.0") == -1)
623819e6405Schristos 			    return -1;
624c02f7f97Schristos 		    } else if (ms->flags & MAGIC_MIME_TYPE) {
625819e6405Schristos 			if (file_printf(ms, "application/x-hwp") == -1)
626819e6405Schristos 			    return -1;
627819e6405Schristos 		    }
628819e6405Schristos 		    i = 1;
629819e6405Schristos 		    goto out5;
630819e6405Schristos 		} else {
63174db5203Schristos 		    cdf_zero_stream(&scn);
632819e6405Schristos 		}
633819e6405Schristos 	}
6341b108b8bSchristos 
6351b108b8bSchristos 	if ((i = cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir,
6361b108b8bSchristos 	    &scn)) == -1) {
63774db5203Schristos 		if (errno != ESRCH) {
6381b108b8bSchristos 			expn = "Cannot read summary info";
6392344ff98Schristos 		}
64074db5203Schristos 	} else {
64174db5203Schristos 		i = cdf_check_summary_info(ms, &info, &h,
64274db5203Schristos 		    &sat, &ssat, &sst, &dir, &scn, root_storage, &expn);
64374db5203Schristos 		cdf_zero_stream(&scn);
6441b108b8bSchristos 	}
64574db5203Schristos 	if (i <= 0) {
64674db5203Schristos 		if ((i = cdf_read_doc_summary_info(&info, &h, &sat, &ssat,
64774db5203Schristos 		    &sst, &dir, &scn)) == -1) {
64874db5203Schristos 			if (errno != ESRCH) {
64974db5203Schristos 				expn = "Cannot read summary info";
65020d96732Schristos 			}
6518dd459ccSchristos 		} else {
65274db5203Schristos 			i = cdf_check_summary_info(ms, &info, &h, &sat, &ssat,
65374db5203Schristos 			    &sst, &dir, &scn, root_storage, &expn);
65479bb278aSchristos 		}
6558dd459ccSchristos 	}
65674db5203Schristos 	if (i <= 0) {
65774db5203Schristos 		i = cdf_file_dir_info(ms, &dir);
65874db5203Schristos 		if (i < 0)
65974db5203Schristos 			expn = "Cannot read section info";
66074db5203Schristos 	}
661819e6405Schristos out5:
66274db5203Schristos 	cdf_zero_stream(&scn);
66374db5203Schristos 	cdf_zero_stream(&sst);
6641b108b8bSchristos out3:
6651b108b8bSchristos 	free(dir.dir_tab);
6661b108b8bSchristos out2:
6671b108b8bSchristos 	free(ssat.sat_tab);
6681b108b8bSchristos out1:
6691b108b8bSchristos 	free(sat.sat_tab);
6701b108b8bSchristos out0:
671c02f7f97Schristos 	/* If we handled it already, return */
672c02f7f97Schristos 	if (i != -1)
673c02f7f97Schristos 		return i;
674c02f7f97Schristos 	/* Provide a default handler */
67520d96732Schristos 	if (NOTMIME(ms)) {
67620d96732Schristos 		if (file_printf(ms,
67720d96732Schristos 		    "Composite Document File V2 Document") == -1)
6781b108b8bSchristos 			return -1;
6791b108b8bSchristos 		if (*expn)
68074db5203Schristos 			if (file_printf(ms, ", %s", expn) == -1)
6811b108b8bSchristos 				return -1;
682c02f7f97Schristos 	} else if (ms->flags & MAGIC_MIME_TYPE) {
6831d4cb158Schristos 		/* https://reposcope.com/mimetype/application/x-ole-storage */
6841d4cb158Schristos 		if (file_printf(ms, "application/x-ole-storage") == -1)
68520d96732Schristos 			return -1;
68620d96732Schristos 	}
687c02f7f97Schristos 	return 1;
6881b108b8bSchristos }
689