changeset 363:3a1d25c579c6 stable-0-6-68

allow folders containing multiple item types; better detection of valid internet headers
author Carl Byington <carl@five-ten-sg.com>
date Mon, 29 Aug 2016 09:50:24 -0700
parents c42273d817c7
children 9c6f93aca0db
files ChangeLog NEWS configure.in libpst.spec.in regression/regression-tests.bash src/libpst.h src/readpst.c xml/libpst.in
diffstat 8 files changed, 233 insertions(+), 205 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Jul 06 12:14:55 2016 -0700
+++ b/ChangeLog	Mon Aug 29 09:50:24 2016 -0700
@@ -1,3 +1,8 @@
+LibPST 0.6.68 (2016-08-29)
+===============================
+    * allow folders containing multiple item types, e.g. email and calendar
+    * better detection of valid internet headers
+
 LibPST 0.6.67 (2016-07-06)
 ===============================
     * Jeffrey Morlan - multiple bug fixes and an optimization
@@ -120,10 +125,10 @@
 LibPST 0.6.43 (2009-09-12)
 ===============================
     * patches from Justin Greer.
-	add code pages 1200 and 1201 to the list for iconv
-    	add support for 0x0201 indirect blocks that point to 0x0101 blocks
-	add readpst -t option to select output item types
-	fix (remove) extra new line inside headers
+    add code pages 1200 and 1201 to the list for iconv
+        add support for 0x0201 indirect blocks that point to 0x0101 blocks
+    add readpst -t option to select output item types
+    fix (remove) extra new line inside headers
     * cleanup base64 encoding to remove duplicate code.
     * patch from Chris White to avoid segfault with embedded appointments.
     * patch from Roberto Polli to add creation of some Thunderbird specific meta files.
--- a/NEWS	Wed Jul 06 12:14:55 2016 -0700
+++ b/NEWS	Mon Aug 29 09:50:24 2016 -0700
@@ -1,3 +1,4 @@
+0.6.68  2016-08-29 allow folders containing multiple item types;  better detection of valid internet headers
 0.6.67  2016-07-06 Jeffrey Morlan - multiple bug fixes and an optimization
 0.6.66  2015-12-21 Igor Stroh  - Added Content-ID header support
 0.6.65  2015-09-11 Jeffrey Morlan - fix multiple Content-Type headers; Hans Liss - debug level output
--- a/configure.in	Wed Jul 06 12:14:55 2016 -0700
+++ b/configure.in	Mon Aug 29 09:50:24 2016 -0700
@@ -1,5 +1,5 @@
 AC_PREREQ(2.60)
-AC_INIT(libpst,0.6.67,carl@five-ten-sg.com)
+AC_INIT(libpst,0.6.68,carl@five-ten-sg.com)
 AC_CONFIG_SRCDIR([src/libpst.c])
 AC_CONFIG_HEADER([config.h])
 AC_CONFIG_MACRO_DIR([m4])
@@ -21,7 +21,7 @@
 #  6. libtool will build libpst.so.x.y.z where the SONAME is libpst.so.x
 #     and x=current-age, y=age, z=revision
 
-libpst_version_info='5:12:1'
+libpst_version_info='5:13:1'
 AC_SUBST(LIBPST_VERSION_INFO, [$libpst_version_info])
 libpst_so_major='4'
 AC_SUBST(LIBPST_SO_MAJOR, [$libpst_so_major])
@@ -52,6 +52,7 @@
 # 0.6.63    libpst.so.4     libpst.so.4.1.10
 # 0.6.66    libpst.so.4     libpst.so.4.1.11
 # 0.6.67    libpst.so.4     libpst.so.4.1.12
+# 0.6.68    libpst.so.4     libpst.so.4.1.13
 
 
 
--- a/libpst.spec.in	Wed Jul 06 12:14:55 2016 -0700
+++ b/libpst.spec.in	Mon Aug 29 09:50:24 2016 -0700
@@ -160,6 +160,13 @@
 
 
 %changelog
