1 /***************************************************************************
2 * tnef2txt
3 * A program to decode application/ms-tnef MIME attachments into text
4 * for those fortunate enough not to be running either a Microsoft
5 * operating system or mailer.
6 *
7 * Brandon Long (blong@uiuc.edu), April 1997
8 * 1.0 Version
9 * Supports most types, but doesn't decode properties. Maybe some other
10 * time.
11 *
12 * 1.1 Version (7/1/97)
13 * Supports saving of attAttachData to a file given by attAttachTitle
14 * start of property decoding support
15 *
16 * 1.2 Version (7/19/97)
17 * Some architectures don't like reading 16/32 bit data on unaligned
18 * boundaries. Fixed, losing efficiency, but this doesn't really
19 * need efficiency anyways. (Still...)
20 * Also, the #pragma pack from the MSVC include file wasn't liked
21 * by most Unix compilers, replaced with a GCCism. This should work
22 * with GCC, but other compilers I don't know.
23 *
24 * 1.3 Version (7/22/97)
25 * Ok, take out the DTR over the stream, now uses read_16.
26 *
27 * NOTE: THIS SOFTWARE IS FOR YOUR PERSONAL GRATIFICATION ONLY. I DON'T
28 * IMPLY IN ANY LEGAL SENSE THAT THIS SOFTWARE DOES ANYTHING OR THAT IT WILL
29 * BE USEFULL IN ANY WAY. But, you can send me fixes to it, I don't mind.
30 ***************************************************************************/
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/stat.h>
36 #include "config.h"
37 #include "tnef.h"
38 #include "mapidefs.h"
39 #include "mapitags.h"
40
41 #define VERSION "tnef2txt/1.3"
42
43 int save_attach_data(char *title, uint8 *tsp, uint32 size);
44
45 int Verbose = FALSE;
46 int SaveData = FALSE;
47
48 /* Some systems don't like to read unaligned data */
read_32(uint8 * tsp)49 uint32 read_32(uint8 *tsp)
50 {
51 uint8 a,b,c,d;
52 uint32 ret;
53
54 a = *tsp;
55 b = *(tsp+1);
56 c = *(tsp+2);
57 d = *(tsp+3);
58
59 ret = long_little_endian(a<<24 | b<<16 | c<<8 | d);
60
61 return ret;
62 }
63
read_16(uint8 * tsp)64 uint16 read_16(uint8 *tsp)
65 {
66 uint8 a,b;
67 uint16 ret;
68
69 a = *tsp;
70 b = *(tsp + 1);
71
72 ret = little_endian(a<<8 | b);
73
74 return ret;
75 }
76
77
attribute_string(uint32 attribute)78 char *attribute_string(uint32 attribute)
79 {
80 switch (attribute) {
81 case attNull:
82 return ("attNull");
83 break;
84 case attFrom:
85 return ("attFrom");
86 break;
87 case attSubject:
88 return ("attSubject");
89 break;
90 case attDateSent:
91 return ("attDateSent");
92 break;
93 case attDateRecd:
94 return ("attDateRecd");
95 break;
96 case attMessageStatus:
97 return ("attMessageStatus");
98 break;
99 case attMessageClass:
100 return ("attMessageClass");
101 break;
102 case attMessageID:
103 return ("attMessageID");
104 break;
105 case attParentID:
106 return ("attParentID");
107 break;
108 case attConversationID:
109 return ("attConversationID");
110 break;
111 case attBody:
112 return ("attBody");
113 break;
114 case attPriority:
115 return ("attPriority");
116 break;
117 case attAttachData:
118 return ("attAttachData");
119 break;
120 case attAttachTitle:
121 return ("attAttachTitle");
122 break;
123 case attAttachMetaFile:
124 return ("attAttachMetaFile");
125 break;
126 case attAttachCreateDate:
127 return ("attAttachCreateDate");
128 break;
129 case attAttachModifyDate:
130 return ("attAttachModifyDate");
131 break;
132 case attDateModified:
133 return ("attDateModified");
134 break;
135 case attAttachTransportFilename:
136 return ("attAttachTransportFilename");
137 break;
138 case attAttachRenddata:
139 return ("attAttachRenddata");
140 break;
141 case attMAPIProps:
142 return ("attMAPIProps");
143 break;
144 case attRecipTable:
145 return ("attRecipTable");
146 break;
147 case attAttachment:
148 return ("attAttachment");
149 break;
150 case attTnefVersion:
151 return ("attTnefVersion");
152 break;
153 case attOemCodepage:
154 return ("attOemCodepage");
155 break;
156 case attOriginalMessageClass:
157 return ("attOriginalMessageClass");
158 break;
159 case attOwner:
160 return ("attOwner");
161 break;
162 case attSentFor:
163 return ("attSentFor");
164 break;
165 case attDelegate:
166 return ("attDelegate");
167 break;
168 case attDateStart:
169 return ("attDateStart");
170 break;
171 case attDateEnd:
172 return ("attDateEnd");
173 break;
174 case attAidOwner:
175 return ("attAidOwner");
176 break;
177 case attRequestRes:
178 return ("attRequestRes");
179 break;
180 default:
181 return ("Unknown");
182 break;
183 }
184 return ("Unknown");
185 }
186
type_string(uint16 type)187 char *type_string(uint16 type)
188 {
189 switch (type) {
190 case atpTriples:
191 return ("atpTriples");
192 break;
193 case atpString:
194 return ("atpString");
195 break;
196 case atpText:
197 return ("atpText");
198 break;
199 case atpDate:
200 return ("atpDate");
201 break;
202 case atpShort:
203 return ("atpShort");
204 break;
205 case atpLong:
206 return ("atpLong");
207 break;
208 case atpByte:
209 return ("atpByte");
210 break;
211 case atpWord:
212 return ("atpWord");
213 break;
214 case atpDword:
215 return ("atpDword");
216 break;
217 default:
218 return ("unknown");
219 break;
220 }
221 return ("unknown");
222 }
223
prop_type_string(uint32 prop_tag)224 char *prop_type_string(uint32 prop_tag)
225 {
226 switch(prop_tag)
227 {
228 case PT_BINARY:
229 return ("PT_BINARY");
230 break;
231 case PT_STRING8:
232 return ("PT_STRING8");
233 break;
234 case PT_UNICODE:
235 return ("PT_UNICODE");
236 break;
237 case PT_OBJECT:
238 return ("PT_OBJECT");
239 break;
240 case PT_I2:
241 return ("PT_I2");
242 break;
243 case PT_LONG:
244 return ("PT_LONG");
245 break;
246 case PT_R4:
247 return ("PT_R4");
248 break;
249 case PT_DOUBLE:
250 return ("PT_DOUBLE");
251 break;
252 case PT_CURRENCY:
253 return ("PT_CURRENCY");
254 break;
255 case PT_APPTIME:
256 return ("PT_APPTIME");
257 break;
258 case PT_ERROR:
259 return ("PT_ERROR");
260 break;
261 case PT_BOOLEAN:
262 return ("PT_BOOLEAN");
263 break;
264 case PT_I8:
265 return ("PT_I8");
266 break;
267 case PT_SYSTIME:
268 return ("PT_SYSTIME");
269 break;
270 }
271 return ("unknown");
272 }
273
274 char *weekday[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
275 "Friday", "Saturday" };
276
date_string(uint8 * tsp)277 char *date_string(uint8 *tsp)
278 {
279 static char date_string[256] = "";
280 DTR date;
281 int bytes = 0;
282
283 date.wYear = read_16(tsp);
284 bytes += 2;
285 date.wMonth = read_16(tsp+bytes);
286 bytes += 2;
287 date.wDay = read_16(tsp+bytes);
288 bytes += 2;
289 date.wHour = read_16(tsp+bytes);
290 bytes += 2;
291 date.wMinute = read_16(tsp+bytes);
292 bytes += 2;
293 date.wSecond = read_16(tsp+bytes);
294 bytes += 2;
295 date.wDayOfWeek = read_16(tsp+bytes);
296 bytes += 2;
297
298 sprintf(date_string, "%s, %d/%d/%d ", weekday[date.wDayOfWeek],
299 date.wMonth, date.wDay, date.wYear);
300 return date_string;
301 }
302
make_string(uint8 * tsp,int size)303 char *make_string(uint8 *tsp, int size)
304 {
305 static char s[256] = "";
306 int len = (size>sizeof(s)-1) ? sizeof(s)-1 : size;
307
308 strncpy(s,tsp, len);
309 s[len] = '\0';
310 return s;
311 }
312
priority_string(uint16 pri)313 char *priority_string(uint16 pri)
314 {
315 switch(pri) {
316 case prioLow:
317 return "(Low)";
318 case prioNorm:
319 return "(Normal)";
320 case prioHigh:
321 return "(High)";
322 default:
323 return "";
324 }
325 return "";
326 }
327
status_string(uint8 status)328 char *status_string(uint8 status)
329 {
330 static char s[256] = "";
331
332 s[0] = '\0';
333 if (status & fmsModified)
334 sprintf(s,"%s%s ",s,"Modified");
335 if (status & fmsLocal)
336 sprintf(s,"%s%s ",s,"Local");
337 if (status & fmsSubmitted)
338 sprintf(s,"%s%s ",s,"Submitted");
339 if (status & fmsRead)
340 sprintf(s,"%s%s ",s,"Read");
341 if (status & fmsHasAttach)
342 sprintf(s,"%s%s ",s,"HasAttach");
343 return s;
344 }
345
handle_props(uint8 * tsp)346 int handle_props(uint8 *tsp)
347 {
348 int bytes = 0;
349 uint32 num_props = 0;
350 uint32 x = 0;
351
352
353 num_props = read_32(tsp);
354 bytes += sizeof(num_props);
355
356 printf("\tNumber of Properties: %ld\n", num_props);
357 while (x < num_props)
358 {
359 uint32 prop_tag;
360 uint32 num;
361 char filename[256];
362 static int file_num = 0;
363
364 prop_tag = read_32(tsp+bytes);
365 bytes += sizeof(prop_tag);
366 if (Verbose)
367 printf("\tProperty: %08lx (%s)\n", prop_tag,
368 prop_type_string(prop_tag & PROP_TYPE_MASK));
369 switch (prop_tag & PROP_TYPE_MASK)
370 {
371 case PT_BINARY:
372 num = read_32(tsp+bytes);
373 bytes += sizeof(num);
374 num = read_32(tsp+bytes);
375 bytes += sizeof(num);
376 if (prop_tag == PR_RTF_COMPRESSED)
377 {
378 sprintf (filename, "tnef_%d_%0lx.rtf", file_num, num);
379 file_num++;
380 if (SaveData) {
381 printf ("\tSaving Compressed RTF to %s\n", filename);
382 save_attach_data(filename, tsp+bytes, num);
383 } else {
384 printf ("\tContains Compressed RTF MAPI property\n");
385 }
386 }
387 /* num + PAD */
388 bytes += num + ((num % 4) ? (4 - num%4) : 0);
389 break;
390 case PT_STRING8:
391 switch (prop_tag)
392 {
393 case PR_CONVERSATION_TOPIC:
394 printf ("\tConversation Topic: ");
395 break;
396 case PR_SENDER_ADDRTYPE:
397 printf ("\tSender Address Type: ");
398 break;
399 case PR_SENDER_EMAIL_ADDRESS:
400 printf ("\tSender Email Address: ");
401 break;
402 case PR_RTF_SYNC_BODY_TAG:
403 printf ("\tRTF Sync body tag: ");
404 break;
405 case PR_SUBJECT_PREFIX:
406 printf ("\tSubject Prefix: ");
407 break;
408 default:
409 printf ("\tUnknown String: ");
410 break;
411 }
412 num = read_32(tsp+bytes);
413 bytes += sizeof(num);
414 num = read_32(tsp+bytes);
415 bytes += sizeof(num);
416 printf ("%s\n", make_string(tsp+bytes,num));
417 bytes += num + ((num % 4) ? (4 - num%4) : 0);
418 break;
419 case PT_UNICODE:
420 case PT_OBJECT:
421 break;
422 case PT_I2:
423 bytes += 2;
424 break;
425 case PT_LONG:
426 bytes += 4;
427 break;
428 case PT_R4:
429 bytes += 4;
430 break;
431 case PT_DOUBLE:
432 bytes += 8;
433 break;
434 case PT_CURRENCY:
435 case PT_APPTIME:
436 case PT_ERROR:
437 bytes += 4;
438 break;
439 case PT_BOOLEAN:
440 bytes += 4;
441 break;
442 case PT_I8:
443 bytes += 8;
444 case PT_SYSTIME:
445 bytes += 8;
446 break;
447 }
448 x++;
449 }
450
451 }
452
save_attach_data(char * title,uint8 * tsp,uint32 size)453 int save_attach_data(char *title, uint8 *tsp, uint32 size)
454 {
455 FILE *out;
456 char filename[256];
457
458 strcpy(filename, title);
459 out = fopen(filename, "w");
460 if (out == NULL) {
461 fprintf(stderr, "Error openning file %s for writing\n", filename);
462 return -1;
463 }
464 fwrite(tsp, sizeof(uint8), size, out);
465 fclose(out);
466 return 0;
467 }
468
default_handler(uint32 attribute,uint8 * tsp,uint32 size)469 int default_handler(uint32 attribute, uint8 *tsp, uint32 size)
470 {
471 uint16 type = ATT_TYPE(attribute);
472
473 switch (type) {
474 case atpTriples:
475 break;
476 case atpString:
477 case atpText:
478 printf("Attribute %s: ", attribute_string(attribute));
479 printf("%s\n",make_string(tsp,size));
480 break;
481 case atpDate:
482 printf("Attribute %s: ", attribute_string(attribute));
483 printf("%s\n",date_string(tsp));
484 break;
485 case atpShort:
486 break;
487 case atpLong:
488 break;
489 case atpByte:
490 break;
491 case atpWord:
492 break;
493 case atpDword:
494 break;
495 default:
496 printf("Attribute %s: ", attribute_string(attribute));
497 printf("Unknown type\n");
498 break;
499 }
500 return 0;
501 }
502
read_attribute(uint8 * tsp)503 int read_attribute(uint8 *tsp)
504 {
505 int bytes = 0, header = 0;
506 uint32 attribute;
507 uint8 component = 0;
508 uint32 size = 0;
509 uint16 checksum = 0;
510 static char attach_title[256] = "default.dat";
511 uint8 *ptr;
512
513 component = *tsp;
514 bytes += sizeof(uint8);
515
516 attribute = read_32(tsp+bytes);
517 bytes += sizeof(attribute);
518 size = read_32(tsp+bytes);
519 bytes += sizeof(size);
520 header = bytes;
521 bytes += size;
522 checksum = read_16(tsp+bytes);
523 bytes += sizeof(checksum);
524
525 if (Verbose) {
526 printf("Attribute: %s, (%08lx)\n",attribute_string(attribute),attribute);
527 if(component == LVL_MESSAGE)
528 printf("\tComponent:\tMessage\n");
529 else
530 printf("\tComponent:\tAttachment\n");
531 printf("\tType:\t\t%s\n",type_string(ATT_TYPE(attribute)));
532 printf("\tSize:\t\t%d\n",size);
533 printf("\tChecksum:\t%04x\n",checksum);
534 }
535
536 switch (attribute) {
537 case attNull:
538 default_handler(attribute, tsp+header, size);
539 break;
540 case attFrom:
541 default_handler(attribute, tsp+header, size);
542 break;
543 case attSubject:
544 printf("Subject: %s\n",make_string(tsp+header,size));
545 break;
546 case attDateSent:
547 printf("Date Sent: %s\n",date_string(tsp+header));
548 break;
549 case attDateRecd:
550 printf("Date Recieved: %s\n",date_string(tsp+header));
551 break;
552 case attMessageStatus:
553 printf("Message Status: %s\n",status_string(*(uint8 *)(tsp+header)));
554 break;
555 case attMessageClass:
556 printf("Message Class: %s\n",make_string(tsp+header,size));
557 break;
558 case attMessageID:
559 printf("Message Id: %s\n",make_string(tsp+header,size));
560 break;
561 case attParentID:
562 printf("Parent Id: %s\n",make_string(tsp+header,size));
563 break;
564 case attConversationID:
565 printf("Conversation Id: %s\n",make_string(tsp+header,size));
566 break;
567 case attBody:
568 default_handler(attribute, tsp+header, size);
569 break;
570 case attPriority:
571 printf("Priority: %d %s\n", read_16(tsp+header),
572 priority_string(read_16(tsp+header)));
573 break;
574 case attAttachData:
575 if (SaveData) {
576 if (!save_attach_data(attach_title, tsp+header,size))
577 printf("Attachment saved to file %s\n", attach_title);
578 else
579 printf("Failure saving attachment to file %s\n", attach_title);
580 } else {
581 printf("TNEF Contains attached file %s\n", attach_title);
582 }
583 break;
584 case attAttachTitle:
585 strcpy(attach_title, make_string(tsp+header,size));
586 printf("Attachment Title: %s\n",attach_title);
587 break;
588 case attAttachMetaFile:
589 default_handler(attribute, tsp+header, size);
590 break;
591 case attAttachCreateDate:
592 printf("Attachment Date Created: %s\n",date_string(tsp+header));
593 break;
594 case attAttachModifyDate:
595 printf("Attachment Date Modified: %s\n",date_string(tsp+header));
596 break;
597 case attDateModified:
598 printf("Date Modified: %s\n",date_string(tsp+header));
599 break;
600 case attAttachTransportFilename:
601 default_handler(attribute, tsp+header, size);
602 break;
603 case attAttachRenddata:
604 default_handler(attribute, tsp+header, size);
605 break;
606 case attMAPIProps:
607 printf("MAPI Properties\n");
608 handle_props(tsp+header);
609 break;
610 case attRecipTable:
611 default_handler(attribute, tsp+header, size);
612 break;
613 case attAttachment:
614 default_handler(attribute, tsp+header, size);
615 break;
616 case attTnefVersion:
617 {
618 uint32 version;
619 version = read_32(tsp+header);
620 printf("TNEF Version: %d.%d\n",((version & 0xFFFF0000)>>16),
621 (version & 0x0000FFFF));
622 }
623 break;
624 case attOemCodepage:
625 default_handler(attribute, tsp+header, size);
626 break;
627 case attOriginalMessageClass:
628 printf("Original Message Class: %s\n",make_string(tsp+header,size));
629 break;
630 case attOwner:
631 default_handler(attribute, tsp+header, size);
632 break;
633 case attSentFor:
634 default_handler(attribute, tsp+header, size);
635 break;
636 case attDelegate:
637 default_handler(attribute, tsp+header, size);
638 break;
639 case attDateStart:
640 printf("Start Date: %s\n",date_string(tsp+header));
641 break;
642 case attDateEnd:
643 printf("End Date: %s\n",date_string(tsp+header));
644 break;
645 case attAidOwner:
646 default_handler(attribute, tsp+header, size);
647 break;
648 case attRequestRes:
649 default_handler(attribute, tsp+header, size);
650 break;
651 default:
652 default_handler(attribute, tsp+header, size);
653 break;
654 }
655 return bytes;
656 }
657
decode_tnef(uint8 * tnef_stream,int size)658 int decode_tnef(uint8 *tnef_stream, int size)
659 {
660 uint8 *tsp;
661
662 tsp = tnef_stream;
663
664 if (TNEF_SIGNATURE == read_32(tsp)) {
665 printf("Good TNEF Signature\n");
666 } else {
667 printf("Bad TNEF Signature, Expecting: %lx Got: %lx\n",TNEF_SIGNATURE,
668 read_32(tsp));
669 }
670 tsp += sizeof(TNEF_SIGNATURE);
671
672 printf("TNEF Attach Key: %x\n",read_16(tsp));
673 tsp += sizeof(uint16);
674
675 while ((tsp - tnef_stream) < size) {
676 tsp += read_attribute(tsp);
677 }
678 return 0;
679 }
680
usage(char * argv0)681 void usage(char *argv0)
682 {
683 fprintf(stderr, "\n%s by Brandon Long\n", VERSION);
684 fprintf(stderr, "Translation Neutral Encapsulation Format (TNEF) decoder\n");
685 fprintf(stderr, "\nUsage: %s [-v][-d][-h][-s] <filename>\n", argv0);
686 fprintf(stderr, "\t-v: Verbose output\n");
687 fprintf(stderr, "\t-d: Dump hex contents\n");
688 fprintf(stderr, "\t-h: This information\n");
689 fprintf(stderr, "\t-s: Save any attached files in the TNEF stream to disk\n");
690 fprintf(stderr, "\n");
691 }
692
main(int argc,char * argv[])693 int main(int argc, char *argv[])
694 {
695 FILE *fp;
696 struct stat sb;
697 int size, nread;
698 uint8 *tnef_stream;
699 int dump = 0;
700 char *filename;
701 int x;
702
703 if (argc < 2 || argc > 4) {
704 usage(argv[0]);
705 exit(-1);
706 }
707
708 x = 1;
709 while (x < argc && argv[x]) {
710 if (!strcmp(argv[x], "-v")) {
711 Verbose = TRUE;
712 }
713 else if (!strcmp(argv[x], "-s")) {
714 SaveData = TRUE;
715 }
716 else if (!strcmp(argv[x], "-h")) {
717 usage(argv[0]);
718 exit(0);
719 }
720 else if (!strcmp(argv[x], "-d")) {
721 dump = TRUE;
722 } else {
723 filename = argv[x];
724 }
725 x++;
726 }
727
728 if (stat(filename,&sb) == -1) {
729 fprintf(stderr, "Error stating file %s\n", filename);
730 perror("stat");
731 usage(argv[0]);
732 exit(-1);
733 }
734
735 size = sb.st_size;
736
737 tnef_stream = (uint8 *)malloc(size);
738
739 if (tnef_stream == NULL) {
740 fprintf(stderr, "Error allocating %d bytes for loading file\n", size);
741 usage(argv[0]);
742 exit(-1);
743 }
744
745 if ((fp = fopen(filename,"r")) == NULL) {
746 fprintf(stderr, "Error openning file %s\n", filename);
747 perror("fopen");
748 usage(argv[0]);
749 exit(-1);
750 }
751
752 nread = fread(tnef_stream, sizeof(uint8), size, fp);
753 if (nread < size) {
754 fprintf(stderr, "Error reading stream from file %s\n",filename);
755 perror("fread");
756 usage(argv[0]);
757 exit(-1);
758 }
759 fclose(fp);
760
761 if (dump) {
762 int x;
763 for (x=0 ; x<size ; x++) {
764 if (x%32 == 0) putchar('\n');
765 if (x%4 == 0) putchar(' ');
766 printf("%02x",*(tnef_stream+x));
767 }
768 putchar('\n');
769 }
770 decode_tnef(tnef_stream,size);
771
772 return 0;
773 }
774