你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:杂志经典 / 网络与通信
4.13 提取dbx文件中的邮件信息
 

一、引言

电子邮件如今已成为人们工作、生活中非常重要的通信工具,Outlook. Express作为微软公司在Windows操作系统中自带的电子邮件软件也因此得到广泛的使用。OE将全部邮件都存放在dbx文件中,这样做精巧简便,却不利于邮件的管理利用。例如用户有时需要将电子邮件存入数据库归档;有时需要根据时间、地址、内容对邮件进行分类检索,在OE中要实现这些操作就很繁琐。下面我们一起来研究一下dbx文件中邮件的存储结构,从而实现将邮件从中提取出来,以单个的.eml文件形式存放,为邮件数据的应用增加一种思路。

 

二、设计编程

1.读取dbx文件头

1 dbx文件头存储结构

文件位置

数据类型

描述

0x0000

 

dbx文件标识

0x0004

 

dbx文件类型标识

......

 

 

0x00c4

Int4

邮件数量

0x00e4

Int4

邮件信息索引起始位置

 

(1)       如表1所示,文件的起始0x0000位置记录了dbx文件的标识,必须是cf ad 12 fe

(2)       在文件的0x0004位置是dbx文件的格式标识。如果是c5 fd 74 6f,则表示该文件是邮件存储文件;如果是c6 fd 74 6f,则表示是OE的文件夹结构文件。本文只讨论邮件存储文件的格式。

(3)       在文件的0x00c4位置记录了本文件中存储的邮件数量。

(4)       在文件的0x00e4位置记录了邮件信息索引的起始位置。从该位置处可以读取全部邮件的信息索引。

读取dbx文件头的代码如下:

  AssignFile(fr,Edit1.text); ReSet(fr,1);

  //查看.dbx文件标志

  BlockRead(fr,l,4);

  if l<>$fe12adcf then begin

    ShowMessage('OE的邮件文件!');

    exit;

  end;

  //查看.dbx文件中的邮件文件,不包括文件夹设置文件

  BlockRead(fr,l,4);

  if l<>$6f74fdc5 then begin

    ShowMessage('可能是文件夹文件,不是邮件存放文件!');

    exit;

  end;

  //邮件数

  Seek(fr,$c4); BlockRead(fr,MsgCount,4);

  if MsgCount=0 then begin

    ShowMessage('没有邮件!');

    exit;

  end;

  //邮件信息起始存放位置

  Seek(fr,$e4); BlockRead(fr,MsgInfoPtr,4);

  if MsgInfoPtr=0 then begin

    ShowMessage('没有可提取的邮件!');

    exit;

  end;

2.读取邮件信息索引

根据文件头0x00e4处所记录的地址找到邮件信息索引,这些索引的存储结构如表2所示。

2 邮件信息索引的存储结构

序号

数据类型

描述

1

Int4

 

2

Int4

 

3

Int4

下一段信息索引区的位置

4

Int4

上一段信息索引区的位置

5

Int1

 

6

Int1

本区域所含邮件信息索引数量

7

Int2

 

8

Int4

 

9

 

记录每封邮件信息的位置

 

......

 

 

信息索引可以分为若干段,字段3、字段4分别记录了下一段、上一段的位置,形成数据链。字段6记录了本段所包含的邮件信息索引数量,字段9处则根据该数量,以3Int4为单位记录每封邮件信息所在的位置。这3Int4包含表3所示的信息:

3 邮件位置存储结构

序号

数据类型

描述

1

Int4

记录邮件信息存储的位置

2

Int4

下一段信息索引区的位置

3

Int4

下一段索引区所含邮件信息索引数量

 

如果字段2的值不为0,表示继续指向下一段邮件信息索引区,此时应该采用递归的方法继续读取;字段2的值为0,表示结束。

读取邮件信息索引的代码如下:

procedure NodeTree(FirstPtr:Longword );

var

  NodeCount : Byte;

  i : Longword;

  t : NodeBody;

  Ptr : Longword;

begin

  Ptr:=FirstPtr;

  while Ptr<>0 do begin

    Seek(fr,Ptr+17); BlockRead(fr,NodeCount,1);

    //将每封邮件的信息存放位置记入Msgs数组的InfoPtr

    for i:=1 to NodeCount do begin

      Seek(fr,Ptr+24+(i-1)*12);

      BlockRead(fr,t,12);

      //变长数组长度+1

      SetLength(Msgs,Length(Msgs)+1);

      Msgs[Length(Msgs)-1].InfoPtr:=t.Ptr;

      //查看是否有子树,如果有,则进入递归读取

      if t.NextPtr<>0 then NodeTree(t.NextPtr);

    end;

    //读取下一个Node0表示结束

    Seek(fr,Ptr+8); BlockRead(fr,Ptr,4);

  end;

end;

3.读取邮件信息

