IdPOP3Server.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10279: IdPOP3Server.pas
  11. {
  12. Rev 1.1 2/19/2003 5:59:48 PM BGooijen
  13. Now an error is returned when the parameters of APOP and TOP are wrong.
  14. before the change the connection was closed on such error.
  15. }
  16. {
  17. { Rev 1.0 2002.11.12 10:48:00 PM czhower
  18. }
  19. unit IdPOP3Server;
  20. interface
  21. {
  22. Indy POP3 Server
  23. Original Programmer: Luke Croteau
  24. Current Maintainer: Andrew Neillans
  25. No Copyright. Code is given to the Indy Pit Crew.
  26. Quick Notes:
  27. A few of the methods return a default message number if a number isn't entered.
  28. The LIST, DELE, RETR, UIDL, and TOP command will return a -1 in the parameters
  29. if the value isn't specified by the client.
  30. Some functions require this capability. For example, the LIST command can operate
  31. either by a certain message number or a with no arguments. See RFC1939 for details.
  32. Revision History:
  33. 02-May-2002: Andy Neillans
  34. - Bug Fix 551116 - StrToIntDef needed 'trimming#
  35. 30-Apr-2002: Allen O'Neill.
  36. - Failsafe .. added check for ParamCount in reading Username and password - previously
  37. if either were sent in blank we got an IndexOutOfBounds error.
  38. 13-Apr-2002:
  39. - Corrections :) And some Greeting.Text / And other response, glitches
  40. 3-Apr-2002:
  41. - Minor changes. (Greeting.Text)
  42. 1-Apr-2002:
  43. - Completed rewrite! At Last!
  44. 15-Feb-2002: Andy
  45. - Started rewrite for use of CommandHandlers
  46. 13-Jan-2002:
  47. -Fixed formatting bug.
  48. 26-Dec-2000:
  49. -Andrew Neillans found a bug on line 157. Originally it was
  50. If Assigned(OnCommandLIST) then OnCommandRETR(...).
  51. Changed to OnCommandLIST(...). Thanks Andrew!
  52. 29-Oct-2000:
  53. -I discovered I really shouldn't program at night.
  54. The error wasn't that it shouldn't be Succ (Because it should), but
  55. because I forgot to implement LIST
  56. 27-Oct-2000:
  57. -Fixed a dumb bug. Originally coded command parsing as Succ(PosInStrArray)
  58. Should be just PosInStrArray b/c it is not a dynamic array. The bounds
  59. are constant.
  60. 25-Oct-2000:
  61. -Created Unit.
  62. -Created new IdPOP3Server Server Component according to RFC 1939
  63. }
  64. uses
  65. Classes,
  66. IdAssignedNumbers,
  67. IdGlobal,
  68. IdTCPServer,
  69. IdMailBox;
  70. type
  71. TIdPOP3ServerState = (Auth, Trans, Update);
  72. TIdPOP3ServerThread = class(TIdPeerThread)
  73. protected
  74. // what needs to be stored...
  75. fUser : String;
  76. fPassword : String;
  77. fState :TIdPOP3ServerState;
  78. procedure BeforeRun; override;
  79. public
  80. constructor Create(ACreateSuspended: Boolean = True); override;
  81. destructor Destroy; override;
  82. // Any functions for vars
  83. property Username : String read fUser write fUser;
  84. property Password : String read fPassword write fPassword;
  85. property State : TIdPOP3ServerState read fState write fState;
  86. end;
  87. TIdPOP3ServerNoParamEvent = procedure (ASender: TIdCommand) of object;
  88. TIdPOP3ServerMessageNumberEvent = procedure (ASender: TIdCommand; AMessageNum :Integer) of object;
  89. TIdPOP3ServerLogin = procedure (AThread :TIdPeerThread; LThread : TIdPOP3ServerThread) of object;
  90. TIdPOP3ServerAPOPCommandEvent = procedure (ASender: TIdCommand; AMailboxID :String; ADigest :String) of object;
  91. TIdPOP3ServerTOPCommandEvent = procedure (ASender: TIdCommand; AMessageNum :Integer; ANumLines :Integer) of object;
  92. TIdPOP3Server = class(TIdTcpServer)
  93. protected
  94. fCommandLogin : TIdPOP3ServerLogin;
  95. fCommandList,
  96. fCommandRetr,
  97. fCommandDele,
  98. fCommandUIDL : TIdPOP3ServerMessageNumberEvent;
  99. fCommandTop : TIdPOP3ServerTOPCommandEvent;
  100. fCommandQuit,
  101. fCommandStat,
  102. fCommandRset : TIdPOP3ServerNoParamEvent;
  103. fCommandAPOP : TIdPOP3ServerAPOPCommandEvent;
  104. // CommandHandlers
  105. procedure CommandUser(ASender: TIdCommand); //
  106. procedure CommandPass(ASender: TIdCommand); //
  107. procedure CommandList(ASender: TIdCommand); //
  108. procedure CommandRetr(ASender: TIdCommand); //
  109. procedure CommandDele(ASender: TIdCommand); //
  110. procedure CommandQuit(ASender: TIdCommand); //
  111. procedure CommandAPOP(ASender: TIdCommand); //
  112. procedure CommandStat(ASender: TIdCommand); //
  113. procedure CommandRset(ASender: TIdCommand); //
  114. procedure CommandTop(ASender: TIdCommand); //
  115. procedure CommandUIDL(ASender: TIdCommand); //
  116. procedure InitializeCommandHandlers; override;
  117. public
  118. constructor Create(AOwner: TComponent); override;
  119. destructor Destroy; override;
  120. published
  121. property DefaultPort default IdPORT_POP3;
  122. // These procedures / functions are exposed
  123. property CheckUser : TIdPOP3ServerLogin read fCommandLogin write fCommandLogin;
  124. property OnLIST : TIdPOP3ServerMessageNumberEvent read fCommandList write fCommandList;
  125. property OnRETR : TIdPOP3ServerMessageNumberEvent read fCommandRetr write fCommandRetr;
  126. property OnDELE : TIdPOP3ServerMessageNumberEvent read fCommandDele write fCommandDele;
  127. property OnUIDL : TIdPOP3ServerMessageNumberEvent read fCommandUidl write fCommandUidl;
  128. property OnSTAT : TIdPOP3ServerNoParamEvent read fCommandStat write fCommandStat;
  129. property OnTOP : TIdPOP3ServerTOPCommandEvent read fCommandTop write fCommandTop;
  130. property OnRSET : TIdPOP3ServerNoParamEvent read fCommandRset write fCommandRset;
  131. property OnQUIT : TIdPOP3ServerNoParamEvent read fCommandQuit write fCommandQuit;
  132. property OnAPOP : TIdPOP3ServerAPOPCommandEvent read fCommandApop write fCommandApop;
  133. End;
  134. implementation
  135. uses
  136. IdResourceStrings,
  137. IdRFCReply,
  138. SysUtils;
  139. procedure TIdPOP3Server.InitializeCommandHandlers;
  140. begin
  141. inherited;
  142. with CommandHandlers.Add do begin
  143. Command := 'USER';
  144. OnCommand := CommandUSER;
  145. ParseParams := True;
  146. end;
  147. with CommandHandlers.Add do begin
  148. Command := 'PASS';
  149. OnCommand := CommandPass;
  150. ParseParams := True;
  151. end;
  152. with CommandHandlers.Add do begin
  153. Command := 'LIST';
  154. OnCommand := CommandList;
  155. ParseParams := True;
  156. end;
  157. with CommandHandlers.Add do begin
  158. Command := 'RETR';
  159. OnCommand := CommandRetr;
  160. ParseParams := True;
  161. end;
  162. with CommandHandlers.Add do begin
  163. Command := 'DELE';
  164. OnCommand := CommandDele;
  165. ParseParams := True;
  166. end;
  167. with CommandHandlers.Add do begin
  168. Command := 'UIDL';
  169. OnCommand := CommandUIDL;
  170. ParseParams := True;
  171. end;
  172. with CommandHandlers.Add do begin
  173. Command := 'STAT';
  174. OnCommand := CommandSTAT;
  175. ParseParams := False;
  176. end;
  177. with CommandHandlers.Add do begin
  178. Command := 'TOP';
  179. OnCommand := CommandTOP;
  180. ParseParams := True;
  181. end;
  182. with CommandHandlers.Add do begin
  183. Command := 'NOOP';
  184. ReplyNormal.Text.Text := '+OK NOOP';
  185. ParseParams := False;
  186. end;
  187. with CommandHandlers.Add do begin
  188. Command := 'APOP';
  189. OnCommand := CommandAPOP;
  190. ParseParams := True;
  191. End;
  192. with CommandHandlers.Add do begin
  193. Command := 'RSET';
  194. ReplyNormal.Text.Text := '+OK Reset';
  195. OnCommand := CommandRset;
  196. ParseParams := False;
  197. end;
  198. with CommandHandlers.Add do begin
  199. Command := 'QUIT';
  200. OnCommand := CommandQuit;
  201. Disconnect := True;
  202. ReplyNormal.Text.Text := '+OK Closing communication channel'; // USE ResourceString ??
  203. ParseParams := False;
  204. end;
  205. end;
  206. { Command Handler Functions here }
  207. procedure TIdPOP3Server.CommandUser(ASender: TIdCommand);
  208. Var
  209. LThread: TIdPOP3ServerThread;
  210. begin
  211. LThread := TIdPOP3ServerThread(ASender.Thread);
  212. if ASender.Params.Count > 0 then
  213. LThread.Username := ASender.Params.Strings[0];
  214. ASender.Thread.Connection.WriteLn('+OK Password required');
  215. end;
  216. procedure TIdPOP3Server.CommandPass(ASender: TIdCommand);
  217. Var
  218. LThread: TIdPOP3ServerThread;
  219. begin
  220. LThread := TIdPOP3ServerThread(ASender.Thread);
  221. if ASender.Params.Count > 0 then
  222. LThread.Password := ASender.Params.Strings[0];
  223. if Assigned(CheckUser) then
  224. CheckUser(ASender.Thread, LThread);
  225. // User to set return state of LThread.State as required.
  226. If LThread.State <> Trans Then
  227. ASender.Thread.Connection.Writeln('-ERR Login failed')
  228. Else
  229. ASender.Thread.Connection.Writeln('+OK Login OK');
  230. end;
  231. procedure TIdPOP3Server.CommandList(ASender: TIdCommand);
  232. Var
  233. LThread: TIdPOP3ServerThread;
  234. begin
  235. LThread := TIdPOP3ServerThread(ASender.Thread);
  236. If LThread.State = Trans Then
  237. Begin
  238. If Assigned(fCommandList) Then
  239. OnList(ASender, StrToIntDef(Trim(ASender.Params.Text), -1))
  240. Else
  241. ASender.Thread.Connection.WriteLn('-ERR ' + Format(RSPOP3SVRNotHandled, ['LIST']));
  242. End
  243. Else
  244. ASender.Thread.Connection.WriteLn('-ERR Please login first'); // RS
  245. end;
  246. procedure TIdPOP3Server.CommandRetr(ASender: TIdCommand);
  247. Var
  248. LThread: TIdPOP3ServerThread;
  249. begin
  250. LThread := TIdPOP3ServerThread(ASender.Thread);
  251. If LThread.State = Trans Then
  252. Begin
  253. If Assigned(fCommandRetr) Then
  254. OnRetr(ASender, StrToIntDef(Trim(ASender.Params.Text), -1))
  255. Else
  256. ASender.Thread.Connection.WriteLn('-ERR ' + Format(RSPOP3SVRNotHandled, ['RETR']));
  257. End
  258. Else
  259. ASender.Thread.Connection.WriteLn('-ERR Please login first'); // RS
  260. end;
  261. procedure TIdPOP3Server.CommandDele(ASender: TIdCommand);
  262. Var
  263. LThread: TIdPOP3ServerThread;
  264. begin
  265. LThread := TIdPOP3ServerThread(ASender.Thread);
  266. If LThread.State = Trans Then
  267. Begin
  268. If Assigned(fCommandDele) Then
  269. Begin
  270. Try
  271. StrToInt(Trim(ASender.Params.Text));
  272. OnDele(ASender, StrToInt(Trim(ASender.Params.Text)))
  273. Except
  274. ASender.Thread.Connection.WriteLn('-ERR Invalid Message Number'); // RS
  275. End;
  276. End
  277. Else
  278. ASender.Thread.Connection.WriteLn('-ERR ' + Format(RSPOP3SVRNotHandled, ['DELE']));
  279. End
  280. Else
  281. ASender.Thread.Connection.WriteLn('-ERR Please login first'); // RS
  282. end;
  283. procedure TIdPOP3Server.CommandQuit(ASender: TIdCommand);
  284. Var
  285. LThread: TIdPOP3ServerThread;
  286. begin
  287. LThread := TIdPOP3ServerThread(ASender.Thread);
  288. If LThread.State = Trans Then
  289. Begin
  290. If Assigned(fCommandQuit) Then OnQuit(ASender)
  291. End;
  292. end;
  293. procedure TIdPOP3Server.CommandAPOP(ASender: TIdCommand);
  294. Var
  295. LThread: TIdPOP3ServerThread;
  296. begin
  297. LThread := TIdPOP3ServerThread(ASender.Thread);
  298. If LThread.State = Auth Then
  299. Begin
  300. If Assigned(fCommandAPOP) Then
  301. Begin
  302. if ASender.Params.Count = 2 then begin
  303. OnAPOP(ASender, ASender.Params.Strings[0], ASender.Params.Strings[1]);
  304. // User to set return state of LThread.State as required.
  305. If LThread.State <> Trans Then begin
  306. ASender.Thread.Connection.Writeln('-ERR Login failed');
  307. End
  308. end else begin
  309. ASender.Thread.Connection.WriteLn('-ERR Invalid Syntax'); //RS
  310. end
  311. end
  312. Else
  313. ASender.Thread.Connection.WriteLn('-ERR ' + Format(RSPOP3SVRNotHandled, ['APOP']));
  314. End
  315. Else
  316. ASender.Thread.Connection.WriteLn('-ERR Wrong State');
  317. end;
  318. procedure TIdPOP3Server.CommandStat(ASender: TIdCommand);
  319. Var
  320. LThread: TIdPOP3ServerThread;
  321. begin
  322. LThread := TIdPOP3ServerThread(ASender.Thread);
  323. If LThread.State = Trans Then
  324. Begin
  325. If Assigned(fCommandStat) Then
  326. OnStat(ASender)
  327. Else
  328. ASender.Thread.Connection.WriteLn('-ERR ' + Format(RSPOP3SVRNotHandled, ['STAT']));
  329. End
  330. Else
  331. ASender.Thread.Connection.WriteLn('-ERR Please login first'); // RS
  332. end;
  333. procedure TIdPOP3Server.CommandRset(ASender: TIdCommand);
  334. Var
  335. LThread: TIdPOP3ServerThread;
  336. begin
  337. LThread := TIdPOP3ServerThread(ASender.Thread);
  338. If LThread.State = Trans Then
  339. Begin
  340. If Assigned(fCommandRSET) Then
  341. OnRset(ASender)
  342. Else
  343. ASender.Thread.Connection.WriteLn('-ERR ' + Format(RSPOP3SVRNotHandled, ['RSET']));
  344. End
  345. Else
  346. ASender.Thread.Connection.WriteLn('-ERR Please login first'); // RS
  347. end;
  348. procedure TIdPOP3Server.CommandTop(ASender: TIdCommand);
  349. Var
  350. LThread: TIdPOP3ServerThread;
  351. begin
  352. LThread := TIdPOP3ServerThread(ASender.Thread);
  353. If LThread.State = Trans Then Begin
  354. If Assigned(fCommandTop) Then Begin
  355. if ASender.Params.Count = 2 then begin
  356. If (StrToIntDef(Trim(ASender.Params.Strings[0]), -1) <> -1) AND (StrToIntDef(Trim(ASender.Params.Strings[1]), -1) <> -1) Then begin
  357. OnTop(ASender, StrToInt(ASender.Params.Strings[0]), StrToInt(ASender.Params.Strings[1]))
  358. end Else begin
  359. ASender.Thread.Connection.WriteLn('-ERR Invalid Syntax'); //RS
  360. End;
  361. end else begin
  362. ASender.Thread.Connection.WriteLn('-ERR Invalid Syntax'); //RS
  363. end
  364. End Else
  365. ASender.Thread.Connection.WriteLn('-ERR ' + Format(RSPOP3SVRNotHandled, ['TOP']));
  366. End
  367. Else
  368. ASender.Thread.Connection.WriteLn('-ERR Please login first'); // RS
  369. end;
  370. procedure TIdPOP3Server.CommandUIDL(ASender: TIdCommand);
  371. Var
  372. LThread: TIdPOP3ServerThread;
  373. begin
  374. LThread := TIdPOP3ServerThread(ASender.Thread);
  375. If LThread.State = Trans Then
  376. Begin
  377. If Assigned(fCommandUidl) Then
  378. OnUidl(ASender, StrToIntDef(Trim(ASender.Params.Text), -1))
  379. Else
  380. ASender.Thread.Connection.WriteLn('-ERR ' + Format(RSPOP3SVRNotHandled, ['UIDL']));
  381. End
  382. Else
  383. ASender.Thread.Connection.WriteLn('-ERR Please login first'); // RS
  384. end;
  385. { Constructor / Destructors }
  386. constructor TIdPOP3Server.Create(AOwner: TComponent);
  387. begin
  388. inherited;
  389. ThreadClass := TIdPOP3ServerThread;
  390. DefaultPort := IdPORT_POP3;
  391. Greeting.Text.Text := '+OK Welcome to Indy POP3 Server'; // RS
  392. ReplyUnknownCommand.Text.Text := '-ERR Sorry, Unknown Command'; // RS
  393. end;
  394. destructor TIdPOP3Server.Destroy;
  395. begin
  396. inherited;
  397. end;
  398. { TIdPOP3ServerThread }
  399. constructor TIdPOP3ServerThread.Create(ACreateSuspended: Boolean);
  400. begin
  401. inherited;
  402. FUser := '';
  403. fState := Auth;
  404. end;
  405. procedure TIdPOP3ServerThread.BeforeRun;
  406. begin
  407. FUser := '';
  408. fState := Auth;
  409. fPassword := '';
  410. inherited BeforeRun;
  411. end;
  412. destructor TIdPOP3ServerThread.Destroy;
  413. begin
  414. inherited;
  415. end;
  416. end.