Как читать файлы Outlook Express

КАК ЧИТАТЬ ФАЙЛЫ OUTLOOK EXPRESS (.DBX) 
(формат файла *.DBX)
== Структура файла ==
Сообщения хранятся в таблицах, расположенных друг за другом, по порядку. Каждая таблица состоит из двух частей - заголовка и записи. Смещение первой таблицы от начала файла располагается со смещением $30 от начала файла и представляет собой знаковое 32-х битное число (тип Integer или LongInt). Общее же количество записей (т.е. этих самых таблиц) расположено со смещением $C4 от начала файла, и представляет собой число типа Integer (LongInt).
Заголовок таблицы содержит количество записей, собственное смещение, а так же смещения предыдущей и следующей таблиц (все смещения, разумеется, от начала файла). Для чтения заголовка таблицы я использую такую структуру:
Toe5_IndexHeader = record
 FilePos: LongInt; { Это смещение структуры, используется для контроля }
 Unknown1: LongInt; { ??? }
 PrevIndex: LongInt; { Смещение предыдущей таблицы }
 NextIndex: LongInt; { Смещение следующей таблицы }
 Count: LongInt; { Количество записей, реальное количество находится так: count shr 8 }
 Unknown2: LongInt; { ??? }
end;
Чтобы получить реальное количество записей, значение поля Count надо конвертировать.
Каждая запись содержит смещение (всегда от начала файла) заголовка письма (Message Header) и позицию другой индексной таблицы (эта таблица используется, чтобы обеспечить поддержку наследования (или связывания) сообщения, то есть любой заголовок сообщения, на который ссылается эта таблица - ребенок текущего сообщения). Вот структура записи:
Toe5_IndexItem = record
 HeaderPos: LongInt; { Смещение (от начала файла) заголовка письма }
 ChildIndex: LongInt; { Смещение (от начала файла) дочерней индексной таблицы }
 Unknown: longint;
end;
На мой взгляд, лучшим методом чтения таблиц является рекурсивная функция (см. пример).
Заголовок сообщения содержит некоторую информацию, которую Outlook Express использует, чтобы избежать доступа к сообщению, пока в этом нет особой необходимости. Эта структура разделена на три части: заголовок структуры, таблицу параметров типа DWORD и блок данных.
* Заголовок структуры - это то, что вам необходимо прочитать в первую очередь, так как он определяет размер оставшихся двух частей. В её состав входят размер всей структуры (все 3 части), суммарный размер таблицы параметров, блока данных и количество параметров в таблице (я называю эти параметры флагами).
THeaderData = record 
 Position: LongInt; { смещение данной структуры от BOF, используется только для контроля }
 DataLength: LongInt; { размер таблицы параметров и блока данных }
 HeaderLength: WORD; { размер всех трех частей }
 FlagCount: WORD; { количество элементов в таблице }
