2
Convert Exchange mails to mbox
6
Copyright (C) Julien Kerihuel 2007
8
This program is free software; you can redistribute it and/or modify
9
it under the terms of the GNU General Public License as published by
10
the Free Software Foundation; either version 3 of the License, or
11
(at your option) any later version.
13
This program is distributed in the hope that it will be useful,
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
GNU General Public License for more details.
18
You should have received a copy of the GNU General Public License
19
along with this program. If not, see <http://www.gnu.org/licenses/>.
22
#include "libmapi/libmapi.h"
23
#include <samba/popt.h>
26
#include <sys/types.h>
40
#include "openchange-tools.h"
42
/* Ugly and lazy but working ... */
43
#define DEFAULT_BOUNDARY_BASE "DocE+STaALJfprDB"
44
#define MESSAGEID "Message-ID: "
45
#define MESSAGEID_LEN 11
48
* how much to request at a time, and it's complex :-(
49
* This was 4096 - was getting NT_STATUS_BUFFER_TOO_SMALL loading large
51
* It must be less than 16K (Windows API is a signed short)
52
* If you ask for more than windows allows you get nothing.
53
* Asking for too little just dogs the performance
55
* found the NTSTATUS error by adding debug to the ReadStream code.
57
#define MAX_READ_SIZE 12000
59
static int message_error = 0; /* did we get an error processing message */
61
static bool opt_test = false;
63
static char boundary_base[128] = DEFAULT_BOUNDARY_BASE;
65
static time_t start_time;
67
static char *boundary(int index)
69
snprintf(boundary_base, sizeof(boundary_base), "%s_%lu_%d",
70
DEFAULT_BOUNDARY_BASE, (unsigned long) start_time, index);
74
static void fix_froms(unsigned char *cp, int len)
76
unsigned char *ep = cp + len;
77
for (; cp + 6 < ep; cp++) {
80
if (strncmp((char *)cp, "\nFrom ", 6) == 0) {
88
* find a Header at the start of a line (or the start of the text)
91
static char *find_header(char *cp, char *hdr)
94
while ((ep = strcasestr(cp, hdr))) {
95
if (ep == cp || ep[-1] == '\n')
104
* delete a message on the exchange server
106
static bool delete_messages(
108
struct mapi_session *session,
112
enum MAPISTATUS retval;
113
mapi_object_t obj_store;
114
mapi_object_t obj_inbox;
115
mapi_object_t obj_table;
117
struct SPropTagArray *SPropTagArray;
118
struct SRowSet SRowSet;
121
struct mapi_profile *profile;
123
if (!del_count || !del_msgid) {
127
profile = session->profile;
129
/* Open the default message store */
130
mapi_object_init(&obj_store);
131
retval = OpenMsgStore(session, &obj_store);
132
if (retval != MAPI_E_SUCCESS) return false;
135
retval = GetReceiveFolder(&obj_store, &id_inbox, NULL);
136
if (retval != MAPI_E_SUCCESS) return false;
138
mapi_object_init(&obj_inbox);
139
retval = OpenFolder(&obj_store, id_inbox, &obj_inbox);
140
if (retval != MAPI_E_SUCCESS) return false;
142
mapi_object_init(&obj_table);
143
retval = GetContentsTable(&obj_inbox, &obj_table, 0, NULL);
144
if (retval != MAPI_E_SUCCESS) return false;
146
SPropTagArray = set_SPropTagArray(mem_ctx, 0x3,
149
PR_INTERNET_MESSAGE_ID);
150
retval = SetColumns(&obj_table, SPropTagArray);
151
MAPIFreeBuffer(SPropTagArray);
153
if (retval != MAPI_E_SUCCESS)
156
while ((retval = QueryRows(&obj_table, 0xa, TBL_ADVANCE, &SRowSet)) == MAPI_E_SUCCESS) {
160
for (i = 0; i < SRowSet.cRows; i++) {
161
const char *message_id;
163
message_id = (const char *)find_SPropValue_data(&(SRowSet.aRow[i]), PR_INTERNET_MESSAGE_ID);
168
for (j = 0; j < del_count; j++) {
169
if (strcmp(message_id, del_msgid[j]) == 0) {
170
id_message = SRowSet.aRow[i].lpProps[1].value.d;
171
retval = DeleteMessage(&obj_inbox, &id_message, 1);
172
if (retval == MAPI_E_SUCCESS) {
173
printf("%s deleted from the Exchange server\n",
176
mapi_profile_delete_string_attr(profile->mapi_ctx,
177
profile->profname, "Message-ID", del_msgid[j]);
179
printf("%s not deleted from profile %s: err=0x%x\n",
180
del_msgid[j], profile->profname, retval);
182
printf("%s NOT deleted from the Exchange server\n",
190
mapi_object_release(&obj_table);
191
mapi_object_release(&obj_inbox);
192
mapi_object_release(&obj_store);
198
* Fetch message ids from the existing mbox
200
static uint32_t update(
201
TALLOC_CTX *mem_ctx, FILE *fp,
202
struct mapi_session *session)
204
enum MAPISTATUS retval;
205
struct mapi_profile *profile;
208
#if !defined(__FreeBSD__)
215
unsigned int mbox_count = 0;
220
unsigned int del_count = 0;
222
profile = session->profile;
224
mbox_msgids = talloc_zero(mem_ctx, char *);
225
/* Add Message-ID attribute to the profile if it is missing */
226
#if defined(__FreeBSD__)
227
while ((line = fgetln(fp, &read_size)) != NULL) {
229
while ((size = getline(&line, &read_size, fp)) != -1) {
231
if (line && !strncmp(line, MESSAGEID, strlen(MESSAGEID))) {
232
msgid = strstr(line, MESSAGEID);
233
id = talloc_strdup(mem_ctx, msgid + strlen(MESSAGEID));
234
id[strlen(id) - 1] = 0;
236
mbox_msgids = talloc_realloc(mem_ctx, mbox_msgids, char *, mbox_count + 2);
237
mbox_msgids[mbox_count] = talloc_strdup(mem_ctx, id);
240
retval = FindProfileAttr(profile, "Message-ID", id);
241
if (GetLastError() == MAPI_E_NOT_FOUND) {
243
printf("[+] Adding %s to %s\n", id, profile->profname);
244
retval = mapi_profile_add_string_attr(profile->mapi_ctx, profile->profname, "Message-ID", id);
245
if (retval != MAPI_E_SUCCESS) {
246
mapi_errstr("mapi_profile_add_string_attr", GetLastError());
248
talloc_free(profname);
249
MAPIUninitialize(profile->mapi_ctx);
261
/* Remove Message-ID and update Exchange mailbox if a
262
* Message-ID is missing in mbox
264
retval = GetProfileAttr(profile, "Message-ID", &count, &prof_msgids);
265
if (retval == MAPI_E_NOT_FOUND) {
266
printf("No Synchonizing is needed at all\n");
267
return MAPI_E_SUCCESS;
269
fprintf(stderr, "GetProfileAttr failed - %x\n", retval);
273
if (count != mbox_count) {
274
printf("{+] Synchonizing mbox with Exchange mailbox\n");
276
del_msgids = talloc_zero(mem_ctx, char *);
279
for (i = 0; i < count; i++) {
281
for (j = 0; j < mbox_count; j++) {
282
if (!strcmp(prof_msgids[i], mbox_msgids[j])) {
286
if (found == false) {
287
del_msgids = talloc_realloc(mem_ctx, del_msgids, char *, del_count + 2);
288
del_msgids[del_count] = talloc_strdup(mem_ctx, prof_msgids[i]);
293
delete_messages(mem_ctx, session, del_msgids, del_count);
295
talloc_free(del_msgids);
298
printf("[+] mbox already synchronized with Exchange Mailbox\n");
301
talloc_free(prof_msgids);
302
talloc_free(mbox_msgids);
304
return MAPI_E_SUCCESS;
307
static const char *get_filename(const char *filename)
311
if (!filename) return NULL;
313
substr = rindex(filename, '/');
314
if (substr) return substr;
320
static char *get_base64_attachment(TALLOC_CTX *mem_ctx, mapi_object_t *obj_attach, const uint32_t size, char **magic)
322
enum MAPISTATUS retval;
325
mapi_object_t obj_stream;
326
uint32_t stream_size;
329
magic_t cookie = NULL;
331
mapi_object_init(&obj_stream);
332
retval = OpenStream(obj_attach, PR_ATTACH_DATA_BIN, 0, &obj_stream);
333
if (retval != MAPI_E_SUCCESS) {
334
fprintf(stderr, "OpenStream failed %x\n", retval);
339
data.data = talloc_zero_size(mem_ctx, size);
341
for (stream_size = 0; stream_size < size; ) {
343
* exchange can only handle about 4K chunks at a time, and don't
344
* ask for more or you get none
346
retval = ReadStream(&obj_stream, data.data + stream_size,
347
(stream_size + MAX_READ_SIZE < size) ? MAX_READ_SIZE :
348
(size - stream_size), &read_size);
349
if ((retval != MAPI_E_SUCCESS) || read_size == 0)
351
stream_size += read_size;
353
if (retval != MAPI_E_SUCCESS) {
354
fprintf(stderr, "ReadStream failed retval=%x read_size=%d "
355
"stream_size=%d size=%d\n",
356
retval, read_size, stream_size, size);
357
talloc_free(data.data);
358
mapi_object_release(&obj_stream);
362
data.length = stream_size;
365
/* if they want a mime magic string try and autodetect one */
366
cookie = magic_open(MAGIC_MIME);
367
if (cookie == NULL) {
368
fprintf(stderr, "%s,%d - NULL\n", __FILE__, __LINE__);
369
printf("%s\n", magic_error(cookie));
370
talloc_free(data.data);
371
mapi_object_release(&obj_stream);
374
if (magic_load(cookie, NULL) == -1) {
375
fprintf(stderr, "%s,%d - NULL\n", __FILE__, __LINE__);
376
printf("%s\n", magic_error(cookie));
377
talloc_free(data.data);
378
mapi_object_release(&obj_stream);
381
tmp = magic_buffer(cookie, (void *)data.data, data.length);
382
*magic = talloc_strdup(mem_ctx, tmp);
386
/* convert attachment to base64 */
387
ret = ldb_base64_encode(mem_ctx, (const char *)data.data, data.length);
389
talloc_free(data.data);
390
mapi_object_release(&obj_stream);
396
#define WRAP_LINES_AT 76
398
static void write_base64_data(FILE *fp, const char *buf)
400
int len = strlen(buf);
404
int chunk = len > WRAP_LINES_AT ? WRAP_LINES_AT : len;
406
n = fwrite(buf, len > WRAP_LINES_AT ? WRAP_LINES_AT : len, 1, fp);
408
fprintf(stderr, "Error writing %d bytes of base64 attachment: %d\n",
412
fwrite("\n", 1, 1, fp);
418
* Read a stream and store it in a DATA_BLOB
420
static enum MAPISTATUS get_stream(TALLOC_CTX *mem_ctx,
421
mapi_object_t *obj_stream,
424
enum MAPISTATUS retval;
426
uint8_t buf[MAX_READ_SIZE];
429
body->data = talloc_zero(mem_ctx, uint8_t);
432
retval = ReadStream(obj_stream, buf, MAX_READ_SIZE, &read_size);
433
MAPI_RETVAL_IF(retval, GetLastError(), body->data);
435
body->data = talloc_realloc(mem_ctx, body->data, uint8_t,
436
body->length + read_size);
437
memcpy(&(body->data[body->length]), buf, read_size);
438
body->length += read_size;
443
return MAPI_E_SUCCESS;
451
/* Fetch the body, no fancy junk */
452
static enum MAPISTATUS get_body(TALLOC_CTX *mem_ctx,
453
mapi_object_t *obj_message,
455
body_stuff_t body[3],
458
mapi_object_t obj_stream;
459
enum MAPISTATUS retval;
461
const struct SBinary_short *bin;
464
memset(body, 0, sizeof(body));
466
data = octool_get_propval(aRow, PR_BODY);
467
if (data && strlen(data)) {
468
body[*body_count].body.data = talloc_memdup(mem_ctx, data, strlen(data));
469
body[*body_count].body.length = strlen(data);
470
body[*body_count].body_header = "Content-Type: text/plain; charset=us-ascii\n";
474
bin = (const struct SBinary_short *) octool_get_propval(aRow, PR_HTML);
475
if (bin && bin->cb) {
476
body[*body_count].body.data = talloc_memdup(mem_ctx, bin->lpb, bin->cb);
477
body[*body_count].body.length = bin->cb;
478
body[*body_count].body_header = "Content-Type: text/html\n";
483
bin = (const struct SBinary_short *) octool_get_propval(aRow, PR_RTF_COMPRESSED);
484
if (bin && bin->cb) {
485
body[*body_count].body.data = talloc_memdup(mem_ctx, bin->lpb, bin->cb);
486
body[*body_count].body.length = bin->cb;
487
body[*body_count].body_header = "Content-Type: text/rtf\n";
492
if (*body_count <= 0) {
493
printf("No HTML or TEXT, generating TEXT body\n");
494
/* generate a body for us ? */
495
mapi_object_init(&obj_stream);
496
retval = OpenStream(obj_message, PR_BODY, 0, &obj_stream);
498
fprintf(stderr, "Failed to get a message body, making empty one: %x\n", retval);
500
body[*body_count].body.data = talloc_zero(mem_ctx, uint8_t);
501
body[*body_count].body.length = 0;
502
body[*body_count].body_header = "Content-Type: text/plain; charset=us-ascii\n";
504
body[*body_count].body.length = 0;
506
retval = get_stream(mem_ctx, &obj_stream, &body[*body_count].body);
508
body[*body_count].body.length = 0;
509
fprintf(stderr, "HTML ERROR1 %x\n", retval);
512
mapi_object_release(&obj_stream);
513
if (body[*body_count].body.length) {
514
body[*body_count].body_header = "Content-Type: text/plain; charset=us-ascii\n";
519
return MAPI_E_SUCCESS;
525
From Administrator Mon Apr 23 14:43:01 2007
526
Date: Mon Apr 23 14:43:01 2007
529
Subject: This is the subject
531
This is a sample mail
535
static bool message2mbox(TALLOC_CTX *mem_ctx, FILE *fp,
536
struct SRow *aRow, mapi_object_t *obj_message,
539
enum MAPISTATUS retval;
540
mapi_object_t obj_tb_attach;
541
mapi_object_t obj_attach;
542
const uint64_t *delivery_date;
543
const char *date = NULL;
544
const char *to = NULL;
545
const char *cc = NULL;
546
const char *bcc = NULL;
547
const char *from = NULL;
548
const char *subject = NULL;
550
const char *msgheaders = NULL;
551
const char *attach_filename;
552
const uint32_t *attach_size;
553
char *attachment_data;
554
const uint8_t *has_attach = NULL;
555
const uint32_t *attach_num = NULL;
558
struct SPropTagArray *SPropTagArray = NULL;
559
struct SPropValue *lpProps;
561
struct SRowSet rowset_attach;
565
body_stuff_t body[3];
568
has_attach = (const uint8_t *) octool_get_propval(aRow, PR_HASATTACH);
569
to = (const char *) octool_get_propval(aRow, PR_DISPLAY_TO);
570
cc = (const char *) octool_get_propval(aRow, PR_DISPLAY_CC);
571
bcc = (const char *) octool_get_propval(aRow, PR_DISPLAY_BCC);
573
delivery_date = (const uint64_t *)octool_get_propval(aRow, PR_MESSAGE_DELIVERY_TIME);
575
date = nt_time_string(mem_ctx, *delivery_date);
580
from = (const char *) octool_get_propval(aRow, PR_SENT_REPRESENTING_NAME);
585
subject = (const char*) octool_get_propval(aRow, PR_SUBJECT);
586
msgid = (const char *) octool_get_propval(aRow, PR_INTERNET_MESSAGE_ID);
588
msgheaders = (const char *) octool_get_propval(aRow, PR_TRANSPORT_MESSAGE_HEADERS);
590
retval = get_body(mem_ctx, obj_message, aRow, body, &body_count);
592
/* First line From - but only if base_level == 0 */
593
if (base_level == 0) {
595
f = talloc_strdup(mem_ctx, from);
596
/* strip out all '"'s, ugly but works */
597
for (p = f; p && *p; ) {
599
memmove(p, p+1, strlen(p)); /* gets NUL */
604
fprintf(fp, "From \"%s\" %s\n", f, date);
609
char *mhalloc, *mhclean, *mhend, *mhp, *mht;
611
mhalloc = talloc_strdup(mem_ctx, msgheaders);
615
/* Skip past the comment Exchange adds to the beginning */
616
mhclean = strchr(mhclean, '\n');
620
} while (isspace(*mhclean));
622
/* Trim off the empty mime parts that Exchange leaves after
623
the end of the headers */
624
mhend = strstr(mhclean, "\n------=");
631
while ((mhend = strchr(mhclean, '\r')))
632
memmove(mhend, mhend+1, strlen(mhend) /* gets NUL */);
635
* strip Content-* headers (we make our own), be sure to get
636
* and extended (indented parts) of this header
640
* strip some bad-for-us headers (poor mans lookup table below :-)
642
mhend = find_header(mhclean, "Content-");
644
mhend = find_header(mhclean, "X-MS-");
646
mhend = find_header(mhclean, "Lines:");
652
while ((mhp = strchr(mht, '\n'))) {
654
if (!*mhp || !isspace(*mhp))
660
/* match was in the middle of other headers */
661
memmove(mhend, mhp, strlen(mhp) + 1);
663
/* match was at the end of the headers, truncate it */
668
/* remove any NL's at end of headers */
669
mhp = mhclean + strlen(mhclean);
670
while (mhp > mhclean && mhp[-1] == '\n')
673
line = talloc_asprintf(mem_ctx, "%s\n", mhclean);
674
if (line) fwrite(line, strlen(line), 1, fp);
676
talloc_free(mhalloc);
681
/* Second line: Date */
682
line = talloc_asprintf(mem_ctx, "Date: %s\n", date);
684
fwrite(line, strlen(line), 1, fp);
688
/* Third line From */
689
line = talloc_asprintf(mem_ctx, "From: %s\n", from);
691
fwrite(line, strlen(line), 1, fp);
697
line = talloc_asprintf(mem_ctx, "To: %s\n", to);
699
fwrite(line, strlen(line), 1, fp);
705
line = talloc_asprintf(mem_ctx, "Cc: %s\n", cc);
707
fwrite(line, strlen(line), 1, fp);
713
line = talloc_asprintf(mem_ctx, "Bcc: %s\n", bcc);
715
fwrite(line, strlen(line), 1, fp);
722
line = talloc_asprintf(mem_ctx, "Subject: %s\n", subject);
724
fwrite(line, strlen(line), 1, fp);
730
line = talloc_asprintf(mem_ctx, "Message-ID: %s\n", msgid);
732
fwrite(line, strlen(line), 1, fp);
738
/* Set multi-type if we have attachment mixed for all things/alternative
740
if (has_attach && *has_attach) {
741
fprintf(fp, "Content-Type: multipart/mixed; boundary=\"%s\"\n",
742
boundary(base_level+0));
743
} else if (body_count > 1) {
744
fprintf(fp, "Content-Type: multipart/alternative; boundary=\"%s\"\n",
745
boundary(base_level+0));
749
if (body_count > 0) {
750
if ((has_attach && *has_attach) || body_count > 1) {
751
/* blank line before content */
753
fwrite("\n", 1, 1, fp);
756
fprintf(fp, "--%s\n", boundary(base_level+0));
760
* if we have more than 1 body we need to do a multipart alternative
761
* within the first attachment
763
if ((has_attach && *has_attach) && body_count > 1) {
764
fprintf(fp, "Content-Type: multipart/alternative; boundary=\"%s\"\n", boundary(base_level+1));
765
fwrite("\n", 1, 1, fp);
766
fprintf(fp, "\n\n--%s\n", boundary(base_level+1));
770
* output content type
772
fwrite(body[0].body_header, strlen(body[0].body_header), 1, fp);
773
fprintf(fp, "Content-Disposition: inline\n");
775
/* blank after header */
776
fwrite("\n", 1, 1, fp);
779
fix_froms(body[0].body.data, body[0].body.length);
780
fwrite(body[0].body.data, body[0].body.length, 1, fp);
781
talloc_free(body[0].body.data);
784
/* blank line before content */
786
fwrite("\n", 1, 1, fp);
790
/* do the other bodies before attachments */
791
for (i = 1; i < body_count && i < 3; i++) {
792
if (has_attach && *has_attach) {
793
fprintf(fp, "\n\n--%s\n", boundary(base_level+1));
795
fprintf(fp, "\n\n--%s\n", boundary(base_level+0));
797
fwrite(body[i].body_header, strlen(body[i].body_header), 1, fp);
798
fprintf(fp, "Content-Disposition: inline\n");
799
fwrite("\n", 1, 1, fp);
800
fix_froms(body[i].body.data, body[i].body.length);
801
fwrite(body[i].body.data, body[i].body.length, 1, fp);
804
if (has_attach && *has_attach) {
805
/* close body attachments */
806
if (body_count > 1) {
807
fprintf(fp, "\n\n--%s--\n", boundary(base_level+1));
810
mapi_object_init(&obj_tb_attach);
811
retval = GetAttachmentTable(obj_message, &obj_tb_attach);
812
if (retval == MAPI_E_SUCCESS) {
813
SPropTagArray = set_SPropTagArray(mem_ctx, 0x1, PR_ATTACH_NUM);
814
retval = SetColumns(&obj_tb_attach, SPropTagArray);
815
MAPIFreeBuffer(SPropTagArray);
816
MAPI_RETVAL_IF(retval, retval, NULL);
818
retval = QueryRows(&obj_tb_attach, 0xa, TBL_ADVANCE, &rowset_attach);
819
MAPI_RETVAL_IF(retval, retval, NULL);
821
for (i = 0; i < rowset_attach.cRows; i++) {
822
//attach_num = (const uint32_t *)find_SPropValue_data(&(rowset_attach.aRow[i]), PR_ATTACH_NUM);
823
uint32_t n = rowset_attach.aRow[i].lpProps[0].value.l;
825
retval = OpenAttach(obj_message, *attach_num, &obj_attach);
826
if (retval == MAPI_E_SUCCESS) {
827
SPropTagArray = set_SPropTagArray(mem_ctx, 0x5,
829
PR_ATTACH_LONG_FILENAME,
834
retval = GetProps(&obj_attach, MAPI_UNICODE, SPropTagArray, &lpProps, &count);
835
MAPIFreeBuffer(SPropTagArray);
836
if (retval == MAPI_E_SUCCESS) {
837
uint32_t *mp, method = -1;
839
aRow2.ulAdrEntryPad = 0;
840
aRow2.cValues = count;
841
aRow2.lpProps = lpProps;
843
mp = (uint32_t *) octool_get_propval(&aRow2, PR_ATTACH_METHOD);
847
attach_filename = get_filename(octool_get_propval(&aRow2, PR_ATTACH_LONG_FILENAME));
848
if (!attach_filename || (attach_filename && !strcmp(attach_filename, ""))) {
849
attach_filename = get_filename(octool_get_propval(&aRow2, PR_ATTACH_FILENAME));
851
attach_size = (const uint32_t *) octool_get_propval(&aRow2, PR_ATTACH_SIZE);
853
attachment_data = NULL;
855
case ATTACH_BY_VALUE:
856
magic = (char *) octool_get_propval(&aRow2, PR_ATTACH_MIME_TAG);
858
magic = talloc_strdup(mem_ctx, magic);
859
attachment_data = get_base64_attachment(mem_ctx,
860
&obj_attach, *attach_size,
861
magic ? NULL : &magic);
862
if (attachment_data == NULL) {
864
fprintf(stderr, "Failed to read attachment for message %s\n", msgid ? msgid : "unknown");
867
fprintf(fp, "\n\n--%s\n", boundary(base_level+0));
868
fprintf(fp, "Content-Disposition: attachment; filename=\"%s\"\n", attach_filename);
869
fprintf(fp, "Content-Type: %s\n", magic);
870
fprintf(fp, "Content-Transfer-Encoding: base64\n\n");
871
write_base64_data(fp, attachment_data);
872
talloc_free(attachment_data);
875
case ATTACH_BY_REFERENCE:
876
fprintf(stderr,"ATTACH_BY_REFERENCE unsupported\n");
879
case ATTACH_BY_REF_RESOLVE:
880
fprintf(stderr,"ATTACH_BY_REF_RESOLVE unsupported\n");
883
case ATTACH_BY_REF_ONLY:
884
fprintf(stderr,"ATTACH_BY_REF_ONLY unsupported\n");
887
case ATTACH_EMBEDDED_MSG: {
888
mapi_object_t obj_embeddedmsg;
889
struct SPropTagArray *embTagArray = NULL;
890
struct SPropValue *embProps;
892
uint32_t emb_count = 0;
894
mapi_object_init(&obj_embeddedmsg);
895
retval = OpenEmbeddedMessage(&obj_attach,
896
&obj_embeddedmsg, MAPI_READONLY);
897
if (retval != MAPI_E_SUCCESS) {
898
fprintf(stderr, "Failed to open Embedded msg: %x\n", retval);
903
embTagArray = set_SPropTagArray(mem_ctx, 0x15,
904
PR_INTERNET_MESSAGE_ID,
905
PR_INTERNET_MESSAGE_ID_UNICODE,
906
PR_CONVERSATION_TOPIC,
907
PR_CONVERSATION_TOPIC_UNICODE,
908
PR_MESSAGE_DELIVERY_TIME,
909
PR_MSG_EDITOR_FORMAT,
915
PR_SENT_REPRESENTING_NAME,
916
PR_SENT_REPRESENTING_NAME_UNICODE,
918
PR_DISPLAY_TO_UNICODE,
920
PR_DISPLAY_CC_UNICODE,
922
PR_DISPLAY_BCC_UNICODE,
924
PR_TRANSPORT_MESSAGE_HEADERS);
925
retval = GetProps(&obj_embeddedmsg, MAPI_UNICODE, embTagArray,
926
&embProps, &emb_count);
927
MAPIFreeBuffer(embTagArray);
929
if (retval != MAPI_E_SUCCESS) {
930
fprintf(stderr, "Failed to get Embedded msg props: %x\n", retval);
935
/* Build a SRow structure */
936
eRow.ulAdrEntryPad = 0;
937
eRow.cValues = emb_count;
938
eRow.lpProps = embProps;
940
fprintf(fp, "\n\n--%s\n", boundary(base_level+0));
941
fprintf(fp, "Content-Type: message/rfc822\n");
942
fprintf(fp, "Content-Disposition: inline\n");
945
message2mbox(mem_ctx, fp, &eRow, &obj_embeddedmsg,
946
base_level + 2 /* 0 = main, 1 = alt */);
947
talloc_free(embProps);
950
fprintf(stderr,"ATTACH_OLE unsupported - "
951
"allowing message through anyway\n");
952
// message_error = 1;
955
fprintf(stderr, "Unsupported attach method = %d\n", method);
960
MAPIFreeBuffer(lpProps);
964
line = talloc_asprintf(mem_ctx, "\n\n--%s--\n\n\n", boundary(base_level+0));
966
fwrite(line, strlen(line), 1, fp);
970
} else if (body_count > 1) {
971
/* close body attachments */
972
fprintf(fp, "\n\n--%s--\n", boundary(base_level+0));
975
fwrite("\n\n\n", 3, 1, fp);
982
int main(int argc, const char *argv[])
984
TALLOC_CTX *mem_ctx = NULL;
985
enum MAPISTATUS retval;
986
struct mapi_context *mapi_ctx = NULL;
987
struct mapi_session *session = NULL;
988
struct mapi_profile *profile = NULL;
989
mapi_object_t obj_store;
990
mapi_object_t obj_inbox;
991
mapi_object_t obj_table;
992
mapi_object_t obj_message;
995
struct SPropTagArray *SPropTagArray = NULL;
996
struct SPropValue *lpProps;
998
struct SRowSet rowset;
1003
const char *opt_profdb = NULL;
1004
char *opt_profname = NULL;
1005
const char *opt_password = NULL;
1006
const char *opt_mbox = NULL;
1007
bool opt_update = false;
1008
bool opt_dumpdata = false;
1009
const char *opt_debug = NULL;
1012
enum {OPT_PROFILE_DB=1000, OPT_PROFILE, OPT_PASSWORD, OPT_MBOX, OPT_UPDATE,
1013
OPT_DEBUG, OPT_DUMPDATA, OPT_TEST};
1015
struct poptOption long_options[] = {
1017
{"test", 't', POPT_ARG_NONE, 0, OPT_TEST, "Do not update server, just download messages to mbox", NULL},
1018
{"database", 'f', POPT_ARG_STRING, NULL, OPT_PROFILE_DB, "set the profile database path", "PATH"},
1019
{"profile", 'p', POPT_ARG_STRING, NULL, OPT_PROFILE, "set the profile name", "PROFILE"},
1020
{"password", 'P', POPT_ARG_STRING, NULL, OPT_PASSWORD, "set the profile password", "PASSWORD"},
1021
{"mbox", 'm', POPT_ARG_STRING, NULL, OPT_MBOX, "set the mbox file", "FILENAME"},
1022
{"update", 'u', POPT_ARG_NONE, 0, OPT_UPDATE, "mirror mbox changes back to the Exchange server", NULL},
1023
{"debuglevel", 'd', POPT_ARG_STRING, NULL, OPT_DEBUG, "set the debug level", "LEVEL"},
1024
{"dump-data", 0, POPT_ARG_NONE, NULL, OPT_DUMPDATA, "dump the hex data", NULL},
1025
POPT_OPENCHANGE_VERSION
1026
{ NULL, 0, POPT_ARG_NONE, NULL, 0, NULL, NULL }
1029
start_time = time(0);
1031
mem_ctx = talloc_named(NULL, 0, "exchange2mbox");
1033
pc = poptGetContext("exchange2mbox", argc, argv, long_options, 0);
1035
while ((opt = poptGetNextOpt(pc)) != -1) {
1037
case OPT_PROFILE_DB:
1038
opt_profdb = poptGetOptArg(pc);
1041
opt_profname = talloc_strdup(mem_ctx, (char *)poptGetOptArg(pc));
1044
opt_password = poptGetOptArg(pc);
1047
opt_mbox = poptGetOptArg(pc);
1056
opt_debug = poptGetOptArg(pc);
1059
opt_dumpdata = true;
1069
opt_profdb = talloc_asprintf(mem_ctx, DEFAULT_PROFDB, getenv("HOME"));
1073
opt_mbox = talloc_asprintf(mem_ctx, DEFAULT_MBOX, getenv("HOME"));
1080
if ((fp = fopen(opt_mbox, "a+")) == NULL) {
1086
* Initialize MAPI subsystem
1089
retval = MAPIInitialize(&mapi_ctx, opt_profdb);
1090
if (retval != MAPI_E_SUCCESS) {
1091
mapi_errstr("MAPIInitialize", GetLastError());
1096
SetMAPIDumpData(mapi_ctx, opt_dumpdata);
1099
SetMAPIDebugLevel(mapi_ctx, atoi(opt_debug));
1102
/* if no profile is supplied use the default one */
1103
if (!opt_profname) {
1104
retval = GetDefaultProfile(mapi_ctx, &opt_profname);
1105
if (retval != MAPI_E_SUCCESS) {
1106
printf("No profile specified and no default profile found\n");
1111
retval = MapiLogonEx(mapi_ctx, &session, opt_profname, opt_password);
1112
talloc_free(opt_profname);
1113
if (retval != MAPI_E_SUCCESS) {
1114
mapi_errstr("MapiLogonEx", GetLastError());
1117
profile = session->profile;
1119
/* not sure about this, but it works and it's nice to have it there */
1120
profile->mapi_ctx = mapi_ctx;
1122
/* do the updates now */
1123
if (opt_update == true) {
1124
retval = update(mem_ctx, fp, session);
1125
if (retval != MAPI_E_SUCCESS) {
1126
printf("Problem encountered during update: %d\n", retval);
1131
/* Open the default message store */
1132
mapi_object_init(&obj_store);
1133
retval = OpenMsgStore(session, &obj_store);
1134
if (retval != MAPI_E_SUCCESS) {
1135
mapi_errstr("OpenMsgStore", GetLastError());
1140
retval = GetReceiveFolder(&obj_store, &id_inbox, NULL);
1141
MAPI_RETVAL_IF(retval, retval, mem_ctx);
1143
mapi_object_init(&obj_inbox);
1144
retval = OpenFolder(&obj_store, id_inbox, &obj_inbox);
1145
MAPI_RETVAL_IF(retval, retval, mem_ctx);
1147
mapi_object_init(&obj_table);
1148
retval = GetContentsTable(&obj_inbox, &obj_table, 0, &count);
1149
MAPI_RETVAL_IF(retval, retval, mem_ctx);
1151
SPropTagArray = set_SPropTagArray(mem_ctx, 0x5,
1156
PR_INTERNET_MESSAGE_ID);
1157
retval = SetColumns(&obj_table, SPropTagArray);
1158
MAPIFreeBuffer(SPropTagArray);
1159
MAPI_RETVAL_IF(retval, retval, mem_ctx);
1161
while ((retval = QueryRows(&obj_table, 0xa, TBL_ADVANCE, &rowset)) != MAPI_E_NOT_FOUND && rowset.cRows) {
1162
for (i = 0; i < rowset.cRows; i++) {
1163
mapi_object_init(&obj_message);
1164
retval = OpenMessage(&obj_store,
1165
rowset.aRow[i].lpProps[0].value.d,
1166
rowset.aRow[i].lpProps[1].value.d,
1168
if (retval == MAPI_E_SUCCESS) {
1169
SPropTagArray = set_SPropTagArray(mem_ctx, 0x1b,
1170
PR_INTERNET_MESSAGE_ID,
1171
PR_INTERNET_MESSAGE_ID_UNICODE,
1172
PR_CONVERSATION_TOPIC,
1173
PR_CONVERSATION_TOPIC_UNICODE,
1174
PR_MESSAGE_DELIVERY_TIME,
1175
PR_MSG_EDITOR_FORMAT,
1181
PR_SENT_REPRESENTING_NAME,
1182
PR_SENT_REPRESENTING_NAME_UNICODE,
1184
PR_DISPLAY_TO_UNICODE,
1186
PR_DISPLAY_CC_UNICODE,
1188
PR_DISPLAY_BCC_UNICODE,
1190
PR_TRANSPORT_MESSAGE_HEADERS,
1192
PR_SUBJECT_PREFIX_UNICODE,
1193
PR_NORMALIZED_SUBJECT,
1194
PR_NORMALIZED_SUBJECT_UNICODE,
1196
PR_SUBJECT_UNICODE);
1197
retval = GetProps(&obj_message, MAPI_UNICODE, SPropTagArray, &lpProps, &count);
1198
MAPIFreeBuffer(SPropTagArray);
1199
if (retval != MAPI_E_SUCCESS) {
1200
fprintf(stderr, "Badness getting row %d attrs\n", i);
1204
/* Build a SRow structure */
1205
aRow.ulAdrEntryPad = 0;
1206
aRow.cValues = count;
1207
aRow.lpProps = lpProps;
1209
msgid = (const char *) octool_get_propval(&aRow, PR_INTERNET_MESSAGE_ID);
1211
retval = FindProfileAttr(profile, "Message-ID", msgid);
1212
if (GetLastError() == MAPI_E_NOT_FOUND) {
1216
ok = message2mbox(mem_ctx, fp, &aRow, &obj_message, 0);
1218
printf("Message-ID: %s error, not added to %s\n", msgid, profile->profname);
1219
} else if (message_error) {
1220
printf("Message-ID: %s error, ignoring\n", msgid);
1221
fprintf(stderr, "Message-ID: %s error, ignoring message (check with OWA if you can, will retry next time)\n", msgid);
1222
} else if (opt_test) {
1223
printf("Message-ID: %s saved but not updated in %s\n", msgid, profile->profname);
1225
(mapi_profile_add_string_attr(profile->mapi_ctx, profile->profname, "Message-ID", msgid) != MAPI_E_SUCCESS) {
1226
mapi_errstr("mapi_profile_add_string_attr", GetLastError());
1228
printf("Message-ID: %s added to profile %s\n", msgid, profile->profname);
1231
printf("Message-ID: %s already in profile %s\n", msgid, profile->profname);
1234
fprintf(stderr, "%s: message with no msgid cannot be downloaded\n", profile->profname);
1236
talloc_free(lpProps);
1238
fprintf(stderr, "could not open row %d: retval=%d GetLastError=%d\n", i, retval, GetLastError());
1240
mapi_object_release(&obj_message);
1246
mapi_object_release(&obj_table);
1247
mapi_object_release(&obj_inbox);
1248
mapi_object_release(&obj_store);
1249
MAPIUninitialize(mapi_ctx);
1251
talloc_free(mem_ctx);