IdPOP3Server.pas 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780
  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.36 2/8/05 5:59:04 PM RLebeau
  18. Updated various CommandHandlers to call TIdReply.SetReply() instead of
  19. setting the Code and Text properties individually
  20. Rev 1.35 12/2/2004 4:23:56 PM JPMugaas
  21. Adjusted for changes in Core.
  22. Rev 1.34 7/6/2004 4:53:38 PM DSiders
  23. Corrected spelling of Challenge in properties, methods, types.
  24. Rev 1.33 6/16/04 12:54:16 PM RLebeau
  25. Removed redundant localization comments
  26. Rev 1.31 6/16/04 12:31:08 PM RLebeau
  27. compiler error
  28. Rev 1.30 6/16/04 12:13:04 PM RLebeau
  29. Added overrides for CreateExceptionReply, CreateGreeting, CreateHelpReply,
  30. CreateMaxConnectionReply, and CreateReplyUnknownCommand methods
  31. Rev 1.29 5/16/04 5:25:22 PM RLebeau
  32. Added GetReplyClass() and GetRepliesClass() overrides.
  33. Rev 1.28 3/1/2004 1:08:36 PM JPMugaas
  34. Fixed for new code.
  35. Rev 1.27 2004.02.03 5:44:14 PM czhower
  36. Name changes
  37. Rev 1.26 1/29/2004 9:14:46 AM JPMugaas
  38. POP3Server should now compile in DotNET.
  39. Rev 1.25 1/21/2004 3:27:08 PM JPMugaas
  40. InitComponent
  41. Rev 1.24 10/25/2003 06:52:16 AM JPMugaas
  42. Updated for new API changes and tried to restore some functionality.
  43. Rev 1.23 10/24/2003 4:38:00 PM DSiders
  44. Added localization comments.
  45. Modified to use OK and ERR constants in response messages.
  46. Rev 1.22 2003.10.21 9:13:12 PM czhower
  47. Now compiles.
  48. Rev 1.21 2003.10.12 4:04:18 PM czhower
  49. compile todos
  50. Rev 1.20 9/19/2003 03:30:20 PM JPMugaas
  51. Now should compile again.
  52. Rev 1.19 7/9/2003 10:59:16 PM BGooijen
  53. Added IdCommandHandlers to the uses-clause
  54. Rev 1.18 5/30/2003 9:05:14 PM BGooijen
  55. changed numeric replycodes to text reply codes
  56. Rev 1.17 5/30/2003 8:49:48 PM BGooijen
  57. Changed TextCode to Code
  58. Rev 1.16 5/26/2003 04:28:22 PM JPMugaas
  59. Removed GenerateReply and ParseResponse calls because those functions are
  60. being removed.
  61. Rev 1.15 5/26/2003 12:24:02 PM JPMugaas
  62. Rev 1.14 5/25/2003 03:46:00 AM JPMugaas
  63. Rev 1.13 5/21/2003 2:25:06 PM BGooijen
  64. changed due to change in IdCmdTCPServer from ExceptionReplyCode: Integer to
  65. ExceptionReply: TIdReply
  66. Rev 1.12 5/20/2003 10:58:24 AM JPMugaas
  67. SetExceptionReplyCode now validated by TIdReplyPOP3. This way, it can only
  68. accept our integer codes for +OK, -ERR, and +.
  69. Rev 1.11 5/19/2003 08:59:30 PM JPMugaas
  70. Now uses new reply object for all commands.
  71. Rev 1.9 5/15/2003 08:30:32 AM JPMugaas
  72. Rev 1.9 5/15/2003 07:38:50 AM JPMugaas
  73. No longer adds a challenge banner to the main Greeting TIdRFCReply.
  74. Rev 1.8 5/13/2003 08:12:12 PM JPMugaas
  75. Rev 1.7 5/13/2003 12:43:20 PM JPMugaas
  76. APOP redesigned so that it will handle the Challenge in the banner and do the
  77. hashes itself. A Challenge will be displayed in the banner if the APOP event
  78. is used.
  79. Rev 1.6 3/20/2003 07:22:28 AM JPMugaas
  80. Rev 1.5 3/17/2003 02:25:30 PM JPMugaas
  81. Updated to use new TLS framework. Now can require that users use TLS. Note
  82. that this setting create an incompatiability with Norton AntiVirus because
  83. that does act as a "man in the middle" when intercepting E-Mail for virus
  84. scanning.
  85. Rev 1.4 3/14/2003 10:44:34 PM BGooijen
  86. Removed warnings, changed StartSSL to PassThrough:=false;
  87. Rev 1.2 3/13/2003 10:05:30 AM JPMugaas
  88. Updated component to work with the new SSL restructure.
  89. Rev 1.1 2/6/2003 03:18:20 AM JPMugaas
  90. Updated components that compile with Indy 10.
  91. Rev 1.0 11/13/2002 07:58:28 AM JPMugaas
  92. 28-Sep-2002: Bas Gooijen
  93. - Added CAPA and STLS (RFC 2449 and 2595)
  94. - Added ImplicitTLS
  95. 02-May-2002: Andy Neillans
  96. - Bug Fix 551116 -Sys. StrToInt needed 'Sys.Trimming#
  97. 30-Apr-2002: Allen O'Neill.
  98. - Failsafe .. added check for ParamCount in reading Username and password - previously
  99. if either were sent in blank we got an IndexOutOfBounds error.
  100. 13-Apr-2002:
  101. - Corrections :) And some Greeting.Text / And other response, glitches
  102. 3-Apr-2002:
  103. - Minor changes. (Greeting.Text)
  104. 1-Apr-2002:
  105. - Completed rewrite! At Last!
  106. 15-Feb-2002: Andy
  107. - Started rewrite for use of CommandHandlers
  108. 13-Jan-2002:
  109. -Fixed Sys.Formatting bug.
  110. 26-Dec-2000:
  111. -Andrew Neillans found a bug on line 157. Originally it was
  112. if Assigned(OnCommandLIST) then OnCommandRETR(...).
  113. Changed to OnCommandLIST(...). Thanks Andrew!
  114. 29-Oct-2000:
  115. -I discovered I really shouldn't program at night.
  116. The error wasn't that it shouldn't be Succ (Because it should), but
  117. because I forgot to implement LIST
  118. 27-Oct-2000:
  119. -Fixed a dumb bug. Originally coded command parsing as Succ(PosInStrArray)
  120. Should be just PosInStrArray b/c it is not a dynamic array. The bounds
  121. are constant.
  122. 25-Oct-2000:
  123. -Created Unit.
  124. -Created new IdPOP3Server Server Component according to RFC 1939
  125. }
  126. unit IdPOP3Server;
  127. interface
  128. {$i IdCompilerDefines.inc}
  129. {
  130. Indy POP3 Server
  131. Original Programmer: Luke Croteau
  132. Current Maintainer: Andrew Neillans
  133. No Copyright. Code is given to the Indy Pit Crew.
  134. Quick Notes:
  135. A few of the methods return a default message number if a number isn't entered.
  136. The LIST, DELE, RETR, UIDL, and TOP command will return a -1 in the parameters
  137. if the value isn't specified by the client.
  138. Some functions require this capability. For example, the LIST command can operate
  139. either by a certain message number or a with no arguments. See RFC1939 for details.
  140. }
  141. uses
  142. Classes,
  143. IdAssignedNumbers,
  144. IdCommandHandlers,
  145. IdContext,
  146. IdCustomTCPServer,
  147. IdCmdTCPServer,
  148. IdException,
  149. IdExplicitTLSClientServerBase,
  150. IdGlobal,
  151. IdReply,
  152. IdMailBox,
  153. IdTCPConnection;
  154. {
  155. We can not port APOP to NET due to the use of GetSystemClock and a process ID
  156. Kudzu: Why not? .NET can get these.....
  157. }
  158. const
  159. DEF_POP3_IMPLICIT_TLS = False;
  160. type
  161. TIdPOP3ServerContext = class(TIdServerContext)
  162. protected
  163. // what needs to be stored...
  164. fUsername : String;
  165. fPassword : String;
  166. fAuthenticated: boolean;
  167. fAPOP3Challenge : String;
  168. //
  169. function GetUsingTLS: Boolean;
  170. function GetCanUseExplicitTLS: Boolean;
  171. function GetTLSIsRequired: Boolean;
  172. public
  173. // Any functions for vars
  174. property APOP3Challenge: string read FAPOP3Challenge write FAPOP3Challenge;
  175. property Authenticated: boolean read fAuthenticated;
  176. property Username: string read fUsername;
  177. property Password: string read fPassword;
  178. property UsingTLS: Boolean read GetUsingTLS;
  179. property CanUseExplicitTLS: Boolean read GetCanUseExplicitTLS;
  180. property TLSIsRequired: Boolean read GetTLSIsRequired;
  181. end;
  182. TIdPOP3ServerNoParamEvent = procedure (aCmd: TIdCommand) of object;
  183. TIdPOP3ServerStatEvent = procedure(aCmd: TIdCommand; out oCount: integer; out oSize: Int64) of object;
  184. TIdPOP3ServerMessageNumberEvent = procedure (aCmd: TIdCommand; AMsgNo :Integer) of object;
  185. TIdPOP3ServerLogin = procedure(aContext: TIdContext; aServerContext: TIdPOP3ServerContext) of object;
  186. TIdPOP3ServerCAPACommandEvent = procedure(aContext: TIdContext; aCapabilities: TStrings) of object;
  187. //Note that we require the users valid password so we can hash it with the Challenge we greeted the user with.
  188. TIdPOP3ServerAPOPCommandEvent = procedure (aCmd: TIdCommand; aMailboxID: String; var vUsersPassword: String) of object;
  189. TIdPOP3ServerTOPCommandEvent = procedure (aCmd: TIdCommand; aMsgNo: Integer; aLines: Integer) of object;
  190. EIdPOP3ServerException = class(EIdException);
  191. EIdPOP3ImplicitTLSRequiresSSL = class(EIdPOP3ServerException);
  192. TIdPOP3Server = class(TIdExplicitTLSServer)
  193. protected
  194. fCommandLogin : TIdPOP3ServerLogin;
  195. fCommandList,
  196. fCommandRetr,
  197. fCommandDele,
  198. fCommandUIDL : TIdPOP3ServerMessageNumberEvent;
  199. fCommandTop : TIdPOP3ServerTOPCommandEvent;
  200. fCommandQuit: TIdPOP3ServerNoParamEvent;
  201. fCommandStat: TIdPOP3ServerStatEvent;
  202. fCommandRset : TIdPOP3ServerNoParamEvent;
  203. fCommandAPOP : TIdPOP3ServerAPOPCommandEvent;
  204. fCommandCapa : TIdPOP3ServerCAPACommandEvent;
  205. function IsAuthed(aCmd: TIdCommand; aAssigned: boolean): boolean;
  206. procedure MustUseTLS(aCmd: TIdCommand);
  207. // CommandHandlers
  208. procedure CommandUser(aCmd: TIdCommand);
  209. procedure CommandPass(aCmd: TIdCommand);
  210. procedure CommandList(aCmd: TIdCommand);
  211. procedure CommandRetr(aCmd: TIdCommand);
  212. procedure CommandDele(aCmd: TIdCommand);
  213. procedure CommandQuit(aCmd: TIdCommand);
  214. procedure CommandAPOP(aCmd: TIdCommand);
  215. procedure CommandStat(aCmd: TIdCommand);
  216. procedure CommandRset(aCmd: TIdCommand);
  217. procedure CommandTop(aCmd: TIdCommand);
  218. procedure CommandUIDL(aCmd: TIdCommand);
  219. procedure CommandSTLS(aCmd: TIdCommand);
  220. procedure CommandCAPA(aCmd: TIdCommand);
  221. function CreateExceptionReply: TIdReply; override;
  222. function CreateGreeting: TIdReply; override;
  223. function CreateHelpReply: TIdReply; override;
  224. function CreateMaxConnectionReply: TIdReply; override;
  225. function CreateReplyUnknownCommand: TIdReply; override;
  226. procedure InitializeCommandHandlers; override;
  227. procedure DoReplyUnknownCommand(AContext: TIdContext; ALine: string); override;
  228. function GetReplyClass: TIdReplyClass; override;
  229. function GetRepliesClass: TIdRepliesClass; override;
  230. procedure SendGreeting(AContext : TIdContext; AGreeting : TIdReply); override;
  231. public
  232. constructor Create(AOwner: TComponent); override;
  233. published
  234. property DefaultPort default IdPORT_POP3;
  235. // These procedures / functions are exposed
  236. property OnCheckUser : TIdPOP3ServerLogin read fCommandLogin write fCommandLogin;
  237. property OnList : TIdPOP3ServerMessageNumberEvent read fCommandList write fCommandList;
  238. property OnRetrieve : TIdPOP3ServerMessageNumberEvent read fCommandRetr write fCommandRetr;
  239. property OnDelete : TIdPOP3ServerMessageNumberEvent read fCommandDele write fCommandDele;
  240. property OnUIDL : TIdPOP3ServerMessageNumberEvent read fCommandUidl write fCommandUidl;
  241. property OnStat: TIdPOP3ServerStatEvent read fCommandStat write fCommandStat;
  242. property OnTop : TIdPOP3ServerTOPCommandEvent read fCommandTop write fCommandTop;
  243. property OnReset : TIdPOP3ServerNoParamEvent read fCommandRset write fCommandRset;
  244. property OnQuit : TIdPOP3ServerNoParamEvent read fCommandQuit write fCommandQuit;
  245. property OnAPOP : TIdPOP3ServerAPOPCommandEvent read fCommandApop write fCommandApop;
  246. property OnCAPA : TIdPOP3ServerCAPACommandEvent read fCommandCapa write fCommandCapa;
  247. property UseTLS;
  248. end;
  249. implementation
  250. uses
  251. IdFIPS,
  252. IdGlobalProtocols, IdHash,
  253. IdHashMessageDigest,
  254. IdReplyPOP3,
  255. IdResourceStringsProtocols,
  256. IdSSL,
  257. IdStack, SysUtils;
  258. procedure TIdPOP3Server.DoReplyUnknownCommand(AContext: TIdContext; ALine: string);
  259. var
  260. LReply: TIdReply;
  261. LLine : String;
  262. begin
  263. LLine := ALine;
  264. // RLebeau 03/17/2007: TIdCmdTCPServer.DoReplyUnknownCommand() adds the
  265. // offending command as a multi-line response generically for all servers.
  266. // POP3 Error replies are not mult-line, however, so overriding the
  267. // behavior here to not do that!
  268. LReply := FReplyClass.CreateWithReplyTexts(nil, ReplyTexts);
  269. try
  270. LReply.SetReply(ST_ERR, IndyFormat(RSPOP3SvrUnknownCmdFmt, [Fetch(LLine)]));
  271. AContext.Connection.IOHandler.Write(LReply.FormattedReply);
  272. finally
  273. LReply.Free;
  274. end;
  275. end;
  276. procedure TIdPOP3Server.InitializeCommandHandlers;
  277. var
  278. LCommandHandler: TIdCommandHandler;
  279. begin
  280. inherited;
  281. LCommandHandler := CommandHandlers.Add;
  282. LCommandHandler.Command := 'USER'; {do not localize}
  283. LCommandHandler.OnCommand := CommandUSER;
  284. LCommandHandler.NormalReply.Code := ST_OK;
  285. LCommandHandler.ExceptionReply.Code := ST_ERR;
  286. LCommandHandler.ParseParams := True;
  287. LCommandHandler := CommandHandlers.Add;
  288. LCommandHandler.Command := 'PASS'; {do not localize}
  289. LCommandHandler.OnCommand := CommandPass;
  290. LCommandHandler.NormalReply.Code := ST_OK;
  291. LCommandHandler.ExceptionReply.Code := ST_ERR;
  292. LCommandHandler.ParseParams := True;
  293. LCommandHandler := CommandHandlers.Add;
  294. LCommandHandler.Command := 'LIST'; {do not localize}
  295. LCommandHandler.OnCommand := CommandList;
  296. LCommandHandler.NormalReply.Code := ST_OK;
  297. LCommandHandler.ExceptionReply.Code := ST_ERR;
  298. LCommandHandler.ParseParams := True;
  299. LCommandHandler := CommandHandlers.Add;
  300. LCommandHandler.Command := 'RETR'; {do not localize}
  301. LCommandHandler.OnCommand := CommandRetr;
  302. LCommandHandler.NormalReply.Code := ST_OK;
  303. LCommandHandler.ExceptionReply.Code := ST_ERR;
  304. LCommandHandler.ParseParams := True;
  305. LCommandHandler := CommandHandlers.Add;
  306. LCommandHandler.Command := 'DELE'; {do not localize}
  307. LCommandHandler.OnCommand := CommandDele;
  308. LCommandHandler.NormalReply.Code := ST_OK;
  309. LCommandHandler.ExceptionReply.Code := ST_ERR;
  310. LCommandHandler.ParseParams := True;
  311. LCommandHandler := CommandHandlers.Add;
  312. LCommandHandler.Command := 'UIDL'; {do not localize}
  313. LCommandHandler.OnCommand := CommandUIDL;
  314. LCommandHandler.NormalReply.Code := ST_OK;
  315. LCommandHandler.ExceptionReply.Code := ST_ERR;
  316. LCommandHandler.ParseParams := True;
  317. LCommandHandler := CommandHandlers.Add;
  318. LCommandHandler.Command := 'STAT'; {do not localize}
  319. LCommandHandler.OnCommand := CommandSTAT;
  320. LCommandHandler.NormalReply.Code := ST_OK;
  321. LCommandHandler.ExceptionReply.Code := ST_ERR;
  322. LCommandHandler.ParseParams := False;
  323. LCommandHandler := CommandHandlers.Add;
  324. LCommandHandler.Command := 'TOP'; {do not localize}
  325. LCommandHandler.OnCommand := CommandTOP;
  326. LCommandHandler.NormalReply.Code := ST_OK;
  327. LCommandHandler.ExceptionReply.Code := ST_ERR;
  328. LCommandHandler.ParseParams := True;
  329. LCommandHandler := CommandHandlers.Add;
  330. LCommandHandler.Command := 'NOOP'; {do not localize}
  331. LCommandHandler.NormalReply.SetReply(ST_OK, RSPOP3SvrNoOp);
  332. LCommandHandler.ExceptionReply.Code := ST_ERR;
  333. LCommandHandler.ParseParams := False;
  334. LCommandHandler := CommandHandlers.Add;
  335. LCommandHandler.Command := 'APOP'; {do not localize}
  336. LCommandHandler.OnCommand := CommandAPOP;
  337. LCommandHandler.NormalReply.Code := ST_OK;
  338. LCommandHandler.ExceptionReply.Code := ST_ERR;
  339. LCommandHandler.ParseParams := True;
  340. LCommandHandler := CommandHandlers.Add;
  341. LCommandHandler.Command := 'RSET'; {do not localize}
  342. LCommandHandler.NormalReply.SetReply(ST_OK, RSPOP3SvrReset);
  343. LCommandHandler.ExceptionReply.Code := ST_ERR;
  344. LCommandHandler.OnCommand := CommandRset;
  345. LCommandHandler.ParseParams := False;
  346. LCommandHandler := CommandHandlers.Add;
  347. LCommandHandler.Command := 'QUIT'; {do not localize}
  348. LCommandHandler.OnCommand := CommandQuit;
  349. LCommandHandler.Disconnect := True;
  350. LCommandHandler.NormalReply.SetReply(ST_OK, RSPOP3SvrClosingConnection);
  351. LCommandHandler.ExceptionReply.Code := ST_ERR;
  352. LCommandHandler.ParseParams := False;
  353. LCommandHandler := CommandHandlers.Add;
  354. LCommandHandler.Command := 'STLS'; {do not localize}
  355. LCommandHandler.NormalReply.Code := ST_OK;
  356. LCommandHandler.ExceptionReply.Code := ST_ERR;
  357. LCommandHandler.OnCommand := CommandSTLS;
  358. LCommandHandler := CommandHandlers.Add;
  359. LCommandHandler.Command := 'CAPA'; {do not localize}
  360. LCommandHandler.NormalReply.Code := ST_OK;
  361. LCommandHandler.ExceptionReply.Code := ST_ERR;
  362. LCommandHandler.OnCommand := CommandCAPA;
  363. end;
  364. { Command Handler Functions here }
  365. procedure TIdPOP3Server.CommandUser(aCmd: TIdCommand);
  366. var
  367. LContext: TIdPOP3ServerContext;
  368. begin
  369. LContext := TIdPOP3ServerContext(aCmd.Context);
  370. if LContext.TLSIsRequired then
  371. begin
  372. MustUseTLS(aCmd);
  373. Exit;
  374. end;
  375. if aCmd.Params.Count > 0 then begin
  376. LContext.fUsername := aCmd.Params.Strings[0];
  377. end;
  378. aCmd.Reply.SetReply(ST_OK, RSPOP3SvrPasswordRequired);
  379. end;
  380. procedure TIdPOP3Server.CommandPass(aCmd: TIdCommand);
  381. var
  382. LContext: TIdPOP3ServerContext;
  383. begin
  384. LContext := TIdPOP3ServerContext(aCmd.Context);
  385. if LContext.TLSIsRequired then
  386. begin
  387. MustUseTLS(aCmd);
  388. Exit;
  389. end;
  390. if aCmd.Params.Count > 0 then begin
  391. LContext.fPassword := aCmd.Params.Strings[0];
  392. end;
  393. if Assigned(OnCheckUser) then begin
  394. OnCheckUser(aCmd.Context, LContext);
  395. end;
  396. LContext.fAuthenticated := True;
  397. aCmd.Reply.SetReply(ST_OK, RSPOP3SvrLoginOk);
  398. end;
  399. procedure TIdPOP3Server.CommandList(aCmd: TIdCommand);
  400. begin
  401. if IsAuthed(aCmd, Assigned(fCommandList)) then begin
  402. OnList(aCmd, IndyStrToInt(aCmd.Params.Text, -1));
  403. end;
  404. end;
  405. procedure TIdPOP3Server.CommandRetr(aCmd: TIdCommand);
  406. begin
  407. if IsAuthed(aCmd, Assigned(fCommandRetr)) then begin
  408. OnRetrieve(aCmd, IndyStrToInt(aCmd.Params[0]));
  409. end;
  410. end;
  411. procedure TIdPOP3Server.CommandDele(aCmd: TIdCommand);
  412. begin
  413. if IsAuthed(aCmd, Assigned(fCommandDele)) then begin
  414. OnDelete(aCmd, IndyStrToInt(aCmd.Params.Text));
  415. end;
  416. end;
  417. procedure TIdPOP3Server.CommandQuit(aCmd: TIdCommand);
  418. begin
  419. if Assigned(fCommandQuit) then begin
  420. OnQuit(aCmd);
  421. end;
  422. end;
  423. procedure TIdPOP3Server.CommandAPOP(aCmd: TIdCommand);
  424. var
  425. LContext: TIdPOP3ServerContext;
  426. LValidPassword : String;
  427. LValidHash : String;
  428. LMD5: TIdHashMessageDigest5;
  429. begin
  430. LContext := TIdPOP3ServerContext(aCmd.Context);
  431. if LContext.Authenticated then
  432. begin
  433. aCmd.Reply.SetReply(ST_ERR, RSPOP3SvrWrongState);
  434. Exit;
  435. end;
  436. if LContext.TLSIsRequired then
  437. begin
  438. MustUseTLS(aCmd);
  439. Exit;
  440. end;
  441. if not Assigned(fCommandAPOP) then
  442. begin
  443. aCmd.Reply.SetReply(ST_ERR, IndyFormat(RSPOP3SVRNotHandled, ['APOP'])); {do not localize}
  444. Exit;
  445. end;
  446. OnAPOP(aCmd, aCmd.Params.Strings[0], LValidPassword);
  447. LMD5 := TIdHashMessageDigest5.Create;
  448. try
  449. LValidHash := IndyLowerCase(LMD5.HashStringAsHex(LContext.APOP3Challenge + LValidPassword));
  450. finally
  451. LMD5.Free;
  452. end;
  453. LContext.fAuthenticated := (LValidHash = aCmd.Params[1]);
  454. // User to set return state of LThread.State as required.
  455. if not LContext.Authenticated then begin
  456. aCmd.Reply.SetReply(ST_ERR, RSPOP3SvrLoginFailed);
  457. end else begin
  458. aCmd.Reply.SetReply(ST_OK, RSPOP3SvrLoginOk);
  459. end;
  460. end;
  461. function TIdPOP3Server.IsAuthed(aCmd: TIdCommand; aAssigned: boolean): boolean;
  462. begin
  463. Result := TIdPOP3ServerContext(aCmd.Context).Authenticated;
  464. if Result then begin
  465. Result := aAssigned;
  466. if not Result then begin
  467. aCmd.Reply.SetReply(ST_ERR, IndyFormat(RSPOP3SVRNotHandled, [aCmd.CommandHandler.Command])); {do not localize}
  468. end;
  469. end else begin
  470. aCmd.Reply.SetReply(ST_ERR, RSPOP3SvrLoginFirst);
  471. end;
  472. end;
  473. procedure TIdPOP3Server.CommandStat(aCmd: TIdCommand);
  474. var
  475. xCount: Integer;
  476. xSize: Int64;
  477. begin
  478. // TODO: Need to make all use this form
  479. if IsAuthed(aCmd, Assigned(fCommandStat)) then begin
  480. OnStat(aCmd, xCount, xSize);
  481. aCmd.Reply.SetReply(ST_OK, IntToStr(xCount) + ' ' + IntToStr(xSize));
  482. end;
  483. end;
  484. procedure TIdPOP3Server.CommandRset(aCmd: TIdCommand);
  485. begin
  486. if IsAuthed(aCmd, assigned(fCommandRSET)) then begin
  487. OnReset(aCmd);
  488. end;
  489. end;
  490. procedure TIdPOP3Server.CommandTop(aCmd: TIdCommand);
  491. var
  492. xMsgNo: integer;
  493. xLines: integer;
  494. begin
  495. if IsAuthed(aCmd, Assigned(fCommandTop)) then begin
  496. if aCmd.Params.Count = 2 then begin
  497. xMsgNo := IndyStrToInt(aCmd.Params.Strings[0], 0);
  498. xLines := IndyStrToInt(aCmd.Params.Strings[1], -1);
  499. if (xMsgNo >= 1) and (xLines >= 0) then begin
  500. OnTop(aCmd, xMsgNo, xLines);
  501. Exit;
  502. end;
  503. end;
  504. aCmd.Reply.SetReply(ST_ERR, RSPOP3SvrInvalidSyntax);
  505. end;
  506. end;
  507. procedure TIdPOP3Server.CommandUIDL(aCmd: TIdCommand);
  508. begin
  509. if IsAuthed(aCmd, Assigned(fCommandUidl)) then begin
  510. OnUidl(aCmd,IndyStrToInt(aCmd.Params.Text, -1))
  511. end;
  512. end;
  513. procedure TIdPOP3Server.CommandSTLS(aCmd: TIdCommand);
  514. var
  515. LContext: TIdPOP3ServerContext;
  516. begin
  517. LContext := TIdPOP3ServerContext(aCmd.Context);
  518. if LContext.CanUseExplicitTLS then begin
  519. if LContext.UsingTLS then begin // we are already using TLS
  520. aCmd.Reply.SetReply(ST_ERR, RSPOP3SvrNotPermittedWithTLS);
  521. Exit;
  522. end;
  523. if LContext.Authenticated then begin //STLS only allowed in auth-state
  524. aCmd.Reply.SetReply(ST_ERR, RSPOP3SvrNotInThisState);
  525. Exit;
  526. end;
  527. aCmd.Reply.SetReply(ST_OK, RSPOP3SvrbeginTLSNegotiation);
  528. aCmd.SendReply;
  529. TIdSSLIOHandlerSocketBase(aCmd.Context.Connection.IOHandler).PassThrough := False;
  530. end else begin
  531. aCmd.Reply.SetReply(ST_ERR, IndyFormat(RSPOP3SVRNotHandled, ['STLS'])); {do not localize}
  532. end;
  533. end;
  534. procedure TIdPOP3Server.CommandCAPA(aCmd: TIdCommand);
  535. var
  536. LContext: TIdPOP3ServerContext;
  537. begin
  538. LContext := TIdPOP3ServerContext(aCmd.Context);
  539. aCmd.Reply.SetReply(ST_OK, RSPOP3SvrCapaList);
  540. // RLebeau: in case no capabilities are specified, the terminating '.' still has to be sent.
  541. aCmd.SendEmptyResponse := True;
  542. if LContext.CanUseExplicitTLS and (not LContext.UsingTLS) then begin
  543. aCmd.Response.Add('STLS'); {do not localize}
  544. end;
  545. if Assigned(fCommandTop) then begin
  546. aCmd.Response.Add('TOP'); {do not localize}
  547. end;
  548. if Assigned(fCommandUidl) then begin
  549. aCmd.Response.Add('UIDL'); {do not localize}
  550. end;
  551. aCmd.Response.Add('USER'); {do not localize}
  552. // aCmd.Response.Add('SASL ......'); // like 'SASL CRAM-MD5 KERBEROS_V4'
  553. if Assigned(fCommandCapa) then begin
  554. OnCAPA(aCmd.Context, aCmd.Response);
  555. end;
  556. end;
  557. { Constructor / Destructors }
  558. constructor TIdPOP3Server.Create(AOwner: TComponent);
  559. begin
  560. inherited Create(AOwner);
  561. FContextClass := TIdPOP3ServerContext;
  562. FRegularProtPort := IdPORT_POP3;
  563. FImplicitTLSProtPort := IdPORT_POP3S;
  564. FExplicitTLSProtPort := IdPORT_POP3;
  565. DefaultPort := IdPORT_POP3;
  566. end;
  567. function TIdPOP3Server.CreateExceptionReply: TIdReply;
  568. begin
  569. Result := TIdReplyPOP3.CreateWithReplyTexts(nil, ReplyTexts);
  570. Result.SetReply(ST_ERR, RSPOP3SvrInternalError);
  571. end;
  572. function TIdPOP3Server.CreateGreeting: TIdReply;
  573. begin
  574. Result := TIdReplyPOP3.CreateWithReplyTexts(nil, ReplyTexts);
  575. Result.SetReply(ST_OK, RSPOP3SvrWelcome);
  576. end;
  577. function TIdPOP3Server.CreateHelpReply: TIdReply;
  578. begin
  579. Result := TIdReplyPOP3.CreateWithReplyTexts(nil, ReplyTexts);
  580. Result.SetReply(ST_OK, RSPOP3SvrHelpFollows);
  581. end;
  582. function TIdPOP3Server.CreateMaxConnectionReply: TIdReply;
  583. begin
  584. Result := TIdReplyPOP3.CreateWithReplyTexts(nil, ReplyTexts);
  585. Result.SetReply(ST_ERR, RSPOP3SvrTooManyCons);
  586. end;
  587. function TIdPOP3Server.CreateReplyUnknownCommand: TIdReply;
  588. begin
  589. Result := TIdReplyPOP3.CreateWithReplyTexts(nil, ReplyTexts);
  590. Result.SetReply(ST_ERR, RSPOP3SvrUnknownCmd);
  591. end;
  592. function TIdPOP3Server.GetReplyClass: TIdReplyClass;
  593. begin
  594. Result := TIdReplyPOP3;
  595. end;
  596. function TIdPOP3Server.GetRepliesClass: TIdRepliesClass;
  597. begin
  598. Result := TIdRepliesPOP3;
  599. end;
  600. { TIdPOP3ServerContext }
  601. function TIdPOP3ServerContext.GetUsingTLS: Boolean;
  602. begin
  603. Result := Connection.IOHandler is TIdSSLIOHandlerSocketBase;
  604. if Result then begin
  605. Result := not TIdSSLIOHandlerSocketBase(Connection.IOHandler).PassThrough;
  606. end;
  607. end;
  608. function TIdPOP3ServerContext.GetCanUseExplicitTLS: Boolean;
  609. begin
  610. Result := Connection.IOHandler is TIdSSLIOHandlerSocketBase;
  611. if Result then begin
  612. Result := TIdPOP3Server(Server).UseTLS in ExplicitTLSVals;
  613. end;
  614. end;
  615. function TIdPOP3ServerContext.GetTLSIsRequired: Boolean;
  616. begin
  617. Result := TIdPOP3Server(Server).UseTLS = utUseRequireTLS;
  618. if Result then begin
  619. Result := not UsingTLS;
  620. end;
  621. end;
  622. procedure TIdPOP3Server.MustUseTLS(aCmd: TIdCommand);
  623. begin
  624. aCmd.Reply.SetReply(ST_ERR, RSPOP3SvrMustUseSTLS);
  625. aCmd.Disconnect := True;
  626. end;
  627. procedure TIdPOP3Server.SendGreeting(AContext: TIdContext;
  628. AGreeting: TIdReply);
  629. var
  630. LThread : TIdPOP3ServerContext;
  631. LGreeting : TIdReplyPOP3;
  632. begin
  633. // AGreeting.Code := ST_OK; {do not localize}
  634. if ( not GetFIPSMode ) and Assigned(fCommandAPOP) then
  635. begin
  636. LThread := TIdPOP3ServerContext(AContext);
  637. LGreeting := TIdReplyPOP3.Create(nil);
  638. try
  639. LThread.APOP3Challenge := '<'+ {do not localize}
  640. IntToStr(Abs( CurrentProcessId )) +
  641. '.'+IntToStr(Abs( GetClockValue ))+'@'+ GStack.HostName +'>'; {do not localize}
  642. if AGreeting.Text.Count > 0 then begin
  643. LGreeting.Text.Add(AGreeting.Text[0] + ' ' + LThread.APOP3Challenge); {do not localize}
  644. end else begin
  645. LGreeting.Text.Add(RSPOP3SvrWelcomeAPOP + LThread.APOP3Challenge);
  646. end;
  647. LGreeting.Code := ST_OK;
  648. AContext.Connection.IOHandler.Write(LGreeting.FormattedReply);
  649. finally
  650. LGreeting.Free;
  651. end;
  652. end
  653. else
  654. begin
  655. inherited SendGreeting(AContext, AGreeting);
  656. end;
  657. end;
  658. end.