Пример #1
0
        // Some sub-nodes use sub-sub-nodes to hold local data, so we need to give access to both levels
        public Node LookupSubNodeAndReadItsSubNodeBtree(FileStream fs, BTree <Node> subNodeTree, NID nid, out BTree <Node> childSubNodeTree)
        {
            childSubNodeTree = null;
            var rn = LookupSubNode(subNodeTree, nid);

            if (rn == null)
            {
                throw new XstException("Node block does not exist");
            }
            // If there is a sub-node, read its btree so that we can resolve references to nodes in it later
            if (rn.SubDataBid != 0)
            {
                childSubNodeTree = ReadSubNodeBtree(fs, rn.SubDataBid);
            }
            return(rn);
        }
Пример #2
0
        private void ReadMessageTables(FileStream fs, BTree <Node> subNodeTree, Message m, bool isAttached = false)
        {
            // Read the recipient table for the message
            var recipientsNid = new NID(EnidSpecial.NID_RECIPIENT_TABLE);

            if (ltp.IsTablePresent(subNodeTree, recipientsNid))
            {
                var rs = ltp.ReadTable <Recipient>(fs, subNodeTree, recipientsNid, pgMessageRecipient, null, (r, p) => r.Properties.Add(p));
                m.Recipients.Clear();
                foreach (var r in rs)
                {
                    // Sort the properties
                    List <Property> lp = new List <Property>(r.Properties);
                    lp.Sort((a, b) => a.Tag.CompareTo(b.Tag));
                    r.Properties.Clear();
                    foreach (var p in lp)
                    {
                        r.Properties.Add(p);
                    }

                    m.Recipients.Add(r);
                }
            }

            // Read any attachments
            var attachmentsNid = new NID(EnidSpecial.NID_ATTACHMENT_TABLE);

            if (m.HasAttachment)
            {
                if (!ltp.IsTablePresent(subNodeTree, attachmentsNid))
                {
                    throw new XstException("Could not find expected Attachment table");
                }

                // Read the attachment table, which is held in the subnode of the message
                var atts = ltp.ReadTable <Attachment>(fs, subNodeTree, attachmentsNid, pgAttachmentList, (a, id) => a.Nid = new NID(id)).ToList();
                foreach (var a in atts)
                {
                    a.XstFile = this; // For lazy reading of the complete properties
                    a.Parent  = m;

                    // If the long name wasn't in the attachment table, go look for it in the attachment properties
                    if (a.LongFileName == null)
                    {
                        ltp.ReadProperties <Attachment>(fs, subNodeTree, a.Nid, pgAttachmentName, a);
                    }

                    // Read properties relating to HTML images presented as attachments
                    ltp.ReadProperties <Attachment>(fs, subNodeTree, a.Nid, pgAttachedHtmlImages, a);

                    // If this is an embedded email, tell the attachment where to look for its properties
                    // This is needed because the email node is not in the main node tree
                    if (isAttached)
                    {
                        a.subNodeTreeProperties = subNodeTree;
                    }
                }

                m.SortAndSaveAttachments(atts);
            }
        }
