Componentes.Terceros.FastRe.../internal/4.2/1/Source/ExportPack/frxSMTP.pas
2007-11-18 19:40:07 +00:00

451 lines
12 KiB
ObjectPascal

{******************************************}
{ }
{ FastReport 4.0 }
{ SMTP connection client unit }
{ }
{ Copyright (c) 2006-2007 }
{ by Alexander Fediachov, }
{ Fast Reports Inc. }
{ }
{******************************************}
unit frxSMTP;
interface
{$I frx.inc}
uses
Windows, SysUtils, Classes, ScktComp, frxNetUtils, frxProgress
{$IFDEF Delphi6}, Variants {$ENDIF};
type
TfrxSMTPClientThread = class;
TfrxSMTPClient = class(TComponent)
private
FActive: Boolean;
FBreaked: Boolean;
FErrors: TStrings;
FHost: String;
FPort: Integer;
FThread: TfrxSMTPClientThread;
FTimeOut: Integer;
FPassword: String;
FMailTo: String;
FUser: String;
FMailFile: String;
FMailFrom: String;
FMailSubject: String;
FMailText: String;
FAnswer: String;
FAccepted: Boolean;
FAuth: String;
FCode: Integer;
FSending: Boolean;
FAttachName: String;
FProgress: TfrxProgress;
FShowProgress: Boolean;
FLogFile: String;
FLog: TStringList;
FAnswerList: TStringList;
F200Flag: Boolean;
F210Flag: Boolean;
F215Flag: Boolean;
FUserName: String;
procedure DoConnect(Sender: TObject; Socket: TCustomWinSocket);
procedure DoDisconnect(Sender: TObject; Socket: TCustomWinSocket);
procedure DoError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure DoRead(Sender: TObject; Socket: TCustomWinSocket);
procedure SetActive(const Value: Boolean);
procedure AddLogIn(const s: String);
procedure AddLogOut(const s: String);
function DomainByEmail(const addr: String): String;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Connect;
procedure Disconnect;
procedure Open;
procedure Close;
property Breaked: Boolean read FBreaked;
property Errors: TStrings read FErrors write Ferrors;
property LogFile: String read FLogFile write FLogFile;
published
property Active: Boolean read FActive write SetActive;
property Host: String read FHost write FHost;
property Port: Integer read FPort write FPort;
property TimeOut: Integer read FTimeOut write FTimeOut;
property UserName: String read FUserName write FUserName;
property User: String read FUser write FUser;
property Password: String read FPassword write FPassword;
property MailFrom: String read FMailFrom write FMailFrom;
property MailTo: String read FMailTo write FMailTo;
property MailSubject: String read FMailSubject write FMailSubject;
property MailText: String read FMailText write FMailText;
property MailFile: String read FMailFile write FMailFile;
property AttachName: String read FAttachName write FAttachName;
property ShowProgress: Boolean read FShowProgress write FShowProgress;
end;
TfrxSMTPClientThread = class (TThread)
protected
FClient: TfrxSMTPClient;
procedure DoOpen;
procedure Execute; override;
public
FSocket: TClientSocket;
constructor Create(Client: TfrxSMTPClient);
destructor Destroy; override;
end;
implementation
uses frxRes, frxrcExports;
const
MIME_STRING_SIZE = 57;
boundary = '----------FastReport';
type
THackThread = class(TThread);
{ TfrxSMTPClient }
constructor TfrxSMTPClient.Create(AOwner: TComponent);
begin
inherited;
FErrors := TStringList.Create;
FHost := '127.0.0.1';
FPort := 25;
FActive := False;
FTimeOut := 60;
FBreaked := False;
FThread := TfrxSMTPClientThread.Create(Self);
FThread.FSocket.OnConnect := DoConnect;
FThread.FSocket.OnRead := DoRead;
FThread.FSocket.OnDisconnect := DoDisconnect;
FThread.FSocket.OnError := DoError;
FAttachName := '';
FShowProgress := False;
FLogFile := '';
FLog := TStringList.Create;
FAnswerList := TStringList.Create;
end;
destructor TfrxSMTPClient.Destroy;
begin
Close;
while FActive do
PMessages;
FThread.Free;
FErrors.Free;
FLog.Free;
FAnswerList.Free;
inherited;
end;
procedure TfrxSMTPClient.Connect;
var
ticks: Cardinal;
begin
FLog.Clear;
if (FLogFile <> '') and FileExists(LogFile) then
FLog.LoadFromFile(LogFile);
FLog.Add(DateTimeToStr(Now));
FErrors.Clear;
FActive := True;
FThread.FSocket.Host := FHost;
FThread.FSocket.Address := FHost;
FThread.FSocket.Port := FPort;
FThread.FSocket.ClientType := ctNonBlocking;
F200Flag := False;
F210Flag := False;
F215Flag := False;
if FShowProgress then
begin
FProgress := TfrxProgress.Create(Self);
FProgress.Execute(100, frxGet(8924) + ' ' + FMailTo, False, True);
end;
FThread.Execute;
try
ticks := GetTickCount;
while FActive and (not FBreaked) do
begin
PMessages;
if FShowProgress then
FProgress.Tick;
if ((GetTickCount - ticks) > Cardinal(FTimeOut * 1000)) then
begin
Errors.Add('Timeout expired (' + IntToStr(FTimeOut) + ')');
break;
end;
if FSending then
ticks := GetTickCount;
Sleep(100);
end;
finally
if FShowProgress then
FProgress.Free;
Disconnect;
end;
FLog.Add('---' + DateTimeToStr(Now));
FLog.AddStrings(FErrors);
if FLogFile <> '' then
FLog.SaveToFile(FLogFile);
end;
procedure TfrxSMTPClient.Disconnect;
begin
FThread.FSocket.Close;
FThread.Terminate;
FActive := False;
end;
procedure TfrxSMTPClient.DoConnect(Sender: TObject;
Socket: TCustomWinSocket);
var
s: String;
begin
s := 'HELO ' + DomainByEmail(FMailFrom) + #13#10;
Socket.SendText(s);
AddLogOut(s);
FCode := 0;
FAuth := FUser;
FAccepted := False;
FSending := False;
end;
procedure TfrxSMTPClient.DoDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
if Pos('221', FAnswer) = 0 then
Errors.Add(FAnswer);
FActive := False;
FSending := False;
end;
procedure TfrxSMTPClient.DoError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin
if FAnswer <> '' then
Errors.Add(FAnswer);
Errors.Add(GetSocketErrorText(ErrorCode));
FActive := False;
ErrorCode := 0;
FSending := False;
end;
procedure TfrxSMTPClient.DoRead(Sender: TObject; Socket: TCustomWinSocket);
var
buf: PChar;
i, k: Integer;
Stream: TMemoryStream;
fbuf: PChar;
FStream: TFileStream;
s: String;
s1: String;
procedure OutStream(const S: String);
begin
Stream.Write(S[1], Length(S));
Stream.Write(#13#10, 2);
end;
begin
i := Socket.ReceiveLength;
GetMem(buf, i);
try
try
i := Socket.ReceiveBuf(buf^, i);
SetLength(FAnswer, i);
CopyMemory(PChar(FAnswer), buf, i);
FAnswerList.Text := FAnswer;
for k := 0 to FAnswerList.Count - 1 do
begin
FAnswer := FAnswerList[k];
FCode := StrToInt(Copy(FAnswer, 1, 3));
AddLogIn(FAnswer);
if (FCode = 235) then
begin
FCode := 220;
FAccepted := True;
end;
if (FUser <> '') and (not FAccepted) and (FCode = 220) then
begin
s := 'AUTH LOGIN'#13#10;
Socket.SendText(s);
AddLogOut(s);
end
else if FCode = 334 then
begin
Socket.SendText(Base64Encode(FAuth) + #13#10);
FAuth := FPassword;
AddLogOut('****');
end
else if (FCode = 220) then
begin
s := 'MAIL FROM: ' + '<' + FMailFrom + '>' + #13#10;
Socket.SendText(s);
AddLogOut(s);
F210Flag := True;
end
else if (FCode = 250) and F210Flag then
begin
s := 'RCPT TO: ' + '<' + FMailTo + '>' + #13#10;
Socket.SendText(s);
AddLogOut(s);
F210Flag := False;
F215Flag := True;
end
else if (FCode = 250) and F215Flag then
begin
s := 'DATA'#13#10;
Socket.SendText(s);
AddLogOut(s);
F215Flag := False;
end
else if (FCode = 250) and F200Flag then
begin
s := 'QUIT'#13#10;
Socket.SendText(s);
AddLogOut(s);
F200Flag := False;
end
else if (FCode = 354) then
begin
FSending := True;
Stream := TMemoryStream.Create;
try
OutStream('Date: ' + DateTimeToRFCDateTime(Now));
OutStream('Subject: ' + FMailSubject);
OutStream('To: ' + FMailTo);
OutStream('From: ' + FMailFrom);
OutStream('X-Mailer: FastReport');
if (FMailFile <> '') and FileExists(FMailFile) then
begin
OutStream('MIME-Version: 1.0');
OutStream('Content-Type: multipart/mixed; boundary="' + boundary +'"');
OutStream(#13#10'--' + boundary);
OutStream('Content-Type: text/plain');
OutStream('Content-Transfer-Encoding: 7bit');
OutStream(#13#10 + FMailText);
OutStream('--' + boundary);
s := GetFileMIMEType(FMailFile);
if FAttachName = '' then
s1 := ExtractFileName(FMailFile)
else
s1 := FAttachName;
OutStream('Content-Type: ' + s + '; name="' + s1 + '"');
OutStream('Content-Transfer-Encoding: base64');
OutStream('Content-Disposition: attachment; filename="' + s1 + '"'#13#10);
FStream := TFileStream.Create(FMailFile, fmOpenRead + fmShareDenyWrite);
GetMem(fbuf, MIME_STRING_SIZE);
try
i := MIME_STRING_SIZE;
while i = MIME_STRING_SIZE do
begin
i := FStream.Read(fbuf^, i);
SetLength(s, i);
CopyMemory(PChar(s), fbuf, i);
s := Base64Encode(s);
OutStream(s);
end;
finally
FreeMem(fbuf);
FStream.Free;
end;
OutStream(#13#10 + '--' + boundary + '--');
end
else
OutStream(#13#10 + FMailText);
OutStream('.');
AddLogOut('message(skipped)');
Socket.SendBuf(Stream.Memory^, Stream.Size);
F200Flag := True;
finally
FSending := False;
Stream.Free;
end;
end;
end;
except
on e: Exception do
Errors.Add('Data receive error: ' + e.Message)
end;
finally
FreeMem(buf);
end;
end;
procedure TfrxSMTPClient.SetActive(const Value: Boolean);
begin
if Value then Connect
else Disconnect;
end;
procedure TfrxSMTPClient.Close;
begin
FBreaked := True;
Active := False;
end;
procedure TfrxSMTPClient.Open;
begin
Active := True;
end;
function TfrxSMTPClient.DomainByEmail(const addr: String): String;
var
i: Integer;
begin
i := Pos('@', addr);
if i > 0 then
Result := Copy(addr, i + 1, Length(addr) - i)
else
Result := addr;
end;
procedure TfrxSMTPClient.AddLogIn(const s: String);
begin
FLog.Add('<' + s);
end;
procedure TfrxSMTPClient.AddLogOut(const s: String);
begin
FLog.Add('>' + s);
end;
{ TfrxSMTPClientThread }
constructor TfrxSMTPClientThread.Create(Client: TfrxSMTPClient);
begin
inherited Create(True);
FClient := Client;
FreeOnTerminate := False;
FSocket := TClientSocket.Create(nil);
end;
destructor TfrxSMTPClientThread.Destroy;
begin
FSocket.Free;
inherited;
end;
procedure TfrxSMTPClientThread.DoOpen;
begin
FSocket.Open;
end;
procedure TfrxSMTPClientThread.Execute;
begin
Synchronize(DoOpen);
end;
end.