Click here to Skip to main content
15,884,099 members
Articles / Programming Languages / C#

DBX Parser

Rate me:
Please Sign up or sign in to vote.
4.52/5 (19 votes)
2 Jul 2009CPOL3 min read 70.8K   2.2K   38  
Read Outlook Express DBX file, extract mail to EML file
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Xnlab.Resio.MSOffice
{
    internal class DBXMail
    {
        internal List<KeyValuePair<int, int>> Segments = new List<KeyValuePair<int,int>>();
    }

    internal class DBX : IDisposable
    {
        private const int DBX_MAGIC = -32330289;//4262637007;
        private List<DBXMail> mails = null;
        private FileStream stream = null;
        private BinaryReader reader = null;

        internal int Parse(string FileName)
        {
            int count = -1;
            CleanUp();
            mails = new List<DBXMail>();
            if (File.Exists(FileName))
            {
                stream = File.OpenRead(FileName);
                if (stream.Length > 512)
                {
                    reader = new BinaryReader(stream);
                    byte[] root = reader.ReadBytes(512);
                    if (_dbx_int32(root, 0) == DBX_MAGIC)
                    {
                        if ((_dbx_int32(root, 228) == 0) == (_dbx_int32(root, 196) == 0))
                        {
                            if (_dbx_int32(root, 228) != 0 && _dbx_int32(root, 196) != 0)
                            {
                                _dbx_list_header(_dbx_int32(root, 228), 0, FileName);
                                count = mails.Count;
                            }
                        }
                    }
                }
            }
            return count;
        }

        private void _dbx_list_header(int Offset, int Parent, string FileName)
        {
            do
            {
                byte[] head;
                _dbx_read(Offset, out head, 24);
                if (_dbx_int32(head, 0) != Offset)
                    throw new IndexOutOfRangeException(string.Format("Self {1} != Offset {2}", _dbx_int32(head, 0), Offset));
                if (_dbx_int32(head, 12) != Parent)
                    throw new IndexOutOfRangeException(string.Format("Back {1} != Parent {2}", _dbx_int32(head, 12), Parent));
                if (_dbx_int32(head, 4) != 0)
                    throw new IndexOutOfRangeException(string.Format("Zero {1} != 0", _dbx_int32(head, 4)));

                Offset += 24;
                int n = (_dbx_int32(head, 16) >> 8) & 0xff;
                for (int i = 0; i < n; i++)
                {
                    byte[] list;
                    _dbx_read(Offset, out list, 12);
                    if (_dbx_int32(list, 0) != 0)
                        _dbx_mail_header(_dbx_int32(list, 0));
                    if (_dbx_int32(list, 4) != 0)
                        _dbx_list_header(_dbx_int32(list, 4), _dbx_int32(head, 0), FileName);
                    Offset += 12;
                }

                Parent = _dbx_int32(head, 0);
                Offset = _dbx_int32(head, 8);

            } while (Offset != 0);
        }

        private void _dbx_mail_header(int Offset)
        {
            int d_offset = 0;
            int s_offset = 0;
            int m_offset = 0;
            bool indirect = false;
            byte[] mail;
            _dbx_read(Offset, out mail, 12);
            if (_dbx_int32(mail, 0) != Offset)
                throw new IndexOutOfRangeException(string.Format("Self {1} != Offset {2}", _dbx_int32(mail, 0), Offset));
            Offset += 12;
            int n = (_dbx_int32(mail, 8) >> 16) & 0xff;
            if (n != 0)
            {
                for (int i = 0; i < n; i++)
                {
                    byte[] info;
                    _dbx_read(Offset, out info, 4);
                    switch (info[0])
                    {
                        case 0x0e:
                            s_offset = _dbx_int24(info, 1);
                            break;
                        case 0x12:
                            d_offset = _dbx_int24(info, 1);
                            break;
                        case 0x04:
                            m_offset = _dbx_int24(info, 1);
                            indirect = true;
                            break;
                        case 0x84:
                            m_offset = _dbx_int24(info, 1);
                            break;
                    }
                    Offset += 4;
                }
            }
            if (m_offset != 0)
            {
                if (indirect)
                {
                    byte[] offset;
                    _dbx_read(Offset + m_offset, out offset, 4);
                    m_offset = _dbx_int32(offset, 0);
                }
                _dbx_mail_message(m_offset);
            }
        }

        internal string Extract(int Message)
        {
            string result = string.Empty;
            if (Message >= 0 && Message < mails.Count)
            {
                using (MemoryStream writer = new MemoryStream())
                {
                    byte[] buffer;
                    for (int i = 0; i < mails[Message].Segments.Count; i++)
                    {
                        stream.Seek(mails[Message].Segments[i].Key, SeekOrigin.Begin);
                        buffer = reader.ReadBytes(mails[Message].Segments[i].Value);
                        writer.Write(buffer, 0, buffer.Length);
                    }
                    writer.Position = 0;
                    buffer = new byte[writer.Length];
                    writer.Read(buffer, 0, buffer.Length);
                    writer.Close();
                    result = Encoding.Default.GetString(buffer);
                }
            }
            return result;
        }

        internal void Extract(int Message, string FileName)
        {
            if (Message >= 0 && Message < mails.Count)
            {
                if (File.Exists(FileName))
                    File.Delete(FileName);
                using (FileStream writer = File.Create(FileName))
                {
                    for (int i = 0; i < mails[Message].Segments.Count; i++)
                    {
                        stream.Seek(mails[Message].Segments[i].Key, SeekOrigin.Begin);
                        byte[] buffer = reader.ReadBytes(mails[Message].Segments[i].Value);
                        writer.Write(buffer, 0, buffer.Length);
                    }
                }
            }
        }

        private void _dbx_mail_message(int Offset)
        {
            int resid = 0;
            DBXMail mail = new DBXMail();
            int start = Offset;
            int end = 0;
            do
            {
                byte[] text;
                _dbx_read(Offset, out text, 16);
                Offset += 16;
                resid = _dbx_int32(text, 8);
                do
                {
                    int n = Math.Min(resid, 4096);
                    byte[] buffer;
                    _dbx_read(Offset, out buffer, n); //mail content!
                    mail.Segments.Add(new KeyValuePair<int, int>(Offset, buffer.Length));
                    Offset += n;
                    resid -= n;

                } while (resid > 0);
                int nextPos = _dbx_int32(text, 12);
                if (nextPos > end && nextPos != 0)
                    end = Offset;
                Offset = nextPos;

            } while (Offset != 0);
            if (end > start)
                mails.Add(mail);
            else
                System.Diagnostics.Debug.WriteLine(string.Format("wrong {0} {1}", start, end));
        }

        private void _dbx_read(int Offset, out byte[] Buffer, int Size)
        {
            if (reader.BaseStream.Seek(Offset, SeekOrigin.Begin) != Offset || (Buffer = reader.ReadBytes(Size)).Length != Size)
                throw new ArgumentException();
        }

        private int _dbx_int24(byte[] Buffer, int Offset)
        {
            return Buffer[Offset + 2] * 65536 +
                Buffer[Offset + 1] * 256 +
                Buffer[Offset];
        }

        private int _dbx_int32(byte[] Buffer, int Offset)
        {
            //return Buffer[Offset + 3] * 16777216 +
                //Buffer[Offset + 2] * 65536    +
                //Buffer[Offset + 1] * 256      +
                //Buffer[Offset];
            return BitConverter.ToInt32(Buffer, Offset);
        }

        private void CleanUp()
        {
            if (reader != null)
                reader.Close();
            if (stream != null)
                stream.Close();
        }

        void IDisposable.Dispose()
        {
            CleanUp();
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Product Manager www.xnlab.com
Australia Australia
I was born in the south of China, started to write GWBASIC code since 1993 when I was 13 years old, with professional .net(c#) and vb, founder of www.xnlab.com

Now I am living in Sydney, Australia.

Comments and Discussions