end;
Чтобы получить размер таблицы флагов, можно написать SizeOfTable := FlagCount * SizeOf(DWord), a размер блока данных используйте DataLength - SizeOfTable.
* Каждый элемент таблицы параметров должен быть декодирован, для того, чтобы получить его идентификатор и значение: идентификатор находят как element and $FF, а значение флага - element shr 8.
* Блок данных содержит такую информацию, как дата получения, отправки, тема, адресат, отправитель, аккаунт и т.д. Для её чтения используется таблица флагов (параметров).
Вот пример таблицы параметров и соответствующего блока данных:
 Flags = 16
 
 80: 00000074
 81: 00000081
 02: 00000000
 84: 0002ECA0
 05: 00000008
 06: 00000025
 07: 0000002D
 08: 0000006E
 0D: 0000008B
 0E: 000000A5
 90: 00000003
 91: 0000376F
 12: 000000D4
 13: 000000DC
 14: 00000102
 1C: 0000012A
 
 Data Block:
 
 00 72 F3 E4 58 22 C0 01 41 63 74 69 76 65 57 65
 r o a X " A _ A c t i v e W e
 
 62 20 44 65 76 65 6C 6F 70 65 72 20 65 58 54 52
 b D e v e l o p e r e X T R
 
 41 20 23 38 00 60 E2 F2 9C 90 35 C0 01 3C 4F 46
 A # 8 ` a o ? ? 5 A _ < O F
 
 45 31 44 36 35 46 38 37 2E 32 39 39 31 37 34 31
 E 1 D 6 5 F 8 7 . 2 9 9 1 7 4 1
 
 42 2D 4F 4E 38 35 32 35 36 39 35 46 2E 30 30 35
 B - O N 8 5 2 5 6 9 5 F . 0 0 5
 
 43 33 46 41 36 40 70 69 6E 6E 61 63 6C 65 70 75
 C 3 F A 6 @ p i n n a c l e p u
 
 62 6C 69 73 68 69 6E 67 2E 63 6F 6D 3E 00 41 63
 b l i s h i n g . c o m > A c
 
 74 69 76 65 57 65 62 20 44 65 76 65 6C 6F 70 65
 t i v e W e b D e v e l o p e
 
 72 20 65 58 54 52 41 20 23 38 00 41 63 74 69 76
 r e X T R A # 8 A c t i v
 
 65 57 65 62 20 44 65 76 65 6C 6F 70 65 72 20 65
 e W e b D e v e l o p e r e
 
 58 54 52 41 00 61 63 74 69 76 65 77 65 62 64 65
 X T R A a c t i v e w e b d e
 
 76 65 6C 6F 70 65 72 65 78 74 72 61 40 70 69 6E
 v e l o p e r e x t r a @ p i n
 
 6E 61 63 6C 65 70 75 62 6C 69 73 68 69 6E 67 2E
 n a c l e p u b l i s h i n g .
 
 63 6F 6D 00 00 23 5E 0F 8B 22 C0 01 41 63 74 69
 c o m # ^ _ ‹ " A _ A c t i
 
 76 65 57 65 62 20 44 65 76 65 6C 6F 70 65 72 20
 v e W e b D e v e l o p e r
 
 65 58 54 52 41 20 53 75 62 73 63 72 69 62 65 72
 e X T R A S u b s c r i b e r
 
 20 00 3C 41 63 74 69 76 65 57 65 62 20 44 65 76
 < A c t i v e W e b D e v
 
 65 6C 6F 70 65 72 20 65 58 54 52 41 20 53 75 62
 e l o p e r e X T R A S u b
 
 73 63 72 69 62 65 72 20 3E 00 88 00 00 00 01 00
 s c r i b e r > ? _
 
 02 4E 00 00 00 00 F9 37 00 00 02 4E 00 00 01 00
 _ N u 7 _ N _
 
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 
 
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 
 
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 
 
 00 00 00 00 00 00 40 05 00 00 F9 37 00 00 00 00
 @ _ u 7
 
 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00
 _
 
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 
 
 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 
 
 00 00 00 00 00 00 6D 70
 m p
Теперь давайте разберёмся, что означает каждый флаг.
Для файла Folders.dbx:
 $2: смещение от начала блока данных, по которому расположено название папки (Nullterminated string)
 $3: смещение от начала блока данных, по которому расположено имя файла, представляющего эту папку (это тоже Nullterminated string)
 $6: не важный флаг. Если папка его имеет, то значит она - специальная папка, у неё нет собственного файла, и она используется лишь для организации;
 $80: идентификатор папки;
 $81: идентификатор родительской папки (флаг $80 родительской папки).
Для других файлов:
 $1: Смещение от начала блока данных, по которому расположен статус сообщения.
 $2: Смещение от начала блока данных, по которому расположена дата отправки (дата представлена в TFileTime)
 $4: Иногда типа DWord не хватает, чтобы представить и идентификатор флага, и смещения тела сообщения от начала файла, тогда смещение, по которому расположено тело, размещается в блоке данных. Значение этого флага - смещение (от начала блока данных) по которому находится это значение.
 $7: Смещение от начала блока данных, по которому расположен идентификатор письма MessageID (nullterminated string)
 $8: Смещение от начала блока данных, по которому расположена тема сообщения (nullterminated string)
 $9: Смещение от начала блока данных, по которому расположен параметр “From Reply” сообщения (nullterminated string)
 $A: Смещение от начала блока данных, по которому расположены ссылки (References)сообщения (nullterminated string)
 $B: Смещение от начала блока данных, по которому расположена новостная группа сообщения (null terminated string)
 $D: Смещение от начала блока данных, по которому расположен адресат (null terminated string)
 $E: Смещение от начала блока данных, по которому расположены данные “Reply to:” (null terminated string)
 $12: Смещение от начала блока данных, по которому расположена дата получения (дата представлена в формате TFileTime)
 $13: Смещение от начала блока данных, по которому расположен получатель (null terminated string)
 $1A: Смещение от начала блока данных, по которому расположен аккаунт (null terminated string)
 $1B: Смещение от начала блока данных, по которому расположен идентификатор аккаунта (null terminated string)
 $80: Номер сообщения
 $81: Используется для хранения статуса письма 
 $84: Смещение тела сообщения в файле
 $91: Размер сообщения.
