Main Page | Namespace List | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

readpst.c

Go to the documentation of this file.
00001 /***
00002  * readpst.c
00003  * Part of the LibPST project
00004  * Written by David Smith
00005  *            dave.s@earthcorp.com
00006  */
00007 
00008 #include "define.h"
00009 #include "lzfu.h"
00010 
00011 #define OUTPUT_TEMPLATE "%s"
00012 #define OUTPUT_KMAIL_DIR_TEMPLATE ".%s.directory"
00013 #define KMAIL_INDEX ".%s.index"
00014 #define SEP_MAIL_FILE_TEMPLATE "%i" /* "%09i" */
00015 
00016 // max size of the c_time char*. It will store the date of the email
00017 #define C_TIME_SIZE 500
00018 
00019 struct file_ll {
00020     char *name;
00021     char *dname;
00022     FILE * output;
00023     int32_t stored_count;
00024     int32_t email_count;
00025     int32_t skip_count;
00026     int32_t type;
00027 };
00028 
00029 void      process(pst_item *outeritem, pst_desc_ll *d_ptr);
00030 void      write_email_body(FILE *f, char *body);
00031 void      removeCR(char *c);
00032 void      usage();
00033 void      version();
00034 char*     mk_kmail_dir(char*);
00035 int       close_kmail_dir();
00036 char*     mk_recurse_dir(char*);
00037 int       close_recurse_dir();
00038 char*     mk_separate_dir(char *dir);
00039 int       close_separate_dir();
00040 int       mk_separate_file(struct file_ll *f);
00041 char*     my_stristr(char *haystack, char *needle);
00042 void      check_filename(char *fname);
00043 void      write_separate_attachment(char f_name[], pst_item_attach* attach, int attach_num, pst_file* pst);
00044 void      write_embedded_message(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pf, char** extra_mime_headers);
00045 void      write_inline_attachment(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pst);
00046 void      header_has_field(char *header, char *field, int *flag);
00047 void      header_get_subfield(char *field, const char *subfield, char *body_subfield, size_t size_subfield);
00048 char*     header_get_field(char *header, char *field);
00049 char*     header_end_field(char *field);
00050 void      header_strip_field(char *header, char *field);
00051 int       test_base64(char *body);
00052 void      find_html_charset(char *html, char *charset, size_t charsetlen);
00053 void      find_rfc822_headers(char** extra_mime_headers);
00054 void      write_body_part(FILE* f_output, char *body, int32_t body_was_unicode, char *mime, char *charset, char *boundary, pst_file* pst);
00055 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);
00056 void      write_vcard(FILE* f_output, pst_item_contact* contact, char comment[]);
00057 void      write_appointment(FILE* f_output, pst_item_appointment* appointment,
00058                             pst_item_email* email, FILETIME* create_date, FILETIME* modify_date);
00059 void      create_enter_dir(struct file_ll* f, pst_item *item);
00060 void      close_enter_dir(struct file_ll *f);
00061 
00062 const char*  prog_name;
00063 char*  output_dir = ".";
00064 char*  kmail_chdir = NULL;
00065 
00066 // Normal mode just creates mbox format files in the current directory. Each file is named
00067 // the same as the folder's name that it represents
00068 #define MODE_NORMAL 0
00069 
00070 // KMail mode creates a directory structure suitable for being used directly
00071 // by the KMail application
00072 #define MODE_KMAIL 1
00073 
00074 // recurse mode creates a directory structure like the PST file. Each directory
00075 // contains only one file which stores the emails in mbox format.
00076 #define MODE_RECURSE 2
00077 
00078 // separate mode creates the same directory structure as recurse. The emails are stored in
00079 // separate files, numbering from 1 upward. Attachments belonging to the emails are
00080 // saved as email_no-filename (e.g. 1-samplefile.doc or 000001-Attachment2.zip)
00081 #define MODE_SEPARATE 3
00082 
00083 // Decrypt the whole file (even the parts that aren't encrypted) and ralph it to stdout
00084 #define MODE_DECSPEW 4
00085 
00086 
00087 // Output Normal just prints the standard information about what is going on
00088 #define OUTPUT_NORMAL 0
00089 
00090 // Output Quiet is provided so that only errors are printed
00091 #define OUTPUT_QUIET 1
00092 
00093 // default mime-type for attachments that have a null mime-type
00094 #define MIME_TYPE_DEFAULT "application/octet-stream"
00095 #define RFC822            "message/rfc822"
00096 
00097 // output mode for contacts
00098 #define CMODE_VCARD 0
00099 #define CMODE_LIST  1
00100 
00101 // output mode for deleted items
00102 #define DMODE_EXCLUDE 0
00103 #define DMODE_INCLUDE 1
00104 
00105 // output settings for RTF bodies
00106 // filename for the attachment
00107 #define RTF_ATTACH_NAME "rtf-body.rtf"
00108 // mime type for the attachment
00109 #define RTF_ATTACH_TYPE "application/rtf"
00110 
00111 // global settings
00112 int mode = MODE_NORMAL;
00113 int mode_MH = 0;
00114 int output_mode = OUTPUT_NORMAL;
00115 int contact_mode = CMODE_VCARD;
00116 int deleted_mode = DMODE_EXCLUDE;
00117 int overwrite = 0;
00118 int save_rtf_body = 1;
00119 pst_file pstfile;
00120 regex_t  meta_charset_pattern;
00121 
00122 
00123 void process(pst_item *outeritem, pst_desc_ll *d_ptr)
00124 {
00125     struct file_ll ff;
00126     pst_item *item = NULL;
00127 
00128     DEBUG_ENT("process");
00129     memset(&ff, 0, sizeof(ff));
00130     create_enter_dir(&ff, outeritem);
00131 
00132     while (d_ptr) {
00133         DEBUG_MAIN(("main: New item record\n"));
00134         if (!d_ptr->desc) {
00135             DEBUG_WARN(("main: ERROR ?? item's desc record is NULL\n"));
00136             ff.skip_count++;
00137         }
00138         else {
00139             DEBUG_MAIN(("main: Desc Email ID %#"PRIx64" [d_ptr->id = %#"PRIx64"]\n", d_ptr->desc->id, d_ptr->id));
00140 
00141             item = pst_parse_item(&pstfile, d_ptr, NULL);
00142             DEBUG_MAIN(("main: About to process item\n"));
00143             if (item && item->email && item->email->subject && item->email->subject->subj) {
00144                 DEBUG_EMAIL(("item->email->subject->subj = %s\n", item->email->subject->subj));
00145             }
00146             if (item) {
00147                 if (item->folder && d_ptr->child && (deleted_mode == DMODE_INCLUDE || strcasecmp(item->file_as, "Deleted Items"))) {
00148                     //if this is a non-empty folder other than deleted items, we want to recurse into it
00149                     if (output_mode != OUTPUT_QUIET) printf("Processing Folder \"%s\"\n", item->file_as);
00150                     process(item, d_ptr->child);
00151 
00152                 } else if (item->contact && (item->type == PST_TYPE_CONTACT)) {
00153                     // deal with a contact
00154                     // write them to the file, one per line in this format
00155                     // Desc Name <email@address>\n
00156                     if (mode == MODE_SEPARATE) mk_separate_file(&ff);
00157                     ff.email_count++;
00158                     DEBUG_MAIN(("main: Processing Contact\n"));
00159                     if (ff.type != PST_TYPE_CONTACT) {
00160                         DEBUG_MAIN(("main: I have a contact, but the folder isn't a contacts folder. Processing anyway\n"));
00161                     }
00162                     if (contact_mode == CMODE_VCARD)
00163                         write_vcard(ff.output, item->contact, item->comment);
00164                     else
00165                         fprintf(ff.output, "%s <%s>\n", item->contact->fullname, item->contact->address1);
00166 
00167                 } else if (item->email && (item->type == PST_TYPE_NOTE || item->type == PST_TYPE_REPORT || item->type == PST_TYPE_OTHER)) {
00168                     char *extra_mime_headers = NULL;
00169                     if (mode == MODE_SEPARATE) mk_separate_file(&ff);
00170                     ff.email_count++;
00171                     DEBUG_MAIN(("main: Processing Email\n"));
00172                     if ((ff.type != PST_TYPE_NOTE) && (ff.type != PST_TYPE_REPORT) && (ff.type != PST_TYPE_OTHER)) {
00173                         DEBUG_MAIN(("main: I have an email, but the folder isn't an email folder. Processing anyway\n"));
00174                     }
00175                     write_normal_email(ff.output, ff.name, item, mode, mode_MH, &pstfile, save_rtf_body, &extra_mime_headers);
00176 
00177                 } else if (item->journal && (item->type == PST_TYPE_JOURNAL)) {
00178                     // deal with journal items
00179                     if (mode == MODE_SEPARATE) mk_separate_file(&ff);
00180                     ff.email_count++;
00181                     DEBUG_MAIN(("main: Processing Journal Entry\n"));
00182                     if (ff.type != PST_TYPE_JOURNAL) {
00183                         DEBUG_MAIN(("main: I have a journal entry, but the folder isn't a journal folder. Processing anyway\n"));
00184                     }
00185                     fprintf(ff.output, "BEGIN:VJOURNAL\n");
00186                     if (item->email && item->email->subject && item->email->subject->subj)
00187                         fprintf(ff.output, "SUMMARY:%s\n", pst_rfc2426_escape(item->email->subject->subj));
00188                     if (item->email && item->email->body)
00189                         fprintf(ff.output, "DESCRIPTION:%s\n", pst_rfc2426_escape(item->email->body));
00190                     if (item->journal->start)
00191                         fprintf(ff.output, "DTSTART;VALUE=DATE-TIME:%s\n", pst_rfc2445_datetime_format(item->journal->start));
00192                     fprintf(ff.output, "END:VJOURNAL\n\n");
00193 
00194                 } else if (item->appointment && (item->type == PST_TYPE_APPOINTMENT)) {
00195                     // deal with Calendar appointments
00196                     if (mode == MODE_SEPARATE) mk_separate_file(&ff);
00197                     ff.email_count++;
00198                     DEBUG_MAIN(("main: Processing Appointment Entry\n"));
00199                     if (ff.type != PST_TYPE_APPOINTMENT) {
00200                         DEBUG_MAIN(("main: I have an appointment, but folder isn't specified as an appointment type. Processing...\n"));
00201                     }
00202                     write_appointment(ff.output, item->appointment, item->email, item->create_date, item->modify_date);
00203 
00204                 } else if (item->message_store) {
00205                     // there should only be one message_store, and we have already done it
00206                     DEBUG_MAIN(("item with message store content, type %i %s folder type %i, skipping it\n", item->type, item->ascii_type, ff.type));
00207 
00208                 } else {
00209                     // these all seem to be things that MS agrees are not included in the item count
00210                     //ff.skip_count++;
00211                     DEBUG_MAIN(("main: Unknown item type %i (%s) name (%s)\n",
00212                                 item->type, item->ascii_type, item->file_as));
00213                 }
00214                 pst_freeItem(item);
00215             } else {
00216                 ff.skip_count++;
00217                 DEBUG_MAIN(("main: A NULL item was seen\n"));
00218             }
00219             d_ptr = d_ptr->next;
00220         }
00221     }
00222     close_enter_dir(&ff);
00223     DEBUG_RET();
00224 }
00225 
00226 
00227 
00228 int main(int argc, char* const* argv) {
00229     pst_item *item = NULL;
00230     pst_desc_ll *d_ptr;
00231     char * fname = NULL;
00232     char *d_log  = NULL;
00233     int c,x;
00234     char *temp = NULL;               //temporary char pointer
00235     prog_name = argv[0];
00236 
00237     time_t now = time(NULL);
00238     srand((unsigned)now);
00239 
00240     if (regcomp(&meta_charset_pattern, "<meta[^>]*content=\"[^>]*charset=([^>\";]*)[\";]", REG_ICASE | REG_EXTENDED)) {
00241         printf("cannot compile regex pattern to find content charset in html bodies\n");
00242         exit(3);
00243     }
00244 
00245     // command-line option handling
00246     while ((c = getopt(argc, argv, "bCc:Dd:hko:qrSMVw"))!= -1) {
00247         switch (c) {
00248         case 'b':
00249             save_rtf_body = 0;
00250             break;
00251         case 'C':
00252             mode = MODE_DECSPEW;
00253             break;
00254         case 'c':
00255             if (optarg && optarg[0]=='v')
00256                 contact_mode=CMODE_VCARD;
00257             else if (optarg && optarg[0]=='l')
00258                 contact_mode=CMODE_LIST;
00259             else {
00260                 usage();
00261                 exit(0);
00262             }
00263             break;
00264         case 'D':
00265             deleted_mode = DMODE_INCLUDE;
00266             break;
00267         case 'd':
00268             d_log = optarg;
00269             break;
00270         case 'h':
00271             usage();
00272             exit(0);
00273             break;
00274         case 'V':
00275             version();
00276             exit(0);
00277             break;
00278         case 'k':
00279             mode = MODE_KMAIL;
00280             break;
00281         case 'M':
00282             mode = MODE_SEPARATE;
00283             mode_MH = 1;
00284             break;
00285         case 'o':
00286             output_dir = optarg;
00287             break;
00288         case 'q':
00289             output_mode = OUTPUT_QUIET;
00290             break;
00291         case 'r':
00292             mode = MODE_RECURSE;
00293             break;
00294         case 'S':
00295             mode = MODE_SEPARATE;
00296             break;
00297         case 'w':
00298             overwrite = 1;
00299             break;
00300         default:
00301             usage();
00302             exit(1);
00303             break;
00304         }
00305     }
00306 
00307     if (argc > optind) {
00308         fname = argv[optind];
00309     } else {
00310         usage();
00311         exit(2);
00312     }
00313 
00314     #ifdef DEBUG_ALL
00315         // force a log file
00316         if (!d_log) d_log = "readpst.log";
00317     #endif // defined DEBUG_ALL
00318     DEBUG_INIT(d_log);
00319     DEBUG_REGISTER_CLOSE();
00320     DEBUG_ENT("main");
00321 
00322     if (mode == MODE_DECSPEW) {
00323         FILE  *fp;
00324         char   buf[1024];
00325         size_t l = 0;
00326         if (NULL == (fp = fopen(fname, "rb"))) {
00327             fprintf(stderr, "Couldn't open file %s\n", fname );
00328             DEBUG_RET();
00329             return 1;
00330         }
00331 
00332         while (0 != (l = fread(buf, 1, 1024, fp))) {
00333             if (0 != pst_decrypt(0, buf, l, PST_COMP_ENCRYPT))
00334                 fprintf(stderr, "pst_decrypt() failed (I'll try to continue)\n");
00335 
00336             if (l != pst_fwrite(buf, 1, l, stdout)) {
00337                 fprintf(stderr, "Couldn't output to stdout?\n");
00338                 DEBUG_RET();
00339                 return 1;
00340             }
00341         }
00342         DEBUG_RET();
00343         return 0;
00344     }
00345 
00346     if (output_mode != OUTPUT_QUIET) printf("Opening PST file and indexes...\n");
00347 
00348     RET_DERROR(pst_open(&pstfile, fname), 1, ("Error opening File\n"));
00349     RET_DERROR(pst_load_index(&pstfile), 2, ("Index Error\n"));
00350 
00351     pst_load_extended_attributes(&pstfile);
00352 
00353     if (chdir(output_dir)) {
00354         x = errno;
00355         pst_close(&pstfile);
00356         DEBUG_RET();
00357         DIE(("main: Cannot change to output dir %s: %s\n", output_dir, strerror(x)));
00358     }
00359 
00360     if (output_mode != OUTPUT_QUIET) printf("About to start processing first record...\n");
00361 
00362     d_ptr = pstfile.d_head; // first record is main record
00363     item  = pst_parse_item(&pstfile, d_ptr, NULL);
00364     if (!item || !item->message_store) {
00365         DEBUG_RET();
00366         DIE(("main: Could not get root record\n"));
00367     }
00368 
00369     // default the file_as to the same as the main filename if it doesn't exist
00370     if (!item->file_as) {
00371         if (!(temp = strrchr(fname, '/')))
00372             if (!(temp = strrchr(fname, '\\')))
00373                 temp = fname;
00374             else
00375                 temp++; // get past the "\\"
00376         else
00377             temp++; // get past the "/"
00378         item->file_as = (char*)xmalloc(strlen(temp)+1);
00379         strcpy(item->file_as, temp);
00380         DEBUG_MAIN(("file_as was blank, so am using %s\n", item->file_as));
00381     }
00382     DEBUG_MAIN(("main: Root Folder Name: %s\n", item->file_as));
00383 
00384     d_ptr = pst_getTopOfFolders(&pstfile, item);
00385     if (!d_ptr) {
00386         DEBUG_RET();
00387         DIE(("Top of folders record not found. Cannot continue\n"));
00388     }
00389 
00390     process(item, d_ptr->child);    // do the children of TOPF
00391     pst_freeItem(item);
00392     pst_close(&pstfile);
00393     DEBUG_RET();
00394     regfree(&meta_charset_pattern);
00395     return 0;
00396 }
00397 
00398 
00399 void write_email_body(FILE *f, char *body) {
00400     char *n = body;
00401     //  DEBUG_MAIN(("write_email_body(): \"%s\"\n", body));
00402     DEBUG_ENT("write_email_body");
00403     while (n) {
00404         if (strncmp(body, "From ", 5) == 0)
00405             fprintf(f, ">");
00406         if ((n = strchr(body, '\n'))) {
00407             n++;
00408             pst_fwrite(body, n-body, 1, f); //write just a line
00409             body = n;
00410         }
00411     }
00412     pst_fwrite(body, strlen(body), 1, f);
00413     DEBUG_RET();
00414 }
00415 
00416 
00417 void removeCR (char *c) {
00418     // converts \r\n to \n
00419     char *a, *b;
00420     DEBUG_ENT("removeCR");
00421     a = b = c;
00422     while (*a != '\0') {
00423         *b = *a;
00424         if (*a != '\r') b++;
00425         a++;
00426     }
00427     *b = '\0';
00428     DEBUG_RET();
00429 }
00430 
00431 
00432 void usage() {
00433     DEBUG_ENT("usage");
00434     version();
00435     printf("Usage: %s [OPTIONS] {PST FILENAME}\n", prog_name);
00436     printf("OPTIONS:\n");
00437     printf("\t-V\t- Version. Display program version\n");
00438     printf("\t-C\t- Decrypt (compressible encryption) the entire file and output on stdout (not typically useful)\n");
00439     printf("\t-D\t- Include deleted items in output\n");
00440     printf("\t-M\t- MH. Write emails in the MH format\n");
00441     printf("\t-S\t- Separate. Write emails in the separate format\n");
00442     printf("\t-b\t- Don't save RTF-Body attachments\n");
00443     printf("\t-c[v|l]\t- Set the Contact output mode. -cv = VCard, -cl = EMail list\n");
00444     printf("\t-d <filename> \t- Debug to file. This is a binary log. Use readpstlog to print it\n");
00445     printf("\t-h\t- Help. This screen\n");
00446     printf("\t-k\t- KMail. Output in kmail format\n");
00447     printf("\t-o <dirname>\t- Output directory to write files to. CWD is changed *after* opening pst file\n");
00448     printf("\t-q\t- Quiet. Only print error messages\n");
00449     printf("\t-r\t- Recursive. Output in a recursive format\n");
00450     printf("\t-w\t- Overwrite any output mbox files\n");
00451     DEBUG_RET();
00452 }
00453 
00454 
00455 void version() {
00456     DEBUG_ENT("version");
00457     printf("ReadPST / LibPST v%s\n", VERSION);
00458 #if BYTE_ORDER == BIG_ENDIAN
00459     printf("Big Endian implementation being used.\n");
00460 #elif BYTE_ORDER == LITTLE_ENDIAN
00461     printf("Little Endian implementation being used.\n");
00462 #else
00463 #  error "Byte order not supported by this library"
00464 #endif
00465 #ifdef __GNUC__
00466     printf("GCC %d.%d : %s %s\n", __GNUC__, __GNUC_MINOR__, __DATE__, __TIME__);
00467 #endif
00468     DEBUG_RET();
00469 }
00470 
00471 
00472 char *mk_kmail_dir(char *fname) {
00473     //change to that directory
00474     //make a directory based on OUTPUT_KMAIL_DIR_TEMPLATE
00475     //allocate space for OUTPUT_TEMPLATE and form a char* with fname
00476     //return that value
00477     char *dir, *out_name, *index;
00478     int x;
00479     DEBUG_ENT("mk_kmail_dir");
00480     if (kmail_chdir && chdir(kmail_chdir)) {
00481         x = errno;
00482         DIE(("mk_kmail_dir: Cannot change to directory %s: %s\n", kmail_chdir, strerror(x)));
00483     }
00484     dir = malloc(strlen(fname)+strlen(OUTPUT_KMAIL_DIR_TEMPLATE)+1);
00485     sprintf(dir, OUTPUT_KMAIL_DIR_TEMPLATE, fname);
00486     check_filename(dir);
00487     if (D_MKDIR(dir)) {
00488         //error occured
00489         if (errno != EEXIST) {
00490             x = errno;
00491             DIE(("mk_kmail_dir: Cannot create directory %s: %s\n", dir, strerror(x)));
00492         }
00493     }
00494     kmail_chdir = realloc(kmail_chdir, strlen(dir)+1);
00495     strcpy(kmail_chdir, dir);
00496     free (dir);
00497 
00498     //we should remove any existing indexes created by KMail, cause they might be different now
00499     index = malloc(strlen(fname)+strlen(KMAIL_INDEX)+1);
00500     sprintf(index, KMAIL_INDEX, fname);
00501     unlink(index);
00502     free(index);
00503 
00504     out_name = malloc(strlen(fname)+strlen(OUTPUT_TEMPLATE)+1);
00505     sprintf(out_name, OUTPUT_TEMPLATE, fname);
00506     DEBUG_RET();
00507     return out_name;
00508 }
00509 
00510 
00511 int close_kmail_dir() {
00512     // change ..
00513     int x;
00514     DEBUG_ENT("close_kmail_dir");
00515     if (kmail_chdir) { //only free kmail_chdir if not NULL. do not change directory
00516         free(kmail_chdir);
00517         kmail_chdir = NULL;
00518     } else {
00519         if (chdir("..")) {
00520             x = errno;
00521             DIE(("close_kmail_dir: Cannot move up dir (..): %s\n", strerror(x)));
00522         }
00523     }
00524     DEBUG_RET();
00525     return 0;
00526 }
00527 
00528 
00529 // this will create a directory by that name, then make an mbox file inside
00530 // that dir.  any subsequent dirs will be created by name, and they will
00531 // contain mbox files
00532 char *mk_recurse_dir(char *dir) {
00533     int x;
00534     char *out_name;
00535     DEBUG_ENT("mk_recurse_dir");
00536     check_filename(dir);
00537     if (D_MKDIR (dir)) {
00538         if (errno != EEXIST) { // not an error because it exists
00539             x = errno;
00540             DIE(("mk_recurse_dir: Cannot create directory %s: %s\n", dir, strerror(x)));
00541         }
00542     }
00543     if (chdir (dir)) {
00544         x = errno;
00545         DIE(("mk_recurse_dir: Cannot change to directory %s: %s\n", dir, strerror(x)));
00546     }
00547     out_name = malloc(strlen("mbox")+1);
00548     strcpy(out_name, "mbox");
00549     DEBUG_RET();
00550     return out_name;
00551 }
00552 
00553 
00554 int close_recurse_dir() {
00555     int x;
00556     DEBUG_ENT("close_recurse_dir");
00557     if (chdir("..")) {
00558         x = errno;
00559         DIE(("close_recurse_dir: Cannot go up dir (..): %s\n", strerror(x)));
00560     }
00561     DEBUG_RET();
00562     return 0;
00563 }
00564 
00565 
00566 char *mk_separate_dir(char *dir) {
00567     size_t dirsize = strlen(dir) + 10;
00568     char dir_name[dirsize];
00569     int x = 0, y = 0;
00570 
00571     DEBUG_ENT("mk_separate_dir");
00572     do {
00573         if (y == 0)
00574             snprintf(dir_name, dirsize, "%s", dir);
00575         else
00576             snprintf(dir_name, dirsize, "%s" SEP_MAIL_FILE_TEMPLATE, dir, y); // enough for 9 digits allocated above
00577 
00578         check_filename(dir_name);
00579         DEBUG_MAIN(("about to try creating %s\n", dir_name));
00580         if (D_MKDIR(dir_name)) {
00581             if (errno != EEXIST) { // if there is an error, and it doesn't already exist
00582                 x = errno;
00583                 DIE(("mk_separate_dir: Cannot create directory %s: %s\n", dir, strerror(x)));
00584             }
00585         } else {
00586             break;
00587         }
00588         y++;
00589     } while (overwrite == 0);
00590 
00591     if (chdir(dir_name)) {
00592         x = errno;
00593         DIE(("mk_separate_dir: Cannot change to directory %s: %s\n", dir, strerror(x)));
00594     }
00595 
00596     if (overwrite) {
00597         // we should probably delete all files from this directory
00598 #if !defined(WIN32) && !defined(__CYGWIN__)
00599         DIR * sdir = NULL;
00600         struct dirent *dirent = NULL;
00601         struct stat filestat;
00602         if (!(sdir = opendir("./"))) {
00603             WARN(("mk_separate_dir: Cannot open dir \"%s\" for deletion of old contents\n", "./"));
00604         } else {
00605             while ((dirent = readdir(sdir))) {
00606                 if (lstat(dirent->d_name, &filestat) != -1)
00607                     if (S_ISREG(filestat.st_mode)) {
00608                         if (unlink(dirent->d_name)) {
00609                             y = errno;
00610                             DIE(("mk_separate_dir: unlink returned error on file %s: %s\n", dirent->d_name, strerror(y)));
00611                         }
00612                     }
00613             }
00614         }
00615 #endif
00616     }
00617 
00618     // we don't return a filename here cause it isn't necessary.
00619     DEBUG_RET();
00620     return NULL;
00621 }
00622 
00623 
00624 int close_separate_dir() {
00625     int x;
00626     DEBUG_ENT("close_separate_dir");
00627     if (chdir("..")) {
00628         x = errno;
00629         DIE(("close_separate_dir: Cannot go up dir (..): %s\n", strerror(x)));
00630     }
00631     DEBUG_RET();
00632     return 0;
00633 }
00634 
00635 
00636 int mk_separate_file(struct file_ll *f) {
00637     const int name_offset = 1;
00638     DEBUG_ENT("mk_separate_file");
00639     DEBUG_MAIN(("opening next file to save email\n"));
00640     if (f->email_count > 999999999) { // bigger than nine 9's
00641         DIE(("mk_separate_file: The number of emails in this folder has become too high to handle"));
00642     }
00643     sprintf(f->name, SEP_MAIL_FILE_TEMPLATE, f->email_count + name_offset);
00644     if (f->output) fclose(f->output);
00645     f->output = NULL;
00646     check_filename(f->name);
00647     if (!(f->output = fopen(f->name, "w"))) {
00648         DIE(("mk_separate_file: Cannot open file to save email \"%s\"\n", f->name));
00649     }
00650     DEBUG_RET();
00651     return 0;
00652 }
00653 
00654 
00655 char *my_stristr(char *haystack, char *needle) {
00656     // my_stristr varies from strstr in that its searches are case-insensitive
00657     char *x=haystack, *y=needle, *z = NULL;
00658     if (!haystack || !needle) {
00659         return NULL;
00660     }
00661     while (*y != '\0' && *x != '\0') {
00662         if (tolower(*y) == tolower(*x)) {
00663             // move y on one
00664             y++;
00665             if (!z) {
00666                 z = x; // store first position in haystack where a match is made
00667             }
00668         } else {
00669             y = needle; // reset y to the beginning of the needle
00670             z = NULL; // reset the haystack storage point
00671         }
00672         x++; // advance the search in the haystack
00673     }
00674     // If the haystack ended before our search finished, it's not a match.
00675     if (*y != '\0') return NULL;
00676     return z;
00677 }
00678 
00679 
00680 void check_filename(char *fname) {
00681     char *t = fname;
00682     DEBUG_ENT("check_filename");
00683     if (!t) {
00684         DEBUG_RET();
00685         return;
00686     }
00687     while ((t = strpbrk(t, "/\\:"))) {
00688         // while there are characters in the second string that we don't want
00689         *t = '_'; //replace them with an underscore
00690     }
00691     DEBUG_RET();
00692 }
00693 
00694 
00695 void write_separate_attachment(char f_name[], pst_item_attach* attach, int attach_num, pst_file* pst)
00696 {
00697     FILE *fp = NULL;
00698     int x = 0;
00699     char *temp = NULL;
00700 
00701     // If there is a long filename (filename2) use that, otherwise
00702     // use the 8.3 filename (filename1)
00703     char *attach_filename = (attach->filename2) ? attach->filename2
00704                                                         : attach->filename1;
00705     DEBUG_ENT("write_separate_attachment");
00706 
00707     check_filename(f_name);
00708     if (!attach_filename) {
00709         // generate our own (dummy) filename for the attachement
00710         temp = xmalloc(strlen(f_name)+15);
00711         sprintf(temp, "%s-attach%i", f_name, attach_num);
00712     } else {
00713         // have an attachment name, make sure it's unique
00714         temp = xmalloc(strlen(f_name)+strlen(attach_filename)+15);
00715         do {
00716             if (fp) fclose(fp);
00717             if (x == 0)
00718                 sprintf(temp, "%s-%s", f_name, attach_filename);
00719             else
00720                 sprintf(temp, "%s-%s-%i", f_name, attach_filename, x);
00721         } while ((fp = fopen(temp, "r")) && ++x < 99999999);
00722         if (x > 99999999) {
00723             DIE(("error finding attachment name. exhausted possibilities to %s\n", temp));
00724         }
00725     }
00726     DEBUG_EMAIL(("Saving attachment to %s\n", temp));
00727     if (!(fp = fopen(temp, "w"))) {
00728         WARN(("write_separate_attachment: Cannot open attachment save file \"%s\"\n", temp));
00729     } else {
00730         if (attach->data)
00731             pst_fwrite(attach->data, 1, attach->size, fp);
00732         else {
00733             (void)pst_attach_to_file(pst, attach, fp);
00734         }
00735         fclose(fp);
00736     }
00737     if (temp) free(temp);
00738     DEBUG_RET();
00739 }
00740 
00741 
00742 void write_embedded_message(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pf, char** extra_mime_headers)
00743 {
00744     pst_index_ll *ptr;
00745     DEBUG_ENT("write_embedded_message");
00746     fprintf(f_output, "\n--%s\n", boundary);
00747     fprintf(f_output, "Content-Type: %s\n\n", attach->mimetype);
00748     ptr = pst_getID(pf, attach->id_val);
00749 
00750     pst_desc_ll d_ptr;
00751     d_ptr.id         = ptr->id;
00752     d_ptr.parent_id  = 0;
00753     d_ptr.list_index = NULL;
00754     d_ptr.desc       = ptr;
00755     d_ptr.no_child   = 0;
00756     d_ptr.prev       = NULL;
00757     d_ptr.next       = NULL;
00758     d_ptr.parent     = NULL;
00759     d_ptr.child      = NULL;
00760     d_ptr.child_tail = NULL;
00761 
00762     pst_item *item = pst_parse_item(pf, &d_ptr, attach->id2_head);
00763     write_normal_email(f_output, "", item, MODE_NORMAL, 0, pf, 0, extra_mime_headers);
00764     pst_freeItem(item);
00765 
00766     DEBUG_RET();
00767 }
00768 
00769 
00770 void write_inline_attachment(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pst)
00771 {
00772     char *attach_filename;
00773     char *enc = NULL; // base64 encoded attachment
00774     DEBUG_ENT("write_inline_attachment");
00775     DEBUG_EMAIL(("Attachment Size is %i, pointer %p, id %d\n", attach->size, attach->data, attach->id_val));
00776     if (attach->data) {
00777         enc = base64_encode (attach->data, attach->size);
00778         if (!enc) {
00779             DEBUG_EMAIL(("ERROR base64_encode returned NULL. Must have failed\n"));
00780             DEBUG_RET();
00781             return;
00782         }
00783     }
00784     else {
00785         // make sure we can fetch data from the id
00786         pst_index_ll *ptr = pst_getID(pst, attach->id_val);
00787         if (!ptr) {
00788             DEBUG_WARN(("Couldn't find ID pointer. Cannot save attachment to file\n"));
00789             DEBUG_RET();
00790             return;
00791         }
00792     }
00793 
00794     fprintf(f_output, "\n--%s\n", boundary);
00795     if (!attach->mimetype) {
00796         fprintf(f_output, "Content-Type: %s\n", MIME_TYPE_DEFAULT);
00797     } else {
00798         fprintf(f_output, "Content-Type: %s\n", attach->mimetype);
00799     }
00800     fprintf(f_output, "Content-Transfer-Encoding: base64\n");
00801 
00802     // If there is a long filename (filename2) use that, otherwise
00803     // use the 8.3 filename (filename1)
00804     attach_filename = (attach->filename2) ? attach->filename2 : attach->filename1;
00805     if (!attach_filename) {
00806         fprintf(f_output, "Content-Disposition: inline\n\n");
00807     } else {
00808         fprintf(f_output, "Content-Disposition: attachment; filename=\"%s\"\n\n", attach_filename);
00809     }
00810 
00811     if (attach->data) {
00812         pst_fwrite(enc, 1, strlen(enc), f_output);
00813         DEBUG_EMAIL(("Attachment Size after encoding is %i\n", strlen(enc)));
00814         free(enc);  // caught by valgrind
00815     } else {
00816         (void)pst_attach_to_file_base64(pst, attach, f_output);
00817     }
00818     fprintf(f_output, "\n\n");
00819     DEBUG_RET();
00820 }
00821 
00822 
00823 void header_has_field(char *header, char *field, int *flag)
00824 {
00825     DEBUG_ENT("header_has_field");
00826     if (my_stristr(header, field) || (strncasecmp(header, field+1, strlen(field)-1) == 0)) {
00827         DEBUG_EMAIL(("header block has %s header\n", field+1));
00828         *flag = 1;
00829     }
00830     DEBUG_RET();
00831 }
00832 
00833 
00834 void header_get_subfield(char *field, const char *subfield, char *body_subfield, size_t size_subfield)
00835 {
00836     if (!field) return;
00837     DEBUG_ENT("header_get_subfield");
00838     char search[60];
00839     snprintf(search, sizeof(search), " %s=", subfield);
00840     field++;
00841     char *n = header_end_field(field);
00842     char *s = my_stristr(field, search);
00843     if (n && s && (s < n)) {
00844         char *e, *f, save;
00845         s += strlen(search);    // skip over subfield=
00846         if (*s == '"') {
00847             s++;
00848             e = strchr(s, '"');
00849         }
00850         else {
00851             e = strchr(s, ';');
00852             f = strchr(s, '\n');
00853             if (e && f && (f < e)) e = f;
00854         }
00855         if (!e || (e > n)) e = n;   // use the trailing lf as terminator if nothing better
00856         save = *e;
00857         *e = '\0';
00858             snprintf(body_subfield, size_subfield, "%s", s);  // copy the subfield to our buffer
00859         *e = save;
00860         DEBUG_EMAIL(("body %s %s from headers\n", subfield, body_subfield));
00861     }
00862     DEBUG_RET();
00863 }
00864 
00865 char* header_get_field(char *header, char *field)
00866 {
00867     char *t = my_stristr(header, field);
00868     if (!t && (strncasecmp(header, field+1, strlen(field)-1) == 0)) t = header;
00869     return t;
00870 }
00871 
00872 
00873 // return pointer to \n at the end of this header field,
00874 // or NULL if this field goes to the end of the string.
00875 char *header_end_field(char *field)
00876 {
00877     char *e = strchr(field+1, '\n');
00878     while (e && ((e[1] == ' ') || (e[1] == '\t'))) {
00879         e = strchr(e+1, '\n');
00880     }
00881     return e;
00882 }
00883 
00884 
00885 void header_strip_field(char *header, char *field)
00886 {
00887     char *t = header_get_field(header, field);
00888     if (t) {
00889         char *e = header_end_field(t);
00890         if (e) {
00891             if (t == header) e++;   // if *t is not \n, we don't want to keep the \n at *e either.
00892             while (*e != '\0') {
00893                 *t = *e;
00894                 t++;
00895                 e++;
00896             }
00897             *t = '\0';
00898         }
00899         else {
00900             // this was the last header field, truncate the headers
00901             *t = '\0';
00902         }
00903     }
00904 }
00905 
00906 
00907 int  test_base64(char *body)
00908 {
00909     int b64 = 0;
00910     uint8_t *b = (uint8_t *)body;
00911     DEBUG_ENT("test_base64");
00912     while (*b != 0) {
00913         if ((*b < 32) && (*b != 9) && (*b != 10)) {
00914             DEBUG_EMAIL(("found base64 byte %d\n", (int)*b));
00915             DEBUG_HEXDUMPC(body, strlen(body), 0x10);
00916             b64 = 1;
00917             break;
00918         }
00919         b++;
00920     }
00921     DEBUG_RET();
00922     return b64;
00923 }
00924 
00925 
00926 void find_html_charset(char *html, char *charset, size_t charsetlen)
00927 {
00928     const int  index = 1;
00929     const int nmatch = index+1;
00930     regmatch_t match[nmatch];
00931     DEBUG_ENT("find_html_charset");
00932     int rc = regexec(&meta_charset_pattern, html, nmatch, match, 0);
00933     if (rc == 0) {
00934         int s = match[index].rm_so;
00935         int e = match[index].rm_eo;
00936         if (s != -1) {
00937             char save = html[e];
00938             html[e] = '\0';
00939                 snprintf(charset, charsetlen, "%s", html+s);    // copy the html charset
00940             html[e] = save;
00941             DEBUG_EMAIL(("charset %s from html text\n", charset));
00942         }
00943         else {
00944             DEBUG_EMAIL(("matching %d %d %d %d", match[0].rm_so, match[0].rm_eo, match[1].rm_so, match[1].rm_eo));
00945             DEBUG_HEXDUMPC(html, strlen(html), 0x10);
00946         }
00947     }
00948     else {
00949         DEBUG_EMAIL(("regexec returns %d\n", rc));
00950     }
00951     DEBUG_RET();
00952 }
00953 
00954 
00955 void find_rfc822_headers(char** extra_mime_headers)
00956 {
00957     DEBUG_ENT("find_rfc822_headers");
00958     char *headers = *extra_mime_headers;
00959     if (headers) {
00960         char *temp, *t;
00961         while ((temp = strstr(headers, "\n\n"))) {
00962             temp[1] = '\0';
00963             t = header_get_field(headers, "\nContent-Type: ");
00964             if (t) {
00965                 t++;
00966                 DEBUG_EMAIL(("found content type header\n"));
00967                 char *n = strchr(t, '\n');
00968                 char *s = strstr(t, ": ");
00969                 char *e = strchr(t, ';');
00970                 if (!e || (e > n)) e = n;
00971                 if (s && (s < e)) {
00972                     s += 2;
00973                     if (!strncasecmp(s, RFC822, e-s)) {
00974                         headers = temp+2;   // found rfc822 header
00975                         DEBUG_EMAIL(("found 822 headers\n%s\n", headers));
00976                         break;
00977                     }
00978                 }
00979             }
00980             //DEBUG_EMAIL(("skipping to next block after\n%s\n", headers));
00981             headers = temp+2;   // skip to next chunk of headers
00982         }
00983         *extra_mime_headers = headers;
00984     }
00985     DEBUG_RET();
00986 }
00987 
00988 
00989 void write_body_part(FILE* f_output, char *body, int32_t body_was_unicode, char *mime, char *charset, char *boundary, pst_file* pst)
00990 {
00991     char *needfree = NULL;
00992     DEBUG_ENT("write_body_part");
00993     if (body_was_unicode && (strcasecmp("utf-8", charset))) {
00994         // try to convert to the specified charset since the target
00995         // is not utf-8, and the data came from a unicode (utf16) field
00996         // and is now in utf-8.
00997         size_t rc;
00998         DEBUG_EMAIL(("Convert %s utf-8 to %s\n", mime, charset));
00999         vbuf *newer = vballoc(2);
01000         rc = vb_utf8to8bit(newer, body, strlen(body) + 1, charset);
01001         if (rc == (size_t)-1) {
01002             // unable to convert, change the charset to utf8
01003             free(newer->b);
01004             DEBUG_EMAIL(("Failed to convert %s utf-8 to %s\n", mime, charset));
01005             charset = "utf-8";
01006         }
01007         else {
01008             needfree = body = newer->b;
01009         }
01010         free(newer);
01011     }
01012     removeCR(body);
01013     int base64 = test_base64(body);
01014     fprintf(f_output, "\n--%s\n", boundary);
01015     fprintf(f_output, "Content-Type: %s; charset=\"%s\"\n", mime, charset);
01016     if (base64) fprintf(f_output, "Content-Transfer-Encoding: base64\n");
01017     fprintf(f_output, "\n");
01018     if (base64) {
01019         char *enc = base64_encode(body, strlen(body));
01020         if (enc) {
01021             write_email_body(f_output, enc);
01022             fprintf(f_output, "\n");
01023             free(enc);
01024         }
01025     }
01026     else {
01027         write_email_body(f_output, body);
01028     }
01029     if (needfree) free(needfree);
01030     DEBUG_RET();
01031 }
01032 
01033 
01034 const char* codepage(int cp);
01035 const char* codepage(int cp) {
01036     static char buffer[20];
01037     switch (cp) {
01038         case   932 : return "iso-2022-jp";
01039         case   936 : return "gb2313";
01040         case   950 : return "big5";
01041         case 20127 : return "us-ascii";
01042         case 20269 : return "iso-6937";
01043         case 20865 : return "iso-8859-15";
01044         case 20866 : return "koi8-r";
01045         case 21866 : return "koi8-u";
01046         case 28591 : return "iso-8859-1";
01047         case 28592 : return "iso-8859-2";
01048         case 28595 : return "iso-8859-5";
01049         case 28596 : return "iso-8859-6";
01050         case 28597 : return "iso-8859-7";
01051         case 28598 : return "iso-8859-8";
01052         case 28599 : return "iso-8859-9";
01053         case 50220 : return "iso-2022-jp";
01054         case 50221 : return "csiso2022jp";
01055         case 51932 : return "euc-jp";
01056         case 51949 : return "euc-kr";
01057         case 65000 : return "utf-7";
01058         case 65001 : return "utf-8";
01059         default :
01060             snprintf(buffer, sizeof(buffer), "windows-%d", cp);
01061             return buffer;
01062     }
01063     return NULL;
01064 }
01065 
01066 
01067 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)
01068 {
01069     char boundary[60];
01070     char body_charset[60];
01071     char body_report[60];
01072     char sender[60];
01073     int  sender_known = 0;
01074     char *temp = NULL;
01075     int attach_num;
01076     time_t em_time;
01077     char *c_time;
01078     char *headers = (item->email->header) ? item->email->header : *extra_mime_headers;
01079     int has_from, has_subject, has_to, has_cc, has_date, has_msgid;
01080     has_from = has_subject = has_to = has_cc = has_date = has_msgid = 0;
01081     DEBUG_ENT("write_normal_email");
01082 
01083     // setup default body character set and report type
01084     snprintf(body_charset, sizeof(body_charset), "%s",
01085         (item->email->body_charset)     ? item->email->body_charset :
01086         (item->email->message_codepage) ? codepage(item->email->message_codepage) :
01087         (item->email->internet_cpid)    ? codepage(item->email->internet_cpid) :
01088         "utf-8");
01089     body_report[0] = '\0';
01090 
01091     // setup default sender
01092     if (item->email->sender_address && strchr(item->email->sender_address, '@')) {
01093         temp = item->email->sender_address;
01094         sender_known = 1;
01095     }
01096     else {
01097         temp = "MAILER-DAEMON";
01098     }
01099     snprintf(sender, sizeof(sender), "%s", temp);
01100 
01101     // convert the sent date if it exists, or set it to a fixed date
01102     if (item->email->sent_date) {
01103         em_time = fileTimeToUnixTime(item->email->sent_date, 0);
01104         c_time = ctime(&em_time);
01105         if (c_time)
01106             c_time[strlen(c_time)-1] = '\0'; //remove end \n
01107         else
01108             c_time = "Fri Dec 28 12:06:21 2001";
01109     } else
01110         c_time= "Fri Dec 28 12:06:21 2001";
01111 
01112     // create our MIME boundary here.
01113     snprintf(boundary, sizeof(boundary), "--boundary-LibPST-iamunique-%i_-_-", rand());
01114 
01115     // we will always look at the headers to discover some stuff
01116     if (headers ) {
01117         char *t;
01118         removeCR(headers);
01119 
01120         temp = strstr(headers, "\n\n");
01121         if (temp) {
01122             // cut off our real rfc822 headers here
01123             temp[1] = '\0';
01124             // pointer to all the embedded MIME headers.
01125             // we use these to find the actual rfc822 headers for embedded message/rfc822 mime parts
01126             *extra_mime_headers = temp+2;
01127             DEBUG_EMAIL(("Found extra mime headers\n%s\n", temp+2));
01128         }
01129 
01130         // Check if the headers have all the necessary fields
01131         header_has_field(headers, "\nFrom: ",        &has_from);
01132         header_has_field(headers, "\nTo: ",          &has_to);
01133         header_has_field(headers, "\nSubject: ",     &has_subject);
01134         header_has_field(headers, "\nDate: ",        &has_date);
01135         header_has_field(headers, "\nCC: ",          &has_cc);
01136         header_has_field(headers, "\nMessage-Id: ",  &has_msgid);
01137 
01138         // look for charset and report-type in Content-Type header
01139         t = header_get_field(headers, "\nContent-Type: ");
01140         header_get_subfield(t, "charset", body_charset, sizeof(body_charset));
01141         header_get_subfield(t, "report-type", body_report, sizeof(body_report));
01142 
01143         // derive a proper sender email address
01144         if (!sender_known) {
01145             t = header_get_field(headers, "\nFrom: ");
01146             if (t) {
01147                 // assume address is on the first line, rather than on a continuation line
01148                 t++;
01149                 char *n = strchr(t, '\n');
01150                 char *s = strchr(t, '<');
01151                 char *e = strchr(t, '>');
01152                 if (s && e && n && (s < e) && (e < n)) {
01153                 char save = *e;
01154                 *e = '\0';
01155                     snprintf(sender, sizeof(sender), "%s", s+1);
01156                 *e = save;
01157                 }
01158             }
01159         }
01160 
01161         // Strip out the mime headers and some others that we don't want to emit
01162         header_strip_field(headers, "\nMicrosoft Mail Internet Headers");
01163         header_strip_field(headers, "\nMIME-Version: ");
01164         header_strip_field(headers, "\nContent-Type: ");
01165         header_strip_field(headers, "\nContent-Transfer-Encoding: ");
01166         header_strip_field(headers, "\nContent-class: ");
01167         header_strip_field(headers, "\nX-MimeOLE: ");
01168         header_strip_field(headers, "\nBcc:");
01169         header_strip_field(headers, "\nX-From_: ");
01170     }
01171 
01172     DEBUG_EMAIL(("About to print Header\n"));
01173 
01174     if (item && item->email && item->email->subject && item->email->subject->subj) {
01175         DEBUG_EMAIL(("item->email->subject->subj = %s\n", item->email->subject->subj));
01176     }
01177 
01178     if (mode != MODE_SEPARATE) {
01179         // most modes need this separator line.
01180         // procmail produces this separator without the quotes around the
01181         // sender email address, but apparently some Mac email client needs
01182         // those quotes, and they don't seem to cause problems for anyone else.
01183         fprintf(f_output, "From \"%s\" %s\n", sender, c_time);
01184     }
01185 
01186     // print the supplied email headers
01187     if (headers) {
01188         int len;
01189         fprintf(f_output, "%s", headers);
01190         // make sure the headers end with a \n
01191         len = strlen(headers);
01192         if (!len || (headers[len-1] != '\n')) fprintf(f_output, "\n");
01193     }
01194 
01195     // create required header fields that are not already written
01196 
01197     if (!has_from) {
01198         fprintf(f_output, "From: \"%s\" <%s>\n", item->email->outlook_sender_name, sender);
01199     }
01200 
01201     if (!has_subject) {
01202         if (item->email->subject && item->email->subject->subj) {
01203             fprintf(f_output, "Subject: %s\n", item->email->subject->subj);
01204         } else {
01205             fprintf(f_output, "Subject: \n");
01206         }
01207     }
01208 
01209     if (!has_to && item->email->sentto_address) {
01210         fprintf(f_output, "To: %s\n", item->email->sentto_address);
01211     }
01212 
01213     if (!has_cc && item->email->cc_address) {
01214         fprintf(f_output, "Cc: %s\n", item->email->cc_address);
01215     }
01216 
01217     if (!has_date && item->email->sent_date) {
01218         char c_time[C_TIME_SIZE];
01219         strftime(c_time, C_TIME_SIZE, "%a, %d %b %Y %H:%M:%S %z", gmtime(&em_time));
01220         fprintf(f_output, "Date: %s\n", c_time);
01221     }
01222 
01223     if (!has_msgid && item->email->messageid) {
01224         fprintf(f_output, "Message-Id: %s\n", item->email->messageid);
01225     }
01226 
01227     // add forensic headers to capture some .pst stuff that is not really
01228     // needed or used by mail clients
01229     if (item->email->sender_address && !strchr(item->email->sender_address, '@')
01230                                     && strcmp(item->email->sender_address, ".")) {
01231         fprintf(f_output, "X-libpst-forensic-sender: %s\n", item->email->sender_address);
01232     }
01233 
01234     if (item->email->bcc_address) {
01235         fprintf(f_output, "X-libpst-forensic-bcc: %s\n", item->email->bcc_address);
01236     }
01237 
01238     // add our own mime headers
01239     fprintf(f_output, "MIME-Version: 1.0\n");
01240     if (body_report[0] != '\0') {
01241         // multipart/report for DSN/MDN reports
01242         fprintf(f_output, "Content-Type: multipart/report; report-type=%s;\n\tboundary=\"%s\"\n", body_report, boundary);
01243     }
01244     else if (item->attach || (item->email->rtf_compressed && save_rtf)
01245                           || item->email->encrypted_body
01246                           || item->email->encrypted_htmlbody) {
01247         // use multipart/mixed if we have attachments
01248         fprintf(f_output, "Content-Type: multipart/mixed;\n\tboundary=\"%s\"\n", boundary);
01249     } else {
01250         // else use multipart/alternative
01251         fprintf(f_output, "Content-Type: multipart/alternative;\n\tboundary=\"%s\"\n", boundary);
01252     }
01253     fprintf(f_output, "\n");    // end of headers, start of body
01254 
01255     // now dump the body parts
01256     if (item->email->body) {
01257         write_body_part(f_output, item->email->body, item->email->body_was_unicode, "text/plain", body_charset, boundary, pst);
01258     }
01259 
01260     if ((item->email->report_text) && (body_report[0] != '\0')) {
01261         write_body_part(f_output, item->email->report_text, item->email->report_was_unicode, "text/plain", body_charset, boundary, pst);
01262         fprintf(f_output, "\n");
01263     }
01264 
01265     if (item->email->htmlbody) {
01266         find_html_charset(item->email->htmlbody, body_charset, sizeof(body_charset));
01267         write_body_part(f_output, item->email->htmlbody, item->email->htmlbody_was_unicode, "text/html", body_charset, boundary, pst);
01268     }
01269 
01270     if (item->email->rtf_compressed && save_rtf) {
01271         pst_item_attach* attach = (pst_item_attach*)xmalloc(sizeof(pst_item_attach));
01272         DEBUG_EMAIL(("Adding RTF body as attachment\n"));
01273         memset(attach, 0, sizeof(pst_item_attach));
01274         attach->next = item->attach;
01275         item->attach = attach;
01276         attach->data = lzfu_decompress(item->email->rtf_compressed, item->email->rtf_compressed_size, &attach->size);
01277         attach->filename2 = strdup(RTF_ATTACH_NAME);
01278         attach->mimetype  = strdup(RTF_ATTACH_TYPE);
01279     }
01280 
01281     if (item->email->encrypted_body || item->email->encrypted_htmlbody) {
01282         // if either the body or htmlbody is encrypted, add them as attachments
01283         if (item->email->encrypted_body) {
01284             pst_item_attach* attach = (pst_item_attach*)xmalloc(sizeof(pst_item_attach));
01285             DEBUG_EMAIL(("Adding Encrypted Body as attachment\n"));
01286             attach = (pst_item_attach*) xmalloc(sizeof(pst_item_attach));
01287             memset(attach, 0, sizeof(pst_item_attach));
01288             attach->next = item->attach;
01289             item->attach = attach;
01290             attach->data = item->email->encrypted_body;
01291             attach->size = item->email->encrypted_body_size;
01292             item->email->encrypted_body = NULL;
01293         }
01294 
01295         if (item->email->encrypted_htmlbody) {
01296             pst_item_attach* attach = (pst_item_attach*)xmalloc(sizeof(pst_item_attach));
01297             DEBUG_EMAIL(("Adding encrypted HTML body as attachment\n"));
01298             attach = (pst_item_attach*) xmalloc(sizeof(pst_item_attach));
01299             memset(attach, 0, sizeof(pst_item_attach));
01300             attach->next = item->attach;
01301             item->attach = attach;
01302             attach->data = item->email->encrypted_htmlbody;
01303             attach->size = item->email->encrypted_htmlbody_size;
01304             item->email->encrypted_htmlbody = NULL;
01305         }
01306         write_email_body(f_output, "The body of this email is encrypted. This isn't supported yet, but the body is now an attachment\n");
01307     }
01308 
01309     // other attachments
01310     {
01311         pst_item_attach* attach;
01312         attach_num = 0;
01313         for (attach = item->attach; attach; attach = attach->next) {
01314             DEBUG_EMAIL(("Attempting Attachment encoding\n"));
01315             if (!attach->data && attach->mimetype && !strcmp(attach->mimetype, RFC822)) {
01316                 DEBUG_EMAIL(("seem to have special embedded message attachment\n"));
01317                 find_rfc822_headers(extra_mime_headers);
01318                 write_embedded_message(f_output, attach, boundary, pst, extra_mime_headers);
01319             }
01320             else if (mode == MODE_SEPARATE && !mode_MH)
01321                 write_separate_attachment(f_name, attach, ++attach_num, pst);
01322             else
01323                 write_inline_attachment(f_output, attach, boundary, pst);
01324         }
01325     }
01326 
01327     // end of this mail message
01328     if (mode != MODE_SEPARATE) { /* do not add a boundary after the last attachment for mode_MH */
01329         DEBUG_EMAIL(("Writing buffer between emails\n"));
01330         fprintf(f_output, "\n--%s--\n", boundary);
01331         fprintf(f_output, "\n\n");
01332     }
01333     DEBUG_RET();
01334 }
01335 
01336 
01337 void write_vcard(FILE* f_output, pst_item_contact* contact, char comment[])
01338 {
01339     // We can only call rfc escape once per printf, since the second call
01340     // may free the buffer returned by the first call.
01341     // I had tried to place those into a single printf - Carl.
01342 
01343     DEBUG_ENT("write_vcard");
01344     // the specification I am following is (hopefully) RFC2426 vCard Mime Directory Profile
01345     fprintf(f_output, "BEGIN:VCARD\n");
01346     fprintf(f_output, "FN:%s\n", pst_rfc2426_escape(contact->fullname));
01347 
01348     //fprintf(f_output, "N:%s;%s;%s;%s;%s\n",
01349     fprintf(f_output, "N:%s;", (!contact->surname)             ? "" : pst_rfc2426_escape(contact->surname));
01350     fprintf(f_output, "%s;",   (!contact->first_name)          ? "" : pst_rfc2426_escape(contact->first_name));
01351     fprintf(f_output, "%s;",   (!contact->middle_name)         ? "" : pst_rfc2426_escape(contact->middle_name));
01352     fprintf(f_output, "%s;",   (!contact->display_name_prefix) ? "" : pst_rfc2426_escape(contact->display_name_prefix));
01353     fprintf(f_output, "%s\n",  (!contact->suffix)              ? "" : pst_rfc2426_escape(contact->suffix));
01354 
01355     if (contact->nickname)
01356         fprintf(f_output, "NICKNAME:%s\n", pst_rfc2426_escape(contact->nickname));
01357     if (contact->address1)
01358         fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address1));
01359     if (contact->address2)
01360         fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address2));
01361     if (contact->address3)
01362         fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address3));
01363     if (contact->birthday)
01364         fprintf(f_output, "BDAY:%s\n", pst_rfc2425_datetime_format(contact->birthday));
01365 
01366     if (contact->home_address) {
01367         //fprintf(f_output, "ADR;TYPE=home:%s;%s;%s;%s;%s;%s;%s\n",
01368         fprintf(f_output, "ADR;TYPE=home:%s;",  (!contact->home_po_box)      ? "" : pst_rfc2426_escape(contact->home_po_box));
01369         fprintf(f_output, "%s;",                ""); // extended Address
01370         fprintf(f_output, "%s;",                (!contact->home_street)      ? "" : pst_rfc2426_escape(contact->home_street));
01371         fprintf(f_output, "%s;",                (!contact->home_city)        ? "" : pst_rfc2426_escape(contact->home_city));
01372         fprintf(f_output, "%s;",                (!contact->home_state)       ? "" : pst_rfc2426_escape(contact->home_state));
01373         fprintf(f_output, "%s;",                (!contact->home_postal_code) ? "" : pst_rfc2426_escape(contact->home_postal_code));
01374         fprintf(f_output, "%s\n",               (!contact->home_country)     ? "" : pst_rfc2426_escape(contact->home_country));
01375         fprintf(f_output, "LABEL;TYPE=home:%s\n", pst_rfc2426_escape(contact->home_address));
01376     }
01377 
01378     if (contact->business_address) {
01379         //fprintf(f_output, "ADR;TYPE=work:%s;%s;%s;%s;%s;%s;%s\n",
01380         fprintf(f_output, "ADR;TYPE=work:%s;",  (!contact->business_po_box)      ? "" : pst_rfc2426_escape(contact->business_po_box));
01381         fprintf(f_output, "%s;",                ""); // extended Address
01382         fprintf(f_output, "%s;",                (!contact->business_street)      ? "" : pst_rfc2426_escape(contact->business_street));
01383         fprintf(f_output, "%s;",                (!contact->business_city)        ? "" : pst_rfc2426_escape(contact->business_city));
01384         fprintf(f_output, "%s;",                (!contact->business_state)       ? "" : pst_rfc2426_escape(contact->business_state));
01385         fprintf(f_output, "%s;",                (!contact->business_postal_code) ? "" : pst_rfc2426_escape(contact->business_postal_code));
01386         fprintf(f_output, "%s\n",               (!contact->business_country)     ? "" : pst_rfc2426_escape(contact->business_country));
01387         fprintf(f_output, "LABEL;TYPE=work:%s\n", pst_rfc2426_escape(contact->business_address));
01388     }
01389 
01390     if (contact->other_address) {
01391         //fprintf(f_output, "ADR;TYPE=postal:%s;%s;%s;%s;%s;%s;%s\n",
01392         fprintf(f_output, "ADR;TYPE=postal:%s;",(!contact->other_po_box)       ? "" : pst_rfc2426_escape(contact->other_po_box));
01393         fprintf(f_output, "%s;",                ""); // extended Address
01394         fprintf(f_output, "%s;",                (!contact->other_street)       ? "" : pst_rfc2426_escape(contact->other_street));
01395         fprintf(f_output, "%s;",                (!contact->other_city)         ? "" : pst_rfc2426_escape(contact->other_city));
01396         fprintf(f_output, "%s;",                (!contact->other_state)        ? "" : pst_rfc2426_escape(contact->other_state));
01397         fprintf(f_output, "%s;",                (!contact->other_postal_code)  ? "" : pst_rfc2426_escape(contact->other_postal_code));
01398         fprintf(f_output, "%s\n",               (!contact->other_country)      ? "" : pst_rfc2426_escape(contact->other_country));
01399         fprintf(f_output, "LABEL;TYPE=postal:%s\n", pst_rfc2426_escape(contact->other_address));
01400     }
01401 
01402     if (contact->business_fax)      fprintf(f_output, "TEL;TYPE=work,fax:%s\n",         pst_rfc2426_escape(contact->business_fax));
01403     if (contact->business_phone)    fprintf(f_output, "TEL;TYPE=work,voice:%s\n",       pst_rfc2426_escape(contact->business_phone));
01404     if (contact->business_phone2)   fprintf(f_output, "TEL;TYPE=work,voice:%s\n",       pst_rfc2426_escape(contact->business_phone2));
01405     if (contact->car_phone)         fprintf(f_output, "TEL;TYPE=car,voice:%s\n",        pst_rfc2426_escape(contact->car_phone));
01406     if (contact->home_fax)          fprintf(f_output, "TEL;TYPE=home,fax:%s\n",         pst_rfc2426_escape(contact->home_fax));
01407     if (contact->home_phone)        fprintf(f_output, "TEL;TYPE=home,voice:%s\n",       pst_rfc2426_escape(contact->home_phone));
01408     if (contact->home_phone2)       fprintf(f_output, "TEL;TYPE=home,voice:%s\n",       pst_rfc2426_escape(contact->home_phone2));
01409     if (contact->isdn_phone)        fprintf(f_output, "TEL;TYPE=isdn:%s\n",             pst_rfc2426_escape(contact->isdn_phone));
01410     if (contact->mobile_phone)      fprintf(f_output, "TEL;TYPE=cell,voice:%s\n",       pst_rfc2426_escape(contact->mobile_phone));
01411     if (contact->other_phone)       fprintf(f_output, "TEL;TYPE=msg:%s\n",              pst_rfc2426_escape(contact->other_phone));
01412     if (contact->pager_phone)       fprintf(f_output, "TEL;TYPE=pager:%s\n",            pst_rfc2426_escape(contact->pager_phone));
01413     if (contact->primary_fax)       fprintf(f_output, "TEL;TYPE=fax,pref:%s\n",         pst_rfc2426_escape(contact->primary_fax));
01414     if (contact->primary_phone)     fprintf(f_output, "TEL;TYPE=phone,pref:%s\n",       pst_rfc2426_escape(contact->primary_phone));
01415     if (contact->radio_phone)       fprintf(f_output, "TEL;TYPE=pcs:%s\n",              pst_rfc2426_escape(contact->radio_phone));
01416     if (contact->telex)             fprintf(f_output, "TEL;TYPE=bbs:%s\n",              pst_rfc2426_escape(contact->telex));
01417     if (contact->job_title)         fprintf(f_output, "TITLE:%s\n",                     pst_rfc2426_escape(contact->job_title));
01418     if (contact->profession)        fprintf(f_output, "ROLE:%s\n",                      pst_rfc2426_escape(contact->profession));
01419     if (contact->assistant_name || contact->assistant_phone) {
01420         fprintf(f_output, "AGENT:BEGIN:VCARD\n");
01421         if (contact->assistant_name)    fprintf(f_output, "FN:%s\n",                    pst_rfc2426_escape(contact->assistant_name));
01422         if (contact->assistant_phone)   fprintf(f_output, "TEL:%s\n",                   pst_rfc2426_escape(contact->assistant_phone));
01423     }
01424     if (contact->company_name)      fprintf(f_output, "ORG:%s\n",                       pst_rfc2426_escape(contact->company_name));
01425     if (comment)                    fprintf(f_output, "NOTE:%s\n",                      pst_rfc2426_escape(comment));
01426 
01427     fprintf(f_output, "VERSION: 3.0\n");
01428     fprintf(f_output, "END:VCARD\n\n");
01429     DEBUG_RET();
01430 }
01431 
01432 
01433 void write_appointment(FILE* f_output, pst_item_appointment* appointment,
01434                pst_item_email* email, FILETIME* create_date, FILETIME* modify_date)
01435 {
01436     fprintf(f_output, "BEGIN:VEVENT\n");
01437     if (create_date)
01438         fprintf(f_output, "CREATED:%s\n",
01439             pst_rfc2445_datetime_format(create_date));
01440     if (modify_date)
01441         fprintf(f_output, "LAST-MOD:%s\n",
01442             pst_rfc2445_datetime_format(modify_date));
01443     if (email && email->subject)
01444         fprintf(f_output, "SUMMARY:%s\n",
01445             pst_rfc2426_escape(email->subject->subj));
01446     if (email && email->body)
01447         fprintf(f_output, "DESCRIPTION:%s\n",
01448             pst_rfc2426_escape(email->body));
01449     if (appointment && appointment->start)
01450         fprintf(f_output, "DTSTART;VALUE=DATE-TIME:%s\n",
01451             pst_rfc2445_datetime_format(appointment->start));
01452     if (appointment && appointment->end)
01453         fprintf(f_output, "DTEND;VALUE=DATE-TIME:%s\n",
01454             pst_rfc2445_datetime_format(appointment->end));
01455     if (appointment && appointment->location)
01456         fprintf(f_output, "LOCATION:%s\n",
01457             pst_rfc2426_escape(appointment->location));
01458     if (appointment) {
01459         switch (appointment->showas) {
01460             case PST_FREEBUSY_TENTATIVE:
01461                 fprintf(f_output, "STATUS:TENTATIVE\n");
01462                 break;
01463             case PST_FREEBUSY_FREE:
01464                 // mark as transparent and as confirmed
01465                 fprintf(f_output, "TRANSP:TRANSPARENT\n");
01466             case PST_FREEBUSY_BUSY:
01467             case PST_FREEBUSY_OUT_OF_OFFICE:
01468                 fprintf(f_output, "STATUS:CONFIRMED\n");
01469                 break;
01470         }
01471         switch (appointment->label) {
01472             case PST_APP_LABEL_NONE:
01473                 fprintf(f_output, "CATEGORIES:NONE\n");
01474                 break;
01475             case PST_APP_LABEL_IMPORTANT:
01476                 fprintf(f_output, "CATEGORIES:IMPORTANT\n");
01477                 break;
01478             case PST_APP_LABEL_BUSINESS:
01479                 fprintf(f_output, "CATEGORIES:BUSINESS\n");
01480                 break;
01481             case PST_APP_LABEL_PERSONAL:
01482                 fprintf(f_output, "CATEGORIES:PERSONAL\n");
01483                 break;
01484             case PST_APP_LABEL_VACATION:
01485                 fprintf(f_output, "CATEGORIES:VACATION\n");
01486                 break;
01487             case PST_APP_LABEL_MUST_ATTEND:
01488                 fprintf(f_output, "CATEGORIES:MUST-ATTEND\n");
01489                 break;
01490             case PST_APP_LABEL_TRAVEL_REQ:
01491                 fprintf(f_output, "CATEGORIES:TRAVEL-REQUIRED\n");
01492                 break;
01493             case PST_APP_LABEL_NEEDS_PREP:
01494                 fprintf(f_output, "CATEGORIES:NEEDS-PREPARATION\n");
01495                 break;
01496             case PST_APP_LABEL_BIRTHDAY:
01497                 fprintf(f_output, "CATEGORIES:BIRTHDAY\n");
01498                 break;
01499             case PST_APP_LABEL_ANNIVERSARY:
01500                 fprintf(f_output, "CATEGORIES:ANNIVERSARY\n");
01501                 break;
01502             case PST_APP_LABEL_PHONE_CALL:
01503                 fprintf(f_output, "CATEGORIES:PHONE-CALL\n");
01504                 break;
01505         }
01506     }
01507     fprintf(f_output, "END:VEVENT\n\n");
01508 }
01509 
01510 
01511 void create_enter_dir(struct file_ll* f, pst_item *item)
01512 {
01513     f->email_count  = 0;
01514     f->skip_count   = 0;
01515     f->type         = item->type;
01516     f->stored_count = (item->folder) ? item->folder->email_count : 0;
01517 
01518     DEBUG_ENT("create_enter_dir");
01519     if (mode == MODE_KMAIL)
01520         f->name = mk_kmail_dir(item->file_as); //create directory and form filename
01521     else if (mode == MODE_RECURSE)
01522         f->name = mk_recurse_dir(item->file_as);
01523     else if (mode == MODE_SEPARATE) {
01524         // do similar stuff to recurse here.
01525         mk_separate_dir(item->file_as);
01526         f->name = (char*) xmalloc(10);
01527         memset(f->name, 0, 10);
01528         //      sprintf(f->name, SEP_MAIL_FILE_TEMPLATE, f->email_count);
01529     } else {
01530         f->name = (char*) xmalloc(strlen(item->file_as)+strlen(OUTPUT_TEMPLATE)+1);
01531         sprintf(f->name, OUTPUT_TEMPLATE, item->file_as);
01532     }
01533 
01534     f->dname = (char*) xmalloc(strlen(item->file_as)+1);
01535     strcpy(f->dname, item->file_as);
01536 
01537     if (overwrite != 1) {
01538         int x = 0;
01539         char *temp = (char*) xmalloc (strlen(f->name)+10); //enough room for 10 digits
01540 
01541         sprintf(temp, "%s", f->name);
01542         check_filename(temp);
01543         while ((f->output = fopen(temp, "r"))) {
01544             DEBUG_MAIN(("need to increase filename because one already exists with that name\n"));
01545             DEBUG_MAIN(("- increasing it to %s%d\n", f->name, x));
01546             x++;
01547             sprintf(temp, "%s%08d", f->name, x);
01548             DEBUG_MAIN(("- trying \"%s\"\n", f->name));
01549             if (x == 99999999) {
01550                 DIE(("create_enter_dir: Why can I not create a folder %s? I have tried %i extensions...\n", f->name, x));
01551             }
01552             fclose(f->output);
01553         }
01554         if (x > 0) { //then the f->name should change
01555             free (f->name);
01556             f->name = temp;
01557         } else {
01558             free(temp);
01559         }
01560     }
01561 
01562     DEBUG_MAIN(("f->name = %s\nitem->folder_name = %s\n", f->name, item->file_as));
01563     if (mode != MODE_SEPARATE) {
01564         check_filename(f->name);
01565         if (!(f->output = fopen(f->name, "w"))) {
01566             DIE(("create_enter_dir: Could not open file \"%s\" for write\n", f->name));
01567         }
01568     }
01569     DEBUG_RET();
01570 }
01571 
01572 
01573 void close_enter_dir(struct file_ll *f)
01574 {
01575     DEBUG_MAIN(("main: Email Count for folder %s is %i\n", f->dname, f->email_count));
01576     if (output_mode != OUTPUT_QUIET)
01577         printf("\t\"%s\" - %i items done, skipped %i, should have been %i\n",
01578                f->dname, f->email_count, f->skip_count, f->stored_count);
01579     if (f->output) fclose(f->output);
01580     free(f->name);
01581     free(f->dname);
01582 
01583     if (mode == MODE_KMAIL)
01584         close_kmail_dir();
01585     else if (mode == MODE_RECURSE)
01586         close_recurse_dir();
01587     else if (mode == MODE_SEPARATE)
01588         close_separate_dir();
01589 }
01590 

Generated on Thu Feb 26 13:40:07 2009 for 'LibPst' by  doxygen 1.3.9.1