Пример #3
0
        private dynamic ReadTableColumnValue(FileStream fs, BTree <Node> subNodeTree, List <HNDataBlock> blocks, RowDataBlock db, long rowOffset, TCOLDESC col)
        {
            dynamic val = null;
            HNID    hnid;

            switch (col.wPropType)
            {
            case EpropertyType.PtypInteger32:
                if (col.cbData != 4)
                {
                    throw new XstException("Unexpected property length");
                }
                val = Map.MapType <Int32>(db.Buffer, (int)rowOffset + col.ibData);
                break;

            case EpropertyType.PtypBoolean:
                val = (db.Buffer[rowOffset + col.ibData] == 0x01);
                break;

            case EpropertyType.PtypBinary:
                if (col.cbData != 4)
                {
                    throw new XstException("Unexpected property length");
                }
                hnid = Map.MapType <HNID>(db.Buffer, (int)rowOffset + col.ibData);

                if (!hnid.HasValue)
                {
                    val = "";
                }
                else
                {
                    var buf = GetBytesForHNID(fs, blocks, subNodeTree, hnid);

                    if (buf == null)
                    {
                        val = null;
                    }
                    else
                    {
                        val = buf;
                    }
                }
                break;

            case EpropertyType.PtypString:      // Unicode string
                if (col.cbData != 4)
                {
                    throw new XstException("Unexpected property length");
                }
                hnid = Map.MapType <HNID>(db.Buffer, (int)rowOffset + col.ibData);

                if (!hnid.HasValue)
                {
                    val = "";
                }
                else
                {
                    var buf = GetBytesForHNID(fs, blocks, subNodeTree, hnid);

                    if (buf == null)
                    {
                        val = "<Could not read string value>";
                    }
                    else
                    {
                        int skip = 0;
                        if (col.wPropId == EpropertyTag.PidTagSubjectW)
                        {
                            if (buf[0] == 0x01 && buf[1] == 0x00)      // Unicode 0x01
                            {
                                skip = 4;
                            }
                        }
                        val = Encoding.Unicode.GetString(buf, skip, buf.Length - skip);
                    }
                }
                if (val == "" && col.wPropId == EpropertyTag.PidTagSubjectW)
                {
                    val = "<No subject>";
                }
                break;

            case EpropertyType.PtypString8:     // Multibyte string in variable encoding
                if (col.cbData != 4)
                {
                    throw new XstException("Unexpected property length");
                }
                hnid = Map.MapType <HNID>(db.Buffer, (int)rowOffset + col.ibData);

                if (!hnid.HasValue)
                {
                    val = "";
                }
                else
                {
                    var buf = GetBytesForHNID(fs, blocks, subNodeTree, hnid);

                    if (buf == null)
                    {
                        val = "<Could not read string value>";
                    }
                    else
                    {
                        int skip = 0;

                        if (col.wPropId == EpropertyTag.PidTagSubjectW)
                        {
                            if (buf[0] == 0x01)      // ANSI 0x01
                            {
                                skip = 2;
                            }
                        }
                        val = Encoding.UTF8.GetString(buf, skip, buf.Length - skip);
                    }
                }
                if (val == "" && col.wPropId == EpropertyTag.PidTagSubjectW)
                {
                    val = "<No subject>";
                }
                break;

            case EpropertyType.PtypTime:
                // In a Table Context, time values are held in line
                if (col.cbData != 8)
                {
                    throw new XstException("Unexpected property length");
                }
                var fileTime = Map.MapType <Int64>(db.Buffer, (int)rowOffset + col.ibData);
                try
                {
                    val = DateTime.FromFileTimeUtc(fileTime);
                }
                catch (System.ArgumentOutOfRangeException)
                {
                    val = null;
                }
                break;

            default:
                val = String.Format("Unsupported property type {0}", col.wPropType);
                break;
            }

            return(val);
        }
Пример #4
0
        // Read the data rows of a table, populating the members of target type T as specified by the supplied property getters, and optionally getting all columns as properties
        private IEnumerable <T> ReadTableData <T>(FileStream fs, TCINFO t, List <HNDataBlock> blocks, List <RowDataBlock> dataBlocks, TCOLDESC[] cols, List <TCOLDESC> colsToGet,
                                                  BTree <Node> subNodeTree, TCROWIDUnicode[] indexes, PropertyGetters <T> g, Action <T, UInt32> idGetter, Action <T, Property> storeProp) where T : new()
        {
            int rgCEBSize = (int)Math.Ceiling((decimal)t.cCols / 8);
            int rowsPerBlock;

            if (ndb.IsUnicode4K)
            {
                rowsPerBlock = (ndb.BlockSize4K - Marshal.SizeOf(typeof(BLOCKTRAILERUnicode4K))) / t.rgibTCI_bm;
            }
            else if (ndb.IsUnicode)
            {
                rowsPerBlock = (ndb.BlockSize - Marshal.SizeOf(typeof(BLOCKTRAILERUnicode))) / t.rgibTCI_bm;
            }
            else
            {
                rowsPerBlock = (ndb.BlockSize - Marshal.SizeOf(typeof(BLOCKTRAILERANSI))) / t.rgibTCI_bm;
            }

            foreach (var index in indexes)
            {
                int blockNum = (int)(index.dwRowIndex / rowsPerBlock);
                if (blockNum >= dataBlocks.Count)
                {
                    throw new XstException("Data block number out of bounds");
                }

                var db = dataBlocks[blockNum];

                long rowOffset = db.Offset + (index.dwRowIndex % rowsPerBlock) * t.rgibTCI_bm;
                T    row       = new T();

                // Retrieve the node ID that accesses the message
                if (idGetter != null)
                {
                    idGetter(row, index.dwRowID);
                }

                if (rowOffset + t.rgibTCI_bm > db.Offset + db.Length)
                {
                    throw new XstException("Out of bounds reading table data");
                }

                // Read the column existence data
                var rgCEB = Map.MapArray <Byte>(db.Buffer, (int)(rowOffset + t.rgibTCI_1b), rgCEBSize);

                foreach (var col in colsToGet)
                {
                    // Check if the column exists
                    if ((rgCEB[col.iBit / 8] & (0x01 << (7 - (col.iBit % 8)))) == 0)
                    {
                        continue;
                    }

                    dynamic val = ReadTableColumnValue(fs, subNodeTree, blocks, db, rowOffset, col);

                    g[col.wPropId](row, val);
                }

                // If we were asked for all column values as properties, read them and store them
                if (storeProp != null)
                {
                    foreach (var col in cols)
                    {
                        // Check if the column exists
                        if ((rgCEB[col.iBit / 8] & (0x01 << (7 - (col.iBit % 8)))) == 0)
                        {
                            continue;
                        }

                        dynamic val = ReadTableColumnValue(fs, subNodeTree, blocks, db, rowOffset, col);

                        Property p = CreatePropertyObject(fs, col.wPropId, val);

                        storeProp(row, p);
                    }
                }

                yield return(row);
            }
            yield break; // No more entries
        }