Сообщения: Каждое письмо или сообщение групп новостей хранятся в блоках по 512 байт, у каждого блока есть заголовок. То есть каждое сообщение делится на части, и к каждой части добавляют заголовок (в котором содержится размер блока, размер занятой части блока, а так же положение следующего блока). Я использую такую структуру для чтения блоков (вместе с их заголовками):
Toe5_MsgItem = record
 FilePos: LongInt; { смещение структуры от BOF, используется для контроля верности операций }
 Unknown: LongInt; { я думаю, это размер блока данных }
 ItemSize: LongInt; { использованная часть блока }
 NextItem: LongInt; { смещение следующего блока от BOF, и 0, если это последний блок }
 MsgContent: array[0..511] of Char; { блок, содержащий непосредственно данные }
end;
== Удаление сообщений ==
Когда какое либо сообщение удаляется, оно сначала помещается в папку "Удаленные", а физически - в соответствующий этой папке файл. Место же, которое занимало сообщение в прежнем файле, добавляется в список пустого пространства, и когда приходит новое сообщение, Outlook Express использует сначала это место. Смещение первого элемента в списке свободного места сохранено по смещению $48 от начала файла. Каждый элемент этого списка разделен на две части: заголовок и блок свободного пространства. Вот структура заголовка:
Toe5_FreeSpace = record
 FilePos: LongInt; { это смещение всей структуры от начала файла BOF, используется для контроля }
 ElementSize: LongInt; { размер структуры - заголовок и свободное место }
 FreeSpaceSize: LongInt; { Размер свободного пространства }
 PreviousElement: LongInt; { смещение (от начала файла) предыдущего элемента }
 NextElement: LongInt; { смещение следующего элемента }
end;
== Даты ==
Все даты в заголовке сообщения сохранены в формате TFileTime, и основанном на UTC. Перед использованием это значение надо перевести в местное время. Вот небольшой пример того, как это можно сделать:
function FiletimeToDatetime(const date: TFileTime): TDateTime;
var
 st: TSystemTime;
 localft: TFileTime;
begin
 FileTimeToLocalFileTime(date, localft);
 FileTimeToSystemTime(localft, st);
 Result := SystemTimeToDateTime(st);
end;
== Статус сообщения ==
Для получения статуса сообщения можно использовать значение флага $81 следующим образом:
 x := <значение флага $81>
 if (x AND constant) <> 0 then
И на последок, некоторые константы:
 DOWNLOADED = $1
 MARKED = $20
 (Отмечено флажком) READED = $80
 DOWNLOAD_LATER = $100
 NEWS_MSG = $800
Эти константы надо проверить:
 ATTACHMENTS = $4000
 REPLY = $80000
 INSPECT_CONVERSATION = $400000
 IGNORE_CONVERSATION = $800000 
Модули к статье можно взять здесь
Все дополнения, модификации, предложения, благодарности и т.п. просьба присылать на Samum2000@mail15.com (особенно к русскому переводу) или на walther_e@yahoo.com (на английском языке, касательно оригинала). 
 Walther Estergaard Walther_e@yahoo.com
Перевод:
 Боднар Денис aka Samum, Samum2000@mail15.com, ICQ: 278395965.
 http://www.samum2000.narod.ru/articles/oedb.html
Author: Walther Estergaard
Source: http://www.samum2000.narod.ru/articles/oedb.html
ID: 04482