changeset 150:06aa84023b48

rename some structure fields to reflect our better understanding of the pst format
author Carl Byington <carl@five-ten-sg.com>
date Thu, 05 Mar 2009 08:23:32 -0800
parents f9773b6368e0
children cda7c812ec01
files ChangeLog regression/regression-tests.bash src/Makefile.am src/getidblock.c src/libpst.c src/libpst.h src/lspst.c src/readpst.c xml/libpst.in
diffstat 9 files changed, 497 insertions(+), 432 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sat Feb 28 11:55:48 2009 -0800
+++ b/ChangeLog	Thu Mar 05 08:23:32 2009 -0800
@@ -2,6 +2,8 @@
 ===============================
     * improve documentation of .pst format.
     * remove decrypt option from getidblock - we always decrypt.
+    * rename some structure fields to reflect our better understanding
+      of the pst format.
 
 LibPST 0.6.29 (2009-02-24)
 ===============================
--- a/regression/regression-tests.bash	Sat Feb 28 11:55:48 2009 -0800
+++ b/regression/regression-tests.bash	Thu Mar 05 08:23:32 2009 -0800
@@ -13,6 +13,21 @@
 }
 
 
+function doldif()
+{
+    n="$1"
+    fn="$2"
+    echo $fn
+    ba=$(basename "$fn" .pst)
+    rm -rf output$n
+    mkdir output$n
+    #$val ../src/pst2ldif -d dumper -b 'o=ams-cc.com, c=US' -c 'newPerson' -o $fn >$ba.ldif.err 2>&1
+    $val ../src/pst2ldif -d dumper -b 'o=ams-cc.com, c=US' -c 'inetOrgPerson' $fn >$ba.ldif.err 2>&1
+         ../src/readpstlog -f I dumper >$ba.ldif.log
+    rm -f dumper
+}
+
+
 function dopst()
 {
     n="$1"
@@ -26,10 +41,6 @@
     $val ../src/readpst -cv -o output$n -d dumper $fn >$ba.err 2>&1
          ../src/readpstlog -f I dumper >$ba.log
 
-    #$val ../src/pst2ldif -d dumper -b 'o=ams-cc.com, c=US' -c 'newPerson' -o $fn >$ba.ldif.err 2>&1
-    #$val ../src/pst2ldif -d dumper -b 'o=ams-cc.com, c=US' -c 'inetOrgPerson' $fn >$ba.ldif.err 2>&1
-    #     ../src/readpstlog -f I dumper >$ba.ldif.log
-
     #../src/getidblock -d -p $fn 0 >$ba.fulldump
     #../src/readpstlog -f I getidblock.log >$ba.fulldump.log
 
@@ -39,7 +50,7 @@
 
 
 val="valgrind --leak-check=full"
-#val=''
+val=''
 
 pushd ..
 make || exit
@@ -50,25 +61,46 @@
     dodii 2 sample_64.pst
     dodii 3 test.pst
     dodii 4 big_mail.pst
+elif [ "$1" == "ldif" ]; then
+    #doldif   1 ams.pst
+    #doldif   2 sample_64.pst
+    #doldif   3 test.pst
+    #doldif   4 big_mail.pst
+    #doldif   5 mbmg.archive.pst
+    #doldif   6 Single2003-read.pst
+    #doldif   7 Single2003-unread.pst
+    #doldif   8 ol2k3high.pst
+    #doldif   9 ol97high.pst
+    #doldif  10 returned_message.pst
+    #doldif  11 flow.pst
+    #doldif  12 test-html.pst
+    #doldif  13 test-text.pst
+    #doldif  14 joe.romanowski.pst
+    #doldif  15 hourig1.pst
+    #doldif  16 hourig2.pst
+    #doldif  17 hourig3.pst
+    #doldif  18 test-mac.pst
+    doldif  19 harris.pst
 else
-   dopst   1 ams.pst
-   dopst   2 sample_64.pst
-   dopst   3 test.pst
-   dopst   4 big_mail.pst
-   dopst   5 mbmg.archive.pst
-   dopst   6 Single2003-read.pst
-   dopst   7 Single2003-unread.pst
-   dopst   8 ol2k3high.pst
-   dopst   9 ol97high.pst
-   dopst  10 returned_message.pst
-   dopst  11 flow.pst
-   dopst  12 test-html.pst
-   dopst  13 test-text.pst
-   dopst  14 joe.romanowski.pst
-   dopst  15 hourig1.pst
-   #dopst  16 hourig2.pst
-   #dopst  17 hourig3.pst
-   dopst  18 test-mac.pst
+    dopst   1 ams.pst
+    dopst   2 sample_64.pst
+    dopst   3 test.pst
+    dopst   4 big_mail.pst
+    dopst   5 mbmg.archive.pst
+    dopst   6 Single2003-read.pst
+    dopst   7 Single2003-unread.pst
+    dopst   8 ol2k3high.pst
+    dopst   9 ol97high.pst
+    dopst  10 returned_message.pst
+    dopst  11 flow.pst
+    dopst  12 test-html.pst
+    dopst  13 test-text.pst
+    dopst  14 joe.romanowski.pst
+    dopst  15 hourig1.pst
+    #dopst  16 hourig2.pst
+    #dopst  17 hourig3.pst
+    dopst  18 test-mac.pst
+    dopst  19 harris.pst
 fi
 
 grep 'lost:' *err | grep -v 'lost: 0 '
--- a/src/Makefile.am	Sat Feb 28 11:55:48 2009 -0800
+++ b/src/Makefile.am	Thu Mar 05 08:23:32 2009 -0800
@@ -76,7 +76,7 @@
         libstrfunc.h\
         timeconv.h  \
         vbuf.h
-    libpst_la_LDFLAGS     = $(NO_UNDEFINED) -version-info 1:1:0
+    libpst_la_LDFLAGS     = $(NO_UNDEFINED) -version-info 1:2:0
 endif
 
 libpst_la_SOURCES     = $(common_source) $(common_header)
--- a/src/getidblock.c	Sat Feb 28 11:55:48 2009 -0800
+++ b/src/getidblock.c	Thu Mar 05 08:23:32 2009 -0800
@@ -45,7 +45,7 @@
         DEBUG_MAIN(("Parsing block id %#"PRIx64"\n", id));
         ptr = pstfile.d_head;
         while (ptr) {
-            if (ptr->list_index && ptr->list_index->id == id)
+            if (ptr->assoc_tree && ptr->assoc_tree->id == id)
                 break;
             if (ptr->desc && ptr->desc->id == id)
                 break;
@@ -53,8 +53,8 @@
         }
         if (!ptr) {
             ptr = (pst_desc_ll *) xmalloc(sizeof(pst_desc_ll));
+            memset(ptr, 0, sizeof(pst_desc_ll));
             ptr->desc = pst_getID(&pstfile, id);
-            ptr->list_index = NULL;
         }
         pst_item *item = pst_parse_item(&pstfile, ptr, NULL);
         if (item) pst_freeItem(item);
@@ -66,9 +66,9 @@
 void dump_desc(pst_desc_ll *ptr)
 {
     while (ptr) {
-        DEBUG_MAIN(("\n\n\nLooking at block desc id %#"PRIx64"\n", ptr->id));
+        DEBUG_MAIN(("\n\n\nLooking at block desc id %#"PRIx64"\n", ptr->d_id));
         if (ptr->desc       && ptr->desc->id)       dumper(ptr->desc->id);
-        if (ptr->list_index && ptr->list_index->id) dumper(ptr->list_index->id);
+        if (ptr->assoc_tree && ptr->assoc_tree->id) dumper(ptr->assoc_tree->id);
         if (ptr->child) dump_desc(ptr->child);
         ptr = ptr->next;
     }
--- a/src/libpst.c	Sat Feb 28 11:55:48 2009 -0800
+++ b/src/libpst.c	Thu Mar 05 08:23:32 2009 -0800
@@ -73,16 +73,16 @@
 typedef struct pst_id2_assoc32 {
     uint32_t id2;
     uint32_t id;
-    uint32_t table2;
+    uint32_t child_id;
 } pst_id2_assoc32;
 
 
 typedef struct pst_id2_assoc {
-    uint32_t id2;       // only 32 bit here?
+    uint32_t id2;       // only 32 bit here
     uint16_t unknown1;
     uint16_t unknown2;
     uint64_t id;
-    uint64_t table2;
+    uint64_t child_id;
 } pst_id2_assoc;
 
 
@@ -103,9 +103,10 @@
 } pst_block_hdr;
 
 
-// for "compressible" encryption, just a simple substitution cipher
-// this is an array of the un-encrypted values. the un-encrypted value is in the position
-// of the encrypted value. ie the encrypted value 0x13 represents 0x02
+/** for "compressible" encryption, just a simple substitution cipher,
+ *  plaintext = comp_enc[ciphertext];
+ *  for "strong" encryption, this is the first rotor of an Enigma 3 rotor cipher.
+ */
 static unsigned char comp_enc [] = {
     0x47, 0xf1, 0xb4, 0xe6, 0x0b, 0x6a, 0x72, 0x48, 0x85, 0x4e, 0x9e, 0xeb, 0xe2, 0xf8, 0x94, 0x53,
     0xe0, 0xbb, 0xa0, 0x02, 0xe8, 0x5a, 0x09, 0xab, 0xdb, 0xe3, 0xba, 0xc6, 0x7c, 0xc3, 0x10, 0xdd,
@@ -125,9 +126,8 @@
     0xd4, 0xe1, 0x11, 0xd0, 0x08, 0x8b, 0x2a, 0xf2, 0xed, 0x9a, 0x64, 0x3f, 0xc1, 0x6c, 0xf9, 0xec
 };
 
-// for "strong" encryption, we have the two additional tables,
-// which (with the previous table) are used as the keys in an
-// Enigma 3 rotor cipher
+/** for "strong" encryption, this is the second rotor of an Enigma 3 rotor cipher.
+ */
 static unsigned char comp_high1 [] = {
     0x41, 0x36, 0x13, 0x62, 0xa8, 0x21, 0x6e, 0xbb, 0xf4, 0x16, 0xcc, 0x04, 0x7f, 0x64, 0xe8, 0x5d,
     0x1e, 0xf2, 0xcb, 0x2a, 0x74, 0xc5, 0x5e, 0x35, 0xd2, 0x95, 0x47, 0x9e, 0x96, 0x2d, 0x9a, 0x88,
@@ -147,6 +147,8 @@
     0xa2, 0x01, 0xf7, 0x2e, 0xbc, 0x24, 0x68, 0x75, 0x0d, 0xfe, 0xba, 0x2f, 0xb5, 0xd0, 0xda, 0x3d
 };
 
+/** for "strong" encryption, this is the third rotor of an Enigma 3 rotor cipher.
+ */
 static unsigned char comp_high2 [] = {
     0x14, 0x53, 0x0f, 0x56, 0xb3, 0xc8, 0x7a, 0x9c, 0xeb, 0x65, 0x48, 0x17, 0x16, 0x15, 0x9f, 0x02,
     0xcc, 0x54, 0x7c, 0x83, 0x00, 0x0d, 0x0c, 0x0b, 0xa2, 0x62, 0xa8, 0x76, 0xdb, 0xd9, 0xed, 0xc7,
@@ -166,6 +168,7 @@
     0x61, 0xe0, 0xc6, 0xc1, 0x59, 0xab, 0xbb, 0x58, 0xde, 0x5f, 0xdf, 0x60, 0x79, 0x7e, 0xb2, 0x8a
 };
 
+
 int pst_open(pst_file *pf, char *name) {
     int32_t sig;
 
@@ -304,9 +307,9 @@
     // find any orphan children of this node, and collect them
     pst_desc_ll *n = pf->d_head;
     while (n) {
-        if (n->parent_id == node->id) {
+        if (n->parent_d_id == node->d_id) {
             // found a child of this node
-            DEBUG_INDEX(("Found orphan child %#"PRIx64" of parent %#"PRIx64"\n", n->id, node->id));
+            DEBUG_INDEX(("Found orphan child %#"PRIx64" of parent %#"PRIx64"\n", n->d_id, node->d_id));
             pst_desc_ll *nn = n->next;
             pst_desc_ll *pp = n->prev;
             node->no_child++;
@@ -322,18 +325,18 @@
     }
 
     // now hook this node into the global tree
-    if (node->parent_id == 0) {
+    if (node->parent_d_id == 0) {
         // add top level node to the descriptor tree
         //DEBUG_INDEX(("Null parent\n"));
         add_descriptor_to_list(node, &pf->d_head, &pf->d_tail);
     }
-    else if (node->parent_id == node->id) {
+    else if (node->parent_d_id == node->d_id) {
         // add top level node to the descriptor tree
         DEBUG_INDEX(("%#"PRIx64" is its own parent. What is this world coming to?\n"));
         add_descriptor_to_list(node, &pf->d_head, &pf->d_tail);
     } else {
         //DEBUG_INDEX(("Searching for parent %#"PRIx64" of %#"PRIx64"\n", node->parent_id, node->id));
-        pst_desc_ll *parent = pst_getDptr(pf, node->parent_id);
+        pst_desc_ll *parent = pst_getDptr(pf, node->parent_d_id);
         if (parent) {
             //DEBUG_INDEX(("Found parent %#"PRIx64"\n", node->parent_id));
             parent->no_child++;
@@ -341,7 +344,7 @@
             add_descriptor_to_list(node, &parent->child, &parent->child_tail);
         }
         else {
-            DEBUG_INDEX(("No parent %#"PRIx64", have an orphan child %#"PRIx64"\n", node->parent_id, node->id));
+            DEBUG_INDEX(("No parent %#"PRIx64", have an orphan child %#"PRIx64"\n", node->parent_d_id, node->d_id));
             add_descriptor_to_list(node, &pf->d_head, &pf->d_tail);
         }
     }
@@ -356,11 +359,11 @@
  * @param   head  pointer to the subtree to be copied
  * @return        pointer to the new copy of the subtree
  */
-static pst_index2_ll* deep_copy(pst_index2_ll *head);
-static pst_index2_ll* deep_copy(pst_index2_ll *head)
+static pst_id2_ll* deep_copy(pst_id2_ll *head);
+static pst_id2_ll* deep_copy(pst_id2_ll *head)
 {
     if (!head) return NULL;
-    pst_index2_ll* me = (pst_index2_ll*) xmalloc(sizeof(pst_index2_ll));
+    pst_id2_ll* me = (pst_id2_ll*) xmalloc(sizeof(pst_id2_ll));
     me->id2 = head->id2;
     me->id  = head->id;
     me->child = deep_copy(head->child);
@@ -389,11 +392,11 @@
     topnode = pst_getDptr(pf, (uint64_t)topid);
     if (!topnode) {
         // add dummy top record to pickup orphan children
-        topnode             = (pst_desc_ll*) xmalloc(sizeof(pst_desc_ll));
-        topnode->id         = topid;
-        topnode->parent_id  = 0;
-        topnode->list_index = NULL;
-        topnode->desc       = NULL;
+        topnode              = (pst_desc_ll*) xmalloc(sizeof(pst_desc_ll));
+        topnode->d_id        = topid;
+        topnode->parent_d_id = 0;
+        topnode->assoc_tree  = NULL;
+        topnode->desc        = NULL;
         record_descriptor(pf, topnode);   // add to the global tree
     }
     DEBUG_RET();
@@ -519,7 +522,7 @@
     // for PST files this will load up ID2 0x61 and check it's "list" attribute.
     pst_desc_ll *p;
     pst_num_array *na;
-    pst_index2_ll *id2_head = NULL;
+    pst_id2_ll *id2_head = NULL;
     char *buffer=NULL, *headerbuffer=NULL;
     size_t bsize=0, hsize=0, bptr=0;
     pst_x_attrib xattrib;
@@ -540,8 +543,8 @@
         return 0;
     }
 
-    if (p->list_index) {
-        id2_head = pst_build_id2(pf, p->list_index);
+    if (p->assoc_tree) {
+        id2_head = pst_build_id2(pf, p->assoc_tree);
         pst_printID2ptr(id2_head);
     } else {
         DEBUG_WARN(("Have not been able to fetch any id2 values for item 0x61. Brace yourself!\n"));
@@ -696,13 +699,13 @@
         memcpy(&d32, buf, sizeof(pst_desc32));
         LE32_CPU(d32.d_id);
         LE32_CPU(d32.desc_id);
-        LE32_CPU(d32.list_id);
-        LE32_CPU(d32.parent_id);
-        desc->d_id      = d32.d_id;
-        desc->desc_id   = d32.desc_id;
-        desc->list_id   = d32.list_id;
-        desc->parent_id = d32.parent_id;
-        desc->u1        = 0;
+        LE32_CPU(d32.tree_id);
+        LE32_CPU(d32.parent_d_id);
+        desc->d_id        = d32.d_id;
+        desc->desc_id     = d32.desc_id;
+        desc->tree_id     = d32.tree_id;
+        desc->parent_d_id = d32.parent_d_id;
+        desc->u1          = 0;
         r = sizeof(pst_desc32);
     }
     return r;
@@ -780,7 +783,7 @@
         memcpy(assoc, buf, sizeof(pst_id2_assoc));
         LE32_CPU(assoc->id2);
         LE64_CPU(assoc->id);
-        LE64_CPU(assoc->table2);
+        LE64_CPU(assoc->child_id);
         r = sizeof(pst_id2_assoc);
     } else {
         pst_id2_assoc32 assoc32;
@@ -790,9 +793,9 @@
         LE32_CPU(assoc32.id2);
         LE32_CPU(assoc32.id);
         LE32_CPU(assoc32.table2);
-        assoc->id2    = assoc32.id2;
-        assoc->id     = assoc32.id;
-        assoc->table2 = assoc32.table2;
+        assoc->id2      = assoc32.id2;
+        assoc->id       = assoc32.id;
+        assoc->child_id = assoc32.child_id;
         r = sizeof(pst_id2_assoc32);
     }
     return r;
@@ -821,6 +824,11 @@
 }
 
 
+/** Process the index1 b-tree from the pst file and create the
+ *  pf->i_head linked list from it. This tree holds the location
+ *  (offset and size) of lower level objects (0xbcec descriptor
+ *  blocks, etc) in the pst file.
+ */
 int pst_build_id_ptr(pst_file *pf, int64_t offset, int32_t depth, uint64_t linku1, uint64_t start_val, uint64_t end_val) {
     struct pst_table_ptr_structn table, table2;
     pst_index_ll *i_ptr=NULL;
@@ -934,6 +942,10 @@
 }
 
 
+/** Process the index2 b-tree from the pst file and create the
+ *  pf->d_head tree from it. This tree holds descriptions of the
+ *  higher level objects (email, contact, etc) in the pst file.
+ */
 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) {
     struct pst_table_ptr_structn table, table2;
     pst_descn desc_rec;
@@ -977,8 +989,8 @@
         }
         for (x=0; x<item_count; x++) {
             bptr += pst_decode_desc(pf, &desc_rec, bptr);
-            DEBUG_INDEX(("[%i] Item(%#x) = [d_id = %#"PRIx64", desc_id = %#"PRIx64", list_id = %#"PRIx64", parent_id = %#x]\n",
-                        depth, x, desc_rec.d_id, desc_rec.desc_id, desc_rec.list_id, desc_rec.parent_id));
+            DEBUG_INDEX(("[%i] Item(%#x) = [d_id = %#"PRIx64", desc_id = %#"PRIx64", tree_id = %#"PRIx64", parent_d_id = %#x]\n",
+                        depth, x, desc_rec.d_id, desc_rec.desc_id, desc_rec.tree_id, desc_rec.parent_d_id));
             if ((desc_rec.d_id >= end_val) || (desc_rec.d_id < old)) {
                 DEBUG_WARN(("This item isn't right. Must be corruption, or I got it wrong!\n"));
                 DEBUG_HEXDUMPC(buf, DESC_BLOCK_SIZE, 16);
@@ -995,12 +1007,12 @@
                     return -1;
                 }
             }
-            DEBUG_INDEX(("New Record %#"PRIx64" with parent %#x\n", desc_rec.d_id, desc_rec.parent_id));
+            DEBUG_INDEX(("New Record %#"PRIx64" with parent %#x\n", desc_rec.d_id, desc_rec.parent_d_id));
             {
                 pst_desc_ll *d_ptr = (pst_desc_ll*) xmalloc(sizeof(pst_desc_ll));
-                d_ptr->id          = desc_rec.d_id;
-                d_ptr->parent_id   = desc_rec.parent_id;
-                d_ptr->list_index  = pst_getID(pf, desc_rec.list_id);
+                d_ptr->d_id        = desc_rec.d_id;
+                d_ptr->parent_d_id = desc_rec.parent_d_id;
+                d_ptr->assoc_tree  = pst_getID(pf, desc_rec.tree_id);
                 d_ptr->desc        = pst_getID(pf, desc_rec.desc_id);
                 record_descriptor(pf, d_ptr);   // add to the global tree
             }
@@ -1049,10 +1061,12 @@
 }
 
 
-pst_item* pst_parse_item(pst_file *pf, pst_desc_ll *d_ptr, pst_index2_ll *m_head) {
+/** Process a high level object from the pst file.
+ */
+pst_item* pst_parse_item(pst_file *pf, pst_desc_ll *d_ptr, pst_id2_ll *m_head) {
     pst_num_array * list;
-    pst_index2_ll *id2_head = m_head;
-    pst_index2_ll *id2_ptr  = NULL;
+    pst_id2_ll *id2_head = m_head;
+    pst_id2_ll *id2_ptr  = NULL;
     pst_item *item = NULL;
     pst_item_attach *attach = NULL;
     int32_t x;
@@ -1069,12 +1083,12 @@
         return NULL;
     }
 
-    if (d_ptr->list_index) {
+    if (d_ptr->assoc_tree) {
         if (m_head) {
             DEBUG_WARN(("supplied master head, but have a list that is building a new id2_head"));
             m_head = NULL;
         }
-        id2_head = pst_build_id2(pf, d_ptr->list_index);
+        id2_head = pst_build_id2(pf, d_ptr->assoc_tree);
     }
     pst_printID2ptr(id2_head);
 
@@ -1239,7 +1253,12 @@
 }
 
 
-pst_num_array * pst_parse_block(pst_file *pf, uint64_t block_id, pst_index2_ll *i2_head, pst_num_array *na_head) {
+/** Process a low level descriptor block (0x0101, 0xbcec, 0x7cec) into a
+ *  list of objects, each of which contains a list of MAPI elements.
+ *
+ *  @return list of objects
+ */
+pst_num_array * pst_parse_block(pst_file *pf, uint64_t block_id, pst_id2_ll *i2_head, pst_num_array *na_head) {
     char  *buf       = NULL;
     size_t read_size = 0;
     pst_subblocks  subblocks;
@@ -1738,16 +1757,16 @@
 
 
 /**
- * process the list of items produced from parse_block()
+ * process the list of objects produced from parse_block()
  *
- * @param list  pointer to the linked list of things from parse_block()
- * @param item  pointer to the item to be updated from the list.
+ * @param list  pointer to the list of objects from parse_block()
+ * @param item  pointer to the high level item to be updated from the list.
  *              this item may be an email, contact or other sort of item.
- *              the type of this item is generally set by the things
+ *              the type of this item is generally set by the MAPI elements
  *              from the list.
- * @param attach pointer to the linked list of attachment records. If
+ * @param attach pointer to the list of attachment records. If
  *               this is non-null, the length of the this attachment list
- *               must be at least as large as the length of the list.
+ *               must be at least as large as the length of the objects list.
  *
  * @return 0 for ok, -1 for error.
  */
@@ -1788,9 +1807,8 @@
                                     *pp = '\0';
                                     char *set = strdup(p);
                                     *pp = '"';
-                                    MALLOC_EMAIL(item);
-                                    if (item->email->body_charset) free(item->email->body_charset);
-                                    item->email->body_charset = set;
+                                    if (item->body_charset) free(item->body_charset);
+                                    item->body_charset = set;
                                     DEBUG_EMAIL(("body charset %s from content-type extra field\n", set));
                                 }
                             }
@@ -3111,17 +3129,15 @@
                     DEBUG_EMAIL(("%s\n", item->contact->other_po_box));
                     break;
                 case 0x3FDE: // PR_INTERNET_CPID
-                    MALLOC_EMAIL(item);
-                    memcpy(&(item->email->internet_cpid), list->items[x]->data, sizeof(item->email->internet_cpid));
-                    LE32_CPU(item->email->internet_cpid);
-                    t = item->email->internet_cpid;
+                    memcpy(&(item->internet_cpid), list->items[x]->data, sizeof(item->internet_cpid));
+                    LE32_CPU(item->internet_cpid);
+                    t = item->internet_cpid;
                     DEBUG_EMAIL(("Internet code page %i\n", (int)t));
                     break;
                 case 0x3FFD: // PR_MESSAGE_CODEPAGE
-                    MALLOC_EMAIL(item);
-                    memcpy(&(item->email->message_codepage), list->items[x]->data, sizeof(item->email->message_codepage));
-                    LE32_CPU(item->email->message_codepage);
-                    t = item->email->message_codepage;
+                    memcpy(&(item->message_codepage), list->items[x]->data, sizeof(item->message_codepage));
+                    LE32_CPU(item->message_codepage);
+                    t = item->message_codepage;
                     DEBUG_EMAIL(("Message code page %i\n", (int)t));
                     break;
                 case 0x65E3: // Entry ID?
@@ -3663,8 +3679,8 @@
 }
 
 
-void pst_free_id2(pst_index2_ll * head) {
-    pst_index2_ll *t;
+void pst_free_id2(pst_id2_ll * head) {
+    pst_id2_ll *t;
     DEBUG_ENT("pst_free_id2");
     while (head) {
         if (head->child) pst_free_id2(head->child);
@@ -3725,15 +3741,15 @@
 }
 
 
-pst_index2_ll * pst_build_id2(pst_file *pf, pst_index_ll* list) {
+pst_id2_ll * pst_build_id2(pst_file *pf, pst_index_ll* list) {
     pst_block_header block_head;
-    pst_index2_ll *head = NULL, *tail = NULL;
+    pst_id2_ll *head = NULL, *tail = NULL;
     uint16_t x = 0;
     char *b_ptr = NULL;
     char *buf = NULL;
     pst_id2_assoc id2_rec;
     pst_index_ll *i_ptr = NULL;
-    pst_index2_ll *i2_ptr = NULL;
+    pst_id2_ll *i2_ptr = NULL;
     DEBUG_ENT("pst_build_id2");
 
     if (pst_read_block_size(pf, list->offset, list->size, &buf) < list->size) {
@@ -3762,14 +3778,14 @@
     b_ptr = buf + ((pf->do_read64) ? 0x08 : 0x04);
     while (x < block_head.count) {
         b_ptr += pst_decode_assoc(pf, &id2_rec, b_ptr);
-        DEBUG_INDEX(("id2 = %#x, id = %#"PRIx64", table2 = %#"PRIx64"\n", id2_rec.id2, id2_rec.id, id2_rec.table2));
+        DEBUG_INDEX(("id2 = %#x, id = %#"PRIx64", child id = %#"PRIx64"\n", id2_rec.id2, id2_rec.id, id2_rec.child_id));
         if ((i_ptr = pst_getID(pf, id2_rec.id)) == NULL) {
             DEBUG_WARN(("%#"PRIx64" - Not Found\n", id2_rec.id));
         } else {
             DEBUG_INDEX(("%#"PRIx64" - Offset %#"PRIx64", u1 %#"PRIx64", Size %"PRIi64"(%#"PRIx64")\n",
                          i_ptr->id, i_ptr->offset, i_ptr->u1, i_ptr->size, i_ptr->size));
             // add it to the tree
-            i2_ptr = (pst_index2_ll*) xmalloc(sizeof(pst_index2_ll));
+            i2_ptr = (pst_id2_ll*) xmalloc(sizeof(pst_id2_ll));
             i2_ptr->id2   = id2_rec.id2;
             i2_ptr->id    = i_ptr;
             i2_ptr->child = NULL;
@@ -3777,12 +3793,11 @@
             if (!head) head = i2_ptr;
             if (tail)  tail->next = i2_ptr;
             tail = i2_ptr;
-            if (id2_rec.table2) {
-                if ((i_ptr = pst_getID(pf, id2_rec.table2)) == NULL) {
-                    DEBUG_WARN(("Table2 [%#"PRIi64"] not found\n", id2_rec.table2));
+            if (id2_rec.child_id) {
+                if ((i_ptr = pst_getID(pf, id2_rec.child_id)) == NULL) {
+                    DEBUG_WARN(("child id [%#"PRIi64"] not found\n", id2_rec.child_id));
                 }
                 else {
-                    DEBUG_INDEX(("Going deeper for table2 [%#"PRIi64"]\n", id2_rec.table2));
                     i2_ptr->child = pst_build_id2(pf, i_ptr);
                 }
             }
@@ -3818,7 +3833,6 @@
         if (item->email) {
             SAFE_FREE(item->email->arrival_date);
             SAFE_FREE(item->email->body);
-            SAFE_FREE(item->email->body_charset);
             SAFE_FREE(item->email->cc_address);
             SAFE_FREE(item->email->bcc_address);
             SAFE_FREE(item->email->common_name);
@@ -4003,6 +4017,7 @@
             free(item->appointment);
         }
         SAFE_FREE(item->ascii_type);
+        SAFE_FREE(item->body_charset);
         SAFE_FREE(item->comment);
         SAFE_FREE(item->create_date);
         SAFE_FREE(item->file_as);
@@ -4021,7 +4036,7 @@
   * Otherwise, the high order 16 bits of offset is the index into the subblocks, and
   * the (low order 16 bits of offset)>>4 is an index into the table of offsets in the subblock.
 */
-int pst_getBlockOffsetPointer(pst_file *pf, pst_index2_ll *i2_head, pst_subblocks *subblocks, uint32_t offset, pst_block_offset_pointer *p) {
+int pst_getBlockOffsetPointer(pst_file *pf, pst_id2_ll *i2_head, pst_subblocks *subblocks, uint32_t offset, pst_block_offset_pointer *p) {
     size_t size;
     pst_block_offset block_offset;
     DEBUG_ENT("pst_getBlockOffsetPointer");
@@ -4116,14 +4131,14 @@
 }
 
 
-pst_index2_ll *pst_getID2(pst_index2_ll *head, uint64_t id2) {
+pst_id2_ll *pst_getID2(pst_id2_ll *head, uint64_t id2) {
     DEBUG_ENT("pst_getID2");
     DEBUG_INDEX(("looking for id2 = %#"PRIx64"\n", id2));
-    pst_index2_ll *ptr = head;
+    pst_id2_ll *ptr = head;
     while (ptr) {
         if (ptr->id2 == id2) break;
         if (ptr->child) {
-            pst_index2_ll *rc = pst_getID2(ptr->child, id2);
+            pst_id2_ll *rc = pst_getID2(ptr->child, id2);
             if (rc) {
                 DEBUG_RET();
                 return rc;
@@ -4146,15 +4161,15 @@
  * find the id in the descriptor tree rooted at pf->d_head
  *
  * @param pf    global pst file pointer
- * @param id    the id we are looking for
+ * @param d_id  the id we are looking for
  *
  * @return pointer to the pst_desc_ll node in the descriptor tree
 */
-pst_desc_ll* pst_getDptr(pst_file *pf, uint64_t id) {
+pst_desc_ll* pst_getDptr(pst_file *pf, uint64_t d_id) {
     pst_desc_ll *ptr = pf->d_head;
     DEBUG_ENT("pst_getDptr");
-    while (ptr && (ptr->id != id)) {
-        //DEBUG_INDEX(("Looking for %#"PRIx64" at node %#"PRIx64" with parent %#"PRIx64"\n", id, ptr->id, ptr->parent_id));
+    while (ptr && (ptr->d_id != d_id)) {
+        //DEBUG_INDEX(("Looking for %#"PRIx64" at node %#"PRIx64" with parent %#"PRIx64"\n", id, ptr->d_id, ptr->parent_d_id));
         if (ptr->child) {
             ptr = ptr->child;
             continue;
@@ -4172,9 +4187,9 @@
 void pst_printDptr(pst_file *pf, pst_desc_ll *ptr) {
     DEBUG_ENT("pst_printDptr");
     while (ptr) {
-        DEBUG_INDEX(("%#"PRIx64" [%i] desc=%#"PRIx64", list=%#"PRIx64"\n", ptr->id, ptr->no_child,
-                    (ptr->desc ? ptr->desc->id : (uint64_t)0),
-                    (ptr->list_index ? ptr->list_index->id : (uint64_t)0)));
+        DEBUG_INDEX(("%#"PRIx64" [%i] desc=%#"PRIx64", assoc tree=%#"PRIx64"\n", ptr->d_id, ptr->no_child,
+                    (ptr->desc       ? ptr->desc->id       : (uint64_t)0),
+                    (ptr->assoc_tree ? ptr->assoc_tree->id : (uint64_t)0)));
         if (ptr->child) {
             pst_printDptr(pf, ptr->child);
         }
@@ -4195,7 +4210,7 @@
 }
 
 
-void pst_printID2ptr(pst_index2_ll *ptr) {
+void pst_printID2ptr(pst_id2_ll *ptr) {
     DEBUG_ENT("pst_printID2ptr");
     while (ptr) {
         DEBUG_INDEX(("%#"PRIx64" id=%#"PRIx64"\n", ptr->id2, (ptr->id ? ptr->id->id : (uint64_t)0)));
@@ -4417,9 +4432,9 @@
 
 
 #define PST_PTR_BLOCK_SIZE 0x120
-size_t pst_ff_getID2block(pst_file *pf, uint64_t id2, pst_index2_ll *id2_head, char** buf) {
+size_t pst_ff_getID2block(pst_file *pf, uint64_t id2, pst_id2_ll *id2_head, char** buf) {
     size_t ret;
-    pst_index2_ll* ptr;
+    pst_id2_ll* ptr;
     pst_holder h = {buf, NULL, 0};
     DEBUG_ENT("pst_ff_getID2block");
     ptr = pst_getID2(id2_head, id2);
--- a/src/libpst.h	Sat Feb 28 11:55:48 2009 -0800
+++ b/src/libpst.h	Thu Mar 05 08:23:32 2009 -0800
@@ -95,17 +95,17 @@
 typedef struct pst_desc_struct32 {
     uint32_t d_id;
     uint32_t desc_id;
-    uint32_t list_id;
-    uint32_t parent_id;
+    uint32_t tree_id;
+    uint32_t parent_d_id;
 } pst_desc32;
 
 
 typedef struct pst_desc_structn {
     uint64_t d_id;
     uint64_t desc_id;
-    uint64_t list_id;
-    uint32_t parent_id;  // not 64 bit ??
-    uint32_t u1;         // padding
+    uint64_t tree_id;
+    uint32_t parent_d_id;   // not 64 bit ??
+    uint32_t u1;            // padding
 } pst_descn;
 
 
@@ -144,19 +144,19 @@
 } pst_index_ll;
 
 
-typedef struct pst_index2_tree {
+typedef struct pst_id2_tree {
     uint64_t id2;
     pst_index_ll *id;
-    struct pst_index2_tree *child;
-    struct pst_index2_tree *next;
-} pst_index2_ll;
+    struct pst_id2_tree *child;
+    struct pst_id2_tree *next;
+} pst_id2_ll;
 
 
 typedef struct pst_desc_tree {
-    uint64_t              id;
-    uint64_t              parent_id;
-    pst_index_ll         *list_index;
+    uint64_t              d_id;
+    uint64_t              parent_d_id;
     pst_index_ll         *desc;
+    pst_index_ll         *assoc_tree;
     int32_t               no_child;
     struct pst_desc_tree *prev;
     struct pst_desc_tree *next;
@@ -178,7 +178,6 @@
     int         autoforward;            // 1 = true, 0 = not set, -1 = false
     char       *body;
     int32_t     body_was_unicode;       // 1 = true, 0 = false
-    char       *body_charset;           // null if not specified
     char       *cc_address;
     char       *bcc_address;
     char       *common_name;
@@ -233,8 +232,6 @@
     char       *sender2_access;
     char       *sender2_address;
     int32_t     sensitivity;
-    int32_t     internet_cpid;
-    int32_t     message_codepage;
     FILETIME   *sent_date;
     pst_entryid            *sentmail_folder;
     char                   *sentto_address;
@@ -383,7 +380,7 @@
     size_t   size;
     uint64_t id2_val;
     uint64_t id_val;            // calculated from id2_val during creation of record
-    pst_index2_ll *id2_head;    // deep copy from child
+    pst_id2_ll *id2_head;    // deep copy from child
     int32_t  method;
     int32_t  position;
     int32_t  sequence;
@@ -425,26 +422,29 @@
 
 
 typedef struct pst_item {
-    struct pst_item_email         *email;           // data reffering to email
-    struct pst_item_folder        *folder;          // data reffering to folder
-    struct pst_item_contact       *contact;         // data reffering to contact
+    struct pst_item_email         *email;           // data referring to email
+    struct pst_item_folder        *folder;          // data referring to folder
+    struct pst_item_contact       *contact;         // data referring to contact
     struct pst_item_attach        *attach;          // linked list of attachments
     struct pst_item_message_store *message_store;   // data referring to the message store
     struct pst_item_extra_field   *extra_fields;    // linked list of extra headers and such
-    struct pst_item_journal       *journal;         // data reffering to a journal entry
-    struct pst_item_appointment   *appointment;     // data reffering to a calendar entry
-    int       type;
-    char     *ascii_type;
-    char     *file_as;
-    char     *comment;
-    int32_t   message_size;
-    char     *outlook_version;
-    char     *record_key; // probably 16 bytes long.
-    size_t    record_key_size;
-    int       response_requested;     // 1 = true, 0 = false
-    FILETIME *create_date;
-    FILETIME *modify_date;
-    int       private_member;         // 1 = true, 0 = false
+    struct pst_item_journal       *journal;         // data referring to a journal entry
+    struct pst_item_appointment   *appointment;     // data referring to a calendar entry
+    int         type;
+    char       *ascii_type;
+    char       *file_as;
+    char       *comment;
+    char       *body_charset;           // null if not specified
+    int32_t     internet_cpid;
+    int32_t     message_codepage;
+    int32_t     message_size;
+    char       *outlook_version;
+    char       *record_key; // probably 16 bytes long.
+    size_t      record_key_size;
+    int         response_requested;     // 1 = true, 0 = false
+    FILETIME   *create_date;
+    FILETIME   *modify_date;
+    int         private_member;         // 1 = true, 0 = false
 } pst_item;
 
 
@@ -549,20 +549,20 @@
 int            pst_build_id_ptr(pst_file *pf, int64_t offset, int32_t depth, uint64_t linku1, uint64_t start_val, uint64_t end_val);
 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);
 pst_item*      pst_getItem(pst_file *pf, pst_desc_ll *d_ptr);
-pst_item*      pst_parse_item (pst_file *pf, pst_desc_ll *d_ptr, pst_index2_ll *m_head);
-pst_num_array* pst_parse_block(pst_file *pf, uint64_t block_id, pst_index2_ll *i2_head, pst_num_array *na_head);
+pst_item*      pst_parse_item (pst_file *pf, pst_desc_ll *d_ptr, pst_id2_ll *m_head);
+pst_num_array* pst_parse_block(pst_file *pf, uint64_t block_id, pst_id2_ll *i2_head, pst_num_array *na_head);
 int            pst_process(pst_num_array *list, pst_item *item, pst_item_attach *attach);
 void           pst_free_list(pst_num_array *list);
 void           pst_freeItem(pst_item *item);
-void           pst_free_id2(pst_index2_ll * head);
+void           pst_free_id2(pst_id2_ll * head);
 void           pst_free_id (pst_index_ll *head);
 void           pst_free_desc (pst_desc_ll *head);
 void           pst_free_xattrib(pst_x_attrib_ll *x);
-int            pst_getBlockOffsetPointer(pst_file *pf, pst_index2_ll *i2_head, pst_subblocks *subblocks, uint32_t offset, pst_block_offset_pointer *p);
+int            pst_getBlockOffsetPointer(pst_file *pf, pst_id2_ll *i2_head, pst_subblocks *subblocks, uint32_t offset, pst_block_offset_pointer *p);
 int            pst_getBlockOffset(char *buf, size_t read_size, uint32_t i_offset, uint32_t offset, pst_block_offset *p);
-pst_index2_ll* pst_build_id2(pst_file *pf, pst_index_ll* list);
+pst_id2_ll* pst_build_id2(pst_file *pf, pst_index_ll* list);
 pst_index_ll*  pst_getID(pst_file* pf, uint64_t id);
-pst_index2_ll* pst_getID2(pst_index2_ll * ptr, uint64_t id);
+pst_id2_ll* pst_getID2(pst_id2_ll * ptr, uint64_t id);
 pst_desc_ll*   pst_getDptr(pst_file *pf, uint64_t id);
 size_t         pst_read_block_size(pst_file *pf, int64_t offset, size_t size, char **buf);
 int            pst_decrypt(uint64_t id, char *buf, size_t size, unsigned char type);
@@ -571,7 +571,7 @@
 size_t         pst_getAtPos(pst_file *pf, int64_t pos, void* buf, size_t size);
 size_t         pst_ff_getIDblock_dec(pst_file *pf, uint64_t id, char **b);
 size_t         pst_ff_getIDblock(pst_file *pf, uint64_t id, char** b);
-size_t         pst_ff_getID2block(pst_file *pf, uint64_t id2, pst_index2_ll *id2_head, char** buf);
+size_t         pst_ff_getID2block(pst_file *pf, uint64_t id2, pst_id2_ll *id2_head, char** buf);
 size_t         pst_ff_getID2data(pst_file *pf, pst_index_ll *ptr, pst_holder *h);
 size_t         pst_ff_compile_ID(pst_file *pf, uint64_t id, pst_holder *h, size_t size);
 
@@ -587,7 +587,7 @@
 
 void           pst_printDptr(pst_file *pf, pst_desc_ll *ptr);
 void           pst_printIDptr(pst_file* pf);
-void           pst_printID2ptr(pst_index2_ll *ptr);
+void           pst_printID2ptr(pst_id2_ll *ptr);
 
 
 // switch from maximal packing back to default packing
--- a/src/lspst.c	Sat Feb 28 11:55:48 2009 -0800
+++ b/src/lspst.c	Thu Mar 05 08:23:32 2009 -0800
@@ -53,13 +53,12 @@
     create_enter_dir(&ff, outeritem);
 
     while (d_ptr) {
-        DEBUG_MAIN(("main: New item record, d_ptr = %p.\n", d_ptr));
         if (!d_ptr->desc) {
-            DEBUG_WARN(("main: ERROR ?? item's desc record is NULL\n"));
+            DEBUG_WARN(("main: ERROR item's desc record is NULL\n"));
             ff.skip_count++;
         }
         else {
-            DEBUG_MAIN(("main: Desc Email ID %x [d_ptr->id = %x]\n", d_ptr->desc->id, d_ptr->id));
+            DEBUG_MAIN(("main: Desc Email ID %"PRIx64" [d_ptr->d_id = %"PRIx64"]\n", d_ptr->desc->id, d_ptr->d_id));
 
             item = pst_parse_item(&pstfile, d_ptr, NULL);
             DEBUG_MAIN(("main: About to process item @ %p.\n", item));
--- a/src/readpst.c	Sat Feb 28 11:55:48 2009 -0800
+++ b/src/readpst.c	Thu Mar 05 08:23:32 2009 -0800
@@ -132,11 +132,11 @@
     while (d_ptr) {
         DEBUG_MAIN(("main: New item record\n"));
         if (!d_ptr->desc) {
-            DEBUG_WARN(("main: ERROR ?? item's desc record is NULL\n"));
+            DEBUG_WARN(("main: ERROR item's desc record is NULL\n"));
             ff.skip_count++;
         }
         else {
-            DEBUG_MAIN(("main: Desc Email ID %#"PRIx64" [d_ptr->id = %#"PRIx64"]\n", d_ptr->desc->id, d_ptr->id));
+            DEBUG_MAIN(("main: Desc Email ID %#"PRIx64" [d_ptr->d_id = %#"PRIx64"]\n", d_ptr->desc->id, d_ptr->d_id));
 
             item = pst_parse_item(&pstfile, d_ptr, NULL);
             DEBUG_MAIN(("main: About to process item\n"));
@@ -748,16 +748,16 @@
     ptr = pst_getID(pf, attach->id_val);
 
     pst_desc_ll d_ptr;
-    d_ptr.id         = ptr->id;
-    d_ptr.parent_id  = 0;
-    d_ptr.list_index = NULL;
-    d_ptr.desc       = ptr;
-    d_ptr.no_child   = 0;
-    d_ptr.prev       = NULL;
-    d_ptr.next       = NULL;
-    d_ptr.parent     = NULL;
-    d_ptr.child      = NULL;
-    d_ptr.child_tail = NULL;
+    d_ptr.d_id        = 0;
+    d_ptr.parent_d_id = 0;
+    d_ptr.assoc_tree  = NULL;
+    d_ptr.desc        = ptr;
+    d_ptr.no_child    = 0;
+    d_ptr.prev        = NULL;
+    d_ptr.next        = NULL;
+    d_ptr.parent      = NULL;
+    d_ptr.child       = NULL;
+    d_ptr.child_tail  = NULL;
 
     pst_item *item = pst_parse_item(pf, &d_ptr, attach->id2_head);
     write_normal_email(f_output, "", item, MODE_NORMAL, 0, pf, 0, extra_mime_headers);
@@ -1082,9 +1082,9 @@
 
     // setup default body character set and report type
     snprintf(body_charset, sizeof(body_charset), "%s",
-        (item->email->body_charset)     ? item->email->body_charset :
-        (item->email->message_codepage) ? codepage(item->email->message_codepage) :
-        (item->email->internet_cpid)    ? codepage(item->email->internet_cpid) :
+        (item->body_charset)     ? item->body_charset :
+        (item->message_codepage) ? codepage(item->message_codepage) :
+        (item->internet_cpid)    ? codepage(item->internet_cpid) :
         "utf-8");
     body_report[0] = '\0';
 
--- a/xml/libpst.in	Sat Feb 28 11:55:48 2009 -0800
+++ b/xml/libpst.in	Thu Mar 05 08:23:32 2009 -0800
@@ -1479,10 +1479,12 @@
                 that pointed to this node.
             </para>
             <para>
-                Each item in this node is a tuple of (D_ID, DESC-I_ID, TREE-I_ID, PARENT-D_ID).
-                The DESC-I_ID points to the main data for this item via the index1 tree.
-                The TREE-I_ID is zero or points to Associated Tree Item 0x0002 via the index1 tree.
-                The PARENT-D_ID points to the parent of this item in this index2 tree.
+                Each item in this node is a tuple of (D_ID, DESC-I_ID, TREE-I_ID,
+                PARENT-D_ID) The DESC-I_ID points to the main data for this item
+                (Associated Descriptor Items 0x7cec, 0xbcec, or 0x0101) via the index1
+                tree.  The TREE-I_ID is zero or points to an Associated Tree Item
+                0x0002 via the index1 tree.  The PARENT-D_ID points to the parent of
+                this item in this index2 tree.
             </para>
         </refsect1>
 
@@ -1539,10 +1541,12 @@
                 that pointed to this node.
             </para>
             <para>
-                Each item in this node is a tuple of (D_ID, DESC-I_ID, TREE-I_ID, PARENT-D_ID)
-                The DESC-I_ID points to the main data for this item via the index1 tree.
-                The TREE-I_ID is zero or points to Associated Tree Item 0x0002 via the index1 tree.
-                The PARENT-D_ID points to the parent of this item in this index2 tree.
+                Each item in this node is a tuple of (D_ID, DESC-I_ID, TREE-I_ID,
+                PARENT-D_ID) The DESC-I_ID points to the main data for this item
+                (Associated Descriptor Items 0x7cec, 0xbcec, or 0x0101) via the index1
+                tree.  The TREE-I_ID is zero or points to an Associated Tree Item
+                0x0002 via the index1 tree.  The PARENT-D_ID points to the parent of
+                this item in this index2 tree.
             </para>
         </refsect1>
 
@@ -1607,6 +1611,7 @@
                 0x00e638, 0, 0) 0x00e638 is the I_ID of the associated descriptor, and we
                 can lookup that I_ID value in the index1 b-tree to find the (offset,size)
                 of the data in the .pst file.
+                This descriptor is eventually decoded to a list of MAPI elements.
             </para>
             <literallayout class="monospaced"><![CDATA[
 0000  3c 01 ec bc  20 00 00 00  00 00 00 00  b5 02 06 00
@@ -1678,13 +1683,15 @@
 0004  descoffset      [4 bytes] 0x0040     index reference
 ]]></literallayout>
             <para>
-                Note the descoffset of 0x0040, which again is an index reference. In this
-                case, it is an internal pointer reference, which needs to be right shifted by 4 bits
-                to become 0x0004, which is then a byte offset to be added to the above
-                indexOffset plus two (to skip the count), so it points to the (0x14, 0x7c)
-                pair. The datasize (6) plus the b5 code (02) gives the size of the entries,
-                in this case 8 bytes. We now have the offset 0x14 of the descriptor array,
-                composed of 8 byte entries. Each descriptor entry has the following format:
+                Note the descoffset of 0x0040, which again is an index reference. In
+                this case, it is an internal pointer reference, which needs to be
+                right shifted by 4 bits to become 0x0004, which is then a byte offset
+                to be added to the above indexOffset plus two (to skip the count), so
+                it points to the (0x14, 0x7c) pair. The datasize (6) plus the b5 code
+                (02) gives the size of the entries, in this case 8 bytes. We now have
+                the offset 0x14 of the descriptor array, composed of 8 byte entries
+                that describe MAPI elements. Each descriptor entry has the following
+                format:
             </para>
             <literallayout class="monospaced"><![CDATA[
 0000  itemType        [2 bytes]
@@ -1727,244 +1734,253 @@
                 are implemented in the code yet.
             </para>
             <literallayout class="monospaced"><![CDATA[
-0002  Alternate recipient allowed
-0003  Extended Attributes Table
-0017  Importance Level
-001a  IPM Context, message class
-0023  Global delivery report requested
-0026  Priority
-0029  Read Receipt
-002b  Reassignment Prohibited
-002e  Original Sensitivity
-0036  Sensitivity
-0037  Email Subject
-0039  Client submit time / date sent
-003b  Outlook Address of Sender
-003f  Outlook structure describing the recipient
-0040  Name of the Outlook recipient structure
-0041  Outlook structure describing the sender
-0042  Name of the Outlook sender structure
-0043  Another structure describing the recipient
-0044  Name of the second recipient structure
-004f  Reply-To Outlook Structure
-0050  Name of the Reply-To structure
-0051  Outlook Name of recipient
-0052  Second Outlook name of recipient
-0057  My address in TO field
-0058  My address in CC field
-0059  Message addressed to me
-0063  Response requested
-0064  Sender's Address access method (SMTP, EX)
-0065  Sender's Address
-0070  Conversation topic, processed subject (with Fwd:, Re, ... removed)
-0071  Conversation index
-0072  Original display BCC
-0073  Original display CC
-0074  Original display TO
-0075  Recipient Address Access Method (SMTP, EX)
-0076  Recipient's Address
-0077  Second Recipient Access Method (SMTP, EX)
-0078  Second Recipient Address
-007d  Email Header. This is the header that was attached to the email
-0c17  Reply Requested
-0c19  Second sender structure
-0c1a  Name of second sender structure
-0c1d  Second outlook name of sender
-0c1e  Second sender access method (SMTP, EX)
-0c1f  Second Sender Address
-0e01  Delete after submit
-0e02  BCC Addresses
-0e03  CC Addresses
-0e04  SentTo Address
-0e06  Date.
-0e07  Flag bits
-          0x01 - Read
-          0x02 - Unmodified
-          0x04 - Submit
-          0x08 - Unsent
-          0x10 - Has Attachments
-          0x20 - From Me
-          0x40 - Associated
-          0x80 - Resend
-          0x100 - RN Pending
-          0x200 - NRN Pending
-0e08  Message Size
-0e0a  Sentmail EntryID
-0e1f  Compressed RTF in Sync
-0e20  Attachment Size
-0ff9  binary record header
-1000  Plain Text Email Body. Does not exist if the email doesn't have a plain text version
-1006  RTF Sync Body CRC
-1007  RTF Sync Body character count
-1008  RTF Sync body tag
-1009  RTF Compressed body
-1010  RTF whitespace prefix count
-1011  RTF whitespace tailing count
-1013  HTML Email Body. Does not exist if the email doesn't have an HTML version
-1035  Message ID
-1042  In-Reply-To or Parent's Message ID
-1046  Return Path
-3001  Folder Name? I have seen this value used for the contacts record aswell
-3002  Address Type
-3003  Contact Address
-3004  Comment
-3007  Date item creation
-3008  Date item modification
-300b  binary record header
-35df  Valid Folder Mask
-35e0  binary record contains a reference to "Top of Personal Folder" item
-35e2  binary record contains a reference to default outbox item
-35e3  binary record contains a reference to "Deleted Items" item
-35e4  binary record contains a reference to sent items folder item
-35e5  binary record contains a reference to user views folder item
-35e6  binary record contains a reference to common views folder item
-35e7  binary record contains a reference to "Search Root" item
-3602  the number of emails stored in a folder
-3603  the number of unread emails in a folder
-360a  Has Subfolders
-3613  the folder content description
-3617  Associate Content count
-3701  Binary Data attachment
-3704  Attachment Filename
-3705  Attachement method
-3707  Attachment Filename long
-370b  Attachment Position
-370e  Attachment mime encoding
-3710  Attachment mime Sequence
-3a00  Contact's Account name
-3a01  Contact Alternate Recipient
-3a02  Callback telephone number
-3a03  Message Conversion Prohibited
-3a05  Contacts Suffix
-3a06  Contacts First Name
-3a07  Contacts Government ID Number
-3a08  Business Telephone Number
-3a09  Home Telephone Number
-3a0a  Contacts Initials
-3a0b  Keyword
-3a0c  Contact's Language
-3a0d  Contact's Location
-3a0e  Mail Permission
-3a0f  MHS Common Name
-3a10  Organizational ID #
-3a11  Contacts Surname
-3a12  original entry id
-3a13  original display name
-3a14  original search key
-3a15  Default Postal Address
-3a16  Company Name
-3a17  Job Title
-3a18  Department Name
-3a19  Office Location
-3a1a  Primary Telephone
-3a1b  Business Phone Number 2
-3a1c  Mobile Phone Number
-3a1d  Radio Phone Number
-3a1e  Car Phone Number
-3a1f  Other Phone Number
-3a20  Transmittable Display Name
-3a21  Pager Phone Number
-3a22  user certificate
-3a23  Primary Fax Number
-3a24  Business Fax Number
-3a25  Home Fax Number
-3a26  Business Address Country
-3a27  Business Address City
-3a28  Business Address State
-3a29  Business Address Street
-3a2a  Business Postal Code
-3a2b  Business PO Box
-3a2c  Telex Number
-3a2d  ISDN Number
-3a2e  Assistant Phone Number
-3a2f  Home Phone 2
-3a30  Assistant's Name
-3a40  Can receive Rich Text
-3a41  Wedding Anniversary
-3a42  Birthday
-3a43  Hobbies
-3a44  Middle Name
-3a45  Display Name Prefix (Title)
-3a46  Profession
-3a47  Preferred By Name
-3a48  Spouse's Name
-3a49  Computer Network Name
-3a4a  Customer ID
-3a4b  TTY/TDD Phone
-3a4c  Ftp Site
-3a4d  Gender
-3a4e  Manager's Name
-3a4f  Nickname
-3a50  Personal Home Page
-3a51  Business Home Page
-3a57  Company Main Phone
-3a58  childrens names
-3a59  Home Address City
-3a5a  Home Address Country
-3a5b  Home Address Postal Code
-3a5c  Home Address State or Province
-3a5d  Home Address Street
-3a5e  Home Address Post Office Box
-3a5f  Other Address City
-3a60  Other Address Country
-3a61  Other Address Postal Code
-3a62  Other Address State
-3a63  Other Address Street
-3a64  Other Address Post Office box
-65e3  Entry ID
-67f2  Attachment ID2 value
-67ff  Password checksum
-6f02  Secure HTML Body
-6f04  Secure Text Body
-7c07  Top of folders RecID
-8005  Contact Fullname
-801a  Home Address
-801b  Business Address
-801c  Other Address
-8045  Work Address Street
-8046  Work Address City
-8047  Work Address State
-8048  Work Address Postal Code
-8049  Work Address Country
-804a  Work Address Post Office Box
-8082  Email Address 1 Transport
-8083  Email Address 1 Address
-8084  Email Address 1 Description
-8085  Email Address 1 Record
-8092  Email Address 2 Transport
-8093  Email Address 2 Address
-8094  Email Address 2 Description
-8095  Email Address 2 Record
-80a2  Email Address 3 Transport
-80a3  Email Address 3 Address
-80a4  Email Address 3 Description
-80a5  Email Address 3 Record
-80d8  Internet Free/Busy
-8205  Appointment shows as
-8208  Appointment Location
-820d  Appointment start
-820e  Appointment end
-8214  Label for appointment
-8215  All day appointment flag
-8231  Recurrence type
-8232  Recurrence description
-8234  TimeZone of times
-8235  Recurrence Start Time
-8236  Recurrence End Time
-8501  Reminder minutes before appointment start
-8503  Reminder alarm
-8516  Common Time Start
-8517  Common Time End
-851f  Play reminder sound filename
-8530  Followup String
-8534  Mileage
-8535  Billing Information
-8554  Outlook Version
-8560  Appointment Reminder Time
-8700  Journal Entry Type
-8706  Start Timestamp
-8708  End Timestamp
-8712  Journal Entry Type - duplicate?
+0x0002  Alternate recipient allowed
+0x0003  Extended Attributes Table
+0x0017  Importance Level
+0x001a  IPM Context, message class
+0x0023  Global delivery report requested
+0x0026  Priority
+0x0029  Read Receipt
+0x002b  Reassignment Prohibited
+0x002e  Original Sensitivity
+0x0032  Report time
+0x0036  Sensitivity
+0x0037  Email Subject
+0x0039  Client submit time / date sent
+0x003b  Outlook Address of Sender
+0x003f  Outlook structure describing the recipient
+0x0040  Name of the Outlook recipient structure
+0x0041  Outlook structure describing the sender
+0x0042  Name of the Outlook sender structure
+0x0043  Another structure describing the recipient
+0x0044  Name of the second recipient structure
+0x004f  Reply-To Outlook Structure
+0x0050  Name of the Reply-To structure
+0x0051  Outlook Name of recipient
+0x0052  Second Outlook name of recipient
+0x0057  My address in TO field
+0x0058  My address in CC field
+0x0059  Message addressed to me
+0x0063  Response requested
+0x0064  Sender's Address access method (SMTP, EX)
+0x0065  Sender's Address
+0x0070  Conversation topic, processed subject (with Fwd:, Re, ... removed)
+0x0071  Conversation index
+0x0072  Original display BCC
+0x0073  Original display CC
+0x0074  Original display TO
+0x0075  Recipient Address Access Method (SMTP, EX)
+0x0076  Recipient's Address
+0x0077  Second Recipient Access Method (SMTP, EX)
+0x0078  Second Recipient Address
+0x007d  Email Header. This is the header that was attached to the email
+0x0c04  NDR Reason code
+0x0c05  NDR Diag code
+0x0c06  Non-receipt notification requested
+0x0c17  Reply Requested
+0x0c19  Second sender structure
+0x0c1a  Name of second sender structure
+0x0c1b  Supplementary info
+0x0c1d  Second outlook name of sender
+0x0c1e  Second sender access method (SMTP, EX)
+0x0c1f  Second Sender Address
+0x0c20  NDR status code
+0x0e01  Delete after submit
+0x0e02  BCC Addresses
+0x0e03  CC Addresses
+0x0e04  SentTo Address
+0x0e06  Date.
+0x0e07  Flag bits
+            0x01 - Read
+            0x02 - Unmodified
+            0x04 - Submit
+            0x08 - Unsent
+            0x10 - Has Attachments
+            0x20 - From Me
+            0x40 - Associated
+            0x80 - Resend
+            0x100 - RN Pending
+            0x200 - NRN Pending
+0x0e08  Message Size
+0x0e0a  Sentmail EntryID
+0x0e1f  Compressed RTF in Sync
+0x0e20  Attachment Size
+0x0ff9  binary record header
+0x1000  Plain Text Email Body. Does not exist if the email doesn't have a plain text version
+0x1001  Report Text
+0x1006  RTF Sync Body CRC
+0x1007  RTF Sync Body character count
+0x1008  RTF Sync body tag
+0x1009  RTF Compressed body
+0x1010  RTF whitespace prefix count
+0x1011  RTF whitespace tailing count
+0x1013  HTML Email Body. Does not exist if the email doesn't have an HTML version
+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
+0x3002  Address Type
+0x3003  Contact Address
+0x3004  Comment
+0x3007  Date item creation
+0x3008  Date item modification
+0x300b  binary record header
+0x35df  Valid Folder Mask
+0x35e0  binary record contains a reference to "Top of Personal Folder" item
+0x35e2  binary record contains a reference to default outbox item
+0x35e3  binary record contains a reference to "Deleted Items" item
+0x35e4  binary record contains a reference to sent items folder item
+0x35e5  binary record contains a reference to user views folder item
+0x35e6  binary record contains a reference to common views folder item
+0x35e7  binary record contains a reference to "Search Root" item
+0x3602  the number of emails stored in a folder
+0x3603  the number of unread emails in a folder
+0x360a  Has Subfolders
+0x3613  the folder content description
+0x3617  Associate Content count
+0x3701  Binary Data attachment
+0x3704  Attachment Filename
+0x3705  Attachement method
+0x3707  Attachment Filename long
+0x370b  Attachment Position
+0x370e  Attachment mime encoding
+0x3710  Attachment mime Sequence
+0x3a00  Contact's Account name
+0x3a01  Contact Alternate Recipient
+0x3a02  Callback telephone number
+0x3a03  Message Conversion Prohibited
+0x3a05  Contacts Suffix
+0x3a06  Contacts First Name
+0x3a07  Contacts Government ID Number
+0x3a08  Business Telephone Number
+0x3a09  Home Telephone Number
+0x3a0a  Contacts Initials
+0x3a0b  Keyword
+0x3a0c  Contact's Language
+0x3a0d  Contact's Location
+0x3a0e  Mail Permission
+0x3a0f  MHS Common Name
+0x3a10  Organizational ID #
+0x3a11  Contacts Surname
+0x3a12  original entry id
+0x3a13  original display name
+0x3a14  original search key
+0x3a15  Default Postal Address
+0x3a16  Company Name
+0x3a17  Job Title
+0x3a18  Department Name
+0x3a19  Office Location
+0x3a1a  Primary Telephone
+0x3a1b  Business Phone Number 2
+0x3a1c  Mobile Phone Number
+0x3a1d  Radio Phone Number
+0x3a1e  Car Phone Number
+0x3a1f  Other Phone Number
+0x3a20  Transmittable Display Name
+0x3a21  Pager Phone Number
+0x3a22  user certificate
+0x3a23  Primary Fax Number
+0x3a24  Business Fax Number
+0x3a25  Home Fax Number
+0x3a26  Business Address Country
+0x3a27  Business Address City
+0x3a28  Business Address State
+0x3a29  Business Address Street
+0x3a2a  Business Postal Code
+0x3a2b  Business PO Box
+0x3a2c  Telex Number
+0x3a2d  ISDN Number
+0x3a2e  Assistant Phone Number
+0x3a2f  Home Phone 2
+0x3a30  Assistant's Name
+0x3a40  Can receive Rich Text
+0x3a41  Wedding Anniversary
+0x3a42  Birthday
+0x3a43  Hobbies
+0x3a44  Middle Name
+0x3a45  Display Name Prefix (Title)
+0x3a46  Profession
+0x3a47  Preferred By Name
+0x3a48  Spouse's Name
+0x3a49  Computer Network Name
+0x3a4a  Customer ID
+0x3a4b  TTY/TDD Phone
+0x3a4c  Ftp Site
+0x3a4d  Gender
+0x3a4e  Manager's Name
+0x3a4f  Nickname
+0x3a50  Personal Home Page
+0x3a51  Business Home Page
+0x3a57  Company Main Phone
+0x3a58  childrens names
+0x3a59  Home Address City
+0x3a5a  Home Address Country
+0x3a5b  Home Address Postal Code
+0x3a5c  Home Address State or Province
+0x3a5d  Home Address Street
+0x3a5e  Home Address Post Office Box
+0x3a5f  Other Address City
+0x3a60  Other Address Country
+0x3a61  Other Address Postal Code
+0x3a62  Other Address State
+0x3a63  Other Address Street
+0x3a64  Other Address Post Office box
+0x3fde  Internet code page
+0x3ffd  Message code page
+0x65e3  Entry ID
+0x67f2  Attachment ID2 value
+0x67ff  Password checksum
+0x6f02  Secure HTML Body
+0x6f04  Secure Text Body
+0x7c07  Top of folders RecID
+0x8005  Contact Fullname
+0x801a  Home Address
+0x801b  Business Address
+0x801c  Other Address
+0x8045  Work Address Street
+0x8046  Work Address City
+0x8047  Work Address State
+0x8048  Work Address Postal Code
+0x8049  Work Address Country
+0x804a  Work Address Post Office Box
+0x8082  Email Address 1 Transport
+0x8083  Email Address 1 Address
+0x8084  Email Address 1 Description
+0x8085  Email Address 1 Record
+0x8092  Email Address 2 Transport
+0x8093  Email Address 2 Address
+0x8094  Email Address 2 Description
+0x8095  Email Address 2 Record
+0x80a2  Email Address 3 Transport
+0x80a3  Email Address 3 Address
+0x80a4  Email Address 3 Description
+0x80a5  Email Address 3 Record
+0x80d8  Internet Free/Busy
+0x8205  Appointment shows as
+0x8208  Appointment Location
+0x820d  Appointment start
+0x820e  Appointment end
+0x8214  Label for appointment
+0x8215  All day appointment flag
+0x8231  Recurrence type
+0x8232  Recurrence description
+0x8234  TimeZone of times
+0x8235  Recurrence Start Time
+0x8236  Recurrence End Time
+0x8501  Reminder minutes before appointment start
+0x8503  Reminder alarm
+0x8516  Common Time Start
+0x8517  Common Time End
+0x851f  Play reminder sound filename
+0x8530  Followup String
+0x8534  Mileage
+0x8535  Billing Information
+0x8554  Outlook Version
+0x8560  Appointment Reminder Time
+0x8700  Journal Entry Type
+0x8706  Start Timestamp
+0x8708  End Timestamp
+0x8712  Journal Entry Type - duplicate?
 ]]></literallayout>
         </refsect1>
 
@@ -1972,6 +1988,7 @@
             <title>Associated Descriptor Item 0x7cec</title>
             <para>
                 This style of descriptor block is similar to the 0xbcec format.
+                This descriptor is also eventually decoded to a list of MAPI elements.
             </para>
             <literallayout class="monospaced"><![CDATA[
 0000  7a 01 ec 7c  40 00 00 00  00 00 00 00  b5 04 02 00