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%s"
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 item_count;
00025     int32_t skip_count;
00026     int32_t type;
00027 };
00028 
00029 int       grim_reaper();
00030 pid_t     try_fork(char* folder);
00031 void      process(pst_item *outeritem, pst_desc_tree *d_ptr);
00032 void      write_email_body(FILE *f, char *body);
00033 void      removeCR(char *c);
00034 void      usage();
00035 void      version();
00036 char*     mk_kmail_dir(char* fname);
00037 int       close_kmail_dir();
00038 char*     mk_recurse_dir(char* dir, int32_t folder_type);
00039 int       close_recurse_dir();
00040 char*     mk_separate_dir(char *dir);
00041 int       close_separate_dir();
00042 int       mk_separate_file(struct file_ll *f, char *extension);
00043 char*     my_stristr(char *haystack, char *needle);
00044 void      check_filename(char *fname);
00045 void      write_separate_attachment(char f_name[], pst_item_attach* attach, int attach_num, pst_file* pst);
00046 void      write_embedded_message(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pf, char** extra_mime_headers);
00047 void      write_inline_attachment(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pst);
00048 void      header_has_field(char *header, char *field, int *flag);
00049 void      header_get_subfield(char *field, const char *subfield, char *body_subfield, size_t size_subfield);
00050 char*     header_get_field(char *header, char *field);
00051 char*     header_end_field(char *field);
00052 void      header_strip_field(char *header, char *field);
00053 int       test_base64(char *body);
00054 void      find_html_charset(char *html, char *charset, size_t charsetlen);
00055 void      find_rfc822_headers(char** extra_mime_headers);
00056 void      write_body_part(FILE* f_output, pst_string *body, char *mime, char *charset, char *boundary, pst_file* pst);
00057 void      write_schedule_part_data(FILE* f_output, pst_item* item, const char* sender, const char* method);
00058 void      write_schedule_part(FILE* f_output, pst_item* item, const char* sender, const char* boundary);
00059 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);
00060 void      write_vcard(FILE* f_output, pst_item *item, pst_item_contact* contact, char comment[]);
00061 int       write_extra_categories(FILE* f_output, pst_item* item);
00062 void      write_journal(FILE* f_output, pst_item* item);
00063 void      write_appointment(FILE* f_output, pst_item *item);
00064 void      create_enter_dir(struct file_ll* f, pst_item *item);
00065 void      close_enter_dir(struct file_ll *f);
00066 
00067 const char*  prog_name;
00068 char*  output_dir = ".";
00069 char*  kmail_chdir = NULL;
00070 
00071 // Normal mode just creates mbox format files in the current directory. Each file is named
00072 // the same as the folder's name that it represents
00073 #define MODE_NORMAL 0
00074 
00075 // KMail mode creates a directory structure suitable for being used directly
00076 // by the KMail application
00077 #define MODE_KMAIL 1
00078 
00079 // recurse mode creates a directory structure like the PST file. Each directory
00080 // contains only one file which stores the emails in mboxrd format.
00081 #define MODE_RECURSE 2
00082 
00083 // separate mode creates the same directory structure as recurse. The emails are stored in
00084 // separate files, numbering from 1 upward. Attachments belonging to the emails are
00085 // saved as email_no-filename (e.g. 1-samplefile.doc or 1-Attachment2.zip)
00086 #define MODE_SEPARATE 3
00087 
00088 
00089 // Output Normal just prints the standard information about what is going on
00090 #define OUTPUT_NORMAL 0
00091 
00092 // Output Quiet is provided so that only errors are printed
00093 #define OUTPUT_QUIET 1
00094 
00095 // default mime-type for attachments that have a null mime-type
00096 #define MIME_TYPE_DEFAULT "application/octet-stream"
00097 #define RFC822            "message/rfc822"
00098 
00099 // output mode for contacts
00100 #define CMODE_VCARD 0
00101 #define CMODE_LIST  1
00102 
00103 // output mode for deleted items
00104 #define DMODE_EXCLUDE 0
00105 #define DMODE_INCLUDE 1
00106 
00107 // Output type mode flags
00108 #define OTMODE_EMAIL        1
00109 #define OTMODE_APPOINTMENT  2
00110 #define OTMODE_JOURNAL      4
00111 #define OTMODE_CONTACT      8
00112 
00113 // output settings for RTF bodies
00114 // filename for the attachment
00115 #define RTF_ATTACH_NAME "rtf-body.rtf"
00116 // mime type for the attachment
00117 #define RTF_ATTACH_TYPE "application/rtf"
00118 
00119 // global settings
00120 int         mode         = MODE_NORMAL;
00121 int         mode_MH      = 0;   // a submode of MODE_SEPARATE
00122 int         mode_EX      = 0;   // a submode of MODE_SEPARATE
00123 int         mode_thunder = 0;   // a submode of MODE_RECURSE
00124 int         output_mode  = OUTPUT_NORMAL;
00125 int         contact_mode = CMODE_VCARD;
00126 int         deleted_mode = DMODE_EXCLUDE;
00127 int         output_type_mode = 0xff;    // Default to all.
00128 int         contact_mode_specified = 0;
00129 int         overwrite = 0;
00130 int         save_rtf_body = 1;
00131 int         file_name_len = 10;     // enough room for MODE_SPEARATE file name
00132 pst_file    pstfile;
00133 regex_t     meta_charset_pattern;
00134 
00135 int         number_processors = 1;  // number of cpus we have
00136 int         max_children  = 0;      // based on number of cpus and command line args
00137 int         max_child_specified = 0;// have command line arg -j
00138 int         active_children;        // number of children of this process, cannot be larger than max_children
00139 pid_t*      child_processes;        // setup by main(), and at the start of new child process
00140 
00141 #ifdef HAVE_SEMAPHORE_H
00142 int         shared_memory_id;
00143 sem_t*      global_children = NULL;
00144 sem_t*      output_mutex    = NULL;
00145 #endif
00146 
00147 
00148 int grim_reaper(int waitall)
00149 {
00150     int available = 0;
00151 #ifdef HAVE_FORK
00152 #ifdef HAVE_SEMAPHORE_H
00153     if (global_children) {
00154         sem_getvalue(global_children, &available);
00155         //printf("grim reaper %s for pid %d (parent %d) with %d children, %d available\n", (waitall) ? "all" : "", getpid(), getppid(), active_children, available);
00156         //fflush(stdout);
00157         int i,j;
00158         for (i=0; i<active_children; i++) {
00159             int status;
00160             pid_t child = child_processes[i];
00161             pid_t ch = waitpid(child, &status, ((waitall) ? 0 : WNOHANG));
00162             if (ch == child) {
00163                 // check termination status
00164                 //if (WIFEXITED(status)) {
00165                 //    int ext = WEXITSTATUS(status);
00166                 //    printf("Process %d exited with status  %d\n", child, ext);
00167                 //    fflush(stdout);
00168                 //}
00169                 if (WIFSIGNALED(status)) {
00170                     int sig = WTERMSIG(status);
00171                     DEBUG_INFO(("Process %d terminated with signal %d\n", child, sig));
00172                     //printf("Process %d terminated with signal %d\n", child, sig);
00173                     //fflush(stdout);
00174                 }
00175                 // this has terminated, remove it from the list
00176                 for (j=i; j<active_children-1; j++) {
00177                     child_processes[j] = child_processes[j+1];
00178                 }
00179                 active_children--;
00180                 i--;
00181             }
00182         }
00183         sem_getvalue(global_children, &available);
00184         //printf("grim reaper %s for pid %d with %d children, %d available\n", (waitall) ? "all" : "", getpid(), active_children, available);
00185         //fflush(stdout);
00186     }
00187 #endif
00188 #endif
00189     return available;
00190 }
00191 
00192 
00193 pid_t try_fork(char *folder)
00194 {
00195 #ifdef HAVE_FORK
00196 #ifdef HAVE_SEMAPHORE_H
00197     int available = grim_reaper(0);
00198     if (available) {
00199         sem_wait(global_children);
00200         pid_t child = fork();
00201         if (child < 0) {
00202             // fork failed, pretend it worked and we are the child
00203             return 0;
00204         }
00205         else if (child == 0) {
00206             // fork worked, and we are the child, reinitialize *our* list of children
00207             active_children = 0;
00208             memset(child_processes, 0, sizeof(pid_t) * max_children);
00209             pst_reopen(&pstfile);   // close and reopen the pst file to get an independent file position pointer
00210         }
00211         else {
00212             // fork worked, and we are the parent, record this child that we need to wait for
00213             //pid_t me = getpid();
00214             //printf("parent %d forked child pid %d to process folder %s\n", me, child, folder);
00215             //fflush(stdout);
00216             child_processes[active_children++] = child;
00217         }
00218         return child;
00219     }
00220     else {
00221         return 0;   // pretend to have forked and we are the child
00222     }
00223 #endif
00224 #endif
00225     return 0;
00226 }
00227 
00228 
00229 void process(pst_item *outeritem, pst_desc_tree *d_ptr)
00230 {
00231     struct file_ll ff;
00232     pst_item *item = NULL;
00233 
00234     DEBUG_ENT("process");
00235     memset(&ff, 0, sizeof(ff));
00236     create_enter_dir(&ff, outeritem);
00237 
00238     for (; d_ptr; d_ptr = d_ptr->next) {
00239         DEBUG_INFO(("New item record\n"));
00240         if (!d_ptr->desc) {
00241             ff.skip_count++;
00242             DEBUG_WARN(("ERROR item's desc record is NULL\n"));
00243             continue;
00244         }
00245         DEBUG_INFO(("Desc Email ID %#"PRIx64" [d_ptr->d_id = %#"PRIx64"]\n", d_ptr->desc->i_id, d_ptr->d_id));
00246 
00247         item = pst_parse_item(&pstfile, d_ptr, NULL);
00248         DEBUG_INFO(("About to process item\n"));
00249 
00250         if (!item) {
00251             ff.skip_count++;
00252             DEBUG_INFO(("A NULL item was seen\n"));
00253             continue;
00254         }
00255 
00256         if (item->subject.str) {
00257             DEBUG_INFO(("item->subject = %s\n", item->subject.str));
00258         }
00259 
00260         if (item->folder && item->file_as.str) {
00261             DEBUG_INFO(("Processing Folder \"%s\"\n", item->file_as.str));
00262             if (output_mode != OUTPUT_QUIET) {
00263                 pst_debug_lock();
00264                     printf("Processing Folder \"%s\"\n", item->file_as.str);
00265                     fflush(stdout);
00266                 pst_debug_unlock();
00267             }
00268             ff.item_count++;
00269             if (d_ptr->child && (deleted_mode == DMODE_INCLUDE || strcasecmp(item->file_as.str, "Deleted Items"))) {
00270                 //if this is a non-empty folder other than deleted items, we want to recurse into it
00271                 pid_t parent = getpid();
00272                 pid_t child = try_fork(item->file_as.str);
00273                 if (child == 0) {
00274                     // we are the child process, or the original parent if no children were available
00275                     pid_t me = getpid();
00276                     process(item, d_ptr->child);
00277 #ifdef HAVE_FORK
00278 #ifdef HAVE_SEMAPHORE_H
00279                     if (me != parent) {
00280                         // we really were a child, forked for the sole purpose of processing this folder
00281                         // free my child count slot before really exiting, since
00282                         // all I am doing here is waiting for my children to exit
00283                         sem_post(global_children);
00284                         grim_reaper(1); // wait for all my child processes to exit
00285                         exit(0);        // really exit
00286                     }
00287 #endif
00288 #endif
00289                 }
00290             }
00291 
00292         } else if (item->contact && (item->type == PST_TYPE_CONTACT)) {
00293             DEBUG_INFO(("Processing Contact\n"));
00294             if (!(output_type_mode & OTMODE_CONTACT)) {
00295                 ff.skip_count++;
00296                 DEBUG_INFO(("skipping contact: not in output type list\n"));
00297             }
00298             else {
00299                 if (!ff.type) ff.type = item->type;
00300                 if ((ff.type != PST_TYPE_CONTACT) && (mode != MODE_SEPARATE)) {
00301                     ff.skip_count++;
00302                     DEBUG_INFO(("I have a contact, but the folder type %"PRIi32" isn't a contacts folder. Skipping it\n", ff.type));
00303                 }
00304                 else {
00305                     ff.item_count++;
00306                     if (mode == MODE_SEPARATE) mk_separate_file(&ff, (mode_EX) ? ".vcf" : "");
00307                     if (contact_mode == CMODE_VCARD) {
00308                         pst_convert_utf8_null(item, &item->comment);
00309                         write_vcard(ff.output, item, item->contact, item->comment.str);
00310                     }
00311                     else {
00312                         pst_convert_utf8(item, &item->contact->fullname);
00313                         pst_convert_utf8(item, &item->contact->address1);
00314                         fprintf(ff.output, "%s <%s>\n", item->contact->fullname.str, item->contact->address1.str);
00315                     }
00316                 }
00317             }
00318 
00319         } else if (item->email && ((item->type == PST_TYPE_NOTE) || (item->type == PST_TYPE_SCHEDULE) || (item->type == PST_TYPE_REPORT))) {
00320             DEBUG_INFO(("Processing Email\n"));
00321             if (!(output_type_mode & OTMODE_EMAIL)) {
00322                 ff.skip_count++;
00323                 DEBUG_INFO(("skipping email: not in output type list\n"));
00324             }
00325             else {
00326                 if (!ff.type) ff.type = item->type;
00327                 if ((ff.type != PST_TYPE_NOTE) && (ff.type != PST_TYPE_SCHEDULE) && (ff.type != PST_TYPE_REPORT) && (mode != MODE_SEPARATE)) {
00328                     ff.skip_count++;
00329                     DEBUG_INFO(("I have an email type %"PRIi32", but the folder type %"PRIi32" isn't an email folder. Skipping it\n", item->type, ff.type));
00330                 }
00331                 else {
00332                     char *extra_mime_headers = NULL;
00333                     ff.item_count++;
00334                     if (mode == MODE_SEPARATE) mk_separate_file(&ff, (mode_EX) ? ".eml" : "");
00335                     write_normal_email(ff.output, ff.name, item, mode, mode_MH, &pstfile, save_rtf_body, &extra_mime_headers);
00336                 }
00337             }
00338 
00339         } else if (item->journal && (item->type == PST_TYPE_JOURNAL)) {
00340             DEBUG_INFO(("Processing Journal Entry\n"));
00341             if (!(output_type_mode & OTMODE_JOURNAL)) {
00342                 ff.skip_count++;
00343                 DEBUG_INFO(("skipping journal entry: not in output type list\n"));
00344             }
00345             else {
00346                 if (!ff.type) ff.type = item->type;
00347                 if ((ff.type != PST_TYPE_JOURNAL) && (mode != MODE_SEPARATE)) {
00348                     ff.skip_count++;
00349                     DEBUG_INFO(("I have a journal entry, but the folder type %"PRIi32" isn't a journal folder. Skipping it\n", ff.type));
00350                 }
00351                 else {
00352                     ff.item_count++;
00353                     if (mode == MODE_SEPARATE) mk_separate_file(&ff, (mode_EX) ? ".ics" : "");
00354                     write_journal(ff.output, item);
00355                     fprintf(ff.output, "\n");
00356                 }
00357             }
00358 
00359         } else if (item->appointment && (item->type == PST_TYPE_APPOINTMENT)) {
00360             DEBUG_INFO(("Processing Appointment Entry\n"));
00361             if (!(output_type_mode & OTMODE_APPOINTMENT)) {
00362                 ff.skip_count++;
00363                 DEBUG_INFO(("skipping appointment: not in output type list\n"));
00364             }
00365             else {
00366                 if (!ff.type) ff.type = item->type;
00367                 if ((ff.type != PST_TYPE_APPOINTMENT) && (mode != MODE_SEPARATE)) {
00368                     ff.skip_count++;
00369                     DEBUG_INFO(("I have an appointment, but the folder type %"PRIi32" isn't an appointment folder. Skipping it\n", ff.type));
00370                 }
00371                 else {
00372                     ff.item_count++;
00373                     if (mode == MODE_SEPARATE) mk_separate_file(&ff, (mode_EX) ? ".ics" : "");
00374                     write_schedule_part_data(ff.output, item, NULL, NULL);
00375                     fprintf(ff.output, "\n");
00376                 }
00377             }
00378 
00379         } else if (item->message_store) {
00380             // there should only be one message_store, and we have already done it
00381             ff.skip_count++;
00382             DEBUG_INFO(("item with message store content, type %i %s folder type %i, skipping it\n", item->type, item->ascii_type, ff.type));
00383 
00384         } else {
00385             ff.skip_count++;
00386             DEBUG_INFO(("Unknown item type %i (%s) name (%s)\n",
00387                         item->type, item->ascii_type, item->file_as.str));
00388         }
00389         pst_freeItem(item);
00390     }
00391     close_enter_dir(&ff);
00392     DEBUG_RET();
00393 }
00394 
00395 
00396 
00397 int main(int argc, char* const* argv) {
00398     pst_item *item = NULL;
00399     pst_desc_tree *d_ptr;
00400     char * fname = NULL;
00401     char *d_log  = NULL;
00402     int c,x;
00403     char *temp = NULL;               //temporary char pointer
00404     prog_name = argv[0];
00405 
00406     time_t now = time(NULL);
00407     srand((unsigned)now);
00408 
00409     if (regcomp(&meta_charset_pattern, "<meta[^>]*content=\"[^>]*charset=([^>\";]*)[\";]", REG_ICASE | REG_EXTENDED)) {
00410         printf("cannot compile regex pattern to find content charset in html bodies\n");
00411         exit(3);
00412     }
00413 
00414     // command-line option handling
00415     while ((c = getopt(argc, argv, "bc:Dd:ehj:kMo:qrSt:uVw"))!= -1) {
00416         switch (c) {
00417         case 'b':
00418             save_rtf_body = 0;
00419             break;
00420         case 'c':
00421             if (optarg && optarg[0]=='v') {
00422                 contact_mode=CMODE_VCARD;
00423                 contact_mode_specified = 1;
00424             }
00425             else if (optarg && optarg[0]=='l') {
00426                 contact_mode=CMODE_LIST;
00427                 contact_mode_specified = 1;
00428             }
00429             else {
00430                 usage();
00431                 exit(0);
00432             }
00433             break;
00434         case 'D':
00435             deleted_mode = DMODE_INCLUDE;
00436             break;
00437         case 'd':
00438             d_log = optarg;
00439             break;
00440         case 'h':
00441             usage();
00442             exit(0);
00443             break;
00444         case 'j':
00445             max_children = atoi(optarg);
00446             max_child_specified = 1;
00447             break;
00448         case 'k':
00449             mode = MODE_KMAIL;
00450             break;
00451         case 'M':
00452             mode = MODE_SEPARATE;
00453             mode_MH = 1;
00454             mode_EX = 0;
00455             break;
00456         case 'e':
00457             mode = MODE_SEPARATE;
00458             mode_MH = 1;
00459             mode_EX = 1;
00460             file_name_len = 14;
00461             break;
00462         case 'o':
00463             output_dir = optarg;
00464             break;
00465         case 'q':
00466             output_mode = OUTPUT_QUIET;
00467             break;
00468         case 'r':
00469             mode = MODE_RECURSE;
00470             mode_thunder = 0;
00471             break;
00472         case 'S':
00473             mode = MODE_SEPARATE;
00474             mode_MH = 0;
00475             mode_EX = 0;
00476             break;
00477         case 't':
00478             // email, appointment, contact, other
00479             if (!optarg) {
00480                 usage();
00481                 exit(0);
00482             }
00483             temp = optarg;
00484             output_type_mode = 0;
00485             while (*temp > 0) {
00486               switch (temp[0]) {
00487                 case 'e':
00488                     output_type_mode |= OTMODE_EMAIL;
00489                     break;
00490                 case 'a':
00491                     output_type_mode |= OTMODE_APPOINTMENT;
00492                     break;
00493                 case 'j':
00494                     output_type_mode |= OTMODE_JOURNAL;
00495                     break;
00496                 case 'c':
00497                     output_type_mode |= OTMODE_CONTACT;
00498                     break;
00499                 default:
00500                     usage();
00501                     exit(0);
00502                     break;
00503               }
00504               temp++;
00505             }
00506             break;
00507         case 'u':
00508             mode = MODE_RECURSE;
00509             mode_thunder = 1;
00510             break;
00511         case 'V':
00512             version();
00513             exit(0);
00514             break;
00515         case 'w':
00516             overwrite = 1;
00517             break;
00518         default:
00519             usage();
00520             exit(1);
00521             break;
00522         }
00523     }
00524 
00525     if (argc > optind) {
00526         fname = argv[optind];
00527     } else {
00528         usage();
00529         exit(2);
00530     }
00531 
00532 #ifdef _SC_NPROCESSORS_ONLN
00533     number_processors =  sysconf(_SC_NPROCESSORS_ONLN);
00534 #endif
00535     max_children    = (max_child_specified) ? max_children : number_processors * 4;
00536     active_children = 0;
00537     child_processes = (pid_t *)pst_malloc(sizeof(pid_t) * max_children);
00538     memset(child_processes, 0, sizeof(pid_t) * max_children);
00539 
00540 #ifdef HAVE_SEMAPHORE_H
00541     if (max_children) {
00542         shared_memory_id = shmget(IPC_PRIVATE, sizeof(sem_t)*2, 0777);
00543         if (shared_memory_id >= 0) {
00544             global_children = (sem_t *)shmat(shared_memory_id, NULL, 0);
00545             if (global_children == (sem_t *)-1) global_children = NULL;
00546             if (global_children) {
00547                 output_mutex = &(global_children[1]);
00548                 sem_init(global_children, 1, max_children);
00549                 sem_init(output_mutex, 1, 1);
00550             }
00551             shmctl(shared_memory_id, IPC_RMID, NULL);
00552         }
00553     }
00554 #endif
00555 
00556     #ifdef DEBUG_ALL
00557         // force a log file
00558         if (!d_log) d_log = "readpst.log";
00559     #endif // defined DEBUG_ALL
00560     #ifdef HAVE_SEMAPHORE_H
00561         DEBUG_INIT(d_log, output_mutex);
00562     #else
00563         DEBUG_INIT(d_log, NULL);
00564     #endif
00565     DEBUG_ENT("main");
00566 
00567     if (output_mode != OUTPUT_QUIET) printf("Opening PST file and indexes...\n");
00568 
00569     RET_DERROR(pst_open(&pstfile, fname), 1, ("Error opening File\n"));
00570     RET_DERROR(pst_load_index(&pstfile), 2, ("Index Error\n"));
00571 
00572     pst_load_extended_attributes(&pstfile);
00573 
00574     if (chdir(output_dir)) {
00575         x = errno;
00576         pst_close(&pstfile);
00577         DEBUG_RET();
00578         DIE(("Cannot change to output dir %s: %s\n", output_dir, strerror(x)));
00579     }
00580 
00581     d_ptr = pstfile.d_head; // first record is main record
00582     item  = pst_parse_item(&pstfile, d_ptr, NULL);
00583     if (!item || !item->message_store) {
00584         DEBUG_RET();
00585         DIE(("Could not get root record\n"));
00586     }
00587 
00588     // default the file_as to the same as the main filename if it doesn't exist
00589     if (!item->file_as.str) {
00590         if (!(temp = strrchr(fname, '/')))
00591             if (!(temp = strrchr(fname, '\\')))
00592                 temp = fname;
00593             else
00594                 temp++; // get past the "\\"
00595         else
00596             temp++; // get past the "/"
00597         item->file_as.str = (char*)pst_malloc(strlen(temp)+1);
00598         strcpy(item->file_as.str, temp);
00599         item->file_as.is_utf8 = 1;
00600         DEBUG_INFO(("file_as was blank, so am using %s\n", item->file_as.str));
00601     }
00602     DEBUG_INFO(("Root Folder Name: %s\n", item->file_as.str));
00603 
00604     d_ptr = pst_getTopOfFolders(&pstfile, item);
00605     if (!d_ptr) {
00606         DEBUG_RET();
00607         DIE(("Top of folders record not found. Cannot continue\n"));
00608     }
00609 
00610     process(item, d_ptr->child);    // do the children of TOPF
00611     grim_reaper(1); // wait for all child processes
00612 
00613     pst_freeItem(item);
00614     pst_close(&pstfile);
00615     DEBUG_RET();
00616 
00617 #ifdef HAVE_SEMAPHORE_H
00618     if (global_children) {
00619         sem_destroy(global_children);
00620         sem_destroy(output_mutex);
00621         shmdt(global_children);
00622     }
00623 #endif
00624 
00625     regfree(&meta_charset_pattern);
00626     return 0;
00627 }
00628 
00629 
00630 void write_email_body(FILE *f, char *body) {
00631     char *n = body;
00632     DEBUG_ENT("write_email_body");
00633     if (mode != MODE_SEPARATE) {
00634         while (n) {
00635             char *p = body;
00636             while (*p == '>') p++;
00637             if (strncmp(p, "From ", 5) == 0) fprintf(f, ">");
00638             if ((n = strchr(body, '\n'))) {
00639                 n++;
00640                 pst_fwrite(body, n-body, 1, f); //write just a line
00641                 body = n;
00642             }
00643         }
00644     }
00645     pst_fwrite(body, strlen(body), 1, f);
00646     DEBUG_RET();
00647 }
00648 
00649 
00650 void removeCR (char *c) {
00651     // converts \r\n to \n
00652     char *a, *b;
00653     DEBUG_ENT("removeCR");
00654     a = b = c;
00655     while (*a != '\0') {
00656         *b = *a;
00657         if (*a != '\r') b++;
00658         a++;
00659     }
00660     *b = '\0';
00661     DEBUG_RET();
00662 }
00663 
00664 
00665 void usage() {
00666     DEBUG_ENT("usage");
00667     version();
00668     printf("Usage: %s [OPTIONS] {PST FILENAME}\n", prog_name);
00669     printf("OPTIONS:\n");
00670     printf("\t-V\t- Version. Display program version\n");
00671     printf("\t-D\t- Include deleted items in output\n");
00672     printf("\t-M\t- Write emails in the MH (rfc822) format\n");
00673     printf("\t-S\t- Separate. Write emails in the separate format\n");
00674     printf("\t-b\t- Don't save RTF-Body attachments\n");
00675     printf("\t-c[v|l]\t- Set the Contact output mode. -cv = VCard, -cl = EMail list\n");
00676     printf("\t-d <filename> \t- Debug to file.\n");
00677     printf("\t-e\t- As with -M, but include extensions on output files\n");
00678     printf("\t-h\t- Help. This screen\n");
00679     printf("\t-j <integer>\t- Number of parallel jobs to run\n");
00680     printf("\t-k\t- KMail. Output in kmail format\n");
00681     printf("\t-o <dirname>\t- Output directory to write files to. CWD is changed *after* opening pst file\n");
00682     printf("\t-q\t- Quiet. Only print error messages\n");
00683     printf("\t-r\t- Recursive. Output in a recursive format\n");
00684     printf("\t-t[eajc]\t- Set the output type list. e = email, a = attachment, j = journal, c = contact\n");
00685     printf("\t-u\t- Thunderbird mode. Write two extra .size and .type files\n");
00686     printf("\t-w\t- Overwrite any output mbox files\n");
00687     printf("\n");
00688     printf("Only one of -k -M -r -S should be specified\n");
00689     DEBUG_RET();
00690 }
00691 
00692 
00693 void version() {
00694     DEBUG_ENT("version");
00695     printf("ReadPST / LibPST v%s\n", VERSION);
00696 #if BYTE_ORDER == BIG_ENDIAN
00697     printf("Big Endian implementation being used.\n");
00698 #elif BYTE_ORDER == LITTLE_ENDIAN
00699     printf("Little Endian implementation being used.\n");
00700 #else
00701 #  error "Byte order not supported by this library"
00702 #endif
00703 #ifdef __GNUC__
00704     printf("GCC %d.%d : %s %s\n", __GNUC__, __GNUC_MINOR__, __DATE__, __TIME__);
00705 #endif
00706     DEBUG_RET();
00707 }
00708 
00709 
00710 char *mk_kmail_dir(char *fname) {
00711     //change to that directory
00712     //make a directory based on OUTPUT_KMAIL_DIR_TEMPLATE
00713     //allocate space for OUTPUT_TEMPLATE and form a char* with fname
00714     //return that value
00715     char *dir, *out_name, *index;
00716     int x;
00717     DEBUG_ENT("mk_kmail_dir");
00718     if (kmail_chdir && chdir(kmail_chdir)) {
00719         x = errno;
00720         DIE(("mk_kmail_dir: Cannot change to directory %s: %s\n", kmail_chdir, strerror(x)));
00721     }
00722     dir = malloc(strlen(fname)+strlen(OUTPUT_KMAIL_DIR_TEMPLATE)+1);
00723     sprintf(dir, OUTPUT_KMAIL_DIR_TEMPLATE, fname);
00724     check_filename(dir);
00725     if (D_MKDIR(dir)) {
00726         if (errno != EEXIST) {  // not an error because it exists
00727             x = errno;
00728             DIE(("mk_kmail_dir: Cannot create directory %s: %s\n", dir, strerror(x)));
00729         }
00730     }
00731     kmail_chdir = realloc(kmail_chdir, strlen(dir)+1);
00732     strcpy(kmail_chdir, dir);
00733     free (dir);
00734 
00735     //we should remove any existing indexes created by KMail, cause they might be different now
00736     index = malloc(strlen(fname)+strlen(KMAIL_INDEX)+1);
00737     sprintf(index, KMAIL_INDEX, fname);
00738     unlink(index);
00739     free(index);
00740 
00741     out_name = malloc(strlen(fname)+strlen(OUTPUT_TEMPLATE)+1);
00742     sprintf(out_name, OUTPUT_TEMPLATE, fname);
00743     DEBUG_RET();
00744     return out_name;
00745 }
00746 
00747 
00748 int close_kmail_dir() {
00749     // change ..
00750     int x;
00751     DEBUG_ENT("close_kmail_dir");
00752     if (kmail_chdir) { //only free kmail_chdir if not NULL. do not change directory
00753         free(kmail_chdir);
00754         kmail_chdir = NULL;
00755     } else {
00756         if (chdir("..")) {
00757             x = errno;
00758             DIE(("close_kmail_dir: Cannot move up dir (..): %s\n", strerror(x)));
00759         }
00760     }
00761     DEBUG_RET();
00762     return 0;
00763 }
00764 
00765 
00766 // this will create a directory by that name,
00767 // then make an mbox file inside that directory.
00768 char *mk_recurse_dir(char *dir, int32_t folder_type) {
00769     int x;
00770     char *out_name;
00771     DEBUG_ENT("mk_recurse_dir");
00772     check_filename(dir);
00773     if (D_MKDIR (dir)) {
00774         if (errno != EEXIST) {  // not an error because it exists
00775             x = errno;
00776             DIE(("mk_recurse_dir: Cannot create directory %s: %s\n", dir, strerror(x)));
00777         }
00778     }
00779     if (chdir (dir)) {
00780         x = errno;
00781         DIE(("mk_recurse_dir: Cannot change to directory %s: %s\n", dir, strerror(x)));
00782     }
00783     switch (folder_type) {
00784         case PST_TYPE_APPOINTMENT:
00785             out_name = strdup("calendar");
00786             break;
00787         case PST_TYPE_CONTACT:
00788             out_name = strdup("contacts");
00789             break;
00790         case PST_TYPE_JOURNAL:
00791             out_name = strdup("journal");
00792             break;
00793         case PST_TYPE_STICKYNOTE:
00794         case PST_TYPE_TASK:
00795         case PST_TYPE_NOTE:
00796         case PST_TYPE_OTHER:
00797         case PST_TYPE_REPORT:
00798         default:
00799             out_name = strdup("mbox");
00800             break;
00801     }
00802     DEBUG_RET();
00803     return out_name;
00804 }
00805 
00806 
00807 int close_recurse_dir() {
00808     int x;
00809     DEBUG_ENT("close_recurse_dir");
00810     if (chdir("..")) {
00811         x = errno;
00812         DIE(("close_recurse_dir: Cannot go up dir (..): %s\n", strerror(x)));
00813     }
00814     DEBUG_RET();
00815     return 0;
00816 }
00817 
00818 
00819 char *mk_separate_dir(char *dir) {
00820     size_t dirsize = strlen(dir) + 10;
00821     char dir_name[dirsize];
00822     int x = 0, y = 0;
00823 
00824     DEBUG_ENT("mk_separate_dir");
00825     do {
00826         if (y == 0)
00827             snprintf(dir_name, dirsize, "%s", dir);
00828         else
00829             snprintf(dir_name, dirsize, "%s" SEP_MAIL_FILE_TEMPLATE, dir, y, ""); // enough for 9 digits allocated above
00830 
00831         check_filename(dir_name);
00832         DEBUG_INFO(("about to try creating %s\n", dir_name));
00833         if (D_MKDIR(dir_name)) {
00834             if (errno != EEXIST) { // if there is an error, and it doesn't already exist
00835                 x = errno;
00836                 DIE(("mk_separate_dir: Cannot create directory %s: %s\n", dir, strerror(x)));
00837             }
00838         } else {
00839             break;
00840         }
00841         y++;
00842     } while (overwrite == 0);
00843 
00844     if (chdir(dir_name)) {
00845         x = errno;
00846         DIE(("mk_separate_dir: Cannot change to directory %s: %s\n", dir, strerror(x)));
00847     }
00848 
00849     if (overwrite) {
00850         // we should probably delete all files from this directory
00851 #if !defined(WIN32) && !defined(__CYGWIN__)
00852         DIR * sdir = NULL;
00853         struct dirent *dirent = NULL;
00854         struct stat filestat;
00855         if (!(sdir = opendir("./"))) {
00856             DEBUG_WARN(("mk_separate_dir: Cannot open dir \"%s\" for deletion of old contents\n", "./"));
00857         } else {
00858             while ((dirent = readdir(sdir))) {
00859                 if (lstat(dirent->d_name, &filestat) != -1)
00860                     if (S_ISREG(filestat.st_mode)) {
00861                         if (unlink(dirent->d_name)) {
00862                             y = errno;
00863                             DIE(("mk_separate_dir: unlink returned error on file %s: %s\n", dirent->d_name, strerror(y)));
00864                         }
00865                     }
00866             }
00867         }
00868 #endif
00869     }
00870 
00871     // we don't return a filename here cause it isn't necessary.
00872     DEBUG_RET();
00873     return NULL;
00874 }
00875 
00876 
00877 int close_separate_dir() {
00878     int x;
00879     DEBUG_ENT("close_separate_dir");
00880     if (chdir("..")) {
00881         x = errno;
00882         DIE(("close_separate_dir: Cannot go up dir (..): %s\n", strerror(x)));
00883     }
00884     DEBUG_RET();
00885     return 0;
00886 }
00887 
00888 
00889 int mk_separate_file(struct file_ll *f, char *extension) {
00890     DEBUG_ENT("mk_separate_file");
00891     DEBUG_INFO(("opening next file to save email\n"));
00892     if (f->item_count > 999999999) { // bigger than nine 9's
00893         DIE(("mk_separate_file: The number of emails in this folder has become too high to handle\n"));
00894     }
00895     sprintf(f->name, SEP_MAIL_FILE_TEMPLATE, f->item_count, extension);
00896     if (f->output) fclose(f->output);
00897     f->output = NULL;
00898     check_filename(f->name);
00899     if (!(f->output = fopen(f->name, "w"))) {
00900         DIE(("mk_separate_file: Cannot open file to save email \"%s\"\n", f->name));
00901     }
00902     DEBUG_RET();
00903     return 0;
00904 }
00905 
00906 
00907 char *my_stristr(char *haystack, char *needle) {
00908     // my_stristr varies from strstr in that its searches are case-insensitive
00909     char *x=haystack, *y=needle, *z = NULL;
00910     if (!haystack || !needle) {
00911         return NULL;
00912     }
00913     while (*y != '\0' && *x != '\0') {
00914         if (tolower(*y) == tolower(*x)) {
00915             // move y on one
00916             y++;
00917             if (!z) {
00918                 z = x; // store first position in haystack where a match is made
00919             }
00920         } else {
00921             y = needle; // reset y to the beginning of the needle
00922             z = NULL; // reset the haystack storage point
00923         }
00924         x++; // advance the search in the haystack
00925     }
00926     // If the haystack ended before our search finished, it's not a match.
00927     if (*y != '\0') return NULL;
00928     return z;
00929 }
00930 
00931 
00932 void check_filename(char *fname) {
00933     char *t = fname;
00934     DEBUG_ENT("check_filename");
00935     if (!t) {
00936         DEBUG_RET();
00937         return;
00938     }
00939     while ((t = strpbrk(t, "/\\:"))) {
00940         // while there are characters in the second string that we don't want
00941         *t = '_'; //replace them with an underscore
00942     }
00943     DEBUG_RET();
00944 }
00945 
00946 
00947 void write_separate_attachment(char f_name[], pst_item_attach* attach, int attach_num, pst_file* pst)
00948 {
00949     FILE *fp = NULL;
00950     int x = 0;
00951     char *temp = NULL;
00952 
00953     // If there is a long filename (filename2) use that, otherwise
00954     // use the 8.3 filename (filename1)
00955     char *attach_filename = (attach->filename2.str) ? attach->filename2.str
00956                                                     : attach->filename1.str;
00957     DEBUG_ENT("write_separate_attachment");
00958     DEBUG_INFO(("Attachment %s Size is %#"PRIx64", data = %#"PRIxPTR", id %#"PRIx64"\n", attach_filename, (uint64_t)attach->data.size, attach->data.data, attach->i_id));
00959 
00960     if (!attach->data.data) {
00961         // make sure we can fetch data from the id
00962         pst_index_ll *ptr = pst_getID(pst, attach->i_id);
00963         if (!ptr) {
00964             DEBUG_WARN(("Couldn't find i_id %#"PRIx64". Cannot save attachment to file\n", attach->i_id));
00965             DEBUG_RET();
00966             return;
00967         }
00968     }
00969 
00970     check_filename(f_name);
00971     if (!attach_filename) {
00972         // generate our own (dummy) filename for the attachement
00973         temp = pst_malloc(strlen(f_name)+15);
00974         sprintf(temp, "%s-attach%i", f_name, attach_num);
00975     } else {
00976         // have an attachment name, make sure it's unique
00977         temp = pst_malloc(strlen(f_name)+strlen(attach_filename)+15);
00978         do {
00979             if (fp) fclose(fp);
00980             if (x == 0)
00981                 sprintf(temp, "%s-%s", f_name, attach_filename);
00982             else
00983                 sprintf(temp, "%s-%s-%i", f_name, attach_filename, x);
00984         } while ((fp = fopen(temp, "r")) && ++x < 99999999);
00985         if (x > 99999999) {
00986             DIE(("error finding attachment name. exhausted possibilities to %s\n", temp));
00987         }
00988     }
00989     DEBUG_INFO(("Saving attachment to %s\n", temp));
00990     if (!(fp = fopen(temp, "w"))) {
00991         DEBUG_WARN(("write_separate_attachment: Cannot open attachment save file \"%s\"\n", temp));
00992     } else {
00993         (void)pst_attach_to_file(pst, attach, fp);
00994         fclose(fp);
00995     }
00996     if (temp) free(temp);
00997     DEBUG_RET();
00998 }
00999 
01000 
01001 void write_embedded_message(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pf, char** extra_mime_headers)
01002 {
01003     pst_index_ll *ptr;
01004     DEBUG_ENT("write_embedded_message");
01005     ptr = pst_getID(pf, attach->i_id);
01006 
01007     pst_desc_tree d_ptr;
01008     d_ptr.d_id        = 0;
01009     d_ptr.parent_d_id = 0;
01010     d_ptr.assoc_tree  = NULL;
01011     d_ptr.desc        = ptr;
01012     d_ptr.no_child    = 0;
01013     d_ptr.prev        = NULL;
01014     d_ptr.next        = NULL;
01015     d_ptr.parent      = NULL;
01016     d_ptr.child       = NULL;
01017     d_ptr.child_tail  = NULL;
01018 
01019     pst_item *item = pst_parse_item(pf, &d_ptr, attach->id2_head);
01020     // It appears that if the embedded message contains an appointment/
01021     // calendar item, pst_parse_item returns NULL due to the presence of
01022     // an unexpected reference type of 0x1048, which seems to represent
01023     // an array of GUIDs representing a CLSID. It's likely that this is
01024     // a reference to an internal Outlook COM class.
01025     //      Log the skipped item and continue on.
01026     if (!item) {
01027         DEBUG_WARN(("write_embedded_message: pst_parse_item was unable to parse the embedded message in attachment ID %llu", attach->i_id));
01028     } else {
01029         if (!item->email) {
01030             DEBUG_WARN(("write_embedded_message: pst_parse_item returned type %d, not an email message", item->type));
01031         } else {
01032             fprintf(f_output, "\n--%s\n", boundary);
01033             fprintf(f_output, "Content-Type: %s\n\n", attach->mimetype.str);
01034             write_normal_email(f_output, "", item, MODE_NORMAL, 0, pf, 0, extra_mime_headers);
01035         }
01036         pst_freeItem(item);
01037     }
01038 
01039     DEBUG_RET();
01040 }
01041 
01042 
01043 void write_inline_attachment(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pst)
01044 {
01045     DEBUG_ENT("write_inline_attachment");
01046     DEBUG_INFO(("Attachment Size is %#"PRIx64", data = %#"PRIxPTR", id %#"PRIx64"\n", (uint64_t)attach->data.size, attach->data.data, attach->i_id));
01047 
01048     if (!attach->data.data) {
01049         // make sure we can fetch data from the id
01050         pst_index_ll *ptr = pst_getID(pst, attach->i_id);
01051         if (!ptr) {
01052             DEBUG_WARN(("Couldn't find ID pointer. Cannot save attachment to file\n"));
01053             DEBUG_RET();
01054             return;
01055         }
01056     }
01057 
01058     fprintf(f_output, "\n--%s\n", boundary);
01059     if (!attach->mimetype.str) {
01060         fprintf(f_output, "Content-Type: %s\n", MIME_TYPE_DEFAULT);
01061     } else {
01062         fprintf(f_output, "Content-Type: %s\n", attach->mimetype.str);
01063     }
01064     fprintf(f_output, "Content-Transfer-Encoding: base64\n");
01065 
01066     if (attach->filename2.str) {
01067         // use the long filename, converted to proper encoding if needed.
01068         // it is already utf8
01069         pst_rfc2231(&attach->filename2);
01070         fprintf(f_output, "Content-Disposition: attachment; \n        filename*=%s\n\n", attach->filename2.str);
01071     }
01072     else if (attach->filename1.str) {
01073         // short filename never needs encoding
01074         fprintf(f_output, "Content-Disposition: attachment; filename=\"%s\"\n\n", attach->filename1.str);
01075     }
01076     else {
01077         // no filename is inline
01078         fprintf(f_output, "Content-Disposition: inline\n\n");
01079     }
01080 
01081     (void)pst_attach_to_file_base64(pst, attach, f_output);
01082     fprintf(f_output, "\n\n");
01083     DEBUG_RET();
01084 }
01085 
01086 
01087 void header_has_field(char *header, char *field, int *flag)
01088 {
01089     DEBUG_ENT("header_has_field");
01090     if (my_stristr(header, field) || (strncasecmp(header, field+1, strlen(field)-1) == 0)) {
01091         DEBUG_INFO(("header block has %s header\n", field+1));
01092         *flag = 1;
01093     }
01094     DEBUG_RET();
01095 }
01096 
01097 
01098 void header_get_subfield(char *field, const char *subfield, char *body_subfield, size_t size_subfield)
01099 {
01100     if (!field) return;
01101     DEBUG_ENT("header_get_subfield");
01102     char search[60];
01103     snprintf(search, sizeof(search), " %s=", subfield);
01104     field++;
01105     char *n = header_end_field(field);
01106     char *s = my_stristr(field, search);
01107     if (n && s && (s < n)) {
01108         char *e, *f, save;
01109         s += strlen(search);    // skip over subfield=
01110         if (*s == '"') {
01111             s++;
01112             e = strchr(s, '"');
01113         }
01114         else {
01115             e = strchr(s, ';');
01116             f = strchr(s, '\n');
01117             if (e && f && (f < e)) e = f;
01118         }
01119         if (!e || (e > n)) e = n;   // use the trailing lf as terminator if nothing better
01120         save = *e;
01121         *e = '\0';
01122             snprintf(body_subfield, size_subfield, "%s", s);  // copy the subfield to our buffer
01123         *e = save;
01124         DEBUG_INFO(("body %s %s from headers\n", subfield, body_subfield));
01125     }
01126     DEBUG_RET();
01127 }
01128 
01129 char* header_get_field(char *header, char *field)
01130 {
01131     char *t = my_stristr(header, field);
01132     if (!t && (strncasecmp(header, field+1, strlen(field)-1) == 0)) t = header;
01133     return t;
01134 }
01135 
01136 
01137 // return pointer to \n at the end of this header field,
01138 // or NULL if this field goes to the end of the string.
01139 char *header_end_field(char *field)
01140 {
01141     char *e = strchr(field+1, '\n');
01142     while (e && ((e[1] == ' ') || (e[1] == '\t'))) {
01143         e = strchr(e+1, '\n');
01144     }
01145     return e;
01146 }
01147 
01148 
01149 void header_strip_field(char *header, char *field)
01150 {
01151     char *t = header_get_field(header, field);
01152     if (t) {
01153         char *e = header_end_field(t);
01154         if (e) {
01155             if (t == header) e++;   // if *t is not \n, we don't want to keep the \n at *e either.
01156             while (*e != '\0') {
01157                 *t = *e;
01158                 t++;
01159                 e++;
01160             }
01161             *t = '\0';
01162         }
01163         else {
01164             // this was the last header field, truncate the headers
01165             *t = '\0';
01166         }
01167     }
01168 }
01169 
01170 
01171 int  test_base64(char *body)
01172 {
01173     int b64 = 0;
01174     uint8_t *b = (uint8_t *)body;
01175     DEBUG_ENT("test_base64");
01176     while (*b) {
01177         if ((*b < 32) && (*b != 9) && (*b != 10)) {
01178             DEBUG_INFO(("found base64 byte %d\n", (int)*b));
01179             DEBUG_HEXDUMPC(body, strlen(body), 0x10);
01180             b64 = 1;
01181             break;
01182         }
01183         b++;
01184     }
01185     DEBUG_RET();
01186     return b64;
01187 }
01188 
01189 
01190 void find_html_charset(char *html, char *charset, size_t charsetlen)
01191 {
01192     const int  index = 1;
01193     const int nmatch = index+1;
01194     regmatch_t match[nmatch];
01195     DEBUG_ENT("find_html_charset");
01196     int rc = regexec(&meta_charset_pattern, html, nmatch, match, 0);
01197     if (rc == 0) {
01198         int s = match[index].rm_so;
01199         int e = match[index].rm_eo;
01200         if (s != -1) {
01201             char save = html[e];
01202             html[e] = '\0';
01203                 snprintf(charset, charsetlen, "%s", html+s);    // copy the html charset
01204             html[e] = save;
01205             DEBUG_INFO(("charset %s from html text\n", charset));
01206         }
01207         else {
01208             DEBUG_INFO(("matching %d %d %d %d\n", match[0].rm_so, match[0].rm_eo, match[1].rm_so, match[1].rm_eo));
01209             DEBUG_HEXDUMPC(html, strlen(html), 0x10);
01210         }
01211     }
01212     else {
01213         DEBUG_INFO(("regexec returns %d\n", rc));
01214     }
01215     DEBUG_RET();
01216 }
01217 
01218 
01219 void find_rfc822_headers(char** extra_mime_headers)
01220 {
01221     DEBUG_ENT("find_rfc822_headers");
01222     char *headers = *extra_mime_headers;
01223     if (headers) {
01224         char *temp, *t;
01225         while ((temp = strstr(headers, "\n\n"))) {
01226             temp[1] = '\0';
01227             t = header_get_field(headers, "\nContent-Type: ");
01228             if (t) {
01229                 t++;
01230                 DEBUG_INFO(("found content type header\n"));
01231                 char *n = strchr(t, '\n');
01232                 char *s = strstr(t, ": ");
01233                 char *e = strchr(t, ';');
01234                 if (!e || (e > n)) e = n;
01235                 if (s && (s < e)) {
01236                     s += 2;
01237                     if (!strncasecmp(s, RFC822, e-s)) {
01238                         headers = temp+2;   // found rfc822 header
01239                         DEBUG_INFO(("found 822 headers\n%s\n", headers));
01240                         break;
01241                     }
01242                 }
01243             }
01244             //DEBUG_INFO(("skipping to next block after\n%s\n", headers));
01245             headers = temp+2;   // skip to next chunk of headers
01246         }
01247         *extra_mime_headers = headers;
01248     }
01249     DEBUG_RET();
01250 }
01251 
01252 
01253 void write_body_part(FILE* f_output, pst_string *body, char *mime, char *charset, char *boundary, pst_file* pst)
01254 {
01255     DEBUG_ENT("write_body_part");
01256     if (body->is_utf8 && (strcasecmp("utf-8", charset))) {
01257         // try to convert to the specified charset since the target
01258         // is not utf-8, and the data came from a unicode (utf16) field
01259         // and is now in utf-8.
01260         size_t rc;
01261         DEBUG_INFO(("Convert %s utf-8 to %s\n", mime, charset));
01262         pst_vbuf *newer = pst_vballoc(2);
01263         rc = pst_vb_utf8to8bit(newer, body->str, strlen(body->str), charset);
01264         if (rc == (size_t)-1) {
01265             // unable to convert, change the charset to utf8
01266             free(newer->b);
01267             DEBUG_INFO(("Failed to convert %s utf-8 to %s\n", mime, charset));
01268             charset = "utf-8";
01269         }
01270         else {
01271             // null terminate the output string
01272             pst_vbgrow(newer, 1);
01273             newer->b[newer->dlen] = '\0';
01274             free(body->str);
01275             body->str = newer->b;
01276         }
01277         free(newer);
01278     }
01279     removeCR(body->str);
01280     int base64 = test_base64(body->str);
01281     fprintf(f_output, "\n--%s\n", boundary);
01282     fprintf(f_output, "Content-Type: %s; charset=\"%s\"\n", mime, charset);
01283     if (base64) fprintf(f_output, "Content-Transfer-Encoding: base64\n");
01284     fprintf(f_output, "\n");
01285     if (base64) {
01286         char *enc = pst_base64_encode(body->str, strlen(body->str));
01287         if (enc) {
01288             write_email_body(f_output, enc);
01289             fprintf(f_output, "\n");
01290             free(enc);
01291         }
01292     }
01293     else {
01294         write_email_body(f_output, body->str);
01295     }
01296     DEBUG_RET();
01297 }
01298 
01299 
01300 void write_schedule_part_data(FILE* f_output, pst_item* item, const char* sender, const char* method)
01301 {
01302     fprintf(f_output, "BEGIN:VCALENDAR\n");
01303     fprintf(f_output, "VERSION:2.0\n");
01304     fprintf(f_output, "PRODID:LibPST v%s\n", VERSION);
01305     if (method) fprintf(f_output, "METHOD:%s\n", method);
01306     fprintf(f_output, "BEGIN:VEVENT\n");
01307     if (sender) {
01308         if (item->email->outlook_sender_name.str) {
01309         fprintf(f_output, "ORGANIZER;CN=\"%s\":MAILTO:%s\n", item->email->outlook_sender_name.str, sender);
01310     } else {
01311         fprintf(f_output, "ORGANIZER;CN=\"\":MAILTO:%s\n", sender);
01312     }
01313     }
01314     write_appointment(f_output, item);
01315     fprintf(f_output, "END:VCALENDAR\n");
01316 }
01317 
01318 
01319 void write_schedule_part(FILE* f_output, pst_item* item, const char* sender, const char* boundary)
01320 {
01321     const char* method  = "REQUEST";
01322     const char* charset = "utf-8";
01323     char fname[30];
01324     if (!item->appointment) return;
01325 
01326     // inline appointment request
01327     fprintf(f_output, "\n--%s\n", boundary);
01328     fprintf(f_output, "Content-Type: %s; method=\"%s\"; charset=\"%s\"\n\n", "text/calendar", method, charset);
01329     write_schedule_part_data(f_output, item, sender, method);
01330     fprintf(f_output, "\n");
01331 
01332     // attachment appointment request
01333     snprintf(fname, sizeof(fname), "i%i.ics", rand());
01334     fprintf(f_output, "\n--%s\n", boundary);
01335     fprintf(f_output, "Content-Type: %s; charset=\"%s\"; name=\"%s\"\n", "text/calendar", "utf-8", fname);
01336     fprintf(f_output, "Content-Disposition: attachment; filename=\"%s\"\n\n", fname);
01337     write_schedule_part_data(f_output, item, sender, method);
01338     fprintf(f_output, "\n");
01339 }
01340 
01341 
01342 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)
01343 {
01344     char boundary[60];
01345     char altboundary[66];
01346     char *altboundaryp = NULL;
01347     char body_charset[30];
01348     char buffer_charset[30];
01349     char body_report[60];
01350     char sender[60];
01351     int  sender_known = 0;
01352     char *temp = NULL;
01353     time_t em_time;
01354     char *c_time;
01355     char *headers = NULL;
01356     int has_from, has_subject, has_to, has_cc, has_date, has_msgid;
01357     has_from = has_subject = has_to = has_cc = has_date = has_msgid = 0;
01358     DEBUG_ENT("write_normal_email");
01359 
01360     pst_convert_utf8_null(item, &item->email->header);
01361     headers = (item->email->header.str) ? item->email->header.str : *extra_mime_headers;
01362 
01363     // setup default body character set and report type
01364     strncpy(body_charset, pst_default_charset(item, sizeof(buffer_charset), buffer_charset), sizeof(body_charset));
01365     body_charset[sizeof(body_charset)-1] = '\0';
01366     strncpy(body_report, "delivery-status", sizeof(body_report));
01367     body_report[sizeof(body_report)-1] = '\0';
01368 
01369     // setup default sender
01370     pst_convert_utf8(item, &item->email->sender_address);
01371     if (item->email->sender_address.str && strchr(item->email->sender_address.str, '@')) {
01372         temp = item->email->sender_address.str;
01373         sender_known = 1;
01374     }
01375     else {
01376         temp = "MAILER-DAEMON";
01377     }
01378     strncpy(sender, temp, sizeof(sender));
01379     sender[sizeof(sender)-1] = '\0';
01380 
01381     // convert the sent date if it exists, or set it to a fixed date
01382     if (item->email->sent_date) {
01383         em_time = pst_fileTimeToUnixTime(item->email->sent_date);
01384         c_time = ctime(&em_time);
01385         if (c_time)
01386             c_time[strlen(c_time)-1] = '\0'; //remove end \n
01387         else
01388             c_time = "Fri Dec 28 12:06:21 2001";
01389     } else
01390         c_time= "Fri Dec 28 12:06:21 2001";
01391 
01392     // create our MIME boundaries here.
01393     snprintf(boundary, sizeof(boundary), "--boundary-LibPST-iamunique-%i_-_-", rand());
01394     snprintf(altboundary, sizeof(altboundary), "alt-%s", boundary);
01395 
01396     // we will always look at the headers to discover some stuff
01397     if (headers ) {
01398         char *t;
01399         removeCR(headers);
01400 
01401         temp = strstr(headers, "\n\n");
01402         if (temp) {
01403             // cut off our real rfc822 headers here
01404             temp[1] = '\0';
01405             // pointer to all the embedded MIME headers.
01406             // we use these to find the actual rfc822 headers for embedded message/rfc822 mime parts
01407             *extra_mime_headers = temp+2;
01408             DEBUG_INFO(("Found extra mime headers\n%s\n", temp+2));
01409         }
01410 
01411         // Check if the headers have all the necessary fields
01412         header_has_field(headers, "\nFrom: ",        &has_from);
01413         header_has_field(headers, "\nTo: ",          &has_to);
01414         header_has_field(headers, "\nSubject: ",     &has_subject);
01415         header_has_field(headers, "\nDate: ",        &has_date);
01416         header_has_field(headers, "\nCC: ",          &has_cc);
01417         header_has_field(headers, "\nMessage-Id: ",  &has_msgid);
01418 
01419         // look for charset and report-type in Content-Type header
01420         t = header_get_field(headers, "\nContent-Type: ");
01421         header_get_subfield(t, "charset", body_charset, sizeof(body_charset));
01422         header_get_subfield(t, "report-type", body_report, sizeof(body_report));
01423 
01424         // derive a proper sender email address
01425         if (!sender_known) {
01426             t = header_get_field(headers, "\nFrom: ");
01427             if (t) {
01428                 // assume address is on the first line, rather than on a continuation line
01429                 t++;
01430                 char *n = strchr(t, '\n');
01431                 char *s = strchr(t, '<');
01432                 char *e = strchr(t, '>');
01433                 if (s && e && n && (s < e) && (e < n)) {
01434                 char save = *e;
01435                 *e = '\0';
01436                     snprintf(sender, sizeof(sender), "%s", s+1);
01437                 *e = save;
01438                 }
01439             }
01440         }
01441 
01442         // Strip out the mime headers and some others that we don't want to emit
01443         header_strip_field(headers, "\nMicrosoft Mail Internet Headers");
01444         header_strip_field(headers, "\nMIME-Version: ");
01445         header_strip_field(headers, "\nContent-Type: ");
01446         header_strip_field(headers, "\nContent-Transfer-Encoding: ");
01447         header_strip_field(headers, "\nContent-class: ");
01448         header_strip_field(headers, "\nX-MimeOLE: ");
01449         header_strip_field(headers, "\nBcc:");
01450         header_strip_field(headers, "\nX-From_: ");
01451     }
01452 
01453     DEBUG_INFO(("About to print Header\n"));
01454 
01455     if (item && item->subject.str) {
01456         pst_convert_utf8(item, &item->subject);
01457         DEBUG_INFO(("item->subject = %s\n", item->subject.str));
01458     }
01459 
01460     if (mode != MODE_SEPARATE) {
01461         // most modes need this separator line.
01462         // procmail produces this separator without the quotes around the
01463         // sender email address, but apparently some Mac email client needs
01464         // those quotes, and they don't seem to cause problems for anyone else.
01465         fprintf(f_output, "From \"%s\" %s\n", sender, c_time);
01466     }
01467 
01468     // print the supplied email headers
01469     if (headers) {
01470         int len = strlen(headers);
01471         if (len > 0) {
01472             fprintf(f_output, "%s", headers);
01473             // make sure the headers end with a \n
01474             if (headers[len-1] != '\n') fprintf(f_output, "\n");
01475             //char *h = headers;
01476             //while (*h) {
01477             //    char *e = strchr(h, '\n');
01478             //    int   d = 1;    // normally e points to trailing \n
01479             //    if (!e) {
01480             //        e = h + strlen(h);  // e points to trailing null
01481             //        d = 0;
01482             //    }
01483             //    // we could do rfc2047 encoding here if needed
01484             //    fprintf(f_output, "%.*s\n", (int)(e-h), h);
01485             //    h = e + d;
01486             //}
01487         }
01488     }
01489 
01490     // create required header fields that are not already written
01491 
01492     if (!has_from) {
01493         if (item->email->outlook_sender_name.str){
01494             pst_rfc2047(item, &item->email->outlook_sender_name, 1);
01495             fprintf(f_output, "From: %s <%s>\n", item->email->outlook_sender_name.str, sender);
01496         } else {
01497             fprintf(f_output, "From: <%s>\n", sender);
01498         }
01499     }
01500 
01501     if (!has_subject) {
01502         if (item->subject.str) {
01503             pst_rfc2047(item, &item->subject, 0);
01504             fprintf(f_output, "Subject: %s\n", item->subject.str);
01505         } else {
01506             fprintf(f_output, "Subject: \n");
01507         }
01508     }
01509 
01510     if (!has_to && item->email->sentto_address.str) {
01511         pst_rfc2047(item, &item->email->sentto_address, 0);
01512         fprintf(f_output, "To: %s\n", item->email->sentto_address.str);
01513     }
01514 
01515     if (!has_cc && item->email->cc_address.str) {
01516         pst_rfc2047(item, &item->email->cc_address, 0);
01517         fprintf(f_output, "Cc: %s\n", item->email->cc_address.str);
01518     }
01519 
01520     if (!has_date && item->email->sent_date) {
01521         char c_time[C_TIME_SIZE];
01522         struct tm stm;
01523         gmtime_r(&em_time, &stm);
01524         strftime(c_time, C_TIME_SIZE, "%a, %d %b %Y %H:%M:%S %z", &stm);
01525         fprintf(f_output, "Date: %s\n", c_time);
01526     }
01527 
01528     if (!has_msgid && item->email->messageid.str) {
01529         pst_convert_utf8(item, &item->email->messageid);
01530         fprintf(f_output, "Message-Id: %s\n", item->email->messageid.str);
01531     }
01532 
01533     // add forensic headers to capture some .pst stuff that is not really
01534     // needed or used by mail clients
01535     pst_convert_utf8_null(item, &item->email->sender_address);
01536     if (item->email->sender_address.str && !strchr(item->email->sender_address.str, '@')
01537                                         && strcmp(item->email->sender_address.str, ".")
01538                                         && (strlen(item->email->sender_address.str) > 0)) {
01539         fprintf(f_output, "X-libpst-forensic-sender: %s\n", item->email->sender_address.str);
01540     }
01541 
01542     if (item->email->bcc_address.str) {
01543         pst_convert_utf8(item, &item->email->bcc_address);
01544         fprintf(f_output, "X-libpst-forensic-bcc: %s\n", item->email->bcc_address.str);
01545     }
01546 
01547     // add our own mime headers
01548     fprintf(f_output, "MIME-Version: 1.0\n");
01549     if (item->type == PST_TYPE_REPORT) {
01550         // multipart/report for DSN/MDN reports
01551         fprintf(f_output, "Content-Type: multipart/report; report-type=%s;\n\tboundary=\"%s\"\n", body_report, boundary);
01552     }
01553     else {
01554         fprintf(f_output, "Content-Type: multipart/mixed;\n\tboundary=\"%s\"\n", boundary);
01555     }
01556     fprintf(f_output, "\n");    // end of headers, start of body
01557 
01558     // now dump the body parts
01559     if ((item->type == PST_TYPE_REPORT) && (item->email->report_text.str)) {
01560         write_body_part(f_output, &item->email->report_text, "text/plain", body_charset, boundary, pst);
01561         fprintf(f_output, "\n");
01562     }
01563 
01564     if (item->body.str && item->email->htmlbody.str) {
01565         // start the nested alternative part
01566         fprintf(f_output, "\n--%s\n", boundary);
01567         fprintf(f_output, "Content-Type: multipart/alternative;\n\tboundary=\"%s\"\n", altboundary);
01568         altboundaryp = altboundary;
01569     }
01570     else {
01571         altboundaryp = boundary;
01572     }
01573 
01574     if (item->body.str) {
01575         write_body_part(f_output, &item->body, "text/plain", body_charset, altboundaryp, pst);
01576     }
01577 
01578     if (item->email->htmlbody.str) {
01579         find_html_charset(item->email->htmlbody.str, body_charset, sizeof(body_charset));
01580         write_body_part(f_output, &item->email->htmlbody, "text/html", body_charset, altboundaryp, pst);
01581     }
01582 
01583     if (item->body.str && item->email->htmlbody.str) {
01584         // end the nested alternative part
01585         fprintf(f_output, "\n--%s--\n", altboundary);
01586     }
01587 
01588     if (item->email->rtf_compressed.data && save_rtf) {
01589         pst_item_attach* attach = (pst_item_attach*)pst_malloc(sizeof(pst_item_attach));
01590         DEBUG_INFO(("Adding RTF body as attachment\n"));
01591         memset(attach, 0, sizeof(pst_item_attach));
01592         attach->next = item->attach;
01593         item->attach = attach;
01594         attach->data.data         = pst_lzfu_decompress(item->email->rtf_compressed.data, item->email->rtf_compressed.size, &attach->data.size);
01595         attach->filename2.str     = strdup(RTF_ATTACH_NAME);
01596         attach->filename2.is_utf8 = 1;
01597         attach->mimetype.str      = strdup(RTF_ATTACH_TYPE);
01598         attach->mimetype.is_utf8  = 1;
01599     }
01600 
01601     if (item->email->encrypted_body.data) {
01602         pst_item_attach* attach = (pst_item_attach*)pst_malloc(sizeof(pst_item_attach));
01603         DEBUG_INFO(("Adding encrypted text body as attachment\n"));
01604         attach = (pst_item_attach*) pst_malloc(sizeof(pst_item_attach));
01605         memset(attach, 0, sizeof(pst_item_attach));
01606         attach->next = item->attach;
01607         item->attach = attach;
01608         attach->data.data = item->email->encrypted_body.data;
01609         attach->data.size = item->email->encrypted_body.size;
01610         item->email->encrypted_body.data = NULL;
01611     }
01612 
01613     if (item->email->encrypted_htmlbody.data) {
01614         pst_item_attach* attach = (pst_item_attach*)pst_malloc(sizeof(pst_item_attach));
01615         DEBUG_INFO(("Adding encrypted HTML body as attachment\n"));
01616         attach = (pst_item_attach*) pst_malloc(sizeof(pst_item_attach));
01617         memset(attach, 0, sizeof(pst_item_attach));
01618         attach->next = item->attach;
01619         item->attach = attach;
01620         attach->data.data = item->email->encrypted_htmlbody.data;
01621         attach->data.size = item->email->encrypted_htmlbody.size;
01622         item->email->encrypted_htmlbody.data = NULL;
01623     }
01624 
01625     if (item->type == PST_TYPE_SCHEDULE) {
01626         write_schedule_part(f_output, item, sender, boundary);
01627     }
01628 
01629     // other attachments
01630     {
01631         pst_item_attach* attach;
01632         int attach_num = 0;
01633         for (attach = item->attach; attach; attach = attach->next) {
01634             pst_convert_utf8_null(item, &attach->filename1);
01635             pst_convert_utf8_null(item, &attach->filename2);
01636             pst_convert_utf8_null(item, &attach->mimetype);
01637             DEBUG_INFO(("Attempting Attachment encoding\n"));
01638             if (attach->method == PST_ATTACH_EMBEDDED) {
01639                 DEBUG_INFO(("have an embedded rfc822 message attachment\n"));
01640                 if (attach->mimetype.str) {
01641                     DEBUG_INFO(("which already has a mime-type of %s\n", attach->mimetype.str));
01642                     free(attach->mimetype.str);
01643                 }
01644                 attach->mimetype.str = strdup(RFC822);
01645                 attach->mimetype.is_utf8 = 1;
01646                 find_rfc822_headers(extra_mime_headers);
01647                 write_embedded_message(f_output, attach, boundary, pst, extra_mime_headers);
01648             }
01649             else if (attach->data.data || attach->i_id) {
01650                 if (mode == MODE_SEPARATE && !mode_MH)
01651                     write_separate_attachment(f_name, attach, ++attach_num, pst);
01652                 else
01653                     write_inline_attachment(f_output, attach, boundary, pst);
01654             }
01655         }
01656     }
01657 
01658     fprintf(f_output, "\n--%s--\n\n", boundary);
01659     DEBUG_RET();
01660 }
01661 
01662 
01663 void write_vcard(FILE* f_output, pst_item* item, pst_item_contact* contact, char comment[])
01664 {
01665     char*  result = NULL;
01666     size_t resultlen = 0;
01667     char   time_buffer[30];
01668     // We can only call rfc escape once per printf, since the second call
01669     // may free the buffer returned by the first call.
01670     // I had tried to place those into a single printf - Carl.
01671 
01672     DEBUG_ENT("write_vcard");
01673 
01674     // make everything utf8
01675     pst_convert_utf8_null(item, &contact->fullname);
01676     pst_convert_utf8_null(item, &contact->surname);
01677     pst_convert_utf8_null(item, &contact->first_name);
01678     pst_convert_utf8_null(item, &contact->middle_name);
01679     pst_convert_utf8_null(item, &contact->display_name_prefix);
01680     pst_convert_utf8_null(item, &contact->suffix);
01681     pst_convert_utf8_null(item, &contact->nickname);
01682     pst_convert_utf8_null(item, &contact->address1);
01683     pst_convert_utf8_null(item, &contact->address2);
01684     pst_convert_utf8_null(item, &contact->address3);
01685     pst_convert_utf8_null(item, &contact->home_po_box);
01686     pst_convert_utf8_null(item, &contact->home_street);
01687     pst_convert_utf8_null(item, &contact->home_city);
01688     pst_convert_utf8_null(item, &contact->home_state);
01689     pst_convert_utf8_null(item, &contact->home_postal_code);
01690     pst_convert_utf8_null(item, &contact->home_country);
01691     pst_convert_utf8_null(item, &contact->home_address);
01692     pst_convert_utf8_null(item, &contact->business_po_box);
01693     pst_convert_utf8_null(item, &contact->business_street);
01694     pst_convert_utf8_null(item, &contact->business_city);
01695     pst_convert_utf8_null(item, &contact->business_state);
01696     pst_convert_utf8_null(item, &contact->business_postal_code);
01697     pst_convert_utf8_null(item, &contact->business_country);
01698     pst_convert_utf8_null(item, &contact->business_address);
01699     pst_convert_utf8_null(item, &contact->other_po_box);
01700     pst_convert_utf8_null(item, &contact->other_street);
01701     pst_convert_utf8_null(item, &contact->other_city);
01702     pst_convert_utf8_null(item, &contact->other_state);
01703     pst_convert_utf8_null(item, &contact->other_postal_code);
01704     pst_convert_utf8_null(item, &contact->other_country);
01705     pst_convert_utf8_null(item, &contact->other_address);
01706     pst_convert_utf8_null(item, &contact->business_fax);
01707     pst_convert_utf8_null(item, &contact->business_phone);
01708     pst_convert_utf8_null(item, &contact->business_phone2);
01709     pst_convert_utf8_null(item, &contact->car_phone);
01710     pst_convert_utf8_null(item, &contact->home_fax);
01711     pst_convert_utf8_null(item, &contact->home_phone);
01712     pst_convert_utf8_null(item, &contact->home_phone2);
01713     pst_convert_utf8_null(item, &contact->isdn_phone);
01714     pst_convert_utf8_null(item, &contact->mobile_phone);
01715     pst_convert_utf8_null(item, &contact->other_phone);
01716     pst_convert_utf8_null(item, &contact->pager_phone);
01717     pst_convert_utf8_null(item, &contact->primary_fax);
01718     pst_convert_utf8_null(item, &contact->primary_phone);
01719     pst_convert_utf8_null(item, &contact->radio_phone);
01720     pst_convert_utf8_null(item, &contact->telex);
01721     pst_convert_utf8_null(item, &contact->job_title);
01722     pst_convert_utf8_null(item, &contact->profession);
01723     pst_convert_utf8_null(item, &contact->assistant_name);
01724     pst_convert_utf8_null(item, &contact->assistant_phone);
01725     pst_convert_utf8_null(item, &contact->company_name);
01726     pst_convert_utf8_null(item, &item->body);
01727 
01728     // the specification I am following is (hopefully) RFC2426 vCard Mime Directory Profile
01729     fprintf(f_output, "BEGIN:VCARD\n");
01730     fprintf(f_output, "FN:%s\n", pst_rfc2426_escape(contact->fullname.str, &result, &resultlen));
01731 
01732     //fprintf(f_output, "N:%s;%s;%s;%s;%s\n",
01733     fprintf(f_output, "N:%s;", (!contact->surname.str)             ? "" : pst_rfc2426_escape(contact->surname.str, &result, &resultlen));
01734     fprintf(f_output, "%s;",   (!contact->first_name.str)          ? "" : pst_rfc2426_escape(contact->first_name.str, &result, &resultlen));
01735     fprintf(f_output, "%s;",   (!contact->middle_name.str)         ? "" : pst_rfc2426_escape(contact->middle_name.str, &result, &resultlen));
01736     fprintf(f_output, "%s;",   (!contact->display_name_prefix.str) ? "" : pst_rfc2426_escape(contact->display_name_prefix.str, &result, &resultlen));
01737     fprintf(f_output, "%s\n",  (!contact->suffix.str)              ? "" : pst_rfc2426_escape(contact->suffix.str, &result, &resultlen));
01738 
01739     if (contact->nickname.str)
01740         fprintf(f_output, "NICKNAME:%s\n", pst_rfc2426_escape(contact->nickname.str, &result, &resultlen));
01741     if (contact->address1.str)
01742         fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address1.str, &result, &resultlen));
01743     if (contact->address2.str)
01744         fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address2.str, &result, &resultlen));
01745     if (contact->address3.str)
01746         fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address3.str, &result, &resultlen));
01747     if (contact->birthday)
01748         fprintf(f_output, "BDAY:%s\n", pst_rfc2425_datetime_format(contact->birthday, sizeof(time_buffer), time_buffer));
01749 
01750     if (contact->home_address.str) {
01751         //fprintf(f_output, "ADR;TYPE=home:%s;%s;%s;%s;%s;%s;%s\n",
01752         fprintf(f_output, "ADR;TYPE=home:%s;",  (!contact->home_po_box.str)      ? "" : pst_rfc2426_escape(contact->home_po_box.str, &result, &resultlen));
01753         fprintf(f_output, "%s;",                ""); // extended Address
01754         fprintf(f_output, "%s;",                (!contact->home_street.str)      ? "" : pst_rfc2426_escape(contact->home_street.str, &result, &resultlen));
01755         fprintf(f_output, "%s;",                (!contact->home_city.str)        ? "" : pst_rfc2426_escape(contact->home_city.str, &result, &resultlen));
01756         fprintf(f_output, "%s;",                (!contact->home_state.str)       ? "" : pst_rfc2426_escape(contact->home_state.str, &result, &resultlen));
01757         fprintf(f_output, "%s;",                (!contact->home_postal_code.str) ? "" : pst_rfc2426_escape(contact->home_postal_code.str, &result, &resultlen));
01758         fprintf(f_output, "%s\n",               (!contact->home_country.str)     ? "" : pst_rfc2426_escape(contact->home_country.str, &result, &resultlen));
01759         fprintf(f_output, "LABEL;TYPE=home:%s\n", pst_rfc2426_escape(contact->home_address.str, &result, &resultlen));
01760     }
01761 
01762     if (contact->business_address.str) {
01763         //fprintf(f_output, "ADR;TYPE=work:%s;%s;%s;%s;%s;%s;%s\n",
01764         fprintf(f_output, "ADR;TYPE=work:%s;",  (!contact->business_po_box.str)      ? "" : pst_rfc2426_escape(contact->business_po_box.str, &result, &resultlen));
01765         fprintf(f_output, "%s;",                ""); // extended Address
01766         fprintf(f_output, "%s;",                (!contact->business_street.str)      ? "" : pst_rfc2426_escape(contact->business_street.str, &result, &resultlen));
01767         fprintf(f_output, "%s;",                (!contact->business_city.str)        ? "" : pst_rfc2426_escape(contact->business_city.str, &result, &resultlen));
01768         fprintf(f_output, "%s;",                (!contact->business_state.str)       ? "" : pst_rfc2426_escape(contact->business_state.str, &result, &resultlen));
01769         fprintf(f_output, "%s;",                (!contact->business_postal_code.str) ? "" : pst_rfc2426_escape(contact->business_postal_code.str, &result, &resultlen));
01770         fprintf(f_output, "%s\n",               (!contact->business_country.str)     ? "" : pst_rfc2426_escape(contact->business_country.str, &result, &resultlen));
01771         fprintf(f_output, "LABEL;TYPE=work:%s\n", pst_rfc2426_escape(contact->business_address.str, &result, &resultlen));
01772     }
01773 
01774     if (contact->other_address.str) {
01775         //fprintf(f_output, "ADR;TYPE=postal:%s;%s;%s;%s;%s;%s;%s\n",
01776         fprintf(f_output, "ADR;TYPE=postal:%s;",(!contact->other_po_box.str)       ? "" : pst_rfc2426_escape(contact->other_po_box.str, &result, &resultlen));
01777         fprintf(f_output, "%s;",                ""); // extended Address
01778         fprintf(f_output, "%s;",                (!contact->other_street.str)       ? "" : pst_rfc2426_escape(contact->other_street.str, &result, &resultlen));
01779         fprintf(f_output, "%s;",                (!contact->other_city.str)         ? "" : pst_rfc2426_escape(contact->other_city.str, &result, &resultlen));
01780         fprintf(f_output, "%s;",                (!contact->other_state.str)        ? "" : pst_rfc2426_escape(contact->other_state.str, &result, &resultlen));
01781         fprintf(f_output, "%s;",                (!contact->other_postal_code.str)  ? "" : pst_rfc2426_escape(contact->other_postal_code.str, &result, &resultlen));
01782         fprintf(f_output, "%s\n",               (!contact->other_country.str)      ? "" : pst_rfc2426_escape(contact->other_country.str, &result, &resultlen));
01783         fprintf(f_output, "LABEL;TYPE=postal:%s\n", pst_rfc2426_escape(contact->other_address.str, &result, &resultlen));
01784     }
01785 
01786     if (contact->business_fax.str)      fprintf(f_output, "TEL;TYPE=work,fax:%s\n",         pst_rfc2426_escape(contact->business_fax.str, &result, &resultlen));
01787     if (contact->business_phone.str)    fprintf(f_output, "TEL;TYPE=work,voice:%s\n",       pst_rfc2426_escape(contact->business_phone.str, &result, &resultlen));
01788     if (contact->business_phone2.str)   fprintf(f_output, "TEL;TYPE=work,voice:%s\n",       pst_rfc2426_escape(contact->business_phone2.str, &result, &resultlen));
01789     if (contact->car_phone.str)         fprintf(f_output, "TEL;TYPE=car,voice:%s\n",        pst_rfc2426_escape(contact->car_phone.str, &result, &resultlen));
01790     if (contact->home_fax.str)          fprintf(f_output, "TEL;TYPE=home,fax:%s\n",         pst_rfc2426_escape(contact->home_fax.str, &result, &resultlen));
01791     if (contact->home_phone.str)        fprintf(f_output, "TEL;TYPE=home,voice:%s\n",       pst_rfc2426_escape(contact->home_phone.str, &result, &resultlen));
01792     if (contact->home_phone2.str)       fprintf(f_output, "TEL;TYPE=home,voice:%s\n",       pst_rfc2426_escape(contact->home_phone2.str, &result, &resultlen));
01793     if (contact->isdn_phone.str)        fprintf(f_output, "TEL;TYPE=isdn:%s\n",             pst_rfc2426_escape(contact->isdn_phone.str, &result, &resultlen));
01794     if (contact->mobile_phone.str)      fprintf(f_output, "TEL;TYPE=cell,voice:%s\n",       pst_rfc2426_escape(contact->mobile_phone.str, &result, &resultlen));
01795     if (contact->other_phone.str)       fprintf(f_output, "TEL;TYPE=msg:%s\n",              pst_rfc2426_escape(contact->other_phone.str, &result, &resultlen));
01796     if (contact->pager_phone.str)       fprintf(f_output, "TEL;TYPE=pager:%s\n",            pst_rfc2426_escape(contact->pager_phone.str, &result, &resultlen));
01797     if (contact->primary_fax.str)       fprintf(f_output, "TEL;TYPE=fax,pref:%s\n",         pst_rfc2426_escape(contact->primary_fax.str, &result, &resultlen));
01798     if (contact->primary_phone.str)     fprintf(f_output, "TEL;TYPE=phone,pref:%s\n",       pst_rfc2426_escape(contact->primary_phone.str, &result, &resultlen));
01799     if (contact->radio_phone.str)       fprintf(f_output, "TEL;TYPE=pcs:%s\n",              pst_rfc2426_escape(contact->radio_phone.str, &result, &resultlen));
01800     if (contact->telex.str)             fprintf(f_output, "TEL;TYPE=bbs:%s\n",              pst_rfc2426_escape(contact->telex.str, &result, &resultlen));
01801     if (contact->job_title.str)         fprintf(f_output, "TITLE:%s\n",                     pst_rfc2426_escape(contact->job_title.str, &result, &resultlen));
01802     if (contact->profession.str)        fprintf(f_output, "ROLE:%s\n",                      pst_rfc2426_escape(contact->profession.str, &result, &resultlen));
01803     if (contact->assistant_name.str || contact->assistant_phone.str) {
01804         fprintf(f_output, "AGENT:BEGIN:VCARD\n");
01805         if (contact->assistant_name.str)    fprintf(f_output, "FN:%s\n",                    pst_rfc2426_escape(contact->assistant_name.str, &result, &resultlen));
01806         if (contact->assistant_phone.str)   fprintf(f_output, "TEL:%s\n",                   pst_rfc2426_escape(contact->assistant_phone.str, &result, &resultlen));
01807     }
01808     if (contact->company_name.str)      fprintf(f_output, "ORG:%s\n",                       pst_rfc2426_escape(contact->company_name.str, &result, &resultlen));
01809     if (comment)                        fprintf(f_output, "NOTE:%s\n",                      pst_rfc2426_escape(comment, &result, &resultlen));
01810     if (item->body.str)                 fprintf(f_output, "NOTE:%s\n",                      pst_rfc2426_escape(item->body.str, &result, &resultlen));
01811 
01812     write_extra_categories(f_output, item);
01813 
01814     fprintf(f_output, "VERSION: 3.0\n");
01815     fprintf(f_output, "END:VCARD\n\n");
01816     if (result) free(result);
01817     DEBUG_RET();
01818 }
01819 
01820 
01828 int write_extra_categories(FILE* f_output, pst_item* item)
01829 {
01830     char*  result = NULL;
01831     size_t resultlen = 0;
01832     pst_item_extra_field *ef = item->extra_fields;
01833     const char *fmt = "CATEGORIES:%s";
01834     int category_started = 0;
01835     while (ef) {
01836         if (strcmp(ef->field_name, "Keywords") == 0) {
01837             fprintf(f_output, fmt, pst_rfc2426_escape(ef->value, &result, &resultlen));
01838             fmt = ", %s";
01839             category_started = 1;
01840         }
01841         ef = ef->next;
01842     }
01843     if (category_started) fprintf(f_output, "\n");
01844     if (result) free(result);
01845     return category_started;
01846 }
01847 
01848 
01849 void write_journal(FILE* f_output, pst_item* item)
01850 {
01851     char*  result = NULL;
01852     size_t resultlen = 0;
01853     char   time_buffer[30];
01854     pst_item_journal* journal = item->journal;
01855 
01856     // make everything utf8
01857     pst_convert_utf8_null(item, &item->subject);
01858     pst_convert_utf8_null(item, &item->body);
01859 
01860     fprintf(f_output, "BEGIN:VJOURNAL\n");
01861     fprintf(f_output, "DTSTAMP:%s\n",                     pst_rfc2445_datetime_format_now(sizeof(time_buffer), time_buffer));
01862     if (item->create_date)
01863         fprintf(f_output, "CREATED:%s\n",                 pst_rfc2445_datetime_format(item->create_date, sizeof(time_buffer), time_buffer));
01864     if (item->modify_date)
01865         fprintf(f_output, "LAST-MOD:%s\n",                pst_rfc2445_datetime_format(item->modify_date, sizeof(time_buffer), time_buffer));
01866     if (item->subject.str)
01867         fprintf(f_output, "SUMMARY:%s\n",                 pst_rfc2426_escape(item->subject.str, &result, &resultlen));
01868     if (item->body.str)
01869         fprintf(f_output, "DESCRIPTION:%s\n",             pst_rfc2426_escape(item->body.str, &result, &resultlen));
01870     if (journal && journal->start)
01871         fprintf(f_output, "DTSTART;VALUE=DATE-TIME:%s\n", pst_rfc2445_datetime_format(journal->start, sizeof(time_buffer), time_buffer));
01872     fprintf(f_output, "END:VJOURNAL\n");
01873     if (result) free(result);
01874 }
01875 
01876 
01877 void write_appointment(FILE* f_output, pst_item* item)
01878 {
01879     char*  result = NULL;
01880     size_t resultlen = 0;
01881     char   time_buffer[30];
01882     pst_item_appointment* appointment = item->appointment;
01883 
01884     // make everything utf8
01885     pst_convert_utf8_null(item, &item->subject);
01886     pst_convert_utf8_null(item, &item->body);
01887     pst_convert_utf8_null(item, &appointment->location);
01888 
01889     fprintf(f_output, "DTSTAMP:%s\n",                     pst_rfc2445_datetime_format_now(sizeof(time_buffer), time_buffer));
01890     if (item->create_date)
01891         fprintf(f_output, "CREATED:%s\n",                 pst_rfc2445_datetime_format(item->create_date, sizeof(time_buffer), time_buffer));
01892     if (item->modify_date)
01893         fprintf(f_output, "LAST-MOD:%s\n",                pst_rfc2445_datetime_format(item->modify_date, sizeof(time_buffer), time_buffer));
01894     if (item->subject.str)
01895         fprintf(f_output, "SUMMARY:%s\n",                 pst_rfc2426_escape(item->subject.str, &result, &resultlen));
01896     if (item->body.str)
01897         fprintf(f_output, "DESCRIPTION:%s\n",             pst_rfc2426_escape(item->body.str, &result, &resultlen));
01898     if (appointment && appointment->start)
01899         fprintf(f_output, "DTSTART;VALUE=DATE-TIME:%s\n", pst_rfc2445_datetime_format(appointment->start, sizeof(time_buffer), time_buffer));
01900     if (appointment && appointment->end)
01901         fprintf(f_output, "DTEND;VALUE=DATE-TIME:%s\n",   pst_rfc2445_datetime_format(appointment->end, sizeof(time_buffer), time_buffer));
01902     if (appointment && appointment->location.str)
01903         fprintf(f_output, "LOCATION:%s\n",                pst_rfc2426_escape(appointment->location.str, &result, &resultlen));
01904     if (appointment) {
01905         switch (appointment->showas) {
01906             case PST_FREEBUSY_TENTATIVE:
01907                 fprintf(f_output, "STATUS:TENTATIVE\n");
01908                 break;
01909             case PST_FREEBUSY_FREE:
01910                 // mark as transparent and as confirmed
01911                 fprintf(f_output, "TRANSP:TRANSPARENT\n");
01912             case PST_FREEBUSY_BUSY:
01913             case PST_FREEBUSY_OUT_OF_OFFICE:
01914                 fprintf(f_output, "STATUS:CONFIRMED\n");
01915                 break;
01916         }
01917         if (appointment->is_recurring) {
01918             const char* rules[] = {"DAILY", "WEEKLY", "MONTHLY", "YEARLY"};
01919             const char* days[]  = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
01920             pst_recurrence *rdata = pst_convert_recurrence(appointment);
01921             fprintf(f_output, "RRULE:FREQ=%s", rules[rdata->type]);
01922             if (rdata->count)       fprintf(f_output, ";COUNT=%u",      rdata->count);
01923             if ((rdata->interval != 1) &&
01924                 (rdata->interval))  fprintf(f_output, ";INTERVAL=%u",   rdata->interval);
01925             if (rdata->dayofmonth)  fprintf(f_output, ";BYMONTHDAY=%d", rdata->dayofmonth);
01926             if (rdata->monthofyear) fprintf(f_output, ";BYMONTH=%d",    rdata->monthofyear);
01927             if (rdata->position)    fprintf(f_output, ";BYSETPOS=%d",   rdata->position);
01928             if (rdata->bydaymask) {
01929                 char byday[40];
01930                 int  empty = 1;
01931                 int i=0;
01932                 memset(byday, 0, sizeof(byday));
01933                 for (i=0; i<6; i++) {
01934                     int bit = 1 << i;
01935                     if (bit & rdata->bydaymask) {
01936                         char temp[40];
01937                         snprintf(temp, sizeof(temp), "%s%s%s", byday, (empty) ? ";BYDAY=" : ";", days[i]);
01938                         strcpy(byday, temp);
01939                         empty = 0;
01940                     }
01941                 }
01942                 fprintf(f_output, "%s", byday);
01943             }
01944             fprintf(f_output, "\n");
01945             pst_free_recurrence(rdata);
01946         }
01947         switch (appointment->label) {
01948             case PST_APP_LABEL_NONE:
01949                 if (!write_extra_categories(f_output, item)) fprintf(f_output, "CATEGORIES:NONE\n");
01950                 break;
01951             case PST_APP_LABEL_IMPORTANT:
01952                 fprintf(f_output, "CATEGORIES:IMPORTANT\n");
01953                 break;
01954             case PST_APP_LABEL_BUSINESS:
01955                 fprintf(f_output, "CATEGORIES:BUSINESS\n");
01956                 break;
01957             case PST_APP_LABEL_PERSONAL:
01958                 fprintf(f_output, "CATEGORIES:PERSONAL\n");
01959                 break;
01960             case PST_APP_LABEL_VACATION:
01961                 fprintf(f_output, "CATEGORIES:VACATION\n");
01962                 break;
01963             case PST_APP_LABEL_MUST_ATTEND:
01964                 fprintf(f_output, "CATEGORIES:MUST-ATTEND\n");
01965                 break;
01966             case PST_APP_LABEL_TRAVEL_REQ:
01967                 fprintf(f_output, "CATEGORIES:TRAVEL-REQUIRED\n");
01968                 break;
01969             case PST_APP_LABEL_NEEDS_PREP:
01970                 fprintf(f_output, "CATEGORIES:NEEDS-PREPARATION\n");
01971                 break;
01972             case PST_APP_LABEL_BIRTHDAY:
01973                 fprintf(f_output, "CATEGORIES:BIRTHDAY\n");
01974                 break;
01975             case PST_APP_LABEL_ANNIVERSARY:
01976                 fprintf(f_output, "CATEGORIES:ANNIVERSARY\n");
01977                 break;
01978             case PST_APP_LABEL_PHONE_CALL:
01979                 fprintf(f_output, "CATEGORIES:PHONE-CALL\n");
01980                 break;
01981         }
01982     }
01983     fprintf(f_output, "END:VEVENT\n");
01984     if (result) free(result);
01985 }
01986 
01987 
01988 void create_enter_dir(struct file_ll* f, pst_item *item)
01989 {
01990     pst_convert_utf8(item, &item->file_as);
01991     f->type         = item->type;
01992     f->stored_count = (item->folder) ? item->folder->item_count : 0;
01993 
01994     DEBUG_ENT("create_enter_dir");
01995     if (mode == MODE_KMAIL)
01996         f->name = mk_kmail_dir(item->file_as.str);
01997     else if (mode == MODE_RECURSE) {
01998         f->name = mk_recurse_dir(item->file_as.str, f->type);
01999         if (mode_thunder) {
02000             FILE *type_file = fopen(".type", "w");
02001             fprintf(type_file, "%d\n", item->type);
02002             fclose(type_file);
02003         }
02004     } else if (mode == MODE_SEPARATE) {
02005         // do similar stuff to recurse here.
02006         mk_separate_dir(item->file_as.str);
02007         f->name = (char*) pst_malloc(file_name_len);
02008         memset(f->name, 0, file_name_len);
02009     } else {
02010         f->name = (char*) pst_malloc(strlen(item->file_as.str)+strlen(OUTPUT_TEMPLATE)+1);
02011         sprintf(f->name, OUTPUT_TEMPLATE, item->file_as.str);
02012     }
02013 
02014     f->dname = (char*) pst_malloc(strlen(item->file_as.str)+1);
02015     strcpy(f->dname, item->file_as.str);
02016 
02017     if (overwrite != 1) {
02018         int x = 0;
02019         char *temp = (char*) pst_malloc (strlen(f->name)+10); //enough room for 10 digits
02020 
02021         sprintf(temp, "%s", f->name);
02022         check_filename(temp);
02023         while ((f->output = fopen(temp, "r"))) {
02024             DEBUG_INFO(("need to increase filename because one already exists with that name\n"));
02025             DEBUG_INFO(("- increasing it to %s%d\n", f->name, x));
02026             x++;
02027             sprintf(temp, "%s%08d", f->name, x);
02028             DEBUG_INFO(("- trying \"%s\"\n", f->name));
02029             if (x == 99999999) {
02030                 DIE(("create_enter_dir: Why can I not create a folder %s? I have tried %i extensions...\n", f->name, x));
02031             }
02032             fclose(f->output);
02033         }
02034         if (x > 0) { //then the f->name should change
02035             free (f->name);
02036             f->name = temp;
02037         } else {
02038             free(temp);
02039         }
02040     }
02041 
02042     DEBUG_INFO(("f->name = %s\nitem->folder_name = %s\n", f->name, item->file_as.str));
02043     if (mode != MODE_SEPARATE) {
02044         check_filename(f->name);
02045         if (!(f->output = fopen(f->name, "w"))) {
02046             DIE(("create_enter_dir: Could not open file \"%s\" for write\n", f->name));
02047         }
02048     }
02049     DEBUG_RET();
02050 }
02051 
02052 
02053 void close_enter_dir(struct file_ll *f)
02054 {
02055     DEBUG_INFO(("processed item count for folder %s is %i, skipped %i, total %i \n",
02056                 f->dname, f->item_count, f->skip_count, f->stored_count));
02057     if (output_mode != OUTPUT_QUIET) {
02058         pst_debug_lock();
02059             printf("\t\"%s\" - %i items done, %i items skipped.\n", f->dname, f->item_count, f->skip_count);
02060             fflush(stdout);
02061         pst_debug_unlock();
02062     }
02063     if (f->output) {
02064         struct stat st;
02065         fclose(f->output);
02066         stat(f->name, &st);
02067         if (!st.st_size) {
02068             DEBUG_WARN(("removing empty output file %s\n", f->name));
02069             remove(f->name);
02070         }
02071     }
02072     free(f->name);
02073     free(f->dname);
02074 
02075     if (mode == MODE_KMAIL)
02076         close_kmail_dir();
02077     else if (mode == MODE_RECURSE) {
02078         if (mode_thunder) {
02079             FILE *type_file = fopen(".size", "w");
02080             fprintf(type_file, "%i %i\n", f->item_count, f->stored_count);
02081             fclose(type_file);
02082         }
02083         close_recurse_dir();
02084     } else if (mode == MODE_SEPARATE)
02085         close_separate_dir();
02086 }
02087 

Generated on Sat Apr 23 12:14:03 2011 for 'LibPst' by  doxygen 1.3.9.1