IdSMTPServer.pas 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.9 2/8/05 6:48:30 PM RLebeau
  18. Misc. tweaks
  19. Rev 1.8 24/10/2004 21:26:14 ANeillans
  20. RCPTList can be set
  21. Rev 1.7 9/15/2004 5:02:06 PM DSiders
  22. Added localization comments.
  23. Rev 1.6 31/08/2004 20:21:34 ANeillans
  24. Bug fix -- format problem.
  25. Rev 1.5 08/08/2004 21:03:10 ANeillans
  26. Continuing....
  27. Rev 1.4 02/08/2004 21:14:28 ANeillans
  28. Auth working
  29. Rev 1.3 01/08/2004 13:02:16 ANeillans
  30. Development.
  31. Rev 1.2 01/08/2004 09:50:26 ANeillans
  32. Continued development.
  33. Rev 1.1 7/28/2004 8:26:46 AM JPMugaas
  34. Further work on the SMTP Server. Not tested yet.
  35. Rev 1.0 7/27/2004 5:14:38 PM JPMugaas
  36. Start on TIdSMTPServer rewrite.
  37. }
  38. unit IdSMTPServer;
  39. interface
  40. {$i IdCompilerDefines.inc}
  41. uses
  42. Classes,
  43. IdAssignedNumbers,
  44. IdCustomTCPServer, //for TIdServerContext
  45. IdCmdTCPServer,
  46. IdCommandHandlers,
  47. IdContext,
  48. IdEMailAddress,
  49. IdException,
  50. IdExplicitTLSClientServerBase,
  51. IdReply,
  52. IdReplySMTP,
  53. IdTCPConnection,
  54. IdYarn,
  55. IdStack,
  56. IdGlobal;
  57. type
  58. EIdSMTPServerError = class(EIdException);
  59. EIdSMTPServerNoRcptTo = class(EIdSMTPServerError);
  60. TIdMailFromReply =
  61. (
  62. mAccept, //accept the mail message
  63. mReject, //reject the mail message
  64. mSystemFull, //no more space on server
  65. mLimitExceeded //exceeded message size limit
  66. );
  67. TIdRCPToReply =
  68. (
  69. rAddressOk, //address is okay
  70. rRelayDenied, //we do not relay for third-parties
  71. rInvalid, //invalid address
  72. rWillForward, //not local - we will forward
  73. rNoForward, //not local - will not forward - please use
  74. rTooManyAddresses, //too many addresses
  75. rDisabledPerm, //disabled permentantly - not accepting E-Mail
  76. rDisabledTemp, //disabled temporarily - not accepting E-Mail
  77. rSystemFull, //no more space on server
  78. rLimitExceeded //exceeded message size limit
  79. );
  80. TIdDataReply =
  81. (
  82. dOk, //accept the mail message
  83. dMBFull, //Mail box full
  84. dSystemFull, //no more space on server
  85. dLocalProcessingError, //local processing error
  86. dTransactionFailed, //transaction failed
  87. dLimitExceeded //exceeded administrative limit
  88. );
  89. TIdSPFReply =
  90. (
  91. spfNone, //no published records or checkable domain
  92. spfNeutral, //domain explicitially stated no assertion
  93. spfPass, //authorized
  94. spfFail, //not authorized
  95. spfSoftFail, //may not be authorized
  96. spfTempError, //transient error
  97. spfPermError //permanent error
  98. );
  99. TIdSMTPServerContext = class;
  100. TOnMailFromEvent = procedure(ASender: TIdSMTPServerContext; const AAddress : string;
  101. AParams: TStrings; var VAction : TIdMailFromReply) of object;
  102. TOnMsgReceive = procedure(ASender: TIdSMTPServerContext; AMsg: TStream;
  103. var VAction : TIdDataReply) of object;
  104. TOnRcptToEvent = procedure(ASender: TIdSMTPServerContext; const AAddress : string;
  105. AParams: TStrings; var VAction : TIdRCPToReply; var VForward : String) of object;
  106. TOnReceived = procedure(ASender: TIdSMTPServerContext; var AReceived : String) of object;
  107. TOnSMTPEvent = procedure(ASender: TIdSMTPServerContext) of object;
  108. TOnSMTPUserLoginEvent = procedure(ASender: TIdSMTPServerContext; const AUsername, APassword: string;
  109. var VAuthenticated: Boolean) of object;
  110. TOnSPFCheck = procedure(ASender: TIdSMTPServerContext; const AIP, ADomain, AIdentity: String;
  111. var VAction: TIdSPFReply) of object;
  112. TOnDataStreamEvent = procedure(ASender: TIdSMTPServerContext; var VStream: TStream) of object;
  113. TIdSMTPServer = class(TIdExplicitTLSServer)
  114. protected
  115. //events
  116. FOnBeforeMsg : TOnDataStreamEvent;
  117. FOnMailFrom : TOnMailFromEvent;
  118. FOnMsgReceive : TOnMsgReceive;
  119. FOnRcptTo : TOnRcptToEvent;
  120. FOnReceived : TOnReceived;
  121. FOnReset: TOnSMTPEvent;
  122. FOnSPFCheck: TOnSPFCheck;
  123. FOnUserLogin : TOnSMTPUserLoginEvent;
  124. //misc
  125. FServerName : String;
  126. FAllowPipelining: Boolean;
  127. FMaxMsgSize: Integer;
  128. //
  129. function CreateGreeting: TIdReply; override;
  130. function CreateReplyUnknownCommand: TIdReply; override;
  131. //
  132. procedure DoAuthLogin(ASender: TIdCommand; const Mechanism, InitialResponse: string);
  133. //
  134. //command handlers
  135. procedure CommandNOOP(ASender: TIdCommand);
  136. procedure CommandQUIT(ASender: TIdCommand);
  137. procedure CommandEHLO(ASender: TIdCommand);
  138. procedure CommandHELO(ASender: TIdCommand);
  139. procedure CommandAUTH(ASender: TIdCommand);
  140. procedure CommandMAIL(ASender: TIdCommand);
  141. procedure CommandRCPT(ASender: TIdCommand);
  142. procedure CommandDATA(ASender: TIdCommand);
  143. procedure CommandRSET(ASender: TIdCommand);
  144. procedure CommandSTARTTLS(ASender: TIdCommand);
  145. procedure CommandBDAT(ASender: TIdCommand);
  146. {
  147. Note that for SMTP, I make a lot of procedures for replies.
  148. The reason is that we use precise enhanced status codes. These serve
  149. as diangostics and give much more information than the 3 number standard replies.
  150. The enhanced codes will sometimes appear in bounce notices.
  151. Note: Enhanced status codes should only appear if a client uses EHLO instead of HELO.
  152. }
  153. //common reply procs
  154. procedure AuthFailed(ASender: TIdCommand);
  155. procedure CmdSyntaxError(AContext: TIdContext; ALine: string; const AReply : TIdReply = nil); overload;
  156. procedure CmdSyntaxError(ASender: TIdCommand); overload;
  157. procedure BadSequenceError(ASender: TIdCommand);
  158. procedure InvalidSyntax(ASender: TIdCommand);
  159. procedure NoHello(ASender: TIdCommand);
  160. procedure MustUseTLS(ASender: TIdCommand);
  161. //Mail From
  162. procedure MailFromAccept(ASender: TIdCommand; const AAddress : String = '');
  163. procedure MailFromReject(ASender: TIdCommand; const AAddress : String = '');
  164. //address replies - RCPT TO
  165. procedure AddrValid(ASender: TIdCommand; const AAddress : String = '');
  166. procedure AddrInvalid(ASender: TIdCommand; const AAddress : String = '');
  167. procedure AddrWillForward(ASender: TIdCommand; const AAddress : String = ''; const ATo : String = '');
  168. procedure AddrNotWillForward(ASender: TIdCommand; const AAddress : String = ''; const ATo : String = '');
  169. procedure AddrDisabledPerm(ASender: TIdCommand; const AAddress : String = '');
  170. procedure AddrDisabledTemp(ASender: TIdCommand; const AAddress : String = '');
  171. procedure AddrNoRelaying(ASender: TIdCommand; const AAddress : String = '');
  172. procedure AddrTooManyRecipients(ASender: TIdCommand);
  173. //mail submit replies
  174. procedure MailSubmitOk(ASender: TIdCommand);
  175. procedure MailSubmitLimitExceeded(ASender: TIdCommand);
  176. procedure MailSubmitStorageExceededFull(ASender: TIdCommand);
  177. procedure MailSubmitTransactionFailed(ASender: TIdCommand);
  178. procedure MailSubmitLocalProcessingError(ASender: TIdCommand);
  179. procedure MailSubmitSystemFull(ASender: TIdCommand);
  180. procedure SetEnhReply(AReply: TIdReply; const ANumericCode: Integer;
  181. const AEnhReply, AText: String; const IsEHLO: Boolean);
  182. // overrides for SMTP
  183. function GetReplyClass: TIdReplyClass; override;
  184. function GetRepliesClass: TIdRepliesClass; override;
  185. procedure InitComponent; override;
  186. procedure DoReplyUnknownCommand(AContext: TIdContext; ALine: string); override;
  187. procedure InitializeCommandHandlers; override;
  188. //
  189. procedure DoReset(AContext: TIdSMTPServerContext; AIsTLSReset: Boolean = False);
  190. procedure MsgBegan(AContext: TIdSMTPServerContext; var VStream: TStream);
  191. procedure MsgReceived(ASender: TIdCommand; AMsgData: TStream);
  192. procedure SetMaxMsgSize(AValue: Integer);
  193. function SPFAuthOk(AContext: TIdSMTPServerContext; AReply: TIdReply; const ACmd, ADomain, AIdentity: String): Boolean;
  194. {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
  195. public
  196. constructor Create(AOwner: TComponent); reintroduce; overload;
  197. {$ENDIF}
  198. published
  199. //events
  200. property OnBeforeMsg : TOnDataStreamEvent read FOnBeforeMsg write FOnBeforeMsg;
  201. property OnMailFrom : TOnMailFromEvent read FOnMailFrom write FOnMailFrom;
  202. property OnMsgReceive : TOnMsgReceive read FOnMsgReceive write FOnMsgReceive;
  203. property OnRcptTo : TOnRcptToEvent read FOnRcptTo write FOnRcptTo;
  204. property OnReceived: TOnReceived read FOnReceived write FOnReceived;
  205. property OnReset: TOnSMTPEvent read FOnReset write FOnReset;
  206. property OnSPFCheck: TOnSPFCheck read FOnSPFCheck write FOnSPFCheck;
  207. property OnUserLogin : TOnSMTPUserLoginEvent read FOnUserLogin write FOnUserLogin;
  208. //properties
  209. property AllowPipelining : Boolean read FAllowPipelining write FAllowPipelining default False;
  210. property DefaultPort default IdPORT_SMTP;
  211. property MaxMsgSize: Integer read FMaxMsgSize write SetMaxMsgSize default 0;
  212. property ServerName : String read FServerName write FServerName;
  213. property UseTLS;
  214. end;
  215. TIdSMTPState = (idSMTPNone, idSMTPHelo, idSMTPMail, idSMTPRcpt, idSMTPData, idSMTPBDat);
  216. TIdSMTPBodyType = (idSMTP7Bit, idSMTP8BitMime, idSMTPBinaryMime);
  217. TIdSMTPServerContext = class(TIdServerContext)
  218. protected
  219. FSMTPState: TIdSMTPState;
  220. FFrom: string;
  221. FRCPTList: TIdEMailAddressList;
  222. FHELO: Boolean;
  223. FEHLO: Boolean;
  224. FHeloString: String;
  225. FUsername: string;
  226. FPassword: string;
  227. FLoggedIn: Boolean;
  228. FMsgSize: Integer;
  229. FPipeLining : Boolean;
  230. FFinalStage : Boolean;
  231. FBDataStream: TStream;
  232. FBodyType: TIdSMTPBodyType;
  233. function GetUsingTLS: Boolean;
  234. function GetCanUseExplicitTLS: Boolean;
  235. function GetTLSIsRequired: Boolean;
  236. procedure SetPipeLining(const AValue : Boolean);
  237. public
  238. constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override;
  239. destructor Destroy; override;
  240. //
  241. procedure CheckPipeLine;
  242. procedure Reset(AIsTLSReset: Boolean = False); virtual;
  243. //
  244. property SMTPState: TIdSMTPState read FSMTPState write FSMTPState;
  245. property From: String read FFrom write FFrom;
  246. property RCPTList: TIdEMailAddressList read FRCPTList;
  247. property HELO: Boolean read FHELO write FHELO;
  248. property EHLO: Boolean read FEHLO write FEHLO;
  249. property HeloString : String read FHeloString write FHeloString;
  250. property Username: String read FUsername write FUsername;
  251. property Password: String read FPassword write FPassword;
  252. property LoggedIn: Boolean read FLoggedIn write FLoggedIn;
  253. property MsgSize: Integer read FMsgSize write FMsgSize;
  254. property FinalStage: Boolean read FFinalStage write FFinalStage;
  255. property UsingTLS: Boolean read GetUsingTLS;
  256. property CanUseExplicitTLS: Boolean read GetCanUseExplicitTLS;
  257. property TLSIsRequired: Boolean read GetTLSIsRequired;
  258. property PipeLining: Boolean read FPipeLining write SetPipeLining;
  259. //
  260. end;
  261. const
  262. IdSMTPSvrReceivedString = 'Received: from $hostname[$ipaddress] (helo=$helo) by $svrhostname[$svripaddress] with $protocol ($servername)'; {do not localize}
  263. implementation
  264. uses
  265. IdCoderMIME,
  266. IdGlobalProtocols,
  267. IdResourceStringsProtocols,
  268. IdSSL, SysUtils;
  269. { TIdSMTPServer }
  270. {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
  271. constructor TIdSMTPServer.Create(AOwner: TComponent);
  272. begin
  273. inherited Create(AOwner);
  274. end;
  275. {$ENDIF}
  276. procedure TIdSMTPServer.CmdSyntaxError(AContext: TIdContext; ALine: string;
  277. const AReply: TIdReply);
  278. var
  279. LTmp : String;
  280. LReply : TIdReply;
  281. begin
  282. //First make the first word uppercase
  283. LTmp := UpCaseFirstWord(ALine);
  284. try
  285. if Assigned(AReply) then begin
  286. LReply := AReply;
  287. end else begin
  288. LReply := TIdReplySMTP.CreateWithReplyTexts(nil, ReplyTexts);
  289. LReply.Assign(ReplyUnknownCommand);
  290. end;
  291. SetEnhReply(LReply, 500, '5.0.0', IndyFormat(RSFTPCmdNotRecognized, [LTmp]), {do not localize}
  292. TIdSMTPServerContext(AContext).Ehlo);
  293. AContext.Connection.IOHandler.Write(LReply.FormattedReply);
  294. finally
  295. if not Assigned(AReply) then begin
  296. FreeAndNil(LReply);
  297. end;
  298. end;
  299. end;
  300. procedure TIdSMTPServer.BadSequenceError(ASender: TIdCommand);
  301. begin
  302. SetEnhReply(ASender.Reply, 503, Id_EHR_PR_OTHER_PERM, RSSMTPSvrBadSequence,
  303. TIdSMTPServerContext(ASender.Context).EHLO);
  304. end;
  305. procedure TIdSMTPServer.CmdSyntaxError(ASender: TIdCommand);
  306. begin
  307. CmdSyntaxError(ASender.Context, ASender.RawLine, FReplyUnknownCommand );
  308. ASender.PerformReply := False;
  309. end;
  310. function TIdSMTPServer.CreateGreeting: TIdReply;
  311. begin
  312. Result := TIdReplySMTP.CreateWithReplyTexts(nil, ReplyTexts);
  313. TIdReplySMTP(Result).SetEnhReply(220, '' ,RSSMTPSvrWelcome)
  314. end;
  315. function TIdSMTPServer.CreateReplyUnknownCommand: TIdReply;
  316. begin
  317. Result := TIdReplySMTP.CreateWithReplyTexts(nil, ReplyTexts);
  318. TIdReplySMTP(Result).SetEnhReply(500, Id_EHR_PR_SYNTAX_ERR, 'Syntax Error'); {do not localize}
  319. end;
  320. procedure TIdSMTPServer.CommandEHLO(ASender: TIdCommand);
  321. var
  322. LContext : TIdSMTPServerContext;
  323. begin
  324. LContext := TIdSMTPServerContext(ASender.Context);
  325. //Note you can not use PIPELINING with EHLO
  326. LContext.PipeLining := False;
  327. DoReset(LContext);
  328. LContext.EHLO := True;
  329. LContext.HeloString := ASender.UnparsedParams;
  330. if SPFAuthOk(LContext, ASender.Reply, 'EHLO', DomainName(ASender.UnparsedParams), ASender.UnparsedParams) then {do not localize}
  331. begin
  332. SetEnhReply(ASender.Reply, 250, '', IndyFormat(RSSMTPSvrHello, [ASender.UnparsedParams]), True);
  333. if Assigned(FOnUserLogin) then begin
  334. ASender.Reply.Text.Add('AUTH LOGIN'); {Do not Localize}
  335. end;
  336. ASender.Reply.Text.Add('ENHANCEDSTATUSCODES'); {do not localize}
  337. if FAllowPipelining then begin
  338. ASender.Reply.Text.Add('PIPELINING'); {do not localize}
  339. end;
  340. ASender.Reply.Text.Add(IndyFormat('SIZE %d', [FMaxMsgSize])); {do not localize}
  341. if LContext.CanUseExplicitTLS and (not LContext.UsingTLS) then begin
  342. ASender.Reply.Text.Add('STARTTLS'); {Do not Localize}
  343. end;
  344. ASender.Reply.Text.Add('CHUNKING'); {do not localize}
  345. ASender.Reply.Text.Add('8BITMIME'); {do not localize}
  346. ASender.Reply.Text.Add('BINARYMIME'); {do not localize}
  347. LContext.SMTPState := idSMTPHelo;
  348. end;
  349. end;
  350. procedure TIdSMTPServer.DoReplyUnknownCommand(AContext: TIdContext; ALine: string);
  351. begin
  352. CmdSyntaxError(AContext, ALine);
  353. end;
  354. function TIdSMTPServer.GetRepliesClass: TIdRepliesClass;
  355. begin
  356. Result := TIdRepliesSMTP;
  357. end;
  358. function TIdSMTPServer.GetReplyClass: TIdReplyClass;
  359. begin
  360. Result := TIdReplySMTP;
  361. end;
  362. procedure TIdSMTPServer.InitComponent;
  363. begin
  364. inherited InitComponent;
  365. FContextClass := TIdSMTPServerContext;
  366. HelpReply.Code := ''; //we will handle the help ourselves
  367. FRegularProtPort := IdPORT_SMTP;
  368. FImplicitTLSProtPort := IdPORT_ssmtp;
  369. FExplicitTLSProtPort := 587; // TODO: define a constant for this!
  370. DefaultPort := IdPORT_SMTP;
  371. FServerName := 'Indy SMTP Server'; {do not localize}
  372. end;
  373. procedure TIdSMTPServer.InitializeCommandHandlers;
  374. var
  375. LCmd : TIdCommandHandler;
  376. begin
  377. inherited InitializeCommandHandlers;
  378. LCmd := CommandHandlers.Add;
  379. LCmd.Command := 'EHLO'; {do not localize}
  380. LCmd.OnCommand := CommandEHLO;
  381. LCmd.NormalReply.NumericCode := 250;
  382. LCmd.ParseParams := True;
  383. SetEnhReply(LCmd.ExceptionReply, 451,Id_EHR_PR_OTHER_TEMP, 'Internal Error', False); {do not localize}
  384. LCmd := CommandHandlers.Add;
  385. LCmd.Command := 'HELO'; {do not localize}
  386. LCmd.OnCommand := CommandHELO;
  387. LCmd.NormalReply.NumericCode := 250;
  388. LCmd.ParseParams := True;
  389. SetEnhReply(LCmd.ExceptionReply, 451,Id_EHR_PR_OTHER_TEMP, 'Internal Error', False); {do not localize}
  390. LCmd := CommandHandlers.Add;
  391. LCmd.Command := 'AUTH'; {do not localize}
  392. LCmd.OnCommand := CommandAUTH;
  393. SetEnhReply(LCmd.ExceptionReply, 451,Id_EHR_PR_OTHER_TEMP, 'Internal Error', False); {do not localize}
  394. LCmd := CommandHandlers.Add;
  395. // NOOP
  396. LCmd.Command := 'NOOP'; {Do not Localize}
  397. SetEnhReply(LCmd.NormalReply ,250,Id_EHR_GENERIC_OK,RSSMTPSvrOk, True);
  398. LCmd.OnCommand := CommandNOOP;
  399. SetEnhReply(LCmd.ExceptionReply, 451,Id_EHR_PR_OTHER_TEMP, 'Internal Error', False); {do not localize}
  400. LCmd := CommandHandlers.Add;
  401. // QUIT
  402. LCmd.Command := 'QUIT'; {Do not Localize}
  403. LCmd.CmdDelimiter := ' '; {Do not Localize}
  404. LCmd.Disconnect := True;
  405. SetEnhReply(LCmd.NormalReply, 221, Id_EHR_GENERIC_OK, RSSMTPSvrQuit, False);
  406. LCmd.OnCommand := CommandQUIT;
  407. LCmd := CommandHandlers.Add;
  408. // RCPT <SP> TO:<forward-path> <CRLF>
  409. LCmd.Command := 'RCPT'; {Do not Localize}
  410. LCmd.CmdDelimiter := ' '; {Do not Localize}
  411. LCmd.OnCommand := CommandRcpt;
  412. SetEnhReply(LCmd.NormalReply, 250, Id_EHR_MSG_VALID_DEST, '', False);
  413. SetEnhReply(LCmd.ExceptionReply, 550, Id_EHR_MSG_BAD_DEST, '', False);
  414. LCmd := CommandHandlers.Add;
  415. // MAIL <SP> FROM:<reverse-path> <CRLF>
  416. LCmd.Command := 'MAIL'; {Do not Localize}
  417. LCmd.CmdDelimiter := ' '; {Do not Localize}
  418. LCmd.OnCommand := CommandMail;
  419. SetEnhReply(LCmd.NormalReply, 250, Id_EHR_MSG_OTH_OK, '', False);
  420. SetEnhReply(LCmd.ExceptionReply, 451, Id_EHR_MSG_BAD_SENDER_ADDR, '', False);
  421. LCmd := CommandHandlers.Add;
  422. // DATA <CRLF>
  423. LCmd.Command := 'DATA'; {Do not Localize}
  424. LCmd.OnCommand := CommandDATA;
  425. SetEnhReply(LCmd.NormalReply , 354, '', RSSMTPSvrStartData, False);
  426. SetEnhReply(LCmd.ExceptionReply, 451, Id_EHR_PR_OTHER_TEMP, 'Internal Error' , False); {do not localize}
  427. LCmd := CommandHandlers.Add;
  428. // RSET <CRLF>
  429. LCmd.Command := 'RSET'; {Do not Localize}
  430. LCmd.NormalReply.SetReply(250, RSSMTPSvrOk);
  431. LCmd.OnCommand := CommandRSET;
  432. LCmd := CommandHandlers.Add;
  433. // STARTTLS <CRLF>
  434. LCmd.Command := 'STARTTLS'; {Do not Localize}
  435. SetEnhReply(LCmd.NormalReply, 220, Id_EHR_GENERIC_OK, RSSMTPSvrReadyForTLS, False);
  436. LCmd.OnCommand := CommandStartTLS;
  437. LCmd := CommandHandlers.Add;
  438. // BDAT <SP> <chunk-size> [<SP> LAST] <CRLF>
  439. LCmd.Command := 'BDAT'; {Do not Localize}
  440. LCmd.OnCommand := CommandBDAT;
  441. LCmd.ParseParams := True;
  442. SetEnhReply(LCmd.NormalReply, 250, Id_EHR_GENERIC_OK, '', False);
  443. SetEnhReply(LCmd.ExceptionReply, 451, Id_EHR_PR_OTHER_TEMP, 'Internal Error' , False); {do not localize}
  444. end;
  445. procedure TIdSMTPServer.MustUseTLS(ASender: TIdCommand);
  446. begin
  447. SetEnhReply(ASender.Reply, 530, Id_EHR_USE_STARTTLS, RSSMTPSvrReqSTARTTLS,
  448. TIdSMTPServerContext(ASender.Context).EHLO);
  449. end;
  450. procedure TIdSMTPServer.CommandAUTH(ASender: TIdCommand);
  451. var
  452. LContext: TIdSMTPServerContext;
  453. S, LMech: String;
  454. begin
  455. LContext := TIdSMTPServerContext(ASender.Context);
  456. //Note you can not use PIPELINING with AUTH
  457. LContext.PipeLining := False;
  458. if not LContext.EHLO then begin // Only available with EHLO
  459. BadSequenceError(ASender);
  460. Exit;
  461. end;
  462. if LContext.TLSIsRequired then begin
  463. MustUseTLS(ASender);
  464. Exit;
  465. end;
  466. if not Assigned(FOnUserLogin) then begin
  467. AuthFailed(ASender);
  468. Exit;
  469. end;
  470. if Length(ASender.UnparsedParams) > 0 then begin
  471. S := ASender.UnparsedParams;
  472. LMech := Fetch(S);
  473. DoAuthLogin(ASender, LMech, Trim(S));
  474. end else begin
  475. CmdSyntaxError(ASender);
  476. end;
  477. end;
  478. procedure TIdSMTPServer.CommandHELO(ASender: TIdCommand);
  479. var
  480. LContext : TIdSMTPServerContext;
  481. begin
  482. LContext := TIdSMTPServerContext(ASender.Context);
  483. //Note you can not use PIPELINING with HELO
  484. LContext.PipeLining := False;
  485. if LContext.SMTPState <> idSMTPNone then begin
  486. BadSequenceError(ASender);
  487. Exit;
  488. end;
  489. DoReset(LContext);
  490. LContext.HeloString := ASender.UnparsedParams;
  491. LContext.HELO := True;
  492. if SPFAuthOk(LContext, ASender.Reply, 'HELO', DomainName(ASender.UnparsedParams), ASender.UnparsedParams) then {do not localize}
  493. begin
  494. ASender.Reply.SetReply(250, IndyFormat(RSSMTPSvrHello, [ASender.UnparsedParams]));
  495. LContext.SMTPState := idSMTPHelo;
  496. end;
  497. end;
  498. procedure TIdSMTPServer.DoAuthLogin(ASender: TIdCommand; const Mechanism, InitialResponse: string);
  499. var
  500. S, LUsername, LPassword: string;
  501. LAuthFailed: Boolean;
  502. LAccepted: Boolean;
  503. LContext : TIdSMTPServerContext;
  504. LEncoder: TIdEncoderMIME;
  505. LDecoder: TIdDecoderMIME;
  506. begin
  507. LContext := TIdSMTPServerContext(ASender.Context);
  508. LAuthFailed := True;
  509. LContext.PipeLining := False;
  510. if TextIsSame(Mechanism, 'LOGIN') then begin {Do not Localize}
  511. // LOGIN USING THE LOGIN AUTH - BASE64 ENCODED
  512. try
  513. LEncoder := TIdEncoderMIME.Create;
  514. try
  515. LDecoder := TIdDecoderMIME.Create;
  516. try
  517. if InitialResponse = '' then begin
  518. // no [initial-response] parameter specified
  519. // Encoding a string literal?
  520. S := LEncoder.Encode('Username:'); {Do not Localize}
  521. ASender.Reply.SetReply(334, S); {Do not Localize}
  522. ASender.SendReply;
  523. S := Trim(LContext.Connection.IOHandler.ReadLn);
  524. end
  525. else if InitialResponse = '=' then begin {Do not Localize}
  526. // empty [initial-response] parameter value
  527. S := '';
  528. end else begin
  529. S := InitialResponse;
  530. end;
  531. if S <> '' then begin {Do not Localize}
  532. LUsername := LDecoder.DecodeString(S);
  533. end;
  534. // What? Encode this string literal?
  535. S := LEncoder.Encode('Password:'); {Do not Localize}
  536. ASender.Reply.SetReply(334, S); {Do not Localize}
  537. ASender.SendReply;
  538. S := Trim(ASender.Context.Connection.IOHandler.ReadLn);
  539. if S <> '' then begin
  540. LPassword := LDecoder.DecodeString(S);
  541. end;
  542. LAuthFailed := False;
  543. finally
  544. FreeAndNil(LDecoder);
  545. end;
  546. finally
  547. FreeAndNil(LEncoder);
  548. end;
  549. except
  550. end;
  551. end;
  552. // Add other login units here
  553. if not LAuthFailed then begin
  554. LAccepted := not Assigned(FOnUserLogin);
  555. if not LAccepted then begin
  556. FOnUserLogin(LContext, LUsername, LPassword, LAccepted);
  557. end;
  558. LContext.LoggedIn := LAccepted;
  559. if LAccepted then begin
  560. LContext.Username := LUsername;
  561. SetEnhReply(ASender.Reply, 235, Id_EHR_SEC_OTHER_OK, ' welcome ' + Trim(LUsername), LContext.EHLO); {Do not Localize}
  562. ASender.SendReply;
  563. Exit;
  564. end;
  565. end;
  566. AuthFailed(ASender);
  567. end;
  568. procedure TIdSMTPServer.SetEnhReply(AReply: TIdReply; const ANumericCode: Integer;
  569. const AEnhReply, AText: String; const IsEHLO: Boolean);
  570. begin
  571. if IsEHLO and (AReply is TIdReplySMTP) then begin
  572. TIdReplySMTP(AReply).SetEnhReply(ANumericCode, AEnhReply, AText);
  573. end else begin
  574. AReply.SetReply(ANumericCode, AText);
  575. end;
  576. end;
  577. procedure TIdSMTPServer.AuthFailed(ASender: TIdCommand);
  578. begin
  579. SetEnhReply(ASender.Reply, 535, Id_EHR_SEC_OTHER_PERM, RSSMTPSvrAuthFailed,
  580. TIdSMTPServerContext(ASender.Context).EHLO);
  581. ASender.SendReply;
  582. end;
  583. procedure TIdSMTPServer.AddrInvalid(ASender: TIdCommand; const AAddress : String = '');
  584. begin
  585. SetEnhReply(ASender.Reply, 500, Id_EHR_MSG_BAD_DEST, IndyFormat(RSSMTPSvrAddressError, [AAddress]),
  586. TIdSMTPServerContext(ASender.Context).EHLO);
  587. end;
  588. procedure TIdSMTPServer.AddrNotWillForward(ASender: TIdCommand; const AAddress : String = ''; const ATo : String = '');
  589. var
  590. LMsg: String;
  591. begin
  592. if ATo <> '' then begin
  593. LMsg := IndyFormat(RSSMTPUserNotLocal, [AAddress, ATo]);
  594. end else begin
  595. LMsg := IndyFormat(RSSMTPUserNotLocalNoAddr, [AAddress]);
  596. end;
  597. SetEnhReply(ASender.Reply, 521, Id_EHR_SEC_DEL_NOT_AUTH, LMsg,
  598. TIdSMTPServerContext(ASender.Context).EHLO);
  599. end;
  600. procedure TIdSMTPServer.AddrValid(ASender: TIdCommand; const AAddress : String = '');
  601. begin
  602. SetEnhReply(ASender.Reply, 250, Id_EHR_MSG_VALID_DEST, IndyFormat(RSSMTPSvrAddressOk, [AAddress]),
  603. TIdSMTPServerContext(ASender.Context).EHLO);
  604. end;
  605. procedure TIdSMTPServer.AddrNoRelaying(ASender: TIdCommand; const AAddress: String);
  606. begin
  607. SetEnhReply(ASender.Reply, 550, Id_EHR_SEC_DEL_NOT_AUTH, IndyFormat(RSSMTPSvrNoRelay, [AAddress]),
  608. TIdSMTPServerContext(ASender.Context).EHLO);
  609. end;
  610. procedure TIdSMTPServer.AddrWillForward(ASender: TIdCommand; const AAddress : String = ''; const ATo : String = '');
  611. var
  612. LMsg: String;
  613. begin
  614. if ATo <> '' then begin
  615. LMsg := IndyFormat(RSSMTPUserNotLocalFwdAddr, [AAddress, ATo]);
  616. end else begin
  617. LMsg := IndyFormat(RSSMTPUserNotLocalNoAddr, [AAddress]);
  618. end;
  619. SetEnhReply(ASender.Reply, 251, Id_EHR_MSG_VALID_DEST, LMsg,
  620. TIdSMTPServerContext(ASender.Context).EHLO);
  621. end;
  622. procedure TIdSMTPServer.AddrTooManyRecipients(ASender: TIdCommand);
  623. begin
  624. SetEnhReply(ASender.Reply,250,Id_EHR_PR_TOO_MANY_RECIPIENTS_PERM, RSSMTPTooManyRecipients,
  625. TIdSMTPServerContext(ASender.Context).EHLO);
  626. end;
  627. procedure TIdSMTPServer.AddrDisabledPerm(ASender: TIdCommand; const AAddress: String);
  628. begin
  629. SetEnhReply(ASender.Reply, 550, Id_EHR_MB_DISABLED_PERM, IndyFormat(RSSMTPAccountDisabled, [AAddress]),
  630. TIdSMTPServerContext(ASender.Context).EHLO);
  631. end;
  632. procedure TIdSMTPServer.AddrDisabledTemp(ASender: TIdCommand; const AAddress: String);
  633. begin
  634. SetEnhReply(ASender.Reply, 550, Id_EHR_MB_DISABLED_TEMP, IndyFormat(RSSMTPAccountDisabled, [AAddress]),
  635. TIdSMTPServerContext(ASender.Context).EHLO);
  636. end;
  637. procedure TIdSMTPServer.MailSubmitLimitExceeded(ASender: TIdCommand);
  638. begin
  639. SetEnhReply(ASender.Reply, 552, Id_EHR_MB_MSG_LEN_LIMIT, RSSMTPMsgLenLimit,
  640. TIdSMTPServerContext(ASender.Context).EHLO);
  641. ASender.SendReply;
  642. end;
  643. procedure TIdSMTPServer.MailSubmitLocalProcessingError(
  644. ASender: TIdCommand);
  645. begin
  646. SetEnhReply(ASender.Reply, 451, Id_EHR_MD_OTHER_TRANS, RSSMTPLocalProcessingError,
  647. TIdSMTPServerContext(ASender.Context).EHLO);
  648. ASender.SendReply;
  649. end;
  650. procedure TIdSMTPServer.MailSubmitOk(ASender: TIdCommand);
  651. begin
  652. SetEnhReply(ASender.Reply, 250, '', RSSMTPSvrOk, TIdSMTPServerContext(ASender.Context).EHLO);
  653. ASender.SendReply;
  654. end;
  655. procedure TIdSMTPServer.MailSubmitStorageExceededFull(ASender: TIdCommand);
  656. begin
  657. SetEnhReply(ASender.Reply, 552, Id_EHR_MB_FULL, RSSMTPSvrExceededStorageAlloc,
  658. TIdSMTPServerContext(ASender.Context).EHLO);
  659. ASender.SendReply;
  660. end;
  661. procedure TIdSMTPServer.MailSubmitSystemFull(ASender: TIdCommand);
  662. begin
  663. SetEnhReply(ASender.Reply, 452, Id_EHR_MD_MAIL_SYSTEM_FULL, RSSMTPSvrInsufficientSysStorage,
  664. TIdSMTPServerContext(ASender.Context).EHLO);
  665. ASender.SendReply;
  666. end;
  667. procedure TIdSMTPServer.MailSubmitTransactionFailed(ASender: TIdCommand);
  668. begin
  669. SetEnhReply(ASender.Reply, 554, Id_EHR_MB_OTHER_STATUS_TRANS, RSSMTPSvrTransactionFailed,
  670. TIdSMTPServerContext(ASender.Context).EHLO);
  671. ASender.SendReply;
  672. end;
  673. procedure TIdSMTPServer.MailFromAccept(ASender: TIdCommand; const AAddress : String = '');
  674. begin
  675. SetEnhReply(ASender.Reply, 250, Id_EHR_MSG_OTH_OK, IndyFormat(RSSMTPSvrAddressOk, [AAddress]),
  676. TIdSMTPServerContext(ASender.Context).EHLO);
  677. end;
  678. procedure TIdSMTPServer.MailFromReject(ASender: TIdCommand; const AAddress : String = '');
  679. begin
  680. SetEnhReply(ASender.Reply, 550, Id_EHR_SEC_DEL_NOT_AUTH, IndyFormat(RSSMTPSvrNotPermitted, [AAddress]),
  681. TIdSMTPServerContext(ASender.Context).EHLO);
  682. end;
  683. procedure TIdSMTPServer.NoHello(ASender: TIdCommand);
  684. begin
  685. SetEnhReply(ASender.Reply, 501, Id_EHR_PR_OTHER_PERM, RSSMTPSvrNoHello,
  686. TIdSMTPServerContext(ASender.Context).EHLO);
  687. end;
  688. procedure TIdSMTPServer.CommandMAIL(ASender: TIdCommand);
  689. var
  690. EMailAddress: TIdEMailAddressItem;
  691. LContext : TIdSMTPServerContext;
  692. LM : TIdMailFromReply;
  693. LParams: TStringList;
  694. S: String;
  695. LSize: Integer;
  696. LBodyType: TIdSMTPBodyType;
  697. begin
  698. //Note that unlike other protocols, it might not be possible
  699. //to completely disable MAIL FROM for people not using SSL
  700. //because SMTP is also used from server to server mail transfers.
  701. LContext := TIdSMTPServerContext(ASender.Context);
  702. if LContext.HELO or LContext.EHLO then begin // Looking for either HELO or EHLO
  703. //reset all information
  704. LContext.From := ''; {Do not Localize}
  705. LContext.RCPTList.Clear;
  706. if TextStartsWith(ASender.UnparsedParams, 'FROM:') then begin {Do not Localize}
  707. EMailAddress := TIdEMailAddressItem.Create(nil);
  708. try
  709. S := TrimLeft(Copy(ASender.UnparsedParams, 6, MaxInt));
  710. EMailAddress.Text := Fetch(S);
  711. if SPFAuthOk(LContext, ASender.Reply, 'MAIL FROM', EmailAddress.Domain, EmailAddress.Address) then {do not localize}
  712. begin
  713. LM := mAccept;
  714. LParams := TStringList.Create;
  715. try
  716. SplitDelimitedString(S, LParams, True);
  717. // RLebeau: check the message size before accepting the message
  718. if LParams.IndexOfName('SIZE') <> -1 then
  719. begin
  720. LSize := IndyStrToInt(LParams.Values['SIZE']);
  721. if (FMaxMsgSize > 0) and (LSize > FMaxMsgSize) then begin
  722. MailSubmitLimitExceeded(ASender);
  723. Exit;
  724. end;
  725. end else begin
  726. LSize := -1;
  727. end;
  728. // RLebeau: get the message encoding type and store it for later use
  729. if LParams.IndexOfName('BODY') <> -1 then {do not localize}
  730. begin
  731. case PosInStrArray(LParams.Values['BODY'], ['7BIT', '8BITMIME', 'BINARYMIME'], False) of {do not localize}
  732. 0: LBodyType := idSMTP7Bit;
  733. 1: LBodyType := idSMTP8BitMime;
  734. 2: LBodyType := idSMTPBinaryMime;
  735. else
  736. InvalidSyntax(ASender);
  737. Exit;
  738. end;
  739. end else begin
  740. LBodyType := idSMTP8BitMime;
  741. end;
  742. // let the user perform custom validations
  743. if Assigned(FOnMailFrom) then begin
  744. FOnMailFrom(LContext, EMailAddress.Address, LParams, LM);
  745. end;
  746. finally
  747. FreeAndNil(LParams);
  748. end;
  749. case LM of
  750. mAccept :
  751. begin
  752. MailFromAccept(ASender, EMailAddress.Address);
  753. LContext.From := EMailAddress.Address;
  754. // RLebeau: store the message size in case the OnRCPT handler
  755. // wants to verify the size on a per-recipient basis
  756. LContext.MsgSize := LSize;
  757. LContext.FBodyType := LBodyType;
  758. LContext.SMTPState := idSMTPMail;
  759. end;
  760. mReject :
  761. begin
  762. MailFromReject(ASender, EMailAddress.Text);
  763. end;
  764. mSystemFull:
  765. begin
  766. MailSubmitSystemFull(ASender);
  767. end;
  768. mLimitExceeded:
  769. begin
  770. MailSubmitLimitExceeded(ASender);
  771. end;
  772. end;
  773. end;
  774. finally
  775. FreeAndNil(EMailAddress);
  776. end;
  777. end else begin
  778. InvalidSyntax(ASender);
  779. end;
  780. end else begin // No EHLO / HELO was received
  781. NoHello(ASender);
  782. end;
  783. LContext.CheckPipeLine;
  784. end;
  785. procedure TIdSMTPServer.InvalidSyntax(ASender: TIdCommand);
  786. begin
  787. SetEnhReply(ASender.Reply, 501, Id_EHR_PR_INVALID_CMD_ARGS, RSPOP3SvrInvalidSyntax,
  788. TIdSMTPServerContext(ASender.Context).EHLO);
  789. end;
  790. procedure TIdSMTPServer.CommandRCPT(ASender: TIdCommand);
  791. var
  792. EMailAddress: TIdEMailAddressItem;
  793. LContext : TIdSMTPServerContext;
  794. LAction : TIdRCPToReply;
  795. LParams: TStringList;
  796. LForward, S : String;
  797. begin
  798. LForward := '';
  799. LContext := TIdSMTPServerContext(ASender.Context);
  800. if not (LContext.SMTPState in [idSMTPMail, idSMTPRcpt]) then begin
  801. BadSequenceError(ASender);
  802. Exit;
  803. end;
  804. if LContext.HELO or LContext.EHLO then begin
  805. if TextStartsWith(ASender.UnparsedParams, 'TO:') then begin {Do not Localize}
  806. LAction := rRelayDenied;
  807. //do not change this in the OnRcptTo event unless one of the following
  808. //things is TRUE:
  809. //
  810. //1. The user authenticated to the SMTP server
  811. //
  812. //2. The user is from an IP address being served by the SMTP server.
  813. // Test the IP address for this.
  814. //
  815. //3. Another SMTP server outside of your network is sending E-Mail to a
  816. // user on YOUR system.
  817. //
  818. //The reason is that you do not want to relay E-Messages for outsiders
  819. //if the E-Mail is from outside of your network. Be very CAREFUL. Otherwise,
  820. //you have a security hazard that spammers can abuse.
  821. EMailAddress := TIdEMailAddressItem.Create(nil);
  822. try
  823. S := TrimLeft(Copy(ASender.UnparsedParams, 4, MaxInt));
  824. // TODO: remove this Fetch() and let TIdEMailAddressItem parse the
  825. // entire text, as it may have embedded spaces in it
  826. EMailAddress.Text := Fetch(S);
  827. if Assigned(FOnRcptTo) then begin
  828. LParams := TStringList.Create;
  829. try
  830. SplitDelimitedString(S, LParams, True);
  831. FOnRcptTo(LContext, EMailAddress.Address, LParams, LAction, LForward);
  832. finally
  833. FreeAndNil(LParams);
  834. end;
  835. case LAction of
  836. rAddressOk :
  837. begin
  838. AddrValid(ASender, EMailAddress.Address);
  839. LContext.RCPTList.Add.Assign(EMailAddress);
  840. LContext.SMTPState := idSMTPRcpt;
  841. end;
  842. rRelayDenied :
  843. begin
  844. AddrNoRelaying(ASender, EMailAddress.Address);
  845. end;
  846. rWillForward :
  847. begin
  848. AddrWillForward(ASender, EMailAddress.Address, LForward);
  849. if LForward <> '' then begin
  850. LContext.RCPTList.Add.Text := LForward;
  851. end else begin
  852. LContext.RCPTList.Add.Assign(EMailAddress);
  853. end;
  854. LContext.SMTPState := idSMTPRcpt;
  855. end;
  856. rNoForward : AddrNotWillForward(ASender, EMailAddress.Address, LForward);
  857. rTooManyAddresses : AddrTooManyRecipients(ASender);
  858. rDisabledPerm : AddrDisabledPerm(ASender, EMailAddress.Address);
  859. rDisabledTemp : AddrDisabledTemp(ASender, EMailAddress.Address);
  860. rSystemFull : MailSubmitSystemFull(ASender);
  861. rLimitExceeded : MailSubmitLimitExceeded(ASender);
  862. else
  863. AddrInvalid(ASender, EMailAddress.Address);
  864. end;
  865. end else begin
  866. raise EIdSMTPServerNoRcptTo.Create(RSSMTPNoOnRcptTo);
  867. end;
  868. finally
  869. FreeAndNil(EMailAddress);
  870. end;
  871. end else begin
  872. SetEnhReply(ASender.Reply, 501, Id_EHR_PR_SYNTAX_ERR,RSSMTPSvrParmErrRcptTo,
  873. LContext.EHLO);
  874. end;
  875. end else begin // No EHLO / HELO was received
  876. NoHello(ASender);
  877. end;
  878. LContext.CheckPipeLine;
  879. end;
  880. procedure TIdSMTPServer.CommandSTARTTLS(ASender: TIdCommand);
  881. var
  882. LContext : TIdSMTPServerContext;
  883. begin
  884. LContext := TIdSMTPServerContext(ASender.Context);
  885. if not LContext.EHLO then begin
  886. BadSequenceError(ASender);
  887. LContext.PipeLining := False;
  888. Exit;
  889. end;
  890. if not LContext.CanUseExplicitTLS then begin
  891. CmdSyntaxError(ASender);
  892. LContext.PipeLining := False;
  893. Exit;
  894. end;
  895. if LContext.UsingTLS then begin // we are already using TLS
  896. BadSequenceError(ASender);
  897. LContext.PipeLining := False;
  898. Exit;
  899. end;
  900. SetEnhReply(ASender.Reply, 220, Id_EHR_GENERIC_OK, RSSMTPSvrReadyForTLS, LContext.EHLO);
  901. ASender.SendReply;
  902. LContext.PipeLining := False;
  903. TIdSSLIOHandlerSocketBase(LContext.Connection.IOHandler).PassThrough := False;
  904. DoReset(LContext, True);
  905. end;
  906. procedure TIdSMTPServer.CommandNOOP(ASender: TIdCommand);
  907. begin
  908. //we just use the default NOOP and only clear pipelining for synchronization
  909. TIdSMTPServerContext(ASender.Context).PipeLining := False;
  910. end;
  911. procedure TIdSMTPServer.CommandQUIT(ASender: TIdCommand);
  912. var
  913. LContext: TIdSMTPServerContext;
  914. begin
  915. //clear pipelining before exit
  916. LContext := TIdSMTPServerContext(ASender.Context);
  917. LContext.PipeLining := False;
  918. DoReset(LContext);
  919. ASender.SendReply;
  920. end;
  921. procedure TIdSMTPServer.CommandRSET(ASender: TIdCommand);
  922. begin
  923. DoReset(TIdSMTPServerContext(ASender.Context));
  924. end;
  925. // RLebeau: if HostByAddress() fails, the received
  926. // message gets lost, so trapping any exceptions here
  927. function AddrFromHost(const AIP: String): String;
  928. begin
  929. try
  930. Result := GStack.HostByAddress(AIP);
  931. except
  932. Result := 'unknown'; {do not localize}
  933. end;
  934. end;
  935. procedure TIdSMTPServer.CommandDATA(ASender: TIdCommand);
  936. const
  937. BodyEncType: array[TIdSMTPBodyType] of IdTextEncodingType = (encASCII, enc8Bit, enc8Bit);
  938. var
  939. LContext : TIdSMTPServerContext;
  940. LStream: TStream;
  941. LEncoding: IIdTextEncoding;
  942. begin
  943. LContext := TIdSMTPServerContext(ASender.Context);
  944. if LContext.SMTPState <> idSMTPRcpt then begin
  945. BadSequenceError(ASender);
  946. LContext.PipeLining := False;
  947. Exit;
  948. end;
  949. if LContext.HELO or LContext.EHLO then begin
  950. // BINARYMIME cannot be used with the DATA command
  951. if LContext.FBodyType = idSMTPBinaryMime then begin
  952. BadSequenceError(ASender);
  953. LContext.PipeLining := False;
  954. Exit;
  955. end;
  956. MsgBegan(LContext, LStream);
  957. try
  958. // RLebeau: TODO - do not even create the stream if the OnMsgReceive
  959. // event is not assigned, or at least create a stream that discards
  960. // any data received...
  961. LEncoding := IndyTextEncoding(BodyEncType[LContext.FBodyType]);
  962. SetEnhReply(ASender.Reply, 354, '', RSSMTPSvrStartData, LContext.EHLO);
  963. ASender.SendReply;
  964. LContext.PipeLining := False;
  965. LContext.Connection.IOHandler.Capture(LStream, '.', True, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF}); {Do not Localize}
  966. MsgReceived(ASender, LStream);
  967. finally
  968. FreeAndNil(LStream);
  969. DoReset(LContext);
  970. end;
  971. end else begin // No EHLO / HELO was received
  972. NoHello(ASender);
  973. end;
  974. LContext.PipeLining := False;
  975. end;
  976. procedure TIdSMTPServer.CommandBDAT(ASender: TIdCommand);
  977. var
  978. LContext : TIdSMTPServerContext;
  979. LSize: TIdStreamSize;
  980. LLast: Boolean;
  981. begin
  982. LContext := TIdSMTPServerContext(ASender.Context);
  983. if not (LContext.SMTPState in [idSMTPRcpt, idSMTPBDat]) then begin
  984. BadSequenceError(ASender);
  985. LContext.PipeLining := False;
  986. Exit;
  987. end;
  988. if LContext.HELO or LContext.EHLO then begin
  989. if ASender.Params.Count > 0 then begin
  990. LSize := IndyStrToStreamSize(ASender.Params[0], -1);
  991. if LSize < 0 then
  992. begin
  993. CmdSyntaxError(ASender);
  994. LContext.PipeLining := False;
  995. Exit;
  996. end;
  997. if ASender.Params.Count > 1 then begin
  998. if not TextIsSame(ASender.Params[1], 'LAST') then begin {do not localize}
  999. LContext.Connection.IOHandler.Discard(LSize);
  1000. CmdSyntaxError(ASender);
  1001. LContext.PipeLining := False;
  1002. Exit;
  1003. end;
  1004. LLast := True;
  1005. end else begin
  1006. LLast := False;
  1007. end;
  1008. LContext.SMTPState := idSMTPBDat;
  1009. if not Assigned(LContext.FBDataStream) then begin
  1010. MsgBegan(LContext, LContext.FBDataStream);
  1011. end;
  1012. LContext.Connection.IOHandler.ReadStream(LContext.FBDataStream, LSize, False);
  1013. if not LLast then begin
  1014. Exit; // do not turn off pipelining yet
  1015. end;
  1016. try
  1017. MsgReceived(ASender, LContext.FBDataStream);
  1018. finally
  1019. DoReset(LContext);
  1020. end;
  1021. end else begin
  1022. CmdSyntaxError(ASender);
  1023. end;
  1024. end else begin // No EHLO / HELO was received
  1025. NoHello(ASender);
  1026. end;
  1027. LContext.PipeLining := False;
  1028. end;
  1029. procedure TIdSMTPServer.DoReset(AContext: TIdSMTPServerContext; AIsTLSReset: Boolean = False);
  1030. begin
  1031. AContext.Reset(AIsTLSReset);
  1032. if Assigned(FOnReset) then begin
  1033. FOnReset(AContext);
  1034. end;
  1035. end;
  1036. procedure TIdSMTPServer.SetMaxMsgSize(AValue: Integer);
  1037. begin
  1038. FMaxMsgSize := IndyMax(AValue, 0);
  1039. end;
  1040. // RLebeau: processing the tokens dynamically now
  1041. // so that only the tokens that are actually present
  1042. // will be processed. This helps to avoid unnecessary
  1043. // lookups for tokens that are not actually used
  1044. function ReplaceReceivedTokens(AContext: TIdSMTPServerContext; const AReceivedString: String): String;
  1045. var
  1046. LTokens: TStringList;
  1047. i: Integer;
  1048. //we do it this way so we can take advantage of the StringBuilder in DotNET.
  1049. ReplaceOld, ReplaceNew: array of string;
  1050. {$IFNDEF HAS_TStrings_ValueFromIndex}
  1051. S: String;
  1052. {$ENDIF}
  1053. begin
  1054. LTokens := TStringList.Create;
  1055. try
  1056. if Pos('$hostname', AReceivedString) <> 0 then begin {do not localize}
  1057. LTokens.Add('$hostname=' + AddrFromHost(AContext.Binding.PeerIP)); {do not localize}
  1058. end;
  1059. if Pos('$ipaddress', AReceivedString) <> 0 then begin {do not localize}
  1060. LTokens.Add('$ipaddress=' + AContext.Binding.PeerIP); {do not localize}
  1061. end;
  1062. if Pos('$helo', AReceivedString) <> 0 then begin {do not localize}
  1063. LTokens.Add('$helo=' + AContext.HeloString); {do not localize}
  1064. end;
  1065. if Pos('$protocol', AReceivedString) <> 0 then begin {do not localize}
  1066. LTokens.Add('$protocol=' + iif(AContext.EHLO, 'esmtp', 'smtp')); {do not localize}
  1067. end;
  1068. if Pos('$servername', AReceivedString) <> 0 then begin {do not localize}
  1069. LTokens.Add('$servername=' + TIdSMTPServer(AContext.Server).ServerName); {do not localize}
  1070. end;
  1071. if Pos('$svrhostname', AReceivedString) <> 0 then begin {do not localize}
  1072. LTokens.Add('$svrhostname=' + AddrFromHost(AContext.Binding.IP)); {do not localize}
  1073. end;
  1074. if Pos('$svripaddress', AReceivedString) <> 0 then begin {do not localize}
  1075. LTokens.Add('$svripaddress=' + AContext.Binding.IP); {do not localize}
  1076. end;
  1077. if LTokens.Count > 0 then
  1078. begin
  1079. SetLength(ReplaceNew, LTokens.Count);
  1080. SetLength(ReplaceOld, LTokens.Count);
  1081. for i := 0 to LTokens.Count-1 do begin
  1082. ReplaceOld[i] := LTokens.Names[i];
  1083. {$IFDEF HAS_TStrings_ValueFromIndex}
  1084. ReplaceNew[i] := LTokens.ValueFromIndex[i];
  1085. {$ELSE}
  1086. S := LTokens.Strings[i];
  1087. ReplaceNew[i] := Copy(S, Pos('=', S)+1, MaxInt);
  1088. {$ENDIF}
  1089. end;
  1090. Result := StringsReplace(AReceivedString, ReplaceOld, ReplaceNew);
  1091. end else begin
  1092. Result := AReceivedString;
  1093. end;
  1094. finally
  1095. FreeAndNil(LTokens);
  1096. end;
  1097. end;
  1098. procedure TIdSMTPServer.MsgBegan(AContext: TIdSMTPServerContext; var VStream: TStream);
  1099. var
  1100. LReceivedString: string;
  1101. begin
  1102. VStream := nil;
  1103. if Assigned(FOnBeforeMsg) then begin
  1104. FOnBeforeMsg(AContext, VStream);
  1105. end;
  1106. if not Assigned(VStream) then begin
  1107. VStream := TMemoryStream.Create;
  1108. end;
  1109. try
  1110. LReceivedString := IdSMTPSvrReceivedString;
  1111. if Assigned(FOnReceived) then begin
  1112. FOnReceived(AContext, LReceivedString);
  1113. end;
  1114. if AContext.FinalStage then begin
  1115. // If at the final delivery stage, add the Return-Path line for the received MAIL FROM line.
  1116. WriteStringToStream(VStream, 'Received-Path: <' + AContext.From + '>' + EOL); {do not localize}
  1117. end;
  1118. if LReceivedString <> '' then begin
  1119. WriteStringToStream(VStream, ReplaceReceivedTokens(AContext, LReceivedString) + EOL);
  1120. end;
  1121. except
  1122. FreeAndNil(VStream);
  1123. raise;
  1124. end;
  1125. end;
  1126. procedure TIdSMTPServer.MsgReceived(ASender: TIdCommand; AMsgData: TStream);
  1127. var
  1128. LAction: TIdDataReply;
  1129. begin
  1130. LAction := dOk;
  1131. AMsgData.Position := 0;
  1132. // RLebeau: verify the message size now
  1133. if (FMaxMsgSize > 0) and (AMsgData.Size > FMaxMsgSize) then begin
  1134. LAction := dLimitExceeded;
  1135. end
  1136. else if Assigned(FOnMsgReceive) then begin
  1137. FOnMsgReceive(TIdSMTPServerContext(ASender.Context), AMsgData, LAction);
  1138. end;
  1139. case LAction of
  1140. dOk : MailSubmitOk(ASender); //accept the mail message
  1141. dMBFull : MailSubmitStorageExceededFull(ASender); //Mail box full
  1142. dSystemFull : MailSubmitSystemFull(ASender); //no more space on server
  1143. dLocalProcessingError : MailSubmitLocalProcessingError(ASender); //local processing error
  1144. dTransactionFailed : MailSubmitTransactionFailed(ASender); //transaction failed
  1145. dLimitExceeded : MailSubmitLimitExceeded(ASender); //exceeded administrative limit
  1146. end;
  1147. end;
  1148. function TIdSMTPServer.SPFAuthOk(AContext: TIdSMTPServerContext; AReply: TIdReply;
  1149. const ACmd, ADomain, AIdentity: String): Boolean;
  1150. var
  1151. LAction: TIdSPFReply;
  1152. begin
  1153. Result := False;
  1154. LAction := spfNeutral;
  1155. if Assigned(FOnSPFCheck) then begin
  1156. FOnSPFCheck(AContext, AContext.Binding.PeerIP, ADomain, AIdentity, LAction);
  1157. end;
  1158. case LAction of
  1159. spfNone, spfNeutral, spfPass, spfSoftFail:
  1160. // let the caller handle the reply as needed
  1161. Result := True;
  1162. spfFail:
  1163. begin
  1164. SetEnhReply(AReply, 550, '5.7.1', IndyFormat(RSSMTPSvrSPFCheckFailed, [ACmd]), AContext.EHLO); {do not localize}
  1165. end;
  1166. spfTempError, spfPermError:
  1167. begin
  1168. SetEnhReply(AReply, 451, '4.4.3', IndyFormat(RSSMTPSvrSPFCheckError, [ACmd]), AContext.EHLO); {do not localize}
  1169. end;
  1170. end;
  1171. end;
  1172. { TIdSMTPServerContext }
  1173. procedure TIdSMTPServerContext.CheckPipeLine;
  1174. begin
  1175. if not Connection.IOHandler.InputBufferIsEmpty then begin
  1176. PipeLining := True;
  1177. end;
  1178. end;
  1179. constructor TIdSMTPServerContext.Create(AConnection: TIdTCPConnection;
  1180. AYarn: TIdYarn; AList: TIdContextThreadList = nil);
  1181. begin
  1182. inherited Create(AConnection, AYarn, AList);
  1183. SMTPState := idSMTPNone;
  1184. From := '';
  1185. HELO := False;
  1186. EHLO := False;
  1187. Username := '';
  1188. Password := '';
  1189. LoggedIn := False;
  1190. FRCPTList := TIdEMailAddressList.Create(nil);
  1191. end;
  1192. destructor TIdSMTPServerContext.Destroy;
  1193. begin
  1194. FreeAndNil(FRCPTList);
  1195. inherited Destroy;
  1196. end;
  1197. function TIdSMTPServerContext.GetUsingTLS: Boolean;
  1198. begin
  1199. Result := Connection.IOHandler is TIdSSLIOHandlerSocketBase;
  1200. if Result then begin
  1201. Result := not TIdSSLIOHandlerSocketBase(Connection.IOHandler).PassThrough;
  1202. end;
  1203. end;
  1204. function TIdSMTPServerContext.GetCanUseExplicitTLS: Boolean;
  1205. begin
  1206. Result := Connection.IOHandler is TIdSSLIOHandlerSocketBase;
  1207. if Result then begin
  1208. Result := TIdSMTPServer(Server).UseTLS in ExplicitTLSVals;
  1209. end;
  1210. end;
  1211. function TIdSMTPServerContext.GetTLSIsRequired: Boolean;
  1212. begin
  1213. Result := TIdSMTPServer(Server).UseTLS = utUseRequireTLS;
  1214. if Result then begin
  1215. Result := not UsingTLS;
  1216. end;
  1217. end;
  1218. procedure TIdSMTPServerContext.Reset(AIsTLSReset: Boolean = False);
  1219. begin
  1220. // RLebeau: do not reset the user authentication except for STARTTLS! A
  1221. // normal reset (RSET, HELO/EHLO after a session is started, and QUIT)
  1222. // should only abort the current mail transaction and clear its buffers
  1223. // and state tables, nothing more
  1224. if (not AIsTLSReset) and (FEHLO or FHELO) then begin
  1225. FSMTPState := idSMTPHelo;
  1226. end else begin
  1227. FSMTPState := idSMTPNone;
  1228. FEHLO := False;
  1229. FHELO := False;
  1230. FHeloString := '';
  1231. FUsername := '';
  1232. FPassword := '';
  1233. FLoggedIn := False;
  1234. end;
  1235. FFrom := '';
  1236. FRCPTList.Clear;
  1237. FMsgSize := 0;
  1238. FBodyType := idSMTP8BitMime;
  1239. FFinalStage := False;
  1240. FreeAndNil(FBDataStream);
  1241. CheckPipeLine;
  1242. end;
  1243. procedure TIdSMTPServerContext.SetPipeLining(const AValue: Boolean);
  1244. begin
  1245. if AValue and (not PipeLining) then begin
  1246. Connection.IOHandler.WriteBufferOpen;
  1247. end
  1248. else if (not AValue) and PipeLining then begin
  1249. Connection.IOHandler.WriteBufferClose;
  1250. end;
  1251. FPipeLining := AValue;
  1252. end;
  1253. end.