libpst

changeset 373:0ccc746c8079

Zachary Travis - Add support for the OST 2013 format, and Content-Disposition filename key fix for outlook compatibility
author Carl Byington <carl@five-ten-sg.com>
date Fri, 21 Jul 2017 20:01:44 -0700
parents 5b52efe35bd8
children 62f05deb2e1c
files configure.in libpst.spec.in src/Makefile.am src/libpst.c src/libpst.h src/readpst.c
diffstat 6 files changed, 188 insertions(+), 60 deletions(-) [+]
line diff
     1.1 --- a/configure.in	Fri Jul 21 19:54:46 2017 -0700
     1.2 +++ b/configure.in	Fri Jul 21 20:01:44 2017 -0700
     1.3 @@ -377,6 +377,8 @@
     1.4  AC_SUBST(GSF_FLAGS, [$gsf_flags])
     1.5  AC_SUBST(GSF_LIBS, [$gsf_libs])
     1.6  
     1.7 +PKG_CHECK_MODULES([ZLIB], [zlib])
     1.8 +
     1.9  AC_OUTPUT(                  \
    1.10      Makefile                \
    1.11      html/Makefile           \
     2.1 --- a/libpst.spec.in	Fri Jul 21 19:54:46 2017 -0700
     2.2 +++ b/libpst.spec.in	Fri Jul 21 20:01:44 2017 -0700
     2.3 @@ -182,8 +182,10 @@
     2.4  
     2.5  
     2.6  %changelog
     2.7 -* Wed Feb 08 2017 Carl Byington <carl@five-ten-sg.com> 0.6.71-1
     2.8 +* Fri Jul 21 2017 Carl Byington <carl@five-ten-sg.com> 0.6.71-1
     2.9  - fedora python naming scheme changes
    2.10 +- Zachary Travis - Add support for the OST 2013 format, and
    2.11 +  Content-Disposition filename key fix for outlook compatibility
    2.12  
    2.13  * Thu Jul 20 2017 Kalev Lember <klember@redhat.com> - 0.6.70-3
    2.14  - Rebuilt for Boost 1.64
     3.1 --- a/src/Makefile.am	Fri Jul 21 19:54:46 2017 -0700
     3.2 +++ b/src/Makefile.am	Fri Jul 21 20:01:44 2017 -0700
     3.3 @@ -91,13 +91,13 @@
     3.4  INCLUDES= -I$(srcdir)/.. $(all_includes)
     3.5  
     3.6  # the library search path.
     3.7 -lspst_LDADD       = $(all_libraries) $(PSTLIB) $(LTLIBICONV)
     3.8 -readpst_LDADD     = $(all_libraries) $(PSTLIB) $(LTLIBICONV) $(REGEXLIB) $(GSF_LIBS)
     3.9 -pst2ldif_LDADD    = $(all_libraries) $(PSTLIB) $(LTLIBICONV)
    3.10 -pst2dii_LDADD     = $(all_libraries) $(PSTLIB) $(LTLIBICONV) -lgd
    3.11 -deltasearch_LDADD = $(all_libraries) $(PSTLIB) $(LTLIBICONV)
    3.12 -dumpblocks_LDADD  = $(all_libraries) $(PSTLIB) $(LTLIBICONV)
    3.13 -getidblock_LDADD  = $(all_libraries) $(PSTLIB) $(LTLIBICONV)
    3.14 -nick2ldif_LDADD   = $(all_libraries) $(PSTLIB) $(LTLIBICONV)
    3.15 +lspst_LDADD       = $(all_libraries) $(PSTLIB) $(LTLIBICONV) @ZLIB_LIBS@
    3.16 +readpst_LDADD     = $(all_libraries) $(PSTLIB) $(LTLIBICONV) $(REGEXLIB) $(GSF_LIBS) @ZLIB_LIBS@
    3.17 +pst2ldif_LDADD    = $(all_libraries) $(PSTLIB) $(LTLIBICONV) @ZLIB_LIBS@
    3.18 +pst2dii_LDADD     = $(all_libraries) $(PSTLIB) $(LTLIBICONV) -lgd @ZLIB_LIBS@
    3.19 +deltasearch_LDADD = $(all_libraries) $(PSTLIB) $(LTLIBICONV) @ZLIB_LIBS@
    3.20 +dumpblocks_LDADD  = $(all_libraries) $(PSTLIB) $(LTLIBICONV) @ZLIB_LIBS@
    3.21 +getidblock_LDADD  = $(all_libraries) $(PSTLIB) $(LTLIBICONV) @ZLIB_LIBS@
    3.22 +nick2ldif_LDADD   = $(all_libraries) $(PSTLIB) $(LTLIBICONV) @ZLIB_LIBS@
    3.23  
    3.24  
     4.1 --- a/src/libpst.c	Fri Jul 21 19:54:46 2017 -0700
     4.2 +++ b/src/libpst.c	Fri Jul 21 20:01:44 2017 -0700
     4.3 @@ -6,6 +6,7 @@
     4.4   */
     4.5  
     4.6  #include "define.h"
     4.7 +#include "zlib.h"
     4.8  
     4.9  
    4.10  // switch to maximal packing for our own internal structures
    4.11 @@ -23,6 +24,7 @@
    4.12  #define INDEX_TYPE32A           0x0F    // unknown, but assumed to be similar for now
    4.13  #define INDEX_TYPE64            0x17
    4.14  #define INDEX_TYPE64A           0x15    // http://sourceforge.net/projects/libpff/
    4.15 +#define INDEX_TYPE4K            0x24
    4.16  #define INDEX_TYPE_OFFSET       (int64_t)0x0A
    4.17  
    4.18  #define FILE_SIZE_POINTER32     (int64_t)0xA8
    4.19 @@ -46,6 +48,7 @@
    4.20  #define SECOND_BACK       ((pf->do_read64) ? SECOND_BACK64       : SECOND_BACK32)
    4.21  #define ENC_TYPE          ((pf->do_read64) ? ENC_TYPE64          : ENC_TYPE32)
    4.22  
    4.23 +
    4.24  #define PST_SIGNATURE 0x4E444221
    4.25  
    4.26  
    4.27 @@ -135,10 +138,19 @@
    4.28  } pst_desc;
    4.29  
    4.30  
    4.31 +typedef struct pst_index64 {
    4.32 +    uint64_t id;
    4.33 +    uint64_t offset;
    4.34 +    uint16_t size;
    4.35 +    int16_t  u0;
    4.36 +    int32_t  u1;
    4.37 +} pst_index64;
    4.38 +
    4.39  typedef struct pst_index {
    4.40      uint64_t id;
    4.41      uint64_t offset;
    4.42      uint16_t size;
    4.43 +    uint16_t inflated_size;
    4.44      int16_t  u0;
    4.45      int32_t  u1;
    4.46  } pst_index;
    4.47 @@ -281,7 +293,8 @@
    4.48  static void             pst_printDptr(pst_file *pf, pst_desc_tree *ptr);
    4.49  static void             pst_printID2ptr(pst_id2_tree *ptr);
    4.50  static int              pst_process(uint64_t block_id, pst_mapi_object *list, pst_item *item, pst_item_attach *attach);
    4.51 -static size_t           pst_read_block_size(pst_file *pf, int64_t offset, size_t size, char **buf);
    4.52 +static size_t           pst_read_block_size(pst_file *pf, int64_t offset, size_t size, size_t inflated_size, char **buf);
    4.53 +static size_t           pst_read_raw_block_size(pst_file *pf, int64_t offset, size_t size, char **buf);
    4.54  static int              pst_decrypt(uint64_t i_id, char *buf, size_t size, unsigned char type);
    4.55  static int              pst_strincmp(char *a, char *b, size_t x);
    4.56  static char*            pst_wide_to_single(char *wt, size_t size);
    4.57 @@ -348,6 +361,9 @@
    4.58          case INDEX_TYPE64A :
    4.59              pf->do_read64 = 1;
    4.60              break;
    4.61 +        case INDEX_TYPE4K :
    4.62 +            pf->do_read64 = 2;
    4.63 +            break;
    4.64          default:
    4.65              (void)fclose(pf->fp);
    4.66              DEBUG_WARN(("unknown .pst format, possibly newer than Outlook 2003 PST file?\n"));
    4.67 @@ -807,31 +823,32 @@
    4.68  
    4.69  
    4.70  #define ITEM_COUNT_OFFSET32        0x1f0    // count byte
    4.71 +#define MAX_COUNT_OFFSET32         0x1f1
    4.72 +#define ENTRY_SIZE_OFFSET32        0x1f2
    4.73  #define LEVEL_INDICATOR_OFFSET32   0x1f3    // node or leaf
    4.74  #define BACKLINK_OFFSET32          0x1f8    // backlink u1 value
    4.75 -#define ITEM_SIZE32                12
    4.76 -#define DESC_SIZE32                16
    4.77 -#define INDEX_COUNT_MAX32          41       // max active items
    4.78 -#define DESC_COUNT_MAX32           31       // max active items
    4.79  
    4.80  #define ITEM_COUNT_OFFSET64        0x1e8    // count byte
    4.81 +#define MAX_COUNT_OFFSET64         0x1e9
    4.82 +#define ENTRY_SIZE_OFFSET64        0x1ea    // node or leaf
    4.83  #define LEVEL_INDICATOR_OFFSET64   0x1eb    // node or leaf
    4.84  #define BACKLINK_OFFSET64          0x1f8    // backlink u1 value
    4.85 -#define ITEM_SIZE64                24
    4.86 -#define DESC_SIZE64                32
    4.87 -#define INDEX_COUNT_MAX64          20       // max active items
    4.88 -#define DESC_COUNT_MAX64           15       // max active items
    4.89 -
    4.90 -#define BLOCK_SIZE                 512      // index blocks
    4.91 -#define DESC_BLOCK_SIZE            512      // descriptor blocks
    4.92 -#define ITEM_COUNT_OFFSET        (size_t)((pf->do_read64) ? ITEM_COUNT_OFFSET64      : ITEM_COUNT_OFFSET32)
    4.93 -#define LEVEL_INDICATOR_OFFSET   (size_t)((pf->do_read64) ? LEVEL_INDICATOR_OFFSET64 : LEVEL_INDICATOR_OFFSET32)
    4.94 -#define BACKLINK_OFFSET          (size_t)((pf->do_read64) ? BACKLINK_OFFSET64        : BACKLINK_OFFSET32)
    4.95 -#define ITEM_SIZE                (size_t)((pf->do_read64) ? ITEM_SIZE64              : ITEM_SIZE32)
    4.96 -#define DESC_SIZE                (size_t)((pf->do_read64) ? DESC_SIZE64              : DESC_SIZE32)
    4.97 -#define INDEX_COUNT_MAX         (int32_t)((pf->do_read64) ? INDEX_COUNT_MAX64        : INDEX_COUNT_MAX32)
    4.98 -#define DESC_COUNT_MAX          (int32_t)((pf->do_read64) ? DESC_COUNT_MAX64         : DESC_COUNT_MAX32)
    4.99 -
   4.100 +
   4.101 +#define ITEM_COUNT_OFFSET4K        0xfd8
   4.102 +#define MAX_COUNT_OFFSET4K         0xfda
   4.103 +#define ENTRY_SIZE_OFFSET4K        0xfdc
   4.104 +#define LEVEL_INDICATOR_OFFSET4K   0xfdd
   4.105 +#define BACKLINK_OFFSET4K          0xff0
   4.106 +
   4.107 +#define BLOCK_SIZE               (size_t)((pf->do_read64 == 2) ? 4096 : 512)      // index blocks
   4.108 +#define DESC_BLOCK_SIZE          (size_t)((pf->do_read64 == 2) ? 4096 : 512)      // descriptor blocks
   4.109 +#define ITEM_COUNT_OFFSET        (size_t)((pf->do_read64) ? (pf->do_read64 == 2 ? ITEM_COUNT_OFFSET4K : ITEM_COUNT_OFFSET64) : ITEM_COUNT_OFFSET32)
   4.110 +#define LEVEL_INDICATOR_OFFSET   (size_t)((pf->do_read64) ? (pf->do_read64 == 2 ? LEVEL_INDICATOR_OFFSET4K : LEVEL_INDICATOR_OFFSET64) : LEVEL_INDICATOR_OFFSET32)
   4.111 +#define BACKLINK_OFFSET          (size_t)((pf->do_read64) ? (pf->do_read64 == 2 ? BACKLINK_OFFSET4K : BACKLINK_OFFSET64) : BACKLINK_OFFSET32)
   4.112 +#define ENTRY_SIZE_OFFSET        (size_t)((pf->do_read64) ? (pf->do_read64 == 2 ? ENTRY_SIZE_OFFSET4K : ENTRY_SIZE_OFFSET64) : ENTRY_SIZE_OFFSET32)
   4.113 +#define MAX_COUNT_OFFSET         (size_t)((pf->do_read64) ? (pf->do_read64 == 2 ? MAX_COUNT_OFFSET4K : MAX_COUNT_OFFSET64) : MAX_COUNT_OFFSET32)
   4.114 +
   4.115 +#define read_twobyte(BUF, OFF)   (int32_t) ((((unsigned)BUF[OFF + 1] & 0xFF)) << 8) | ((unsigned)BUF[OFF] & 0xFF);
   4.116  
   4.117  static size_t pst_decode_desc(pst_file *pf, pst_desc *desc, char *buf);
   4.118  static size_t pst_decode_desc(pst_file *pf, pst_desc *desc, char *buf) {
   4.119 @@ -899,16 +916,34 @@
   4.120  static size_t pst_decode_index(pst_file *pf, pst_index *index, char *buf);
   4.121  static size_t pst_decode_index(pst_file *pf, pst_index *index, char *buf) {
   4.122      size_t r;
   4.123 -    if (pf->do_read64) {
   4.124 -        DEBUG_INFO(("Decoding index64\n"));
   4.125 +    if (pf->do_read64 == 2) {
   4.126 +        DEBUG_INFO(("Decoding index4k\n"));
   4.127          DEBUG_HEXDUMPC(buf, sizeof(pst_index), 0x10);
   4.128          memcpy(index, buf, sizeof(pst_index));
   4.129          LE64_CPU(index->id);
   4.130          LE64_CPU(index->offset);
   4.131          LE16_CPU(index->size);
   4.132 +        LE16_CPU(index->inflated_size);
   4.133          LE16_CPU(index->u0);
   4.134          LE32_CPU(index->u1);
   4.135          r = sizeof(pst_index);
   4.136 +    } else  if (pf->do_read64 == 1) {
   4.137 +        pst_index64 index64;
   4.138 +        DEBUG_INFO(("Decoding index64\n"));
   4.139 +        DEBUG_HEXDUMPC(buf, sizeof(pst_index64), 0x10);
   4.140 +        memcpy(&index64, buf, sizeof(pst_index64));
   4.141 +        LE64_CPU(index64.id);
   4.142 +        LE64_CPU(index64.offset);
   4.143 +        LE16_CPU(index64.size);
   4.144 +        LE16_CPU(index64.u0);
   4.145 +        LE32_CPU(index64.u1);
   4.146 +        index->id     = index64.id;
   4.147 +        index->offset = index64.offset;
   4.148 +        index->size   = index64.size;
   4.149 +        index->inflated_size = index64.size;
   4.150 +        index->u0     = index64.u0;
   4.151 +        index->u1     = index64.u1;
   4.152 +        r = sizeof(pst_index64);
   4.153      } else {
   4.154          pst_index32 index32;
   4.155          DEBUG_INFO(("Decoding index32\n"));
   4.156 @@ -921,6 +956,7 @@
   4.157          index->id     = index32.id;
   4.158          index->offset = index32.offset;
   4.159          index->size   = index32.size;
   4.160 +        index->inflated_size = index32.size;
   4.161          index->u0     = 0;
   4.162          index->u1     = index32.u1;
   4.163          r = sizeof(pst_index32);
   4.164 @@ -990,7 +1026,7 @@
   4.165      struct pst_table_ptr_struct table, table2;
   4.166      pst_index_ll *i_ptr=NULL;
   4.167      pst_index index;
   4.168 -    int32_t x, item_count;
   4.169 +    int32_t x, item_count, count_max;
   4.170      uint64_t old = start_val;
   4.171      char *buf = NULL, *bptr;
   4.172  
   4.173 @@ -1002,17 +1038,23 @@
   4.174          return -1;
   4.175      }
   4.176      DEBUG_INFO(("Reading index block\n"));
   4.177 -    if (pst_read_block_size(pf, offset, BLOCK_SIZE, &buf) < BLOCK_SIZE) {
   4.178 +    if (pst_read_block_size(pf, offset, BLOCK_SIZE, BLOCK_SIZE, &buf) < BLOCK_SIZE) {
   4.179          DEBUG_WARN(("Failed to read %i bytes\n", BLOCK_SIZE));
   4.180          if (buf) free(buf);
   4.181          DEBUG_RET();
   4.182          return -1;
   4.183      }
   4.184      bptr = buf;
   4.185 -    DEBUG_HEXDUMPC(buf, BLOCK_SIZE, ITEM_SIZE32);
   4.186 -    item_count = (int32_t)(unsigned)(buf[ITEM_COUNT_OFFSET]);
   4.187 -    if (item_count > INDEX_COUNT_MAX) {
   4.188 -        DEBUG_WARN(("Item count %i too large, max is %i\n", item_count, INDEX_COUNT_MAX));
   4.189 +    DEBUG_HEXDUMPC(buf, BLOCK_SIZE, 0x10);
   4.190 +    if (pf->do_read64 == 2) {
   4.191 +        item_count = read_twobyte(buf, ITEM_COUNT_OFFSET);
   4.192 +        count_max = read_twobyte(buf, MAX_COUNT_OFFSET);
   4.193 +    } else {
   4.194 +        item_count = (int32_t)(unsigned)(buf[ITEM_COUNT_OFFSET]);
   4.195 +        count_max = (int32_t)(unsigned)(buf[MAX_COUNT_OFFSET]);
   4.196 +    }
   4.197 +    if (item_count > count_max) {
   4.198 +        DEBUG_WARN(("Item count %i too large, max is %i\n", item_count, count_max));
   4.199          if (buf) free(buf);
   4.200          DEBUG_RET();
   4.201          return -1;
   4.202 @@ -1024,12 +1066,14 @@
   4.203          DEBUG_RET();
   4.204          return -1;
   4.205      }
   4.206 -
   4.207 +    int entry_size = (int32_t)(unsigned)(buf[ENTRY_SIZE_OFFSET]);
   4.208 +    DEBUG_INFO(("count %#"PRIx64" max %#"PRIx64" size %#"PRIx64"\n", item_count, count_max, entry_size));
   4.209      if (buf[LEVEL_INDICATOR_OFFSET] == '\0') {
   4.210          // this node contains leaf pointers
   4.211          x = 0;
   4.212          while (x < item_count) {
   4.213 -            bptr += pst_decode_index(pf, &index, bptr);
   4.214 +            pst_decode_index(pf, &index, bptr);
   4.215 +            bptr += entry_size;
   4.216              x++;
   4.217              if (index.id == 0) break;
   4.218              DEBUG_INFO(("[%i]%i Item [id = %#"PRIx64", offset = %#"PRIx64", u1 = %#x, size = %i(%#x)]\n",
   4.219 @@ -1051,12 +1095,14 @@
   4.220              i_ptr->offset = index.offset;
   4.221              i_ptr->u1     = index.u1;
   4.222              i_ptr->size   = index.size;
   4.223 +            i_ptr->inflated_size = index.inflated_size;
   4.224          }
   4.225      } else {
   4.226          // this node contains node pointers
   4.227          x = 0;
   4.228          while (x < item_count) {
   4.229 -            bptr += pst_decode_table(pf, &table, bptr);
   4.230 +            pst_decode_table(pf, &table, bptr);
   4.231 +            bptr += entry_size;
   4.232              x++;
   4.233              if (table.start == 0) break;
   4.234              if (x < item_count) {
   4.235 @@ -1090,7 +1136,7 @@
   4.236  static int pst_build_desc_ptr (pst_file *pf, int64_t offset, int32_t depth, uint64_t linku1, uint64_t start_val, uint64_t end_val) {
   4.237      struct pst_table_ptr_struct table, table2;
   4.238      pst_desc desc_rec;
   4.239 -    int32_t item_count;
   4.240 +    int32_t item_count, count_max;
   4.241      uint64_t old = start_val;
   4.242      int x;
   4.243      char *buf = NULL, *bptr;
   4.244 @@ -1103,15 +1149,20 @@
   4.245          return -1;
   4.246      }
   4.247      DEBUG_INFO(("Reading desc block\n"));
   4.248 -    if (pst_read_block_size(pf, offset, DESC_BLOCK_SIZE, &buf) < DESC_BLOCK_SIZE) {
   4.249 +    if (pst_read_block_size(pf, offset, DESC_BLOCK_SIZE, DESC_BLOCK_SIZE, &buf) < DESC_BLOCK_SIZE) {
   4.250          DEBUG_WARN(("Failed to read %i bytes\n", DESC_BLOCK_SIZE));
   4.251          if (buf) free(buf);
   4.252          DEBUG_RET();
   4.253          return -1;
   4.254      }
   4.255      bptr = buf;
   4.256 -    item_count = (int32_t)(unsigned)(buf[ITEM_COUNT_OFFSET]);
   4.257 -
   4.258 +    if (pf->do_read64 == 2) {
   4.259 +        item_count = read_twobyte(buf, ITEM_COUNT_OFFSET);
   4.260 +        count_max = read_twobyte(buf, MAX_COUNT_OFFSET);
   4.261 +    } else {
   4.262 +        item_count = (int32_t)(unsigned)(buf[ITEM_COUNT_OFFSET]);
   4.263 +        count_max = (int32_t)(unsigned)(buf[MAX_COUNT_OFFSET]);
   4.264 +    }
   4.265      desc_rec.d_id = pst_getIntAt(pf, buf+BACKLINK_OFFSET);
   4.266      if (desc_rec.d_id != linku1) {
   4.267          DEBUG_WARN(("Backlink %#"PRIx64" in this node does not match required %#"PRIx64"\n", desc_rec.d_id, linku1));
   4.268 @@ -1119,17 +1170,19 @@
   4.269          DEBUG_RET();
   4.270          return -1;
   4.271      }
   4.272 +    int32_t entry_size = (int32_t)(unsigned)(buf[ENTRY_SIZE_OFFSET]);
   4.273      if (buf[LEVEL_INDICATOR_OFFSET] == '\0') {
   4.274          // this node contains leaf pointers
   4.275 -        DEBUG_HEXDUMPC(buf, DESC_BLOCK_SIZE, DESC_SIZE32);
   4.276 -        if (item_count > DESC_COUNT_MAX) {
   4.277 -            DEBUG_WARN(("Item count %i too large, max is %i\n", item_count, DESC_COUNT_MAX));
   4.278 +        DEBUG_HEXDUMPC(buf, DESC_BLOCK_SIZE, entry_size);
   4.279 +        if (item_count > count_max) {
   4.280 +            DEBUG_WARN(("Item count %i too large, max is %i\n", item_count, count_max));
   4.281              if (buf) free(buf);
   4.282              DEBUG_RET();
   4.283              return -1;
   4.284          }
   4.285          for (x=0; x<item_count; x++) {
   4.286 -            bptr += pst_decode_desc(pf, &desc_rec, bptr);
   4.287 +            pst_decode_desc(pf, &desc_rec, bptr);
   4.288 +            bptr += entry_size;
   4.289              DEBUG_INFO(("[%i] Item(%#x) = [d_id = %#"PRIx64", desc_id = %#"PRIx64", tree_id = %#"PRIx64", parent_d_id = %#x]\n",
   4.290                          depth, x, desc_rec.d_id, desc_rec.desc_id, desc_rec.tree_id, desc_rec.parent_d_id));
   4.291              if ((desc_rec.d_id >= end_val) || (desc_rec.d_id < old)) {
   4.292 @@ -1152,15 +1205,16 @@
   4.293          }
   4.294      } else {
   4.295          // this node contains node pointers
   4.296 -        DEBUG_HEXDUMPC(buf, DESC_BLOCK_SIZE, ITEM_SIZE32);
   4.297 -        if (item_count > INDEX_COUNT_MAX) {
   4.298 -            DEBUG_WARN(("Item count %i too large, max is %i\n", item_count, INDEX_COUNT_MAX));
   4.299 +        DEBUG_HEXDUMPC(buf, DESC_BLOCK_SIZE, entry_size);
   4.300 +        if (item_count > count_max) {
   4.301 +            DEBUG_WARN(("Item count %i too large, max is %i\n", item_count, count_max));
   4.302              if (buf) free(buf);
   4.303              DEBUG_RET();
   4.304              return -1;
   4.305          }
   4.306          for (x=0; x<item_count; x++) {
   4.307 -            bptr += pst_decode_table(pf, &table, bptr);
   4.308 +            pst_decode_table(pf, &table, bptr);
   4.309 +            bptr += entry_size;
   4.310              if (table.start == 0) break;
   4.311              if (x < (item_count-1)) {
   4.312                  (void)pst_decode_table(pf, &table2, bptr);
   4.313 @@ -3246,7 +3300,7 @@
   4.314      pst_id2_tree *i2_ptr = NULL;
   4.315      DEBUG_ENT("pst_build_id2");
   4.316  
   4.317 -    if (pst_read_block_size(pf, list->offset, list->size, &buf) < list->size) {
   4.318 +    if (pst_read_block_size(pf, list->offset, list->size, list->inflated_size, &buf) < list->size) {
   4.319          //an error occured in block read
   4.320          DEBUG_WARN(("block read error occured. offset = %#"PRIx64", size = %#"PRIx64"\n", list->offset, list->size));
   4.321          if (buf) free(buf);
   4.322 @@ -3277,7 +3331,7 @@
   4.323              DEBUG_WARN(("%#"PRIx64" - Not Found\n", id2_rec.id));
   4.324          } else {
   4.325              DEBUG_INFO(("%#"PRIx64" - Offset %#"PRIx64", u1 %#"PRIx64", Size %"PRIi64"(%#"PRIx64")\n",
   4.326 -                         i_ptr->i_id, i_ptr->offset, i_ptr->u1, i_ptr->size, i_ptr->size));
   4.327 +                         i_ptr->i_id, i_ptr->offset, i_ptr->u1, i_ptr->size, i_ptr->inflated_size));
   4.328              // add it to the tree
   4.329              i2_ptr = (pst_id2_tree*) pst_malloc(sizeof(pst_id2_tree));
   4.330              i2_ptr->id2   = id2_rec.id2;
   4.331 @@ -3564,8 +3618,13 @@
   4.332          }
   4.333      }
   4.334      else {
   4.335 +        DEBUG_WARN(("Found internal %#x value.\n", offset));
   4.336          // internal index reference
   4.337          size_t subindex  = offset >> 16;
   4.338 +        if (pf->do_read64 == 2) {
   4.339 +            // Shift over 3 more bits for new flags.
   4.340 +            subindex = subindex >> 3;
   4.341 +        }
   4.342          size_t suboffset = offset & 0xffff;
   4.343          if (subindex < subblocks->subblock_count) {
   4.344              if (pst_getBlockOffset(subblocks->subs[subindex].buf,
   4.345 @@ -3720,10 +3779,10 @@
   4.346                   is non-NULL, it will first be free()d
   4.347   * @return       size of block read into memory
   4.348   */
   4.349 -static size_t pst_read_block_size(pst_file *pf, int64_t offset, size_t size, char **buf) {
   4.350 +static size_t pst_read_raw_block_size(pst_file *pf, int64_t offset, size_t size, char **buf) {
   4.351      size_t rsize;
   4.352 -    DEBUG_ENT("pst_read_block_size");
   4.353 -    DEBUG_INFO(("Reading block from %#"PRIx64", %x bytes\n", offset, size));
   4.354 +    DEBUG_ENT("pst_read_raw_block_size");
   4.355 +    DEBUG_INFO(("Reading raw block from %#"PRIx64", %x bytes\n", offset, size));
   4.356  
   4.357      if (*buf) {
   4.358          DEBUG_INFO(("Freeing old memory\n"));
   4.359 @@ -3747,6 +3806,36 @@
   4.360      return rsize;
   4.361  }
   4.362  
   4.363 +static size_t pst_read_block_size(pst_file *pf, int64_t offset, size_t size, size_t inflated_size, char **buf) {
   4.364 +    DEBUG_ENT("pst_read_block_size");
   4.365 +    DEBUG_INFO(("Reading block from %#"PRIx64", %x bytes, %x inflated\n", offset, size, inflated_size));
   4.366 +    if (inflated_size <= size) {
   4.367 +        // Not deflated.
   4.368 +        size_t ret = pst_read_raw_block_size(pf, offset, size, buf);
   4.369 +        DEBUG_RET();
   4.370 +        return ret;
   4.371 +    }
   4.372 +    // We need to read the raw block and inflate it.
   4.373 +    char *zbuf = NULL;
   4.374 +    if (pst_read_raw_block_size(pf, offset, size, &zbuf) != size) {
   4.375 +        DEBUG_WARN(("Failed to read %i bytes\n", size));
   4.376 +        if (zbuf) free(zbuf);
   4.377 +        DEBUG_RET();
   4.378 +        return -1;
   4.379 +    }
   4.380 +    *buf = (char *) pst_malloc(inflated_size);
   4.381 +    size_t result_size = inflated_size;
   4.382 +    if (uncompress((Bytef *) *buf, &result_size, (Bytef *) zbuf, size) != Z_OK || result_size != inflated_size) {
   4.383 +        DEBUG_WARN(("Failed to uncompress %i bytes to %i bytes, got %i\n", size, inflated_size, result_size));
   4.384 +        if (zbuf) free(zbuf);
   4.385 +        DEBUG_RET();
   4.386 +        return -1;
   4.387 +    }
   4.388 +    DEBUG_RET();
   4.389 +    return inflated_size;
   4.390 +}
   4.391 +
   4.392 +
   4.393  
   4.394  /** Decrypt a block of data from the pst file.
   4.395   * @param i_id identifier of this block, needed as part of the key for the enigma cipher
   4.396 @@ -3923,7 +4012,7 @@
   4.397          return 0;
   4.398      }
   4.399      DEBUG_INFO(("id = %#"PRIx64", record size = %#x, offset = %#x\n", i_id, rec->size, rec->offset));
   4.400 -    rsize = pst_read_block_size(pf, rec->offset, rec->size, buf);
   4.401 +    rsize = pst_read_block_size(pf, rec->offset, rec->size, rec->inflated_size, buf);
   4.402      DEBUG_RET();
   4.403      return rsize;
   4.404  }
     5.1 --- a/src/libpst.h	Fri Jul 21 19:54:46 2017 -0700
     5.2 +++ b/src/libpst.h	Fri Jul 21 20:01:44 2017 -0700
     5.3 @@ -105,6 +105,7 @@
     5.4      uint64_t i_id;
     5.5      uint64_t offset;
     5.6      uint64_t size;
     5.7 +    uint64_t inflated_size;
     5.8      int64_t  u1;
     5.9  } pst_index_ll;
    5.10  
    5.11 @@ -907,7 +908,8 @@
    5.12      pst_block_recorder *block_head;
    5.13  
    5.14      /** @li 0 is 32-bit pst file, pre Outlook 2003;
    5.15 -     *  @li 1 is 64-bit pst file, Outlook 2003 or later */
    5.16 +     *  @li 1 is 64-bit pst file, Outlook 2003 or later;
    5.17 +     *  @li 2 is 64-bit OST file, Outlook 2013 or later */
    5.18      int do_read64;
    5.19      /** file offset of the first b-tree node in the index tree */
    5.20      uint64_t index1;
     6.1 --- a/src/readpst.c	Fri Jul 21 19:54:46 2017 -0700
     6.2 +++ b/src/readpst.c	Fri Jul 21 20:01:44 2017 -0700
     6.3 @@ -66,6 +66,7 @@
     6.4  void      write_appointment(FILE* f_output, pst_item *item);
     6.5  void      create_enter_dir(struct file_ll* f, pst_item *item);
     6.6  void      close_enter_dir(struct file_ll *f);
     6.7 +char*     quote_string(char *inp);
     6.8  
     6.9  const char*  prog_name;
    6.10  char*  output_dir = ".";
    6.11 @@ -1151,6 +1152,32 @@
    6.12      DEBUG_RET();
    6.13  }
    6.14  
    6.15 +/**
    6.16 + * Backslash-escape quotes and backslashes in the given string.
    6.17 + */
    6.18 +char *quote_string(char *inp) {
    6.19 +    int i = 0;
    6.20 +    int count = 0;
    6.21 +    char *curr = inp;
    6.22 +    while (*curr) {
    6.23 +        *curr++;
    6.24 +        if (*curr == '\"' || *curr == '\\') {
    6.25 +            count++;
    6.26 +        }
    6.27 +        i++;
    6.28 +    }
    6.29 +    char *res = malloc(i + count + 1);
    6.30 +    char *curr_in = inp;
    6.31 +    char *curr_out = res;
    6.32 +    while (*curr_in) {
    6.33 +        if (*curr_in == '\"' || *curr_in == '\\') {
    6.34 +            *curr_out++ = '\\';
    6.35 +        }
    6.36 +        *curr_out++ = *curr_in++;
    6.37 +    }
    6.38 +    *curr_out = '\0';
    6.39 +    return res;
    6.40 +}
    6.41  
    6.42  void write_inline_attachment(FILE* f_output, pst_item_attach* attach, char *boundary, pst_file* pst)
    6.43  {
    6.44 @@ -1182,8 +1209,14 @@
    6.45      if (attach->filename2.str) {
    6.46          // use the long filename, converted to proper encoding if needed.
    6.47          // it is already utf8
    6.48 +        char *escaped = quote_string(attach->filename2.str);
    6.49          pst_rfc2231(&attach->filename2);
    6.50 -        fprintf(f_output, "Content-Disposition: attachment; \n        filename*=%s\n\n", attach->filename2.str);
    6.51 +        fprintf(f_output, "Content-Disposition: attachment; \n        filename*=%s;\n", attach->filename2.str);
    6.52 +        // Also include the (escaped) utf8 filename in the 'filename' header directly - this is not strictly valid
    6.53 +        // (since this header should be ASCII) but is almost always handled correctly (and in fact this is the only
    6.54 +        // way to get MS Outlook to correctly read a UTF8 filename, AFAICT, which is why we're doing it).
    6.55 +        fprintf(f_output, "        filename=\"%s\"\n\n", escaped);
    6.56 +        free(escaped);
    6.57      }
    6.58      else if (attach->filename1.str) {
    6.59          // short filename never needs encoding