{******************************************************************************}
{ }
{ Project JEDI Code Library (JCL) }
{ }
{ The contents of this file are subject to the Mozilla Public License Version }
{ 1.1 (the "License"); you may not use this file except in compliance with the }
{ License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ }
{ }
{ Software distributed under the License is distributed on an "AS IS" basis, }
{ WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for }
{ the specific language governing rights and limitations under the License. }
{ }
{ The Original Code is JclMapi.pas. }
{ }
{ The Initial Developer of the Original Code is documented in the accompanying }
{ help file JCL.chm. Portions created by these individuals are Copyright (C) }
{ of these individuals. }
{ }
{******************************************************************************}
{ }
{ Various classes and support routines for sending e-mail through Simple MAPI }
{ }
{ Unit owner: Petr Vones }
{ Last modified: November 7, 2001 }
{ }
{******************************************************************************}
unit JclMapi;
{$I jcl.inc}
interface
uses
Windows, Classes, Mapi, SysUtils,
{$IFDEF DELPHI5_UP}
Contnrs,
{$ENDIF DELPHI5_UP}
JclBase;
type
EJclMapiError = class (EJclError)
private
FErrorCode: DWORD;
public
property ErrorCode: DWORD read FErrorCode;
end;
//------------------------------------------------------------------------------
// Simple MAPI interface
//------------------------------------------------------------------------------
TJclMapiClient = record
ClientName: string;
ClientPath: string;
RegKeyName: string;
Valid: Boolean;
end;
TJclMapiClientConnect = (ctAutomatic, ctMapi, ctDirect);
TJclSimpleMapi = class (TObject)
private
FAnyClientInstalled: Boolean;
FBeforeUnloadClient: TNotifyEvent;
FClients: array of TJclMapiClient;
FClientConnectKind: TJclMapiClientConnect;
FClientLibHandle: THandle;
FDefaultClientIndex: Integer;
FDefaultProfileName: string;
FFunctions: array of ^Pointer;
FMapiInstalled: Boolean;
FMapiVersion: string;
FProfiles: array of string;
FSelectedClientIndex: Integer;
FSimpleMapiInstalled: Boolean;
FMapiAddress: TFNMapiAddress;
FMapiDeleteMail: TFNMapiDeleteMail;
FMapiDetails: TFNMapiDetails;
FMapiFindNext: TFNMapiFindNext;
FMapiFreeBuffer: TFNMapiFreeBuffer;
FMapiLogOff: TFNMapiLogOff;
FMapiLogOn: TFNMapiLogOn;
FMapiReadMail: TFNMapiReadMail;
FMapiResolveName: TFNMapiResolveName;
FMapiSaveMail: TFNMapiSaveMail;
FMapiSendDocuments: TFNMapiSendDocuments;
FMapiSendMail: TFNMapiSendMail;
function GetClientCount: Integer;
function GetClients(Index: Integer): TJclMapiClient;
function GetCurrentClientName: string;
function GetProfileCount: Integer;
function GetProfiles(Index: Integer): string;
procedure SetSelectedClientIndex(const Value: Integer);
procedure SetClientConnectKind(const Value: TJclMapiClientConnect);
function UseMapi: Boolean;
protected
procedure BeforeUnloadClientLib; dynamic;
procedure CheckListIndex(I, ArrayLength: Integer);
function GetClientLibName: string;
class function ProfilesRegKey: string;
procedure ReadMapiSettings;
public
constructor Create;
destructor Destroy; override;
function ClientLibLoaded: Boolean;
procedure LoadClientLib;
procedure UnloadClientLib;
property AnyClientInstalled: Boolean read FAnyClientInstalled;
property ClientConnectKind: TJclMapiClientConnect read FClientConnectKind write SetClientConnectKind;
property ClientCount: Integer read GetClientCount;
property Clients[Index: Integer]: TJclMapiClient read GetClients; default;
property CurrentClientName: string read GetCurrentClientName;
property DefaultClientIndex: Integer read FDefaultClientIndex;
property DefaultProfileName: string read FDefaultProfileName;
property MapiInstalled: Boolean read FMapiInstalled;
property MapiVersion: string read FMapiVersion;
property ProfileCount: Integer read GetProfileCount;
property Profiles[Index: Integer]: string read GetProfiles;
property SelectedClientIndex: Integer read FSelectedClientIndex write SetSelectedClientIndex;
property SimpleMapiInstalled: Boolean read FSimpleMapiInstalled;
property BeforeUnloadClient: TNotifyEvent read FBeforeUnloadClient write FBeforeUnloadClient;
// Simple MAPI functions
property MapiAddress: TFNMapiAddress read FMapiAddress;
property MapiDeleteMail: TFNMapiDeleteMail read FMapiDeleteMail;
property MapiDetails: TFNMapiDetails read FMapiDetails;
property MapiFindNext: TFNMapiFindNext read FMapiFindNext;
property MapiFreeBuffer: TFNMapiFreeBuffer read FMapiFreeBuffer;
property MapiLogOff: TFNMapiLogOff read FMapiLogOff;
property MapiLogOn: TFNMapiLogOn read FMapiLogOn;
property MapiReadMail: TFNMapiReadMail read FMapiReadMail;
property MapiResolveName: TFNMapiResolveName read FMapiResolveName;
property MapiSaveMail: TFNMapiSaveMail read FMapiSaveMail;
property MapiSendDocuments: TFNMapiSendDocuments read FMapiSendDocuments;
property MapiSendMail: TFNMapiSendMail read FMapiSendMail;
end;
//------------------------------------------------------------------------------
// Simple email classes
//------------------------------------------------------------------------------
TJclEmailRecipKind = (rkOriginator, rkTO, rkCC, rkBCC);
TJclEmailRecip = class (TObject)
private
FAddress: string;
FAddressType: string;
FKind: TJclEmailRecipKind;
FName: string;
protected
function SortingName: string;
public
function AddressAndName: string;
property AddressType: string read FAddressType write FAddressType;
property Address: string read FAddress write FAddress;
property Kind: TJclEmailRecipKind read FKind write FKind;
property Name: string read FName write FName;
end;
TJclEmailRecips = class (TObjectList)
private
FAddressesType: string;
function GetItems(Index: Integer): TJclEmailRecip;
function GetOriginator: TJclEmailRecip;
public
function Add(const Address: string;
const Name: string {$IFDEF SUPPORTS_DEFAULTPARAMS} = '' {$ENDIF};
const Kind: TJclEmailRecipKind {$IFDEF SUPPORTS_DEFAULTPARAMS} = rkTO {$ENDIF};
const AddressType: string {$IFDEF SUPPORTS_DEFAULTPARAMS} = '' {$ENDIF}): Integer;
procedure SortRecips;
property AddressesType: string read FAddressesType write FAddressesType;
property Items[Index: Integer]: TJclEmailRecip read GetItems; default;
property Originator: TJclEmailRecip read GetOriginator;
end;
TJclEmailFindOption = (foFifo, foUnreadOnly);
TJclEmailLogonOption = (loLogonUI, loNewSession, loForceDownload);
TJclEmailReadOption = (roAttachments, roHeaderOnly, roMarkAsRead);
TJclEmailFindOptions = set of TJclEmailFindOption;
TJclEmailLogonOptions = set of TJclEmailLogonOption;
TJclEmailReadOptions = set of TJclEmailReadOption;
TJclEmailReadMsg = record
ConversationID: string;
DateReceived: TDateTime;
MessageType: string;
Flags: FLAGS;
end;
TJclEmail = class (TJclSimpleMapi)
private
FAttachments: TStrings;
FBody: string;
FFindOptions: TJclEmailFindOptions;
FHtmlBody: Boolean;
FLogonOptions: TJclEmailLogonOptions;
FParentWnd: HWND;
FReadMsg: TJclEmailReadMsg;
FRecipients: TJclEmailRecips;
FSeedMessageID: string;
FSessionHandle: THandle;
FSubject: string;
function GetUserLogged: Boolean;
procedure SetBody(const Value: string);
function GetParentWnd: HWND;
protected
procedure BeforeUnloadClientLib; override;
procedure DecodeRecips(RecipDesc: PMapiRecipDesc; Count: Integer);
function InternalSendOrSave(Save: Boolean; ShowDialog: Boolean): Boolean;
function LogonOptionsToFlags(ShowDialog: Boolean): DWORD;
public
constructor Create;
destructor Destroy; override;
function Address(const Caption: string {$IFDEF SUPPORTS_DEFAULTPARAMS} = '' {$ENDIF};
EditFields: Integer {$IFDEF SUPPORTS_DEFAULTPARAMS} = 3 {$ENDIF}): Boolean;
procedure Clear;
function Delete(const MessageID: string): Boolean;
function FindFirstMessage: Boolean;
function FindNextMessage: Boolean;
procedure LogOff;
procedure LogOn(const ProfileName: string {$IFDEF SUPPORTS_DEFAULTPARAMS} = '' {$ENDIF};
const Password: string {$IFDEF SUPPORTS_DEFAULTPARAMS} = '' {$ENDIF});
function MessageReport(Strings: TStrings;
MaxWidth: Integer {$IFDEF SUPPORTS_DEFAULTPARAMS} = 80 {$ENDIF};
IncludeAddresses: Boolean {$IFDEF SUPPORTS_DEFAULTPARAMS} = False {$ENDIF}): Integer;
function Read(const Options: TJclEmailReadOptions {$IFDEF SUPPORTS_DEFAULTPARAMS} = [] {$ENDIF}): Boolean;
function ResolveName(var Name, Address: string;
ShowDialog: Boolean {$IFDEF SUPPORTS_DEFAULTPARAMS} = False {$ENDIF}): Boolean;
function Save: Boolean;
function Send(ShowDialog: Boolean {$IFDEF SUPPORTS_DEFAULTPARAMS} = True {$ENDIF}): Boolean;
procedure SortAttachments;
property Attachments: TStrings read FAttachments;
property Body: string read FBody write SetBody;
property FindOptions: TJclEmailFindOptions read FFindOptions write FFindOptions;
property HtmlBody: Boolean read FHtmlBody write FHtmlBody;
property LogonOptions: TJclEmailLogonOptions read FLogonOptions write FLogonOptions;
property ParentWnd: HWND read GetParentWnd write FParentWnd;
property ReadMsg: TJclEmailReadMsg read FReadMsg;
property Recipients: TJclEmailRecips read FRecipients;
property SeedMessageID: string read FSeedMessageID write FSeedMessageID;
property SessionHandle: THandle read FSessionHandle;
property Subject: string read FSubject write FSubject;
property UserLogged: Boolean read GetUserLogged;
end;
//------------------------------------------------------------------------------
// Simple email send function
//------------------------------------------------------------------------------
function JclSimpleSendMail(const ARecipient, AName, ASubject, ABody: string;
const AAttachment: TFileName {$IFDEF SUPPORTS_DEFAULTPARAMS} = '' {$ENDIF};
ShowDialog: Boolean {$IFDEF SUPPORTS_DEFAULTPARAMS} = True {$ENDIF};
AParentWND: HWND {$IFDEF SUPPORTS_DEFAULTPARAMS} = 0 {$ENDIF}): Boolean;
function JclSimpleBringUpSendMailDialog(const ASubject, ABody: string;
const AAttachment: TFileName {$IFDEF SUPPORTS_DEFAULTPARAMS} = '' {$ENDIF};
AParentWND: HWND {$IFDEF SUPPORTS_DEFAULTPARAMS} = 0 {$ENDIF}): Boolean;
//------------------------------------------------------------------------------
// MAPI Errors
//------------------------------------------------------------------------------
function MapiCheck(const Res: DWORD; IgnoreUserAbort: Boolean {$IFDEF SUPPORTS_DEFAULTPARAMS} = True {$ENDIF}): DWORD;
function MapiErrorMessage(const ErrorCode: DWORD): string;
implementation
uses
Registry,
JclFileUtils, JclLogic, JclResources, JclStrings, JclSysInfo, JclSysUtils;
const
MapiDll = 'mapi32.dll';
MapiExportNames: array [0..11] of PChar =
('MAPIAddress',
'MAPIDeleteMail',
'MAPIDetails',
'MAPIFindNext',
'MAPIFreeBuffer',
'MAPILogoff',
'MAPILogon',
'MAPIReadMail',
'MAPIResolveName',
'MAPISaveMail',
'MAPISendDocuments',
'MAPISendMail'
);
//------------------------------------------------------------------------------
// MAPI Errors check
//------------------------------------------------------------------------------
function MapiCheck(const Res: DWORD; IgnoreUserAbort: Boolean): DWORD;
var
Error: EJclMapiError;
begin
if (Res = SUCCESS_SUCCESS) or (IgnoreUserAbort and (Res = MAPI_E_USER_ABORT)) then
Result := Res
else
begin
Error := EJclMapiError.CreateResRecFmt(@RsMapiError, [Res, MapiErrorMessage(Res)]);
Error.FErrorCode := Res;
raise Error;
end;
end;
//------------------------------------------------------------------------------
function MapiErrorMessage(const ErrorCode: DWORD): string;
begin
case ErrorCode of
MAPI_E_USER_ABORT:
Result := RsMapiErrUSER_ABORT;
MAPI_E_FAILURE:
Result := RsMapiErrFAILURE;
MAPI_E_LOGIN_FAILURE:
Result := RsMapiErrLOGIN_FAILURE;
MAPI_E_DISK_FULL:
Result := RsMapiErrDISK_FULL;
MAPI_E_INSUFFICIENT_MEMORY:
Result := RsMapiErrINSUFFICIENT_MEMORY;
MAPI_E_ACCESS_DENIED:
Result := RsMapiErrACCESS_DENIED;
MAPI_E_TOO_MANY_SESSIONS:
Result := RsMapiErrTOO_MANY_SESSIONS;
MAPI_E_TOO_MANY_FILES:
Result := RsMapiErrTOO_MANY_FILES;
MAPI_E_TOO_MANY_RECIPIENTS:
Result := RsMapiErrTOO_MANY_RECIPIENTS;
MAPI_E_ATTACHMENT_NOT_FOUND:
Result := RsMapiErrATTACHMENT_NOT_FOUND;
MAPI_E_ATTACHMENT_OPEN_FAILURE:
Result := RsMapiErrATTACHMENT_OPEN_FAILURE;
MAPI_E_ATTACHMENT_WRITE_FAILURE:
Result := RsMapiErrATTACHMENT_WRITE_FAILURE;
MAPI_E_UNKNOWN_RECIPIENT:
Result := RsMapiErrUNKNOWN_RECIPIENT;
MAPI_E_BAD_RECIPTYPE:
Result := RsMapiErrBAD_RECIPTYPE;
MAPI_E_NO_MESSAGES:
Result := RsMapiErrNO_MESSAGES;
MAPI_E_INVALID_MESSAGE:
Result := RsMapiErrINVALID_MESSAGE;
MAPI_E_TEXT_TOO_LARGE:
Result := RsMapiErrTEXT_TOO_LARGE;
MAPI_E_INVALID_SESSION:
Result := RsMapiErrINVALID_SESSION;
MAPI_E_TYPE_NOT_SUPPORTED:
Result := RsMapiErrTYPE_NOT_SUPPORTED;
MAPI_E_AMBIGUOUS_RECIPIENT:
Result := RsMapiErrAMBIGUOUS_RECIPIENT;
MAPI_E_MESSAGE_IN_USE:
Result := RsMapiErrMESSAGE_IN_USE;
MAPI_E_NETWORK_FAILURE:
Result := RsMapiErrNETWORK_FAILURE;
MAPI_E_INVALID_EDITFIELDS:
Result := RsMapiErrINVALID_EDITFIELDS;
MAPI_E_INVALID_RECIPS:
Result := RsMapiErrINVALID_RECIPS;
MAPI_E_NOT_SUPPORTED:
Result := RsMapiErrNOT_SUPPORTED;
else
Result := '';
end;
end;
//==============================================================================
// TJclSimpleMapi
//==============================================================================
procedure TJclSimpleMapi.BeforeUnloadClientLib;
begin
if Assigned(FBeforeUnloadClient) then
FBeforeUnloadClient(Self);
end;
//------------------------------------------------------------------------------
procedure TJclSimpleMapi.CheckListIndex(I, ArrayLength: Integer);
begin
if (I < 0) or (I >= ArrayLength) then
raise EJclMapiError.CreateResRecFmt(@RsMapiInvalidIndex, [I]);
end;
//------------------------------------------------------------------------------
function TJclSimpleMapi.ClientLibLoaded: Boolean;
begin
Result := FClientLibHandle <> 0;
end;
//------------------------------------------------------------------------------
constructor TJclSimpleMapi.Create;
begin
SetLength(FFunctions, Length(MapiExportNames));
FFunctions[0] := @@FMapiAddress;
FFunctions[1] := @@FMapiDeleteMail;
FFunctions[2] := @@FMapiDetails;
FFunctions[3] := @@FMapiFindNext;
FFunctions[4] := @@FMapiFreeBuffer;
FFunctions[5] := @@FMapiLogOff;
FFunctions[6] := @@FMapiLogOn;
FFunctions[7] := @@FMapiReadMail;
FFunctions[8] := @@FMapiResolveName;
FFunctions[9] := @@FMapiSaveMail;
FFunctions[10] := @@FMapiSendDocuments;
FFunctions[11] := @@FMapiSendMail;
FDefaultClientIndex := -1;
FClientConnectKind := ctAutomatic;
FSelectedClientIndex := -1;
ReadMapiSettings;
end;
//------------------------------------------------------------------------------
destructor TJclSimpleMapi.Destroy;
begin
UnloadClientLib;
inherited;
end;
//------------------------------------------------------------------------------
function TJclSimpleMapi.GetClientCount: Integer;
begin
Result := Length(FClients);
end;
//------------------------------------------------------------------------------
function TJclSimpleMapi.GetClientLibName: string;
begin
if UseMapi then
Result := MapiDll
else
Result := FClients[FSelectedClientIndex].ClientPath;
end;
//------------------------------------------------------------------------------
function TJclSimpleMapi.GetClients(Index: Integer): TJclMapiClient;
begin
CheckListIndex(Index, ClientCount);
Result := FClients[Index];
end;
//------------------------------------------------------------------------------
function TJclSimpleMapi.GetCurrentClientName: string;
begin
if UseMapi then
Result := 'MAPI'
else
if ClientCount > 0 then
Result := Clients[SelectedClientIndex].ClientName
else
Result := '';
end;
//------------------------------------------------------------------------------
function TJclSimpleMapi.GetProfileCount: Integer;
begin
Result := Length(FProfiles);
end;
//------------------------------------------------------------------------------
function TJclSimpleMapi.GetProfiles(Index: Integer): string;
begin
CheckListIndex(Index, ProfileCount);
Result := FProfiles[Index];
end;
//------------------------------------------------------------------------------
procedure TJclSimpleMapi.LoadClientLib;
var
I: Integer;
P: Pointer;
begin
if ClientLibLoaded then
Exit;
FClientLibHandle := LoadLibrary(PChar(GetClientLibName));
if FClientLibHandle = 0 then
RaiseLastOSError;
for I := 0 to Length(FFunctions) - 1 do
begin
P := GetProcAddress(FClientLibHandle, PChar(MapiExportNames[I]));
if P = nil then
begin
UnloadClientLib;
raise EJclMapiError.CreateResRecFmt(@RsMapiMissingExport, [MapiExportNames[I]]);
end
else
FFunctions[I]^ := P;
end;
end;
//------------------------------------------------------------------------------
class function TJclSimpleMapi.ProfilesRegKey: string;
begin
if IsWinNT then
Result := 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles'
else
Result := 'SOFTWARE\Microsoft\Windows Messaging Subsystem\Profiles';
end;
//------------------------------------------------------------------------------
procedure TJclSimpleMapi.ReadMapiSettings;
const
MessageSubsytemKey = 'SOFTWARE\Microsoft\Windows Messaging Subsystem';
MailClientsKey = 'SOFTWARE\Clients\Mail';
var
DefaultValue: string;
SL: TStringList;
I: Integer;
function CheckValid(var Client: TJclMapiClient): Boolean;
var
I: Integer;
LibHandle: THandle;
begin
LibHandle := LoadLibraryEx(PChar(Client.ClientPath), 0, DONT_RESOLVE_DLL_REFERENCES);
Result := (LibHandle <> 0);
if Result then
begin
for I := Low(MapiExportNames) to High(MapiExportNames) do
if GetProcAddress(LibHandle, PChar(MapiExportNames[I])) = nil then
begin
Result := False;
Break;
end;
FreeLibrary(LibHandle);
end;
Client.Valid := Result;
end;
begin
FClients := nil;
FDefaultClientIndex := -1;
FProfiles := nil;
FDefaultProfileName := '';
SL := TStringList.Create;
with TRegistry.Create do
try
RootKey := HKEY_LOCAL_MACHINE;
if OpenKeyReadOnly(MessageSubsytemKey) then
begin
FMapiInstalled := ReadString('MAPIX') = '1';
FSimpleMapiInstalled := ReadString('MAPI') = '1';
FMapiVersion := ReadString('MAPIXVER');
CloseKey;
end;
FAnyClientInstalled := FMapiInstalled;
if OpenKeyReadOnly(MailClientsKey) then
begin
DefaultValue := ReadString('');
GetKeyNames(SL);
CloseKey;
SetLength(FClients, SL.Count);
for I := 0 to SL.Count - 1 do
begin
FClients[I].RegKeyName := SL[I];
FClients[I].Valid := False;
if OpenKeyReadOnly(MailClientsKey + '\' + SL[I]) then
begin
FClients[I].ClientName := ReadString('');
FClients[I].ClientPath := ReadString('DLLPath');
ExpandEnvironmentVar(FClients[I].ClientPath);
if CheckValid(FClients[I]) then
FAnyClientInstalled := True;
CloseKey;
end;
end;
FDefaultClientIndex := SL.IndexOf(DefaultValue);
FSelectedClientIndex := FDefaultClientIndex;
end;
RootKey := HKEY_CURRENT_USER;
if OpenKeyReadOnly(ProfilesRegKey) then
begin
FDefaultProfileName := ReadString('DefaultProfile');
GetKeyNames(SL);
CloseKey;
SetLength(FProfiles, SL.Count);
for I := 0 to SL.Count - 1 do
FProfiles[I] := SL[I];
end;
finally
Free;
SL.Free;
end;
end;
//------------------------------------------------------------------------------
procedure TJclSimpleMapi.SetClientConnectKind(const Value: TJclMapiClientConnect);
begin
if FClientConnectKind <> Value then
begin
FClientConnectKind := Value;
UnloadClientLib;
end;
end;
//------------------------------------------------------------------------------
procedure TJclSimpleMapi.SetSelectedClientIndex(const Value: Integer);
begin
CheckListIndex(Value, ClientCount);
if FSelectedClientIndex <> Value then
begin
FSelectedClientIndex := Value;
UnloadClientLib;
end;
end;
//------------------------------------------------------------------------------
procedure TJclSimpleMapi.UnloadClientLib;
var
I: Integer;
begin
if ClientLibLoaded then
begin
BeforeUnloadClientLib;
FreeLibrary(FClientLibHandle);
FClientLibHandle := 0;
for I := 0 to Length(FFunctions) - 1 do
FFunctions[I]^ := nil;
end;
end;
//------------------------------------------------------------------------------
function TJclSimpleMapi.UseMapi: Boolean;
begin
case FClientConnectKind of
ctAutomatic:
UseMapi := FSimpleMapiInstalled;
ctMapi:
UseMapi := True;
ctDirect:
UseMapi := False;
else
UseMapi := True;
end;
end;
//==============================================================================
// TJclEmailRecip
//==============================================================================
function TJclEmailRecip.AddressAndName: string;
var
N: string;
begin
if Name = '' then
N := Address
else
N := Name;
Result := Format('"%s" <%s>', [N, Address]);
end;
//------------------------------------------------------------------------------
function TJclEmailRecip.SortingName: string;
begin
if FName = '' then
Result := FAddress
else
Result := FName;
end;
//==============================================================================
// TJclEmailRecips
//==============================================================================
function TJclEmailRecips.Add(const Address, Name: string;
const Kind: TJclEmailRecipKind; const AddressType: string): Integer;
var