+* Mon Aug 29 2016 Carl Byington <carl@five-ten-sg.com> 0.6.68-1
+- allow folders containing multiple item types, e.g. email and calendar
+- better detection of valid internet headers
+
+* Tue Jul 19 2016 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.67-2
+- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages
+
 * Wed Jul 06 2016 Carl Byington <carl@five-ten-sg.com> 0.6.67-1
 - Jeffrey Morlan - multiple bug fixes and an optimization
 
@@ -179,15 +186,13 @@
 * Thu Aug 27 2015 Jonathan Wakely <jwakely@redhat.com> - 0.6.64-6
 - Rebuilt for Boost 1.59
 
-* Wed Jul 29 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> -
- 0.6.64-5
+* Wed Jul 29 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.64-5
 - Rebuilt for https://fedoraproject.org/wiki/Changes/F23Boost159
 
 * Wed Jul 22 2015 David Tardon <dtardon@redhat.com> - 0.6.64-4
 - rebuild for Boost 1.58
 
-* Wed Jun 17 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> -
- 0.6.64-3
+* Wed Jun 17 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.6.64-3
 - Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
 
 * Sat May 02 2015 Kalev Lember <kalevlember@gmail.com> - 0.6.64-2
--- a/regression/regression-tests.bash	Wed Jul 06 12:14:55 2016 -0700
+++ b/regression/regression-tests.bash	Mon Aug 29 09:50:24 2016 -0700
@@ -73,6 +73,7 @@
             utf='-8'
             echo $val ../src/readpst $utf $acc -C $char -j 0 -r -cv -o output$n -d $ba.log $fn
                  $val ../src/readpst $utf $acc -C $char -j 0 -r -cv -o output$n -d $ba.log $fn >$ba.err 2>&1
+            #readpst $utf $acc -C $char -j 0 -r -cv -o output$n -d $ba.log $fn >$ba.err 2>&1
 
             ## separate mode with filename extensions and .msg files
             #echo $val ../src/readpst $jobs     -r -m -D -cv -o output$n -d $ba.log $fn
--- a/src/libpst.h	Wed Jul 06 12:14:55 2016 -0700
+++ b/src/libpst.h	Mon Aug 29 09:50:24 2016 -0700
@@ -32,6 +32,7 @@
 #define PST_TYPE_TASK       12
 #define PST_TYPE_OTHER      13
 #define PST_TYPE_REPORT     14
+#define PST_TYPE_MAX        15
 
 // defines types of possible encryption
 #define PST_NO_ENCRYPT   0
--- a/src/readpst.c	Wed Jul 06 12:14:55 2016 -0700
+++ b/src/readpst.c	Mon Aug 29 09:50:24 2016 -0700
@@ -18,13 +18,12 @@
 #define C_TIME_SIZE 500
 
 struct file_ll {
-    char *name;
+    char *name[PST_TYPE_MAX];
     char *dname;
-    FILE * output;
+    FILE * output[PST_TYPE_MAX];
     int32_t stored_count;
     int32_t item_count;
     int32_t skip_count;
-    int32_t type;
 };
 
 int       grim_reaper();
@@ -36,11 +35,11 @@
 void      version();
 char*     mk_kmail_dir(char* fname);
 int       close_kmail_dir();
-char*     mk_recurse_dir(char* dir, int32_t folder_type);
+void      mk_recurse_dir(char* dir);
 int       close_recurse_dir();
-char*     mk_separate_dir(char *dir);
+void      mk_separate_dir(char *dir);
 int       close_separate_dir();
-void      mk_separate_file(struct file_ll *f, char *extension, int openit);
+void      mk_separate_file(struct file_ll *f, int32_t t, char *extension, int openit);
 void      close_separate_file(struct file_ll *f);
 char*     my_stristr(char *haystack, char *needle);
 void      check_filename(char *fname);
@@ -244,7 +243,6 @@
     pst_item *item = NULL;
 
     DEBUG_ENT("process");
