Revert "Import file-5.22."
[dragonfly.git] / contrib / file / src / readcdf.c
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
29 FILE_RCSID("@(#)$File: readcdf.c,v 1.40 2014/03/06 15:23:33 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 #if defined(HAVE_LOCALE_H)
39 #include <locale.h>
40 #endif
41
42 #include "cdf.h"
43 #include "magic.h"
44
45 #define NOTMIME(ms) (((ms)->flags & MAGIC_MIME) == 0)
46
47 static const struct nv {
48         const char *pattern;
49         const char *mime;
50 } app2mime[] =  {
51         { "Word",                       "msword",               },
52         { "Excel",                      "vnd.ms-excel",         },
53         { "Powerpoint",                 "vnd.ms-powerpoint",    },
54         { "Crystal Reports",            "x-rpt",                },
55         { "Advanced Installer",         "vnd.ms-msi",           },
56         { "InstallShield",              "vnd.ms-msi",           },
57         { "Microsoft Patch Compiler",   "vnd.ms-msi",           },
58         { "NAnt",                       "vnd.ms-msi",           },
59         { "Windows Installer",          "vnd.ms-msi",           },
60         { NULL,                         NULL,                   },
61 }, name2mime[] = {
62         { "WordDocument",               "msword",               },
63         { "PowerPoint",                 "vnd.ms-powerpoint",    },
64         { "DigitalSignature",           "vnd.ms-msi",           },
65         { NULL,                         NULL,                   },
66 }, name2desc[] = {
67         { "WordDocument",               "Microsoft Office Word",},
68         { "PowerPoint",                 "Microsoft PowerPoint", },
69         { "DigitalSignature",           "Microsoft Installer",  },
70         { NULL,                         NULL,                   },
71 };
72
73 static const struct cv {
74         uint64_t clsid[2];
75         const char *mime;
76 } clsid2mime[] = {
77         {
78                 { 0x00000000000c1084LLU, 0x46000000000000c0LLU },
79                 "x-msi",
80         }
81 }, clsid2desc[] = {
82         {
83                 { 0x00000000000c1084LLU, 0x46000000000000c0LLU },
84                 "MSI Installer",
85         },
86 };
87
88 private const char *
89 cdf_clsid_to_mime(const uint64_t clsid[2], const struct cv *cv)
90 {
91         size_t i;
92         for (i = 0; cv[i].mime != NULL; i++) {
93                 if (clsid[0] == cv[i].clsid[0] && clsid[1] == cv[i].clsid[1])
94                         return cv[i].mime;
95         }
96         return NULL;
97 }
98
99 private const char *
100 cdf_app_to_mime(const char *vbuf, const struct nv *nv)
101 {
102         size_t i;
103         const char *rv = NULL;
104         char *old_lc_ctype;
105
106         old_lc_ctype = setlocale(LC_CTYPE, NULL);
107         assert(old_lc_ctype != NULL);
108         old_lc_ctype = strdup(old_lc_ctype);
109         assert(old_lc_ctype != NULL);
110         (void)setlocale(LC_CTYPE, "C");
111         for (i = 0; nv[i].pattern != NULL; i++)
112                 if (strcasestr(vbuf, nv[i].pattern) != NULL) {
113                         rv = nv[i].mime;
114                         break;
115                 }
116         (void)setlocale(LC_CTYPE, old_lc_ctype);
117         free(old_lc_ctype);
118         return rv;
119 }
120
121 private int
122 cdf_file_property_info(struct magic_set *ms, const cdf_property_info_t *info,
123     size_t count, const uint64_t clsid[2])
124 {
125         size_t i;
126         cdf_timestamp_t tp;
127         struct timespec ts;
128         char buf[64];
129         const char *str = NULL;
130         const char *s;
131         int len;
132
133         if (!NOTMIME(ms))
134                 str = cdf_clsid_to_mime(clsid, clsid2mime);
135
136         for (i = 0; i < count; i++) {
137                 cdf_print_property_name(buf, sizeof(buf), info[i].pi_id);
138                 switch (info[i].pi_type) {
139                 case CDF_NULL:
140                         break;
141                 case CDF_SIGNED16:
142                         if (NOTMIME(ms) && file_printf(ms, ", %s: %hd", buf,
143                             info[i].pi_s16) == -1)
144                                 return -1;
145                         break;
146                 case CDF_SIGNED32:
147                         if (NOTMIME(ms) && file_printf(ms, ", %s: %d", buf,
148                             info[i].pi_s32) == -1)
149                                 return -1;
150                         break;
151                 case CDF_UNSIGNED32:
152                         if (NOTMIME(ms) && file_printf(ms, ", %s: %u", buf,
153                             info[i].pi_u32) == -1)
154                                 return -1;
155                         break;
156                 case CDF_FLOAT:
157                         if (NOTMIME(ms) && file_printf(ms, ", %s: %g", buf,
158                             info[i].pi_f) == -1)
159                                 return -1;
160                         break;
161                 case CDF_DOUBLE:
162                         if (NOTMIME(ms) && file_printf(ms, ", %s: %g", buf,
163                             info[i].pi_d) == -1)
164                                 return -1;
165                         break;
166                 case CDF_LENGTH32_STRING:
167                 case CDF_LENGTH32_WSTRING:
168                         len = info[i].pi_str.s_len;
169                         if (len > 1) {
170                                 char vbuf[1024];
171                                 size_t j, k = 1;
172
173                                 if (info[i].pi_type == CDF_LENGTH32_WSTRING)
174                                     k++;
175                                 s = info[i].pi_str.s_buf;
176                                 for (j = 0; j < sizeof(vbuf) && len--;
177                                     j++, s += k) {
178                                         if (*s == '\0')
179                                                 break;
180                                         if (isprint((unsigned char)*s))
181                                                 vbuf[j] = *s;
182                                 }
183                                 if (j == sizeof(vbuf))
184                                         --j;
185                                 vbuf[j] = '\0';
186                                 if (NOTMIME(ms)) {
187                                         if (vbuf[0]) {
188                                                 if (file_printf(ms, ", %s: %s",
189                                                     buf, vbuf) == -1)
190                                                         return -1;
191                                         }
192                                 } else if (str == NULL && info[i].pi_id ==
193                                     CDF_PROPERTY_NAME_OF_APPLICATION) {
194                                         str = cdf_app_to_mime(vbuf, app2mime);
195                                 }
196                         }
197                         break;
198                 case CDF_FILETIME:
199                         tp = info[i].pi_tp;
200                         if (tp != 0) {
201                                 char tbuf[64];
202                                 if (tp < 1000000000000000LL) {
203                                         cdf_print_elapsed_time(tbuf,
204                                             sizeof(tbuf), tp);
205                                         if (NOTMIME(ms) && file_printf(ms,
206                                             ", %s: %s", buf, tbuf) == -1)
207                                                 return -1;
208                                 } else {
209                                         char *c, *ec;
210                                         cdf_timestamp_to_timespec(&ts, tp);
211                                         c = cdf_ctime(&ts.tv_sec, tbuf);
212                                         if (c != NULL &&
213                                             (ec = strchr(c, '\n')) != NULL)
214                                                 *ec = '\0';
215
216                                         if (NOTMIME(ms) && file_printf(ms,
217                                             ", %s: %s", buf, c) == -1)
218                                                 return -1;
219                                 }
220                         }
221                         break;
222                 case CDF_CLIPBOARD:
223                         break;
224                 default:
225                         return -1;
226                 }
227         }
228         if (!NOTMIME(ms)) {
229                 if (str == NULL)
230                         return 0;
231                 if (file_printf(ms, "application/%s", str) == -1)
232                         return -1;
233         }
234         return 1;
235 }
236
237 private int
238 cdf_file_summary_info(struct magic_set *ms, const cdf_header_t *h,
239     const cdf_stream_t *sst, const uint64_t clsid[2])
240 {
241         cdf_summary_info_header_t si;
242         cdf_property_info_t *info;
243         size_t count;
244         int m;
245
246         if (cdf_unpack_summary_info(sst, h, &si, &info, &count) == -1)
247                 return -1;
248
249         if (NOTMIME(ms)) {
250                 const char *str;
251
252                 if (file_printf(ms, "Composite Document File V2 Document")
253                     == -1)
254                         return -1;
255
256                 if (file_printf(ms, ", %s Endian",
257                     si.si_byte_order == 0xfffe ?  "Little" : "Big") == -1)
258                         return -2;
259                 switch (si.si_os) {
260                 case 2:
261                         if (file_printf(ms, ", Os: Windows, Version %d.%d",
262                             si.si_os_version & 0xff,
263                             (uint32_t)si.si_os_version >> 8) == -1)
264                                 return -2;
265                         break;
266                 case 1:
267                         if (file_printf(ms, ", Os: MacOS, Version %d.%d",
268                             (uint32_t)si.si_os_version >> 8,
269                             si.si_os_version & 0xff) == -1)
270                                 return -2;
271                         break;
272                 default:
273                         if (file_printf(ms, ", Os %d, Version: %d.%d", si.si_os,
274                             si.si_os_version & 0xff,
275                             (uint32_t)si.si_os_version >> 8) == -1)
276                                 return -2;
277                         break;
278                 }
279                 str = cdf_clsid_to_mime(clsid, clsid2desc);
280                 if (str)
281                         if (file_printf(ms, ", %s", str) == -1)
282                                 return -2;
283         }
284
285         m = cdf_file_property_info(ms, info, count, clsid);
286         free(info);
287
288         return m == -1 ? -2 : m;
289 }
290
291 #ifdef notdef
292 private char *
293 format_clsid(char *buf, size_t len, const uint64_t uuid[2]) {
294         snprintf(buf, len, "%.8" PRIx64 "-%.4" PRIx64 "-%.4" PRIx64 "-%.4" 
295             PRIx64 "-%.12" PRIx64,
296             (uuid[0] >> 32) & (uint64_t)0x000000000ffffffffLLU,
297             (uuid[0] >> 16) & (uint64_t)0x0000000000000ffffLLU,
298             (uuid[0] >>  0) & (uint64_t)0x0000000000000ffffLLU, 
299             (uuid[1] >> 48) & (uint64_t)0x0000000000000ffffLLU,
300             (uuid[1] >>  0) & (uint64_t)0x0000fffffffffffffLLU);
301         return buf;
302 }
303 #endif
304
305 protected int
306 file_trycdf(struct magic_set *ms, int fd, const unsigned char *buf,
307     size_t nbytes)
308 {
309         cdf_info_t info;
310         cdf_header_t h;
311         cdf_sat_t sat, ssat;
312         cdf_stream_t sst, scn;
313         cdf_dir_t dir;
314         int i;
315         const char *expn = "";
316         const char *corrupt = "corrupt: ";
317
318         info.i_fd = fd;
319         info.i_buf = buf;
320         info.i_len = nbytes;
321         if (ms->flags & MAGIC_APPLE)
322                 return 0;
323         if (cdf_read_header(&info, &h) == -1)
324                 return 0;
325 #ifdef CDF_DEBUG
326         cdf_dump_header(&h);
327 #endif
328
329         if ((i = cdf_read_sat(&info, &h, &sat)) == -1) {
330                 expn = "Can't read SAT";
331                 goto out0;
332         }
333 #ifdef CDF_DEBUG
334         cdf_dump_sat("SAT", &sat, CDF_SEC_SIZE(&h));
335 #endif
336
337         if ((i = cdf_read_ssat(&info, &h, &sat, &ssat)) == -1) {
338                 expn = "Can't read SSAT";
339                 goto out1;
340         }
341 #ifdef CDF_DEBUG
342         cdf_dump_sat("SSAT", &ssat, CDF_SHORT_SEC_SIZE(&h));
343 #endif
344
345         if ((i = cdf_read_dir(&info, &h, &sat, &dir)) == -1) {
346                 expn = "Can't read directory";
347                 goto out2;
348         }
349
350         const cdf_directory_t *root_storage;
351         if ((i = cdf_read_short_stream(&info, &h, &sat, &dir, &sst,
352             &root_storage)) == -1) {
353                 expn = "Cannot read short stream";
354                 goto out3;
355         }
356 #ifdef CDF_DEBUG
357         cdf_dump_dir(&info, &h, &sat, &ssat, &sst, &dir);
358 #endif
359 #ifdef notdef
360         if (root_storage) {
361                 if (NOTMIME(ms)) {
362                         char clsbuf[128];
363                         if (file_printf(ms, "CLSID %s, ",
364                             format_clsid(clsbuf, sizeof(clsbuf),
365                             root_storage->d_storage_uuid)) == -1)
366                                 return -1;
367                 }
368         }
369 #endif
370
371         if ((i = cdf_read_summary_info(&info, &h, &sat, &ssat, &sst, &dir,
372             &scn)) == -1) {
373                 if (errno == ESRCH) {
374                         corrupt = expn;
375                         expn = "No summary info";
376                 } else {
377                         expn = "Cannot read summary info";
378                 }
379                 goto out4;
380         }
381 #ifdef CDF_DEBUG
382         cdf_dump_summary_info(&h, &scn);
383 #endif
384         if ((i = cdf_file_summary_info(ms, &h, &scn,
385             root_storage->d_storage_uuid)) < 0)
386                 expn = "Can't expand summary_info";
387
388         if (i == 0) {
389                 const char *str = NULL;
390                 cdf_directory_t *d;
391                 char name[__arraycount(d->d_name)];
392                 size_t j, k;
393
394                 for (j = 0; str == NULL && j < dir.dir_len; j++) {
395                         d = &dir.dir_tab[j];
396                         for (k = 0; k < sizeof(name); k++)
397                                 name[k] = (char)cdf_tole2(d->d_name[k]);
398                         str = cdf_app_to_mime(name,
399                             NOTMIME(ms) ? name2desc : name2mime);
400                 }
401                 if (NOTMIME(ms)) {
402                         if (str != NULL) {
403                                 if (file_printf(ms, "%s", str) == -1)
404                                         return -1;
405                                 i = 1;
406                         }
407                 } else {
408                         if (str == NULL)
409                                 str = "vnd.ms-office";
410                         if (file_printf(ms, "application/%s", str) == -1)
411                                 return -1;
412                         i = 1;
413                 }
414         }
415         free(scn.sst_tab);
416 out4:
417         free(sst.sst_tab);
418 out3:
419         free(dir.dir_tab);
420 out2:
421         free(ssat.sat_tab);
422 out1:
423         free(sat.sat_tab);
424 out0:
425         if (i == -1) {
426             if (NOTMIME(ms)) {
427                 if (file_printf(ms,
428                     "Composite Document File V2 Document") == -1)
429                     return -1;
430                 if (*expn)
431                     if (file_printf(ms, ", %s%s", corrupt, expn) == -1)
432                         return -1;
433             } else {
434                 if (file_printf(ms, "application/CDFV2-corrupt") == -1)
435                     return -1;
436             }
437             i = 1;
438         }
439         return i;
440 }