2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2004 Net Integration Technologies, Inc.
5 * This is the main TNEF handling class.
10 #include "wvstringlist.h"
12 #include "wvbufstream.h"
18 // Check if we're on a big-endian machine
19 int WvTnef::_endian_test
= 0x01020304;
20 bool WvTnef::_isbigendian
= *(char *)&_endian_test
== 1;
22 typedef WvMapPair
<PropertyKey
, StringList
*> PropertyPair
;
24 // Lets us read, write and update the checksum! Oh my!
25 const void *WvTnef::read_write_chk(const size_t num_bytes
,
27 WvStream
*ostream
) const
29 const void *stuff
= read_write(num_bytes
, istream
, ostream
);
31 const unsigned char *chk
= (const unsigned char *)stuff
;
32 for (size_t i
= 0; i
< num_bytes
; i
++)
38 // Lets us simultaneously read from one stream, do stuff
39 // with what we got, and write it to another stream. For
40 // "non-destructive" parsing of TNEFs
41 const void *WvTnef::read_write(const size_t num_bytes
,
43 WvStream
*ostream
) const
48 if (istream
&& istream
->isok() && istream
->isreadable())
49 num_ret
= istream
->read(tmp_buf
, num_bytes
);
51 if (num_bytes
!= num_ret
)
52 log("Asked for %s, got %s\n", num_bytes
, num_ret
);
54 if (ostream
&& ostream
->isok() && ostream
->select(0, false, true))
55 ostream
->write(tmp_buf
.peek(0, num_ret
), num_ret
);
57 return tmp_buf
.get(num_ret
);
60 uint64_t WvTnef::swap8(const uint64_t i
)
64 char *ptr
= (char *)&i
;
67 for (int j
= 0; j
< 8; ++j
)
68 ret
= (ret
<< j
*8) | ptr
[j
];
72 uint32_t WvTnef::swap4(const uint32_t i
)
76 char *ptr
= (char *)&i
;
77 return (uint32_t)((ptr
[3] << 24) |
83 uint16_t WvTnef::swap2(const uint16_t i
)
87 char *ptr
= (char *)&i
;
88 return (uint16_t)((ptr
[1] << 8) | ptr
[0]);
91 // Reading bytes from the stream independent of byte-order
92 uint64_t WvTnef::read_write8u(WvStream
*istream
, WvStream
*ostream
) const
95 (const uint64_t *)read_write(8, istream
, ostream
);
99 return (uint64_t)swap8(*ptr
);
102 uint32_t WvTnef::read_write4u(WvStream
*istream
, WvStream
*ostream
) const
104 const uint32_t *ptr
=
105 (const uint32_t *)read_write(4, istream
, ostream
);
109 return (uint32_t)swap4(*ptr
);
112 uint16_t WvTnef::read_write2u(WvStream
*istream
, WvStream
*ostream
) const
114 const uint16_t *ptr
=
115 (const uint16_t *)read_write(2, istream
, ostream
);
119 return (uint16_t)swap2(*ptr
);
122 int64_t WvTnef::read_write8(WvStream
*istream
, WvStream
*ostream
) const
125 (const int64_t *)read_write(8, istream
, ostream
);
129 return (int64_t)swap8(*ptr
);
132 int32_t WvTnef::read_write4(WvStream
*istream
, WvStream
*ostream
) const
135 (const int32_t *)read_write(4, istream
, ostream
);
139 return (int32_t)swap4(*ptr
);
142 int16_t WvTnef::read_write2(WvStream
*istream
, WvStream
*ostream
) const
145 (const int16_t *)read_write(2, istream
, ostream
);
149 return (int16_t)swap2(*ptr
);
152 // Reading bytes from the stream independent of byte-order,
153 // and updating the checksum. Whee!
154 uint64_t WvTnef::read_write8u_chk(WvStream
*istream
, WvStream
*ostream
) const
156 const uint64_t *ptr
=
157 (const uint64_t *)read_write_chk(8, istream
, ostream
);
161 return (uint64_t)swap8(*ptr
);
164 uint32_t WvTnef::read_write4u_chk(WvStream
*istream
, WvStream
*ostream
) const
166 const uint32_t *ptr
=
167 (const uint32_t *)read_write_chk(4, istream
, ostream
);
171 return (uint32_t)swap4(*ptr
);
174 uint16_t WvTnef::read_write2u_chk(WvStream
*istream
, WvStream
*ostream
) const
176 const uint16_t *ptr
=
177 (const uint16_t *)read_write_chk(2, istream
, ostream
);
181 return (uint16_t)swap2(*ptr
);
184 int64_t WvTnef::read_write8_chk(WvStream
*istream
, WvStream
*ostream
) const
187 (const int64_t *)read_write_chk(8, istream
, ostream
);
191 return (int64_t)swap8(*ptr
);
194 int32_t WvTnef::read_write4_chk(WvStream
*istream
, WvStream
*ostream
) const
197 (const int32_t *)read_write_chk(4, istream
, ostream
);
201 return (int32_t)swap4(*ptr
);
204 int16_t WvTnef::read_write2_chk(WvStream
*istream
, WvStream
*ostream
) const
207 (const int16_t *)read_write_chk(2, istream
, ostream
);
211 return (int16_t)swap2(*ptr
);
214 uint64_t WvTnef::read8u(FILE *file
) const
220 fread(&i
, 8, 1, file
);
222 return (uint64_t)swap8(i
);
225 uint32_t WvTnef::read4u(FILE *file
) const
231 fread(&i
, 4, 1, file
);
233 return (uint32_t)swap4(i
);
236 uint16_t WvTnef::read2u(FILE *file
) const
242 fread(&i
, 2, 1, file
);
244 return (uint16_t)swap2(i
);
247 int64_t WvTnef::read8(FILE *file
) const
253 fread(&i
, 8, 1, file
);
255 return (int64_t)swap8(i
);
258 int32_t WvTnef::read4(FILE *file
) const
264 fread(&i
, 4, 1, file
);
266 return (int32_t)swap4(i
);
269 int16_t WvTnef::read2(FILE *file
) const
275 fread(&i
, 2, 1, file
);
277 return (int16_t)swap2(i
);
280 // Appending integers to a file
281 uint32_t WvTnef::append2(uint16_t i
, FILE *file
) const
287 fwrite(&i
, 1, 2, file
);
292 uint32_t WvTnef::append4(uint32_t i
, FILE *file
) const
298 fwrite(&i
, 1, 4, file
);
303 uint32_t WvTnef::append8(uint64_t i
, FILE *file
) const
309 fwrite(&i
, 1, 8, file
);
314 // We do modify both streams
315 WvTnef::WvTnef(WvStream
*istream
, WvStream
*ostream
) :
319 log("WvTnef", WvLog::Debug5
)
321 if (istream
&& istream
->isok() && istream
->isreadable())
324 parse(istream
, ostream
);
330 log("Got a bad input stream: %s\n", istream
->errstr());
338 log("WvTnef", WvLog::Debug5
),
348 WvString
WvTnef::inttohex(int value
)
351 WvString
hexdigits("0123456789ABCDEF");
352 for (int i
=2*sizeof(int)-1; i
>=0; i
--)
355 sprintf(buf
, "%c", hexdigits
[((value
>> i
*4) &0xF)]);
356 ret
.append("%s", buf
);
361 // We do modify the buffer
362 bool WvTnef::rewrite(WvStringParm filename
)
364 FILE *file
= fopen(filename
, "w+");
369 // For storing properties
372 append4(TNEF_SIGNATURE
, file
);
375 // Write blank header
376 fwrite(ATTR_BLANK
, 1, ATTR_HEAD
, file
);
378 // attTnefVersion attribute
382 if (!dump_attribute(1, attTnefVersion
, 4, file
))
384 log("Failed to dump attTnefVersion attribute!\n");
388 // Write blank header
389 fwrite(ATTR_BLANK
, 1, ATTR_HEAD
, file
);
391 // attOemCodepage attribute
392 // I'm pretty lazy too!
396 if (!dump_attribute(1, attOemCodepage
, 8, file
))
398 log("Failed to dump attOemCodepage attribute!\n");
402 // attRecipTable attribute
403 s
= get_property(MAPI_TAG(PT_OBJECT
, PR_MESSAGE_RECIPIENTS
));
404 if (s
&& s
->length())
406 fseek(file
, ATTR_HEAD
, SEEK_CUR
);
407 fwrite(s
->data(), 1, s
->length(), file
);
409 if (!dump_attribute(1, attRecipTable
, s
->length(), file
))
411 log("Failed to dump attRecipTable attribute!\n");
416 _properties
.remove(PropertyKey(MAPI_TAG(PT_OBJECT
,
417 PR_MESSAGE_RECIPIENTS
)));
422 // attAttach attributes
423 // FIXME: Get stream for attachments
424 s
= get_property(str2guid(GUID_5
), 0);
425 if (s
&& s
->length())
428 ibuf
.put(s
->data(), s
->length());
430 if (dump_attachments(ibuf
, file
))
433 _properties
.remove(PropertyKey(str2guid(GUID_5
), 0));
442 // attMAPIProps attribute
443 if (dump_mapi_properties(file
))
446 log("Finished writing TNEF\n");
451 log("Failed to dump attMAPIProps attribute!\n");
456 bool WvTnef::dump_attribute(const unsigned char x
,
470 log("Dumping attribute of size 0 -- bad!\n");
474 // Header, data, checksum
475 offset
= 0 - (ATTR_HEAD
+ size
);
476 fseek(file
, offset
, SEEK_CUR
);
478 fwrite(&x
, 1, 1, file
);
482 // Calculate checksum
483 // maybe calc checksums on the fly sometime,
484 // but disk caching should make this ok-ish
485 for (uint32_t i
= 0; i
< size
; i
++)
487 fread(&ch
, 1, 1, file
);
492 append2(_checksum
, file
);
498 // FIXME: Make this handle big streams better
499 bool WvTnef::dump_attachments(WvStream
*stream
, FILE *file
) const
504 uint32_t size
= read4u(stream
);
507 while (stream
->isok() && stream
->isreadable())
509 size
= read4u(stream
);
513 fwrite(ATTR_BLANK
, 1, ATTR_HEAD
, file
);
514 out
= stream
->read(tmp_buf
, size
);
515 fwrite(tmp_buf
.get(out
), 1, out
, file
);
517 dump_attribute(2, attAttachRenddata
, size
, file
);
519 if (!stream
->isok() || !stream
->isreadable())
522 size
= read4u(stream
);
526 fwrite(ATTR_BLANK
, 1, ATTR_HEAD
, file
);
527 out
= stream
->read(tmp_buf
, size
);
528 fwrite(tmp_buf
.get(out
), 1, out
, file
);
530 dump_attribute(2, attAttachment
, size
, file
);
536 // We do modify the buffer
537 bool WvTnef::dump_mapi_properties(FILE *file
) const
539 char *padding
[] = { "", "\0", "\0\0", "\0\0\0" };
542 // Write a blank header
543 fwrite(ATTR_BLANK
, 1, ATTR_HEAD
, file
);
545 log("Writing prop_count %s\n", _properties
.count());
546 size
+= append4(_properties
.count(), file
);
548 WvMap
<PropertyKey
, StringList
*,
549 OpEqComp
, WvScatterHash
>::Iter
i(_properties
);
551 for (i
.rewind(); i
.next(); )
553 PropertyKey k
= i().key
;
554 StringList
*l
= i().data
;
556 uint16_t prop_type
= (uint16_t)MAPI_TYPE(k
.tag
);
557 uint16_t prop_id
= (uint16_t)MAPI_ID(k
.tag
);
559 size
+= append2(prop_type
, file
);
560 size
+= append2(prop_id
, file
);
562 log("Writing mapi prop at pos %s in file (prop_type: %s, prop_id: %s)\n",
563 ftell(file
), MAPI_TYPE(prop_type
), inttohex(prop_id
));
565 if (prop_id
>= 0x8000)
567 uint32_t name_type
= 0;
569 if (k
.name_str
.length())
572 if (k
.guid
.length() != 16)
574 log("Got a GUID of length %s -- bad!\n", k
.guid
.length());
578 size
+= fwrite(k
.guid
.data(), 1, k
.guid
.length(), file
);
579 size
+= append4(name_type
, file
);
583 uint32_t name_len
= k
.name_str
.length();
584 int skip
= name_len
% 4;
585 skip
= skip
? 4 - skip
: 0;
587 size
+= append4(name_len
, file
);
588 size
+= fwrite(k
.name_str
.data(), 1, k
.name_str
.length(), file
);
591 size
+= fwrite(padding
[skip
], 1, skip
, file
);
594 size
+= append4(k
.name_id
, file
);
597 bool multivalue
= (prop_type
& PROP_MULTIVALUE
) != 0;
598 prop_type
&= ~PROP_MULTIVALUE
;
600 if (prop_type
== PT_BINARY
|| prop_type
== PT_STRING8
||
601 prop_type
== PT_UNICODE
|| prop_type
== PT_OBJECT
)
603 size
+= append4(l
->count(), file
);
605 StringList::Iter
i(*l
);
606 for (i
.rewind(); i
.next(); )
608 int skip
= i().length() % 4;
609 skip
= skip
? 4 - skip
: 0;
611 size
+= append4(i().length(), file
);
612 size
+= fwrite(i().data(), 1, i().length(), file
);
615 size
+= fwrite(padding
[skip
], 1, skip
, file
);
618 else if (prop_type
== PT_SYSTIME
|| prop_type
== PT_APPTIME
||
619 prop_type
== PT_CURRENCY
|| prop_type
== PT_I8
||
620 prop_type
== PT_DOUBLE
)
623 size
+= append4(l
->count(), file
);
625 if (!multivalue
&& l
->count() > 1)
627 log("Property is not multivalue but count is %s -- bad!\n",
632 StringList::Iter
i(*l
);
633 for (i
.rewind(); i
.next(); )
635 if (i().length() != 8)
637 log("Incorrect data length for type %s -- bad!\n",
642 size
+= fwrite(i().data(), 1, i().length(), file
);
645 else if (prop_type
== PT_BOOLEAN
|| prop_type
== PT_LONG
||
646 prop_type
== PT_R4
|| prop_type
== PT_ERROR
)
649 size
+= append4(l
->count(), file
);
651 if (!multivalue
&& l
->count() > 1)
653 log("Property is not multivalue but count is %s -- bad!\n",
658 StringList::Iter
i(*l
);
659 for (i
.rewind(); i
.next(); )
661 if (i().length() != 4)
663 log("Incorrect data length for type %s -- bad!\n",
668 size
+= fwrite(i().data(), 1, i().length(), file
);
671 else if (prop_type
== PT_I2
)
674 size
+= append4(l
->count(), file
);
676 if (!multivalue
&& l
->count() > 1)
678 log("Property is not multivalue but count is %s -- bad!\n",
684 StringList::Iter
i(*l
);
685 for (i
.rewind(); i
.next(); )
687 if (i().length() != 2)
689 log("Incorrect data length for type %s -- bad!\n",
695 size
+= fwrite(i().data(), 1, i().length(), file
);
698 int skip
= l
->count()*2 % 4;
699 skip
= skip
? 4 - skip
: 0;
702 size
+= fwrite(padding
[skip
], 1, skip
, file
);
706 if (dump_attribute(1, attMAPIProps
, size
, file
))
712 WvStream
* WvTnef::get_wvstream_for_property(StringList
const *list
) const
714 if (list
== NULL
|| list
->isempty())
717 WvStream
*stream
= new WvBufStream
;
718 stream
->write(list
->first()->c_str(), list
->first()->size());
723 string
const *WvTnef::get_property(const uint32_t tag
) const
725 StringList
const *list
= get_properties(tag
);
726 return (list
!= 0 && !list
->isempty()) ? list
->first() : 0;
729 WvStream
* WvTnef::get_property_as_stream(const uint32_t tag
) const
731 StringList
const *list
= get_properties(tag
);
733 return get_wvstream_for_property(list
);
736 string
const *WvTnef::get_property(const string
&guid
, const uint32_t name_id
) const
738 StringList
const *list
= get_properties(guid
, name_id
);
739 return (list
!= 0 && !list
->isempty()) ? list
->first() : 0;
742 WvStream
*WvTnef::get_property_as_stream(const string
&guid
, const uint32_t name_id
) const
744 StringList
const *list
= get_properties(guid
, name_id
);
746 return get_wvstream_for_property(list
);
749 string
const *WvTnef::get_property(const string
&guid
, const string
&name_str
) const
751 StringList
const *list
= get_properties(guid
, name_str
);
752 return (list
!= 0 && !list
->isempty()) ? list
->first() : 0;
755 WvStream
*WvTnef::get_property_as_stream(const string
&guid
, const string
&name_str
) const
757 StringList
const *list
= get_properties(guid
, name_str
);
759 return get_wvstream_for_property(list
);
762 StringList
const *WvTnef::get_properties(const uint32_t tag
) const
767 StringList
**ret
= _properties
.find(key
);
768 return (ret
) ? *ret
: 0;
771 StringList
const *WvTnef::get_properties(const string
&guid
, const uint32_t name_id
) const
775 key
.name_id
= name_id
;
777 StringList
**ret
= _properties
.find(key
);
778 return (ret
) ? *ret
: 0;
781 StringList
const *WvTnef::get_properties(const string
&guid
, const string
&name_str
) const
786 key
.name_str
= name_str
;
788 StringList
**ret
= _properties
.find(key
);
789 return (ret
) ? *ret
: 0;
792 bool WvTnef::set_property(const uint32_t tag
, string
*value
)
800 if (_properties
.exists(key
))
801 _properties
.remove(key
);
803 StringList
*list
= new StringList
;
804 list
->append(value
, true);
805 _properties
.add(key
, list
, true);
810 bool WvTnef::set_property(const string
&guid
, const uint32_t name_id
,
811 string
*value
, const uint32_t mapi_type
)
818 key
.name_id
= name_id
;
820 if (_properties
.exists(key
))
823 PropertyPair
*pair
= _properties
.find_pair(key
);
824 key
.tag
= pair
->key
.tag
;
826 // then nuke the property
827 _properties
.remove(key
);
830 key
.tag
= MAPI_TAG(mapi_type
, _max_id
++);
832 StringList
*list
= new StringList
;
833 list
->append(value
, true);
834 _properties
.add(key
, list
, true);
839 bool WvTnef::set_property(const string
&guid
, const string
&name_str
,
840 string
*value
, const uint32_t mapi_type
)
847 key
.name_str
= name_str
;
849 if (_properties
.exists(key
))
852 PropertyPair
*pair
= _properties
.find_pair(key
);
853 key
.tag
= pair
->key
.tag
;
855 // then nuke the property
856 _properties
.remove(key
);
859 key
.tag
= MAPI_TAG(mapi_type
, _max_id
++);
861 StringList
*list
= new StringList
;
862 list
->append(value
, true);
863 _properties
.add(key
, list
, true);
868 bool WvTnef::append_property(const uint32_t tag
,
877 StringList
*list
= NULL
;
878 StringList
*newlist
= new StringList
;
879 if (_properties
.exists(key
))
881 list
= _properties
[key
];
883 StringList::Iter
i(*list
);
884 for (i
.rewind(); i
.next(); )
885 newlist
->append(i
.ptr(), true);
887 _properties
.remove(key
);
890 newlist
->append(value
, true);
891 _properties
.add(key
, list
, true);
896 bool WvTnef::append_property(const string
&guid
, const uint32_t name_id
,
897 string
*value
, const uint32_t mapi_type
)
904 key
.name_id
= name_id
;
906 StringList
*list
= NULL
;
907 StringList
*newlist
= new StringList
;
908 if (_properties
.exists(key
))
910 PropertyPair
*pair
= _properties
.find_pair(key
);
912 key
.tag
= pair
->key
.tag
;
914 StringList::Iter
i(*list
);
915 for (i
.rewind(); i
.next(); )
916 newlist
->append(i
.ptr(), true);
918 _properties
.remove(key
);
921 key
.tag
= MAPI_TAG(mapi_type
, _max_id
++);
923 newlist
->append(value
, true);
924 _properties
.add(key
, list
, true);
929 bool WvTnef::append_property(const string
&guid
, const string
&name_str
,
930 string
*value
, const uint32_t mapi_type
)
937 key
.name_str
= name_str
;
939 StringList
*list
= NULL
;
940 StringList
*newlist
= new StringList
;
941 if (_properties
.exists(key
))
943 PropertyPair
*pair
= _properties
.find_pair(key
);
945 key
.tag
= pair
->key
.tag
;
947 StringList::Iter
i(*list
);
948 for (i
.rewind(); i
.next(); )
949 newlist
->append(i
.ptr(), true);
951 _properties
.remove(key
);
954 key
.tag
= MAPI_TAG(mapi_type
, _max_id
++);
956 newlist
->append(value
, true);
957 _properties
.add(key
, list
, true);
962 bool WvTnef::set_properties(const uint32_t tag
, StringList
*list
)
970 if (_properties
.exists(key
))
971 _properties
.set(key
, list
, true);
973 _properties
.add(key
, list
, true);
978 bool WvTnef::set_properties(const string
&guid
, const uint32_t name_id
,
979 StringList
*list
, const uint32_t mapi_type
)
986 key
.name_id
= name_id
;
988 if (_properties
.exists(key
))
990 PropertyPair
*pair
= _properties
.find_pair(key
);
991 key
.tag
= pair
->key
.tag
;
992 _properties
.set(key
, list
, true);
996 key
.tag
= MAPI_TAG(mapi_type
, _max_id
++);
997 _properties
.add(key
, list
, true);
1003 bool WvTnef::set_properties(const string
&guid
, const string
&name_str
,
1004 StringList
*list
, const uint32_t mapi_type
)
1011 key
.name_str
= name_str
;
1013 if (_properties
.exists(key
))
1015 PropertyPair
*pair
= _properties
.find_pair(key
);
1016 key
.tag
= pair
->key
.tag
;
1017 _properties
.set(key
, list
, true);
1021 key
.tag
= MAPI_TAG(mapi_type
, _max_id
++);
1022 _properties
.add(key
, list
, true);
1028 bool WvTnef::check_sum(WvStream
*istream
, WvStream
*ostream
)
1030 uint16_t checksum
= read_write2u(istream
, ostream
);
1033 if (_checksum
== checksum
)
1036 log("Failed the checksum!\n");
1042 // Check the attribute header
1043 bool WvTnef::check_header(const void *_header
)
1045 const char *header
= (const char *)_header
;
1048 return (*header
== LVL_MESSAGE
|| *header
== LVL_ATTACHMENT
);
1053 // Gotos everywhere since apparently "exceptions are evil" --sharvil
1054 bool WvTnef::parse(WvStream
*istream
, WvStream
*ostream
)
1056 // Can't parse nothing!
1057 if (NULL
== istream
)
1064 // Check for the all-important signature
1065 if (read_write4u(istream
, ostream
) != TNEF_SIGNATURE
)
1067 log("Buffer doesn't have a valid TNEF signature!\n");
1071 // The key is a nonzero unsigned integer according to specs
1072 if (read_write2u(istream
, ostream
) == 0)
1074 log("The key is zero - this violates TNEF specification!\n");
1078 // If the header for the first attribute is bad,
1079 // we're kinda screwed
1080 if (!check_header(read_write(1, istream
, ostream
)))
1083 // Make sure we're ok for reading
1084 while (istream
->isok() && istream
->isreadable())
1086 uint32_t type
= read_write4u(istream
, ostream
);
1087 uint32_t size
= read_write4u(istream
, ostream
);
1091 // Make sure we've still got stuff
1092 if (istream
->isok() && istream
->isreadable())
1094 log("Reading an attribute (type: %s, size: %s)\n", type
, size
);
1097 case attMessageClass
:
1099 WvList
<string
> *list
= new WvList
<string
>;
1100 PropertyKey
key(MAPI_TAG(PT_STRING8
, PR_MESSAGE_CLASS
));
1102 const void *value
= read_write_chk(size
,
1105 string
*data
= new string((const char *)value
, size
);
1107 if (!check_sum(istream
, ostream
))
1110 list
->append(data
, true);
1111 _properties
.add(key
, list
, true);
1116 WvList
<string
> *list
= new WvList
<string
>;
1117 PropertyKey
key(MAPI_TAG(PT_OBJECT
, PR_MESSAGE_RECIPIENTS
));
1119 const void *value
= read_write_chk(size
,
1122 string
*data
= new string((const char *)value
, size
);
1124 if (!check_sum(istream
, ostream
))
1127 list
->append(data
, true);
1128 _properties
.add(key
, list
, true);
1133 if (!parse_mapi_properties(istream
, ostream
))
1135 if (!check_sum(istream
, ostream
))
1140 // FIXME: Add cases for the attAttach
1141 // attributes and use WvFile to stream
1145 // We might not care about this, but we still
1146 // need to stream it through and check the sum
1147 read_write_chk(size
, istream
, ostream
);
1149 if (!check_sum(istream
, ostream
))
1159 else // Not sure if this is an error.. but what
1160 ; // would a zero size payload mean? Crack!
1162 // If the header for the next attribute is bad, we'll
1163 // break and assume we're at the end of the TNEF
1164 if (!check_header(read_write(1, istream
, ostream
)))
1168 log("Finished parsing TNEF\n");
1176 // Returns true if the properties were parsed successfully,
1177 // false if there was serious error.
1178 bool WvTnef::parse_mapi_properties(WvStream
*istream
, WvStream
*ostream
)
1180 uint32_t prop_count
= read_write4u_chk(istream
, ostream
);
1181 log("Got prop_count %s\n", prop_count
);
1182 for(unsigned int i
= 0; i
< prop_count
; ++i
)
1185 key
.tag
= read_write4u_chk(istream
, ostream
);
1186 log("I'm a key: %s %s\n",
1187 MAPI_TYPE(key
.tag
), inttohex(MAPI_ID(key
.tag
)));
1189 // The property is named so handle the Proptag_Name field.
1190 if(MAPI_ID(key
.tag
) >= 0x8000)
1192 const void *value
= read_write_chk(16, istream
, ostream
);
1193 key
.guid
.assign((const char *)value
, 16);
1195 uint32_t named
= read_write4u_chk(istream
, ostream
);
1198 // Get string length, and read that much
1199 uint32_t strlen
= read_write4u_chk(istream
, ostream
);
1200 value
= read_write_chk(strlen
, istream
, ostream
);
1202 // Set as name_str for key
1203 key
.name_str
.assign((const char *)value
, strlen
);
1204 size_t skip
= (4 - (strlen
% 4)) %4;
1205 read_write(skip
, istream
, ostream
);
1208 key
.name_id
= read_write4u_chk(istream
, ostream
);
1210 // Update the max id
1211 if (MAPI_ID(key
.tag
) > _max_id
)
1212 _max_id
= MAPI_ID(key
.tag
);
1215 bool multivalue
= (key
.tag
& PROP_MULTIVALUE
) != 0;
1216 key
.tag
&= ~PROP_MULTIVALUE
;
1218 uint32_t value_count
= 1;
1220 MAPI_TYPE(key
.tag
) == PT_BINARY
||
1221 MAPI_TYPE(key
.tag
) == PT_STRING8
||
1222 MAPI_TYPE(key
.tag
) == PT_UNICODE
||
1223 MAPI_TYPE(key
.tag
) == PT_OBJECT
)
1225 value_count
= read_write4u_chk(istream
, ostream
);
1226 log("Value Count: %s\n", value_count
);
1229 bool odd
= value_count
% 2;
1231 StringList
*list
= new StringList
;
1232 for(unsigned int j
= 0; j
< value_count
; ++j
)
1234 string
*s
= new string
;
1235 switch(MAPI_TYPE(key
.tag
))
1238 s
->assign((const char *)read_write_chk(2, istream
, ostream
), 2);
1246 s
->assign((const char *)read_write_chk(4, istream
, ostream
), 4);
1253 uint32_t size
= read_write4u_chk(istream
, ostream
);
1254 log("Length: %s\n", size
);
1256 const void *value
= read_write_chk(size
, istream
, ostream
);
1257 s
->assign((const char *)value
, size
);
1259 // Pad to 4-byte alignment
1260 size_t skip
= (4 - (size
% 4)) % 4;
1261 read_write(skip
, istream
, ostream
);
1267 s
->assign((const char *)read_write_chk(8, istream
, ostream
), 8);
1270 log("%s isn't a MAPI Type that I understand\n",
1271 MAPI_TYPE(key
.tag
));
1276 log("MAPI Type: %s\n", MAPI_TYPE(key
.tag
));
1277 log("%s", hexdump_buffer(s
->data(), s
->length()));
1278 log("--------------------------------------\n");
1279 list
->append(s
, true);
1282 // Stay 4-byte aligned
1283 if ((MAPI_TYPE(key
.tag
) == PT_I2
) && odd
)
1284 read_write(2, istream
, ostream
);
1286 _properties
.add(key
, list
, true);
1291 void WvTnef::dump() const
1293 WvMap
<PropertyKey
, StringList
*, OpEqComp
, WvScatterHash
>::Iter
i(_properties
);
1295 for (i
.rewind(); i
.next(); )
1298 log("%s\n", inttohex(MAPI_TAG_REVERSE(MAPI_TYPE(i
->key
.tag
), MAPI_ID(i
->key
.tag
))));
1299 switch (MAPI_TAG_REVERSE(MAPI_TYPE(i
->key
.tag
), MAPI_ID(i
->key
.tag
)))
1309 case PR_MESSAGE_CLASS
:
1310 id
= "MESSAGE CLASS";
1312 case PR_CREATION_TIME
:
1313 id
= "PR_CREATION_TIME";
1315 case PR_LAST_MODIFICATION_TIME
:
1316 id
= "PR_LAST_MODIFICATION_TIME";
1324 case PR_DISPLAY_BCC
:
1327 case PR_NORMALIZED_SUBJECT
:
1328 id
= "NORMALIZED_SUBJECT";
1330 case PR_SENDER_EMAIL_ADDRESS
:
1331 id
= "SENDER EMAIL";
1333 case PR_SENT_REPRESENTING_ADDRTYPE
:
1334 id
= "PR_SENT_REPRESENTING_ADDRTYPE";
1336 case PR_SENT_REPRESENTING_NAME
:
1337 id
= "PR_SENT_REPRESENTING_NAME";
1339 case PR_CLIENT_SUBMIT_TIME
:
1340 id
= "PR_CLIENT_SUBMIT_TIME";
1342 case PR_CONVERSATION_TOPIC
:
1343 id
= "PR_CONVERSATION_TOPIC";
1345 case PR_SENDER_ADDRTYPE
:
1346 id
= "PR_SENDER_ADDRTYPE";
1348 case PR_SUBJECT_PREFIX
:
1349 id
= "PR_SUBJECT_PREFIX";
1351 case PR_SENDER_NAME
:
1352 id
= "PR_SENDER_NAME";
1355 id
= inttohex(i
->key
.tag
);
1357 switch (MAPI_TYPE(i
->key
.tag
))
1360 log("%s (%s) UUID -> %s = %s\n\n", MAPI_TYPE(i
->key
.tag
), id
,
1361 i
->key
.name_str
.data(), i
->data
->first()->c_str());
1364 log("%s (%s) = SYSTIME\n%sENDSYSTIME\n", MAPI_TYPE(i
->key
.tag
), id
,
1365 hexdump_buffer(i
->data
->first()->data(), i
->data
->first()->length()));
1368 log("%s (%s) = BINARY\n%sENDBINARY\n", MAPI_TYPE(i
->key
.tag
), id
,
1369 hexdump_buffer(i
->data
->first()->data(), i
->data
->first()->length()));
1377 /* the following code has been shamelessly ripped out of pphaneuf's
1378 * XPLC library - However, since it's not the same, and we don't want to
1379 * incurr symbol clashes, let's just not call it exactly the same thing, shall we?
1381 typedef struct _TNEFUUID
{
1382 unsigned long Data1
;
1383 unsigned short Data2
;
1384 unsigned short Data3
;
1385 unsigned char Data4
[8];
1388 static const TNEFUUID TNEFUUID_null
= {0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}};
1390 // Make a guid that will match those stored in PropertyKeys
1391 string
WvTnef::str2guid(WvStringParm s
)
1393 const char *str
= s
.cstr();
1397 bool format1
= false;
1407 rv
.Data1
= strtoul(str
, &end
, 16);
1416 rv
.Data2
= static_cast<unsigned short>(strtoul(str
, &end
, 16));
1425 rv
.Data3
= static_cast<unsigned short>(strtoul(str
, &end
, 16));
1436 strncpy(tmp
, str
, 2);
1437 rv
.Data4
[0] = static_cast<unsigned char>(strtoul(tmp
, &end
, 16));
1442 strncpy(tmp
, str
, 2);
1443 rv
.Data4
[1] = static_cast<unsigned char>(strtoul(tmp
, &end
, 16));
1452 for(int i
= 2; i
< 8; ++i
)
1454 strncpy(tmp
, str
, 2);
1455 rv
.Data4
[i
] = static_cast<unsigned char>(strtoul(tmp
, &end
, 16));
1477 return string((const char *)&rv
, 16);
1481 string
WvTnef::str2guid(WvStringParm str
)
1483 // A 32-char string with 4 dashes
1484 if (36 != str
.len())
1488 sects
.split(str
, "-");
1490 // If there were 4 dashes, we should get 5 strings
1491 if (5 != sects
.count())
1494 unsigned char buf
[16];
1495 int bufprev
= 0; // Index of previous string in buffer
1496 int bufnext
= 0; // Index of next string in buffer
1497 int bufi
= 0; // Current position in buffer
1498 char chi
= 0; // Current char in string
1500 for (unsigned int i
= 0; i
< 5; i
++)
1502 WvString curr
= sects
.popstr();
1504 for (unsigned int j
= 0; j
< curr
.len(); j
++)
1506 bufi
= bufnext
+ (j
/ 2);
1509 // The high 4 bits ...
1511 buf
[bufi
] = tolower(chi
) - 87;
1512 else if (isdigit(chi
))
1513 buf
[bufi
] = atoi(&chi
);
1515 buf
[bufi
] = buf
[bufi
] << 4;
1518 // ... and the low 4 bits
1520 buf
[bufi
] += tolower(chi
) - 87;
1521 else if (isdigit(chi
))
1522 buf
[bufi
] += atoi(&chi
);
1528 // Have to reverse first 3 groups on little
1529 // endian, last 2 groups on big endian
1530 if (!_isbigendian
&& i
< 3 || _isbigendian
&& i
> 2)
1532 int mid
= bufprev
+ ((bufnext
- bufprev
) / 2);
1535 for (int j
= bufprev
; j
<= mid
; j
++)
1537 swap
= bufnext
-(j
-bufprev
);
1549 return string((const char *)&buf
, 16);
1553 // Fake a 2-byte string from a C string for looking up named properties
1554 string
WvTnef::str2ustr(WvStringParm str
)
1573 unsigned WvHash(const string
&str
)
1575 // The upper bits are more random -- this is basically a sortof linear
1576 // feedback shift register with a crappy polynomial. But upper bits being
1577 // more random is A Good Thing since the bottom bits will be dropped when
1578 // scaling to fit inside the table.
1580 unsigned hash
= 0xDeadBeef;
1581 for(unsigned int i
= 0; i
< str
.length(); ++i
)
1582 hash
= (hash
>> 7) ^ ((hash
& 0x7F) << 23) ^ (str
[i
] << 24);
1586 // We do a check on the guid length because the 'tag' field is not valid if
1587 // searching for a named property (named properties have a guid)
1588 unsigned WvHash(const PropertyKey
&key
)
1590 return (key
.guid
.length() == 0) ? key
.tag
: (WvHash(key
.guid
) ^ WvHash(key
.name_str
) ^ key
.name_id
);
1594 string
binary_to_text(const string
&bin
)
1597 for (string::const_iterator i
= bin
.begin(); i
!= bin
.end(); ++i
)
1600 sprintf(buf
, "%02x", *i
& 0xff);