-    memset(&ff, 0, sizeof(ff));
     create_enter_dir(&ff, outeritem);
 
     for (; d_ptr; d_ptr = d_ptr->next) {
@@ -308,25 +306,18 @@
                 DEBUG_INFO(("skipping contact: not in output type list\n"));
             }
             else {
-                if (!ff.type) ff.type = item->type;
-                if ((ff.type != PST_TYPE_CONTACT) && (mode != MODE_SEPARATE)) {
-                    ff.skip_count++;
-                    DEBUG_INFO(("I have a contact, but the folder type %"PRIi32" isn't a contacts folder. Skipping it\n", ff.type));
+                ff.item_count++;
+                if (mode == MODE_SEPARATE) mk_separate_file(&ff, PST_TYPE_CONTACT, (mode_EX) ? ".vcf" : "", 1);
+                if (contact_mode == CMODE_VCARD) {
+                    pst_convert_utf8_null(item, &item->comment);
+                    write_vcard(ff.output[PST_TYPE_CONTACT], item, item->contact, item->comment.str);
                 }
                 else {
-                    ff.item_count++;
-                    if (mode == MODE_SEPARATE) mk_separate_file(&ff, (mode_EX) ? ".vcf" : "", 1);
-                    if (contact_mode == CMODE_VCARD) {
-                        pst_convert_utf8_null(item, &item->comment);
-                        write_vcard(ff.output, item, item->contact, item->comment.str);
-                    }
-                    else {
-                        pst_convert_utf8(item, &item->contact->fullname);
-                        pst_convert_utf8(item, &item->contact->address1);
-                        fprintf(ff.output, "%s <%s>\n", item->contact->fullname.str, item->contact->address1.str);
-                    }
-                    if (mode == MODE_SEPARATE) close_separate_file(&ff);
+                    pst_convert_utf8(item, &item->contact->fullname);
+                    pst_convert_utf8(item, &item->contact->address1);
+                    fprintf(ff.output[PST_TYPE_CONTACT], "%s <%s>\n", item->contact->fullname.str, item->contact->address1.str);
                 }
+                if (mode == MODE_SEPARATE) close_separate_file(&ff);
             }
 
         } else if (item->email && ((item->type == PST_TYPE_NOTE) || (item->type == PST_TYPE_SCHEDULE) || (item->type == PST_TYPE_REPORT))) {
@@ -336,46 +327,39 @@
                 DEBUG_INFO(("skipping email: not in output type list\n"));
             }
             else {
-                if (!ff.type) ff.type = item->type;
-                if ((ff.type != PST_TYPE_NOTE) && (ff.type != PST_TYPE_SCHEDULE) && (ff.type != PST_TYPE_REPORT) && (mode != MODE_SEPARATE)) {
-                    ff.skip_count++;
-                    DEBUG_INFO(("I have an email type %"PRIi32", but the folder type %"PRIi32" isn't an email folder. Skipping it\n", item->type, ff.type));
+                char *extra_mime_headers = NULL;
+                ff.item_count++;
+                if (mode == MODE_SEPARATE) {
+                    // process this single email message, possibly forking
+                    pid_t parent = getpid();
+                    pid_t child = try_fork(item->file_as.str);
+                    if (child == 0) {
+                        // we are the child process, or the original parent if no children were available
+                        pid_t me = getpid();
+                        mk_separate_file(&ff, PST_TYPE_NOTE, (mode_EX) ? ".eml" : "", 1);
+                        write_normal_email(ff.output[PST_TYPE_NOTE], ff.name[PST_TYPE_NOTE], item, mode, mode_MH, &pstfile, save_rtf_body, PST_TYPE_NOTE, &extra_mime_headers);
+                        close_separate_file(&ff);
+                        if (mode_MSG) {
+                            mk_separate_file(&ff, PST_TYPE_NOTE, ".msg", 0);
+                            write_msg_email(ff.name[PST_TYPE_NOTE], item, &pstfile);
+                        }
+#ifdef HAVE_FORK
+#ifdef HAVE_SEMAPHORE_H
+                        if (me != parent) {
+                            // we really were a child, forked for the sole purpose of processing this message
+                            // free my child count slot before really exiting, since
+                            // all I am doing here is waiting for my children to exit
+                            sem_post(global_children);
+                            grim_reaper(1); // wait for all my child processes to exit - there should not be any
+                            exit(0);        // really exit
+                        }
+#endif
+#endif
+                    }
                 }
                 else {
-                    char *extra_mime_headers = NULL;
-                    ff.item_count++;
-                    if (mode == MODE_SEPARATE) {
-                        // process this single email message, possibly forking
-                        pid_t parent = getpid();
-                        pid_t child = try_fork(item->file_as.str);
-                        if (child == 0) {
-                            // we are the child process, or the original parent if no children were available
-                            pid_t me = getpid();
-                            mk_separate_file(&ff, (mode_EX) ? ".eml" : "", 1);
-                            write_normal_email(ff.output, ff.name, item, mode, mode_MH, &pstfile, save_rtf_body, 0, &extra_mime_headers);
-                            close_separate_file(&ff);
-                            if (mode_MSG) {
-                                mk_separate_file(&ff, ".msg", 0);
-                                write_msg_email(ff.name, item, &pstfile);
-                            }
-#ifdef HAVE_FORK
-#ifdef HAVE_SEMAPHORE_H
-                            if (me != parent) {
-                                // we really were a child, forked for the sole purpose of processing this message
-                                // free my child count slot before really exiting, since
-                                // all I am doing here is waiting for my children to exit
-                                sem_post(global_children);
-                                grim_reaper(1); // wait for all my child processes to exit - there should not be any
-                                exit(0);        // really exit
-                            }
-#endif
-#endif
-                        }
-                    }
-                    else {
-                        // process this single email message, cannot fork since not separate mode
-                        write_normal_email(ff.output, ff.name, item, mode, mode_MH, &pstfile, save_rtf_body, 0, &extra_mime_headers);
-                    }
+                    // process this single email message, cannot fork since not separate mode
+                    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);
                 }
             }
 
