Subject Re: [IBO] import outlook messages into gdb
Author Jason Wharton
Here's the source for the component:

{* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Author: Fran├žois PIETTE
Description: TMbxFile handle the MBX file format (read only)
The MBX file format is used by Microsoft Internet Mail.
Warning: This component is based on my interpretation of the MBX file
format. I did'nt find any info about it. So there may be
errors !
Currently, no provision is made for deleted messages.
If you find a problem, please EMail me a description and
attach
the MBX file in error.
EMail: francois.piette@... http://www.rtfm.be/fpiette
francois.piette@...
2:293/2202@..., BBS +32-4-3651395
Creation: November 16, 1997
Version: 1.00
WebSite: http://www.rtfm.be/fpiette/indexuk.htm
Support: Use the mailing list twsocket@... See website for details.
Legal issues: Copyright (C) 1997 by Fran├žois PIETTE
<francois.piette@...>

This software is provided 'as-is', without any express or
implied warranty. In no event will the author be held liable
for any damages arising from the use of this software.

Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it
and redistribute it freely, subject to the following
restrictions:

1. The origin of this software must not be misrepresented,
you must not claim that you wrote the original software.
If you use this software in a product, an acknowledgment
in the product documentation would be appreciated but is
not required.

2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.

3. This notice may not be removed or altered from any source
distribution.

Contributing Authors: Jason Wharton <jwharton@...>

Updates:

JLW: 04/01/2000 I corrected the problem where the MsgNum value was a non-
continuous figure from 1 to n. It was starting in the
thousands and ending higher which caused the former Eof
property to be calculated incorrectly in "Deleted
Items.MBX".

I also noticed it wasn't a continuous figure by one each
time.
It would skip from time to time so I added a "cursor"
tracker
so that it would always know where it was relative to the
start. See the CurRec property.

I also fixed a bug in the handling of each individual
message.
It was allowing additional bytes of longer messages to
bleed
onto the end of shorter messages.

I couldn't resist also formatting the code so that it met
my preferences.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*}
unit
MbxFile;

interface

uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
Forms, Dialogs, ExtCtrls;

const
MbxFileVersion = 100;
MSG_SIGNATURE = $7F007F00;

type
TMbxHeader = packed record
Signature: array [0..3] of char;
Reserved1: Word;
Reserved2: Word;
MsgCount: DWord;
Reserved3: DWord; // A MsgCount copy ?
EndPointer: DWord;
Reserved4: array [0..15] of DWord;
end;

TMsgHeader = packed record
Signature: DWord; // $7F007F00
MsgNum: DWord;
MsgSize: DWord;
Reserved1: DWord;
end;

TCustomMbxHandler = class( TComponent )
private
FFileHdr: TMbxHeader;
FFileName: String;
FFileStream: TFileStream;
FMsgHeader: TMsgHeader;
FMsgStream: TMemoryStream;
FCurPos: LongInt;
FCurRec: longint;
public
constructor Create( AOwner: TComponent ); override;
destructor Destroy; override;
procedure Open;
procedure Close;
procedure First;
procedure Next;
procedure Prior;
procedure Last;
private
procedure CheckOpen;
procedure InternalPrior;
procedure ReadNextMessage;
procedure SetActive( AValue : boolean );
function GetActive: boolean;
function GetMsgCount: Integer;
function GetMsgNum: Integer;
function GetEof: boolean;
function GetBof: boolean;
public
property FileName: String read FFileName write FFileName;
property Active: boolean read GetActive write SetActive;
property MsgCount: Integer read GetMsgCount;
property MsgNum: Integer read GetMsgNum;
property MsgStream: TMemoryStream read FMsgStream;
property Eof: boolean read GetEof;
property Bof: boolean read GetBof;
property CurRec: longint read FCurRec;
end;

TMbxHandler = class( TCustomMbxHandler )
published
property FileName: String read FFileName write FFileName;
property Active: boolean read GetActive write SetActive;
end;

procedure Register;

implementation

procedure Register;
begin
RegisterComponents( 'FPiette', [ TMbxHandler ] );
end;

constructor TCustomMbxHandler.Create( AOwner : TComponent );
begin
inherited Create( AOwner );
FMsgStream := TMemoryStream.Create;
end;

destructor TCustomMbxHandler.Destroy;
begin
Close;
FMsgStream.Free;
inherited Destroy;
end;

procedure TCustomMbxHandler.CheckOpen;
begin
if not Assigned( FFileStream ) then
raise Exception.Create( 'Message file not opened' );
end;

procedure TCustomMbxHandler.Open;
begin
Close;
FFileStream := TFileStream.Create( FFileName, fmOpenRead );
FFileStream.Read( FFileHdr, SizeOf( FFileHdr ));
if FFileHdr.Signature <> 'JMF6' then
begin
Close;
raise Exception.Create( 'Not a valid Outlook Express MBX EMail file' );
end;
FCurRec := 0;
if MsgCount > 0 then
ReadNextMessage;
end;

