557 lines
17 KiB
Plaintext
557 lines
17 KiB
Plaintext
|
{
|
||
|
$Project$
|
||
|
$Workfile$
|
||
|
$Revision$
|
||
|
$DateUTC$
|
||
|
$Id$
|
||
|
|
||
|
This file is part of the Indy (Internet Direct) project, and is offered
|
||
|
under the dual-licensing agreement described on the Indy website.
|
||
|
(http://www.indyproject.org/)
|
||
|
|
||
|
Copyright:
|
||
|
(c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
|
||
|
}
|
||
|
{
|
||
|
$Log$
|
||
|
}
|
||
|
{
|
||
|
Rev 1.19 10/01/2005 16:31:20 ANeillans
|
||
|
Minor bug fix for Exim compatibility.
|
||
|
|
||
|
Rev 1.18 11/27/04 3:03:22 AM RLebeau
|
||
|
Bug fix for 'STARTTLS' response handling
|
||
|
|
||
|
Rev 1.17 6/11/2004 9:38:44 AM DSiders
|
||
|
Added "Do not Localize" comments.
|
||
|
|
||
|
Rev 1.16 2004.02.03 5:45:46 PM czhower
|
||
|
Name changes
|
||
|
|
||
|
Rev 1.15 2004.02.03 2:12:18 PM czhower
|
||
|
$I path change
|
||
|
|
||
|
Rev 1.14 1/28/2004 8:08:10 PM JPMugaas
|
||
|
Fixed a bug in SendGreeting. It was sending an invalid EHLO and probably an
|
||
|
invalid HELO command. The problem is that it was getting the computer name.
|
||
|
There's an issue in NET with that which Kudzu commented on in IdGlobal.
|
||
|
Thus, "EHLO<space>" and probably "HELO<space>" were being sent causing a
|
||
|
failure. The fix is to to try to get the local computer's DNS name with
|
||
|
GStack.HostName. We only use the computer name if GStack.HostName fails.
|
||
|
|
||
|
Rev 1.13 1/25/2004 3:11:48 PM JPMugaas
|
||
|
SASL Interface reworked to make it easier for developers to use.
|
||
|
SSL and SASL reenabled components.
|
||
|
|
||
|
Rev 1.12 2004.01.22 10:29:58 PM czhower
|
||
|
Now supports default login mechanism with just username and pw.
|
||
|
|
||
|
Rev 1.11 1/21/2004 4:03:24 PM JPMugaas
|
||
|
InitComponent
|
||
|
|
||
|
Rev 1.10 22/12/2003 00:46:36 CCostelloe
|
||
|
.NET fixes
|
||
|
|
||
|
Rev 1.9 4/12/2003 10:24:08 PM GGrieve
|
||
|
Fix to Compile
|
||
|
|
||
|
Rev 1.8 25/11/2003 12:24:22 PM SGrobety
|
||
|
various IdStream fixes with ReadLn/D6
|
||
|
|
||
|
Rev 1.7 10/17/2003 1:02:56 AM DSiders
|
||
|
Added localization comments.
|
||
|
|
||
|
Rev 1.6 2003.10.14 1:31:16 PM czhower
|
||
|
DotNet
|
||
|
|
||
|
Rev 1.5 2003.10.12 6:36:42 PM czhower
|
||
|
Now compiles.
|
||
|
|
||
|
Rev 1.4 10/11/2003 7:14:36 PM BGooijen
|
||
|
Changed IdCompilerDefines.inc path
|
||
|
|
||
|
Rev 1.3 10/10/2003 10:45:12 PM BGooijen
|
||
|
DotNet
|
||
|
|
||
|
Rev 1.2 2003.10.02 9:27:54 PM czhower
|
||
|
DotNet Excludes
|
||
|
|
||
|
Rev 1.1 6/15/2003 03:28:24 PM JPMugaas
|
||
|
Minor class change.
|
||
|
|
||
|
Rev 1.0 6/15/2003 01:06:48 PM JPMugaas
|
||
|
TIdSMTP and TIdDirectSMTP now share common code in this base class.
|
||
|
}
|
||
|
|
||
|
unit IdSMTPBase;
|
||
|
|
||
|
interface
|
||
|
|
||
|
{$i IdCompilerDefines.inc}
|
||
|
|
||
|
uses
|
||
|
IdGlobal,
|
||
|
IdEMailAddress,
|
||
|
IdMessage,
|
||
|
IdMessageClient,
|
||
|
IdReply,
|
||
|
IdTCPClient;
|
||
|
|
||
|
//default property values
|
||
|
const
|
||
|
DEF_SMTP_PIPELINE = False;
|
||
|
IdDEF_UseEhlo = True; //APR: default behavior
|
||
|
IdDEF_UseVerp = False;
|
||
|
|
||
|
const
|
||
|
CAPAPIPELINE = 'PIPELINING'; {do not localize}
|
||
|
CAPAVERP = 'VERP'; {do not localize}
|
||
|
XMAILER_HEADER = 'X-Mailer'; {do not localize}
|
||
|
|
||
|
const
|
||
|
RCPTTO_ACCEPT : array [0..1] of Int16 = (250, 251);
|
||
|
MAILFROM_ACCEPT : Int16 = 250;
|
||
|
DATA_ACCEPT : Int16 = 354;
|
||
|
DATA_PERIOD_ACCEPT : Int16 = 250;
|
||
|
RSET_ACCEPT : Int16 = 250;
|
||
|
|
||
|
const
|
||
|
RSET_CMD = 'RSET'; {do not localize}
|
||
|
MAILFROM_CMD = 'MAIL FROM:'; {do not localize}
|
||
|
RCPTTO_CMD = 'RCPT TO:'; {do not localize}
|
||
|
DATA_CMD = 'DATA'; {do not localize}
|
||
|
|
||
|
type
|
||
|
TIdSMTPFailedRecipient = procedure(Sender: TObject; const AAddress, ACode, AText: String;
|
||
|
var VContinue: Boolean) of object;
|
||
|
|
||
|
TIdSMTPBase = class(TIdMessageClient)
|
||
|
protected
|
||
|
FMailAgent: string;
|
||
|
FHeloName : String;
|
||
|
FPipeline : Boolean;
|
||
|
FUseEhlo : Boolean;
|
||
|
FUseVerp : Boolean;
|
||
|
FVerpDelims: string;
|
||
|
FOnFailedRecipient: TIdSMTPFailedRecipient;
|
||
|
//
|
||
|
function GetSupportsTLS : Boolean; override;
|
||
|
function GetReplyClass: TIdReplyClass; override;
|
||
|
procedure InitComponent; override;
|
||
|
procedure SendGreeting;
|
||
|
procedure SetUseEhlo(const AValue: Boolean); virtual;
|
||
|
procedure SetPipeline(const AValue: Boolean);
|
||
|
procedure StartTLS;
|
||
|
|
||
|
function FailedRecipientCanContinue(const AAddress: string): Boolean;
|
||
|
//No pipeline send methods
|
||
|
function WriteRecipientNoPipelining(const AEmailAddress: TIdEmailAddressItem): Boolean;
|
||
|
procedure WriteRecipientsNoPipelining(AList: TIdEmailAddressList);
|
||
|
procedure SendNoPipelining(AMsg: TIdMessage; const AFrom: String; ARecipients: TIdEMailAddressList);
|
||
|
//pipeline send methods
|
||
|
procedure WriteRecipientPipeLine(const AEmailAddress: TIdEmailAddressItem);
|
||
|
procedure WriteRecipientsPipeLine(AList: TIdEmailAddressList);
|
||
|
procedure SendPipelining(AMsg: TIdMessage; const AFrom: String; ARecipients: TIdEMailAddressList);
|
||
|
//
|
||
|
procedure InternalSend(AMsg: TIdMessage; const AFrom: String; ARecipients: TIdEMailAddressList); virtual;
|
||
|
public
|
||
|
procedure Send(AMsg: TIdMessage); overload; virtual;
|
||
|
procedure Send(AMsg: TIdMessage; ARecipients: TIdEMailAddressList); overload; virtual;
|
||
|
procedure Send(AMsg: TIdMessage; const AFrom: string); overload; virtual;
|
||
|
procedure Send(AMsg: TIdMessage; ARecipients: TIdEMailAddressList; const AFrom: string); overload; virtual;
|
||
|
published
|
||
|
property MailAgent: string read FMailAgent write FMailAgent;
|
||
|
property HeloName : string read FHeloName write FHeloName;
|
||
|
property UseEhlo: Boolean read FUseEhlo write SetUseEhlo default IdDEF_UseEhlo;
|
||
|
property PipeLine : Boolean read FPipeLine write SetPipeline default DEF_SMTP_PIPELINE;
|
||
|
property UseVerp: Boolean read FUseVerp write FUseVerp default IdDEF_UseVerp;
|
||
|
property VerpDelims: string read FVerpDelims write FVerpDelims;
|
||
|
//
|
||
|
property OnFailedRecipient: TIdSMTPFailedRecipient read FOnFailedRecipient write FOnFailedRecipient;
|
||
|
end;
|
||
|
|
||
|
implementation
|
||
|
|
||
|
uses
|
||
|
{$IFDEF VCL_XE3_OR_ABOVE}
|
||
|
System.Classes,
|
||
|
{$ENDIF}
|
||
|
IdAssignedNumbers, IdException,
|
||
|
IdExplicitTLSClientServerBase,
|
||
|
IdGlobalProtocols, IdIOHandler, IdReplySMTP,
|
||
|
IdSSL,
|
||
|
IdStack, SysUtils; //required as we need to get the local computer DNS hostname.
|
||
|
|
||
|
{ TIdSMTPBase }
|
||
|
|
||
|
function TIdSMTPBase.GetReplyClass:TIdReplyClass;
|
||
|
begin
|
||
|
Result := TIdReplySMTP;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSMTPBase.InitComponent;
|
||
|
begin
|
||
|
inherited InitComponent;
|
||
|
FImplicitTLSProtPort := IdPORT_ssmtp;
|
||
|
FRegularProtPort := IdPORT_SMTP;
|
||
|
FPipeLine := DEF_SMTP_PIPELINE;
|
||
|
FUseEhlo := IdDEF_UseEhlo;
|
||
|
FUseVerp := IdDEF_UseVerp;
|
||
|
FMailAgent := '';
|
||
|
FHeloName := '';
|
||
|
Port := IdPORT_SMTP;
|
||
|
end;
|
||
|
|
||
|
function TIdSMTPBase.GetSupportsTLS: Boolean;
|
||
|
begin
|
||
|
Result := ( FCapabilities.IndexOf('STARTTLS') > -1 ); //do not localize
|
||
|
end;
|
||
|
|
||
|
procedure TIdSMTPBase.SendGreeting;
|
||
|
var
|
||
|
LNameToSend : String;
|
||
|
begin
|
||
|
Capabilities.Clear;
|
||
|
if HeloName <> '' then begin
|
||
|
LNameToSend := HeloName;
|
||
|
end else begin
|
||
|
//Note: IndyComputerName gets the computer name.
|
||
|
//This is not always reliable in Indy because in Dot.NET,
|
||
|
//it is done with This is available through System.Windows.Forms.SystemInformation.ComputerName
|
||
|
//and that requires that we link to a problematic dependancy (Wystem.Windows.Forms).
|
||
|
//Besides, I think RFC 821 was refering to the computer's Internet
|
||
|
//DNS name. We use the Computer name only if we can't get the DNS name.
|
||
|
LNameToSend := GStack.HostName;
|
||
|
if LNameToSend = '' then
|
||
|
begin
|
||
|
LNameToSend := IndyComputerName;
|
||
|
end;
|
||
|
end;
|
||
|
if UseEhlo and (SendCmd('EHLO ' + LNameToSend ) = 250) then begin //APR: user can prevent EHLO {Do not Localize}
|
||
|
Capabilities.AddStrings(LastCmdResult.Text);
|
||
|
if Capabilities.Count > 0 then begin
|
||
|
//we drop the initial greeting. We only want the feature list
|
||
|
Capabilities.Delete(0);
|
||
|
end;
|
||
|
end else begin
|
||
|
SendCmd('HELO ' + LNameToSend, 250); {Do not Localize}
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSMTPBase.SetPipeline(const AValue: Boolean);
|
||
|
begin
|
||
|
FPipeLine := AValue;
|
||
|
if AValue then begin
|
||
|
FUseEhlo := True;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSMTPBase.SetUseEhlo(const AValue: Boolean);
|
||
|
begin
|
||
|
FUseEhlo := AValue;
|
||
|
if not AValue then
|
||
|
begin
|
||
|
FPipeLine := False;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSMTPBase.SendNoPipelining(AMsg: TIdMessage; const AFrom: String; ARecipients: TIdEMailAddressList);
|
||
|
var
|
||
|
LCmd: string;
|
||
|
begin
|
||
|
LCmd := MAILFROM_CMD + '<' + AFrom + '>'; {Do not Localize}
|
||
|
if FUseVerp then begin
|
||
|
if Capabilities.IndexOf(CAPAVERP) > -1 then begin
|
||
|
LCmd := LCmd + ' VERP'; {Do not Localize}
|
||
|
end else begin
|
||
|
LCmd := LCmd + ' XVERP'; {Do not Localize}
|
||
|
end;
|
||
|
if FVerpDelims <> '' then begin
|
||
|
LCmd := LCmd + '=' + FVerpDelims; {Do not Localize}
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
// RLebeau 4/29/2013: DO NOT send a RSET command before the MAIL FROM command!
|
||
|
// Some servers are buggy and will reset the entire session, including any
|
||
|
// previously accepted authentication, when they are supposed to reset only
|
||
|
// their mail sending buffers and nothing else. Send a RSET only if the mail
|
||
|
// transaction fails and needs to be cleaned up...
|
||
|
// TODO: make this configurable?
|
||
|
|
||
|
//SendCmd(RSET_CMD);
|
||
|
SendCmd(LCmd, MAILFROM_ACCEPT);
|
||
|
try
|
||
|
WriteRecipientsNoPipelining(ARecipients);
|
||
|
SendCmd(DATA_CMD, DATA_ACCEPT);
|
||
|
SendMsg(AMsg);
|
||
|
SendCmd('.', DATA_PERIOD_ACCEPT); {Do not Localize}
|
||
|
except
|
||
|
on E: EIdSMTPReplyError do begin
|
||
|
SendCmd(RSET_CMD);
|
||
|
raise;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSMTPBase.SendPipelining(AMsg: TIdMessage; const AFrom: String; ARecipients: TIdEMailAddressList);
|
||
|
var
|
||
|
LError : TIdReplySMTP;
|
||
|
I, LFailedRecips : Integer;
|
||
|
LCmd: string;
|
||
|
LBufferingStarted: Boolean;
|
||
|
|
||
|
function SetupErrorReply: TIdReplySMTP;
|
||
|
begin
|
||
|
Result := TIdReplySMTP.Create(nil);
|
||
|
Result.Assign(LastCmdResult);
|
||
|
end;
|
||
|
|
||
|
begin
|
||
|
LError := nil;
|
||
|
LCmd := MAILFROM_CMD + '<' + AFrom + '>'; {Do not Localize}
|
||
|
if FUseVerp then begin
|
||
|
if Capabilities.IndexOf(CAPAVERP) > -1 then begin
|
||
|
LCmd := LCmd + ' VERP'; {Do not Localize}
|
||
|
end else begin
|
||
|
LCmd := LCmd + ' XVERP'; {Do not Localize}
|
||
|
end;
|
||
|
if FVerpDelims <> '' then begin
|
||
|
LCmd := LCmd + '=' + FVerpDelims; {Do not Localize}
|
||
|
end;
|
||
|
end;
|
||
|
try
|
||
|
LBufferingStarted := not IOHandler.WriteBufferingActive;
|
||
|
if LBufferingStarted then begin
|
||
|
IOHandler.WriteBufferOpen;
|
||
|
end;
|
||
|
|
||
|
// RLebeau 4/29/2013: DO NOT send a RSET command before the MAIL FROM command!
|
||
|
// Some servers are buggy and will reset the entire session, including any
|
||
|
// previously accepted authentication, when they are supposed to reset only
|
||
|
// their mail sending buffers and nothing else. Send a RSET only if the mail
|
||
|
// transaction fails and needs to be cleaned up...
|
||
|
// TODO: make this configurable?
|
||
|
|
||
|
try
|
||
|
//IOHandler.WriteLn(RSET_CMD);
|
||
|
IOHandler.WriteLn(LCmd);
|
||
|
WriteRecipientsPipeLine(ARecipients);
|
||
|
IOHandler.WriteLn(DATA_CMD);
|
||
|
if LBufferingStarted then begin
|
||
|
IOHandler.WriteBufferClose;
|
||
|
end;
|
||
|
except
|
||
|
if LBufferingStarted then begin
|
||
|
IOHandler.WriteBufferCancel;
|
||
|
end;
|
||
|
raise;
|
||
|
end;
|
||
|
{
|
||
|
//RSET
|
||
|
if PosInSmallIntArray(GetResponse, RSET_ACCEPT) = -1 then begin
|
||
|
LError := SetupErrorReply;
|
||
|
end;
|
||
|
}
|
||
|
//MAIL FROM:
|
||
|
if PosInSmallIntArray(GetResponse, MAILFROM_ACCEPT) = -1 then begin
|
||
|
if not Assigned(LError) then begin
|
||
|
LError := SetupErrorReply;
|
||
|
end;
|
||
|
end;
|
||
|
//RCPT TO:
|
||
|
if ARecipients.Count > 0 then begin
|
||
|
LFailedRecips := 0;
|
||
|
for I := 0 to ARecipients.Count - 1 do begin
|
||
|
if PosInSmallIntArray(GetResponse, RCPTTO_ACCEPT) = -1 then begin
|
||
|
Inc(LFailedRecips);
|
||
|
if not FailedRecipientCanContinue(ARecipients[I].Address) then begin
|
||
|
if not Assigned(LError) then begin
|
||
|
LError := SetupErrorReply;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
if not Assigned(LError) and (LFailedRecips = ARecipients.Count) then begin
|
||
|
LError := SetupErrorReply;
|
||
|
end;
|
||
|
end;
|
||
|
//DATA - last in the batch
|
||
|
if PosInSmallIntArray(GetResponse, DATA_ACCEPT) <> -1 then begin
|
||
|
SendMsg(AMsg);
|
||
|
if PosInSmallIntArray(SendCmd('.'), DATA_PERIOD_ACCEPT) = -1 then begin {Do not Localize}
|
||
|
if not Assigned(LError) then begin
|
||
|
LError := SetupErrorReply;
|
||
|
end;
|
||
|
end;
|
||
|
end else begin
|
||
|
if not Assigned(LError) then begin
|
||
|
LError := SetupErrorReply;
|
||
|
end;
|
||
|
end;
|
||
|
if Assigned(LError) then begin
|
||
|
SendCmd(RSET_CMD);
|
||
|
LError.RaiseReplyError;
|
||
|
end;
|
||
|
finally
|
||
|
FreeAndNil(LError);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSMTPBase.StartTLS;
|
||
|
var
|
||
|
LIO : TIdSSLIOHandlerSocketBase;
|
||
|
LSendQuitOnError: Boolean;
|
||
|
begin
|
||
|
LSendQuitOnError := True;
|
||
|
try
|
||
|
if (IOHandler is TIdSSLIOHandlerSocketBase) and (FUseTLS <> utNoTLSSupport) then
|
||
|
begin
|
||
|
LIO := TIdSSLIOHandlerSocketBase(IOHandler);
|
||
|
//we check passthrough because we can either be using TLS currently with
|
||
|
//implicit TLS support or because STARTLS was issued previously.
|
||
|
if LIO.PassThrough then
|
||
|
begin
|
||
|
if SupportsTLS then
|
||
|
begin
|
||
|
if SendCmd('STARTTLS') = 220 then begin {do not localize}
|
||
|
LSendQuitOnError := False;
|
||
|
TLSHandshake;
|
||
|
//send EHLO
|
||
|
SendGreeting;
|
||
|
end else begin
|
||
|
ProcessTLSNegCmdFailed;
|
||
|
end;
|
||
|
end else begin
|
||
|
ProcessTLSNotAvail;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
except
|
||
|
Disconnect(LSendQuitOnError); // RLebeau: do not send the QUIT command if the handshake was started
|
||
|
Raise;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdSMTPBase.FailedRecipientCanContinue(const AAddress: string): Boolean;
|
||
|
begin
|
||
|
Result := Assigned(FOnFailedRecipient);
|
||
|
if Result then begin
|
||
|
FOnFailedRecipient(Self, AAddress, LastCmdResult.Code, LastCmdResult.Text.Text, Result);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
function TIdSMTPBase.WriteRecipientNoPipelining(const AEmailAddress: TIdEmailAddressItem): Boolean;
|
||
|
var
|
||
|
LReply: Int16;
|
||
|
begin
|
||
|
LReply := SendCmd(RCPTTO_CMD + '<' + AEMailAddress.Address + '>'); {do not localize}
|
||
|
Result := PosInSmallIntArray(LReply, RCPTTO_ACCEPT) <> -1;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSMTPBase.WriteRecipientPipeLine(const AEmailAddress: TIdEmailAddressItem);
|
||
|
begin
|
||
|
//we'll read the reply - LATER
|
||
|
IOHandler.WriteLn(RCPTTO_CMD + '<' + AEMailAddress.Address + '>');
|
||
|
end;
|
||
|
|
||
|
procedure TIdSMTPBase.WriteRecipientsNoPipelining(AList: TIdEmailAddressList);
|
||
|
var
|
||
|
I, LFailedRecips: Integer;
|
||
|
LContinue: Boolean;
|
||
|
begin
|
||
|
if AList.Count > 0 then begin
|
||
|
LFailedRecips := 0;
|
||
|
LContinue := True;
|
||
|
for I := 0 to AList.Count - 1 do begin
|
||
|
if not WriteRecipientNoPipelining(AList[I]) then begin
|
||
|
Inc(LFailedRecips);
|
||
|
if not FailedRecipientCanContinue(AList[I].Address) then begin
|
||
|
LContinue := False;
|
||
|
Break;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
if (not LContinue) or (LFailedRecips = AList.Count) then begin
|
||
|
LastCmdResult.RaiseReplyError;
|
||
|
end;
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSMTPBase.WriteRecipientsPipeLine(AList: TIdEmailAddressList);
|
||
|
var
|
||
|
I: integer;
|
||
|
begin
|
||
|
for I := 0 to AList.Count - 1 do begin
|
||
|
WriteRecipientPipeLine(AList[I]);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
procedure TIdSMTPBase.InternalSend(AMsg: TIdMessage; const AFrom: String; ARecipients: TIdEMailAddressList);
|
||
|
begin
|
||
|
if Pipeline and (Capabilities.IndexOf(CAPAPIPELINE) > -1) then begin
|
||
|
SendPipelining(AMsg, AFrom, ARecipients);
|
||
|
end else begin
|
||
|
SendNoPipelining(AMsg, AFrom, ARecipients);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
// this version of Send() uses the TIdMessage to determine both the
|
||
|
// sender and the recipients...
|
||
|
procedure TIdSMTPBase.Send(AMsg: TIdMessage);
|
||
|
var
|
||
|
LRecipients: TIdEMailAddressList;
|
||
|
begin
|
||
|
LRecipients := TIdEMailAddressList.Create(Self);
|
||
|
try
|
||
|
LRecipients.AddItems(AMsg.Recipients);
|
||
|
LRecipients.AddItems(AMsg.CCList);
|
||
|
LRecipients.AddItems(AMsg.BccList);
|
||
|
Send(AMsg, LRecipients);
|
||
|
finally
|
||
|
FreeAndNil(LRecipients);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
// this version of Send() uses the TIdMessage to determine the
|
||
|
// sender, but sends to the caller's specified recipients
|
||
|
procedure TIdSMTPBase.Send(AMsg: TIdMessage; ARecipients: TIdEMailAddressList);
|
||
|
var
|
||
|
LSender: string;
|
||
|
begin
|
||
|
LSender := Trim(AMsg.Sender.Address);
|
||
|
if LSender = '' then begin
|
||
|
LSender := Trim(AMsg.From.Address);
|
||
|
end;
|
||
|
InternalSend(AMsg, LSender, ARecipients);
|
||
|
end;
|
||
|
|
||
|
// this version of Send() uses the TIdMessage to determine the
|
||
|
// recipients, but sends using the caller's specified sender.
|
||
|
// The sender can be empty, which is useful for server-generated
|
||
|
// error messages...
|
||
|
procedure TIdSMTPBase.Send(AMsg: TIdMessage; const AFrom: string);
|
||
|
var
|
||
|
LRecipients: TIdEMailAddressList;
|
||
|
begin
|
||
|
LRecipients := TIdEMailAddressList.Create(Self);
|
||
|
try
|
||
|
LRecipients.AddItems(AMsg.Recipients);
|
||
|
LRecipients.AddItems(AMsg.CCList);
|
||
|
LRecipients.AddItems(AMsg.BccList);
|
||
|
Send(AMsg, LRecipients, AFrom);
|
||
|
finally
|
||
|
FreeAndNil(LRecipients);
|
||
|
end;
|
||
|
end;
|
||
|
|
||
|
// this version of Send() uses the caller's specified sender and
|
||
|
// recipients. The sender can be empty, which is useful for
|
||
|
// server-generated error messages...
|
||
|
procedure TIdSMTPBase.Send(AMsg: TIdMessage; ARecipients: TIdEMailAddressList;
|
||
|
const AFrom: string);
|
||
|
begin
|
||
|
InternalSend(AMsg, AFrom, ARecipients);
|
||
|
end;
|
||
|
|
||
|
end.
|