@@ -386,18 +370,11 @@
                 DEBUG_INFO(("skipping journal entry: not in output type list\n"));
             }
             else {
-                if (!ff.type) ff.type = item->type;
-                if ((ff.type != PST_TYPE_JOURNAL) && (mode != MODE_SEPARATE)) {
-                    ff.skip_count++;
-                    DEBUG_INFO(("I have a journal entry, but the folder type %"PRIi32" isn't a journal folder. Skipping it\n", ff.type));
-                }
-                else {
-                    ff.item_count++;
-                    if (mode == MODE_SEPARATE) mk_separate_file(&ff, (mode_EX) ? ".ics" : "", 1);
-                    write_journal(ff.output, item);
-                    fprintf(ff.output, "\n");
-                    if (mode == MODE_SEPARATE) close_separate_file(&ff);
-                }
+                ff.item_count++;
+                if (mode == MODE_SEPARATE) mk_separate_file(&ff, PST_TYPE_JOURNAL, (mode_EX) ? ".ics" : "", 1);
+                write_journal(ff.output[PST_TYPE_JOURNAL], item);
+                fprintf(ff.output[PST_TYPE_JOURNAL], "\n");
+                if (mode == MODE_SEPARATE) close_separate_file(&ff);
             }
 
         } else if (item->appointment && (item->type == PST_TYPE_APPOINTMENT)) {
@@ -407,24 +384,17 @@
                 DEBUG_INFO(("skipping appointment: not in output type list\n"));
             }
             else {
-                if (!ff.type) ff.type = item->type;
-                if ((ff.type != PST_TYPE_APPOINTMENT) && (mode != MODE_SEPARATE)) {
-                    ff.skip_count++;
-                    DEBUG_INFO(("I have an appointment, but the folder type %"PRIi32" isn't an appointment folder. Skipping it\n", ff.type));
-                }
-                else {
-                    ff.item_count++;
-                    if (mode == MODE_SEPARATE) mk_separate_file(&ff, (mode_EX) ? ".ics" : "", 1);
-                    write_schedule_part_data(ff.output, item, NULL, NULL);
-                    fprintf(ff.output, "\n");
-                    if (mode == MODE_SEPARATE) close_separate_file(&ff);
-                }
+                ff.item_count++;
+                if (mode == MODE_SEPARATE) mk_separate_file(&ff, PST_TYPE_APPOINTMENT, (mode_EX) ? ".ics" : "", 1);
+                write_schedule_part_data(ff.output[PST_TYPE_APPOINTMENT], item, NULL, NULL);
+                fprintf(ff.output[PST_TYPE_APPOINTMENT], "\n");
+                if (mode == MODE_SEPARATE) close_separate_file(&ff);
             }
 
         } else if (item->message_store) {
             // there should only be one message_store, and we have already done it
             ff.skip_count++;
-            DEBUG_WARN(("item with message store content, type %i %s folder type %i, skipping it\n", item->type, item->ascii_type, ff.type));
+            DEBUG_WARN(("item with message store content, type %i %s, skipping it\n", item->type, item->ascii_type));
 
         } else {
             ff.skip_count++;
@@ -847,11 +817,55 @@
 }
 
 
