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#include "file.h"
27
28#ifndef lint
29FILE_RCSID("@(#)$File: readcdf.c,v 1.22 2010/01/20 01:36:55 christos Exp $")
30#endif
31
32#include <stdlib.h>
33#include <unistd.h>
34#include <string.h>
35#include <time.h>
36#include <ctype.h>
37
38#include "cdf.h"
39#include "magic.h"
40
41#define NOTMIME(ms) (((ms)->flags & MAGIC_MIME) == 0)
42
43private int
44cdf_file_property_info(struct magic_set *ms, const cdf_property_info_t *info,
45    size_t count)
46{
47	size_t i;
48	cdf_timestamp_t tp;
49	struct timespec ts;
50	char buf[64];
51	const char *str = "vnd.ms-office";
52	const char *s;
53	int len;
54
55	for (i = 0; i < count; i++) {
56		cdf_print_property_name(buf, sizeof(buf), info[i].pi_id);
57		switch (info[i].pi_type) {
58		case CDF_NULL:
59			break;
60		case CDF_SIGNED16:
61			if (NOTMIME(ms) && file_printf(ms, ", %s: %hd", buf,
62			    info[i].pi_s16) == -1)
63				return -1;
64			break;
65		case CDF_SIGNED32:
66			if (NOTMIME(ms) && file_printf(ms, ", %s: %d", buf,
67			    info[i].pi_s32) == -1)
68				return -1;
69			break;
70		case CDF_UNSIGNED32:
71			if (NOTMIME(ms) && file_printf(ms, ", %s: %u", buf,
72			    info[i].pi_u32) == -1)
73				return -1;
74			break;
75		case CDF_LENGTH32_STRING:
76		case CDF_LENGTH32_WSTRING:
77			len = info[i].pi_str.s_len;
78			if (len > 1) {
79				char vbuf[1024];
80				size_t j, k = 1;
81
82				if (info[i].pi_type == CDF_LENGTH32_WSTRING)
83				    k++;
84				s = info[i].pi_str.s_buf;
85				for (j = 0; j < sizeof(vbuf) && len--;
86				    j++, s += k) {
87					if (*s == '\0')
88						break;
89					if (isprint((unsigned char)*s))
90						vbuf[j] = *s;
91				}
92				if (j == sizeof(vbuf))
93					--j;
94				vbuf[j] = '\0';
95				if (NOTMIME(ms)) {
96					if (vbuf[0]) {
97						if (file_printf(ms, ", %s: %s",
98						    buf, vbuf) == -1)
99							return -1;
100					}
101				} else if (info[i].pi_id ==
102					CDF_PROPERTY_NAME_OF_APPLICATION) {
103					if (strstr(vbuf, "Word"))
104						str = "msword";
105					else if (strstr(vbuf, "Excel"))
106						str = "vnd.ms-excel";
107					else if (strstr(vbuf, "Powerpoint"))
108						str = "vnd.ms-powerpoint";
109					else if (strstr(vbuf,
110					    "Crystal Reports"))
111						str = "x-rpt";
112				}
113			}
114			break;
115		case CDF_FILETIME:
116			tp = info[i].pi_tp;
117			if (tp != 0) {
118				if (tp < 1000000000000000LL) {
119					char tbuf[64];
120					cdf_print_elapsed_time(tbuf,
121					    sizeof(tbuf), tp);
122					if (NOTMIME(ms) && file_printf(ms,
123					    ", %s: %s", buf, tbuf) == -1)
124						return -1;
125				} else {
126					char *c, *ec;
127					cdf_timestamp_to_timespec(&ts, tp);
128					c = ctime(&ts.tv_sec);
129					if (c != NULL && (ec = strchr(c, '\n')) != NULL)
130						*ec = '\0';
131
132					if (NOTMIME(ms) && file_printf(ms,
133					    ", %s: %s", buf, c) == -1)
134						return -1;
135				}
136			}
137			break;
138		case CDF_CLIPBOARD:
139			break;
140		default:
141			return -1;
142		}
143	}
144	if (!NOTMIME(ms)) {
145		if (file_printf(ms, "application/%s", str) == -1)
146			return -1;
147	}
148	return 1;
149}
150
151private int
152cdf_file_summary_info(struct magic_set *ms, const cdf_stream_t *sst)
153{
154	cdf_summary_info_header_t si;
155	cdf_property_info_t *info;
156	size_t count;
157	int m;
158
159	if (cdf_unpack_summary_info(sst, &si, &info, &count) == -1)
160		return -1;
161
162	if (NOTMIME(ms)) {
163		if (file_printf(ms, "CDF V2 Document") == -1)
164			return -1;
165
166		if (file_printf(ms, ", %s Endian",
167		    si.si_byte_order == 0xfffe ?  "Little" : "Big") == -1)
168			return -1;
169		switch (si.si_os) {
170		case 2:
171			if (file_printf(ms, ", Os: Windows, Version %d.%d",
172			    si.si_os_version & 0xff,
173			    (uint32_t)si.si_os_version >> 8) == -1)
174				return -1;
175			break;
176		case 1:
177			if (file_printf(ms, ", Os: MacOS, Version %d.%d",
178			    (uint32_t)si.si_os_version >> 8,
179			    si.si_os_version & 0xff) == -1)
180				return -1;
181			break;
182		default:
183			if (file_printf(ms, ", Os %d, Version: %d.%d", si.si_os,
184			    si.si_os_version & 0xff,
185			    (uint32_t)si.si_os_version >> 8) == -1)
186				return -1;
187			break;
188		}
189	}
190
191	m = cdf_file_property_info(ms, info, count);
192	free(info);
193
194	return m;
195}
196
197protected int
198file_trycdf(struct magic_set *ms, int fd, const unsigned char *buf,
199    size_t nbytes)
200{
201	cdf_info_t info;
202	cdf_header_t h;
203	cdf_sat_t sat, ssat;
204	cdf_stream_t sst, scn;
205	cdf_dir_t dir;
206	int i;
207	const char *expn = "";
208	const char *corrupt = "corrupt: ";
209
210	info.i_fd = fd;
211	info.i_buf = buf;
212	info.i_len = nbytes;
213	if (ms->flags & MAGIC_APPLE)
214		return 0;
215	if (cdf_read_header(&info, &h) == -1)
216		return 0;
217#ifdef CDF_DEBUG
218	cdf_dump_header(&h);
219#endif
220
221	if ((i = cdf_read_sat(&info, &h, &sat)) == -1) {
222		expn = "Can't read SAT";
223		goto out0;
224	}
225#ifdef CDF_DEBUG
226	cdf_dump_sat("SAT", &sat, CDF_SEC_SIZE(&h));
227#endif
228
229	if ((i = cdf_read_ssat(&info, &h, &sat, &ssat)) == -1) {
230		expn = "Can't read SSAT";
231		goto out1;
232	}
233#ifdef CDF_DEBUG
234	cdf_dump_sat("SSAT", &ssat, CDF_SHORT_SEC_SIZE(&h));
235#endif
236
237	if ((i = cdf_read_dir(&info, &h, &sat, &dir)) == -1) {
238		expn = "Can't read directory";
239		goto out2;
240	}
241
242	if ((i = cdf_read_short_stream(&info, &h, &sat, &dir, &sst)) == -1) {
243		expn = "Cannot read short stream";
244		goto out3;
245	}
246#ifdef CDF_DEBUG
247	cdf_dump_dir(&info, &h, &sat, &ssat, &sst, &dir);
248#endif
249
250	if ((i = cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir,
251	    &scn)) == -1) {
252		if (errno == ESRCH) {
253			corrupt = expn;
254			expn = "No summary info";
255		} else {
256			expn = "Cannot read summary info";
257		}
258		goto out4;
259	}
260#ifdef CDF_DEBUG
261	cdf_dump_summary_info(&h, &scn);
262#endif
263	if ((i = cdf_file_summary_info(ms, &scn)) == -1)
264		expn = "Can't expand summary_info";
265	free(scn.sst_tab);
266out4:
267	free(sst.sst_tab);
268out3:
269	free(dir.dir_tab);
270out2:
271	free(ssat.sat_tab);
272out1:
273	free(sat.sat_tab);
274out0:
275	if (i != 1) {
276		if (file_printf(ms, "CDF V2 Document") == -1)
277			return -1;
278		if (*expn)
279			if (file_printf(ms, ", %s%s", corrupt, expn) == -1)
280				return -1;
281		i = 1;
282	}
283	return i;
284}
285