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