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

Generated on Sun May 22 17:26:44 2011 for 'LibPst' by  doxygen 1.3.9.1