1 /*
2 * tnef.c -- extract files from microsoft TNEF format
3 *
4 * Copyright (C)1999-2006 Mark Simpson <damned@theworld.com>
5 * Copyright (C)1997 Thomas Boll <tb@boll.ch> [ORIGINAL AUTHOR]
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
10 * any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you can either send email to this
19 * program's maintainer or write to: The Free Software Foundation,
20 * Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.
21 *
22 * Commentary:
23 * scans tnef file and extracts all attachments
24 * attachments are written to their original file-names if possible
25 */
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif /* HAVE_CONFIG_H */
29
30 #include "common.h"
31
32 #include "tnef.h"
33
34 #include "alloc.h"
35 #include "attr.h"
36 #include "debug.h"
37 #include "file.h"
38 #include "mapi_attr.h"
39 #include "options.h"
40 #include "path.h"
41 #include "rtf.h"
42 #include "util.h"
43
44 typedef struct
45 {
46 VarLenData **text_body;
47 VarLenData **html_bodies;
48 VarLenData **rtf_bodies;
49 } MessageBody;
50
51 typedef enum
52 {
53 TEXT = 't',
54 HTML = 'h',
55 RTF = 'r'
56 } MessageBodyTypes;
57
58 /* Reads and decodes a object from the stream */
59
60 static Attr*
read_object(FILE * in)61 read_object (FILE *in)
62 {
63 Attr *attr = NULL;
64
65 /* peek to see if there is more to read from this stream */
66 int tmp_char = fgetc(in);
67 if (tmp_char == -1) return NULL;
68 ungetc(tmp_char, in);
69
70 attr = attr_read (in);
71
72 return attr;
73 }
74
75 static void
free_bodies(VarLenData ** bodies,int len)76 free_bodies(VarLenData **bodies, int len)
77 {
78 while (len--)
79 {
80 XFREE(bodies[len]->data);
81 XFREE(bodies[len]);
82 }
83 }
84
85 static File**
get_body_files(const char * filename,const char pref,const MessageBody * body)86 get_body_files (const char* filename,
87 const char pref,
88 const MessageBody* body)
89 {
90 File **files = NULL;
91 VarLenData **data;
92 char *ext = "";
93 char *type = "unknown";
94 int i;
95
96 switch (pref)
97 {
98 case 'r':
99 data = body->rtf_bodies;
100 ext = ".rtf";
101 type = "text/rtf";
102 break;
103 case 'h':
104 data = body->html_bodies;
105 ext = ".html";
106 type = "text/html";
107 break;
108 case 't':
109 data = body->text_body;
110 ext = ".txt";
111 type = "text/plain";
112 break;
113 default:
114 data = NULL;
115 break;
116 }
117
118 if (data)
119 {
120 int count = 0;
121 char *tmp
122 = CHECKED_XCALLOC(char,
123 strlen(filename) + strlen(ext) + 1);
124 strcpy (tmp, filename);
125 strcat (tmp, ext);
126
127 char *mime = CHECKED_XCALLOC(char, strlen(type) + 1);
128 strcpy (mime, type);
129
130 /* first get a count */
131 while (data[count++]);
132
133 files = (File**)XCALLOC(File*, count + 1);
134 for (i = 0; data[i]; i++)
135 {
136 files[i] = (File*)XCALLOC(File, 1);
137 files[i]->name = tmp;
138 files[i]->mime_type = mime;
139 files[i]->len = data[i]->len;
140 files[i]->data
141 = CHECKED_XMALLOC(unsigned char, data[i]->len);
142 memmove (files[i]->data, data[i]->data, data[i]->len);
143 }
144 }
145 return files;
146 }
147
148 static VarLenData**
get_text_data(Attr * attr)149 get_text_data (Attr *attr)
150 {
151 VarLenData **body = XCALLOC(VarLenData*, 2);
152
153 body[0] = XCALLOC(VarLenData, 1);
154 body[0]->len = attr->len;
155 body[0]->data = CHECKED_XCALLOC(unsigned char, attr->len);
156 memmove (body[0]->data, attr->buf, attr->len);
157 return body;
158 }
159
160 static VarLenData**
get_html_data(MAPI_Attr * a)161 get_html_data (MAPI_Attr *a)
162 {
163 VarLenData **body = XCALLOC(VarLenData*, a->num_values + 1);
164
165 int j;
166 for (j = 0; j < a->num_values; j++)
167 {
168 body[j] = XMALLOC(VarLenData, 1);
169 body[j]->len = a->values[j].len;
170 body[j]->data = CHECKED_XCALLOC(unsigned char, a->values[j].len);
171 memmove (body[j]->data, a->values[j].data.buf, body[j]->len);
172 }
173 return body;
174 }
175
176 int
data_left(FILE * input_file)177 data_left (FILE* input_file)
178 {
179 int retval = 1;
180
181 if (feof(input_file)) retval = 0;
182 else if (input_file != stdin)
183 {
184 /* check if there is enough data left */
185 struct stat statbuf;
186 size_t pos, data_left;
187 fstat (fileno(input_file), &statbuf);
188 pos = ftell(input_file);
189 data_left = (statbuf.st_size - pos);
190
191 if (data_left > 0 && data_left < MINIMUM_ATTR_LENGTH)
192 {
193 if ( CRUFT_SKIP )
194 {
195 /* look for specific flavor of cruft -- trailing "\r\n" */
196
197 if ( data_left == 2 )
198 {
199 int c = fgetc( input_file );
200
201 if ( c < 0 ) /* this should never happen */
202 {
203 fprintf( stderr, "ERROR: confused beyond all redemption.\n" );
204 exit (1);
205 }
206
207 ungetc( c, input_file );
208
209 if ( c == 0x0d ) /* test for "\r" part of "\r\n" */
210 {
211 /* "trust" that next char is 0x0a and ignore this cruft */
212
213 if ( VERBOSE_ON )
214 fprintf( stderr, "WARNING: garbage at end of file (ignored)\n" );
215
216 if ( DEBUG_ON )
217 debug_print( "!!garbage at end of file (ignored)\n" );
218 }
219 else
220 {
221 fprintf( stderr, "ERROR: garbage at end of file.\n" );
222 }
223 }
224 else
225 {
226 fprintf (stderr, "ERROR: garbage at end of file.\n");
227 }
228 }
229 else
230 {
231 fprintf (stderr, "ERROR: garbage at end of file.\n");
232 }
233
234 retval = 0;
235 }
236 }
237 return retval;
238 }
239
240
241 /* The entry point into this module. This parses an entire TNEF file. */
242 int
parse_file(FILE * input_file,char * directory,char * body_filename,char * body_pref,int flags)243 parse_file (FILE* input_file, char* directory,
244 char *body_filename, char *body_pref,
245 int flags)
246 {
247 uint32 d;
248 uint16 key;
249 Attr *attr = NULL;
250 File *file = NULL;
251 int rtf_size = 0, html_size = 0;
252 MessageBody body;
253 memset (&body, '\0', sizeof (MessageBody));
254
255 /* store the program options in our file global variables */
256 g_flags = flags;
257
258 /* check that this is in fact a TNEF file */
259 d = geti32(input_file);
260 if (d != TNEF_SIGNATURE)
261 {
262 fprintf (stdout, "Seems not to be a TNEF file\n");
263 return 1;
264 }
265
266 /* Get the key */
267 key = geti16(input_file);
268 debug_print ("TNEF Key: %hx\n", key);
269
270 /* The rest of the file is a series of 'messages' and 'attachments' */
271 while ( data_left( input_file ) )
272 {
273 attr = read_object( input_file );
274
275 if ( attr == NULL ) break;
276
277 /* This signals the beginning of a file */
278 if (attr->name == attATTACHRENDDATA)
279 {
280 if (file)
281 {
282 file_write (file, directory);
283 file_free (file);
284 }
285 else
286 {
287 file = CHECKED_XCALLOC (File, 1);
288 }
289 }
290
291 /* Add the data to our lists. */
292 switch (attr->lvl_type)
293 {
294 case LVL_MESSAGE:
295 if (attr->name == attBODY)
296 {
297 body.text_body = get_text_data (attr);
298 }
299 else if (attr->name == attMAPIPROPS)
300 {
301 MAPI_Attr **mapi_attrs
302 = mapi_attr_read (attr->len, attr->buf);
303 if (mapi_attrs)
304 {
305 int i;
306 for (i = 0; mapi_attrs[i]; i++)
307 {
308 MAPI_Attr *a = mapi_attrs[i];
309
310 if (a->name == MAPI_BODY_HTML)
311 {
312 body.html_bodies = get_html_data (a);
313 html_size = a->num_values;
314 }
315 else if (a->name == MAPI_RTF_COMPRESSED)
316 {
317 body.rtf_bodies = get_rtf_data (a);
318 rtf_size = a->num_values;
319 }
320 }
321 /* cannot save attributes to file, since they
322 * are not attachment attributes */
323 /* file_add_mapi_attrs (file, mapi_attrs); */
324 mapi_attr_free_list (mapi_attrs);
325 XFREE (mapi_attrs);
326 }
327 }
328 break;
329 case LVL_ATTACHMENT:
330 file_add_attr (file, attr);
331 break;
332 default:
333 fprintf (stderr, "Invalid lvl type on attribute: %d\n",
334 attr->lvl_type);
335 return 1;
336 break;
337 }
338 attr_free (attr);
339 XFREE (attr);
340 }
341
342 if (file)
343 {
344 file_write (file, directory);
345 file_free (file);
346 XFREE (file);
347 }
348
349 /* Write the message body */
350 if (flags & SAVEBODY)
351 {
352 int i = 0;
353 int all_flag = 0;
354 if (strcmp (body_pref, "all") == 0)
355 {
356 all_flag = 1;
357 body_pref = "rht";
358 }
359
360 for (; i < 3; i++)
361 {
362 File **files
363 = get_body_files (body_filename, body_pref[i], &body);
364 if (files)
365 {
366 int j = 0;
367 for (; files[j]; j++)
368 {
369 file_write(files[j], directory);
370 file_free (files[j]);
371 XFREE(files[j]);
372 }
373 XFREE(files);
374 if (!all_flag) break;
375 }
376 }
377 }
378
379 if (body.text_body)
380 {
381 free_bodies(body.text_body, 1);
382 XFREE(body.text_body);
383 }
384 if (rtf_size > 0)
385 {
386 free_bodies(body.rtf_bodies, rtf_size);
387 XFREE(body.rtf_bodies);
388 }
389 if (html_size > 0)
390 {
391 free_bodies(body.html_bodies, html_size);
392 XFREE(body.html_bodies);
393 }
394 return 0;
395 }
396
397