lsmtp.pp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. { lNet SMTP unit
  2. CopyRight (C) 2005-2008 Ales Katona
  3. This library is Free software; you can rediStribute it and/or modify it
  4. under the terms of the GNU Library General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or (at your
  6. option) any later version.
  7. This program is diStributed in the hope that it will be useful, but WITHOUT
  8. ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
  9. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  10. for more details.
  11. You should have received a Copy of the GNU Library General Public License
  12. along with This library; if not, Write to the Free Software Foundation,
  13. Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  14. This license has been modified. See File LICENSE.ADDON for more inFormation.
  15. Should you find these sources without a LICENSE File, please contact
  16. me at [email protected]
  17. }
  18. unit lsmtp;
  19. {$mode objfpc}{$H+}
  20. {$inline on}
  21. interface
  22. uses
  23. Classes, SysUtils, Contnrs, Base64,
  24. lNet, lEvents, lCommon, lMimeWrapper, lMimeStreams;
  25. type
  26. TLSMTP = class;
  27. TLSMTPClient = class;
  28. TLSMTPStatus = (ssNone, ssCon, ssHelo, ssEhlo, ssAuthLogin, ssAuthPlain,
  29. ssStartTLS, ssMail, ssRcpt, ssData, ssRset, ssQuit, ssLast);
  30. TLSMTPStatusSet = set of TLSMTPStatus;
  31. TLSMTPStatusRec = record
  32. Status: TLSMTPStatus;
  33. Args: array[1..2] of string;
  34. end;
  35. { TLSMTPStatusFront }
  36. {$DEFINE __front_type__ := TLSMTPStatusRec}
  37. {$i lcontainersh.inc}
  38. TLSMTPStatusFront = TLFront;
  39. TLSMTPClientStatusEvent = procedure (aSocket: TLSocket;
  40. const aStatus: TLSMTPStatus) of object;
  41. { TMail }
  42. TMail = class
  43. protected
  44. FMailText: string;
  45. FMailStream: TMimeStream;
  46. FRecipients: string;
  47. FSender: string;
  48. FSubject: string;
  49. function GetCount: Integer;
  50. function GetSection(i: Integer): TMimeSection;
  51. procedure SetSection(i: Integer; const AValue: TMimeSection);
  52. public
  53. constructor Create;
  54. destructor Destroy; override;
  55. procedure AddTextSection(const aText: string; const aCharSet: string = 'UTF-8');
  56. procedure AddFileSection(const aFileName: string);
  57. procedure AddStreamSection(aStream: TStream; const FreeStream: Boolean = False);
  58. procedure DeleteSection(const i: Integer);
  59. procedure RemoveSection(aSection: TMimeSection);
  60. procedure Reset;
  61. public
  62. property MailText: string read FMailText write FMailText; deprecated; // use sections!
  63. property Sender: string read FSender write FSender;
  64. property Recipients: string read FRecipients write FRecipients;
  65. property Subject: string read FSubject write FSubject;
  66. property Sections[i: Integer]: TMimeSection read GetSection write SetSection; default;
  67. property SectionCount: Integer read GetCount;
  68. end;
  69. TLSMTP = class(TLComponent)
  70. protected
  71. FConnection: TLTcp;
  72. FFeatureList: TStringList;
  73. protected
  74. function GetTimeout: Integer;
  75. procedure SetTimeout(const AValue: Integer);
  76. function GetSession: TLSession;
  77. procedure SetSession(const AValue: TLSession);
  78. procedure SetCreator(AValue: TLComponent); override;
  79. function GetConnected: Boolean;
  80. function GetSocketClass: TLSocketClass;
  81. procedure SetSocketClass(const AValue: TLSocketClass);
  82. function GetEventer: TLEventer;
  83. procedure SetEventer(Value: TLEventer);
  84. public
  85. constructor Create(aOwner: TComponent); override;
  86. destructor Destroy; override;
  87. function HasFeature(aFeature: string): Boolean;
  88. public
  89. property Connected: Boolean read GetConnected;
  90. property Connection: TLTcp read FConnection;
  91. property SocketClass: TLSocketClass read GetSocketClass write SetSocketClass;
  92. property Eventer: TLEventer read GetEventer write SetEventer;
  93. property Timeout: Integer read GetTimeout write SetTimeout;
  94. property Session: TLSession read GetSession write SetSession;
  95. property FeatureList: TStringList read FFeatureList;
  96. end;
  97. { TLSMTPClient }
  98. TLSMTPClient = class(TLSMTP, ILClient)
  99. protected
  100. FStatus: TLSMTPStatusFront;
  101. FCommandFront: TLSMTPStatusFront;
  102. FPipeLine: Boolean;
  103. FAuthStep: Integer;
  104. FOnConnect: TLSocketEvent;
  105. FOnReceive: TLSocketEvent;
  106. FOnDisconnect: TLSocketEvent;
  107. FOnSuccess: TLSMTPClientStatusEvent;
  108. FOnFailure: TLSMTPClientStatusEvent;
  109. FOnError: TLSocketErrorEvent;
  110. FOnSent: TLSocketProgressEvent;
  111. FSL: TStringList;
  112. FStatusSet: TLSMTPStatusSet;
  113. FBuffer: string;
  114. FDataBuffer: string; // intermediate wait buffer on DATA command
  115. FTempBuffer: string; // used independently from FBuffer for feature list
  116. FCharCount: Integer; // count of chars from last CRLF
  117. FStream: TStream;
  118. protected
  119. procedure OnEr(const msg: string; aSocket: TLSocket);
  120. procedure OnRe(aSocket: TLSocket);
  121. procedure OnCo(aSocket: TLSocket);
  122. procedure OnDs(aSocket: TLSocket);
  123. procedure OnCs(aSocket: TLSocket);
  124. protected
  125. function CanContinue(const aStatus: TLSMTPStatus; const Arg1, Arg2: string): Boolean;
  126. function CleanInput(var s: string): Integer;
  127. procedure EvaluateServer;
  128. procedure EvaluateFeatures;
  129. procedure EvaluateAnswer(const Ans: string);
  130. procedure ExecuteFrontCommand;
  131. procedure AddToBuffer(s: string);
  132. procedure SendData(const FromStream: Boolean = False);
  133. function EncodeBase64(const s: string): string;
  134. public
  135. constructor Create(aOwner: TComponent); override;
  136. destructor Destroy; override;
  137. function Connect(const aHost: string; const aPort: Word = 25): Boolean; virtual; overload;
  138. function Connect: Boolean; virtual; overload;
  139. function Get(var aData; const aSize: Integer; aSocket: TLSocket = nil): Integer; virtual;
  140. function GetMessage(out msg: string; aSocket: TLSocket = nil): Integer; virtual;
  141. procedure SendMail(From, Recipients, Subject, Msg: string);
  142. procedure SendMail(From, Recipients, Subject: string; aStream: TStream);
  143. procedure SendMail(aMail: TMail);
  144. procedure Helo(aHost: string = '');
  145. procedure Ehlo(aHost: string = '');
  146. procedure StartTLS;
  147. procedure AuthLogin(aName, aPass: string);
  148. procedure AuthPlain(aName, aPass: string);
  149. procedure Mail(const From: string);
  150. procedure Rcpt(const RcptTo: string);
  151. procedure Data(const Msg: string);
  152. procedure Rset;
  153. procedure Quit;
  154. procedure Disconnect(const Forced: Boolean = True); override;
  155. procedure CallAction; override;
  156. public
  157. property PipeLine: Boolean read FPipeLine write FPipeLine;
  158. property StatusSet: TLSMTPStatusSet read FStatusSet write FStatusSet;
  159. property OnConnect: TLSocketEvent read FOnConnect write FOnConnect;
  160. property OnReceive: TLSocketEvent read FOnReceive write FOnReceive;
  161. property OnDisconnect: TLSocketEvent read FOnDisconnect write FOnDisconnect;
  162. property OnSuccess: TLSMTPClientStatusEvent read FOnSuccess write FOnSuccess;
  163. property OnFailure: TLSMTPClientStatusEvent read FOnFailure write FOnFailure;
  164. property OnError: TLSocketErrorEvent read FOnError write FOnError;
  165. property OnSent: TLSocketProgressEvent read FOnSent write FOnSent;
  166. end;
  167. implementation
  168. const
  169. EMPTY_REC: TLSMTPStatusRec = (Status: ssNone; Args: ('', ''));
  170. {$i lcontainers.inc}
  171. function StatusToStr(const aStatus: TLSMTPStatus): string;
  172. const
  173. STATAR: array[ssNone..ssLast] of string = ('ssNone', 'ssCon', 'ssHelo', 'ssEhlo',
  174. 'ssStartTLS', 'ssAuthLogin', 'ssAuthPlain',
  175. 'ssMail', 'ssRcpt', 'ssData', 'ssRset', 'ssQuit', 'ssLast');
  176. begin
  177. Result := STATAR[aStatus];
  178. end;
  179. function MakeStatusRec(const aStatus: TLSMTPStatus; const Arg1, Arg2: string): TLSMTPStatusRec;
  180. begin
  181. Result.Status := aStatus;
  182. Result.Args[1] := Arg1;
  183. Result.Args[2] := Arg2;
  184. end;
  185. { TLSMTP }
  186. function TLSMTP.GetSession: TLSession;
  187. begin
  188. Result := FConnection.Session;
  189. end;
  190. procedure TLSMTP.SetSession(const AValue: TLSession);
  191. begin
  192. FConnection.Session := aValue;
  193. end;
  194. procedure TLSMTP.SetCreator(AValue: TLComponent);
  195. begin
  196. inherited SetCreator(AValue);
  197. FConnection.Creator := AValue;
  198. end;
  199. function TLSMTP.GetTimeout: Integer;
  200. begin
  201. Result := FConnection.Timeout;
  202. end;
  203. procedure TLSMTP.SetTimeout(const AValue: Integer);
  204. begin
  205. FConnection.Timeout := aValue;
  206. end;
  207. function TLSMTP.GetConnected: Boolean;
  208. begin
  209. Result := FConnection.Connected;
  210. end;
  211. function TLSMTP.GetSocketClass: TLSocketClass;
  212. begin
  213. Result := FConnection.SocketClass;
  214. end;
  215. procedure TLSMTP.SetSocketClass(const AValue: TLSocketClass);
  216. begin
  217. FConnection.SocketClass := AValue;
  218. end;
  219. function TLSMTP.GetEventer: TLEventer;
  220. begin
  221. Result := FConnection.Eventer;
  222. end;
  223. procedure TLSMTP.SetEventer(Value: TLEventer);
  224. begin
  225. FConnection.Eventer := Value;
  226. end;
  227. constructor TLSMTP.Create(aOwner: TComponent);
  228. begin
  229. inherited Create(aOwner);
  230. FFeatureList := TStringList.Create;
  231. FConnection := TLTcp.Create(nil);
  232. FConnection.Creator := Self;
  233. // TODO: rework to use the new TLSocketTCP
  234. FConnection.SocketClass := TLSocket;
  235. end;
  236. destructor TLSMTP.Destroy;
  237. begin
  238. FFeatureList.Free;
  239. FConnection.Free;
  240. inherited Destroy;
  241. end;
  242. function TLSMTP.HasFeature(aFeature: string): Boolean;
  243. var
  244. tmp: TStringList;
  245. i, j: Integer;
  246. AllArgs: Boolean;
  247. begin
  248. Result := False;
  249. try
  250. tmp := TStringList.Create;
  251. aFeature := UpperCase(aFeature);
  252. aFeature := StringReplace(aFeature, ' ', ',', [rfReplaceAll]);
  253. tmp.CommaText := aFeature;
  254. for i := 0 to FFeatureList.Count - 1 do begin
  255. if Pos(tmp[0], FFeatureList[i]) = 1 then begin
  256. if tmp.Count = 1 then // no arguments, feature found, just exit true
  257. Exit(True)
  258. else begin // check arguments
  259. AllArgs := True;
  260. for j := 1 to tmp.Count - 1 do
  261. if Pos(tmp[j], FFeatureList[i]) <= 0 then begin // some argument not found
  262. AllArgs := False;
  263. Break;
  264. end;
  265. if AllArgs then
  266. Exit(True);
  267. end;
  268. end;
  269. end;
  270. finally
  271. tmp.Free;
  272. end;
  273. end;
  274. { TLSMTPClient }
  275. constructor TLSMTPClient.Create(aOwner: TComponent);
  276. begin
  277. inherited Create(aOwner);
  278. FPort := 25;
  279. FStatusSet := [ssNone..ssLast]; // full set
  280. FSL := TStringList.Create;
  281. // {$warning TODO: fix pipelining support when server does it}
  282. FPipeLine := False;
  283. FConnection.OnError := @OnEr;
  284. FConnection.OnCanSend := @OnCs;
  285. FConnection.OnReceive := @OnRe;
  286. FConnection.OnConnect := @OnCo;
  287. FConnection.OnDisconnect := @OnDs;
  288. FStatus := TLSMTPStatusFront.Create(EMPTY_REC);
  289. FCommandFront := TLSMTPStatusFront.Create(EMPTY_REC);
  290. end;
  291. destructor TLSMTPClient.Destroy;
  292. begin
  293. if FConnection.Connected then
  294. Quit;
  295. FSL.Free;
  296. FStatus.Free;
  297. FCommandFront.Free;
  298. inherited Destroy;
  299. end;
  300. procedure TLSMTPClient.OnEr(const msg: string; aSocket: TLSocket);
  301. begin
  302. if Assigned(FOnFailure) then begin
  303. while not FStatus.Empty do
  304. FOnFailure(aSocket, FStatus.Remove.Status);
  305. end else
  306. FStatus.Clear;
  307. if Assigned(FOnError) then
  308. FOnError(msg, aSocket);
  309. end;
  310. procedure TLSMTPClient.OnRe(aSocket: TLSocket);
  311. begin
  312. if Assigned(FOnReceive) then
  313. FOnReceive(aSocket);
  314. end;
  315. procedure TLSMTPClient.OnCo(aSocket: TLSocket);
  316. begin
  317. if Assigned(FOnConnect) then
  318. FOnConnect(aSocket);
  319. end;
  320. procedure TLSMTPClient.OnDs(aSocket: TLSocket);
  321. begin
  322. if Assigned(FOnDisconnect) then
  323. FOnDisconnect(aSocket);
  324. end;
  325. procedure TLSMTPClient.OnCs(aSocket: TLSocket);
  326. begin
  327. SendData(FStatus.First.Status = ssData);
  328. end;
  329. function TLSMTPClient.CanContinue(const aStatus: TLSMTPStatus; const Arg1, Arg2: string): Boolean;
  330. begin
  331. Result := FPipeLine or FStatus.Empty;
  332. if not Result then
  333. FCommandFront.Insert(MakeStatusRec(aStatus, Arg1, Arg2));
  334. end;
  335. function TLSMTPClient.CleanInput(var s: string): Integer;
  336. var
  337. i: Integer;
  338. begin
  339. FSL.Text := s;
  340. case FStatus.First.Status of // TODO: clear this to a proper place, the whole thing needs an overhaul
  341. ssCon,
  342. ssEhlo: FTempBuffer := FTempBuffer + UpperCase(s);
  343. end;
  344. if FSL.Count > 0 then
  345. for i := 0 to FSL.Count - 1 do
  346. if Length(FSL[i]) > 0 then EvaluateAnswer(FSL[i]);
  347. s := StringReplace(s, CRLF, LineEnding, [rfReplaceAll]);
  348. i := Pos('PASS', s);
  349. if i > 0 then
  350. s := Copy(s, 1, i-1) + 'PASS';
  351. Result := Length(s);
  352. end;
  353. procedure TLSMTPClient.EvaluateServer;
  354. begin
  355. FFeatureList.Clear;
  356. if Length(FTempBuffer) = 0 then
  357. Exit;
  358. if Pos('ESMTP', FTempBuffer) > 0 then
  359. FFeatureList.Append('EHLO');
  360. FTempBuffer := '';
  361. end;
  362. procedure TLSMTPClient.EvaluateFeatures;
  363. var
  364. i: Integer;
  365. begin
  366. FFeatureList.Clear;
  367. if Length(FTempBuffer) = 0 then
  368. Exit;
  369. FFeatureList.Text := FTempBuffer;
  370. FTempBuffer := '';
  371. FFeatureList.Delete(0);
  372. i := 0;
  373. while i < FFeatureList.Count do begin;
  374. FFeatureList[i] := Copy(FFeatureList[i], 5, Length(FFeatureList[i])); // delete the response code crap
  375. FFeatureList[i] := StringReplace(FFeatureList[i], '=', ' ', [rfReplaceAll]);
  376. if FFeatureList.IndexOf(FFeatureList[i]) <> i then begin
  377. FFeatureList.Delete(i);
  378. Continue;
  379. end;
  380. Inc(i);
  381. end;
  382. end;
  383. procedure TLSMTPClient.EvaluateAnswer(const Ans: string);
  384. function GetNum: Integer;
  385. begin
  386. try
  387. Result := StrToInt(Copy(Ans, 1, 3));
  388. except
  389. Result := -1;
  390. end;
  391. end;
  392. function ValidResponse(const Answer: string): Boolean; inline;
  393. begin
  394. Result := (Length(Ans) >= 3) and
  395. (Ans[1] in ['1'..'5']) and
  396. (Ans[2] in ['0'..'9']) and
  397. (Ans[3] in ['0'..'9']);
  398. if Result then
  399. Result := (Length(Ans) = 3) or ((Length(Ans) > 3) and (Ans[4] = ' '));
  400. end;
  401. procedure Eventize(const aStatus: TLSMTPStatus; const Res: Boolean);
  402. begin
  403. FStatus.Remove;
  404. if Res then begin
  405. if Assigned(FOnSuccess) and (aStatus in FStatusSet) then
  406. FOnSuccess(FConnection.Iterator, aStatus);
  407. end else begin
  408. if Assigned(FOnFailure) and (aStatus in FStatusSet) then
  409. FOnFailure(FConnection.Iterator, aStatus);
  410. end;
  411. end;
  412. var
  413. x: Integer;
  414. begin
  415. x := GetNum;
  416. if ValidResponse(Ans) and not FStatus.Empty then
  417. case FStatus.First.Status of
  418. ssCon,
  419. ssHelo,
  420. ssEhlo: case x of
  421. 200..299: begin
  422. case FStatus.First.Status of
  423. ssCon : EvaluateServer;
  424. ssEhlo : EvaluateFeatures;
  425. end;
  426. Eventize(FStatus.First.Status, True);
  427. end;
  428. else begin
  429. Eventize(FStatus.First.Status, False);
  430. Disconnect(False);
  431. FFeatureList.Clear;
  432. FTempBuffer := '';
  433. end;
  434. end;
  435. ssStartTLS:
  436. case x of
  437. 200..299: begin
  438. Eventize(FStatus.First.Status, True);
  439. FConnection.Iterator.SetState(ssSSLActive);
  440. end;
  441. else begin
  442. Eventize(FStatus.First.Status, False);
  443. end;
  444. end;
  445. ssAuthLogin:
  446. case x of
  447. 200..299: begin
  448. Eventize(FStatus.First.Status, True);
  449. end;
  450. 300..399: if FAuthStep = 0 then begin
  451. AddToBuffer(FStatus.First.Args[1] + CRLF);
  452. Inc(FAuthStep);
  453. SendData;
  454. end else if FAuthStep = 1 then begin
  455. AddToBuffer(FStatus.First.Args[2] + CRLF);
  456. Inc(FAuthStep);
  457. SendData;
  458. end else begin
  459. Eventize(FStatus.First.Status, False);
  460. end;
  461. else begin
  462. Eventize(FStatus.First.Status, False);
  463. end;
  464. end;
  465. ssAuthPlain:
  466. case x of
  467. 200..299: begin
  468. Eventize(FStatus.First.Status, True);
  469. end;
  470. 300..399: begin
  471. AddToBuffer(FStatus.First.Args[1] + FStatus.First.Args[2] + CRLF);
  472. SendData;
  473. end;
  474. else begin
  475. Eventize(FStatus.First.Status, False);
  476. end;
  477. end;
  478. ssMail,
  479. ssRcpt: begin
  480. Eventize(FStatus.First.Status, (x >= 200) and (x < 299));
  481. end;
  482. ssData: case x of
  483. 200..299: begin
  484. Eventize(FStatus.First.Status, True);
  485. end;
  486. 300..399: begin
  487. AddToBuffer(FDataBuffer);
  488. FDataBuffer := '';
  489. SendData(True);
  490. end;
  491. else begin
  492. FDataBuffer := '';
  493. Eventize(FStatus.First.Status, False);
  494. end;
  495. end;
  496. ssRset: begin
  497. Eventize(FStatus.First.Status, (x >= 200) and (x < 299));
  498. end;
  499. ssQuit: begin
  500. Eventize(FStatus.First.Status, (x >= 200) and (x < 299));
  501. { if Assigned(FOnDisconnect) then
  502. FOnDisconnect(FConnection.Iterator);}
  503. Disconnect(False);
  504. end;
  505. end;
  506. if FStatus.Empty and not FCommandFront.Empty then
  507. ExecuteFrontCommand;
  508. end;
  509. procedure TLSMTPClient.ExecuteFrontCommand;
  510. begin
  511. with FCommandFront.First do
  512. case Status of
  513. ssHelo: Helo(Args[1]);
  514. ssEhlo: Ehlo(Args[1]);
  515. ssMail: Mail(Args[1]);
  516. ssRcpt: Rcpt(Args[1]);
  517. ssData: Data(Args[1]);
  518. ssRset: Rset;
  519. ssQuit: Quit;
  520. end;
  521. FCommandFront.Remove;
  522. end;
  523. procedure TLSMTPClient.AddToBuffer(s: string);
  524. var
  525. i: Integer;
  526. Skip: Boolean = False;
  527. begin
  528. for i := 1 to Length(s) do begin
  529. if Skip then begin
  530. Skip := False;
  531. Continue;
  532. end;
  533. if (s[i] = #13) or (s[i] = #10) then begin
  534. if s[i] = #13 then
  535. if (i < Length(s)) and (s[i + 1] = #10) then begin
  536. FCharCount := 0;
  537. Skip := True; // skip the crlf
  538. end else begin // insert LF to a standalone CR
  539. System.Insert(#10, s, i + 1);
  540. FCharCount := 0;
  541. Skip := True; // skip the new crlf
  542. end;
  543. if s[i] = #10 then begin
  544. System.Insert(#13, s, i);
  545. FCharCount := 0;
  546. Skip := True; // skip the new crlf
  547. end;
  548. end else if FCharCount >= 1000 then begin // line too long
  549. System.Insert(CRLF, s, i);
  550. FCharCount := 0;
  551. Skip := True;
  552. end else
  553. Inc(FCharCount);
  554. end;
  555. FBuffer := FBuffer + s;
  556. end;
  557. procedure TLSMTPClient.SendData(const FromStream: Boolean = False);
  558. const
  559. SBUF_SIZE = 65535;
  560. procedure FillBuffer;
  561. var
  562. s: string;
  563. begin
  564. SetLength(s, SBUF_SIZE - Length(FBuffer));
  565. SetLength(s, FStream.Read(s[1], Length(s)));
  566. AddToBuffer(s);
  567. if FStream.Position = FStream.Size then begin // we finished the stream
  568. AddToBuffer(CRLF + '.' + CRLF);
  569. FStream := nil;
  570. end;
  571. end;
  572. var
  573. n: Integer;
  574. Sent: Integer;
  575. begin
  576. if FromStream and Assigned(FStream) then
  577. FillBuffer;
  578. n := 1;
  579. Sent := 0;
  580. while (Length(FBuffer) > 0) and (n > 0) do begin
  581. n := FConnection.SendMessage(FBuffer);
  582. Sent := Sent + n;
  583. if n > 0 then
  584. Delete(FBuffer, 1, n);
  585. if FromStream and Assigned(FStream) and (Length(FBuffer) < SBUF_SIZE) then
  586. FillBuffer;
  587. end;
  588. if Assigned(FOnSent) and (FStatus.First.Status = ssData) then
  589. FOnSent(FConnection.Iterator, Sent);
  590. end;
  591. function TLSMTPClient.EncodeBase64(const s: string): string;
  592. var
  593. Dummy: TBogusStream;
  594. Enc: TBase64EncodingStream;
  595. begin
  596. Result := '';
  597. if Length(s) = 0 then
  598. Exit;
  599. Dummy := TBogusStream.Create;
  600. Enc := TBase64EncodingStream.Create(Dummy);
  601. Enc.Write(s[1], Length(s));
  602. Enc.Free;
  603. SetLength(Result, Dummy.Size);
  604. Dummy.Read(Result[1], Dummy.Size);
  605. Dummy.Free;
  606. end;
  607. function TLSMTPClient.Connect(const aHost: string; const aPort: Word = 25): Boolean;
  608. begin
  609. Result := False;
  610. Disconnect(True);
  611. if FConnection.Connect(aHost, aPort) then begin
  612. FTempBuffer := '';
  613. FHost := aHost;
  614. FPort := aPort;
  615. FStatus.Insert(MakeStatusRec(ssCon, '', ''));
  616. Result := True;
  617. end;
  618. end;
  619. function TLSMTPClient.Connect: Boolean;
  620. begin
  621. Result := Connect(FHost, FPort);
  622. end;
  623. function TLSMTPClient.Get(var aData; const aSize: Integer; aSocket: TLSocket): Integer;
  624. var
  625. s: string;
  626. begin
  627. Result := FConnection.Get(aData, aSize, aSocket);
  628. if Result > 0 then begin
  629. SetLength(s, Result);
  630. Move(aData, PChar(s)^, Result);
  631. CleanInput(s);
  632. end;
  633. end;
  634. function TLSMTPClient.GetMessage(out msg: string; aSocket: TLSocket): Integer;
  635. begin
  636. Result := FConnection.GetMessage(msg, aSocket);
  637. if Result > 0 then
  638. Result := CleanInput(msg);
  639. end;
  640. procedure TLSMTPClient.SendMail(From, Recipients, Subject, Msg: string);
  641. var
  642. i: Integer;
  643. begin
  644. FStream := nil;
  645. From := EncodeMimeHeaderText(From);
  646. Recipients := EncodeMimeHeaderText(Recipients);
  647. Subject := EncodeMimeHeaderText(Subject);
  648. if (Length(Recipients) > 0) and (Length(From) > 0) then begin
  649. Mail(From);
  650. FSL.CommaText := StringReplace(Recipients, ' ', ',', [rfReplaceAll]);
  651. for i := 0 to FSL.Count-1 do
  652. Rcpt(FSL[i]);
  653. Data('From: ' + From + CRLF + 'Subject: ' + Subject + CRLF + 'To: ' + FSL.CommaText + CRLF + CRLF + Msg);
  654. end;
  655. end;
  656. procedure TLSMTPClient.SendMail(From, Recipients, Subject: string; aStream: TStream);
  657. var
  658. i: Integer;
  659. begin
  660. From := EncodeMimeHeaderText(From);
  661. Recipients := EncodeMimeHeaderText(Recipients);
  662. Subject := EncodeMimeHeaderText(Subject);
  663. FStream := aStream;
  664. if (Length(Recipients) > 0) and (Length(From) > 0) then begin
  665. Mail(From);
  666. FSL.CommaText := StringReplace(Recipients, ' ', ',', [rfReplaceAll]);
  667. for i := 0 to FSL.Count-1 do
  668. Rcpt(FSL[i]);
  669. Data('From: ' + From + CRLF + 'Subject: ' + Subject + CRLF + 'To: ' + FSL.CommaText + CRLF);
  670. end;
  671. end;
  672. procedure TLSMTPClient.SendMail(aMail: TMail);
  673. begin
  674. if Length(aMail.FMailText) > 0 then
  675. SendMail(aMail.Sender, aMail.Recipients, aMail.Subject, aMail.FMailText)
  676. else if Assigned(aMail.FMailStream) then
  677. SendMail(aMail.Sender, aMail.Recipients, aMail.Subject, aMail.FMailStream);
  678. end;
  679. procedure TLSMTPClient.Helo(aHost: string = '');
  680. begin
  681. if Length(aHost) = 0 then
  682. aHost := FHost;
  683. if CanContinue(ssHelo, aHost, '') then begin
  684. AddToBuffer('HELO ' + aHost + CRLF);
  685. FStatus.Insert(MakeStatusRec(ssHelo, '', ''));
  686. SendData;
  687. end;
  688. end;
  689. procedure TLSMTPClient.Ehlo(aHost: string = '');
  690. begin
  691. if Length(aHost) = 0 then
  692. aHost := FHost;
  693. if CanContinue(ssEhlo, aHost, '') then begin
  694. FTempBuffer := ''; // for ehlo response
  695. AddToBuffer('EHLO ' + aHost + CRLF);
  696. FStatus.Insert(MakeStatusRec(ssEhlo, '', ''));
  697. SendData;
  698. end;
  699. end;
  700. procedure TLSMTPClient.StartTLS;
  701. begin
  702. if CanContinue(ssStartTLS, '', '') then begin
  703. AddToBuffer('STARTTLS' + CRLF);
  704. FStatus.Insert(MakeStatusRec(ssStartTLS, '', ''));
  705. SendData;
  706. end;
  707. end;
  708. procedure TLSMTPClient.AuthLogin(aName, aPass: string);
  709. begin
  710. aName := EncodeBase64(aName);
  711. aPass := EncodeBase64(aPass);
  712. FAuthStep := 0; // first, send username
  713. if CanContinue(ssAuthLogin, aName, aPass) then begin
  714. AddToBuffer('AUTH LOGIN' + CRLF);
  715. FStatus.Insert(MakeStatusRec(ssAuthLogin, aName, aPass));
  716. SendData;
  717. end;
  718. end;
  719. procedure TLSMTPClient.AuthPlain(aName, aPass: string);
  720. begin
  721. aName := EncodeBase64(#0 + aName);
  722. aPass := EncodeBase64(#0 + aPass);
  723. FAuthStep := 0;
  724. if CanContinue(ssAuthPlain, aName, aPass) then begin
  725. AddToBuffer('AUTH PLAIN' + CRLF);
  726. FStatus.Insert(MakeStatusRec(ssAuthPlain, aName, aPass));
  727. SendData;
  728. end;
  729. end;
  730. procedure TLSMTPClient.Mail(const From: string);
  731. begin
  732. if CanContinue(ssMail, From, '') then begin
  733. AddToBuffer('MAIL FROM:' + '<' + From + '>' + CRLF);
  734. FStatus.Insert(MakeStatusRec(ssMail, '', ''));
  735. SendData;
  736. end;
  737. end;
  738. procedure TLSMTPClient.Rcpt(const RcptTo: string);
  739. begin
  740. if CanContinue(ssRcpt, RcptTo, '') then begin
  741. AddToBuffer('RCPT TO:' + '<' + RcptTo + '>' + CRLF);
  742. FStatus.Insert(MakeStatusRec(ssRcpt, '', ''));
  743. SendData;
  744. end;
  745. end;
  746. procedure TLSMTPClient.Data(const Msg: string);
  747. begin
  748. if CanContinue(ssData, Msg, '') then begin
  749. AddToBuffer('DATA ' + CRLF);
  750. FDataBuffer := '';
  751. if Assigned(FStream) then begin
  752. if Length(Msg) > 0 then
  753. FDataBuffer := Msg;
  754. end else
  755. FDataBuffer := Msg + CRLF + '.' + CRLF;
  756. FStatus.Insert(MakeStatusRec(ssData, '', ''));
  757. SendData(False);
  758. end;
  759. end;
  760. procedure TLSMTPClient.Rset;
  761. begin
  762. if CanContinue(ssRset, '', '') then begin
  763. AddToBuffer('RSET' + CRLF);
  764. FStatus.Insert(MakeStatusRec(ssRset, '', ''));
  765. SendData;
  766. end;
  767. end;
  768. procedure TLSMTPClient.Quit;
  769. begin
  770. if CanContinue(ssQuit, '', '') then begin
  771. AddToBuffer('QUIT' + CRLF);
  772. FStatus.Insert(MakeStatusRec(ssQuit, '', ''));
  773. SendData;
  774. end;
  775. end;
  776. procedure TLSMTPClient.Disconnect(const Forced: Boolean = True);
  777. begin
  778. FConnection.Disconnect(Forced);
  779. FStatus.Clear;
  780. FCommandFront.Clear;
  781. end;
  782. procedure TLSMTPClient.CallAction;
  783. begin
  784. FConnection.CallAction;
  785. end;
  786. { TMail }
  787. function TMail.GetCount: Integer;
  788. begin
  789. Result := FMailStream.Count;
  790. end;
  791. function TMail.GetSection(i: Integer): TMimeSection;
  792. begin
  793. Result := FMailStream.Sections[i];
  794. end;
  795. procedure TMail.SetSection(i: Integer; const AValue: TMimeSection);
  796. begin
  797. FMailStream.Sections[i] := aValue;
  798. end;
  799. constructor TMail.Create;
  800. begin
  801. FMailStream := TMimeStream.Create;
  802. end;
  803. destructor TMail.Destroy;
  804. begin
  805. FMailStream.Free;
  806. end;
  807. procedure TMail.AddTextSection(const aText: string; const aCharSet: string);
  808. begin
  809. FMailStream.AddTextSection(aText, aCharSet);
  810. end;
  811. procedure TMail.AddFileSection(const aFileName: string);
  812. begin
  813. FMailStream.AddFileSection(aFileName);
  814. end;
  815. procedure TMail.AddStreamSection(aStream: TStream; const FreeStream: Boolean);
  816. begin
  817. FMailStream.AddStreamSection(aStream, FreeStream);
  818. end;
  819. procedure TMail.DeleteSection(const i: Integer);
  820. begin
  821. FMailStream.Delete(i);
  822. end;
  823. procedure TMail.RemoveSection(aSection: TMimeSection);
  824. begin
  825. FMailStream.Remove(aSection);
  826. end;
  827. procedure TMail.Reset;
  828. begin
  829. FMailStream.Reset;
  830. end;
  831. end.