根据邮件信息索引中字段9所记录的位置可以找到每封邮件的信息,这些信息存储的结构如表4所示。

4  邮件信息的存结构

序号

数据类型

描述

1

Int4

 

2

Int4

 

3

Int2

 

4

Int1

所包含的索引项数量

5

Int1

 

 

 

索引项数量*4

6

Int1 Bit 8

高位标志

7

   Bit 1-7

索引号

8

Int3

数值

......

 

 

9

 

数据区

 

如果字段6Bit 8位为0,表示该索引项的内容需要从下面的字段9数据区取得,起始位置就是Bit 1-7所记录的数据;如果字段6Bit 8位为1,表示该索引项的数据就是Bit 1-7所记录的数据。

根据索引数据就可以列举出全部邮件的信息,如表5所示。

5  全部邮件信息

索引号

数据类型

描述

0x00

Int4

 

0x01

Int4

 

0x02

 

 

0x03

Int4

 

0x04

Int4

存放邮件内容的起始块

......

 

 

0x08

String

邮件标题

......

 

 

 

读取邮件信息的代码如下:

  //读取每封邮件的标题和内容的存放位置

  for i:=0 to Length(Msgs)-1 do begin

    ProgressBar1.Position:=i*100 div Length(Msgs); Application.ProcessMessages;

    Seek(fr,Msgs[i].InfoPtr+10); BlockRead(fr,NodeCount,1);

    for j:=1 to NodeCount do begin

      Seek(fr,Msgs[i].InfoPtr+12+(j-1)*4);

      BlockRead(fr,b1,1); BlockRead(fr,b2,1); BlockRead(fr,b3,1); BlockRead(fr,b4,1);

      m.Hi:=b1 shr 7;

      m.Pos:=b1 and $7F;

      m.Value:=(b4 shl 16)+(b3 shl 8)+b2;

      if m.Hi=0 then m.Value:=Msgs[i].InfoPtr+12+NodeCount*4+m.Value;

      case m.Pos of

        4 : begin

          //邮件内容存放的起始位置

          if m.Hi=1 then Msgs[i].EMLPtr:=m.Value else begin

            Seek(fr,m.Value); BlockRead(fr,l,4);

            Msgs[i].EMLPtr:=l;

          end;

        end;

        8 : begin

          //邮件标题

          s:='';

          Seek(fr,m.Value);

          repeat

            BlockRead(fr,c,1);

            if c in BadFilenameChar then c:='_';

            if c<>#0 then s:=s+c;

          until c=#0;

          if s='' then s:='[无标题]';

          if Length(s)>200 then s:=Copy(s,1,200);

          Msgs[i].Subject:=s;

        end;

      end;

    end;

  end;

dbx文件中邮件的内容数据是分块存储的,起始块的位置就记录在索引号为0x04的数据中。邮件内容数据块的数据结构如表6所示:

6 邮件内容数据块的数据结构

序号

数据类型

描述

1

Int4

 

2

Int4

本块数据区长度

3

Int4

数据区内实际数据长度

4

Int4

下一个数据块的位置

5

 

数据区

 

如果字段3的数值小于字段2,即数据区内实际的数据长度小于本块数据区长度,则后面的是无用数据。字段4记录了下一块内容数据的位置,0x0000表示结束。

读取邮件内容,并以邮件标题为文件名生成eml文件的代码如下:

  //读取每封邮件的内容并存盘

  for i:=0 to Length(Msgs)-1 do begin

    if Msgs[i].EMLPtr=0 then continue;

    //先检查是否有同名文件,有的话在文件名后面添加计数

    s1:=Edit2.Text+Msgs[i].Subject; s:=s1;

    j:=1;

    while FileExists(s+'.eml') do begin

      s:=s1+' ['+InttoStr(j)+']';

      Inc(j);

    end;

    s:=s+'.eml';

    AssignFile(fw,s); ReWrite(fw,1);

    Seek(fr,Msgs[i].EMLPtr);

    repeat

      BlockRead(fr,mh,16);

      SetLength(Buf,mh.BlockLen);

      //按照BlockLen读取

      BlockRead(fr,Buf[0],mh.BlockLen);

      //按照TextLen实际内容长度存盘

      BlockWrite(fw,Buf[0],mh.TextLen);

      //读取下一段所在位置

      Seek(fr,mh.NextPtr);

    until mh.NextPtr=0;

    CloseFile(fw);

  end;

 

三、程序运行界面

提取dbx文件中邮件程序的运行界面如图1所示。


1 程序运行界面

 

四、结语

以上简单介绍了dbx文件的存储结构,虽然OE本身也提供了通过拖拽方式生成.eml文件的功能,但是在我们了解dbx文件的存储结构后,就可以实现邮件数据在不同操作系统、不同应用软件之间交换,从而方便用户灵活使用。

 

  推荐精品文章

·2024年12月目录 
·2024年11月目录 
·2024年10月目录 
·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089