скрыть

скрыть

  Форум  

Delphi FAQ - Часто задаваемые вопросы

| Базы данных | Графика и Игры | Интернет и Сети | Компоненты и Классы | Мультимедиа |
| ОС и Железо | Программа и Интерфейс | Рабочий стол | Синтаксис | Технологии | Файловая система |



Google  
 

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



Автор: Samum
WEB-сайт: http://samum2000.narod.ru

Структура файла

Сообщения хранятся в таблицах, расположенных друг за другом, по порядку. Каждая таблица состоит из двух частей - заголовка и _входа_. Смещение первой таблицы от начала файла располагается со смещением $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.






Copyright © 2004-2016 "Delphi Sources". Delphi World FAQ




Группа ВКонтакте   Ссылка на Twitter   Группа на Facebook