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

Generated on Tue Jun 23 20:30:06 2009 for 'LibPst' by  doxygen 1.3.9.1