Пример #5
0
        // Common implementation of table reading takes a data ID for a block in the main block tree
        private IEnumerable <T> ReadTableInternal <T>(FileStream fs, BTree <Node> subNodeTree, UInt64 dataBid, PropertyGetters <T> g,
                                                      Action <T, UInt32> idGetter, Action <T, Property> storeProp) where T : new()
        {
            var blocks = ReadHeapOnNode(fs, dataBid);
            var h      = blocks.First();

            if (h.bClientSig != EbType.bTypeTC)
            {
                throw new XstException("Was expecting a table");
            }

            // Read the table information
            var t = MapType <TCINFO>(blocks, h.hidUserRoot);

            // Read the column descriptions
            var cols = MapArray <TCOLDESC>(blocks, h.hidUserRoot, t.cCols, Marshal.SizeOf(typeof(TCINFO)));

            // Read the row index
            TCROWIDUnicode[] indexes;
            if (ndb.IsUnicode)
            {
                indexes = ReadBTHIndex <TCROWIDUnicode>(blocks, t.hidRowIndex).ToArray();
            }
            else
            {
                // For ANSI, convert the index entries to the slightly more capacious Unicode equivalents
                indexes = ReadBTHIndex <TCROWIDANSI>(blocks, t.hidRowIndex).Select(e => new TCROWIDUnicode {
                    dwRowID = e.dwRowID, dwRowIndex = e.dwRowIndex
                }).ToArray();
            }

            // Work out which of the columns are both present in the table and have getters defined
            var colsToGet = cols.Where(c => g.ContainsKey(c.wPropId)).ToList();

            // The data rows may be held in line, or in a sub node
            if (t.hnidRows.IsHID)
            {
                // Data is in line
                var buf        = GetBytesForHNID(fs, blocks, subNodeTree, t.hnidRows);
                var dataBlocks = new List <RowDataBlock>
                {
                    new RowDataBlock
                    {
                        Buffer = buf,
                        Offset = 0,
                        Length = buf.Length,
                    }
                };
                return(ReadTableData <T>(fs, t, blocks, dataBlocks, cols, colsToGet, subNodeTree, indexes, g, idGetter, storeProp));
            }
            else if (t.hnidRows.NID.HasValue)
            {
                // Don't use GetBytesForHNID in this case, as we need to handle multiple blocks
                var dataBlocks = ReadSubNodeRowDataBlocks(fs, subNodeTree, t.hnidRows.NID);
                return(ReadTableData <T>(fs, t, blocks, dataBlocks, cols, colsToGet, subNodeTree, indexes, g, idGetter, storeProp));
            }
            else
            {
                return(Enumerable.Empty <T>());
            }
        }
