Как читать файлы Outlook Express
КАК ЧИТАТЬ ФАЙЛЫ OUTLOOK EXPRESS (.DBX)
(формат файла *.DBX)
1)Структура файла.
Сообщения хранятся в таблицах, расположенных друг за другом, по порядку. Каждая таблица состоит из двух частей - заголовка и _входа_. Смещение первой таблицы от начала файла располагается со смещением $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 части), суммарный размер таблицы параметров, блока данных и количество параметров в таблице (я называю эти параметры флагами). |
position: longint; {смещение данной структуры от BOF, используется только для контроля}
DataLength: longint; {размер таблицы параметров и блока данных}
HeaderLength: WORD; {размер всех трех частей}
FlagCount: WORD; {количество элементов в таблице}
end ;
Чтобы получить размер таблицы флагов, можно написать SizeOfTable:=FlagCount*SizeOf(DWord), a размер блока данных используйте DataLength-SizeOfTable.· | Каждый элемент таблицы параметров должен быть декодирован, для того, чтобы получить его идентификатор и значение: идентификатор находят как element and $FF, а значение флага - element shr 8. |
· | Блок данных содержит такую информацию, как дата получения, отправки, тема, адресат, отправитель, аккаунт и т.д. Для её чтения используется таблица флагов (параметров). Вот пример таблицы параметров и соответствующего блока данных: |
$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 = recordFilePos: longint; {смещение структуры от BOF, используется для контроля верности операций}
Unknown: longint; {я думаю, это размер блока данных}
ItemSize: longint; {использованная часть блока}
NextItem: longint; {смещение следующего блока от BOF, и 0, если это последний блок}
MsgContent: array[0..511] of Char; {блок, содержащий непосредственно данные}
end ;
2) Удаление сообщений.
Когда какое либо сообщение удаляется, оно сначала помещается в папку "Удаленные", а физически - в соответствующий этой папке файл. Место же, которое занимало сообщение в прежнем файле, добавляется в список пустого пространства, и когда приходит новое сообщение, Outlook Express использует сначала это место. Смещение первого элемента в списке сободного места сохранено по смещению $48 от начала файла. Каждый элемент этого списка разделен на две части: заголовок и блок свободного пространства. Вот структура заголовка:
Toe5_FreeSpace = record FilePos: longint;{это смещение всей структуры от начала файла BOF, используется для контроля} ElementSize:longint; {размер структуры - заголовок и свободное место} FreeSpaceSize: longint;{Размер свободного пространства} PreviousElement: longint; {смещение (от начала файла) предыдущего элемента} NextElement: longint; {смещение следующего элемента} end ;
3) Даты.
Все даты в заголовке сообщения сохранены в формате TFileTime, и основаном на UTC. Перед использованием это значение надо перевести в местное время. Вот небольшой пример того, как это можно сделать:
function FiletimeToDatetime(const date: TFileTime): TDateTime; var st:TSystemTime; localft: TFileTime; begin FileTimeToLocalFileTime(date, localft); FileTimeToSystemTime(localft, st); Result:=SystemTimeToDateTime(st); end;
4)Статус сообщения.
Для получения статуса сообщения можно использовать значение флага $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.