HEAD: merged recent changes from r3_80.
[wvapps.git] / wvmapi / wvtnef.cc
blob66ea51dec408a45d65217508e7c8c3382e005860
1 /*
2 * Worldvisions Weaver Software:
3 * Copyright (C) 1997-2004 Net Integration Technologies, Inc.
5 * This is the main TNEF handling class.
6 */
8 #include "wvtnef.h"
9 #include "strutils.h"
10 #include "wvstringlist.h"
12 #include "wvbufstream.h"
14 #include <ctype.h>
16 using namespace std;
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,
26 WvStream *istream,
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++)
33 _checksum += chk[i];
35 return stuff;
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,
42 WvStream *istream,
43 WvStream *ostream) const
45 tmp_buf.zap();
46 size_t num_ret = 0;
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)
62 if (!_isbigendian)
63 return i;
64 char *ptr = (char *)&i;
66 uint64_t ret = 0;
67 for (int j = 0; j < 8; ++j)
68 ret = (ret << j*8) | ptr[j];
69 return ret;
72 uint32_t WvTnef::swap4(const uint32_t i)
74 if (!_isbigendian)
75 return i;
76 char *ptr = (char *)&i;
77 return (uint32_t)((ptr[3] << 24) |
78 (ptr[2] << 16) |
79 (ptr[1] << 8) |
80 (ptr[0]));
83 uint16_t WvTnef::swap2(const uint16_t i)
85 if (!_isbigendian)
86 return 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
94 const uint64_t *ptr =
95 (const uint64_t *)read_write(8, istream, ostream);
97 if (NULL == ptr)
98 return 0;
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);
107 if (NULL == ptr)
108 return 0;
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);
117 if (NULL == ptr)
118 return 0;
119 return (uint16_t)swap2(*ptr);
122 int64_t WvTnef::read_write8(WvStream *istream, WvStream *ostream) const
124 const int64_t *ptr =
125 (const int64_t *)read_write(8, istream, ostream);
127 if (NULL == ptr)
128 return 0;
129 return (int64_t)swap8(*ptr);
132 int32_t WvTnef::read_write4(WvStream *istream, WvStream *ostream) const
134 const int32_t *ptr =
135 (const int32_t *)read_write(4, istream, ostream);
137 if (NULL == ptr)
138 return 0;
139 return (int32_t)swap4(*ptr);
142 int16_t WvTnef::read_write2(WvStream *istream, WvStream *ostream) const
144 const int16_t *ptr =
145 (const int16_t *)read_write(2, istream, ostream);
147 if (NULL == ptr)
148 return 0;
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);
159 if (NULL == ptr)
160 return 0;
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);
169 if (NULL == ptr)
170 return 0;
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);
179 if (NULL == ptr)
180 return 0;
181 return (uint16_t)swap2(*ptr);
184 int64_t WvTnef::read_write8_chk(WvStream *istream, WvStream *ostream) const
186 const int64_t *ptr =
187 (const int64_t *)read_write_chk(8, istream, ostream);
189 if (NULL == ptr)
190 return 0;
191 return (int64_t)swap8(*ptr);
194 int32_t WvTnef::read_write4_chk(WvStream *istream, WvStream *ostream) const
196 const int32_t *ptr =
197 (const int32_t *)read_write_chk(4, istream, ostream);
199 if (NULL == ptr)
200 return 0;
201 return (int32_t)swap4(*ptr);
204 int16_t WvTnef::read_write2_chk(WvStream *istream, WvStream *ostream) const
206 const int16_t *ptr =
207 (const int16_t *)read_write_chk(2, istream, ostream);
209 if (NULL == ptr)
210 return 0;
211 return (int16_t)swap2(*ptr);
214 uint64_t WvTnef::read8u(FILE *file) const
216 if (NULL == file)
217 return 0;
219 uint64_t i;
220 fread(&i, 8, 1, file);
222 return (uint64_t)swap8(i);
225 uint32_t WvTnef::read4u(FILE *file) const
227 if (NULL == file)
228 return 0;
230 uint32_t i;
231 fread(&i, 4, 1, file);
233 return (uint32_t)swap4(i);
236 uint16_t WvTnef::read2u(FILE *file) const
238 if (NULL == file)
239 return 0;
241 uint16_t i;
242 fread(&i, 2, 1, file);
244 return (uint16_t)swap2(i);
247 int64_t WvTnef::read8(FILE *file) const
249 if (NULL == file)
250 return 0;
252 int64_t i;
253 fread(&i, 8, 1, file);
255 return (int64_t)swap8(i);
258 int32_t WvTnef::read4(FILE *file) const
260 if (NULL == file)
261 return 0;
263 int32_t i;
264 fread(&i, 4, 1, file);
266 return (int32_t)swap4(i);
269 int16_t WvTnef::read2(FILE *file) const
271 if (NULL == file)
272 return 0;
274 int16_t i;
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
283 if (NULL == file)
284 return 0;
286 i = swap2(i);
287 fwrite(&i, 1, 2, file);
289 return 2;
292 uint32_t WvTnef::append4(uint32_t i, FILE *file) const
294 if (NULL == file)
295 return 0;
297 i = swap4(i);
298 fwrite(&i, 1, 4, file);
300 return 4;
303 uint32_t WvTnef::append8(uint64_t i, FILE *file) const
305 if (NULL == file)
306 return 0;
308 i = swap8(i);
309 fwrite(&i, 1, 8, file);
311 return 8;
314 // We do modify both streams
315 WvTnef::WvTnef(WvStream *istream, WvStream *ostream) :
316 _properties(0),
317 _checksum(0),
318 _max_id(0x8000),
319 log("WvTnef", WvLog::Debug5)
321 if (istream && istream->isok() && istream->isreadable())
323 _isok = true;
324 parse(istream, ostream);
326 else
328 _isok = false;
329 if (istream)
330 log("Got a bad input stream: %s\n", istream->errstr());
334 WvTnef::WvTnef() :
335 _properties(0),
336 _checksum(0),
337 _max_id(0x8000),
338 log("WvTnef", WvLog::Debug5),
339 _isok(true)
343 WvTnef::~WvTnef()
345 _properties.zap();
348 WvString WvTnef::inttohex(int value)
350 WvString ret;
351 WvString hexdigits("0123456789ABCDEF");
352 for (int i=2*sizeof(int)-1; i>=0; i--)
354 char buf[3];
355 sprintf(buf, "%c", hexdigits[((value >> i*4) &0xF)]);
356 ret.append("%s", buf);
358 return ret;
361 // We do modify the buffer
362 bool WvTnef::rewrite(WvStringParm filename)
364 FILE *file = fopen(filename, "w+");
366 if (!file)
367 return false;
369 // For storing properties
370 string const *s;
372 append4(TNEF_SIGNATURE, file);
373 append2(42, file);
375 // Write blank header
376 fwrite(ATTR_BLANK, 1, ATTR_HEAD, file);
378 // attTnefVersion attribute
379 append2(0, file);
380 append2(1, file);
382 if (!dump_attribute(1, attTnefVersion, 4, file))
384 log("Failed to dump attTnefVersion attribute!\n");
385 return false;
388 // Write blank header
389 fwrite(ATTR_BLANK, 1, ATTR_HEAD, file);
391 // attOemCodepage attribute
392 // I'm pretty lazy too!
393 append4(1252, file);
394 append4(0, file);
396 if (!dump_attribute(1, attOemCodepage, 8, file))
398 log("Failed to dump attOemCodepage attribute!\n");
399 return false;
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");
412 return false;
414 else
416 _properties.remove(PropertyKey(MAPI_TAG(PT_OBJECT,
417 PR_MESSAGE_RECIPIENTS)));
421 #if 0
422 // attAttach attributes
423 // FIXME: Get stream for attachments
424 s = get_property(str2guid(GUID_5), 0);
425 if (s && s->length())
427 WvDynBuf ibuf;
428 ibuf.put(s->data(), s->length());
430 if (dump_attachments(ibuf, file))
432 ibuf.zap();
433 _properties.remove(PropertyKey(str2guid(GUID_5), 0));
435 else
437 return false;
440 #endif
442 // attMAPIProps attribute
443 if (dump_mapi_properties(file))
445 fclose(file);
446 log("Finished writing TNEF\n");
448 return true;
451 log("Failed to dump attMAPIProps attribute!\n");
452 fclose(file);
453 return false;
456 bool WvTnef::dump_attribute(const unsigned char x,
457 const uint32_t att,
458 const uint32_t size,
459 FILE *file) const
461 long offset;
462 unsigned char ch;
463 _checksum = 0;
465 if (NULL == file)
466 return false;
468 if (0 == size)
470 log("Dumping attribute of size 0 -- bad!\n");
471 return false;
474 // Header, data, checksum
475 offset = 0 - (ATTR_HEAD + size);
476 fseek(file, offset, SEEK_CUR);
478 fwrite(&x, 1, 1, file);
479 append4(att, file);
480 append4(size, 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);
488 _checksum += ch;
491 // Write it
492 append2(_checksum, file);
494 _checksum = 0;
495 return true;
498 // FIXME: Make this handle big streams better
499 bool WvTnef::dump_attachments(WvStream *stream, FILE *file) const
501 if (NULL == stream)
502 return false;
504 uint32_t size = read4u(stream);
505 uint32_t out;
507 while (stream->isok() && stream->isreadable())
509 size = read4u(stream);
510 if (0 == size)
511 return false;
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())
520 return false;
522 size = read4u(stream);
523 if (0 == size)
524 return false;
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);
533 return true;
536 // We do modify the buffer
537 bool WvTnef::dump_mapi_properties(FILE *file) const
539 char *padding[] = { "", "\0", "\0\0", "\0\0\0" };
540 uint32_t size = 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())
570 name_type = 1;
572 if (k.guid.length() != 16)
574 log("Got a GUID of length %s -- bad!\n", k.guid.length());
575 return false;
578 size += fwrite(k.guid.data(), 1, k.guid.length(), file);
579 size += append4(name_type, file);
581 if (name_type)
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);
590 if (skip)
591 size += fwrite(padding[skip], 1, skip, file);
593 else
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);
614 if (skip)
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)
622 if (multivalue)
623 size += append4(l->count(), file);
625 if (!multivalue && l->count() > 1)
627 log("Property is not multivalue but count is %s -- bad!\n",
628 l->count());
629 return false;
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",
638 prop_type);
639 return false;
641 else
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)
648 if (multivalue)
649 size += append4(l->count(), file);
651 if (!multivalue && l->count() > 1)
653 log("Property is not multivalue but count is %s -- bad!\n",
654 l->count());
655 return false;
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",
664 prop_type);
665 return false;
667 else
668 size += fwrite(i().data(), 1, i().length(), file);
671 else if (prop_type == PT_I2)
673 if (multivalue)
674 size += append4(l->count(), file);
676 if (!multivalue && l->count() > 1)
678 log("Property is not multivalue but count is %s -- bad!\n",
679 l->count());
681 return false;
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",
690 prop_type);
692 return false;
694 else
695 size += fwrite(i().data(), 1, i().length(), file);
698 int skip = l->count()*2 % 4;
699 skip = skip ? 4 - skip : 0;
701 if (skip)
702 size += fwrite(padding[skip], 1, skip, file);
706 if (dump_attribute(1, attMAPIProps, size, file))
707 return true;
708 else
709 return false;
712 WvStream * WvTnef::get_wvstream_for_property(StringList const *list) const
714 if (list == NULL || list->isempty())
715 return NULL;
717 WvStream *stream = new WvBufStream;
718 stream->write(list->first()->c_str(), list->first()->size());
720 return stream;
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
764 PropertyKey key;
765 key.tag = tag;
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
773 PropertyKey key;
774 key.guid = guid;
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
783 PropertyKey key;
785 key.guid = guid;
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)
794 if (NULL == value)
795 return false;
797 PropertyKey key;
798 key.tag = tag;
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);
807 return true;
810 bool WvTnef::set_property(const string &guid, const uint32_t name_id,
811 string *value, const uint32_t mapi_type)
813 if (NULL == value)
814 return false;
816 PropertyKey key;
817 key.guid = guid;
818 key.name_id = name_id;
820 if (_properties.exists(key))
822 // Get the tag ...
823 PropertyPair *pair = _properties.find_pair(key);
824 key.tag = pair->key.tag;
826 // then nuke the property
827 _properties.remove(key);
829 else
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);
836 return true;
839 bool WvTnef::set_property(const string &guid, const string &name_str,
840 string *value, const uint32_t mapi_type)
842 if (NULL == value)
843 return false;
845 PropertyKey key;
846 key.guid = guid;
847 key.name_str = name_str;
849 if (_properties.exists(key))
851 // Get the tag ...
852 PropertyPair *pair = _properties.find_pair(key);
853 key.tag = pair->key.tag;
855 // then nuke the property
856 _properties.remove(key);
858 else
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);
865 return true;
868 bool WvTnef::append_property(const uint32_t tag,
869 string *value)
871 if (NULL == value)
872 return false;
874 PropertyKey key;
875 key.tag = 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);
893 return true;
896 bool WvTnef::append_property(const string &guid, const uint32_t name_id,
897 string *value, const uint32_t mapi_type)
899 if (NULL == value)
900 return false;
902 PropertyKey key;
903 key.guid = guid;
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);
911 list = pair->data;
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);
920 else
921 key.tag = MAPI_TAG(mapi_type, _max_id++);
923 newlist->append(value, true);
924 _properties.add(key, list, true);
926 return true;
929 bool WvTnef::append_property(const string &guid, const string &name_str,
930 string *value, const uint32_t mapi_type)
932 if (NULL == value)
933 return false;
935 PropertyKey key;
936 key.guid = guid;
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);
944 list = pair->data;
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);
953 else
954 key.tag = MAPI_TAG(mapi_type, _max_id++);
956 newlist->append(value, true);
957 _properties.add(key, list, true);
959 return true;
962 bool WvTnef::set_properties(const uint32_t tag, StringList *list)
964 if (NULL == list)
965 return false;
967 PropertyKey key;
968 key.tag = tag;
970 if (_properties.exists(key))
971 _properties.set(key, list, true);
972 else
973 _properties.add(key, list, true);
975 return true;
978 bool WvTnef::set_properties(const string &guid, const uint32_t name_id,
979 StringList *list, const uint32_t mapi_type)
981 if (NULL == list)
982 return false;
984 PropertyKey key;
985 key.guid = guid;
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);
994 else
996 key.tag = MAPI_TAG(mapi_type, _max_id++);
997 _properties.add(key, list, true);
1000 return true;
1003 bool WvTnef::set_properties(const string &guid, const string &name_str,
1004 StringList *list, const uint32_t mapi_type)
1006 if (NULL == list)
1007 return false;
1009 PropertyKey key;
1010 key.guid = guid;
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);
1019 else
1021 key.tag = MAPI_TAG(mapi_type, _max_id++);
1022 _properties.add(key, list, true);
1025 return true;
1028 bool WvTnef::check_sum(WvStream *istream, WvStream *ostream)
1030 uint16_t checksum = read_write2u(istream, ostream);
1031 bool ret = false;
1033 if (_checksum == checksum)
1034 ret = true;
1035 else
1036 log("Failed the checksum!\n");
1038 _checksum = 0;
1039 return ret;
1042 // Check the attribute header
1043 bool WvTnef::check_header(const void *_header)
1045 const char *header = (const char *)_header;
1047 if (header)
1048 return (*header == LVL_MESSAGE || *header == LVL_ATTACHMENT);
1049 else
1050 return false;
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)
1058 goto bad_input;
1060 _properties.zap();
1061 _checksum = 0;
1062 _isok = true;
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");
1068 goto bad_input;
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");
1075 goto bad_input;
1078 // If the header for the first attribute is bad,
1079 // we're kinda screwed
1080 if (!check_header(read_write(1, istream, ostream)))
1081 goto bad_input;
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);
1089 if (size > 0)
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);
1095 switch(type)
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,
1103 istream,
1104 ostream);
1105 string *data = new string((const char *)value, size);
1107 if (!check_sum(istream, ostream))
1108 goto bad_input;
1110 list->append(data, true);
1111 _properties.add(key, list, true);
1112 break;
1114 case attRecipTable:
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,
1120 istream,
1121 ostream);
1122 string *data = new string((const char *)value, size);
1124 if (!check_sum(istream, ostream))
1125 goto bad_input;
1127 list->append(data, true);
1128 _properties.add(key, list, true);
1129 break;
1131 case attMAPIProps:
1133 if (!parse_mapi_properties(istream, ostream))
1134 goto bad_input;
1135 if (!check_sum(istream, ostream))
1136 goto bad_input;
1138 break;
1140 // FIXME: Add cases for the attAttach
1141 // attributes and use WvFile to stream
1142 // to disk
1143 default:
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))
1150 goto bad_input;
1152 break;
1156 else
1157 goto bad_input;
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)))
1165 break;
1168 log("Finished parsing TNEF\n");
1169 return true;
1171 bad_input:
1172 _isok = false;
1173 return false;
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)
1184 PropertyKey key;
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);
1196 if(named)
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);
1207 else
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;
1219 if(multivalue ||
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))
1237 case PT_I2:
1238 s->assign((const char *)read_write_chk(2, istream, ostream), 2);
1239 break;
1240 case PT_BOOLEAN:
1241 case PT_LONG:
1242 case PT_R4:
1243 case PT_CURRENCY:
1244 case PT_APPTIME:
1245 case PT_ERROR:
1246 s->assign((const char *)read_write_chk(4, istream, ostream), 4);
1247 break;
1248 case PT_STRING8:
1249 case PT_BINARY:
1250 case PT_OBJECT:
1251 case PT_UNICODE:
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);
1262 break;
1264 case PT_DOUBLE:
1265 case PT_I8:
1266 case PT_SYSTIME:
1267 s->assign((const char *)read_write_chk(8, istream, ostream), 8);
1268 break;
1269 default:
1270 log("%s isn't a MAPI Type that I understand\n",
1271 MAPI_TYPE(key.tag));
1273 return false;
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);
1288 return true;
1291 void WvTnef::dump() const
1293 WvMap<PropertyKey, StringList *, OpEqComp, WvScatterHash>::Iter i(_properties);
1295 for (i.rewind(); i.next(); )
1297 WvString id;
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)))
1301 case PR_SUBJECT:
1302 id = "PR_SUBJECT";
1303 break;
1304 case PR_BODY:
1305 id = "BODY";
1306 break;
1307 case PR_TITLE:
1308 id = "TITLE";
1309 case PR_MESSAGE_CLASS:
1310 id = "MESSAGE CLASS";
1311 break;
1312 case PR_CREATION_TIME:
1313 id = "PR_CREATION_TIME";
1314 break;
1315 case PR_LAST_MODIFICATION_TIME:
1316 id = "PR_LAST_MODIFICATION_TIME";
1317 break;
1318 case PR_DISPLAY_TO:
1319 id = "DISPLAY_TO";
1320 break;
1321 case PR_DISPLAY_CC:
1322 id = "DISPLAY_CC";
1323 break;
1324 case PR_DISPLAY_BCC:
1325 id = "DISPLAY_BCC";
1326 break;
1327 case PR_NORMALIZED_SUBJECT:
1328 id = "NORMALIZED_SUBJECT";
1329 break;
1330 case PR_SENDER_EMAIL_ADDRESS:
1331 id = "SENDER EMAIL";
1332 break;
1333 case PR_SENT_REPRESENTING_ADDRTYPE:
1334 id = "PR_SENT_REPRESENTING_ADDRTYPE";
1335 break;
1336 case PR_SENT_REPRESENTING_NAME:
1337 id = "PR_SENT_REPRESENTING_NAME";
1338 break;
1339 case PR_CLIENT_SUBMIT_TIME:
1340 id = "PR_CLIENT_SUBMIT_TIME";
1341 break;
1342 case PR_CONVERSATION_TOPIC:
1343 id = "PR_CONVERSATION_TOPIC";
1344 break;
1345 case PR_SENDER_ADDRTYPE:
1346 id = "PR_SENDER_ADDRTYPE";
1347 break;
1348 case PR_SUBJECT_PREFIX:
1349 id = "PR_SUBJECT_PREFIX";
1350 break;
1351 case PR_SENDER_NAME:
1352 id = "PR_SENDER_NAME";
1353 break;
1354 default:
1355 id = inttohex(i->key.tag);
1357 switch (MAPI_TYPE(i->key.tag))
1359 case PT_STRING8:
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());
1362 break;
1363 case PT_SYSTIME:
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()));
1366 break;
1367 case PT_BINARY:
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()));
1370 break;
1371 default:
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];
1386 } TNEFUUID;
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();
1394 TNEFUUID rv;
1395 char tmp[3];
1396 char* end;
1397 bool format1 = false;
1398 bool ok = false;
1400 do {
1401 if(*str == '{')
1403 format1 = true;
1404 ++str;
1407 rv.Data1 = strtoul(str, &end, 16);
1408 if(end != str + 8)
1409 break;
1410 str = end;
1412 if(*str != '-')
1413 break;
1414 ++str;
1416 rv.Data2 = static_cast<unsigned short>(strtoul(str, &end, 16));
1417 if(end != str + 4)
1418 break;
1419 str = end;
1421 if(*str != '-')
1422 break;
1423 ++str;
1425 rv.Data3 = static_cast<unsigned short>(strtoul(str, &end, 16));
1426 if(end != str + 4)
1427 break;
1428 str = end;
1430 if(*str != '-')
1431 break;
1432 ++str;
1434 tmp[2] = 0;
1436 strncpy(tmp, str, 2);
1437 rv.Data4[0] = static_cast<unsigned char>(strtoul(tmp, &end, 16));
1438 if(end != tmp + 2)
1439 break;
1440 str += 2;
1442 strncpy(tmp, str, 2);
1443 rv.Data4[1] = static_cast<unsigned char>(strtoul(tmp, &end, 16));
1444 if(end != tmp + 2)
1445 break;
1446 str += 2;
1448 if(*str != '-')
1449 break;
1450 ++str;
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));
1456 if(end != tmp + 2)
1457 break;
1458 str += 2;
1461 if(format1)
1463 if(*str != '}')
1464 break;
1465 ++str;
1468 if(*str != 0)
1469 break;
1471 ok = true;
1472 } while(0);
1474 if(!ok)
1475 rv = TNEFUUID_null;
1477 return string((const char *)&rv, 16);
1480 #if 0
1481 string WvTnef::str2guid(WvStringParm str)
1483 // A 32-char string with 4 dashes
1484 if (36 != str.len())
1485 return string("");
1487 WvStringList sects;
1488 sects.split(str, "-");
1490 // If there were 4 dashes, we should get 5 strings
1491 if (5 != sects.count())
1492 return string("");
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);
1507 chi = curr[j];
1509 // The high 4 bits ...
1510 if (isalpha(chi))
1511 buf[bufi] = tolower(chi) - 87;
1512 else if (isdigit(chi))
1513 buf[bufi] = atoi(&chi);
1515 buf[bufi] = buf[bufi] << 4;
1516 chi = curr[++j];
1518 // ... and the low 4 bits
1519 if (isalpha(chi))
1520 buf[bufi] += tolower(chi) - 87;
1521 else if (isdigit(chi))
1522 buf[bufi] += atoi(&chi);
1525 bufprev = bufnext;
1526 bufnext = bufi;
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);
1533 int swap = 0;
1535 for (int j = bufprev; j <= mid; j++)
1537 swap = bufnext-(j-bufprev);
1539 char tmp = buf[j];
1540 buf[j] = buf[swap];
1541 buf[swap] = tmp;
1545 bufnext++;
1549 return string((const char *)&buf, 16);
1551 #endif
1553 // Fake a 2-byte string from a C string for looking up named properties
1554 string WvTnef::str2ustr(WvStringParm str)
1556 string ret;
1557 int count = 0;
1559 while (str[count])
1561 ret += str[count];
1562 ret += '\0';
1564 count++;
1567 ret += '\0';
1568 ret += '\0';
1570 return ret;
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);
1583 return hash;
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);
1593 #if 0
1594 string binary_to_text(const string &bin)
1596 string ret;
1597 for (string::const_iterator i = bin.begin(); i != bin.end(); ++i)
1599 char buf[4];
1600 sprintf(buf, "%02x", *i & 0xff);
1601 ret += buf;
1603 return ret;
1605 #endif