Пример #6
0
        private dynamic ReadPropertyValue(FileStream fs, BTree <Node> subNodeTree, List <HNDataBlock> blocks, PCBTH prop)
        {
            dynamic val = null;

            byte[] buf = null;

            switch (prop.wPropType)
            {
            case EpropertyType.PtypInteger16:
                val = (Int16)prop.dwValueHnid.dwValue;
                break;

            case EpropertyType.PtypInteger32:
                val = prop.dwValueHnid.dwValue;
                break;

            case EpropertyType.PtypInteger64:
                buf = GetBytesForHNID(fs, blocks, subNodeTree, prop.dwValueHnid);

                if (buf == null)
                {
                    val = "<Could not read Integer64 value>";
                }
                else
                {
                    val = Map.MapType <Int64>(buf);
                }
                break;

            case EpropertyType.PtypFloating64:
                buf = GetBytesForHNID(fs, blocks, subNodeTree, prop.dwValueHnid);

                if (buf == null)
                {
                    val = "<Could not read Floating64 value>";
                }
                else
                {
                    val = Map.MapType <Double>(buf);
                }
                break;

            case EpropertyType.PtypMultipleInteger32:
                buf = GetBytesForHNID(fs, blocks, subNodeTree, prop.dwValueHnid);

                if (buf == null)
                {
                    val = "<Could not read MultipleInteger32 value>";
                }
                else
                {
                    val = Map.MapArray <Int32>(buf, 0, buf.Length / sizeof(Int32));
                }
                break;

            case EpropertyType.PtypBoolean:
                val = (prop.dwValueHnid.dwValue == 0x01);
                break;

            case EpropertyType.PtypBinary:
                if (prop.dwValueHnid.HasValue && prop.dwValueHnid.hidType != EnidType.HID && prop.wPropId == EpropertyTag.PidTagAttachDataBinary)
                {
                    // Special case for out of line attachment contents: don't dereference to binary yet
                    val = prop.dwValueHnid.NID;
                }
                else
                {
                    buf = GetBytesForHNID(fs, blocks, subNodeTree, prop.dwValueHnid);

                    if (buf == null)
                    {
                        val = null;
                    }
                    else
                    {
                        val = buf;
                    }
                }
                break;

            case EpropertyType.PtypString:     // Unicode string
                if (!prop.dwValueHnid.HasValue)
                {
                    val = "";
                }
                else
                {
                    buf = GetBytesForHNID(fs, blocks, subNodeTree, prop.dwValueHnid);

                    if (buf == null)
                    {
                        val = "<Could not read string value>";
                    }
                    else
                    {
                        val = Encoding.Unicode.GetString(buf, 0, buf.Length);
                    }
                }
                break;

            case EpropertyType.PtypString8:      // Multipoint string in variable encoding
                if (!prop.dwValueHnid.HasValue)
                {
                    val = "";
                }
                else
                {
                    buf = GetBytesForHNID(fs, blocks, subNodeTree, prop.dwValueHnid);

                    if (buf == null)
                    {
                        val = "<Could not read string value>";
                    }
                    else
                    {
                        val = Encoding.UTF8.GetString(buf, 0, buf.Length);
                    }
                }
                break;

            case EpropertyType.PtypMultipleString:     // Unicode strings
                if (!prop.dwValueHnid.HasValue)
                {
                    val = "";
                }
                else
                {
                    buf = GetBytesForHNID(fs, blocks, subNodeTree, prop.dwValueHnid);

                    if (buf == null)
                    {
                        val = "<Could not read MultipleString value>";
                    }
                    else
                    {
                        var count   = Map.MapType <UInt32>(buf);
                        var offsets = Map.MapArray <UInt32>(buf, sizeof(UInt32), (int)count);
                        var ss      = new string[count];

                        // Offsets are relative to the start of the buffer
                        for (int i = 0; i < count; i++)
                        {
                            int len;
                            if (i < count - 1)
                            {
                                len = (int)(offsets[i + 1] - offsets[i]);
                            }
                            else
                            {
                                len = buf.Length - (int)offsets[i];
                            }

                            ss[i] = Encoding.Unicode.GetString(buf, (int)offsets[i], len);
                        }
                        val = ss;
                    }
                }
                break;

            case EpropertyType.PtypTime:
                // In a Property Context, time values are references to data
                buf = GetBytesForHNID(fs, blocks, subNodeTree, prop.dwValueHnid);

                if (buf != null)
                {
                    var fileTime = Map.MapType <Int64>(buf);
                    val = DateTime.FromFileTimeUtc(fileTime);
                }
                break;

            case EpropertyType.PtypGuid:
                buf = GetBytesForHNID(fs, blocks, subNodeTree, prop.dwValueHnid);

                if (buf == null)
                {
                    val = "<Could not read Guid value>";
                }
                else
                {
                    val = new Guid(buf);
                }
                break;

            case EpropertyType.PtypObject:
                buf = GetBytesForHNID(fs, blocks, subNodeTree, prop.dwValueHnid);

                if (buf == null)
                {
                    val = "<Could not read Object value>";
                }
                else
                {
                    val = Map.MapType <PtypObjectValue>(buf);
                }
                break;

            default:
                val = String.Format("Unsupported property type {0}", prop.wPropType);
                break;
            }

            return(val);
        }
Пример #7
0
 // Test for the  presence of an optional table in the supplied sub node tree
 public bool IsTablePresent(BTree <Node> subNodeTree, NID nid)
 {
     return(subNodeTree != null && NDB.LookupSubNode(subNodeTree, nid) != null);
 }