'LibPst'
readpst.c
Go to the documentation of this file.
1 /***
2  * readpst.c
3  * Part of the LibPST project
4  * Written by David Smith
5  * dave.s@earthcorp.com
6  */
7 
8 #include "define.h"
9 #include "lzfu.h"
10 #include "msg.h"
11 
12 #define OUTPUT_TEMPLATE "%s.%s"
13 #define OUTPUT_KMAIL_DIR_TEMPLATE ".%s.directory"
14 #define KMAIL_INDEX "../.%s.index"
15 #define SEP_MAIL_FILE_TEMPLATE "%i%s"
16 
17 // max size of the c_time char*. It will store the date of the email
18 #define C_TIME_SIZE 500
19 
20 struct file_ll {
22  char *dname;
24  int32_t stored_count;
25  int32_t item_count;
26  int32_t skip_count;
27 };
28 
29 int grim_reaper();
30 pid_t try_fork(char* folder);
31 void process(pst_item *outeritem, pst_desc_tree *d_ptr);
32 void write_email_body(FILE *f, char *body);
33 void removeCR(char *c);
34 void usage();
35 void version();
36 void mk_kmail_dir(char* fname);
37 int close_kmail_dir();
38 void mk_recurse_dir(char* dir);
39 int close_recurse_dir();
40 void mk_separate_dir(char *dir);
41 int close_separate_dir();
42 void mk_separate_file(struct file_ll *f, int32_t t, char *extension, int openit);
43 void close_separate_file(struct file_ll *f);
44 char* my_stristr(char *haystack, char *needle);
45 void check_filename(char *fname);
46 int acceptable_ext(pst_item_attach* attach);
47 void write_separate_attachment(char f_name[], pst_item_attach* attach, int attach_num, pst_file* pst);
48 void write_embedded_message(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pf, int save_rtf, char** extra_mime_headers);
49 void write_inline_attachment(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pst);
50 int valid_headers(char *header);
51 void header_has_field(char *header, char *field, int *flag);
52 void header_get_subfield(char *field, const char *subfield, char *body_subfield, size_t size_subfield);
53 char* header_get_field(char *header, char *field);
54 char* header_end_field(char *field);
55 void header_strip_field(char *header, char *field);
56 int test_base64(char *body, size_t len);
57 void find_html_charset(char *html, char *charset, size_t charsetlen);
58 void find_rfc822_headers(char** extra_mime_headers);
59 void write_body_part(FILE* f_output, pst_string *body, char *mime, char *charset, char *boundary, pst_file* pst);
60 void write_schedule_part_data(FILE* f_output, pst_item* item, const char* sender, const char* method);
61 void write_schedule_part(FILE* f_output, pst_item* item, const char* sender, const char* boundary);
62 void write_normal_email(FILE* f_output, char f_name[], pst_item* item, int mode, int mode_MH, pst_file* pst, int save_rtf, int embedding, char** extra_mime_headers);
63 void write_vcard(FILE* f_output, pst_item *item, pst_item_contact* contact, char comment[]);
64 int write_extra_categories(FILE* f_output, pst_item* item);
65 void write_journal(FILE* f_output, pst_item* item);
66 void write_appointment(FILE* f_output, pst_item *item);
67 void create_enter_dir(struct file_ll* f, pst_item *item);
68 void close_enter_dir(struct file_ll *f);
69 char* quote_string(char *inp);
70 
71 const char* prog_name;
72 char* output_dir = ".";
73 
74 // Normal mode just creates mbox format files in the current directory. Each file is named
75 // the same as the folder's name that it represents
76 #define MODE_NORMAL 0
77 
78 // KMail mode creates a directory structure suitable for being used directly
79 // by the KMail application
80 #define MODE_KMAIL 1
81 
82 // recurse mode creates a directory structure like the PST file. Each directory
83 // contains only one file which stores the emails in mboxrd format.
84 #define MODE_RECURSE 2
85 
86 // separate mode creates the same directory structure as recurse. The emails are stored in
87 // separate files, numbering from 1 upward. Attachments belonging to the emails are
88 // saved as email_no-filename (e.g. 1-samplefile.doc or 1-Attachment2.zip)
89 #define MODE_SEPARATE 3
90 
91 
92 // Output Normal just prints the standard information about what is going on
93 #define OUTPUT_NORMAL 0
94 
95 // Output Quiet is provided so that only errors are printed
96 #define OUTPUT_QUIET 1
97 
98 // default mime-type for attachments that have a null mime-type
99 #define MIME_TYPE_DEFAULT "application/octet-stream"
100 #define RFC822 "message/rfc822"
101 
102 // output mode for contacts
103 #define CMODE_VCARD 0
104 #define CMODE_LIST 1
105 
106 // output mode for deleted items
107 #define DMODE_EXCLUDE 0
108 #define DMODE_INCLUDE 1
109 
110 // Output type mode flags
111 #define OTMODE_EMAIL 1
112 #define OTMODE_APPOINTMENT 2
113 #define OTMODE_JOURNAL 4
114 #define OTMODE_CONTACT 8
115 
116 // output settings for RTF bodies
117 // filename for the attachment
118 #define RTF_ATTACH_NAME "rtf-body.rtf"
119 // mime type for the attachment
120 #define RTF_ATTACH_TYPE "application/rtf"
121 
122 // global settings
124 int mode_MH = 0; // a submode of MODE_SEPARATE
125 int mode_EX = 0; // a submode of MODE_SEPARATE
126 int mode_MSG = 0; // a submode of MODE_SEPARATE
127 int mode_thunder = 0; // a submode of MODE_RECURSE
131 int output_type_mode = 0xff; // Default to all.
133 int overwrite = 0;
134 int prefer_utf8 = 0;
136 int file_name_len = 10; // enough room for MODE_SPEARATE file name
139 char* default_charset = NULL;
141 
142 int number_processors = 1; // number of cpus we have
143 int max_children = 0; // based on number of cpus and command line args
144 int max_child_specified = 0;// have command line arg -j
145 int active_children; // number of children of this process, cannot be larger than max_children
146 pid_t* child_processes; // setup by main(), and at the start of new child process
147 
148 #ifdef HAVE_SEMAPHORE_H
149 int shared_memory_id;
150 sem_t* global_children = NULL;
151 sem_t* output_mutex = NULL;
152 #endif
153 
154 
155 int grim_reaper(int waitall)
156 {
157  int available = 0;
158 #ifdef HAVE_FORK
159 #ifdef HAVE_SEMAPHORE_H
160  if (global_children) {
161  //sem_getvalue(global_children, &available);
162  //printf("grim reaper %s for pid %d (parent %d) with %d children, %d available\n", (waitall) ? "all" : "", getpid(), getppid(), active_children, available);
163  //fflush(stdout);
164  int i,j;
165  for (i=0; i<active_children; i++) {
166  int status;
167  pid_t child = child_processes[i];
168  pid_t ch = waitpid(child, &status, ((waitall) ? 0 : WNOHANG));
169  if (ch == child) {
170  // check termination status
171  //if (WIFEXITED(status)) {
172  // int ext = WEXITSTATUS(status);
173  // printf("Process %d exited with status %d\n", child, ext);
174  // fflush(stdout);
175  //}
176  if (WIFSIGNALED(status)) {
177  int sig = WTERMSIG(status);
178  DEBUG_INFO(("Process %d terminated with signal %d\n", child, sig));
179  //printf("Process %d terminated with signal %d\n", child, sig);
180  //fflush(stdout);
181  }
182  if (status != 0) {
183  exit(status);
184  }
185  // this has terminated, remove it from the list
186  for (j=i; j<active_children-1; j++) {
188  }
189  active_children--;
190  i--;
191  }
192  }
193  sem_getvalue(global_children, &available);
194  //printf("grim reaper %s for pid %d with %d children, %d available\n", (waitall) ? "all" : "", getpid(), active_children, available);
195  //fflush(stdout);
196  }
197 #endif
198 #endif
199  return available;
200 }
201 
202 
203 pid_t try_fork(char *folder)
204 {
205 #ifdef HAVE_FORK
206 #ifdef HAVE_SEMAPHORE_H
207  int available = grim_reaper(0);
208  // If children have called sem_post but not exited yet, we could have available > 0 but active_children == max_children
209  if (available && active_children < max_children) {
210  sem_wait(global_children);
211  pid_t child = fork();
212  if (child < 0) {
213  // fork failed, pretend it worked and we are the child
214  return 0;
215  }
216  else if (child == 0) {
217  // fork worked, and we are the child, reinitialize *our* list of children
218  active_children = 0;
219  memset(child_processes, 0, sizeof(pid_t) * max_children);
220  pst_reopen(&pstfile); // close and reopen the pst file to get an independent file position pointer
221  }
222  else {
223  // fork worked, and we are the parent, record this child that we need to wait for
224  //pid_t me = getpid();
225  //printf("parent %d forked child pid %d to process folder %s\n", me, child, folder);
226  //fflush(stdout);
227  child_processes[active_children++] = child;
228  }
229  return child;
230  }
231  else {
232  return 0; // pretend to have forked and we are the child
233  }
234 #endif
235 #endif
236  return 0;
237 }
238 
239 
240 void process(pst_item *outeritem, pst_desc_tree *d_ptr)
241 {
242  struct file_ll ff;
243  pst_item *item = NULL;
244 
245  DEBUG_ENT("process");
246  create_enter_dir(&ff, outeritem);
247 
248  for (; d_ptr; d_ptr = d_ptr->next) {
249  DEBUG_INFO(("New item record\n"));
250  if (!d_ptr->desc) {
251  ff.skip_count++;
252  DEBUG_WARN(("ERROR item's desc record is NULL\n"));
253  continue;
254  }
255  DEBUG_INFO(("Desc Email ID %#"PRIx64" [d_ptr->d_id = %#"PRIx64"]\n", d_ptr->desc->i_id, d_ptr->d_id));
256 
257  item = pst_parse_item(&pstfile, d_ptr, NULL);
258  DEBUG_INFO(("About to process item\n"));
259 
260  if (!item) {
261  ff.skip_count++;
262  DEBUG_INFO(("A NULL item was seen\n"));
263  continue;
264  }
265 
266  if (item->subject.str) {
267  DEBUG_INFO(("item->subject = %s\n", item->subject.str));
268  }
269 
270  if (item->folder && item->file_as.str) {
271  DEBUG_INFO(("Processing Folder \"%s\"\n", item->file_as.str));
272  if (output_mode != OUTPUT_QUIET) {
273  pst_debug_lock();
274  printf("Processing Folder \"%s\"\n", item->file_as.str);
275  fflush(stdout);
277  }
278  ff.item_count++;
279  if (d_ptr->child && (deleted_mode == DMODE_INCLUDE || strcasecmp(item->file_as.str, "Deleted Items"))) {
280  //if this is a non-empty folder other than deleted items, we want to recurse into it
281  pid_t parent = getpid();
282  pid_t child = try_fork(item->file_as.str);
283  if (child == 0) {
284  // we are the child process, or the original parent if no children were available
285  pid_t me = getpid();
286  process(item, d_ptr->child);
287 #ifdef HAVE_FORK
288 #ifdef HAVE_SEMAPHORE_H
289  if (me != parent) {
290  // we really were a child, forked for the sole purpose of processing this folder
291  // free my child count slot before really exiting, since
292  // all I am doing here is waiting for my children to exit
293  sem_post(global_children);
294  grim_reaper(1); // wait for all my child processes to exit
295  exit(0); // really exit
296  }
297 #endif
298 #endif
299  }
300  }
301 
302  } else if (item->contact && (item->type == PST_TYPE_CONTACT)) {
303  DEBUG_INFO(("Processing Contact\n"));
304  if (!(output_type_mode & OTMODE_CONTACT)) {
305  ff.skip_count++;
306  DEBUG_INFO(("skipping contact: not in output type list\n"));
307  }
308  else {
309  ff.item_count++;
310  if (mode == MODE_SEPARATE) mk_separate_file(&ff, PST_TYPE_CONTACT, (mode_EX) ? ".vcf" : "", 1);
311  if (contact_mode == CMODE_VCARD) {
312  pst_convert_utf8_null(item, &item->comment);
313  write_vcard(ff.output[PST_TYPE_CONTACT], item, item->contact, item->comment.str);
314  }
315  else {
316  pst_convert_utf8(item, &item->contact->fullname);
317  pst_convert_utf8(item, &item->contact->address1);
318  fprintf(ff.output[PST_TYPE_CONTACT], "%s <%s>\n", item->contact->fullname.str, item->contact->address1.str);
319  }
321  }
322 
323  } else if (item->email && ((item->type == PST_TYPE_NOTE) || (item->type == PST_TYPE_SCHEDULE) || (item->type == PST_TYPE_REPORT))) {
324  DEBUG_INFO(("Processing Email\n"));
325  if (!(output_type_mode & OTMODE_EMAIL)) {
326  ff.skip_count++;
327  DEBUG_INFO(("skipping email: not in output type list\n"));
328  }
329  else {
330  char *extra_mime_headers = NULL;
331  ff.item_count++;
332  if (mode == MODE_SEPARATE) {
333  // process this single email message, possibly forking
334  pid_t parent = getpid();
335  pid_t child = try_fork(item->file_as.str);
336  if (child == 0) {
337  // we are the child process, or the original parent if no children were available
338  pid_t me = getpid();
339  mk_separate_file(&ff, PST_TYPE_NOTE, (mode_EX) ? ".eml" : "", 1);
341  close_separate_file(&ff);
342  if (mode_MSG) {
343  mk_separate_file(&ff, PST_TYPE_NOTE, ".msg", 0);
345  }
346 #ifdef HAVE_FORK
347 #ifdef HAVE_SEMAPHORE_H
348  if (me != parent) {
349  // we really were a child, forked for the sole purpose of processing this message
350  // free my child count slot before really exiting, since
351  // all I am doing here is waiting for my children to exit
352  sem_post(global_children);
353  grim_reaper(1); // wait for all my child processes to exit - there should not be any
354  exit(0); // really exit
355  }
356 #endif
357 #endif
358  }
359  }
360  else {
361  // process this single email message, cannot fork since not separate mode
362  write_normal_email(ff.output[PST_TYPE_NOTE], ff.name[PST_TYPE_NOTE], item, mode, mode_MH, &pstfile, save_rtf_body, 0, &extra_mime_headers);
363  }
364  }
365 
366  } else if (item->journal && (item->type == PST_TYPE_JOURNAL)) {
367  DEBUG_INFO(("Processing Journal Entry\n"));
368  if (!(output_type_mode & OTMODE_JOURNAL)) {
369  ff.skip_count++;
370  DEBUG_INFO(("skipping journal entry: not in output type list\n"));
371  }
372  else {
373  ff.item_count++;
374  if (mode == MODE_SEPARATE) mk_separate_file(&ff, PST_TYPE_JOURNAL, (mode_EX) ? ".ics" : "", 1);
376  fprintf(ff.output[PST_TYPE_JOURNAL], "\n");
378  }
379 
380  } else if (item->appointment && (item->type == PST_TYPE_APPOINTMENT)) {
381  DEBUG_INFO(("Processing Appointment Entry\n"));
383  ff.skip_count++;
384  DEBUG_INFO(("skipping appointment: not in output type list\n"));
385  }
386  else {
387  ff.item_count++;
388  if (mode == MODE_SEPARATE) mk_separate_file(&ff, PST_TYPE_APPOINTMENT, (mode_EX) ? ".ics" : "", 1);
390  fprintf(ff.output[PST_TYPE_APPOINTMENT], "\n");
392  }
393 
394  } else if (item->message_store) {
395  // there should only be one message_store, and we have already done it
396  ff.skip_count++;
397  DEBUG_WARN(("item with message store content, type %i %s, skipping it\n", item->type, item->ascii_type));
398 
399  } else {
400  ff.skip_count++;
401  DEBUG_WARN(("Unknown item type %i (%s) name (%s)\n",
402  item->type, item->ascii_type, item->file_as.str));
403  }
404  pst_freeItem(item);
405  }
406  close_enter_dir(&ff);
407  DEBUG_RET();
408 }
409 
410 
411 
412 int main(int argc, char* const* argv) {
413  pst_item *item = NULL;
414  pst_desc_tree *d_ptr;
415  char * fname = NULL;
416  char *d_log = NULL;
417  int c,x;
418  char *temp = NULL; //temporary char pointer
419  prog_name = argv[0];
420 
421  time_t now = time(NULL);
422  srand((unsigned)now);
423 
424  if (regcomp(&meta_charset_pattern, "<meta[^>]*content=\"[^>]*charset=([^>\";]*)[\";]", REG_ICASE | REG_EXTENDED)) {
425  printf("cannot compile regex pattern to find content charset in html bodies\n");
426  exit(3);
427  }
428 
429  // command-line option handling
430  while ((c = getopt(argc, argv, "a:bC:c:Dd:emhj:kMo:qrSt:uVwL:8"))!= -1) {
431  switch (c) {
432  case 'a':
433  if (optarg) {
434  int n = strlen(optarg);
435  acceptable_extensions = (char*)pst_malloc(n+2);
436  strcpy(acceptable_extensions, optarg);
437  acceptable_extensions[n+1] = '\0'; // double null terminates array of non-empty null terminated strings.
438  char *p = acceptable_extensions;
439  while (*p) {
440  if (*p == ',') *p = '\0';
441  p++;
442  }
443  }
444  break;
445  case 'b':
446  save_rtf_body = 0;
447  break;
448  case 'C':
449  if (optarg) {
451  }
452  else {
453  usage();
454  exit(0);
455  }
456  break;
457  case 'c':
458  if (optarg && optarg[0]=='v') {
461  }
462  else if (optarg && optarg[0]=='l') {
465  }
466  else {
467  usage();
468  exit(0);
469  }
470  break;
471  case 'D':
473  break;
474  case 'd':
475  d_log = optarg;
476  break;
477  case 'h':
478  usage();
479  exit(0);
480  break;
481  case 'j':
482  max_children = atoi(optarg);
484  break;
485  case 'k':
486  mode = MODE_KMAIL;
487  break;
488  case 'M':
490  mode_MH = 1;
491  mode_EX = 0;
492  mode_MSG = 0;
493  break;
494  case 'e':
496  mode_MH = 1;
497  mode_EX = 1;
498  mode_MSG = 0;
499  file_name_len = 14;
500  break;
501  case 'L':
502  pst_debug_setlevel(atoi(optarg));
503  break;
504  case 'm':
506  mode_MH = 1;
507  mode_EX = 1;
508  mode_MSG = 1;
509  file_name_len = 14;
510  break;
511  case 'o':
512  output_dir = optarg;
513  break;
514  case 'q':
516  break;
517  case 'r':
518  mode = MODE_RECURSE;
519  mode_thunder = 0;
520  break;
521  case 'S':
523  mode_MH = 0;
524  mode_EX = 0;
525  mode_MSG = 0;
526  break;
527  case 't':
528  // email, appointment, contact, other
529  if (!optarg) {
530  usage();
531  exit(0);
532  }
533  temp = optarg;
534  output_type_mode = 0;
535  while (*temp > 0) {
536  switch (temp[0]) {
537  case 'e':
539  break;
540  case 'a':
542  break;
543  case 'j':
545  break;
546  case 'c':
548  break;
549  default:
550  usage();
551  exit(0);
552  break;
553  }
554  temp++;
555  }
556  break;
557  case 'u':
558  mode = MODE_RECURSE;
559  mode_thunder = 1;
560  break;
561  case 'V':
562  version();
563  exit(0);
564  break;
565  case 'w':
566  overwrite = 1;
567  break;
568  case '8':
569  prefer_utf8 = 1;
570  break;
571  default:
572  usage();
573  exit(1);
574  break;
575  }
576  }
577 
578  if (argc > optind) {
579  fname = argv[optind];
580  } else {
581  usage();
582  exit(2);
583  }
584 
585 #ifdef _SC_NPROCESSORS_ONLN
586  number_processors = sysconf(_SC_NPROCESSORS_ONLN);
587 #endif
589  active_children = 0;
590  child_processes = (pid_t *)pst_malloc(sizeof(pid_t) * max_children);
591  memset(child_processes, 0, sizeof(pid_t) * max_children);
592 
593 #ifdef HAVE_SEMAPHORE_H
594  if (max_children) {
595  shared_memory_id = shmget(IPC_PRIVATE, sizeof(sem_t)*2, 0777);
596  if (shared_memory_id >= 0) {
597  global_children = (sem_t *)shmat(shared_memory_id, NULL, 0);
598  if (global_children == (sem_t *)-1) global_children = NULL;
599  if (global_children) {
600  output_mutex = &(global_children[1]);
601  sem_init(global_children, 1, max_children);
602  sem_init(output_mutex, 1, 1);
603  }
604  shmctl(shared_memory_id, IPC_RMID, NULL);
605  }
606  }
607 #endif
608 
609  #ifdef DEBUG_ALL
610  // force a log file
611  if (!d_log) d_log = "readpst.log";
612  #endif // defined DEBUG_ALL
613  #ifdef HAVE_SEMAPHORE_H
614  DEBUG_INIT(d_log, output_mutex);
615  #else
616  DEBUG_INIT(d_log, NULL);
617  #endif
618  DEBUG_ENT("main");
619 
620  if (output_mode != OUTPUT_QUIET) printf("Opening PST file and indexes...\n");
621  RET_DERROR(pst_open(&pstfile, fname, default_charset), 1, ("Error opening File\n"));
622  RET_DERROR(pst_load_index(&pstfile), 2, ("Index Error\n"));
623 
625 
626  if (chdir(output_dir)) {
627  x = errno;
628  pst_close(&pstfile);
629  DEBUG_RET();
630  DIE(("Cannot change to output dir %s: %s\n", output_dir, strerror(x)));
631  }
632 
633  d_ptr = pstfile.d_head; // first record is main record
634  item = pst_parse_item(&pstfile, d_ptr, NULL);
635  if (!item || !item->message_store) {
636  DEBUG_RET();
637  DIE(("Could not get root record\n"));
638  }
639 
640  // default the file_as to the same as the main filename if it doesn't exist
641  if (!item->file_as.str) {
642  if (!(temp = strrchr(fname, '/')))
643  if (!(temp = strrchr(fname, '\\')))
644  temp = fname;
645  else
646  temp++; // get past the "\\"
647  else
648  temp++; // get past the "/"
649  item->file_as.str = (char*)pst_malloc(strlen(temp)+1);
650  strcpy(item->file_as.str, temp);
651  item->file_as.is_utf8 = 1;
652  DEBUG_INFO(("file_as was blank, so am using %s\n", item->file_as.str));
653  }
654  DEBUG_INFO(("Root Folder Name: %s\n", item->file_as.str));
655 
656  d_ptr = pst_getTopOfFolders(&pstfile, item);
657  if (!d_ptr) {
658  DEBUG_RET();
659  DIE(("Top of folders record not found. Cannot continue\n"));
660  }
661 
662  process(item, d_ptr->child); // do the children of TOPF
663  grim_reaper(1); // wait for all child processes
664 
665  pst_freeItem(item);
666  pst_close(&pstfile);
667  DEBUG_RET();
668 
669 #ifdef HAVE_SEMAPHORE_H
670  if (global_children) {
671  sem_destroy(global_children);
672  sem_destroy(output_mutex);
673  shmdt(global_children);
674  }
675 #endif
676 
677  regfree(&meta_charset_pattern);
678  return 0;
679 }
680 
681 
682 void write_email_body(FILE *f, char *body) {
683  char *n = body;
684  DEBUG_ENT("write_email_body");
685  if (mode != MODE_SEPARATE) {
686  while (n) {
687  char *p = body;
688  while (*p == '>') p++;
689  if (strncmp(p, "From ", 5) == 0) fprintf(f, ">");
690  if ((n = strchr(body, '\n'))) {
691  n++;
692  pst_fwrite(body, n-body, 1, f); //write just a line
693  body = n;
694  }
695  }
696  }
697  pst_fwrite(body, strlen(body), 1, f);
698  DEBUG_RET();
699 }
700 
701 
702 void removeCR (char *c) {
703  // converts \r\n to \n
704  char *a, *b;
705  DEBUG_ENT("removeCR");
706  a = b = c;
707  while (*a != '\0') {
708  *b = *a;
709  if (*a != '\r') b++;
710  a++;
711  }
712  *b = '\0';
713  DEBUG_RET();
714 }
715 
716 
717 void usage() {
718  DEBUG_ENT("usage");
719  version();
720  printf("Usage: %s [OPTIONS] {PST FILENAME}\n", prog_name);
721  printf("OPTIONS:\n");
722  printf("\t-V\t- Version. Display program version\n");
723  printf("\t-C charset\t- character set for items with an unspecified character set\n");
724  printf("\t-D\t- Include deleted items in output\n");
725  printf("\t-L <level> \t- Set debug level; 1=debug,2=info,3=warn.\n");
726  printf("\t-M\t- Write emails in the MH (rfc822) format\n");
727  printf("\t-S\t- Separate. Write emails in the separate format\n");
728  printf("\t-a <attachment-extension-list>\t- Discard any attachment without an extension on the list\n");
729  printf("\t-b\t- Don't save RTF-Body attachments\n");
730  printf("\t-c[v|l]\t- Set the Contact output mode. -cv = VCard, -cl = EMail list\n");
731  printf("\t-d <filename> \t- Debug to file.\n");
732  printf("\t-e\t- As with -M, but include extensions on output files\n");
733  printf("\t-h\t- Help. This screen\n");
734  printf("\t-j <integer>\t- Number of parallel jobs to run\n");
735  printf("\t-k\t- KMail. Output in kmail format\n");
736  printf("\t-m\t- As with -e, but write .msg files also\n");
737  printf("\t-o <dirname>\t- Output directory to write files to. CWD is changed *after* opening pst file\n");
738  printf("\t-q\t- Quiet. Only print error messages\n");
739  printf("\t-r\t- Recursive. Output in a recursive format\n");
740  printf("\t-t[eajc]\t- Set the output type list. e = email, a = attachment, j = journal, c = contact\n");
741  printf("\t-u\t- Thunderbird mode. Write two extra .size and .type files\n");
742  printf("\t-w\t- Overwrite any output mbox files\n");
743  printf("\t-8\t- Output bodies in UTF-8, rather than original encoding, if UTF-8 version is available\n");
744  printf("\n");
745  printf("Only one of -M -S -e -k -m -r should be specified\n");
746  DEBUG_RET();
747 }
748 
749 
750 void version() {
751  DEBUG_ENT("version");
752  printf("ReadPST / LibPST v%s\n", VERSION);
753 #if BYTE_ORDER == BIG_ENDIAN
754  printf("Big Endian implementation being used.\n");
755 #elif BYTE_ORDER == LITTLE_ENDIAN
756  printf("Little Endian implementation being used.\n");
757 #else
758 # error "Byte order not supported by this library"
759 #endif
760  DEBUG_RET();
761 }
762 
763 
764 void mk_kmail_dir(char *fname) {
765  //make a directory based on OUTPUT_KMAIL_DIR_TEMPLATE
766  //change to that directory
767  char *dir, *index;
768  int x;
769  DEBUG_ENT("mk_kmail_dir");
770  dir = pst_malloc(strlen(fname)+strlen(OUTPUT_KMAIL_DIR_TEMPLATE)+1);
771  sprintf(dir, OUTPUT_KMAIL_DIR_TEMPLATE, fname);
772  check_filename(dir);
773  if (D_MKDIR(dir)) {
774  if (errno != EEXIST) { // not an error because it exists
775  x = errno;
776  DIE(("mk_kmail_dir: Cannot create directory %s: %s\n", dir, strerror(x)));
777  }
778  }
779  if (chdir(dir)) {
780  x = errno;
781  DIE(("mk_kmail_dir: Cannot change to directory %s: %s\n", dir, strerror(x)));
782  }
783  free (dir);
784 
785  //we should remove any existing indexes created by KMail, cause they might be different now
786  index = pst_malloc(strlen(fname)+strlen(KMAIL_INDEX)+1);
787  sprintf(index, KMAIL_INDEX, fname);
788  unlink(index);
789  free(index);
790 
791  DEBUG_RET();
792 }
793 
794 
796  int x;
797  DEBUG_ENT("close_kmail_dir");
798  if (chdir("..")) {
799  x = errno;
800  DIE(("close_kmail_dir: Cannot move up dir (..): %s\n", strerror(x)));
801  }
802  DEBUG_RET();
803  return 0;
804 }
805 
806 
807 char *item_type_to_name(int32_t item_type) {
808  char *name;
809  switch (item_type) {
811  name = "calendar";
812  break;
813  case PST_TYPE_CONTACT:
814  name = "contacts";
815  break;
816  case PST_TYPE_JOURNAL:
817  name = "journal";
818  break;
819  case PST_TYPE_STICKYNOTE:
820  case PST_TYPE_TASK:
821  case PST_TYPE_NOTE:
822  case PST_TYPE_OTHER:
823  case PST_TYPE_REPORT:
824  default:
825  name = "mbox";
826  break;
827  }
828  return name;
829 }
830 
831 
832 int32_t reduced_item_type(int32_t item_type) {
833  int32_t reduced;
834  switch (item_type) {
836  case PST_TYPE_CONTACT:
837  case PST_TYPE_JOURNAL:
838  reduced = item_type;
839  break;
840  case PST_TYPE_STICKYNOTE:
841  case PST_TYPE_TASK:
842  case PST_TYPE_NOTE:
843  case PST_TYPE_OTHER:
844  case PST_TYPE_REPORT:
845  default:
846  reduced = PST_TYPE_NOTE;
847  break;
848  }
849  return reduced;
850 }
851 
852 
853 // this will create a directory by that name
854 void mk_recurse_dir(char *dir) {
855  int x;
856  DEBUG_ENT("mk_recurse_dir");
857  check_filename(dir);
858  if (D_MKDIR (dir)) {
859  if (errno != EEXIST) { // not an error because it exists
860  x = errno;
861  DIE(("mk_recurse_dir: Cannot create directory %s: %s\n", dir, strerror(x)));
862  }
863  }
864  if (chdir(dir)) {
865  x = errno;
866  DIE(("mk_recurse_dir: Cannot change to directory %s: %s\n", dir, strerror(x)));
867  }
868  DEBUG_RET();
869 }
870 
871 
873  int x;
874  DEBUG_ENT("close_recurse_dir");
875  if (chdir("..")) {
876  x = errno;
877  DIE(("close_recurse_dir: Cannot go up dir (..): %s\n", strerror(x)));
878  }
879  DEBUG_RET();
880  return 0;
881 }
882 
883 
884 void mk_separate_dir(char *dir) {
885  size_t dirsize = strlen(dir) + 10;
886  char dir_name[dirsize];
887  int x = 0, y = 0;
888 
889  DEBUG_ENT("mk_separate_dir");
890  do {
891  if (y == 0)
892  snprintf(dir_name, dirsize, "%s", dir);
893  else
894  snprintf(dir_name, dirsize, "%s" SEP_MAIL_FILE_TEMPLATE, dir, y, ""); // enough for 9 digits allocated above
895 
896  check_filename(dir_name);
897  DEBUG_INFO(("about to try creating %s\n", dir_name));
898  if (D_MKDIR(dir_name)) {
899  if (errno != EEXIST) { // if there is an error, and it doesn't already exist
900  x = errno;
901  DIE(("mk_separate_dir: Cannot create directory %s: %s\n", dir, strerror(x)));
902  }
903  } else {
904  break;
905  }
906  y++;
907  } while (overwrite == 0);
908 
909  if (chdir(dir_name)) {
910  x = errno;
911  DIE(("mk_separate_dir: Cannot change to directory %s: %s\n", dir, strerror(x)));
912  }
913 
914  if (overwrite) {
915  // we should probably delete all files from this directory
916 #if !defined(WIN32) && !defined(__CYGWIN__)
917  DIR * sdir = NULL;
918  struct dirent *dirent = NULL;
919  struct stat filestat;
920  if (!(sdir = opendir("./"))) {
921  DEBUG_WARN(("mk_separate_dir: Cannot open dir \"%s\" for deletion of old contents\n", "./"));
922  } else {
923  while ((dirent = readdir(sdir))) {
924  if (lstat(dirent->d_name, &filestat) != -1)
925  if (S_ISREG(filestat.st_mode)) {
926  if (unlink(dirent->d_name)) {
927  y = errno;
928  DIE(("mk_separate_dir: unlink returned error on file %s: %s\n", dirent->d_name, strerror(y)));
929  }
930  }
931  }
932  closedir(sdir); // cppcheck detected leak
933  }
934 #endif
935  }
936 
937  DEBUG_RET();
938 }
939 
940 
942  int x;
943  DEBUG_ENT("close_separate_dir");
944  if (chdir("..")) {
945  x = errno;
946  DIE(("close_separate_dir: Cannot go up dir (..): %s\n", strerror(x)));
947  }
948  DEBUG_RET();
949  return 0;
950 }
951 
952 
953 void mk_separate_file(struct file_ll *f, int32_t t, char *extension, int openit) {
954  DEBUG_ENT("mk_separate_file");
955  DEBUG_INFO(("opening next file to save email type %s\n", item_type_to_name(t)));
956  if (f->item_count > 999999999) { // bigger than nine 9's
957  DIE(("mk_separate_file: The number of emails in this folder has become too high to handle\n"));
958  }
959  sprintf(f->name[t], SEP_MAIL_FILE_TEMPLATE, f->item_count, extension);
960  check_filename(f->name[t]);
961  if (openit) {
962  if (!(f->output[t] = fopen(f->name[t], "w"))) {
963  DIE(("mk_separate_file: Cannot open file to save email \"%s\"\n", f->name[t]));
964  }
965  }
966  DEBUG_RET();
967 }
968 
969 
970 void close_separate_file(struct file_ll *f) {
971  int32_t t;
972  DEBUG_ENT("close_separate_file");
973  for (t=0; t<PST_TYPE_MAX; t++) {
974  if (f->output[t]) {
975  struct stat st;
976  fclose(f->output[t]);
977  stat(f->name[t], &st);
978  if (!st.st_size) {
979  DEBUG_WARN(("removing empty output file %s\n", f->name[t]));
980  remove(f->name[t]);
981  }
982  f->output[t] = NULL;
983  }
984  }
985  DEBUG_RET();
986 }
987 
988 
989 char *my_stristr(char *haystack, char *needle) {
990  // my_stristr varies from strstr in that its searches are case-insensitive
991  char *x=haystack, *y=needle, *z = NULL;
992  if (!haystack || !needle) {
993  return NULL;
994  }
995  while (*y != '\0' && *x != '\0') {
996  if (tolower(*y) == tolower(*x)) {
997  // move y on one
998  y++;
999  if (!z) {
1000  z = x; // store first position in haystack where a match is made
1001  }
1002  } else {
1003  y = needle; // reset y to the beginning of the needle
1004  z = NULL; // reset the haystack storage point
1005  }
1006  x++; // advance the search in the haystack
1007  }
1008  // If the haystack ended before our search finished, it's not a match.
1009  if (*y != '\0') return NULL;
1010  return z;
1011 }
1012 
1013 
1014 void check_filename(char *fname) {
1015  char *t = fname;
1016  DEBUG_ENT("check_filename");
1017  if (!t) {
1018  DEBUG_RET();
1019  return;
1020  }
1021  while ((t = strpbrk(t, "/\\:"))) {
1022  // while there are characters in the second string that we don't want
1023  *t = '_'; //replace them with an underscore
1024  }
1025  DEBUG_RET();
1026 }
1027 
1028 
1036 {
1037  if (!acceptable_extensions || *acceptable_extensions == '\0') return 1; // acceptable list missing or empty
1038  char *attach_filename = (attach->filename2.str) ? attach->filename2.str
1039  : attach->filename1.str;
1040  if (!attach_filename) return 1; // attachment with no name is always acceptable
1041  char *e = strrchr(attach_filename, '.');
1042  if (!e) return 1; // attachment with no extension is always acceptable.
1043  DEBUG_ENT("acceptable_ext");
1044  DEBUG_INFO(("attachment extension %s\n", e));
1045  int rc = 0;
1046  char *a = acceptable_extensions;
1047  while (*a) {
1048  if (pst_stricmp(a, e) == 0) {
1049  rc = 1;
1050  break;
1051  }
1052  a += strlen(a) + 1;
1053  }
1054  DEBUG_INFO(("attachment acceptable returns %d\n", rc));
1055  DEBUG_RET();
1056  return rc;
1057 }
1058 
1059 
1060 void write_separate_attachment(char f_name[], pst_item_attach* attach, int attach_num, pst_file* pst)
1061 {
1062  FILE *fp = NULL;
1063  int x = 0;
1064  char *temp = NULL;
1065 
1066  // If there is a long filename (filename2) use that, otherwise
1067  // use the 8.3 filename (filename1)
1068  char *attach_filename = (attach->filename2.str) ? attach->filename2.str
1069  : attach->filename1.str;
1070  DEBUG_ENT("write_separate_attachment");
1071  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));
1072 
1073  if (!attach->data.data) {
1074  // make sure we can fetch data from the id
1075  pst_index_ll *ptr = pst_getID(pst, attach->i_id);
1076  if (!ptr) {
1077  DEBUG_WARN(("Couldn't find i_id %#"PRIx64". Cannot save attachment to file\n", attach->i_id));
1078  DEBUG_RET();
1079  return;
1080  }
1081  }
1082 
1083  check_filename(f_name);
1084  if (!attach_filename) {
1085  // generate our own (dummy) filename for the attachment
1086  temp = pst_malloc(strlen(f_name)+15);
1087  sprintf(temp, "%s-attach%i", f_name, attach_num);
1088  } else {
1089  // have an attachment name, make sure it's unique
1090  temp = pst_malloc(strlen(f_name)+strlen(attach_filename)+15);
1091  do {
1092  if (fp) fclose(fp);
1093  if (x == 0)
1094  sprintf(temp, "%s-%s", f_name, attach_filename);
1095  else
1096  sprintf(temp, "%s-%s-%i", f_name, attach_filename, x);
1097  } while ((fp = fopen(temp, "r")) && ++x < 99999999);
1098  if (x > 99999999) {
1099  DIE(("error finding attachment name. exhausted possibilities to %s\n", temp));
1100  }
1101  }
1102  DEBUG_INFO(("Saving attachment to %s\n", temp));
1103  if (!(fp = fopen(temp, "w"))) {
1104  DEBUG_WARN(("write_separate_attachment: Cannot open attachment save file \"%s\"\n", temp));
1105  } else {
1106  (void)pst_attach_to_file(pst, attach, fp);
1107  fclose(fp);
1108  }
1109  if (temp) free(temp);
1110  DEBUG_RET();
1111 }
1112 
1113 
1114 void write_embedded_message(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pf, int save_rtf, char** extra_mime_headers)
1115 {
1116  pst_index_ll *ptr;
1117  DEBUG_ENT("write_embedded_message");
1118  ptr = pst_getID(pf, attach->i_id);
1119 
1120  pst_desc_tree d_ptr;
1121  d_ptr.d_id = 0;
1122  d_ptr.parent_d_id = 0;
1123  d_ptr.assoc_tree = NULL;
1124  d_ptr.desc = ptr;
1125  d_ptr.no_child = 0;
1126  d_ptr.prev = NULL;
1127  d_ptr.next = NULL;
1128  d_ptr.parent = NULL;
1129  d_ptr.child = NULL;
1130  d_ptr.child_tail = NULL;
1131 
1132  pst_item *item = pst_parse_item(pf, &d_ptr, attach->id2_head);
1133  // It appears that if the embedded message contains an appointment/
1134  // calendar item, pst_parse_item returns NULL due to the presence of
1135  // an unexpected reference type of 0x1048, which seems to represent
1136  // an array of GUIDs representing a CLSID. It's likely that this is
1137  // a reference to an internal Outlook COM class.
1138  // Log the skipped item and continue on.
1139  if (!item) {
1140  DEBUG_WARN(("write_embedded_message: pst_parse_item was unable to parse the embedded message in attachment ID %llu", attach->i_id));
1141  } else {
1142  if (!item->email) {
1143  DEBUG_WARN(("write_embedded_message: pst_parse_item returned type %d, not an email message", item->type));
1144  } else {
1145  fprintf(f_output, "\n--%s\n", boundary);
1146  fprintf(f_output, "Content-Type: %s\n\n", attach->mimetype.str);
1147  write_normal_email(f_output, "", item, MODE_NORMAL, 0, pf, save_rtf, 1, extra_mime_headers);
1148  }
1149  pst_freeItem(item);
1150  }
1151 
1152  DEBUG_RET();
1153 }
1154 
1158 char *quote_string(char *inp) {
1159  int i = 0;
1160  int count = 0;
1161  char *curr = inp;
1162  while (*curr) {
1163  if (*curr == '\"' || *curr == '\\') {
1164  count++;
1165  }
1166  curr++;
1167  i++;
1168  }
1169  char *res = malloc(i + count + 1);
1170  char *curr_in = inp;
1171  char *curr_out = res;
1172  while (*curr_in) {
1173  if (*curr_in == '\"' || *curr_in == '\\') {
1174  *curr_out++ = '\\';
1175  }
1176  *curr_out++ = *curr_in++;
1177  }
1178  *curr_out = '\0';
1179  return res;
1180 }
1181 
1187 char *rfc2231_string(char *inp) {
1188  int needs = 0;
1189  const int8_t *x = (int8_t *)inp;
1190  while (*x) {
1191  if (*x <= 32) needs++;
1192  x++;
1193  }
1194  int n = strlen(inp) + 2*needs + 15;
1195  char *buffer = pst_malloc(n);
1196  strcpy(buffer, "utf-8''");
1197  x = (int8_t *)inp;
1198  const uint8_t *y = (uint8_t *)inp;
1199  uint8_t *z = (uint8_t *)buffer;
1200  z += strlen(buffer); // skip the utf8 prefix
1201  while (*y) {
1202  if (*x <= 32) {
1203  *(z++) = (uint8_t)'%';
1204  snprintf(z, 3, "%2x", *y);
1205  z += 2;
1206  }
1207  else {
1208  *(z++) = *y;
1209  }
1210  x++;
1211  y++;
1212  }
1213  *z = '\0';
1214  return buffer;
1215 }
1216 
1217 void write_inline_attachment(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pst)
1218 {
1219  DEBUG_ENT("write_inline_attachment");
1220  DEBUG_INFO(("Attachment Size is %#"PRIx64", data = %#"PRIxPTR", id %#"PRIx64"\n", (uint64_t)attach->data.size, attach->data.data, attach->i_id));
1221 
1222  if (!attach->data.data) {
1223  // make sure we can fetch data from the id
1224  pst_index_ll *ptr = pst_getID(pst, attach->i_id);
1225  if (!ptr) {
1226  DEBUG_WARN(("Couldn't find ID pointer. Cannot save attachment to file\n"));
1227  DEBUG_RET();
1228  return;
1229  }
1230  }
1231 
1232  fprintf(f_output, "\n--%s\n", boundary);
1233  if (!attach->mimetype.str) {
1234  fprintf(f_output, "Content-Type: %s\n", MIME_TYPE_DEFAULT);
1235  } else {
1236  fprintf(f_output, "Content-Type: %s\n", attach->mimetype.str);
1237  }
1238  fprintf(f_output, "Content-Transfer-Encoding: base64\n");
1239 
1240  if (attach->content_id.str) {
1241  fprintf(f_output, "Content-ID: <%s>\n", attach->content_id.str);
1242  }
1243 
1244  if (attach->filename2.str) {
1245  // use the long filename, converted to proper encoding if needed.
1246  // it is already utf8
1247  char *escaped = quote_string(attach->filename2.str);
1248  // encode long filename as rfc2231 without modifying original -- we may still need the original long filename
1249  char *rfc2231 = rfc2231_string(attach->filename2.str);
1250  fprintf(f_output, "Content-Disposition: attachment; \n filename*=%s;\n", rfc2231);
1251  free (rfc2231);
1252  // Also include the (escaped) utf8 filename in the 'filename' header directly - this is not strictly valid
1253  // (since this header should be ASCII) but is almost always handled correctly (and in fact this is the only
1254  // way to get MS Outlook to correctly read a UTF8 filename, AFAICT, which is why we're doing it).
1255  fprintf(f_output, " filename=\"%s\"\n\n", escaped);
1256  free(escaped);
1257  }
1258  else if (attach->filename1.str) {
1259  // short filename never needs encoding
1260  fprintf(f_output, "Content-Disposition: attachment; filename=\"%s\"\n\n", attach->filename1.str);
1261  }
1262  else {
1263  // no filename is inline
1264  fprintf(f_output, "Content-Disposition: inline\n\n");
1265  }
1266 
1267  (void)pst_attach_to_file_base64(pst, attach, f_output);
1268  fprintf(f_output, "\n\n");
1269  DEBUG_RET();
1270 }
1271 
1272 
1273 int header_match(char *header, char*field) {
1274  int n = strlen(field);
1275  if (strncasecmp(header, field, n) == 0) return 1; // tag:{space}
1276  if ((field[n-1] == ' ') && (strncasecmp(header, field, n-1) == 0)) {
1277  char *crlftab = "\r\n\t";
1278  DEBUG_INFO(("Possible wrapped header = %s\n", header));
1279  if (strncasecmp(header+n-1, crlftab, 3) == 0) return 1; // tag:{cr}{lf}{tab}
1280  }
1281  return 0;
1282 }
1283 
1284 int valid_headers(char *header)
1285 {
1286  // headers are sometimes really bogus - they seem to be fragments of the
1287  // message body, so we only use them if they seem to be real rfc822 headers.
1288  // this list is composed of ones that we have seen in real pst files.
1289  // there are surely others. the problem is - given an arbitrary character
1290  // string, is it a valid (or even reasonable) set of rfc822 headers?
1291  if (header) {
1292  if (header_match(header, "Content-Type: " )) return 1;
1293  if (header_match(header, "Date: " )) return 1;
1294  if (header_match(header, "From: " )) return 1;
1295  if (header_match(header, "MIME-Version: " )) return 1;
1296  if (header_match(header, "Microsoft Mail Internet Headers")) return 1;
1297  if (header_match(header, "Received: " )) return 1;
1298  if (header_match(header, "Return-Path: " )) return 1;
1299  if (header_match(header, "Subject: " )) return 1;
1300  if (header_match(header, "To: " )) return 1;
1301  if (header_match(header, "X-ASG-Debug-ID: " )) return 1;
1302  if (header_match(header, "X-Barracuda-URL: " )) return 1;
1303  if (header_match(header, "X-x: " )) return 1;
1304  if (strlen(header) > 2) {
1305  DEBUG_INFO(("Ignore bogus headers = %s\n", header));
1306  }
1307  return 0;
1308  }
1309  else return 0;
1310 }
1311 
1312 
1313 void header_has_field(char *header, char *field, int *flag)
1314 {
1315  DEBUG_ENT("header_has_field");
1316  if (my_stristr(header, field) || (strncasecmp(header, field+1, strlen(field)-1) == 0)) {
1317  DEBUG_INFO(("header block has %s header\n", field+1));
1318  *flag = 1;
1319  }
1320  DEBUG_RET();
1321 }
1322 
1323 
1324 void header_get_subfield(char *field, const char *subfield, char *body_subfield, size_t size_subfield)
1325 {
1326  if (!field) return;
1327  DEBUG_ENT("header_get_subfield");
1328  char search[60];
1329  snprintf(search, sizeof(search), " %s=", subfield);
1330  field++;
1331  char *n = header_end_field(field);
1332  char *s = my_stristr(field, search);
1333  if (n && s && (s < n)) {
1334  char *e, *f, save;
1335  s += strlen(search); // skip over subfield=
1336  if (*s == '"') {
1337  s++;
1338  e = strchr(s, '"');
1339  }
1340  else {
1341  e = strchr(s, ';');
1342  f = strchr(s, '\n');
1343  if (e && f && (f < e)) e = f;
1344  }
1345  if (!e || (e > n)) e = n; // use the trailing lf as terminator if nothing better
1346  save = *e;
1347  *e = '\0';
1348  snprintf(body_subfield, size_subfield, "%s", s); // copy the subfield to our buffer
1349  *e = save;
1350  DEBUG_INFO(("body %s %s from headers\n", subfield, body_subfield));
1351  }
1352  DEBUG_RET();
1353 }
1354 
1355 char* header_get_field(char *header, char *field)
1356 {
1357  char *t = my_stristr(header, field);
1358  if (!t && (strncasecmp(header, field+1, strlen(field)-1) == 0)) t = header;
1359  return t;
1360 }
1361 
1362 
1363 // return pointer to \n at the end of this header field,
1364 // or NULL if this field goes to the end of the string.
1365 char *header_end_field(char *field)
1366 {
1367  char *e = strchr(field+1, '\n');
1368  while (e && ((e[1] == ' ') || (e[1] == '\t'))) {
1369  e = strchr(e+1, '\n');
1370  }
1371  return e;
1372 }
1373 
1374 
1375 void header_strip_field(char *header, char *field)
1376 {
1377  char *t;
1378  while ((t = header_get_field(header, field))) {
1379  char *e = header_end_field(t);
1380  if (e) {
1381  if (t == header) e++; // if *t is not \n, we don't want to keep the \n at *e either.
1382  while (*e != '\0') {
1383  *t = *e;
1384  t++;
1385  e++;
1386  }
1387  *t = '\0';
1388  }
1389  else {
1390  // this was the last header field, truncate the headers
1391  *t = '\0';
1392  }
1393  }
1394 }
1395 
1396 
1397 int test_base64(char *body, size_t len)
1398 {
1399  int b64 = 0;
1400  uint8_t *b = (uint8_t *)body;
1401  DEBUG_ENT("test_base64");
1402  while (len--) {
1403  if ((*b < 32) && (*b != 9) && (*b != 10)) {
1404  DEBUG_INFO(("found base64 byte %d\n", (int)*b));
1405  DEBUG_HEXDUMPC(body, strlen(body), 0x10);
1406  b64 = 1;
1407  break;
1408  }
1409  b++;
1410  }
1411  DEBUG_RET();
1412  return b64;
1413 }
1414 
1415 
1416 void find_html_charset(char *html, char *charset, size_t charsetlen)
1417 {
1418  const int index = 1;
1419  const int nmatch = index+1;
1420  regmatch_t match[nmatch];
1421  DEBUG_ENT("find_html_charset");
1422  int rc = regexec(&meta_charset_pattern, html, nmatch, match, 0);
1423  if (rc == 0) {
1424  int s = match[index].rm_so;
1425  int e = match[index].rm_eo;
1426  if (s != -1) {
1427  char save = html[e];
1428  html[e] = '\0';
1429  snprintf(charset, charsetlen, "%s", html+s); // copy the html charset
1430  html[e] = save;
1431  DEBUG_INFO(("charset %s from html text\n", charset));
1432  }
1433  else {
1434  DEBUG_INFO(("matching %d %d %d %d\n", match[0].rm_so, match[0].rm_eo, match[1].rm_so, match[1].rm_eo));
1435  DEBUG_HEXDUMPC(html, strlen(html), 0x10);
1436  }
1437  }
1438  else {
1439  DEBUG_INFO(("regexec returns %d\n", rc));
1440  }
1441  DEBUG_RET();
1442 }
1443 
1444 
1445 void find_rfc822_headers(char** extra_mime_headers)
1446 {
1447  DEBUG_ENT("find_rfc822_headers");
1448  char *headers = *extra_mime_headers;
1449  if (headers) {
1450  char *temp, *t;
1451  while ((temp = strstr(headers, "\n\n"))) {
1452  temp[1] = '\0';
1453  t = header_get_field(headers, "\nContent-Type:");
1454  if (t) {
1455  t++;
1456  DEBUG_INFO(("found content type header\n"));
1457  char *n = strchr(t, '\n');
1458  char *s = strstr(t, ": ");
1459  char *e = strchr(t, ';');
1460  if (!e || (e > n)) e = n;
1461  if (s && (s < e)) {
1462  s += 2;
1463  if (!strncasecmp(s, RFC822, e-s)) {
1464  headers = temp+2; // found rfc822 header
1465  DEBUG_INFO(("found 822 headers\n%s\n", headers));
1466  break;
1467  }
1468  }
1469  }
1470  //DEBUG_INFO(("skipping to next block after\n%s\n", headers));
1471  headers = temp+2; // skip to next chunk of headers
1472  }
1473  *extra_mime_headers = headers;
1474  }
1475  DEBUG_RET();
1476 }
1477 
1478 
1479 void write_body_part(FILE* f_output, pst_string *body, char *mime, char *charset, char *boundary, pst_file* pst)
1480 {
1481  DEBUG_ENT("write_body_part");
1482  removeCR(body->str);
1483  size_t body_len = strlen(body->str);
1484 
1485  if (body->is_utf8 && (strcasecmp("utf-8", charset))) {
1486  if (prefer_utf8) {
1487  charset = "utf-8";
1488  } else {
1489  // try to convert to the specified charset since the target
1490  // is not utf-8, and the data came from a unicode (utf16) field
1491  // and is now in utf-8.
1492  size_t rc;
1493  DEBUG_INFO(("Convert %s utf-8 to %s\n", mime, charset));
1494  pst_vbuf *newer = pst_vballoc(2);
1495  rc = pst_vb_utf8to8bit(newer, body->str, body_len, charset);
1496  if (rc == (size_t)-1) {
1497  // unable to convert, change the charset to utf8
1498  free(newer->b);
1499  DEBUG_INFO(("Failed to convert %s utf-8 to %s\n", mime, charset));
1500  charset = "utf-8";
1501  } else {
1502  // null terminate the output string
1503  pst_vbgrow(newer, 1);
1504  newer->b[newer->dlen] = '\0';
1505  free(body->str);
1506  body->str = newer->b;
1507  body_len = newer->dlen;
1508  }
1509  free(newer);
1510  }
1511  }
1512  int base64 = test_base64(body->str, body_len);
1513  fprintf(f_output, "\n--%s\n", boundary);
1514  fprintf(f_output, "Content-Type: %s; charset=\"%s\"\n", mime, charset);
1515  if (base64) fprintf(f_output, "Content-Transfer-Encoding: base64\n");
1516  fprintf(f_output, "\n");
1517  // Any body that uses an encoding with NULLs, e.g. UTF16, will be base64-encoded here.
1518  if (base64) {
1519  char *enc = pst_base64_encode(body->str, body_len);
1520  if (enc) {
1521  write_email_body(f_output, enc);
1522  fprintf(f_output, "\n");
1523  free(enc);
1524  }
1525  }
1526  else {
1527  write_email_body(f_output, body->str);
1528  }
1529  DEBUG_RET();
1530 }
1531 
1532 
1533 void write_schedule_part_data(FILE* f_output, pst_item* item, const char* sender, const char* method)
1534 {
1535  fprintf(f_output, "BEGIN:VCALENDAR\n");
1536  fprintf(f_output, "VERSION:2.0\n");
1537  fprintf(f_output, "PRODID:LibPST v%s\n", VERSION);
1538  if (method) fprintf(f_output, "METHOD:%s\n", method);
1539  fprintf(f_output, "BEGIN:VEVENT\n");
1540  if (sender) {
1541  if (item->email->outlook_sender_name.str) {
1542  fprintf(f_output, "ORGANIZER;CN=\"%s\":MAILTO:%s\n", item->email->outlook_sender_name.str, sender);
1543  } else {
1544  fprintf(f_output, "ORGANIZER;CN=\"\":MAILTO:%s\n", sender);
1545  }
1546  }
1547  write_appointment(f_output, item);
1548  fprintf(f_output, "END:VCALENDAR\n");
1549 }
1550 
1551 
1552 void write_schedule_part(FILE* f_output, pst_item* item, const char* sender, const char* boundary)
1553 {
1554  const char* method = "REQUEST";
1555  const char* charset = "utf-8";
1556  char fname[30];
1557  if (!item->appointment) return;
1558 
1559  // inline appointment request
1560  fprintf(f_output, "\n--%s\n", boundary);
1561  fprintf(f_output, "Content-Type: %s; method=\"%s\"; charset=\"%s\"\n\n", "text/calendar", method, charset);
1562  write_schedule_part_data(f_output, item, sender, method);
1563  fprintf(f_output, "\n");
1564 
1565  // attachment appointment request
1566  snprintf(fname, sizeof(fname), "i%i.ics", rand());
1567  fprintf(f_output, "\n--%s\n", boundary);
1568  fprintf(f_output, "Content-Type: %s; charset=\"%s\"; name=\"%s\"\n", "text/calendar", "utf-8", fname);
1569  fprintf(f_output, "Content-Disposition: attachment; filename=\"%s\"\n\n", fname);
1570  write_schedule_part_data(f_output, item, sender, method);
1571  fprintf(f_output, "\n");
1572 }
1573 
1574 
1575 void write_normal_email(FILE* f_output, char f_name[], pst_item* item, int mode, int mode_MH, pst_file* pst, int save_rtf, int embedding, char** extra_mime_headers)
1576 {
1577  char boundary[60];
1578  char altboundary[66];
1579  char *altboundaryp = NULL;
1580  char body_charset[30];
1581  char buffer_charset[30];
1582  char body_report[60];
1583  char sender[60];
1584  int sender_known = 0;
1585  char *temp = NULL;
1586  time_t em_time;
1587  char *c_time;
1588  char *headers = NULL;
1589  int has_from, has_subject, has_to, has_cc, has_date, has_msgid;
1590  has_from = has_subject = has_to = has_cc = has_date = has_msgid = 0;
1591  DEBUG_ENT("write_normal_email");
1592 
1593  pst_convert_utf8_null(item, &item->email->header);
1594  headers = valid_headers(item->email->header.str) ? item->email->header.str :
1595  valid_headers(*extra_mime_headers) ? *extra_mime_headers :
1596  NULL;
1597 
1598  // setup default body character set and report type
1599  strncpy(body_charset, pst_default_charset(item, sizeof(buffer_charset), buffer_charset), sizeof(body_charset));
1600  body_charset[sizeof(body_charset)-1] = '\0';
1601  strncpy(body_report, "delivery-status", sizeof(body_report));
1602  body_report[sizeof(body_report)-1] = '\0';
1603 
1604  // setup default sender
1605  pst_convert_utf8(item, &item->email->sender_address);
1606  if (item->email->sender_address.str && strchr(item->email->sender_address.str, '@')) {
1607  temp = item->email->sender_address.str;
1608  sender_known = 1;
1609  }
1610  else {
1611  temp = "MAILER-DAEMON";
1612  }
1613  strncpy(sender, temp, sizeof(sender));
1614  sender[sizeof(sender)-1] = '\0';
1615 
1616  // convert the sent date if it exists, or set it to a fixed date
1617  if (item->email->sent_date) {
1618  em_time = pst_fileTimeToUnixTime(item->email->sent_date);
1619  c_time = ctime(&em_time);
1620  if (c_time)
1621  c_time[strlen(c_time)-1] = '\0'; //remove end \n
1622  else
1623  c_time = "Thu Jan 1 00:00:00 1970";
1624  } else
1625  c_time = "Thu Jan 1 00:00:00 1970";
1626 
1627  // create our MIME boundaries here.
1628  snprintf(boundary, sizeof(boundary), "--boundary-LibPST-iamunique-%i_-_-", rand());
1629  snprintf(altboundary, sizeof(altboundary), "alt-%s", boundary);
1630 
1631  // we will always look at the headers to discover some stuff
1632  if (headers ) {
1633  char *t;
1634  removeCR(headers);
1635 
1636  temp = strstr(headers, "\n\n");
1637  if (temp) {
1638  // cut off our real rfc822 headers here
1639  temp[1] = '\0';
1640  // pointer to all the embedded MIME headers.
1641  // we use these to find the actual rfc822 headers for embedded message/rfc822 mime parts
1642  // but only for the outermost message
1643  if (!*extra_mime_headers) *extra_mime_headers = temp+2;
1644  DEBUG_INFO(("Found extra mime headers\n%s\n", temp+2));
1645  }
1646 
1647  // Check if the headers have all the necessary fields
1648  header_has_field(headers, "\nFrom:", &has_from);
1649  header_has_field(headers, "\nTo:", &has_to);
1650  header_has_field(headers, "\nSubject:", &has_subject);
1651  header_has_field(headers, "\nDate:", &has_date);
1652  header_has_field(headers, "\nCC:", &has_cc);
1653  header_has_field(headers, "\nMessage-Id:", &has_msgid);
1654 
1655  // look for charset and report-type in Content-Type header
1656  t = header_get_field(headers, "\nContent-Type:");
1657  header_get_subfield(t, "charset", body_charset, sizeof(body_charset));
1658  header_get_subfield(t, "report-type", body_report, sizeof(body_report));
1659 
1660  // derive a proper sender email address
1661  if (!sender_known) {
1662  t = header_get_field(headers, "\nFrom:");
1663  if (t) {
1664  // assume address is on the first line, rather than on a continuation line
1665  t++;
1666  char *n = strchr(t, '\n');
1667  char *s = strchr(t, '<');
1668  char *e = strchr(t, '>');
1669  if (s && e && n && (s < e) && (e < n)) {
1670  char save = *e;
1671  *e = '\0';
1672  snprintf(sender, sizeof(sender), "%s", s+1);
1673  *e = save;
1674  }
1675  }
1676  }
1677 
1678  // Strip out the mime headers and some others that we don't want to emit
1679  header_strip_field(headers, "\nMicrosoft Mail Internet Headers");
1680  header_strip_field(headers, "\nMIME-Version:");
1681  header_strip_field(headers, "\nContent-Type:");
1682  header_strip_field(headers, "\nContent-Transfer-Encoding:");
1683  header_strip_field(headers, "\nContent-class:");
1684  header_strip_field(headers, "\nX-MimeOLE:");
1685  header_strip_field(headers, "\nX-From_:");
1686  }
1687 
1688  DEBUG_INFO(("About to print Header\n"));
1689 
1690  if (item && item->subject.str) {
1691  pst_convert_utf8(item, &item->subject);
1692  DEBUG_INFO(("item->subject = %s\n", item->subject.str));
1693  }
1694 
1695  if (mode != MODE_SEPARATE) {
1696  // most modes need this separator line.
1697  // procmail produces this separator without the quotes around the
1698  // sender email address, but apparently some Mac email client needs
1699  // those quotes, and they don't seem to cause problems for anyone else.
1700  char *quo = (embedding) ? ">" : "";
1701  fprintf(f_output, "%sFrom \"%s\" %s\n", quo, sender, c_time);
1702  }
1703 
1704  // print the supplied email headers
1705  if (headers) {
1706  int len = strlen(headers);
1707  if (len > 0) {
1708  fprintf(f_output, "%s", headers);
1709  // make sure the headers end with a \n
1710  if (headers[len-1] != '\n') fprintf(f_output, "\n");
1711  //char *h = headers;
1712  //while (*h) {
1713  // char *e = strchr(h, '\n');
1714  // int d = 1; // normally e points to trailing \n
1715  // if (!e) {
1716  // e = h + strlen(h); // e points to trailing null
1717  // d = 0;
1718  // }
1719  // // we could do rfc2047 encoding here if needed
1720  // fprintf(f_output, "%.*s\n", (int)(e-h), h);
1721  // h = e + d;
1722  //}
1723  }
1724  }
1725 
1726  // record read status
1727  if ((item->flags & PST_FLAG_READ) == PST_FLAG_READ) {
1728  fprintf(f_output, "Status: RO\n");
1729  }
1730 
1731  // create required header fields that are not already written
1732 
1733  if (!has_from) {
1734  if (item->email->outlook_sender_name.str){
1735  pst_rfc2047(item, &item->email->outlook_sender_name, 1);
1736  fprintf(f_output, "From: %s <%s>\n", item->email->outlook_sender_name.str, sender);
1737  } else {
1738  fprintf(f_output, "From: <%s>\n", sender);
1739  }
1740  }
1741 
1742  if (!has_subject) {
1743  if (item->subject.str) {
1744  pst_rfc2047(item, &item->subject, 0);
1745  fprintf(f_output, "Subject: %s\n", item->subject.str);
1746  } else {
1747  fprintf(f_output, "Subject: \n");
1748  }
1749  }
1750 
1751  if (!has_to && item->email->sentto_address.str) {
1752  pst_rfc2047(item, &item->email->sentto_address, 0);
1753  fprintf(f_output, "To: %s\n", item->email->sentto_address.str);
1754  }
1755 
1756  if (!has_cc && item->email->cc_address.str) {
1757  pst_rfc2047(item, &item->email->cc_address, 0);
1758  fprintf(f_output, "Cc: %s\n", item->email->cc_address.str);
1759  }
1760 
1761  if (!has_date && item->email->sent_date) {
1762  char c_time[C_TIME_SIZE];
1763  struct tm stm;
1764  gmtime_r(&em_time, &stm);
1765  strftime(c_time, C_TIME_SIZE, "%a, %d %b %Y %H:%M:%S %z", &stm);
1766  fprintf(f_output, "Date: %s\n", c_time);
1767  }
1768 
1769  if (!has_msgid && item->email->messageid.str) {
1770  pst_convert_utf8(item, &item->email->messageid);
1771  fprintf(f_output, "Message-Id: %s\n", item->email->messageid.str);
1772  }
1773 
1774  // add forensic headers to capture some .pst stuff that is not really
1775  // needed or used by mail clients
1777  if (item->email->sender_address.str && !strchr(item->email->sender_address.str, '@')
1778  && strcmp(item->email->sender_address.str, ".")
1779  && (strlen(item->email->sender_address.str) > 0)) {
1780  fprintf(f_output, "X-libpst-forensic-sender: %s\n", item->email->sender_address.str);
1781  }
1782 
1783  if (item->email->bcc_address.str) {
1784  pst_convert_utf8(item, &item->email->bcc_address);
1785  fprintf(f_output, "X-libpst-forensic-bcc: %s\n", item->email->bcc_address.str);
1786  }
1787 
1788  // add our own mime headers
1789  fprintf(f_output, "MIME-Version: 1.0\n");
1790  if (item->type == PST_TYPE_REPORT) {
1791  // multipart/report for DSN/MDN reports
1792  fprintf(f_output, "Content-Type: multipart/report; report-type=%s;\n\tboundary=\"%s\"\n", body_report, boundary);
1793  }
1794  else {
1795  fprintf(f_output, "Content-Type: multipart/mixed;\n\tboundary=\"%s\"\n", boundary);
1796  }
1797  fprintf(f_output, "\n"); // end of headers, start of body
1798 
1799  // now dump the body parts
1800  if ((item->type == PST_TYPE_REPORT) && (item->email->report_text.str)) {
1801  write_body_part(f_output, &item->email->report_text, "text/plain", body_charset, boundary, pst);
1802  fprintf(f_output, "\n");
1803  }
1804 
1805  if (item->body.str && item->email->htmlbody.str) {
1806  // start the nested alternative part
1807  fprintf(f_output, "\n--%s\n", boundary);
1808  fprintf(f_output, "Content-Type: multipart/alternative;\n\tboundary=\"%s\"\n", altboundary);
1809  altboundaryp = altboundary;
1810  }
1811  else {
1812  altboundaryp = boundary;
1813  }
1814 
1815  if (item->body.str) {
1816  write_body_part(f_output, &item->body, "text/plain", body_charset, altboundaryp, pst);
1817  }
1818 
1819  if (item->email->htmlbody.str) {
1820  find_html_charset(item->email->htmlbody.str, body_charset, sizeof(body_charset));
1821  write_body_part(f_output, &item->email->htmlbody, "text/html", body_charset, altboundaryp, pst);
1822  }
1823 
1824  if (item->body.str && item->email->htmlbody.str) {
1825  // end the nested alternative part
1826  fprintf(f_output, "\n--%s--\n", altboundary);
1827  }
1828 
1829  if (item->email->rtf_compressed.data && save_rtf) {
1831  DEBUG_INFO(("Adding RTF body as attachment\n"));
1832  memset(attach, 0, sizeof(pst_item_attach));
1833  attach->next = item->attach;
1834  item->attach = attach;
1835  attach->data.data = pst_lzfu_decompress(item->email->rtf_compressed.data, item->email->rtf_compressed.size, &attach->data.size);
1836  attach->filename2.str = strdup(RTF_ATTACH_NAME);
1837  attach->filename2.is_utf8 = 1;
1838  attach->mimetype.str = strdup(RTF_ATTACH_TYPE);
1839  attach->mimetype.is_utf8 = 1;
1840  }
1841 
1842  if (item->email->encrypted_body.data) {
1844  DEBUG_INFO(("Adding encrypted text body as attachment\n"));
1845  memset(attach, 0, sizeof(pst_item_attach));
1846  attach->next = item->attach;
1847  item->attach = attach;
1848  attach->data.data = item->email->encrypted_body.data;
1849  attach->data.size = item->email->encrypted_body.size;
1850  item->email->encrypted_body.data = NULL;
1851  }
1852 
1853  if (item->email->encrypted_htmlbody.data) {
1855  DEBUG_INFO(("Adding encrypted HTML body as attachment\n"));
1856  memset(attach, 0, sizeof(pst_item_attach));
1857  attach->next = item->attach;
1858  item->attach = attach;
1859  attach->data.data = item->email->encrypted_htmlbody.data;
1860  attach->data.size = item->email->encrypted_htmlbody.size;
1861  item->email->encrypted_htmlbody.data = NULL;
1862  }
1863 
1864  if (item->type == PST_TYPE_SCHEDULE) {
1865  write_schedule_part(f_output, item, sender, boundary);
1866  }
1867 
1868  // other attachments
1869  {
1870  pst_item_attach* attach;
1871  int attach_num = 0;
1872  for (attach = item->attach; attach; attach = attach->next) {
1873  pst_convert_utf8_null(item, &attach->filename1);
1874  pst_convert_utf8_null(item, &attach->filename2);
1875  pst_convert_utf8_null(item, &attach->mimetype);
1876  DEBUG_INFO(("Attempting Attachment encoding\n"));
1877  if (attach->method == PST_ATTACH_EMBEDDED) {
1878  DEBUG_INFO(("have an embedded rfc822 message attachment\n"));
1879  if (attach->mimetype.str) {
1880  DEBUG_INFO(("which already has a mime-type of %s\n", attach->mimetype.str));
1881  free(attach->mimetype.str);
1882  }
1883  attach->mimetype.str = strdup(RFC822);
1884  attach->mimetype.is_utf8 = 1;
1885  find_rfc822_headers(extra_mime_headers);
1886  write_embedded_message(f_output, attach, boundary, pst, save_rtf, extra_mime_headers);
1887  }
1888  else if (attach->data.data || attach->i_id) {
1889  if (acceptable_ext(attach)) {
1890  if (mode == MODE_SEPARATE && !mode_MH)
1891  write_separate_attachment(f_name, attach, ++attach_num, pst);
1892  else
1893  write_inline_attachment(f_output, attach, boundary, pst);
1894  }
1895  }
1896  }
1897  }
1898 
1899  fprintf(f_output, "\n--%s--\n\n", boundary);
1900  DEBUG_RET();
1901 }
1902 
1903 
1904 void write_vcard(FILE* f_output, pst_item* item, pst_item_contact* contact, char comment[])
1905 {
1906  char* result = NULL;
1907  size_t resultlen = 0;
1908  char time_buffer[30];
1909  // We can only call rfc escape once per printf, since the second call
1910  // may free the buffer returned by the first call.
1911  // I had tried to place those into a single printf - Carl.
1912 
1913  DEBUG_ENT("write_vcard");
1914 
1915  // make everything utf8
1916  pst_convert_utf8_null(item, &contact->fullname);
1917  pst_convert_utf8_null(item, &contact->surname);
1918  pst_convert_utf8_null(item, &contact->first_name);
1919  pst_convert_utf8_null(item, &contact->middle_name);
1920  pst_convert_utf8_null(item, &contact->display_name_prefix);
1921  pst_convert_utf8_null(item, &contact->suffix);
1922  pst_convert_utf8_null(item, &contact->nickname);
1923  pst_convert_utf8_null(item, &contact->address1);
1924  pst_convert_utf8_null(item, &contact->address2);
1925  pst_convert_utf8_null(item, &contact->address3);
1926  pst_convert_utf8_null(item, &contact->home_po_box);
1927  pst_convert_utf8_null(item, &contact->home_street);
1928  pst_convert_utf8_null(item, &contact->home_city);
1929  pst_convert_utf8_null(item, &contact->home_state);
1930  pst_convert_utf8_null(item, &contact->home_postal_code);
1931  pst_convert_utf8_null(item, &contact->home_country);
1932  pst_convert_utf8_null(item, &contact->home_address);
1933  pst_convert_utf8_null(item, &contact->business_po_box);
1934  pst_convert_utf8_null(item, &contact->business_street);
1935  pst_convert_utf8_null(item, &contact->business_city);
1936  pst_convert_utf8_null(item, &contact->business_state);
1938  pst_convert_utf8_null(item, &contact->business_country);
1939  pst_convert_utf8_null(item, &contact->business_address);
1940  pst_convert_utf8_null(item, &contact->other_po_box);
1941  pst_convert_utf8_null(item, &contact->other_street);
1942  pst_convert_utf8_null(item, &contact->other_city);
1943  pst_convert_utf8_null(item, &contact->other_state);
1944  pst_convert_utf8_null(item, &contact->other_postal_code);
1945  pst_convert_utf8_null(item, &contact->other_country);
1946  pst_convert_utf8_null(item, &contact->other_address);
1947  pst_convert_utf8_null(item, &contact->business_fax);
1948  pst_convert_utf8_null(item, &contact->business_phone);
1949  pst_convert_utf8_null(item, &contact->business_phone2);
1950  pst_convert_utf8_null(item, &contact->car_phone);
1951  pst_convert_utf8_null(item, &contact->home_fax);
1952  pst_convert_utf8_null(item, &contact->home_phone);
1953  pst_convert_utf8_null(item, &contact->home_phone2);
1954  pst_convert_utf8_null(item, &contact->isdn_phone);
1955  pst_convert_utf8_null(item, &contact->mobile_phone);
1956  pst_convert_utf8_null(item, &contact->other_phone);
1957  pst_convert_utf8_null(item, &contact->pager_phone);
1958  pst_convert_utf8_null(item, &contact->primary_fax);
1959  pst_convert_utf8_null(item, &contact->primary_phone);
1960  pst_convert_utf8_null(item, &contact->radio_phone);
1961  pst_convert_utf8_null(item, &contact->telex);
1962  pst_convert_utf8_null(item, &contact->job_title);
1963  pst_convert_utf8_null(item, &contact->profession);
1964  pst_convert_utf8_null(item, &contact->assistant_name);
1965  pst_convert_utf8_null(item, &contact->assistant_phone);
1966  pst_convert_utf8_null(item, &contact->company_name);
1967  pst_convert_utf8_null(item, &item->body);
1968 
1969  // the specification I am following is (hopefully) RFC2426 vCard Mime Directory Profile
1970  fprintf(f_output, "BEGIN:VCARD\n");
1971  fprintf(f_output, "FN:%s\n", pst_rfc2426_escape(contact->fullname.str, &result, &resultlen));
1972 
1973  //fprintf(f_output, "N:%s;%s;%s;%s;%s\n",
1974  fprintf(f_output, "N:%s;", (!contact->surname.str) ? "" : pst_rfc2426_escape(contact->surname.str, &result, &resultlen));
1975  fprintf(f_output, "%s;", (!contact->first_name.str) ? "" : pst_rfc2426_escape(contact->first_name.str, &result, &resultlen));
1976  fprintf(f_output, "%s;", (!contact->middle_name.str) ? "" : pst_rfc2426_escape(contact->middle_name.str, &result, &resultlen));
1977  fprintf(f_output, "%s;", (!contact->display_name_prefix.str) ? "" : pst_rfc2426_escape(contact->display_name_prefix.str, &result, &resultlen));
1978  fprintf(f_output, "%s\n", (!contact->suffix.str) ? "" : pst_rfc2426_escape(contact->suffix.str, &result, &resultlen));
1979 
1980  if (contact->nickname.str)
1981  fprintf(f_output, "NICKNAME:%s\n", pst_rfc2426_escape(contact->nickname.str, &result, &resultlen));
1982  if (contact->address1.str)
1983  fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address1.str, &result, &resultlen));
1984  if (contact->address2.str)
1985  fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address2.str, &result, &resultlen));
1986  if (contact->address3.str)
1987  fprintf(f_output, "EMAIL:%s\n", pst_rfc2426_escape(contact->address3.str, &result, &resultlen));
1988  if (contact->birthday)
1989  fprintf(f_output, "BDAY:%s\n", pst_rfc2425_datetime_format(contact->birthday, sizeof(time_buffer), time_buffer));
1990 
1991  if (contact->home_address.str) {
1992  //fprintf(f_output, "ADR;TYPE=home:%s;%s;%s;%s;%s;%s;%s\n",
1993  fprintf(f_output, "ADR;TYPE=home:%s;", (!contact->home_po_box.str) ? "" : pst_rfc2426_escape(contact->home_po_box.str, &result, &resultlen));
1994  fprintf(f_output, "%s;", ""); // extended Address
1995  fprintf(f_output, "%s;", (!contact->home_street.str) ? "" : pst_rfc2426_escape(contact->home_street.str, &result, &resultlen));
1996  fprintf(f_output, "%s;", (!contact->home_city.str) ? "" : pst_rfc2426_escape(contact->home_city.str, &result, &resultlen));
1997  fprintf(f_output, "%s;", (!contact->home_state.str) ? "" : pst_rfc2426_escape(contact->home_state.str, &result, &resultlen));
1998  fprintf(f_output, "%s;", (!contact->home_postal_code.str) ? "" : pst_rfc2426_escape(contact->home_postal_code.str, &result, &resultlen));
1999  fprintf(f_output, "%s\n", (!contact->home_country.str) ? "" : pst_rfc2426_escape(contact->home_country.str, &result, &resultlen));
2000  fprintf(f_output, "LABEL;TYPE=home:%s\n", pst_rfc2426_escape(contact->home_address.str, &result, &resultlen));
2001  }
2002 
2003  if (contact->business_address.str) {
2004  //fprintf(f_output, "ADR;TYPE=work:%s;%s;%s;%s;%s;%s;%s\n",
2005  fprintf(f_output, "ADR;TYPE=work:%s;", (!contact->business_po_box.str) ? "" : pst_rfc2426_escape(contact->business_po_box.str, &result, &resultlen));
2006  fprintf(f_output, "%s;", ""); // extended Address
2007  fprintf(f_output, "%s;", (!contact->business_street.str) ? "" : pst_rfc2426_escape(contact->business_street.str, &result, &resultlen));
2008  fprintf(f_output, "%s;", (!contact->business_city.str) ? "" : pst_rfc2426_escape(contact->business_city.str, &result, &resultlen));
2009  fprintf(f_output, "%s;", (!contact->business_state.str) ? "" : pst_rfc2426_escape(contact->business_state.str, &result, &resultlen));
2010  fprintf(f_output, "%s;", (!contact->business_postal_code.str) ? "" : pst_rfc2426_escape(contact->business_postal_code.str, &result, &resultlen));
2011  fprintf(f_output, "%s\n", (!contact->business_country.str) ? "" : pst_rfc2426_escape(contact->business_country.str, &result, &resultlen));
2012  fprintf(f_output, "LABEL;TYPE=work:%s\n", pst_rfc2426_escape(contact->business_address.str, &result, &resultlen));
2013  }
2014 
2015  if (contact->other_address.str) {
2016  //fprintf(f_output, "ADR;TYPE=postal:%s;%s;%s;%s;%s;%s;%s\n",
2017  fprintf(f_output, "ADR;TYPE=postal:%s;",(!contact->other_po_box.str) ? "" : pst_rfc2426_escape(contact->other_po_box.str, &result, &resultlen));
2018  fprintf(f_output, "%s;", ""); // extended Address
2019  fprintf(f_output, "%s;", (!contact->other_street.str) ? "" : pst_rfc2426_escape(contact->other_street.str, &result, &resultlen));
2020  fprintf(f_output, "%s;", (!contact->other_city.str) ? "" : pst_rfc2426_escape(contact->other_city.str, &result, &resultlen));
2021  fprintf(f_output, "%s;", (!contact->other_state.str) ? "" : pst_rfc2426_escape(contact->other_state.str, &result, &resultlen));
2022  fprintf(f_output, "%s;", (!contact->other_postal_code.str) ? "" : pst_rfc2426_escape(contact->other_postal_code.str, &result, &resultlen));
2023  fprintf(f_output, "%s\n", (!contact->other_country.str) ? "" : pst_rfc2426_escape(contact->other_country.str, &result, &resultlen));
2024  fprintf(f_output, "LABEL;TYPE=postal:%s\n", pst_rfc2426_escape(contact->other_address.str, &result, &resultlen));
2025  }
2026 
2027  if (contact->business_fax.str) fprintf(f_output, "TEL;TYPE=work,fax:%s\n", pst_rfc2426_escape(contact->business_fax.str, &result, &resultlen));
2028  if (contact->business_phone.str) fprintf(f_output, "TEL;TYPE=work,voice:%s\n", pst_rfc2426_escape(contact->business_phone.str, &result, &resultlen));
2029  if (contact->business_phone2.str) fprintf(f_output, "TEL;TYPE=work,voice:%s\n", pst_rfc2426_escape(contact->business_phone2.str, &result, &resultlen));
2030  if (contact->car_phone.str) fprintf(f_output, "TEL;TYPE=car,voice:%s\n", pst_rfc2426_escape(contact->car_phone.str, &result, &resultlen));
2031  if (contact->home_fax.str) fprintf(f_output, "TEL;TYPE=home,fax:%s\n", pst_rfc2426_escape(contact->home_fax.str, &result, &resultlen));
2032  if (contact->home_phone.str) fprintf(f_output, "TEL;TYPE=home,voice:%s\n", pst_rfc2426_escape(contact->home_phone.str, &result, &resultlen));
2033  if (contact->home_phone2.str) fprintf(f_output, "TEL;TYPE=home,voice:%s\n", pst_rfc2426_escape(contact->home_phone2.str, &result, &resultlen));
2034  if (contact->isdn_phone.str) fprintf(f_output, "TEL;TYPE=isdn:%s\n", pst_rfc2426_escape(contact->isdn_phone.str, &result, &resultlen));
2035  if (contact->mobile_phone.str) fprintf(f_output, "TEL;TYPE=cell,voice:%s\n", pst_rfc2426_escape(contact->mobile_phone.str, &result, &resultlen));
2036  if (contact->other_phone.str) fprintf(f_output, "TEL;TYPE=msg:%s\n", pst_rfc2426_escape(contact->other_phone.str, &result, &resultlen));
2037  if (contact->pager_phone.str) fprintf(f_output, "TEL;TYPE=pager:%s\n", pst_rfc2426_escape(contact->pager_phone.str, &result, &resultlen));
2038  if (contact->primary_fax.str) fprintf(f_output, "TEL;TYPE=fax,pref:%s\n", pst_rfc2426_escape(contact->primary_fax.str, &result, &resultlen));
2039  if (contact->primary_phone.str) fprintf(f_output, "TEL;TYPE=phone,pref:%s\n", pst_rfc2426_escape(contact->primary_phone.str, &result, &resultlen));
2040  if (contact->radio_phone.str) fprintf(f_output, "TEL;TYPE=pcs:%s\n", pst_rfc2426_escape(contact->radio_phone.str, &result, &resultlen));
2041  if (contact->telex.str) fprintf(f_output, "TEL;TYPE=bbs:%s\n", pst_rfc2426_escape(contact->telex.str, &result, &resultlen));
2042  if (contact->job_title.str) fprintf(f_output, "TITLE:%s\n", pst_rfc2426_escape(contact->job_title.str, &result, &resultlen));
2043  if (contact->profession.str) fprintf(f_output, "ROLE:%s\n", pst_rfc2426_escape(contact->profession.str, &result, &resultlen));
2044  if (contact->assistant_name.str || contact->assistant_phone.str) {
2045  fprintf(f_output, "AGENT:BEGIN:VCARD\n");
2046  if (contact->assistant_name.str) fprintf(f_output, "FN:%s\n", pst_rfc2426_escape(contact->assistant_name.str, &result, &resultlen));
2047  if (contact->assistant_phone.str) fprintf(f_output, "TEL:%s\n", pst_rfc2426_escape(contact->assistant_phone.str, &result, &resultlen));
2048  }
2049  if (contact->company_name.str) fprintf(f_output, "ORG:%s\n", pst_rfc2426_escape(contact->company_name.str, &result, &resultlen));
2050  if (comment) fprintf(f_output, "NOTE:%s\n", pst_rfc2426_escape(comment, &result, &resultlen));
2051  if (item->body.str) fprintf(f_output, "NOTE:%s\n", pst_rfc2426_escape(item->body.str, &result, &resultlen));
2052 
2053  write_extra_categories(f_output, item);
2054 
2055  fprintf(f_output, "VERSION:3.0\n");
2056  fprintf(f_output, "END:VCARD\n\n");
2057  if (result) free(result);
2058  DEBUG_RET();
2059 }
2060 
2061 
2069 int write_extra_categories(FILE* f_output, pst_item* item)
2070 {
2071  char* result = NULL;
2072  size_t resultlen = 0;
2073  pst_item_extra_field *ef = item->extra_fields;
2074  const char *fmt = "CATEGORIES:%s";
2075  int category_started = 0;
2076  while (ef) {
2077  if (strcmp(ef->field_name, "Keywords") == 0) {
2078  fprintf(f_output, fmt, pst_rfc2426_escape(ef->value, &result, &resultlen));
2079  fmt = ", %s";
2080  category_started = 1;
2081  }
2082  ef = ef->next;
2083  }
2084  if (category_started) fprintf(f_output, "\n");
2085  if (result) free(result);
2086  return category_started;
2087 }
2088 
2089 
2090 void write_journal(FILE* f_output, pst_item* item)
2091 {
2092  char* result = NULL;
2093  size_t resultlen = 0;
2094  char time_buffer[30];
2095  pst_item_journal* journal = item->journal;
2096 
2097  // make everything utf8
2098  pst_convert_utf8_null(item, &item->subject);
2099  pst_convert_utf8_null(item, &item->body);
2100 
2101  fprintf(f_output, "BEGIN:VJOURNAL\n");
2102  fprintf(f_output, "DTSTAMP:%s\n", pst_rfc2445_datetime_format_now(sizeof(time_buffer), time_buffer));
2103  if (item->create_date)
2104  fprintf(f_output, "CREATED:%s\n", pst_rfc2445_datetime_format(item->create_date, sizeof(time_buffer), time_buffer));
2105  if (item->modify_date)
2106  fprintf(f_output, "LAST-MOD:%s\n", pst_rfc2445_datetime_format(item->modify_date, sizeof(time_buffer), time_buffer));
2107  if (item->subject.str)
2108  fprintf(f_output, "SUMMARY:%s\n", pst_rfc2426_escape(item->subject.str, &result, &resultlen));
2109  if (item->body.str)
2110  fprintf(f_output, "DESCRIPTION:%s\n", pst_rfc2426_escape(item->body.str, &result, &resultlen));
2111  if (journal && journal->start)
2112  fprintf(f_output, "DTSTART;VALUE=DATE-TIME:%s\n", pst_rfc2445_datetime_format(journal->start, sizeof(time_buffer), time_buffer));
2113  fprintf(f_output, "END:VJOURNAL\n");
2114  if (result) free(result);
2115 }
2116 
2117 
2118 void write_appointment(FILE* f_output, pst_item* item)
2119 {
2120  char* result = NULL;
2121  size_t resultlen = 0;
2122  char time_buffer[30];
2123  pst_item_appointment* appointment = item->appointment;
2124 
2125  // make everything utf8
2126  pst_convert_utf8_null(item, &item->subject);
2127  pst_convert_utf8_null(item, &item->body);
2128  pst_convert_utf8_null(item, &appointment->location);
2129 
2130  fprintf(f_output, "UID:%#"PRIx64"\n", item->block_id);
2131  fprintf(f_output, "DTSTAMP:%s\n", pst_rfc2445_datetime_format_now(sizeof(time_buffer), time_buffer));
2132  if (item->create_date)
2133  fprintf(f_output, "CREATED:%s\n", pst_rfc2445_datetime_format(item->create_date, sizeof(time_buffer), time_buffer));
2134  if (item->modify_date)
2135  fprintf(f_output, "LAST-MOD:%s\n", pst_rfc2445_datetime_format(item->modify_date, sizeof(time_buffer), time_buffer));
2136  if (item->subject.str)
2137  fprintf(f_output, "SUMMARY:%s\n", pst_rfc2426_escape(item->subject.str, &result, &resultlen));
2138  if (item->body.str)
2139  fprintf(f_output, "DESCRIPTION:%s\n", pst_rfc2426_escape(item->body.str, &result, &resultlen));
2140  if (appointment && appointment->start)
2141  fprintf(f_output, "DTSTART;VALUE=DATE-TIME:%s\n", pst_rfc2445_datetime_format(appointment->start, sizeof(time_buffer), time_buffer));
2142  if (appointment && appointment->end)
2143  fprintf(f_output, "DTEND;VALUE=DATE-TIME:%s\n", pst_rfc2445_datetime_format(appointment->end, sizeof(time_buffer), time_buffer));
2144  if (appointment && appointment->location.str)
2145  fprintf(f_output, "LOCATION:%s\n", pst_rfc2426_escape(appointment->location.str, &result, &resultlen));
2146  if (appointment) {
2147  switch (appointment->showas) {
2149  fprintf(f_output, "STATUS:TENTATIVE\n");
2150  break;
2151  case PST_FREEBUSY_FREE:
2152  // mark as transparent and as confirmed
2153  fprintf(f_output, "TRANSP:TRANSPARENT\n");
2154  case PST_FREEBUSY_BUSY:
2156  fprintf(f_output, "STATUS:CONFIRMED\n");
2157  break;
2158  }
2159  if (appointment->is_recurring) {
2160  const char* rules[] = {"DAILY", "WEEKLY", "MONTHLY", "YEARLY"};
2161  const char* days[] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
2162  pst_recurrence *rdata = pst_convert_recurrence(appointment);
2163  fprintf(f_output, "RRULE:FREQ=%s", rules[rdata->type]);
2164  if (rdata->count) fprintf(f_output, ";COUNT=%u", rdata->count);
2165  if ((rdata->interval != 1) &&
2166  (rdata->interval)) fprintf(f_output, ";INTERVAL=%u", rdata->interval);
2167  if (rdata->dayofmonth) fprintf(f_output, ";BYMONTHDAY=%d", rdata->dayofmonth);
2168  if (rdata->monthofyear) fprintf(f_output, ";BYMONTH=%d", rdata->monthofyear);
2169  if (rdata->position) fprintf(f_output, ";BYSETPOS=%d", rdata->position);
2170  if (rdata->bydaymask) {
2171  char byday[40];
2172  int empty = 1;
2173  int i=0;
2174  memset(byday, 0, sizeof(byday));
2175  for (i=0; i<7; i++) {
2176  int bit = 1 << i;
2177  if (bit & rdata->bydaymask) {
2178  char temp[40];
2179  snprintf(temp, sizeof(temp), "%s%s%s", byday, (empty) ? ";BYDAY=" : ";", days[i]);
2180  strcpy(byday, temp);
2181  empty = 0;
2182  }
2183  }
2184  fprintf(f_output, "%s", byday);
2185  }
2186  fprintf(f_output, "\n");
2187  pst_free_recurrence(rdata);
2188  }
2189  switch (appointment->label) {
2190  case PST_APP_LABEL_NONE:
2191  if (!write_extra_categories(f_output, item)) fprintf(f_output, "CATEGORIES:NONE\n");
2192  break;
2194  fprintf(f_output, "CATEGORIES:IMPORTANT\n");
2195  break;
2197  fprintf(f_output, "CATEGORIES:BUSINESS\n");
2198  break;
2200  fprintf(f_output, "CATEGORIES:PERSONAL\n");
2201  break;
2203  fprintf(f_output, "CATEGORIES:VACATION\n");
2204  break;
2206  fprintf(f_output, "CATEGORIES:MUST-ATTEND\n");
2207  break;
2209  fprintf(f_output, "CATEGORIES:TRAVEL-REQUIRED\n");
2210  break;
2212  fprintf(f_output, "CATEGORIES:NEEDS-PREPARATION\n");
2213  break;
2215  fprintf(f_output, "CATEGORIES:BIRTHDAY\n");
2216  break;
2218  fprintf(f_output, "CATEGORIES:ANNIVERSARY\n");
2219  break;
2221  fprintf(f_output, "CATEGORIES:PHONE-CALL\n");
2222  break;
2223  }
2224  // ignore bogus alarms
2225  if (appointment->alarm && (appointment->alarm_minutes >= 0) && (appointment->alarm_minutes < 1440)) {
2226  fprintf(f_output, "BEGIN:VALARM\n");
2227  fprintf(f_output, "TRIGGER:-PT%dM\n", appointment->alarm_minutes);
2228  fprintf(f_output, "ACTION:DISPLAY\n");
2229  fprintf(f_output, "DESCRIPTION:Reminder\n");
2230  fprintf(f_output, "END:VALARM\n");
2231  }
2232  }
2233  fprintf(f_output, "END:VEVENT\n");
2234  if (result) free(result);
2235 }
2236 
2237 
2238 void create_enter_dir(struct file_ll* f, pst_item *item)
2239 {
2240  memset(f, 0, sizeof(*f));
2241  f->stored_count = (item->folder) ? item->folder->item_count : 0;
2242  pst_convert_utf8(item, &item->file_as);
2243  f->dname = (char*) pst_malloc(strlen(item->file_as.str)+1);
2244  strcpy(f->dname, item->file_as.str);
2245 
2246  DEBUG_ENT("create_enter_dir");
2247  if (mode == MODE_KMAIL) {
2248  int32_t t;
2249  mk_kmail_dir(item->file_as.str);
2250  for (t=0; t<PST_TYPE_MAX; t++) {
2251  if (t == reduced_item_type(t)) {
2252  f->name[t] = (char*) pst_malloc(strlen(item->file_as.str)+strlen(OUTPUT_TEMPLATE)+30);
2253  sprintf(f->name[t], OUTPUT_TEMPLATE, item->file_as.str, item_type_to_name(t));
2254  }
2255  }
2256  } else if (mode == MODE_RECURSE) {
2257  int32_t t;
2258  mk_recurse_dir(item->file_as.str);
2259  for (t=0; t<PST_TYPE_MAX; t++) {
2260  if (t == reduced_item_type(t)) {
2261  f->name[t] = strdup(item_type_to_name(t));
2262  }
2263  }
2264  if (mode_thunder) {
2265  FILE *type_file = fopen(".type", "w");
2266  fprintf(type_file, "%d\n", item->type);
2267  fclose(type_file);
2268  }
2269  } else if (mode == MODE_SEPARATE) {
2270  // do similar stuff to recurse here.
2271  int32_t t;
2272  mk_separate_dir(item->file_as.str);
2273  for (t=0; t<PST_TYPE_MAX; t++) {
2274  if (t == reduced_item_type(t)) {
2275  f->name[t] = (char*) pst_malloc(file_name_len);
2276  memset(f->name[t], 0, file_name_len);
2277  }
2278  }
2279  } else {
2280  // MODE_NORMAL
2281  int32_t t;
2282  for (t=0; t<PST_TYPE_MAX; t++) {
2283  if (t == reduced_item_type(t)) {
2284  f->name[t] = (char*) pst_malloc(strlen(item->file_as.str)+strlen(OUTPUT_TEMPLATE)+30);
2285  sprintf(f->name[t], OUTPUT_TEMPLATE, item->file_as.str, item_type_to_name(t));
2286  }
2287  }
2288  }
2289 
2290  if (mode != MODE_SEPARATE) {
2291  int32_t t;
2292  for (t=0; t<PST_TYPE_MAX; t++) {
2293  if (f->name[t]) {
2294  if (!overwrite) {
2295  int x = 0;
2296  char *temp = (char*) pst_malloc (strlen(f->name[t])+10); //enough room for 10 digits
2297 
2298  sprintf(temp, "%s", f->name[t]);
2299  check_filename(temp);
2300  while ((f->output[t] = fopen(temp, "r"))) {
2301  DEBUG_INFO(("need to increase filename because one already exists with that name\n"));
2302  x++;
2303  sprintf(temp, "%s%08d", f->name[t], x);
2304  DEBUG_INFO(("- bump file name and try \"%s\"\n", temp));
2305  if (x == 99999999) {
2306  DIE(("create_enter_dir: Why can I not create a folder %s? I have tried %i extensions...\n", f->name[t], x));
2307  }
2308  fclose(f->output[t]);
2309  }
2310  if (x > 0) { //then the f->name should change
2311  free (f->name[t]);
2312  f->name[t] = temp;
2313  } else {
2314  free(temp);
2315  }
2316  }
2317  check_filename(f->name[t]);
2318  if (!(f->output[t] = fopen(f->name[t], "w"))) {
2319  DIE(("create_enter_dir: Could not open file \"%s\" for write\n", f->name[t]));
2320  }
2321  DEBUG_INFO(("f->name = %s\nitem->folder_name = %s\n", f->name[t], item->file_as.str));
2322  }
2323  }
2324  }
2325  DEBUG_RET();
2326 }
2327 
2328 
2329 void close_enter_dir(struct file_ll *f)
2330 {
2331  int32_t t;
2332  DEBUG_INFO(("processed item count for folder %s is %i, skipped %i, total %i \n",
2333  f->dname, f->item_count, f->skip_count, f->stored_count));
2334  if (output_mode != OUTPUT_QUIET) {
2335  pst_debug_lock();
2336  printf("\t\"%s\" - %i items done, %i items skipped.\n", f->dname, f->item_count, f->skip_count);
2337  fflush(stdout);
2338  pst_debug_unlock();
2339  }
2340  for (t=0; t<PST_TYPE_MAX; t++) {
2341  if (f->output[t]) {
2342  if (mode == MODE_SEPARATE) DEBUG_WARN(("close_enter_dir finds open separate file\n"));
2343  fclose(f->output[t]);
2344  f->output[t] = NULL;
2345  }
2346  if (f->name[t]) {
2347  struct stat st;
2348  stat(f->name[t], &st);
2349  if (!st.st_size) {
2350  DEBUG_WARN(("removing empty output file %s\n", f->name[t]));
2351  remove(f->name[t]);
2352  }
2353  free(f->name[t]);
2354  f->name[t] = NULL;
2355  }
2356  }
2357  free(f->dname);
2358 
2359  if (mode == MODE_KMAIL)
2360  close_kmail_dir();
2361  else if (mode == MODE_RECURSE) {
2362  if (mode_thunder) {
2363  FILE *type_file = fopen(".size", "w");
2364  fprintf(type_file, "%i %i\n", f->item_count, f->stored_count);
2365  fclose(type_file);
2366  }
2368  } else if (mode == MODE_SEPARATE)
2370 }
2371 
pst_string sender_address
mapi element 0x0065 PR_SENT_REPRESENTING_EMAIL_ADDRESS
Definition: libpst.h:299
#define OUTPUT_TEMPLATE
Definition: readpst.c:12
char * item_type_to_name(int32_t item_type)
Definition: readpst.c:807
uint32_t bydaymask
bit mask of days of the week
Definition: libpst.h:697
pst_string other_postal_code
mapi element 0x3a61 PR_OTHER_ADDRESS_POSTAL_CODE
Definition: libpst.h:554
int save_rtf_body
Definition: readpst.c:135
pst_file pstfile
Definition: readpst.c:137
pst_string suffix
mapi element 0x3a05 PR_GENERATION (Jr., Sr., III, etc)
Definition: libpst.h:580
int32_t alarm_minutes
mapi element 0x8501 PR_OUTLOOK_COMMON_REMINDER_MINUTES_BEFORE
Definition: libpst.h:727
void check_filename(char *fname)
Definition: readpst.c:1014
int mode_MH
Definition: readpst.c:124
int output_mode
Definition: readpst.c:128
#define PST_APP_LABEL_BUSINESS
Definition: libpst.h:63
int number_processors
Definition: readpst.c:142
pst_string home_address
mapi element 0x801a
Definition: libpst.h:492
pst_binary encrypted_htmlbody
mapi element 0x6f02
Definition: libpst.h:190
pst_item_appointment * appointment
calendar mapi elements
Definition: libpst.h:800
FILETIME * modify_date
mapi element 0x3008 PR_LAST_MODIFICATION_TIME
Definition: libpst.h:855
void removeCR(char *c)
Definition: readpst.c:702
int32_t flags
mapi element 0x0e07 PR_MESSAGE_FLAGS
Definition: libpst.h:825
pst_string subject
mapi element 0x0037 PR_SUBJECT
Definition: libpst.h:835
pst_vbuf * pst_vballoc(size_t len)
Definition: vbuf.c:130
char * pst_lzfu_decompress(char *rtfcomp, uint32_t compsize, size_t *size)
decompress lz compressed rtf data.
Definition: lzfu.c:38
void version()
Definition: readpst.c:750
uint32_t position
occurrence of day for 2nd Tuesday of month, in which case position is 2
Definition: libpst.h:703
void pst_debug_unlock()
Definition: debug.c:36
char * default_charset
Definition: readpst.c:139
pst_string home_fax
mapi element 0x3a25 PR_HOME_FAX_NUMBER
Definition: libpst.h:498
pst_string mobile_phone
mapi element 0x3a1c PR_MOBILE_TELEPHONE_NUMBER
Definition: libpst.h:534
pst_string car_phone
mapi element 0x3a1e PR_CAR_TELEPHONE_NUMBER
Definition: libpst.h:457
void header_strip_field(char *header, char *field)
Definition: readpst.c:1375
const char * prog_name
Definition: readpst.c:71
#define D_MKDIR(x)
Definition: define.h:107
int32_t reduced_item_type(int32_t item_type)
Definition: readpst.c:832
pst_string header
mapi element 0x007d PR_TRANSPORT_MESSAGE_HEADERS
Definition: libpst.h:192
int grim_reaper()
#define CMODE_VCARD
Definition: readpst.c:103
#define DMODE_INCLUDE
Definition: readpst.c:108
struct pst_desc_tree * child_tail
Definition: libpst.h:136
#define DEBUG_WARN(x)
Definition: define.h:165
#define DIE(x)
Definition: define.h:160
void header_has_field(char *header, char *field, int *flag)
Definition: readpst.c:1313
FILETIME * create_date
mapi element 0x3007 PR_CREATION_TIME
Definition: libpst.h:853
pst_string home_city
mapi element 0x3a59 PR_HOME_ADDRESS_CITY
Definition: libpst.h:494
#define MODE_NORMAL
Definition: readpst.c:76
pst_string other_country
mapi element 0x3a60 PR_OTHER_ADDRESS_COUNTRY
Definition: libpst.h:548
void write_body_part(FILE *f_output, pst_string *body, char *mime, char *charset, char *boundary, pst_file *pst)
Definition: readpst.c:1479
#define VERSION
Definition: config.h:503
pst_recurrence * pst_convert_recurrence(pst_item_appointment *appt)
Decode raw recurrence data into a better structure.
Definition: libpst.c:4586
struct pst_desc_tree * parent
Definition: libpst.h:134
pst_string telex
mapi element 0x3a2c PR_TELEX_NUMBER
Definition: libpst.h:584
void pst_rfc2047(pst_item *item, pst_string *str, int needs_quote)
Convert str to rfc2047 encoding of str, possibly enclosed in quotes if it contains spaces...
Definition: libpst.c:4502
uint64_t parent_d_id
Definition: libpst.h:128
pst_string home_street
mapi element 0x3a5d PR_HOME_ADDRESS_STREET
Definition: libpst.h:510
void mk_separate_dir(char *dir)
Definition: readpst.c:884
#define RTF_ATTACH_TYPE
Definition: readpst.c:120
int alarm
mapi element 0x8503 PR_OUTLOOK_COMMON_REMINDER_SET
Definition: libpst.h:723
pst_string business_state
mapi element 0x3a28 PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE
Definition: libpst.h:451
void pst_freeItem(pst_item *item)
Free the item returned by pst_parse_item().
Definition: libpst.c:3381
void write_msg_email(char *fname, pst_item *item, pst_file *pst)
Definition: msg.cpp:200
#define PST_APP_LABEL_VACATION
Definition: libpst.h:65
size_t dlen
Definition: vbuf.h:10
pst_string business_po_box
mapi element 0x3a2b PR_BUSINESS_PO_BOX
Definition: libpst.h:447
int pst_load_extended_attributes(pst_file *pf)
Try to load the extended attributes from the pst file.
Definition: libpst.c:698
pst_item_journal * journal
journal mapi elements
Definition: libpst.h:798
int contact_mode_specified
Definition: readpst.c:132
#define PST_TYPE_TASK
Definition: libpst.h:37
char * pst_rfc2426_escape(char *str, char **buf, size_t *buflen)
Add any necessary escape characters for rfc2426 vcard format.
Definition: libpst.c:4293
void * pst_malloc(size_t size)
Definition: debug.c:169
pst_string other_street
mapi element 0x3a63 PR_OTHER_ADDRESS_STREET
Definition: libpst.h:558
uint64_t block_id
block id that can be used to generate uid
Definition: libpst.h:784
int mode_thunder
Definition: readpst.c:127
Definition: vbuf.h:9
pst_string other_po_box
mapi element 0x3a64 PR_OTHER_ADDRESS_POST_OFFICE_BOX
Definition: libpst.h:552
pst_string primary_fax
mapi element 0x3a23 PR_PRIMARY_FAX_NUMBER
Definition: libpst.h:566
pst_binary encrypted_body
mapi element 0x6f04
Definition: libpst.h:188
#define C_TIME_SIZE
Definition: readpst.c:18
#define OUTPUT_NORMAL
Definition: readpst.c:93
#define PST_FREEBUSY_BUSY
Definition: libpst.h:57
int pst_close(pst_file *pf)
Close a pst file.
Definition: libpst.c:410
pst_string business_country
mapi element 0x3a26 PR_BUSINESS_ADDRESS_COUNTRY
Definition: libpst.h:437
uint64_t d_id
Definition: libpst.h:127
pst_string surname
mapi element 0x3a11 PR_SURNAME
Definition: libpst.h:582
int close_kmail_dir()
Definition: readpst.c:795
struct pst_item_extra_field * next
Definition: libpst.h:646
size_t size
Definition: libpst.h:154
void write_schedule_part(FILE *f_output, pst_item *item, const char *sender, const char *boundary)
Definition: readpst.c:1552
int file_name_len
Definition: readpst.c:136
#define PST_TYPE_SCHEDULE
Definition: libpst.h:32
int max_child_specified
Definition: readpst.c:144
pst_string display_name_prefix
mapi element 0x3a45 PR_DISPLAY_NAME_PREFIX
Definition: libpst.h:471
void write_email_body(FILE *f, char *body)
Definition: readpst.c:682
#define PST_APP_LABEL_IMPORTANT
Definition: libpst.h:62
pid_t * child_processes
Definition: readpst.c:146
int32_t showas
mapi element 0x8205 PR_OUTLOOK_EVENT_SHOW_TIME_AS
Definition: libpst.h:737
#define PST_APP_LABEL_ANNIVERSARY
Definition: libpst.h:70
#define DEBUG_INFO(x)
Definition: define.h:166
pst_string company_name
mapi element 0x3a16 PR_COMPANY_NAME
Definition: libpst.h:461
This contains the common mapi elements, and pointers to structures for each major mapi item type...
Definition: libpst.h:780
#define DEBUG_ENT(x)
Definition: define.h:171
#define OUTPUT_QUIET
Definition: readpst.c:96
int contact_mode
Definition: readpst.c:129
int32_t method
mapi element 0x3705 PR_ATTACH_METHOD
Definition: libpst.h:633
int getopt(int argc, char *const *argv, char *optstring)
Definition: XGetopt.c:139
int32_t skip_count
Definition: lspst.c:15
pst_string profession
mapi element 0x3a46 PR_PROFESSION
Definition: libpst.h:570
#define DEBUG_RET()
Definition: define.h:176
This contains the attachment related mapi elements.
Definition: libpst.h:608
size_t pst_attach_to_file_base64(pst_file *pf, pst_item_attach *attach, FILE *fp)
Write a binary attachment base64 encoded to a file.
Definition: libpst.c:624
const char * pst_default_charset(pst_item *item, int buflen, char *result)
Get the default character set for this item.
Definition: libpst.c:4451
int max_children
Definition: readpst.c:143
pst_binary data
mapi element 0x3701 PR_ATTACH_DATA_OBJ
Definition: libpst.h:618
pst_string report_text
mapi element 0x1001 PR_REPORT_TEXT, delivery report dsn body
Definition: libpst.h:317
pst_string nickname
mapi element 0x3a4f PR_NICKNAME
Definition: libpst.h:536
#define PST_TYPE_REPORT
Definition: libpst.h:39
pst_string file_as
mapi element 0x3001 PR_DISPLAY_NAME
Definition: libpst.h:827
pst_string assistant_phone
mapi element 0x3a2e PR_ASSISTANT_TELEPHONE_NUMBER
Definition: libpst.h:427
void find_html_charset(char *html, char *charset, size_t charsetlen)
Definition: readpst.c:1416
#define OTMODE_APPOINTMENT
Definition: readpst.c:112
pst_string first_name
mapi element 0x3a06 PR_GIVEN_NAME
Definition: libpst.h:473
pst_string messageid
mapi element 0x1035
Definition: libpst.h:215
#define PST_ATTACH_EMBEDDED
Definition: libpst.h:86
#define MIME_TYPE_DEFAULT
Definition: readpst.c:99
pst_string address2
mapi element 0x8093
Definition: libpst.h:409
void write_journal(FILE *f_output, pst_item *item)
Definition: readpst.c:2090
int overwrite
Definition: readpst.c:133
uint32_t monthofyear
month of year for yearly recurrences
Definition: libpst.h:701
The string is either utf8 encoded, or it is in the code page specified by the containing mapi object...
Definition: libpst.h:144
int output_type_mode
Definition: readpst.c:131
pst_string business_fax
mapi element 0x3a24 PR_BUSINESS_FAX_NUMBER
Definition: libpst.h:439
uint32_t type
Definition: libpst.h:674
size_t pst_vb_utf8to8bit(pst_vbuf *dest, const char *inbuf, int iblen, const char *charset)
Definition: vbuf.c:246
pst_string job_title
mapi element 0x3a17 PR_TITLE
Definition: libpst.h:516
uint64_t i_id
Definition: libpst.h:110
struct pst_desc_tree * next
Definition: libpst.h:133
int32_t stored_count
Definition: lspst.c:13
pst_desc_tree * pst_getTopOfFolders(pst_file *pf, const pst_item *root)
Get the top of folders descriptor tree.
Definition: libpst.c:544
#define MODE_SEPARATE
Definition: readpst.c:89
int acceptable_ext(pst_item_attach *attach)
check if the file name extension is acceptable.
Definition: readpst.c:1035
pst_string mimetype
mapi element 0x370e PR_ATTACH_MIME_TAG
Definition: libpst.h:614
int32_t label
mapi element 0x8214
Definition: libpst.h:750
struct pst_desc_tree * child
Definition: libpst.h:135
int pst_open(pst_file *pf, const char *name, const char *charset)
Open a pst file.
Definition: libpst.c:315
FILETIME * birthday
mapi element 0x3a42 PR_BIRTHDAY
Definition: libpst.h:431
int mode
Definition: readpst.c:123
void pst_vbgrow(pst_vbuf *vb, size_t len)
out: vbavail(vb) >= len, data are preserved
Definition: vbuf.c:146
void usage()
Definition: readpst.c:717
pst_index_ll * pst_getID(pst_file *pf, uint64_t i_id)
Lookup the i_id in the index linked list, and return a pointer to the element.
Definition: libpst.c:3681
time_t pst_fileTimeToUnixTime(const FILETIME *filetime)
Convert a FILETIME to unix time_t value.
Definition: timeconv.c:24
void header_get_subfield(char *field, const char *subfield, char *body_subfield, size_t size_subfield)
Definition: readpst.c:1324
char * pst_rfc2445_datetime_format_now(int buflen, char *result)
Convert the current time rfc2445 date/time format 19531015T231000Z.
Definition: libpst.c:4381
pst_string cc_address
mapi element 0x0e03 PR_DISPLAY_CC
Definition: libpst.h:170
char * rfc2231_string(char *inp)
Convert inp to rfc2231 encoding of string.
Definition: readpst.c:1187
#define PST_FREEBUSY_TENTATIVE
Definition: libpst.h:56
void mk_recurse_dir(char *dir)
Definition: readpst.c:854
#define PST_FLAG_READ
Definition: libpst.h:90
char * data
Definition: libpst.h:155
#define PST_FREEBUSY_OUT_OF_OFFICE
Definition: libpst.h:58
pst_index_ll * desc
Definition: libpst.h:129
pst_string sentto_address
mapi element 0x0e04 PR_DISPLAY_TO
Definition: libpst.h:315
void mk_kmail_dir(char *fname)
Definition: readpst.c:764
FILETIME * sent_date
mapi element 0x0039 PR_CLIENT_SUBMIT_TIME
Definition: libpst.h:311
pst_string other_phone
mapi element 0x3a1f PR_OTHER_TELEPHONE_NUMBER
Definition: libpst.h:550
pst_string filename1
mapi element 0x3704 PR_ATTACH_FILENAME
Definition: libpst.h:610
uint32_t dayofmonth
day of month for monthly and yearly recurrences
Definition: libpst.h:699
pst_string body
mapi element 0x1000 PR_BODY
Definition: libpst.h:833
pst_item_message_store * message_store
message store mapi elements
Definition: libpst.h:794
pst_string business_phone2
mapi element 0x3a1b PR_BUSINESS2_TELEPHONE_NUMBER
Definition: libpst.h:445
int test_base64(char *body, size_t len)
Definition: readpst.c:1397
FILETIME * end
mapi element 0x820e PR_OUTLOOK_EVENT_START_END
Definition: libpst.h:717
#define PST_TYPE_STICKYNOTE
Definition: libpst.h:36
#define DEBUG_HEXDUMPC(x, s, c)
Definition: define.h:168
int32_t no_child
Definition: libpst.h:131
#define DMODE_EXCLUDE
Definition: readpst.c:107
#define OTMODE_CONTACT
Definition: readpst.c:114
int prefer_utf8
Definition: readpst.c:134
uint32_t interval
recurrence interval in terms of the recurrence type
Definition: libpst.h:695
void pst_free_recurrence(pst_recurrence *r)
Free a recurrence structure.
Definition: libpst.c:4661
char * b
Definition: vbuf.h:13
#define PST_APP_LABEL_TRAVEL_REQ
Definition: libpst.h:67
char * pst_base64_encode(void *data, size_t size)
Definition: libstrfunc.c:21
void create_enter_dir(struct file_ll *f, pst_item *item)
Definition: readpst.c:2238
pst_string business_address
mapi element 0x801b
Definition: libpst.h:433
#define RET_DERROR(res, ret_val, x)
Definition: define.h:184
pst_string assistant_name
mapi element 0x3a30 PR_ASSISTANT
Definition: libpst.h:425
void write_appointment(FILE *f_output, pst_item *item)
Definition: readpst.c:2118
int mode_EX
Definition: readpst.c:125
uint64_t i_id
calculated from id2_val during creation of record
Definition: libpst.h:622
pst_string bcc_address
mapi element 0x0e02 PR_DISPLAY_BCC
Definition: libpst.h:172
pst_string address3
mapi element 0x80a3
Definition: libpst.h:417
#define PST_TYPE_MAX
Definition: libpst.h:40
char * my_stristr(char *haystack, char *needle)
Definition: readpst.c:989
#define PST_APP_LABEL_PHONE_CALL
Definition: libpst.h:71
#define OTMODE_EMAIL
Definition: readpst.c:111
int close_separate_dir()
Definition: readpst.c:941
pst_string home_po_box
mapi element 0x3a5e PR_HOME_ADDRESS_POST_OFFICE_BOX
Definition: libpst.h:504
pst_index_ll * assoc_tree
Definition: libpst.h:130
int write_extra_categories(FILE *f_output, pst_item *item)
write extra vcard or vcalendar categories from the extra keywords fields
Definition: readpst.c:2069
char * header_get_field(char *header, char *field)
Definition: readpst.c:1355
int is_recurring
mapi element 0x8223 PR_OUTLOOK_EVENT_IS_RECURRING
Definition: libpst.h:758
pst_string htmlbody
mapi element 0x1013
Definition: libpst.h:194
int type
derived from mapi elements 0x001a PR_MESSAGE_CLASS or 0x3613 PR_CONTAINER_CLASS
Definition: libpst.h:811
void write_separate_attachment(char f_name[], pst_item_attach *attach, int attach_num, pst_file *pst)
Definition: readpst.c:1060
#define CMODE_LIST
Definition: readpst.c:104
int pst_reopen(pst_file *pf)
Reopen the pst file after a fork.
Definition: libpst.c:395
char * pst_rfc2425_datetime_format(const FILETIME *ft, int buflen, char *result)
Convert a FILETIME into rfc2425 date/time format 1953-10-15T23:10:00Z which is the same as one of the...
Definition: libpst.c:4357
regex_t meta_charset_pattern
Definition: readpst.c:138
int32_t item_count
Definition: lspst.c:14
pst_string radio_phone
mapi element 0x3a1d PR_RADIO_TELEPHONE_NUMBER
Definition: libpst.h:572
void process(pst_item *outeritem, pst_desc_tree *d_ptr)
Definition: readpst.c:240
This contains the appointment related mapi elements.
Definition: libpst.h:713
pst_item_extra_field * extra_fields
linked list of extra headers and such
Definition: libpst.h:796
pst_string home_country
mapi element 0x3a5a PR_HOME_ADDRESS_COUNTRY
Definition: libpst.h:496
char * header_end_field(char *field)
Definition: readpst.c:1365
pst_string home_phone
mapi element 0x3a09 PR_HOME_TELEPHONE_NUMBER
Definition: libpst.h:500
linked list of extra header fields
Definition: libpst.h:643
string name
Definition: pst2dii.cpp:22
int main(int argc, char *const *argv)
Definition: readpst.c:412
#define PST_APP_LABEL_MUST_ATTEND
Definition: libpst.h:66
pst_string outlook_sender_name
mapi element 0x0042 PR_SENT_REPRESENTING_NAME
Definition: libpst.h:237
pst_string home_postal_code
mapi element 0x3a5b PR_HOME_ADDRESS_POSTAL_CODE
Definition: libpst.h:506
int is_utf8
Definition: libpst.h:147
pst_binary rtf_compressed
mapi element 0x1009 PR_RTF_COMPRESSED, the compressed rtf body data.
Definition: libpst.h:281
This contains the contact related mapi elements.
Definition: libpst.h:397
#define PST_APP_LABEL_BIRTHDAY
Definition: libpst.h:69
#define PST_TYPE_NOTE
Definition: libpst.h:31
void close_separate_file(struct file_ll *f)
Definition: readpst.c:970
pst_item * pst_parse_item(pst_file *pf, pst_desc_tree *d_ptr, pst_id2_tree *m_head)
Process a high level object from the pst file.
Definition: libpst.c:1249
pst_string home_state
mapi element 0x3a5c PR_HOME_ADDRESS_STATE_OR_PROVINCE
Definition: libpst.h:508
pid_t try_fork(char *folder)
Definition: readpst.c:203
void close_enter_dir(struct file_ll *f)
Definition: readpst.c:2329
#define MODE_KMAIL
Definition: readpst.c:80
pst_string filename2
mapi element 0x3707 PR_ATTACH_LONG_FILENAME
Definition: libpst.h:612
int pst_stricmp(char *a, char *b)
compare strings case-insensitive.
Definition: libpst.c:4231
size_t pst_attach_to_file(pst_file *pf, pst_item_attach *attach, FILE *fp)
Write a binary attachment to a file.
Definition: libpst.c:600
pst_string business_city
mapi element 0x3a27 PR_BUSINESS_ADDRESS_CITY
Definition: libpst.h:435
This contains the journal related mapi elements.
Definition: libpst.h:652
void pst_debug_lock()
Definition: debug.c:28
void write_schedule_part_data(FILE *f_output, pst_item *item, const char *sender, const char *method)
Definition: readpst.c:1533
pst_string pager_phone
mapi element 0x3a21 PR_PAGER_TELEPHONE_NUMBER
Definition: libpst.h:560
#define OUTPUT_KMAIL_DIR_TEMPLATE
Definition: readpst.c:13
pst_string business_phone
mapi element 0x3a08 PR_BUSINESS_TELEPHONE_NUMBER
Definition: libpst.h:443
int mode_MSG
Definition: readpst.c:126
char * pst_rfc2445_datetime_format(const FILETIME *ft, int buflen, char *result)
Convert a FILETIME into rfc2445 date/time format 19531015T231000Z.
Definition: libpst.c:4369
pst_string isdn_phone
mapi element 0x3a2d PR_ISDN_NUMBER
Definition: libpst.h:514
int optind
Definition: XGetopt.c:137
pst_desc_tree * d_head
the head and tail of the top level of the descriptor tree
Definition: libpst.h:907
void find_rfc822_headers(char **extra_mime_headers)
Definition: readpst.c:1445
#define PST_APP_LABEL_NONE
Definition: libpst.h:61
pst_string primary_phone
mapi element 0x3a1a PR_PRIMARY_TELEPHONE_NUMBER
Definition: libpst.h:568
void mk_separate_file(struct file_ll *f, int32_t t, char *extension, int openit)
Definition: readpst.c:953
#define OTMODE_JOURNAL
Definition: readpst.c:113
Definition: lspst.c:11
char * output_dir
Definition: readpst.c:72
#define PST_APP_LABEL_NEEDS_PREP
Definition: libpst.h:68
pst_string other_address
mapi element 0x801c
Definition: libpst.h:544
pst_item_email * email
email mapi elements
Definition: libpst.h:786
#define PST_FREEBUSY_FREE
Definition: libpst.h:55
int close_recurse_dir()
Definition: readpst.c:872
#define PST_TYPE_JOURNAL
Definition: libpst.h:35
#define RTF_ATTACH_NAME
Definition: readpst.c:118
#define PST_TYPE_APPOINTMENT
Definition: libpst.h:33
#define PST_TYPE_CONTACT
Definition: libpst.h:34
pst_string fullname
mapi element 0x8005
Definition: libpst.h:481
pst_item_attach * attach
linked list of attachments
Definition: libpst.h:792
void write_vcard(FILE *f_output, pst_item *item, pst_item_contact *contact, char comment[])
Definition: readpst.c:1904
pst_string address1
mapi element 0x3003 PR_EMAIL_ADDRESS, or 0x8083
Definition: libpst.h:401
#define PST_TYPE_OTHER
Definition: libpst.h:38
FILETIME * start
mapi element 0x820d PR_OUTLOOK_EVENT_START_DATE
Definition: libpst.h:715
int active_children
Definition: readpst.c:145
#define DEBUG_INIT(fname, mutex)
Definition: define.h:182
pst_string business_street
mapi element 0x3a29 PR_BUSINESS_ADDRESS_STREET
Definition: libpst.h:453
struct pst_item_attach * next
Definition: libpst.h:638
#define RFC822
Definition: readpst.c:100
uint32_t count
number of occurrences, even if recurrence terminates based on date
Definition: libpst.h:705
pst_string business_postal_code
mapi element 0x3a2a PR_BUSINESS_POSTAL_CODE
Definition: libpst.h:449
void write_embedded_message(FILE *f_output, pst_item_attach *attach, char *boundary, pst_file *pf, int save_rtf, char **extra_mime_headers)
Definition: readpst.c:1114
char * acceptable_extensions
Definition: readpst.c:140
char * dname
Definition: lspst.c:12
int pst_load_index(pst_file *pf)
Load the index entries from the pst file.
Definition: libpst.c:652
pst_string other_state
mapi element 0x3a62 PR_OTHER_ADDRESS_STATE_OR_PROVINCE
Definition: libpst.h:556
#define MODE_RECURSE
Definition: readpst.c:84
size_t pst_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
fwrite with checking for null pointer.
Definition: libpst.c:4261
pst_string middle_name
mapi element 0x3a44 PR_MIDDLE_NAME
Definition: libpst.h:530
struct pst_desc_tree * prev
Definition: libpst.h:132
void write_normal_email(FILE *f_output, char f_name[], pst_item *item, int mode, int mode_MH, pst_file *pst, int save_rtf, int embedding, char **extra_mime_headers)
Definition: readpst.c:1575
pst_item_folder * folder
folder mapi elements
Definition: libpst.h:788
void pst_convert_utf8_null(pst_item *item, pst_string *str)
Convert str to utf8 if possible; null strings are preserved.
Definition: libpst.c:4535
int header_match(char *header, char *field)
Definition: readpst.c:1273
pst_string other_city
mapi element 0x3a5f PR_OTHER_ADDRESS_CITY
Definition: libpst.h:546
char * str
Definition: libpst.h:148
int valid_headers(char *header)
Definition: readpst.c:1284
char * optarg
Definition: XGetopt.c:136
pst_id2_tree * id2_head
id2 tree needed to resolve attachments by reference
Definition: libpst.h:624
FILE * output[15]
Definition: readpst.c:23
FILETIME * start
mapi element 0x8706
Definition: libpst.h:654
#define PST_APP_LABEL_PERSONAL
Definition: libpst.h:64
char * quote_string(char *inp)
Backslash-escape quotes and backslashes in the given string.
Definition: readpst.c:1158
int32_t item_count
mapi element 0x3602 PR_CONTENT_COUNT
Definition: libpst.h:345
This contains the recurrence data separated into fields.
Definition: libpst.h:667
int deleted_mode
Definition: readpst.c:130
void write_inline_attachment(FILE *f_output, pst_item_attach *attach, char *boundary, pst_file *pst)
Definition: readpst.c:1217
void pst_convert_utf8(pst_item *item, pst_string *str)
Convert str to utf8 if possible; null strings are converted into empty strings.
Definition: libpst.c:4546
pst_string home_phone2
mapi element 0x3a2f PR_HOME2_TELEPHONE_NUMBER
Definition: libpst.h:502
#define KMAIL_INDEX
Definition: readpst.c:14
#define SEP_MAIL_FILE_TEMPLATE
Definition: readpst.c:15
void pst_debug_setlevel(int level)
Definition: debug.c:23
pst_string location
mapi element 0x8208 PR_OUTLOOK_EVENT_LOCATION
Definition: libpst.h:719
pst_string content_id
mapi element 0x3712 PR_ATTACH_CONTENT_ID
Definition: libpst.h:616