# HG changeset patch # User Carl Byington # Date 1235450451 28800 # Node ID 2189a6b8134e01087e93c3ad4de9abedf091950a # Parent fd429788431980ca428650c9ddcd893fea2ad0da improve character set handling - don't try to convert utf-8 to single byte for fields that were not originally unicode. if the conversion fails, leave the data in utf-8. diff -r fd4297884319 -r 2189a6b8134e ChangeLog --- a/ChangeLog Sat Feb 14 11:02:37 2009 -0800 +++ b/ChangeLog Mon Feb 23 20:40:51 2009 -0800 @@ -4,6 +4,9 @@ that are not used by normal mail clients. * improve decoding of multipart/report and message/rfc822 mime types. + * improve character set handling - don't try to convert utf-8 + to single byte for fields that were not originally unicode. + if the conversion fails, leave the data in utf-8. LibPST 0.6.27 (2009-02-07) =============================== diff -r fd4297884319 -r 2189a6b8134e regression/regression-tests.bash --- a/regression/regression-tests.bash Sat Feb 14 11:02:37 2009 -0800 +++ b/regression/regression-tests.bash Mon Feb 23 20:40:51 2009 -0800 @@ -46,24 +46,24 @@ dodii 3 test.pst dodii 4 big_mail.pst else - #dopst 1 ams.pst - #dopst 2 sample_64.pst - #dopst 3 test.pst - #dopst 4 big_mail.pst - #dopst 5 mbmg.archive.pst - #dopst 6 Single2003-read.pst - #dopst 7 Single2003-unread.pst - #dopst 8 ol2k3high.pst - #dopst 9 ol97high.pst - #dopst 10 returned_message.pst - #dopst 11 flow.pst - #dopst 12 test-html.pst - #dopst 13 test-text.pst - #dopst 14 joe.romanowski.pst - #dopst 15 hourig1.pst + dopst 1 ams.pst + dopst 2 sample_64.pst + dopst 3 test.pst + dopst 4 big_mail.pst + dopst 5 mbmg.archive.pst + dopst 6 Single2003-read.pst + dopst 7 Single2003-unread.pst + dopst 8 ol2k3high.pst + dopst 9 ol97high.pst + dopst 10 returned_message.pst + dopst 11 flow.pst + dopst 12 test-html.pst + dopst 13 test-text.pst + dopst 14 joe.romanowski.pst + dopst 15 hourig1.pst #dopst 16 hourig2.pst - dopst 17 hourig3.pst - #dopst 18 test-mac.pst + #dopst 17 hourig3.pst + dopst 18 test-mac.pst fi grep 'lost:' *err | grep -v 'lost: 0 ' diff -r fd4297884319 -r 2189a6b8134e src/Makefile.am --- a/src/Makefile.am Sat Feb 14 11:02:37 2009 -0800 +++ b/src/Makefile.am Mon Feb 23 20:40:51 2009 -0800 @@ -76,7 +76,7 @@ libstrfunc.h\ timeconv.h \ vbuf.h - libpst_la_LDFLAGS = $(NO_UNDEFINED) -version-info 1:0:0 + libpst_la_LDFLAGS = $(NO_UNDEFINED) -version-info 1:1:0 endif libpst_la_SOURCES = $(common_source) $(common_header) diff -r fd4297884319 -r 2189a6b8134e src/getidblock.c --- a/src/getidblock.c Sat Feb 14 11:02:37 2009 -0800 +++ b/src/getidblock.c Mon Feb 23 20:40:51 2009 -0800 @@ -1,19 +1,92 @@ #include "define.h" -static void usage(); +int decrypt = 0, process = 0, binary = 0; +pst_file pstfile; + + +void usage(); +void usage() +{ + printf("usage: getidblock [options] filename id\n"); + printf("\tfilename - name of the file to access\n"); + printf("\tid - ID of the block to fetch (0 to fetch all) - can begin with 0x for hex\n"); + printf("\toptions\n"); + printf("\t\t-d\tDecrypt the block before printing\n"); + printf("\t\t-p\tProcess the block before finishing.\n"); + printf("\t\t\tView the debug log for information\n"); +} + + +void dumper(uint64_t id); +void dumper(uint64_t id) +{ + char *buf = NULL; + size_t readSize; + pst_desc_ll *ptr; + + DEBUG_MAIN(("\n\n\nLooking at block index1 id %#"PRIx64"\n", id)); + + if ((readSize = pst_ff_getIDblock(&pstfile, id, &buf)) <= 0 || buf == 0) { + DIE(("Error loading block\n")); + } + + if (decrypt) + if (pst_decrypt(id, buf, readSize, (int) pstfile.encryption) != 0) { + DIE(("Error decrypting block\n")); + } + + DEBUG_MAIN(("Printing block id %#"PRIx64", size %#x\n", id, readSize)); + if (binary) { + if (fwrite(buf, 1, readSize, stdout) != 0) { + DIE(("Error occured during writing of buf to stdout\n")); + } + } else { + printf("Block id %#"PRIx64", size %#x\n", id, readSize); + pst_debug_hexdumper(stdout, buf, readSize, 0x10, 0); + } + if (buf) free(buf); + + if (process) { + DEBUG_MAIN(("Parsing block id %#"PRIx64"\n", id)); + ptr = pstfile.d_head; + while (ptr) { + if (ptr->list_index && ptr->list_index->id == id) + break; + if (ptr->desc && ptr->desc->id == id) + break; + ptr = pst_getNextDptr(ptr); + } + if (!ptr) { + ptr = (pst_desc_ll *) xmalloc(sizeof(pst_desc_ll)); + ptr->desc = pst_getID(&pstfile, id); + ptr->list_index = NULL; + } + pst_item *item = pst_parse_item(&pstfile, ptr); + if (item) pst_freeItem(item); + } +} + + +void dump_desc(pst_desc_ll *ptr); +void dump_desc(pst_desc_ll *ptr) +{ + while (ptr) { + DEBUG_MAIN(("\n\n\nLooking at block desc id %#"PRIx64"\n", ptr->id)); + if (ptr->desc && ptr->desc->id) dumper(ptr->desc->id); + if (ptr->list_index && ptr->list_index->id) dumper(ptr->list_index->id); + if (ptr->child) dump_desc(ptr->child); + ptr = ptr->next; + } +} + int main(int argc, char* const* argv) { // pass the id number to display on the command line char *fname, *sid; - pst_file pstfile; uint64_t id; - int decrypt = 0, process = 0, binary = 0, c; - char *buf = NULL; - size_t readSize; - pst_item *item; - pst_desc_ll *ptr; + int c; DEBUG_INIT("getidblock.log"); DEBUG_REGISTER_CLOSE(); @@ -58,56 +131,17 @@ if (pst_load_index(&pstfile) != 0) { DIE(("Error loading file index\n")); } - // if ((ptr = pst_getID(&pstfile, id)) == NULL) { - // DIE(("id not found [%#x]\n", id)); - // } - DEBUG_MAIN(("Loading block\n")); - - if ((readSize = pst_ff_getIDblock(&pstfile, id, &buf)) <= 0 || buf == NULL) { - // if ((readSize = pst_read_block_size(&pstfile, ptr->offset, ptr->size, &buf, 1, 1)) < ptr->size) { - DIE(("Error loading block\n")); + if (id) { + dumper(id); } - if (binary == 0) - printf("Block %#"PRIx64", size %#x[%i]\n", id, (unsigned int) readSize, (int) readSize); - - if (decrypt != 0) - if (pst_decrypt(id, buf, readSize, (int) pstfile.encryption) != 0) { - DIE(("Error decrypting block\n")); - } - - DEBUG_MAIN(("Printing block... [id %#x, size %#x]\n", id, readSize)); - if (binary == 0) { - pst_debug_hexdumper(stdout, buf, readSize, 0x10, 0); - } else { - if (fwrite(buf, 1, readSize, stdout) != 0) { - DIE(("Error occured during writing of buf to stdout\n")); + else { + pst_index_ll *ptr = pstfile.i_head; + while (ptr) { + dumper(ptr->id); + ptr = ptr->next; } - } - free(buf); - - if (process != 0) { - DEBUG_MAIN(("Parsing block...\n")); - ptr = pstfile.d_head; - while (ptr != NULL) { - if (ptr->list_index != NULL && ptr->list_index->id == id) - break; - if (ptr->desc != NULL && ptr->desc->id == id) - break; - ptr = pst_getNextDptr(ptr); - } - if (ptr == NULL) { - ptr = (pst_desc_ll *) xmalloc(sizeof(pst_desc_ll)); - ptr->desc = pst_getID(&pstfile, id); - ptr->list_index = NULL; - } - if (ptr != NULL) { - if ((item = pst_parse_item(&pstfile, ptr)) != NULL) - pst_freeItem(item); - } else { - DEBUG_MAIN(("item not found with this ID\n")); - printf("Cannot find the owning Record of this ID. Cannot parse\n"); - } + dump_desc(pstfile.d_head); } if (pst_close(&pstfile) != 0) { @@ -118,13 +152,3 @@ return 0; } -void usage() -{ - printf("usage: getidblock [options] filename id\n"); - printf("\tfilename - name of the file to access\n"); - printf("\tid - ID of the block to fetch - can begin with 0x for hex\n"); - printf("\toptions\n"); - printf("\t\t-d\tDecrypt the block before printing\n"); - printf("\t\t-p\tProcess the block before finishing.\n"); - printf("\t\t\tView the debug log for information\n"); -} diff -r fd4297884319 -r 2189a6b8134e src/libpst.c --- a/src/libpst.c Sat Feb 14 11:02:37 2009 -0800 +++ b/src/libpst.c Mon Feb 23 20:40:51 2009 -0800 @@ -125,7 +125,9 @@ 0xd4, 0xe1, 0x11, 0xd0, 0x08, 0x8b, 0x2a, 0xf2, 0xed, 0x9a, 0x64, 0x3f, 0xc1, 0x6c, 0xf9, 0xec }; -// for "strong" encryption, we have the two additional tables +// for "strong" encryption, we have the two additional tables, +// which (with the previous table) are used as the keys in an +// Enigma 3 rotor cipher static unsigned char comp_high1 [] = { 0x41, 0x36, 0x13, 0x62, 0xa8, 0x21, 0x6e, 0xbb, 0xf4, 0x16, 0xcc, 0x04, 0x7f, 0x64, 0xe8, 0x5d, 0x1e, 0xf2, 0xcb, 0x2a, 0x74, 0xc5, 0x5e, 0x35, 0xd2, 0x95, 0x47, 0x9e, 0x96, 0x2d, 0x9a, 0x88, @@ -519,7 +521,7 @@ } if (p->list_index) { - id2_head = pst_build_id2(pf, p->list_index, NULL); + id2_head = pst_build_id2(pf, p->list_index); pst_printID2ptr(id2_head); } else { DEBUG_WARN(("Have not been able to fetch any id2 values for item 0x61. Brace yourself!\n")); @@ -1047,10 +1049,8 @@ } if (d_ptr->list_index) { - id2_head = pst_build_id2(pf, d_ptr->list_index, NULL); + id2_head = pst_build_id2(pf, d_ptr->list_index); (void)pst_printID2ptr(id2_head); - } else { - DEBUG_WARN(("Have not been able to fetch any id2 values for this item. Brace yourself!\n")); } list = pst_parse_block(pf, d_ptr->desc->id, id2_head, NULL); @@ -1073,16 +1073,48 @@ return NULL; } if (list) pst_free_list(list); - list = NULL; //pst_process will free the items in the list + list = NULL; + + if ((id_ptr = pst_getID2(id2_head, (uint64_t)0x692))) { + // DSN/MDN reports? + DEBUG_EMAIL(("DSN/MDN processing \n")); + if ((list = pst_parse_block(pf, id_ptr->id, id2_head, NULL)) == NULL) { + DEBUG_WARN(("ERROR error processing main DSN/MDN record\n")); + if (item) pst_freeItem(item); + if (list) pst_free_list(list); + if (id2_head) pst_free_id2(id2_head); + DEBUG_RET(); + return item; + } + else { + for (x=0; x < list->count_array; x++) { + attach = (pst_item_attach*) xmalloc(sizeof(pst_item_attach)); + memset(attach, 0, sizeof(pst_item_attach)); + attach->next = item->attach; + item->attach = attach; + } + + if (pst_process(list, item, item->attach)) { + DEBUG_WARN(("ERROR pst_process() failed with attachments\n")); + if (item) pst_freeItem(item); + if (list) pst_free_list(list); + if (id2_head) pst_free_id2(id2_head); + DEBUG_RET(); + return NULL; + } + if (list) pst_free_list(list); + list = NULL; + } + } if ((id_ptr = pst_getID2(id2_head, (uint64_t)0x671))) { // should not have any existing attachments anyway - while (item->attach) { - DEBUG_EMAIL(("throw away existing attachment\n")); - attach = item->attach->next; - free(item->attach); - item->attach = attach; - } + //while (item->attach) { + // DEBUG_EMAIL(("throw away existing attachment\n")); + // attach = item->attach->next; + // free(item->attach); + // item->attach = attach; + //} DEBUG_EMAIL(("ATTACHMENT processing attachment\n")); if ((list = pst_parse_block(pf, id_ptr->id, id2_head, NULL)) == NULL) { @@ -1126,6 +1158,9 @@ attach = attach->next; continue; } + if (list->count_array > 1) { + DEBUG_WARN(("ERROR probably fatal, list count array will overrun attach structure.\n")); + } if (pst_process(list, item, attach)) { DEBUG_WARN(("ERROR pst_process() failed with an attachment\n")); if (list) pst_free_list(list); @@ -1146,6 +1181,7 @@ } } else { DEBUG_WARN(("ERROR cannot locate id2 value %#"PRIx64"\n", attach->id2_val)); + attach->id2_val = 0; // suppress this missing attachment } attach = attach->next; } @@ -1684,27 +1720,22 @@ #define NULL_CHECK(x) { if (!x) { DEBUG_EMAIL(("NULL_CHECK: Null Found\n")); break;} } -#define MOVE_NEXT(targ) { \ - if (next){\ - if (!targ) {\ - DEBUG_EMAIL(("MOVE_NEXT: Target is NULL. Will stop processing this option\n"));\ - break;\ - }\ - targ = targ->next;\ - if (!targ) {\ - DEBUG_EMAIL(("MOVE_NEXT: Target is NULL after next. Will stop processing this option\n"));\ - break;\ - }\ - next=0;\ - }\ -} - - -int pst_process(pst_num_array *list , pst_item *item, pst_item_attach *attach) { - int32_t x, t; - int next = 0; - pst_item_extra_field *ef; - + +/** + * process the list of items produced from parse_block() + * + * @param list pointer to the linked list of things from parse_block() + * @param item pointer to the item to be updated from the list. + * this item may be an email, contact or other sort of item. + * the type of this item is generally set by the things + * from the list. + * @param attach pointer to the linked list of attachment records. If + * this is non-null, the length of the this attachment list + * must be at least as large as the length of the list. + * + * @return 0 for ok, -1 for error. + */ +int pst_process(pst_num_array *list, pst_item *item, pst_item_attach *attach) { DEBUG_ENT("pst_process"); if (!item) { DEBUG_EMAIL(("item cannot be NULL.\n")); @@ -1713,8 +1744,10 @@ } while (list) { - x = 0; + int32_t x = 0; while (x < list->count_item) { + int32_t t; + pst_item_extra_field *ef; // check here to see if the id is one that is mapped. DEBUG_EMAIL(("#%d - id: %#x type: %#x length: %#x\n", x, list->items[x]->id, list->items[x]->type, list->items[x]->size)); @@ -1771,13 +1804,14 @@ // 0 - Low // 1 - Normal // 2 - High - DEBUG_EMAIL(("Importance Level - ")); MALLOC_EMAIL(item); memcpy(&(item->email->importance), list->items[x]->data, sizeof(item->email->importance)); LE32_CPU(item->email->importance); t = item->email->importance; - DEBUG_EMAIL(("%s [%i]\n", ((int)t==0?"Low":((int)t==1?"Normal":"High")), t)); + DEBUG_EMAIL(("%s [%i]\n", ((int)t==0?"Low": + ((int)t==1?"Normal": + "High")), t)); break; case 0x001A: // PR_MESSAGE_CLASS Ascii type of messages - NOT FOLDERS // must be case insensitive @@ -1862,22 +1896,32 @@ memcpy(&(item->email->orig_sensitivity), list->items[x]->data, sizeof(item->email->orig_sensitivity)); LE32_CPU(item->email->orig_sensitivity); t = item->email->orig_sensitivity; - DEBUG_EMAIL(("%s [%i]\n", ((int)t==0?"None":((int)t==1?"Personal": - ((int)t==2?"Private":"Company Confidential"))), t)); + DEBUG_EMAIL(("%s [%i]\n", ((int)t==0?"None": + ((int)t==1?"Personal": + ((int)t==2?"Private": + "Company Confidential"))), t)); + break; + case 0x0032: // PR_REPORT_TIME + DEBUG_EMAIL(("Report time - ")); + MALLOC_EMAIL(item); + LIST_COPY_TIME(item->email->report_time); + DEBUG_EMAIL(("%s", fileTimeToAscii(item->email->report_time))); break; case 0x0036: // PR_SENSITIVITY // sender's opinion of the sensitivity of an email // 0 - None // 1 - Personal // 2 - Private - // 3 - Company Confidiential + // 3 - Company Confidential DEBUG_EMAIL(("Sensitivity - ")); MALLOC_EMAIL(item); memcpy(&(item->email->sensitivity), list->items[x]->data, sizeof(item->email->sensitivity)); LE32_CPU(item->email->sensitivity); t = item->email->sensitivity; - DEBUG_EMAIL(("%s [%i]\n", ((int)t==0?"None":((int)t==1?"Personal": - ((int)t==2?"Private":"Company Confidential"))), t)); + DEBUG_EMAIL(("%s [%i]\n", ((int)t==0?"None": + ((int)t==1?"Personal": + ((int)t==2?"Private": + "Company Confidential"))), t)); break; case 0x0037: // PR_SUBJECT raw subject DEBUG_EMAIL(("Raw Subject - ")); @@ -2086,6 +2130,28 @@ LIST_COPY(item->email->header, (char*)); DEBUG_EMAIL(("%s\n", item->email->header)); break; + case 0x0C04: // PR_NDR_REASON_CODE + MALLOC_EMAIL(item); + memcpy(&(item->email->ndr_reason_code), list->items[x]->data, sizeof(item->email->ndr_reason_code)); + LE32_CPU(item->email->ndr_reason_code); + t = item->email->ndr_reason_code; + DEBUG_EMAIL(("NDR reason code - [%i]\n", (int)t)); + break; + case 0x0C05: // PR_NDR_DIAG_CODE + MALLOC_EMAIL(item); + memcpy(&(item->email->ndr_diag_code), list->items[x]->data, sizeof(item->email->ndr_diag_code)); + LE32_CPU(item->email->ndr_diag_code); + t = item->email->ndr_diag_code; + DEBUG_EMAIL(("NDR diag code - [%i]\n", (int)t)); + break; + case 0x0C06: // PR_NON_RECEIPT_NOTIFICATION_REQUESTED + DEBUG_EMAIL(("Non-Receipt Notification Requested - (ignored) - ")); + if (*(int16_t*)list->items[x]->data) { + DEBUG_EMAIL(("True\n")); + } else { + DEBUG_EMAIL(("False\n")); + } + break; case 0x0C17: // PR_REPLY_REQUESTED DEBUG_EMAIL(("Reply Requested - ")); MALLOC_EMAIL(item); @@ -2103,6 +2169,12 @@ case 0x0C1A: // PR_SENDER_NAME Name of Sender Structure 2 DEBUG_EMAIL(("Name of Sender Structure 2 -- NOT HANDLED\n")); break; + case 0x0C1B: // PR_SUPPLEMENTARY_INFO + DEBUG_EMAIL(("Supplementary info - ")); + MALLOC_EMAIL(item); + LIST_COPY(item->email->supplementary_info, (char*)); + DEBUG_EMAIL(("%s\n", item->email->supplementary_info)); + break; case 0x0C1D: // PR_SENDER_SEARCH_KEY Name of Sender Address 2 DEBUG_EMAIL(("Name of Sender Address 2 (Sender search key) - ")); MALLOC_EMAIL(item); @@ -2121,6 +2193,13 @@ LIST_COPY(item->email->sender2_address, (char*)); DEBUG_EMAIL(("%s\n", item->email->sender2_address)); break; + case 0x0C20: // PR_NDR_STATUS_CODE + MALLOC_EMAIL(item); + memcpy(&(item->email->ndr_status_code), list->items[x]->data, sizeof(item->email->ndr_status_code)); + LE32_CPU(item->email->ndr_status_code); + t = item->email->ndr_status_code; + DEBUG_EMAIL(("NDR status code - [%i]\n", (int)t)); + break; case 0x0E01: // PR_DELETE_AFTER_SUBMIT // I am not too sure how this works DEBUG_EMAIL(("Delete after submit - ")); @@ -2206,7 +2285,6 @@ case 0x0E20: // PR_ATTACH_SIZE binary Attachment data in record DEBUG_EMAIL(("Attachment Size - ")); NULL_CHECK(attach); - MOVE_NEXT(attach); t = (*(int32_t*)list->items[x]->data); LE32_CPU(t); attach->size = (size_t)t; @@ -2219,11 +2297,20 @@ DEBUG_EMAIL_HEXPRINT(item->record_key, item->record_key_size); DEBUG_EMAIL(("\n")); break; - case 0x1000: // PR_BODY Plain Text body - DEBUG_EMAIL(("Plain Text body - ")); + case 0x1000: // PR_BODY MALLOC_EMAIL(item); LIST_COPY(item->email->body, (char*)); - DEBUG_EMAIL(("%s\n", item->email->body)); + item->email->body_was_unicode = (list->items[x]->type == 0x1f) ? 1 : 0; + DEBUG_EMAIL(("Plain Text body %s - \n%s\n", (item->email->body_was_unicode) ? "unicode" : "sbcs", + item->email->body)); + break; + case 0x1001: // PR_REPORT_TEXT + DEBUG_EMAIL(("Report Text - ")); + MALLOC_EMAIL(item); + LIST_COPY(item->email->report_text, (char*)); + item->email->report_was_unicode = (list->items[x]->type == 0x1f) ? 1 : 0; + DEBUG_EMAIL(("Report Text %s - \n%s\n", (item->email->report_was_unicode) ? "unicode" : "sbcs", + item->email->report_text)); break; case 0x1006: // PR_RTF_SYNC_BODY_CRC DEBUG_EMAIL(("RTF Sync Body CRC - ")); @@ -2271,10 +2358,11 @@ DEBUG_EMAIL(("%i\n", item->email->rtf_ws_trailing_count)); break; case 0x1013: // HTML body - DEBUG_EMAIL(("HTML body - ")); MALLOC_EMAIL(item); LIST_COPY(item->email->htmlbody, (char*)); - DEBUG_EMAIL(("%s\n", item->email->htmlbody)); + item->email->htmlbody_was_unicode = (list->items[x]->type == 0x1f) ? 1 : 0; + DEBUG_EMAIL(("HTML body %s - \n%s\n", (item->email->htmlbody_was_unicode) ? "unicode" : "sbcs", + item->email->htmlbody)); break; case 0x1035: // Message ID DEBUG_EMAIL(("Message ID - ")); @@ -2452,7 +2540,6 @@ case 0x3701: // PR_ATTACH_DATA_OBJ binary data of attachment DEBUG_EMAIL(("Binary Data [Size %i] - ", list->items[x]->size)); NULL_CHECK(attach); - MOVE_NEXT(attach); if (!list->items[x]->data) { //special case attach->id2_val = list->items[x]->type; DEBUG_EMAIL(("Seen a Reference. The data hasn't been loaded yet. [%#"PRIx64"][%#x]\n", @@ -2466,7 +2553,6 @@ case 0x3704: // PR_ATTACH_FILENAME Attachment filename (8.3) DEBUG_EMAIL(("Attachment Filename - ")); NULL_CHECK(attach); - MOVE_NEXT(attach); LIST_COPY(attach->filename1, (char*)); DEBUG_EMAIL(("%s\n", attach->filename1)); break; @@ -2480,7 +2566,6 @@ // 6 - OLE DEBUG_EMAIL(("Attachment method - ")); NULL_CHECK(attach); - MOVE_NEXT(attach); memcpy(&(attach->method), list->items[x]->data, sizeof(attach->method)); LE32_CPU(attach->method); t = attach->method; @@ -2494,7 +2579,6 @@ case 0x3707: // PR_ATTACH_LONG_FILENAME Attachment filename (long?) DEBUG_EMAIL(("Attachment Filename long - ")); NULL_CHECK(attach); - MOVE_NEXT(attach); LIST_COPY(attach->filename2, (char*)); DEBUG_EMAIL(("%s\n", attach->filename2)); break; @@ -2502,7 +2586,6 @@ // position in characters that the attachment appears in the plain text body DEBUG_EMAIL(("Attachment Position - ")); NULL_CHECK(attach); - MOVE_NEXT(attach); memcpy(&(attach->position), list->items[x]->data, sizeof(attach->position)); LE32_CPU(attach->position); DEBUG_EMAIL(("%i [%#x]\n", attach->position)); @@ -2510,7 +2593,6 @@ case 0x370E: // PR_ATTACH_MIME_TAG Mime type of encoding DEBUG_EMAIL(("Attachment mime encoding - ")); NULL_CHECK(attach); - MOVE_NEXT(attach); LIST_COPY(attach->mimetype, (char*)); DEBUG_EMAIL(("%s\n", attach->mimetype)); break; @@ -2518,7 +2600,6 @@ // sequence number for mime parts. Includes body DEBUG_EMAIL(("Attachment Mime Sequence - ")); NULL_CHECK(attach); - MOVE_NEXT(attach); memcpy(&(attach->sequence), list->items[x]->data, sizeof(attach->sequence)); LE32_CPU(attach->sequence); DEBUG_EMAIL(("%i\n", attach->sequence)); @@ -3013,6 +3094,20 @@ LIST_COPY(item->contact->other_po_box, (char*)); DEBUG_EMAIL(("%s\n", item->contact->other_po_box)); break; + case 0x3FDE: // PR_INTERNET_CPID + MALLOC_EMAIL(item); + memcpy(&(item->email->internet_cpid), list->items[x]->data, sizeof(item->email->internet_cpid)); + LE32_CPU(item->email->internet_cpid); + t = item->email->internet_cpid; + DEBUG_EMAIL(("Internet code page %i\n", (int)t)); + break; + case 0x3FFD: // PR_MESSAGE_CODEPAGE + MALLOC_EMAIL(item); + memcpy(&(item->email->message_codepage), list->items[x]->data, sizeof(item->email->message_codepage)); + LE32_CPU(item->email->message_codepage); + t = item->email->message_codepage; + DEBUG_EMAIL(("Message code page %i\n", (int)t)); + break; case 0x65E3: // Entry ID? DEBUG_EMAIL(("Entry ID - ")); item->record_key = (char*) xmalloc(16+1); @@ -3025,7 +3120,6 @@ DEBUG_EMAIL(("Attachment ID2 value - ")); if (attach) { uint32_t tempid; - MOVE_NEXT(attach); memcpy(&(tempid), list->items[x]->data, sizeof(tempid)); LE32_CPU(tempid); attach->id2_val = tempid; @@ -3523,9 +3617,8 @@ } x++; } - x = 0; list = list->next; - next = 1; + if (attach) attach = attach->next; } DEBUG_RET(); return 0; @@ -3558,8 +3651,9 @@ pst_index2_ll *t; DEBUG_ENT("pst_free_id2"); while (head) { + if (head->child) pst_free_id2(head->child); t = head->next; - free (head); + free(head); head = t; } DEBUG_RET(); @@ -3615,7 +3709,7 @@ } -pst_index2_ll * pst_build_id2(pst_file *pf, pst_index_ll* list, pst_index2_ll* head_ptr) { +pst_index2_ll * pst_build_id2(pst_file *pf, pst_index_ll* list) { pst_block_header block_head; pst_index2_ll *head = NULL, *tail = NULL; uint16_t x = 0; @@ -3626,10 +3720,6 @@ pst_index2_ll *i2_ptr = NULL; DEBUG_ENT("pst_build_id2"); - if (head_ptr) { - head = head_ptr; - while (head_ptr) head_ptr = (tail = head_ptr)->next; - } if (pst_read_block_size(pf, list->offset, list->size, &buf) < list->size) { //an error occured in block read WARN(("block read error occured. offset = %#"PRIx64", size = %#"PRIx64"\n", list->offset, list->size)); @@ -3656,41 +3746,28 @@ b_ptr = buf + ((pf->do_read64) ? 0x08 : 0x04); while (x < block_head.count) { b_ptr += pst_decode_assoc(pf, &id2_rec, b_ptr); - DEBUG_INDEX(("\tid2 = %#x, id = %#"PRIx64", table2 = %#"PRIx64"\n", id2_rec.id2, id2_rec.id, id2_rec.table2)); + DEBUG_INDEX(("id2 = %#x, id = %#"PRIx64", table2 = %#"PRIx64"\n", id2_rec.id2, id2_rec.id, id2_rec.table2)); if ((i_ptr = pst_getID(pf, id2_rec.id)) == NULL) { - DEBUG_WARN(("\t\t%#"PRIx64" - Not Found\n", id2_rec.id)); + DEBUG_WARN(("%#"PRIx64" - Not Found\n", id2_rec.id)); } else { - DEBUG_INDEX(("\t\t%#"PRIx64" - Offset %#"PRIx64", u1 %#"PRIx64", Size %"PRIi64"(%#"PRIx64")\n", + DEBUG_INDEX(("%#"PRIx64" - Offset %#"PRIx64", u1 %#"PRIx64", Size %"PRIi64"(%#"PRIx64")\n", i_ptr->id, i_ptr->offset, i_ptr->u1, i_ptr->size, i_ptr->size)); - // add it to the linked list + // add it to the tree i2_ptr = (pst_index2_ll*) xmalloc(sizeof(pst_index2_ll)); - i2_ptr->id2 = id2_rec.id2; - i2_ptr->id = i_ptr; - i2_ptr->next = NULL; + i2_ptr->id2 = id2_rec.id2; + i2_ptr->id = i_ptr; + i2_ptr->child = NULL; + i2_ptr->next = NULL; if (!head) head = i2_ptr; if (tail) tail->next = i2_ptr; tail = i2_ptr; - if (id2_rec.table2 != 0) { + if (id2_rec.table2) { if ((i_ptr = pst_getID(pf, id2_rec.table2)) == NULL) { - DEBUG_WARN(("\tTable2 [%#x] not found\n", id2_rec.table2)); + DEBUG_WARN(("Table2 [%#x] not found\n", id2_rec.table2)); } else { - DEBUG_INDEX(("\tGoing deeper for table2 [%#x]\n", id2_rec.table2)); - if ((i2_ptr = pst_build_id2(pf, i_ptr, head))) { - // DEBUG_INDEX(("pst_build_id2(): \t\tAdding new list onto end of current\n")); - // if (!head) - // head = i2_ptr; - // if (tail) - // tail->next = i2_ptr; - // while (i2_ptr->next) - // i2_ptr = i2_ptr->next; - // tail = i2_ptr; - } - // need to re-establish tail - DEBUG_INDEX(("Returned from depth\n")); - if (tail) { - while (tail->next) tail = tail->next; - } + DEBUG_INDEX(("Going deeper for table2 [%#x]\n", id2_rec.table2)); + i2_ptr->child = pst_build_id2(pf, i_ptr); } } } @@ -3749,6 +3826,9 @@ if (item->email->subject) SAFE_FREE(item->email->subject->subj); SAFE_FREE(item->email->subject); + SAFE_FREE(item->email->report_text); + SAFE_FREE(item->email->report_time); + SAFE_FREE(item->email->supplementary_info); free(item->email); } if (item->folder) { @@ -4012,10 +4092,19 @@ } -pst_index_ll * pst_getID2(pst_index2_ll *ptr, uint64_t id) { +pst_index_ll *pst_getID2(pst_index2_ll *head, uint64_t id) { DEBUG_ENT("pst_getID2"); - DEBUG_INDEX(("Head = %p id = %#"PRIx64"\n", ptr, id)); - while (ptr && (ptr->id2 != id)) { + DEBUG_INDEX(("looking for id = %#"PRIx64"\n", id)); + pst_index2_ll *ptr = head; + while (ptr) { + if (ptr->id2 == id) break; + if (ptr->child) { + pst_index_ll *rc = pst_getID2(ptr->child, id); + if (rc) { + DEBUG_RET(); + return rc; + } + } ptr = ptr->next; } if (ptr) { @@ -4024,7 +4113,7 @@ DEBUG_RET(); return ptr->id; } - DEBUG_INDEX(("ERROR Not Found\n")); + //DEBUG_INDEX(("ERROR Not Found\n")); DEBUG_RET(); return NULL; } @@ -4087,6 +4176,7 @@ DEBUG_ENT("pst_printID2ptr"); while (ptr) { DEBUG_INDEX(("%#"PRIx64" id=%#"PRIx64"\n", ptr->id2, (ptr->id ? ptr->id->id : (uint64_t)0))); + if (ptr->child) pst_printID2ptr(ptr->child); ptr = ptr->next; } DEBUG_RET(); diff -r fd4297884319 -r 2189a6b8134e src/libpst.h --- a/src/libpst.h Sat Feb 14 11:02:37 2009 -0800 +++ b/src/libpst.h Mon Feb 23 20:40:51 2009 -0800 @@ -140,28 +140,29 @@ uint64_t offset; uint64_t size; int64_t u1; - struct pst_index_tree * next; + struct pst_index_tree *next; } pst_index_ll; typedef struct pst_index2_tree { uint64_t id2; pst_index_ll *id; - struct pst_index2_tree * next; + struct pst_index2_tree *child; + struct pst_index2_tree *next; } pst_index2_ll; typedef struct pst_desc_tree { uint64_t id; uint64_t parent_id; - pst_index_ll * list_index; - pst_index_ll * desc; + pst_index_ll *list_index; + pst_index_ll *desc; int32_t no_child; - struct pst_desc_tree * prev; - struct pst_desc_tree * next; - struct pst_desc_tree * parent; - struct pst_desc_tree * child; - struct pst_desc_tree * child_tail; + struct pst_desc_tree *prev; + struct pst_desc_tree *next; + struct pst_desc_tree *parent; + struct pst_desc_tree *child; + struct pst_desc_tree *child_tail; } pst_desc_ll; @@ -173,67 +174,79 @@ typedef struct pst_item_email { - FILETIME *arrival_date; - int autoforward; // 1 = true, 0 = not set, -1 = false - char *body; - char *body_charset; // null if not specified - char *cc_address; - char *bcc_address; - char *common_name; - int32_t conv_index; - int conversion_prohib; // 1 = true, 0 = false - int delete_after_submit; // 1 = true, 0 = false - int delivery_report; // 1 = true, 0 = false - char *encrypted_body; - size_t encrypted_body_size; - char *encrypted_htmlbody; - size_t encrypted_htmlbody_size; - int32_t flag; - char *header; - char *htmlbody; - int32_t importance; - char *in_reply_to; - int message_cc_me; // 1 = true, 0 = false - int message_recip_me; // 1 = true, 0 = false - int message_to_me; // 1 = true, 0 = false - char *messageid; - int32_t orig_sensitivity; - char *original_bcc; - char *original_cc; - char *original_to; - char *outlook_recipient; - char *outlook_recipient_name; - char *outlook_recipient2; - char *outlook_sender; - char *outlook_sender_name; - char *outlook_sender2; - int32_t priority; - char *proc_subject; - int read_receipt; // 1 = true, 0 = false - char *recip_access; - char *recip_address; - char *recip2_access; - char *recip2_address; - int reply_requested; // 1 = true, 0 = false - char *reply_to; - char *return_path_address; - int32_t rtf_body_char_count; - int32_t rtf_body_crc; - char *rtf_body_tag; - char *rtf_compressed; - uint32_t rtf_compressed_size; - int rtf_in_sync; // 1 = true, 0 = doesn't exist, -1 = false - int32_t rtf_ws_prefix_count; - int32_t rtf_ws_trailing_count; - char *sender_access; - char *sender_address; - char *sender2_access; - char *sender2_address; - int32_t sensitivity; - FILETIME *sent_date; - pst_entryid *sentmail_folder; - char *sentto_address; + FILETIME *arrival_date; + int autoforward; // 1 = true, 0 = not set, -1 = false + char *body; + int32_t body_was_unicode; // 1 = true, 0 = false + char *body_charset; // null if not specified + char *cc_address; + char *bcc_address; + char *common_name; + int32_t conv_index; + int conversion_prohib; // 1 = true, 0 = false + int delete_after_submit; // 1 = true, 0 = false + int delivery_report; // 1 = true, 0 = false + char *encrypted_body; + size_t encrypted_body_size; + char *encrypted_htmlbody; + size_t encrypted_htmlbody_size; + int32_t flag; + char *header; + char *htmlbody; + int32_t htmlbody_was_unicode; // 1 = true, 0 = false + int32_t importance; + char *in_reply_to; + int message_cc_me; // 1 = true, 0 = false + int message_recip_me; // 1 = true, 0 = false + int message_to_me; // 1 = true, 0 = false + char *messageid; + int32_t orig_sensitivity; + char *original_bcc; + char *original_cc; + char *original_to; + char *outlook_recipient; + char *outlook_recipient_name; + char *outlook_recipient2; + char *outlook_sender; + char *outlook_sender_name; + char *outlook_sender2; + int32_t priority; + char *proc_subject; + int read_receipt; // 1 = true, 0 = false + char *recip_access; + char *recip_address; + char *recip2_access; + char *recip2_address; + int reply_requested; // 1 = true, 0 = false + char *reply_to; + char *return_path_address; + int32_t rtf_body_char_count; + int32_t rtf_body_crc; + char *rtf_body_tag; + char *rtf_compressed; + uint32_t rtf_compressed_size; + int rtf_in_sync; // 1 = true, 0 = doesn't exist, -1 = false + int32_t rtf_ws_prefix_count; + int32_t rtf_ws_trailing_count; + char *sender_access; + char *sender_address; + char *sender2_access; + char *sender2_address; + int32_t sensitivity; + int32_t internet_cpid; + int32_t message_codepage; + FILETIME *sent_date; + pst_entryid *sentmail_folder; + char *sentto_address; pst_item_email_subject *subject; + // delivery report fields + char *report_text; + int32_t report_was_unicode; + FILETIME *report_time; + int32_t ndr_reason_code; + int32_t ndr_diag_code; + char *supplementary_info; + int32_t ndr_status_code; } pst_item_email; @@ -457,18 +470,16 @@ pst_x_attrib_ll *x_head; pst_block_recorder *block_head; - //set this to 0 to read 32-bit pst files (pre Outlook 2003) - //set this to 1 to read 64-bit pst files (Outlook 2003 and later) - int do_read64; - + int do_read64; // 0 is 32-bit pst file, pre Outlook 2003; + // 1 is 64-bit pst file, Outlook 2003 and later uint64_t index1; uint64_t index1_back; uint64_t index2; uint64_t index2_back; - FILE * fp; // file pointer to opened PST file - uint64_t size; // pst file size - unsigned char encryption; // pst encryption setting - unsigned char ind_type; // pst index type + FILE * fp; // file pointer to opened PST file + uint64_t size; // pst file size + unsigned char encryption; // pst encryption setting + unsigned char ind_type; // pst index type } pst_file; @@ -548,7 +559,7 @@ void pst_free_xattrib(pst_x_attrib_ll *x); int pst_getBlockOffsetPointer(pst_file *pf, pst_index2_ll *i2_head, pst_subblocks *subblocks, uint32_t offset, pst_block_offset_pointer *p); int pst_getBlockOffset(char *buf, size_t read_size, uint32_t i_offset, uint32_t offset, pst_block_offset *p); -pst_index2_ll* pst_build_id2(pst_file *pf, pst_index_ll* list, pst_index2_ll* head_ptr); +pst_index2_ll* pst_build_id2(pst_file *pf, pst_index_ll* list); pst_index_ll* pst_getID(pst_file* pf, uint64_t id); pst_index_ll* pst_getID2(pst_index2_ll * ptr, uint64_t id); pst_desc_ll* pst_getDptr(pst_file *pf, uint64_t id); diff -r fd4297884319 -r 2189a6b8134e src/readpst.c --- a/src/readpst.c Sat Feb 14 11:02:37 2009 -0800 +++ b/src/readpst.c Mon Feb 23 20:40:51 2009 -0800 @@ -51,7 +51,7 @@ int test_base64(char *body); void find_html_charset(char *html, char *charset, size_t charsetlen); void find_rfc822_headers(char** extra_mime_headers); -void write_body_part(FILE* f_output, char *body, char *mime, char *charset, char *boundary); +void write_body_part(FILE* f_output, char *body, int32_t body_was_unicode, char *mime, char *charset, char *boundary, pst_file* pst); void write_normal_email(FILE* f_output, char f_name[], pst_item* item, int mode, int mode_MH, pst_file* pst, int save_rtf, char** extra_mime_headers); void write_vcard(FILE* f_output, pst_item_contact* contact, char comment[]); void write_appointment(FILE* f_output, pst_item_appointment* appointment, @@ -136,13 +136,12 @@ ff.skip_count++; } else { - DEBUG_MAIN(("main: Desc Email ID %#x [d_ptr->id = %#x]\n", d_ptr->desc->id, d_ptr->id)); + DEBUG_MAIN(("main: Desc Email ID %#"PRIx64" [d_ptr->id = %#"PRIx64"]\n", d_ptr->desc->id, d_ptr->id)); item = pst_parse_item(&pstfile, d_ptr); DEBUG_MAIN(("main: About to process item\n")); if (item && item->email && item->email->subject && item->email->subject->subj) { - DEBUG_EMAIL(("item->email->subject = %p\n", item->email->subject)); - DEBUG_EMAIL(("item->email->subject->subj = %p\n", item->email->subject->subj)); + DEBUG_EMAIL(("item->email->subject->subj = %s\n", item->email->subject->subj)); } if (item) { if (item->folder && d_ptr->child && (deleted_mode == DMODE_INCLUDE || strcasecmp(item->file_as, "Deleted Items"))) { @@ -766,8 +765,7 @@ char *attach_filename; char *enc = NULL; // base64 encoded attachment DEBUG_ENT("write_inline_attachment"); - DEBUG_EMAIL(("Attachment Size is %i\n", attach->size)); - DEBUG_EMAIL(("Attachment Pointer is %p\n", attach->data)); + DEBUG_EMAIL(("Attachment Size is %i, pointer %p, id %d\n", attach->size, attach->data, attach->id_val)); if (attach->data) { enc = base64_encode (attach->data, attach->size); if (!enc) { @@ -776,6 +774,15 @@ return; } } + else { + // make sure we can fetch data from the id + pst_index_ll *ptr = pst_getID(pst, attach->id_val); + if (!ptr) { + DEBUG_WARN(("Couldn't find ID pointer. Cannot save attachment to file\n")); + DEBUG_RET(); + return; + } + } fprintf(f_output, "\n--%s\n", boundary); if (!attach->mimetype) { @@ -964,7 +971,7 @@ } } } - DEBUG_EMAIL(("skipping to next block after\n%s\n", headers)); + //DEBUG_EMAIL(("skipping to next block after\n%s\n", headers)); headers = temp+2; // skip to next chunk of headers } *extra_mime_headers = headers; @@ -973,20 +980,23 @@ } -void write_body_part(FILE* f_output, char *body, char *mime, char *charset, char *boundary) +void write_body_part(FILE* f_output, char *body, int32_t body_was_unicode, char *mime, char *charset, char *boundary, pst_file* pst) { char *needfree = NULL; DEBUG_ENT("write_body_part"); - if (strcasecmp("utf-8", charset)) { - // try to convert to the specified charset since it is not utf-8 + if (body_was_unicode && (strcasecmp("utf-8", charset))) { + // try to convert to the specified charset since the target + // is not utf-8, and the data came from a unicode (utf16) field + // and is now in utf-8. size_t rc; DEBUG_EMAIL(("Convert %s utf-8 to %s\n", mime, charset)); vbuf *newer = vballoc(2); rc = vb_utf8to8bit(newer, body, strlen(body) + 1, charset); if (rc == (size_t)-1) { - // unable to convert, maybe it is already in that character set + // unable to convert, change the charset to utf8 free(newer->b); DEBUG_EMAIL(("Failed to convert %s utf-8 to %s\n", mime, charset)); + charset = "utf-8"; } else { needfree = body = newer->b; @@ -1015,6 +1025,38 @@ } +const char* codepage(int cp) { + static char buffer[20]; + switch (cp) { + case 932 : return "iso-2022-jp"; + case 936 : return "gb2313"; + case 950 : return "big5"; + case 20127 : return "us-ascii"; + case 20269 : return "iso-6937"; + case 20865 : return "iso-8859-15"; + case 20866 : return "koi8-r"; + case 21866 : return "koi8-u"; + case 28591 : return "iso-8859-1"; + case 28592 : return "iso-8859-2"; + case 28595 : return "iso-8859-5"; + case 28596 : return "iso-8859-6"; + case 28597 : return "iso-8859-7"; + case 28598 : return "iso-8859-8"; + case 28599 : return "iso-8859-9"; + case 50220 : return "iso-2022-jp"; + case 50221 : return "csiso2022jp"; + case 51932 : return "euc-jp"; + case 51949 : return "euc-kr"; + case 65000 : return "utf-7"; + case 65001 : return "utf-8"; + default : + snprintf(buffer, sizeof(buffer), "cp%d", cp); + return buffer; + } + return NULL; +} + + void write_normal_email(FILE* f_output, char f_name[], pst_item* item, int mode, int mode_MH, pst_file* pst, int save_rtf, char** extra_mime_headers) { char boundary[60]; @@ -1032,7 +1074,11 @@ DEBUG_ENT("write_normal_email"); // setup default body character set and report type - snprintf(body_charset, sizeof(body_charset), "%s", (item->email->body_charset) ? item->email->body_charset : "utf-8"); + snprintf(body_charset, sizeof(body_charset), "%s", + (item->email->body_charset) ? item->email->body_charset : + (item->email->message_codepage) ? codepage(item->email->message_codepage) : + (item->email->internet_cpid) ? codepage(item->email->internet_cpid) : + "utf-8"); body_report[0] = '\0'; // setup default sender @@ -1123,8 +1169,11 @@ } if (mode != MODE_SEPARATE) { - // most modes need this separator line - fprintf(f_output, "From %s %s\n", sender, c_time); + // most modes need this separator line. + // procmail produces this separator without the quotes around the + // sender email address, but apparently some Mac email client needs + // those quotes, and they don't seem to cause problems for anyone else. + fprintf(f_output, "From \"%s\" %s\n", sender, c_time); } // print the supplied email headers @@ -1198,12 +1247,17 @@ // now dump the body parts if (item->email->body) { - write_body_part(f_output, item->email->body, "text/plain", body_charset, boundary); + write_body_part(f_output, item->email->body, item->email->body_was_unicode, "text/plain", body_charset, boundary, pst); + } + + if ((item->email->report_text) && (body_report[0] != '\0')) { + write_body_part(f_output, item->email->report_text, item->email->report_was_unicode, "text/plain", body_charset, boundary, pst); + fprintf(f_output, "\n"); } if (item->email->htmlbody) { find_html_charset(item->email->htmlbody, body_charset, sizeof(body_charset)); - write_body_part(f_output, item->email->htmlbody, "text/html", body_charset, boundary); + write_body_part(f_output, item->email->htmlbody, item->email->htmlbody_was_unicode, "text/html", body_charset, boundary, pst); } if (item->email->rtf_compressed && save_rtf) { diff -r fd4297884319 -r 2189a6b8134e src/vbuf.c --- a/src/vbuf.c Sat Feb 14 11:02:37 2009 -0800 +++ b/src/vbuf.c Mon Feb 23 20:40:51 2009 -0800 @@ -43,6 +43,7 @@ static int unicode_up = 0; static iconv_t i16to8; static const char *target_charset = NULL; +static int target_open = 0; static iconv_t i8totarget; @@ -61,10 +62,11 @@ void unicode_close() { iconv_close(i16to8); - if (target_charset) { + if (target_open) { iconv_close(i8totarget); free((char *)target_charset); target_charset = NULL; + target_open = 0; } unicode_up = 0; } @@ -130,19 +132,23 @@ size_t outbytesleft = 0; char *outbuf = NULL; - if (!target_charset || (target_charset && strcasecmp(target_charset, charset))) { - if (target_charset) { + if (!target_charset || strcasecmp(target_charset, charset)) { + if (target_open) { iconv_close(i8totarget); free((char *)target_charset); } target_charset = strdup(charset); + target_open = 1; i8totarget = iconv_open(target_charset, "UTF-8"); if (i8totarget == (iconv_t)-1) { + target_open = 0; fprintf(stderr, "Couldn't open iconv descriptor for UTF-8 to %s.\n", target_charset); return (size_t)-1; } } + if (!target_open) return (size_t)-1; // previous failure to open the target + if (2 > dest->blen) vbresize(dest, 2); dest->dlen = 0;