-// this will create a directory by that name,
-// then make an mbox file inside that directory.
-char *mk_recurse_dir(char *dir, int32_t folder_type) {
+char *item_type_to_name(int32_t item_type) {
+    char *name;
+    switch (item_type) {
+        case PST_TYPE_APPOINTMENT:
+            name = "calendar";
+            break;
+        case PST_TYPE_CONTACT:
+            name = "contacts";
+            break;
+        case PST_TYPE_JOURNAL:
+            name = "journal";
+            break;
+        case PST_TYPE_STICKYNOTE:
+        case PST_TYPE_TASK:
+        case PST_TYPE_NOTE:
+        case PST_TYPE_OTHER:
+        case PST_TYPE_REPORT:
+        default:
+            name = "mbox";
+            break;
+    }
+    return name;
+}
+
+
+int32_t reduced_item_type(int32_t item_type) {
+    int32_t reduced;
+    switch (item_type) {
+        case PST_TYPE_APPOINTMENT:
+        case PST_TYPE_CONTACT:
+        case PST_TYPE_JOURNAL:
+            reduced = item_type;
+            break;
+        case PST_TYPE_STICKYNOTE:
+        case PST_TYPE_TASK:
+        case PST_TYPE_NOTE:
+        case PST_TYPE_OTHER:
+        case PST_TYPE_REPORT:
+        default:
+            reduced = PST_TYPE_NOTE;
+            break;
+    }
+    return reduced;
+}
+
+
+// this will create a directory by that name
+void mk_recurse_dir(char *dir) {
     int x;
-    char *out_name;
     DEBUG_ENT("mk_recurse_dir");
     check_filename(dir);
     if (D_MKDIR (dir)) {
@@ -864,27 +878,7 @@
         x = errno;
         DIE(("mk_recurse_dir: Cannot change to directory %s: %s\n", dir, strerror(x)));
     }
-    switch (folder_type) {
-        case PST_TYPE_APPOINTMENT:
-            out_name = strdup("calendar");
-            break;
-        case PST_TYPE_CONTACT:
-            out_name = strdup("contacts");
-            break;
-        case PST_TYPE_JOURNAL:
-            out_name = strdup("journal");
-            break;
-        case PST_TYPE_STICKYNOTE:
-        case PST_TYPE_TASK:
-        case PST_TYPE_NOTE:
-        case PST_TYPE_OTHER:
-        case PST_TYPE_REPORT:
-        default:
-            out_name = strdup("mbox");
-            break;
-    }
     DEBUG_RET();
-    return out_name;
 }
 
 
@@ -900,7 +894,7 @@
 }
 
 
-char *mk_separate_dir(char *dir) {
+void mk_separate_dir(char *dir) {
     size_t dirsize = strlen(dir) + 10;
     char dir_name[dirsize];
     int x = 0, y = 0;
@@ -953,9 +947,7 @@
 #endif
     }
 
-    // we don't return a filename here cause it isn't necessary.
     DEBUG_RET();
-    return NULL;
 }
 
 
@@ -971,17 +963,17 @@
 }
 
 
