1 extern "C" {
2     #include "define.h"
3     #include "msg.h"
4 }
5 
6 #include <gsf/gsf-utils.h>
7 
8 #include <gsf/gsf-input-stdio.h>
9 #include <gsf/gsf-infile.h>
10 #include <gsf/gsf-infile-stdio.h>
11 
12 #include <gsf/gsf-output-stdio.h>
13 #include <gsf/gsf-outfile.h>
14 #include <gsf/gsf-outfile-msole.h>
15 
16 #include <list>
17 #include <vector>
18 #include <string>
19 
20 using namespace std;
21 
22 struct property {
23     uint32_t  tag;
24     uint32_t  flags;
25     uint32_t  length; // or value
26     uint32_t  reserved;
27 };
28 typedef list<property> property_list;
29 
30 
31 /** Convert str to an 8 bit charset if it is utf8, null strings are preserved.
32  *
33  *  @param str     reference to the mapi string of interest
34  *  @param charset pointer to the 8 bit charset to use
35  */
36 static void convert_8bit(pst_string &str, const char *charset);
convert_8bit(pst_string & str,const char * charset)37 static void convert_8bit(pst_string &str, const char *charset) {
38     if (!str.str)     return;  // null
39     if (!str.is_utf8) return;  // not utf8
40 
41     DEBUG_ENT("convert_8bit");
42     pst_vbuf *newer = pst_vballoc(2);
43     size_t strsize = strlen(str.str);
44     size_t rc = pst_vb_utf8to8bit(newer, str.str, strsize, charset);
45     if (rc == (size_t)-1) {
46         // unable to convert, change the charset to utf8
47         free(newer->b);
48         DEBUG_INFO(("Failed to convert utf-8 to %s\n", charset));
49         DEBUG_HEXDUMPC(str.str, strsize, 0x10);
50     }
51     else {
52         // null terminate the output string
53         pst_vbgrow(newer, 1);
54         newer->b[newer->dlen] = '\0';
55         free(str.str);
56         str.str = newer->b;
57     }
58     free(newer);
59     DEBUG_RET();
60 }
61 
62 
63 static void empty_property(GsfOutfile *out, uint32_t tag);
empty_property(GsfOutfile * out,uint32_t tag)64 static void empty_property(GsfOutfile *out, uint32_t tag) {
65     vector<char> n(50);
66     snprintf(&n[0], n.size(), "__substg1.0_%08X", tag);
67     GsfOutput* dst = gsf_outfile_new_child(out, &n[0], false);
68     gsf_output_close(dst);
69     g_object_unref(G_OBJECT(dst));
70 }
71 
72 
73 static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, const char *contents, size_t size);
string_property(GsfOutfile * out,property_list & prop,uint32_t tag,const char * contents,size_t size)74 static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, const char *contents, size_t size) {
75     if (!contents) return;
76     size_t term = ((tag & 0x0000ffff) == 0x001e) ? 1 :
77                   ((tag & 0x0000ffff) == 0x001f) ? 2 : 0;  // null terminator
78     vector<char> n(50);
79     snprintf(&n[0], n.size(), "__substg1.0_%08X", tag);
80     GsfOutput* dst = gsf_outfile_new_child(out, &n[0], false);
81     gsf_output_write(dst, size, (const guint8*)contents);
82     if (term) {
83         memset(&n[0], 0, term);
84         gsf_output_write(dst, term, (const guint8*)&n[0]);
85         size += term;
86     }
87     gsf_output_close(dst);
88     g_object_unref(G_OBJECT(dst));
89 
90     property p;
91     p.tag      = tag;
92     p.flags    = 0x6;   // make all the properties writable
93     p.length   = size;
94     p.reserved = 0;
95     prop.push_back(p);
96 }
97 
98 
99 static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, FILE *fp);
string_property(GsfOutfile * out,property_list & prop,uint32_t tag,FILE * fp)100 static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, FILE *fp) {
101     vector<char> n(50);
102     snprintf(&n[0], n.size(), "__substg1.0_%08X", tag);
103     GsfOutput* dst = gsf_outfile_new_child(out, &n[0], false);
104 
105     size_t size = 0;
106     const size_t bsize = 10000;
107     char buf[bsize];
108 
109     while (1) {
110         size_t s = fread(buf, 1, bsize, fp);
111         if (!s) break;
112         gsf_output_write(dst, s, (const guint8*)buf);
113     }
114 
115     gsf_output_close(dst);
116     g_object_unref(G_OBJECT(dst));
117 
118     property p;
119     p.tag      = tag;
120     p.flags    = 0x6;   // make all the properties writable
121     p.length   = size;
122     p.reserved = 0;
123     prop.push_back(p);
124 }
125 
126 
127 static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, const char* charset, pst_string &contents);
string_property(GsfOutfile * out,property_list & prop,uint32_t tag,const char * charset,pst_string & contents)128 static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, const char* charset, pst_string &contents) {
129     if (contents.str) {
130         convert_8bit(contents, charset);
131         string_property(out, prop, tag, contents.str, strlen(contents.str));
132     }
133 }
134 
135 
136 static void strin0_property(GsfOutfile *out, property_list &prop, uint32_t tag, const char* charset, pst_string &contents);
strin0_property(GsfOutfile * out,property_list & prop,uint32_t tag,const char * charset,pst_string & contents)137 static void strin0_property(GsfOutfile *out, property_list &prop, uint32_t tag, const char* charset, pst_string &contents) {
138     if (contents.str) {
139         convert_8bit(contents, charset);
140         string_property(out, prop, tag, contents.str, strlen(contents.str)+1);
141     }
142 }
143 
144 
145 static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, const string &contents);
string_property(GsfOutfile * out,property_list & prop,uint32_t tag,const string & contents)146 static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, const string &contents) {
147     string_property(out, prop, tag, contents.c_str(), contents.size());
148 }
149 
150 
151 static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, pst_binary &contents);
string_property(GsfOutfile * out,property_list & prop,uint32_t tag,pst_binary & contents)152 static void string_property(GsfOutfile *out, property_list &prop, uint32_t tag, pst_binary &contents) {
153     if (contents.size) string_property(out, prop, tag, contents.data, contents.size);
154 }
155 
156 
157 static void write_properties(GsfOutfile *out, property_list &prop, const guint8* header, size_t hlen);
write_properties(GsfOutfile * out,property_list & prop,const guint8 * header,size_t hlen)158 static void write_properties(GsfOutfile *out, property_list &prop, const guint8* header, size_t hlen) {
159     GsfOutput* dst = gsf_outfile_new_child(out, "__properties_version1.0", false);
160     gsf_output_write(dst, hlen, header);
161     for (property_list::iterator i=prop.begin(); i!=prop.end(); i++) {
162         property &p = *i;
163         gsf_output_write(dst, sizeof(property), (const guint8*)&p);
164     }
165     gsf_output_close(dst);
166     g_object_unref(G_OBJECT(dst));
167 }
168 
169 
170 static void int_property(property_list &prop_list, uint32_t tag, uint32_t flags, uint32_t value);
int_property(property_list & prop_list,uint32_t tag,uint32_t flags,uint32_t value)171 static void int_property(property_list &prop_list, uint32_t tag, uint32_t flags, uint32_t value) {
172     property p;
173     p.tag      = tag;
174     p.flags    = flags;
175     p.length   = value;
176     p.reserved = 0;
177     prop_list.push_back(p);
178 }
179 
180 
181 static void i64_property(property_list &prop_list, uint32_t tag, uint32_t flags, FILETIME *value);
i64_property(property_list & prop_list,uint32_t tag,uint32_t flags,FILETIME * value)182 static void i64_property(property_list &prop_list, uint32_t tag, uint32_t flags, FILETIME *value) {
183     if (value) {
184         property p;
185         p.tag      = tag;
186         p.flags    = flags;
187         p.length   = value->dwLowDateTime;
188         p.reserved = value->dwHighDateTime;
189         prop_list.push_back(p);
190     }
191 }
192 
193 
194 static void nzi_property(property_list &prop_list, uint32_t tag, uint32_t flags, uint32_t value);
nzi_property(property_list & prop_list,uint32_t tag,uint32_t flags,uint32_t value)195 static void nzi_property(property_list &prop_list, uint32_t tag, uint32_t flags, uint32_t value) {
196     if (value) int_property(prop_list, tag, flags, value);
197 }
198 
199 
write_msg_email(char * fname,pst_item * item,pst_file * pst)200 void write_msg_email(char *fname, pst_item* item, pst_file* pst) {
201     // this is not an email item
202     if (!item->email) return;
203     DEBUG_ENT("write_msg_email");
204 
205     pst_item_email &email = *(item->email);
206 
207     char charset[30];
208     const char* body_charset = pst_default_charset(item, sizeof(charset), charset);
209     DEBUG_INFO(("%s body charset seems to be %s\n", fname, body_charset));
210     body_charset = "iso-8859-1//TRANSLIT//IGNORE";
211 
212     gsf_init();
213 
214     GsfOutfile *outfile;
215     GsfOutput  *output;
216     GError    *err = NULL;
217 
218     output = gsf_output_stdio_new(fname, &err);
219     if (output == NULL) {
220         gsf_shutdown();
221         DEBUG_INFO(("unable to open output .msg file %s\n", fname));
222         DEBUG_RET();
223         return;
224     }
225 
226     struct top_property_header {
227         uint32_t  reserved1;
228         uint32_t  reserved2;
229         uint32_t  next_recipient;   // same as recipient count
230         uint32_t  next_attachment;  // same as attachment count
231         uint32_t  recipient_count;
232         uint32_t  attachment_count;
233         uint32_t  reserved3;
234         uint32_t  reserved4;
235     };
236 
237     top_property_header top_head;
238     memset(&top_head, 0, sizeof(top_head));
239 
240     outfile = gsf_outfile_msole_new(output);
241     g_object_unref(G_OBJECT(output));
242 
243     output = GSF_OUTPUT(outfile);
244     property_list prop_list;
245 
246     int_property(prop_list, 0x00170003, 0x6, email.importance);
247     nzi_property(prop_list, 0x0023000B, 0x6, email.delivery_report);
248     nzi_property(prop_list, 0x00260003, 0x6, email.priority);
249     nzi_property(prop_list, 0x0029000B, 0x6, email.read_receipt);
250     nzi_property(prop_list, 0x002E0003, 0x6, email.original_sensitivity);
251     nzi_property(prop_list, 0x00360003, 0x6, email.sensitivity);
252     nzi_property(prop_list, 0x0C17000B, 0x6, email.reply_requested);
253     nzi_property(prop_list, 0x0E01000B, 0x6, email.delete_after_submit);
254     int_property(prop_list, 0x0E070003, 0x6, item->flags);
255     i64_property(prop_list, 0x00390040, 0x6, email.sent_date);
256     GsfOutfile *out = GSF_OUTFILE (output);
257     string_property(out, prop_list, 0x001A001E, item->ascii_type);
258     string_property(out, prop_list, 0x0037001E, body_charset, item->subject);
259     strin0_property(out, prop_list, 0x003B0102, body_charset, email.outlook_sender);
260     string_property(out, prop_list, 0x003D001E, string(""));
261     string_property(out, prop_list, 0x0040001E, body_charset, email.outlook_received_name1);
262     string_property(out, prop_list, 0x0042001E, body_charset, email.outlook_sender_name);
263     string_property(out, prop_list, 0x0044001E, body_charset, email.outlook_recipient_name);
264     string_property(out, prop_list, 0x0050001E, body_charset, email.reply_to);
265     strin0_property(out, prop_list, 0x00510102, body_charset, email.outlook_recipient);
266     strin0_property(out, prop_list, 0x00520102, body_charset, email.outlook_recipient2);
267     string_property(out, prop_list, 0x0064001E, body_charset, email.sender_access);
268     string_property(out, prop_list, 0x0065001E, body_charset, email.sender_address);
269     string_property(out, prop_list, 0x0070001E, body_charset, email.processed_subject);
270     string_property(out, prop_list, 0x00710102,               email.conversation_index);
271     string_property(out, prop_list, 0x0072001E, body_charset, email.original_bcc);
272     string_property(out, prop_list, 0x0073001E, body_charset, email.original_cc);
273     string_property(out, prop_list, 0x0074001E, body_charset, email.original_to);
274     string_property(out, prop_list, 0x0075001E, body_charset, email.recip_access);
275     string_property(out, prop_list, 0x0076001E, body_charset, email.recip_address);
276     string_property(out, prop_list, 0x0077001E, body_charset, email.recip2_access);
277     string_property(out, prop_list, 0x0078001E, body_charset, email.recip2_address);
278     string_property(out, prop_list, 0x007D001E, body_charset, email.header);
279     string_property(out, prop_list, 0x0C1A001E, body_charset, email.outlook_sender_name2);
280     strin0_property(out, prop_list, 0x0C1D0102, body_charset, email.outlook_sender2);
281     string_property(out, prop_list, 0x0C1E001E, body_charset, email.sender2_access);
282     string_property(out, prop_list, 0x0C1F001E, body_charset, email.sender2_address);
283     string_property(out, prop_list, 0x0E02001E, body_charset, email.bcc_address);
284     string_property(out, prop_list, 0x0E03001E, body_charset, email.cc_address);
285     string_property(out, prop_list, 0x0E04001E, body_charset, email.sentto_address);
286     string_property(out, prop_list, 0x0E1D001E, body_charset, email.outlook_normalized_subject);
287     string_property(out, prop_list, 0x1000001E, body_charset, item->body);
288     string_property(out, prop_list, 0x1013001E, body_charset, email.htmlbody);
289     string_property(out, prop_list, 0x1035001E, body_charset, email.messageid);
290     string_property(out, prop_list, 0x1042001E, body_charset, email.in_reply_to);
291     string_property(out, prop_list, 0x1046001E, body_charset, email.return_path_address);
292     // any property over 0x8000 needs entries in the __nameid to make them
293     // either string named or numerical named properties.
294 
295     {
296         vector<char> n(50);
297         {
298             snprintf(&n[0], n.size(), "__recip_version1.0_#%08X", top_head.recipient_count);
299             GsfOutput  *output = gsf_outfile_new_child(out, &n[0], true);
300             {
301                 int v = 1;  // to
302                 property_list prop_list;
303                 int_property(prop_list, 0x0C150003, 0x6, v);                        // PidTagRecipientType
304                 int_property(prop_list, 0x30000003, 0x6, top_head.recipient_count); // PR_ROWID
305                 GsfOutfile *out = GSF_OUTFILE (output);
306                 string_property(out, prop_list, 0x3001001E, body_charset, item->file_as);
307                 if (item->contact) {
308                     string_property(out, prop_list, 0x3002001E, body_charset, item->contact->address1_transport);
309                     string_property(out, prop_list, 0x3003001E, body_charset, item->contact->address1);
310                     string_property(out, prop_list, 0x5ff6001E, body_charset, item->contact->address1);
311                 }
312                 strin0_property(out, prop_list, 0x300B0102, body_charset, email.outlook_search_key);
313                 write_properties(out, prop_list, (const guint8*)&top_head, 8);  // convenient 8 bytes of reserved zeros
314                 gsf_output_close(output);
315                 g_object_unref(G_OBJECT(output));
316                 top_head.next_recipient++;
317                 top_head.recipient_count++;
318             }
319         }
320         if (email.cc_address.str) {
321             snprintf(&n[0], n.size(), "__recip_version1.0_#%08X", top_head.recipient_count);
322             GsfOutput  *output = gsf_outfile_new_child(out, &n[0], true);
323             {
324                 int v = 2;  // cc
325                 property_list prop_list;
326                 int_property(prop_list, 0x0C150003, 0x6, v);                        // PidTagRecipientType
327                 int_property(prop_list, 0x30000003, 0x6, top_head.recipient_count); // PR_ROWID
328                 GsfOutfile *out = GSF_OUTFILE (output);
329                 string_property(out, prop_list, 0x3001001E, body_charset, email.cc_address);
330                 string_property(out, prop_list, 0x3003001E, body_charset, email.cc_address);
331                 string_property(out, prop_list, 0x5ff6001E, body_charset, email.cc_address);
332                 write_properties(out, prop_list, (const guint8*)&top_head, 8);  // convenient 8 bytes of reserved zeros
333                 gsf_output_close(output);
334                 g_object_unref(G_OBJECT(output));
335                 top_head.next_recipient++;
336                 top_head.recipient_count++;
337             }
338         }
339         if (email.bcc_address.str) {
340             snprintf(&n[0], n.size(), "__recip_version1.0_#%08X", top_head.recipient_count);
341             GsfOutput  *output = gsf_outfile_new_child(out, &n[0], true);
342             {
343                 int v = 3;  // bcc
344                 property_list prop_list;
345                 int_property(prop_list, 0x0C150003, 0x6, v);                        // PidTagRecipientType
346                 int_property(prop_list, 0x30000003, 0x6, top_head.recipient_count); // PR_ROWID
347                 GsfOutfile *out = GSF_OUTFILE (output);
348                 string_property(out, prop_list, 0x3001001E, body_charset, email.bcc_address);
349                 string_property(out, prop_list, 0x3003001E, body_charset, email.bcc_address);
350                 string_property(out, prop_list, 0x5ff6001E, body_charset, email.bcc_address);
351                 write_properties(out, prop_list, (const guint8*)&top_head, 8);  // convenient 8 bytes of reserved zeros
352                 gsf_output_close(output);
353                 g_object_unref(G_OBJECT(output));
354                 top_head.next_recipient++;
355                 top_head.recipient_count++;
356             }
357         }
358     }
359 
360     pst_item_attach *a = item->attach;
361     while (a) {
362         if (a->method == PST_ATTACH_EMBEDDED) {
363             // not implemented yet
364         }
365         else if (a->data.data || a->i_id) {
366             vector<char> n(50);
367             snprintf(&n[0], n.size(), "__attach_version1.0_#%08X", top_head.attachment_count);
368             GsfOutput  *output = gsf_outfile_new_child(out, &n[0], true);
369             {
370                 FILE *fp = fopen("temp_file_attachment", "w+b");
371                 if (fp) {
372                     pst_attach_to_file(pst, a, fp); // data is now in the file
373                     fseek(fp, 0, SEEK_SET);
374                     property_list prop_list;
375                     int_property(prop_list, 0x0E210003, 0x2, top_head.attachment_count);    // MAPI_ATTACH_NUM
376                     int_property(prop_list, 0x0FF40003, 0x2, 2);            // PR_ACCESS read
377                     int_property(prop_list, 0x0FF70003, 0x2, 0);            // PR_ACCESS_LEVEL read only
378                     int_property(prop_list, 0x0FFE0003, 0x2, 7);            // PR_OBJECT_TYPE attachment
379                     int_property(prop_list, 0x37050003, 0x7, 1);            // PR_ATTACH_METHOD by value
380                     int_property(prop_list, 0x370B0003, 0x7, a->position);  // PR_RENDERING_POSITION
381                     int_property(prop_list, 0x37100003, 0x6, a->sequence);  // PR_ATTACH_MIME_SEQUENCE
382                     GsfOutfile *out = GSF_OUTFILE (output);
383                     string_property(out, prop_list, 0x0FF90102, item->record_key);
384                     string_property(out, prop_list, 0x37010102, fp);
385                     if (a->filename2.str) {
386                         // have long file name
387                         string_property(out, prop_list, 0x3707001E, body_charset, a->filename2);
388                     }
389                     else if (a->filename1.str) {
390                         // have short file name
391                         string_property(out, prop_list, 0x3704001E, body_charset, a->filename1);
392                     }
393                     else {
394                         // make up a name
395                         const char *n = "inline";
396                         string_property(out, prop_list, 0x3704001E, n, strlen(n));
397                     }
398                     string_property(out, prop_list, 0x370E001E, body_charset, a->mimetype);
399                     write_properties(out, prop_list, (const guint8*)&top_head, 8);  // convenient 8 bytes of reserved zeros
400                     gsf_output_close(output);
401                     g_object_unref(G_OBJECT(output));
402                     top_head.next_attachment++;
403                     top_head.attachment_count++;
404                     fclose(fp);
405                 }
406             }
407         }
408         a = a->next;
409     }
410 
411     write_properties(out, prop_list, (const guint8*)&top_head, sizeof(top_head));
412 
413     {
414         GsfOutput  *output = gsf_outfile_new_child(out, "__nameid_version1.0", true);
415         {
416             GsfOutfile *out = GSF_OUTFILE (output);
417             empty_property(out, 0x00020102);
418             empty_property(out, 0x00030102);
419             empty_property(out, 0x00040102);
420             gsf_output_close(output);
421             g_object_unref(G_OBJECT(output));
422         }
423     }
424 
425     gsf_output_close(output);
426     g_object_unref(G_OBJECT(output));
427 
428     gsf_shutdown();
429     DEBUG_RET();
430 }
431 
432