procedure TCustomMbxHandler.Close;
begin
if Assigned( FFileStream ) then
begin
FFileStream.Free;
FFileStream := nil;
end;
end;

procedure TCustomMbxHandler.First;
begin
CheckOpen;
if MsgCount > 0 then
begin
FFileStream.Position := SizeOf( TMbxHeader );
FCurRec := 0;
ReadNextMessage;
end;
end;

procedure TCustomMbxHandler.Last;
begin
CheckOpen;
if MsgCount > 0 then
begin
FCurPos := FFileStream.Seek( 0, soFromEnd );
FCurRec := MsgCount + 1;
InternalPrior;
end;
end;

procedure TCustomMbxHandler.Next;
begin
CheckOpen;
if Eof then
raise Exception.Create( 'No more messages' );
ReadNextMessage;
end;

procedure TCustomMbxHandler.Prior;
begin
CheckOpen;
if Bof then
raise Exception.Create( 'No more messages' );
InternalPrior;
end;

procedure TCustomMbxHandler.SetActive( AValue: boolean );
begin
if AValue <> Active then
if AValue then
Open
else
Close;
end;

function TCustomMbxHandler.GetActive: boolean;
begin
Result := Assigned( FFileStream );
end;

function TCustomMbxHandler.GetMsgCount: Integer;
begin
CheckOpen;
Result := FFileHdr.MsgCount;
end;

function TCustomMbxHandler.GetMsgNum: Integer;
begin
CheckOpen;
Result := FMsgHeader.MsgNum;
end;

function TCustomMbxHandler.GetEof: boolean;
begin
Result := ( not Active ) or
( MsgCount <= 0 ) or
( FCurRec > MsgCount );
end;

function TCustomMbxHandler.GetBof: boolean;
begin
Result := ( not Active ) or
( MsgCount <= 0 ) or
( FCurRec < 1 );
end;

procedure TCustomMbxHandler.ReadNextMessage;
var
tmpLen: longint;
begin
FCurPos := FFileStream.Position;
tmpLen := FFileStream.Read( FMsgHeader, SizeOf( FMsgHeader ));
if tmpLen > 0 then
begin
if FMsgHeader.Signature <> MSG_SIGNATURE then
begin
Close;
raise Exception.Create( 'Invalid signature in message header' );
end;
FMsgStream.SetSize( FMsgHeader.MsgSize - 16 );
FMsgStream.Seek( 0, soFromBeginning );
FFileStream.Read( FMsgStream.Memory^, FMsgHeader.MsgSize - 16 );
end else
begin
FMsgStream.SetSize( 0 );
FillChar( FMsgHeader, SizeOf( FMsgHeader ), #0 );
end;
Inc( FCurRec );
end;

procedure TCustomMbxHandler.InternalPrior;
var
NewPos: LongInt;
Buf: PChar;
p: PChar;
More: boolean;
Sign: DWord;
Cnt: Integer;
begin
if ( CurRec > 1 ) and ( MsgCount > 0 ) then
begin
FMsgStream.SetSize( 8192 );
Buf := FMsgStream.Memory;
Cnt := 8192;
NewPos := FCurPos - Cnt;
More := TRUE;
while More do begin
if NewPos < SizeOf( TMbxHeader ) then begin
Cnt := Cnt - SizeOf( TMbxHeader ) + NewPos;
NewPos := SizeOf( TMbxHeader );
end;
FFileStream.Position := NewPos;
FFileStream.Read( Buf^, Cnt );
p := Buf + Cnt - 1;
while ( p > Buf ) do begin
while ( p >= Buf ) and ( p^ <> #$7F ) do
Dec( p );
if p^ = #$7F then begin
FFileStream.Position := NewPos + p - Buf - 3;
FFileStream.Read( Sign, SizeOf( Sign ));
if Sign = MSG_SIGNATURE then
begin
NewPos := NewPos + p - Buf - 3;
More := FALSE;
Break;
end;
end;
Dec( p );
end;
if not More then
Break;
if NewPos <= SizeOf( TMbxHeader ) then
Break;
Cnt := 8192;
NewPos := NewPos - Cnt;
end;
FFileStream.Position := NewPos;
Dec( FCurRec, 2 );
ReadNextMessage;
end else
begin
FFileStream.Position := SizeOf( TMbxHeader );
FCurRec := 0;
FMsgStream.SetSize( 0 );
FillChar( FMsgHeader, SizeOf( FMsgHeader ), #0 );
end;
end;

end.


Jason Wharton
CPS - Mesa AZ
http://www.ibobjects.com