-void mk_separate_file(struct file_ll *f, char *extension, int openit) {
+void mk_separate_file(struct file_ll *f, int32_t t, char *extension, int openit) {
     DEBUG_ENT("mk_separate_file");
     DEBUG_INFO(("opening next file to save email\n"));
     if (f->item_count > 999999999) { // bigger than nine 9's
         DIE(("mk_separate_file: The number of emails in this folder has become too high to handle\n"));
     }
-    sprintf(f->name, SEP_MAIL_FILE_TEMPLATE, f->item_count, extension);
-    check_filename(f->name);
+    sprintf(f->name[t], SEP_MAIL_FILE_TEMPLATE, f->item_count, extension);
+    check_filename(f->name[t]);
     if (openit) {
-        if (!(f->output = fopen(f->name, "w"))) {
-            DIE(("mk_separate_file: Cannot open file to save email \"%s\"\n", f->name));
+        if (!(f->output[t] = fopen(f->name[t], "w"))) {
+            DIE(("mk_separate_file: Cannot open file to save email \"%s\"\n", f->name[t]));
         }
     }
     DEBUG_RET();
@@ -989,16 +981,19 @@
 
 
 void close_separate_file(struct file_ll *f) {
+    int32_t t;
     DEBUG_ENT("close_separate_file");
-    if (f->output) {
-        struct stat st;
-        fclose(f->output);
-        stat(f->name, &st);
-        if (!st.st_size) {
-            DEBUG_WARN(("removing empty output file %s\n", f->name));
-            remove(f->name);
+    for (t=0; t<PST_TYPE_MAX; t++) {
+        if (f->output[t]) {
+            struct stat st;
+            fclose(f->output[t]);
+            stat(f->name[t], &st);
+            if (!st.st_size) {
+                DEBUG_WARN(("removing empty output file %s\n", f->name[t]));
+                remove(f->name[t]);
+            }
+            f->output[t] = NULL;
         }
-        f->output = NULL;
     }
     DEBUG_RET();
 }
@@ -1227,16 +1222,19 @@
     // there are surely others. the problem is - given an arbitrary character
     // string, is it a valid (or even reasonable) set of rfc822 headers?
     if (header) {
-        if ((strncasecmp(header, "X-Barracuda-URL: ", 17) == 0) ||
-            (strncasecmp(header, "X-ASG-Debug-ID: ",  16) == 0) ||
-            (strncasecmp(header, "Return-Path: ",     13) == 0) ||
-            (strncasecmp(header, "Received: ",        10) == 0) ||
-            (strncasecmp(header, "Subject: ",          9) == 0) ||
-            (strncasecmp(header, "Date: ",             6) == 0) ||
-            (strncasecmp(header, "From: ",             6) == 0) ||
-            (strncasecmp(header, "X-x: ",              5) == 0) ||
-            (strncasecmp(header, "Microsoft Mail Internet Headers", 31) == 0)) {
-            return 1;
+        if ((strncasecmp(header, "Content-Type: ",                  14) == 0) ||
+            (strncasecmp(header, "Date: ",                           6) == 0) ||
+            (strncasecmp(header, "From: ",                           6) == 0) ||
+            (strncasecmp(header, "MIME-Version: ",                  14) == 0) ||
+            (strncasecmp(header, "Microsoft Mail Internet Headers", 31) == 0) ||
+            (strncasecmp(header, "Received: ",                      10) == 0) ||
+            (strncasecmp(header, "Return-Path: ",                   13) == 0) ||
+            (strncasecmp(header, "Subject: ",                        9) == 0) ||
+            (strncasecmp(header, "To: ",                             4) == 0) ||
+            (strncasecmp(header, "X-ASG-Debug-ID: ",                16) == 0) ||
+            (strncasecmp(header, "X-Barracuda-URL: ",               17) == 0) ||
+            (strncasecmp(header, "X-x: ",                            5) == 0)) {
+            return 1;                                 
         }
         else {
             if (strlen(header) > 2) {
@@ -2176,15 +2174,23 @@
 
 void create_enter_dir(struct file_ll* f, pst_item *item)
 {
+    memset(f, 0, sizeof(*f));
+    f->stored_count = (item->folder) ? item->folder->item_count : 0;
     pst_convert_utf8(item, &item->file_as);
-    f->type         = item->type;
-    f->stored_count = (item->folder) ? item->folder->item_count : 0;
+    f->dname = (char*) pst_malloc(strlen(item->file_as.str)+1);
+    strcpy(f->dname, item->file_as.str);
 
     DEBUG_ENT("create_enter_dir");
     if (mode == MODE_KMAIL)
-        f->name = mk_kmail_dir(item->file_as.str);
+        f->name[0] = mk_kmail_dir(item->file_as.str);
     else if (mode == MODE_RECURSE) {
-        f->name = mk_recurse_dir(item->file_as.str, f->type);
+        int32_t t;
+        mk_recurse_dir(item->file_as.str);
+        for (t=0; t<PST_TYPE_MAX; t++) {
+            if (t == reduced_item_type(t)) {
+                f->name[t] = strdup(item_type_to_name(t));
+            }
+        }
         if (mode_thunder) {
             FILE *type_file = fopen(".type", "w");
             fprintf(type_file, "%d\n", item->type);
@@ -2193,46 +2199,47 @@
     } else if (mode == MODE_SEPARATE) {
         // do similar stuff to recurse here.
         mk_separate_dir(item->file_as.str);
-        f->name = (char*) pst_malloc(file_name_len);
-        memset(f->name, 0, file_name_len);
+        f->name[0] = (char*) pst_malloc(file_name_len);
+        memset(f->name[0], 0, file_name_len);
     } else {
-        f->name = (char*) pst_malloc(strlen(item->file_as.str)+strlen(OUTPUT_TEMPLATE)+1);
-        sprintf(f->name, OUTPUT_TEMPLATE, item->file_as.str);
+        f->name[0] = (char*) pst_malloc(strlen(item->file_as.str)+strlen(OUTPUT_TEMPLATE)+1);
+        sprintf(f->name[0], OUTPUT_TEMPLATE, item->file_as.str);
     }
 
-    f->dname = (char*) pst_malloc(strlen(item->file_as.str)+1);
-    strcpy(f->dname, item->file_as.str);
-
-    if (overwrite != 1) {
-        int x = 0;
-        char *temp = (char*) pst_malloc (strlen(f->name)+10); //enough room for 10 digits
+    if (mode != MODE_SEPARATE) {
+        int32_t t;
+        for (t=0; t<PST_TYPE_MAX; t++) {
+            if (f->name[t]) {
+                if (!overwrite) {
+                    int x = 0;
+                    char *temp = (char*) pst_malloc (strlen(f->name[t])+10); //enough room for 10 digits
 
-        sprintf(temp, "%s", f->name);
-        check_filename(temp);
-        while ((f->output = fopen(temp, "r"))) {
-            DEBUG_INFO(("need to increase filename because one already exists with that name\n"));
-            DEBUG_INFO(("- increasing it to %s%d\n", f->name, x));
-            x++;
-            sprintf(temp, "%s%08d", f->name, x);
-            DEBUG_INFO(("- trying \"%s\"\n", f->name));
-            if (x == 99999999) {
-                DIE(("create_enter_dir: Why can I not create a folder %s? I have tried %i extensions...\n", f->name, x));
+                    sprintf(temp, "%s", f->name[t]);
+                    check_filename(temp);
+                    while ((f->output[t] = fopen(temp, "r"))) {
+                        DEBUG_INFO(("need to increase filename because one already exists with that name\n"));
+                        DEBUG_INFO(("- increasing it to %s%d\n", f->name, x));
+                        x++;
+                        sprintf(temp, "%s%08d", f->name, x);
+                        DEBUG_INFO(("- trying \"%s\"\n", f->name));
+                        if (x == 99999999) {
+                            DIE(("create_enter_dir: Why can I not create a folder %s? I have tried %i extensions...\n", f->name, x));
+                        }
+                        fclose(f->output[t]);
+                    }
+                    if (x > 0) { //then the f->name should change
+                        free (f->name[t]);
+                        f->name[t] = temp;
+                    } else {
+                        free(temp);
+                    }
+                }
+                check_filename(f->name[t]);
+                if (!(f->output[t] = fopen(f->name[t], "w"))) {
+                    DIE(("create_enter_dir: Could not open file \"%s\" for write\n", f->name[t]));
+                }
+                DEBUG_INFO(("f->name = %s\nitem->folder_name = %s\n", f->name[t], item->file_as.str));
             }
-            fclose(f->output);
-        }
-        if (x > 0) { //then the f->name should change
-            free (f->name);
-            f->name = temp;
-        } else {
-            free(temp);
-        }
-    }
-
-    DEBUG_INFO(("f->name = %s\nitem->folder_name = %s\n", f->name, item->file_as.str));
-    if (mode != MODE_SEPARATE) {
-        check_filename(f->name);
-        if (!(f->output = fopen(f->name, "w"))) {
-            DIE(("create_enter_dir: Could not open file \"%s\" for write\n", f->name));
         }
     }
     DEBUG_RET();
@@ -2241,6 +2248,7 @@
 
 void close_enter_dir(struct file_ll *f)
 {
+    int32_t t;
     DEBUG_INFO(("processed item count for folder %s is %i, skipped %i, total %i \n",
                 f->dname, f->item_count, f->skip_count, f->stored_count));
     if (output_mode != OUTPUT_QUIET) {
@@ -2249,18 +2257,21 @@
             fflush(stdout);
         pst_debug_unlock();
     }
-    if (f->output) {
-        if (mode == MODE_SEPARATE) DEBUG_WARN(("close_enter_dir finds open separate file\n"));
-        struct stat st;
-        fclose(f->output);
-        stat(f->name, &st);
-        if (!st.st_size) {
-            DEBUG_WARN(("removing empty output file %s\n", f->name));
-            remove(f->name);
+    for (t=0; t<PST_TYPE_MAX; t++) {
+        if (f->output[t]) {
+            if (mode == MODE_SEPARATE) DEBUG_WARN(("close_enter_dir finds open separate file\n"));
+            struct stat st;
+            fclose(f->output[t]);
+            stat(f->name[t], &st);
+            if (!st.st_size) {
+                DEBUG_WARN(("removing empty output file %s\n", f->name[t]));
+                remove(f->name[t]);
+            }
+            f->output[t] = NULL;
         }
-        f->output = NULL;
+        free(f->name[t]);
+        f->name[t] = NULL;
     }
-    free(f->name);
     free(f->dname);
 
     if (mode == MODE_KMAIL)
--- a/xml/libpst.in	Wed Jul 06 12:14:55 2016 -0700
+++ b/xml/libpst.in	Mon Aug 29 09:50:24 2016 -0700
@@ -35,7 +35,7 @@
 
     <refentry id="readpst.1">
         <refentryinfo>
-            <date>2015-03-09</date>
+            <date>2016-08-29</date>
         </refentryinfo>
 
         <refmeta>
@@ -208,10 +208,13 @@
                 <varlistentry>
                     <term>-r</term>
                     <listitem><para>
-                        Changes the output format to Recursive. This will create folders as
-                        named in the PST file, and will put all emails in a file called "mbox"
-                        inside each folder. These files are then compatible with all
-                        mbox-compatible email clients. This format uses mboxrd from quoting.
+                        Changes the output format to Recursive. This will create folders
+                        as named in the PST file, and will put all emails in a file called
+                        "mbox" inside each folder. Appointments go into a file called
+                        "calendar", address book entries go into a file called "contacts",
+                        and journal entries go into a file called "journal".  These files
+                        are then compatible with all mbox-compatible email clients. This
+                        format uses mboxrd from quoting.
                     </para></listitem>
                 </varlistentry>
                 <varlistentry>
@@ -302,7 +305,7 @@
 
     <refentry id="lspst.1">
         <refentryinfo>
-            <date>2015-03-09</date>
+            <date>2016-08-29</date>
         </refentryinfo>
 
         <refmeta>
@@ -397,7 +400,7 @@
 
     <refentry id="pst2ldif.1">
         <refentryinfo>
-            <date>2015-03-09</date>
+            <date>2016-08-29</date>
         </refentryinfo>
 
         <refmeta>
@@ -565,7 +568,7 @@
 
     <refentry id="pst2dii.1">
         <refentryinfo>
-            <date>2015-03-09</date>
+            <date>2016-08-29</date>
         </refentryinfo>
 
         <refmeta>
@@ -698,7 +701,7 @@
 
     <refentry id="pst.5">
         <refentryinfo>
-            <date>2015-03-09</date>
+            <date>2016-08-29</date>
         </refentryinfo>
 
         <refmeta>
@@ -1687,7 +1690,7 @@
 0x1035  Message ID
 0x1042  In-Reply-To or Parent's Message ID
 0x1046  Return Path
-0x3001  Folder Name? I have seen this value used for the contacts record aswell
+0x3001  Folder Name? I have also seen this value used for the contacts record
 0x3002  Address Type
 0x3003  Contact Address
 0x3004  Comment