PageRenderTime 48ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/units/ics/Pop3cli.pas

http://github.com/rofl0r/KOL
Pascal | 904 lines | 592 code | 145 blank | 167 comment | 66 complexity | 637e0b6a82768bbdd5f64cd655fe92f8 MD5 | raw file
  1. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2. +-----------------------------------------------------------------------+
  3. | THIS IS AN OUTDATED COMPONENT. FOR NEW APPLICATIONS, USE POP3PROT.PAS |
  4. | SOURCE. THE NEW POP3 COMPONENT IS ASYNCHONOUS AND MUCH FASTER. |
  5. | YOU NEED TO CHANGE YOUR APPLICATION BECAUSE NEW COMPONENT HAS NOT |
  6. | EXACTLY THE SAME INTERFACE. COMPONENT CLASS NAME HAS BEEN CHANGED |
  7. | SO THAT YOU CAN INSTALL BOTH WHILE YOU ARE UPDATING YOUR APPLICATIONS.|
  8. | THERE IS ALSO A NEW DEMO: MAILRCV.DPR. |
  9. +-----------------------------------------------------------------------+
  10. | IF YOU REALLY NEED THIS OLD COMPONENT, YOU ALSO NEED AN OLD TWSOCKET! |
  11. +-----------------------------------------------------------------------+
  12. Author: François PIETTE
  13. Object: TPop3Client class implements the POP3 protocol
  14. (RFC-1225, RFC-1939)
  15. EMail: francois.piette@pophost.eunet.be francois.piette@ping.be
  16. francois.piette@rtfm.be http://www.rtfm.be/fpiette
  17. WebSite: http://www.rtfm.be/fpiette
  18. Creation: 03 october 1997
  19. Version: 1.16
  20. Support: Use the mailing list twsocket@rtfm.be See website for details.
  21. Legal issues: Copyright (C) 1997, 1998 by François PIETTE
  22. Rue de Grady 24, 4053 Embourg, Belgium. Fax: +32-4-365.74.56
  23. <francois.piette@pophost.eunet.be>
  24. This software is provided 'as-is', without any express or
  25. implied warranty. In no event will the author be held liable
  26. for any damages arising from the use of this software.
  27. Permission is granted to anyone to use this software for any
  28. purpose, including commercial applications, and to alter it
  29. and redistribute it freely, subject to the following
  30. restrictions:
  31. 1. The origin of this software must not be misrepresented,
  32. you must not claim that you wrote the original software.
  33. If you use this software in a product, an acknowledgment
  34. in the product documentation would be appreciated but is
  35. not required.
  36. 2. Altered source versions must be plainly marked as such, and
  37. must not be misrepresented as being the original software.
  38. 3. This notice may not be removed or altered from any source
  39. distribution.
  40. 4. You must register this software by sending a picture postcard
  41. to the author. Use a nice stamp and mention your name, street
  42. address, EMail address and any comment you like to say.
  43. Updates:
  44. Sept 09, 1997 Modified TOP to be able to request 0 lines (bug reported by
  45. damien@jetman.demon.co.uk)
  46. Oct 10, 1997 V1.10. Published ProtocolState property, made TOP command
  47. complies with RFC-1939 as suggested by damien@jetman.demon.co.uk
  48. Implemented the UIDL command.
  49. Oct 11, 1997 V1.11 Implemented the APOP command, but not tested because no
  50. server available to test it.
  51. Made internal error message look like POP3 error messages (-ERR)
  52. Oct 28, 1997 V1.12 Modified TWSocket to handle line buffer overflow and
  53. TPop3Client to handle that in GetMultiLine.
  54. Jan 10, 1998 V1.13 Made FWSocket accessible with a read only property. This
  55. eases DNSLookup without a supplementary TWSocket.
  56. Added a Port property.
  57. Apr 01, 1998 V1.14 Adapted for BCB V3
  58. May 05, 1998 V1.15 Changed GetMultiLine to correctly handle double dots at
  59. line start.
  60. Jun 01, 1998 V1.16 Ben Robinson <zeppelin@wwa.com> found that Last did'nt
  61. update MsgNum and MsgSize.
  62. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  63. unit pop3cli;
  64. interface
  65. {$B-} { Enable partial boolean evaluation }
  66. {$T-} { Untyped pointers }
  67. {$IFNDEF VER80} { Not for Delphi 1 }
  68. {$J+} { Allow typed constant to be modified }
  69. {$ENDIF}
  70. {$IFDEF VER110} { C++ Builder V3.0 }
  71. {$ObjExportAll On}
  72. {$ENDIF}
  73. uses
  74. WinTypes,
  75. WinProcs,
  76. SysUtils,
  77. Messages,
  78. Classes,
  79. Graphics,
  80. Controls,
  81. Forms,
  82. Dialogs,
  83. Menus,
  84. WSocket,
  85. Wait,
  86. MD5;
  87. const
  88. Pop3CliVersion = 116;
  89. type
  90. TPop3Display = procedure(Sender: TObject; Msg : String) of object;
  91. TPop3State = (pop3Disconnected, pop3WaitingUser, pop3WaitingPass, pop3Transaction, pop3Update);
  92. TPop3Method = function : boolean of object;
  93. TPop3Client = class(TComponent)
  94. private
  95. FWSocket : TWSocket;
  96. FHost : String;
  97. FPort : String;
  98. FUserName : String;
  99. FPassWord : String;
  100. FProtocolState : TPop3State;
  101. FLastResponse : String;
  102. FErrorMessage : String;
  103. FTimeStamp : String;
  104. FWait : TWait;
  105. FTimeout : Integer;
  106. FTimeOutFlag : Boolean;
  107. FLineTooLong : Boolean;
  108. FMsgCount : Integer;
  109. FMsgSize : Integer;
  110. FMsgNum : Integer;
  111. FMsgUidl : String;
  112. FMsgLines : Integer;
  113. FTag : LongInt;
  114. FOnDisplay : TPop3Display;
  115. FOnMessageBegin : TNotifyEvent;
  116. FOnMessageEnd : TNotifyEvent;
  117. FOnMessageLine : TNotifyEvent;
  118. FOnListBegin : TNotifyEvent;
  119. FOnListEnd : TNotifyEvent;
  120. FOnListLine : TNotifyEvent;
  121. FOnUidlBegin : TNotifyEvent;
  122. FOnUidlEnd : TNotifyEvent;
  123. FOnUidlLine : TNotifyEvent;
  124. protected
  125. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  126. procedure ClearErrorMessage;
  127. procedure SetErrorMessage;
  128. procedure Display(Msg : String);
  129. procedure SetWait(Value : TWait);
  130. function GetResponse : Boolean;
  131. procedure SendCommand(Cmd : String);
  132. procedure WaitTimeOut(Sender : TObject);
  133. procedure SessionClosed(Sender : TObject; Error : WORD);
  134. procedure LineTooLong(Sender : TObject);
  135. function ExtractNumbers(var N1 : Integer; var N2 : Integer) : Boolean;
  136. function ExtractUidl(var N1 : Integer; var N2 : String) : Boolean;
  137. procedure ProcessUidl(Sender : TObject);
  138. procedure ProcessList(Sender : TObject);
  139. function GetMultiLine(aOnBegin : TNotifyEvent;
  140. aOnLine : TNotifyEvent;
  141. aOnEnd : TNotifyEvent;
  142. aProcess : TNotifyEvent) : Boolean;
  143. function StartTransaction(OpCode, Params : String) : Boolean;
  144. function PassRpop(OpCode : String) : Boolean;
  145. public
  146. constructor Create(AOwner : TComponent); override;
  147. destructor Destroy; override;
  148. function Connect : Boolean;
  149. function User : Boolean;
  150. function Pass : Boolean;
  151. function Quit : Boolean;
  152. function Stat : Boolean;
  153. function List : Boolean;
  154. function Retr : Boolean;
  155. function Dele : Boolean;
  156. function Noop : Boolean;
  157. function Last : Boolean;
  158. function Rset : Boolean;
  159. function Top : Boolean;
  160. function Rpop : Boolean;
  161. function Uidl : Boolean;
  162. function Apop : Boolean;
  163. property WSocket : TWSocket read FWSocket;
  164. published
  165. property Host : String read FHost
  166. write FHost;
  167. property Port : String read FPort
  168. write FPort;
  169. property UserName : String read FUserName
  170. write FUserName;
  171. property PassWord : String read FPassWord
  172. write FPassWord;
  173. property Wait : TWait read FWait
  174. write SetWait;
  175. property TimeOut : Integer read FTimeout
  176. write FTimeout;
  177. property ErrorMessage : String read FErrorMessage;
  178. property LastResponse : String read FLastResponse;
  179. property ProtocolState : TPop3State read FProtocolState;
  180. {:Updated by the Stat method with the number of
  181. messages in the maildrop }
  182. property MsgCount : Integer read FMsgCount;
  183. {:Updated by the Stat method with the total size
  184. in byte for the messages in the maildrop }
  185. property MsgSize : Integer read FMsgSize;
  186. {:This is the number of lines to display in the TOP command
  187. Set to zero if you wants the default value }
  188. property MsgLines : Integer read FMsgLines
  189. write FMsgLines;
  190. {:This is the message number which must be returned by the Retr
  191. method. It is also updated by the Last method }
  192. property MsgNum : Integer read FMsgNum
  193. write FMsgNum;
  194. property MsgUidl : String read FMsgUidl;
  195. property Tag : LongInt read FTag
  196. write FTag;
  197. property OnDisplay : TPop3Display read FOnDisplay
  198. write FOnDisplay;
  199. property OnMessageBegin : TNotifyEvent read FOnMessageBegin
  200. write FOnMessageBegin;
  201. property OnMessageEnd : TNotifyEvent read FOnMessageEnd
  202. write FOnMessageEnd;
  203. property OnMessageLine : TNotifyEvent read FOnMessageLine
  204. write FOnMessageLine;
  205. property OnListBegin : TNotifyEvent read FOnListBegin
  206. write FOnListBegin;
  207. property OnListEnd : TNotifyEvent read FOnListEnd
  208. write FOnListEnd;
  209. property OnListLine : TNotifyEvent read FOnListLine
  210. write FOnListLine;
  211. property OnUidlBegin : TNotifyEvent read FOnUidlBegin
  212. write FOnUidlBegin;
  213. property OnUidlEnd : TNotifyEvent read FOnUidlEnd
  214. write FOnUidlEnd;
  215. property OnUidlLine : TNotifyEvent read FOnUidlLine
  216. write FOnUidlLine;
  217. end;
  218. procedure Register;
  219. implementation
  220. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  221. {$IFDEF VER80}
  222. procedure SetLength(var S: string; NewLength: Integer);
  223. begin
  224. S[0] := chr(NewLength);
  225. end;
  226. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  227. function RTrim(Str : String) : String;
  228. var
  229. i : Integer;
  230. begin
  231. i := Length(Str);
  232. while (i > 0) and (Str[i] = ' ') do
  233. i := i - 1;
  234. Result := Copy(Str, 1, i);
  235. end;
  236. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  237. function LTrim(Str : String) : String;
  238. var
  239. i : Integer;
  240. begin
  241. if Str[1] <> ' ' then { Petite optimisation: pas d'espace }
  242. Result := Str
  243. else begin
  244. i := 1;
  245. while (i <= Length(Str)) and (Str[i] = ' ') do
  246. i := i + 1;
  247. Result := Copy(Str, i, Length(Str) - i + 1);
  248. end;
  249. end;
  250. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  251. function Trim(Str : String) : String;
  252. begin
  253. Result := LTrim(Rtrim(Str));
  254. end;
  255. {$ENDIF}
  256. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  257. function stpblk(PValue : PChar) : PChar;
  258. begin
  259. Result := PValue;
  260. while Result^ in [' ', #9, #10, #13] do
  261. Inc(Result);
  262. end;
  263. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  264. function atoi(PValue : PChar) : Integer;
  265. begin
  266. Result := 0;
  267. PValue := stpblk(PValue);
  268. while PValue^ in ['0'..'9'] do begin
  269. Result := Result * 10 + ord(PValue^) - ord('0');
  270. Inc(PValue);
  271. end;
  272. end;
  273. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  274. constructor TPop3Client.Create(AOwner : TComponent);
  275. begin
  276. inherited Create(AOwner);
  277. FWSocket := TWSocket.Create(nil);
  278. FWSocket.OnSessionClosed := SessionClosed;
  279. FWSocket.OnLineTooLong := LineTooLong;
  280. FTimeout := 15;
  281. FProtocolState := pop3Disconnected;
  282. FPort := 'pop3';
  283. end;
  284. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  285. destructor TPop3Client.Destroy;
  286. begin
  287. if Assigned(FWSocket) then begin
  288. FWSocket.Destroy;
  289. FWSocket := nil;
  290. end;
  291. inherited Destroy;
  292. end;
  293. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  294. function TPop3Client.Connect : Boolean;
  295. var
  296. I, J : Integer;
  297. begin
  298. Result := FALSE;
  299. FTimeStamp := '';
  300. if FWait = nil then begin
  301. FErrorMessage := '-ERR No wait object';
  302. Display(FErrorMessage);
  303. Exit;
  304. end;
  305. FWait.OnTimeout := WaitTimeout;
  306. FTimeOutFlag := FALSE;
  307. if FProtocolState > pop3Disconnected then begin
  308. { Already connected, it's ok }
  309. Result := TRUE;
  310. Exit;
  311. end;
  312. ClearErrorMessage;
  313. if Length(FHost) = 0 then begin
  314. FErrorMessage := '-ERR No host specified';
  315. Display(FErrorMessage);
  316. Exit;
  317. end;
  318. try
  319. FWSocket.Proto := 'tcp';
  320. FWSocket.Port := FPort;
  321. FWSocket.Addr := FHost;
  322. FWSocket.OnDataAvailable := nil;
  323. FWSocket.Connect;
  324. except
  325. on E:ESocketException do begin
  326. FErrorMessage := '-ERR ' + E.Message;
  327. Exit;
  328. end;
  329. end;
  330. if not FWSocket.Wait(FTimeout, wsConnected) then begin
  331. FErrorMessage := '-ERR Can''t connect to host ''' + FHost + '''';
  332. Display(FErrorMessage);
  333. FWSocket.Close;
  334. Exit;
  335. end;
  336. Display('Connected with POP3 server');
  337. if not GetResponse then begin
  338. SetErrorMessage;
  339. FWSocket.Close;
  340. Exit;
  341. end;
  342. I := Pos('<', FLastResponse);
  343. J := Pos('>', Copy(FLastResponse, I, Length(FLastREsponse)));
  344. if (I > 0) and (J > 0) then
  345. FTimeStamp := Copy(FLastResponse, I, J);
  346. FProtocolState := pop3WaitingUser;
  347. Result := TRUE;
  348. end;
  349. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  350. function TPop3Client.Quit : Boolean;
  351. begin
  352. if FProtocolState = pop3Disconnected then begin
  353. { Not connected, it's ok }
  354. Result := TRUE;
  355. Exit;
  356. end;
  357. try
  358. SendCommand('QUIT');
  359. Result := GetResponse;
  360. except
  361. Result := FALSE;
  362. end;
  363. FProtocolState := pop3Disconnected;
  364. FWSocket.Close;
  365. end;
  366. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  367. function TPop3Client.User : Boolean;
  368. begin
  369. Result := FALSE;
  370. if FProtocolState > pop3WaitingUser then begin
  371. FErrorMessage := '-ERR USER command invalid now';
  372. Display(FErrorMessage);
  373. Exit;
  374. end;
  375. if (FProtocolState = pop3Disconnected) and (not Connect) then
  376. Exit;
  377. SendCommand('USER ' + Trim(FUserName));
  378. if not GetResponse then
  379. Exit;
  380. Result := TRUE;
  381. FProtocolState := pop3WaitingPass;
  382. end;
  383. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  384. function TPop3Client.Apop : Boolean;
  385. begin
  386. Result := FALSE;
  387. if FProtocolState > pop3WaitingUser then begin
  388. FErrorMessage := '-ERR APOP command invalid now';
  389. Display(FErrorMessage);
  390. Exit;
  391. end;
  392. if (FProtocolState = pop3Disconnected) and (not Connect) then
  393. Exit;
  394. if FTimeStamp = '' then begin
  395. FErrorMessage := '-ERR Server do not support APOP (no timestamp)';
  396. Display(FErrorMessage);
  397. Exit;
  398. end;
  399. SendCommand('APOP ' + Trim(FUserName)+ ' ' +
  400. StrMD5(FTimeStamp + FPassWord));
  401. if not GetResponse then
  402. Exit;
  403. Result := TRUE;
  404. FProtocolState := pop3WaitingPass;
  405. end;
  406. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  407. function TPop3Client.Pass : Boolean;
  408. begin
  409. Result := PassRpop('PASS');
  410. end;
  411. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  412. function TPop3Client.Rpop : Boolean;
  413. begin
  414. Result := PassRpop('RPOP');
  415. end;
  416. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  417. function TPop3Client.PassRpop(OpCode : String) : Boolean;
  418. begin
  419. Result := FALSE;
  420. if FProtocolState > pop3WaitingPass then begin
  421. FErrorMessage := '-ERR ' + OpCode + ' command invalid now';
  422. Display(FErrorMessage);
  423. Exit;
  424. end;
  425. if (FProtocolState < pop3WaitingPass) and (not User) then
  426. Exit;
  427. SendCommand(OpCode + ' ' + Trim(FPassWord));
  428. if not GetResponse then
  429. Exit;
  430. Result := TRUE;
  431. FProtocolState := pop3Transaction;
  432. end;
  433. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  434. function TPop3Client.Retr : Boolean;
  435. begin
  436. Result := StartTransaction('RETR', IntToStr(FMsgNum));
  437. if not Result then
  438. Exit;
  439. Result := GetMultiLine(FOnMessageBegin, FOnMessageLine, FOnMessageEnd, nil);
  440. end;
  441. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  442. function TPop3Client.Stat : Boolean;
  443. begin
  444. FMsgCount := 0;
  445. FMsgSize := 0;
  446. Result := StartTransaction('STAT', '');
  447. if not Result then
  448. Exit;
  449. Result := ExtractNumbers(FMsgCount, FMsgSize);
  450. end;
  451. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  452. procedure TPop3Client.ProcessUidl(Sender : TObject);
  453. begin
  454. ExtractUidl(FMsgNum, FMsgUidl);
  455. end;
  456. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  457. procedure TPop3Client.ProcessList(Sender : TObject);
  458. begin
  459. ExtractNumbers(FMsgNum, FMsgSize);
  460. end;
  461. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  462. function TPop3Client.List : Boolean;
  463. begin
  464. if FMsgNum <= 0 then begin
  465. { Scan LIST command (all messages) }
  466. Result := StartTransaction('LIST', '');
  467. if not Result then
  468. Exit;
  469. Result := GetMultiLine(FOnListBegin, FOnListLine,
  470. FOnListEnd, ProcessList);
  471. end
  472. else begin
  473. { Single message LIST command }
  474. Result := StartTransaction('LIST', IntToStr(FMsgNum));
  475. if not Result then
  476. Exit;
  477. Result := ExtractNumbers(FMsgNum, FMsgSize);
  478. end;
  479. end;
  480. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  481. function TPop3Client.Uidl : Boolean;
  482. begin
  483. if FMsgNum <= 0 then begin
  484. { UIDL command (all messages) }
  485. Result := StartTransaction('UIDL', '');
  486. if not Result then
  487. Exit;
  488. Result := GetMultiLine(FOnUidlBegin, FOnUidlLine,
  489. FOnUidlEnd, ProcessUidl);
  490. end
  491. else begin
  492. { Single message UIDL command }
  493. Result := StartTransaction('UIDL', IntToStr(FMsgNum));
  494. if not Result then
  495. Exit;
  496. Result := ExtractUidl(FMsgNum, FMsgUidl);
  497. end;
  498. end;
  499. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  500. function TPop3Client.Dele : Boolean;
  501. begin
  502. Result := StartTransaction('DELE', IntToStr(FMsgNum));
  503. end;
  504. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  505. function TPop3Client.Noop : Boolean;
  506. begin
  507. Result := StartTransaction('NOOP', '');
  508. end;
  509. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  510. function TPop3Client.Last : Boolean;
  511. begin
  512. Result := StartTransaction('LAST', '');
  513. if Result then
  514. Result := ExtractNumbers(FMsgNum, FMsgSize);
  515. end;
  516. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  517. function TPop3Client.Rset : Boolean;
  518. begin
  519. Result := StartTransaction('RSET', '');
  520. end;
  521. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  522. function TPop3Client.Top : Boolean;
  523. begin
  524. if FMsgLines < 0 then
  525. Result := FALSE
  526. else
  527. Result := StartTransaction('TOP' , IntToStr(FMsgNum) + ' ' +
  528. IntToStr(FMsgLines));
  529. if not Result then
  530. Exit;
  531. Result := GetMultiLine(FOnMessageBegin, FOnMessageLine, FOnMessageEnd, nil);
  532. end;
  533. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  534. function TPop3Client.StartTransaction(OpCode, Params : String) : Boolean;
  535. begin
  536. Result := FALSE;
  537. if (FProtocolState < pop3Transaction) and (not Pass) then
  538. Exit;
  539. if FProtocolState <> pop3Transaction then begin
  540. FErrorMessage := '-ERR ' + OpCode + ' command invalid now';
  541. Display(FErrorMessage);
  542. Exit;
  543. end;
  544. if Params <> '' then
  545. SendCommand(OpCode + ' ' + Params)
  546. else
  547. SendCommand(OpCode);
  548. Result := GetResponse;
  549. end;
  550. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  551. function TPop3Client.GetMultiLine(
  552. aOnBegin : TNotifyEvent;
  553. aOnLine : TNotifyEvent;
  554. aOnEnd : TNotifyEvent;
  555. aProcess : TNotifyEvent) : Boolean;
  556. var
  557. bFlag : Boolean;
  558. begin
  559. { Let the application know that the message is beginning }
  560. if Assigned(aOnBegin) then
  561. aOnBegin(Self);
  562. bFlag := FALSE;
  563. try
  564. while TRUE do begin
  565. { Read a message line }
  566. FLineTooLong := FALSE;
  567. if FWSocket.State = wsConnected then
  568. FWSocket.ReadLine(FTimeout, FLastResponse);
  569. { Check if we are still connected }
  570. if FWSocket.State <> wsConnected then begin
  571. FErrorMessage := '-ERR Disconneced unexpectedly';
  572. Display(FErrorMessage);
  573. break;
  574. end;
  575. { Check if we timed out }
  576. if FTimeOutFlag then begin
  577. FErrorMessage := '-ERR Receive Timeout';
  578. Display(FErrorMessage);
  579. break;
  580. end;
  581. { Check if end of message }
  582. if (not bFlag) and (FLastResponse = '.') then begin
  583. FLastResponse := '';
  584. break;
  585. end;
  586. { Check if message contains end-of-message mark }
  587. if (Length(FLastResponse) >= 2) and
  588. (FLastResponse[1] = '.') and (FLastResponse[2] = '.') then
  589. { Remove byte-stuff }
  590. FLastResponse := Copy(FLastResponse, 2, Length(FLastResponse));
  591. { Additional process }
  592. if Assigned(aProcess) then
  593. aProcess(Self);
  594. { Let the application process the message line }
  595. if Assigned(aOnLine) then
  596. aOnLine(Self);
  597. bFlag := FLineTooLong;
  598. { Let other application breaze }
  599. Application.ProcessMessages;
  600. end;
  601. finally
  602. { Let the application know that the message is finished }
  603. if Assigned(aOnEnd) then
  604. aOnEnd(Self);
  605. end;
  606. Result := not FTimeOutFlag;
  607. end;
  608. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  609. function TPop3Client.ExtractUidl(var N1 : Integer; var N2 : String) : Boolean;
  610. var
  611. p : PChar;
  612. begin
  613. Result := FALSE;
  614. N1 := 0;
  615. N2 := '';
  616. {$IFDEF VER80}
  617. { Delphi 1 do not automatically nul terminate strings }
  618. FLastResponse := FLastResponse + #0;
  619. {$ENDIF}
  620. { Search for first digit in response }
  621. p := @FLastResponse[1];
  622. while (p^ <> #0) and (not (p^ in ['0'..'9'])) do
  623. Inc(p);
  624. if p^ = #0 then { Invalid response, need a number }
  625. Exit;
  626. { Convert first number }
  627. N1 := atoi(p);
  628. { Search end of number }
  629. while (p^ <> #0) and (p^ in ['0'..'9']) do
  630. Inc(p);
  631. { Search Uidl }
  632. while (p^ = ' ') do
  633. Inc(p);
  634. { Copy UIDL }
  635. while (p^ <> #0) and (p^ in [#33..#126]) do begin
  636. N2 := N2 + p^;
  637. Inc(p);
  638. end;
  639. Result := TRUE;
  640. end;
  641. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  642. function TPop3Client.ExtractNumbers(var N1 : Integer; var N2 : Integer) : Boolean;
  643. var
  644. p : PChar;
  645. begin
  646. Result := FALSE;
  647. {$IFDEF VER80}
  648. { Delphi 1 do not automatically nul terminate strings }
  649. FLastResponse := FLastResponse + #0;
  650. {$ENDIF}
  651. { Search for first digit in response }
  652. p := @FLastResponse[1];
  653. while (p^ <> #0) and (not (p^ in ['0'..'9'])) do
  654. Inc(p);
  655. if p^ = #0 then begin
  656. { Invalid response, need a number }
  657. N1 := 0;
  658. N2 := 0;
  659. Exit;
  660. end;
  661. { Convert first number }
  662. N1 := atoi(p);
  663. { Search end of number }
  664. while (p^ <> #0) and (p^ in ['0'..'9']) do
  665. Inc(p);
  666. { Search next number }
  667. p := stpblk(p);
  668. if p^ = #0 then begin
  669. { Invalid response, need a number }
  670. N1 := 0;
  671. N2 := 0;
  672. Exit;
  673. end;
  674. N2 := atoi(p);
  675. Result := TRUE;
  676. end;
  677. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  678. procedure TPop3Client.WaitTimeOut(Sender : TObject);
  679. begin
  680. FTimeOutFlag := TRUE;
  681. end;
  682. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  683. procedure TPop3Client.LineTooLong(Sender : TObject);
  684. begin
  685. FLineTooLong := TRUE;
  686. end;
  687. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  688. procedure TPop3Client.SessionClosed(Sender : TObject; Error : WORD);
  689. begin
  690. if Assigned(FWait) then
  691. FWait.Stop;
  692. FProtocolState := pop3Disconnected;
  693. end;
  694. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  695. procedure TPop3Client.SendCommand(Cmd : String);
  696. begin
  697. Display('> ' + Cmd);
  698. Application.ProcessMessages;
  699. FWSocket.SendStr(Cmd + #13 + #10);
  700. end;
  701. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  702. function TPop3Client.GetResponse : Boolean;
  703. begin
  704. FWSocket.ReadLine(FTimeout, FLastResponse);
  705. Display('< ' + FLastResponse);
  706. Result := ((Length(FLastResponse) > 0) and (FLastResponse[1] = '+'));
  707. end;
  708. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  709. procedure TPop3Client.SetWait(Value : TWait);
  710. begin
  711. FWait := Value;
  712. FWSocket.WaitCtrl := Value;
  713. end;
  714. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  715. procedure TPop3Client.Notification(AComponent: TComponent; Operation: TOperation);
  716. begin
  717. inherited Notification(AComponent, Operation);
  718. if Operation = opRemove then begin
  719. if AComponent = FWait then
  720. FWait := nil
  721. else if AComponent = FWSocket then
  722. FWSocket := nil;
  723. end;
  724. end;
  725. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  726. procedure TPop3Client.Display(Msg : String);
  727. begin
  728. if Assigned(FOnDisplay) then
  729. FOnDisplay(Self, Msg);
  730. end;
  731. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  732. procedure TPop3Client.ClearErrorMessage;
  733. begin
  734. FErrorMessage := '';
  735. end;
  736. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  737. procedure TPop3Client.SetErrorMessage;
  738. begin
  739. if FErrorMessage = '' then
  740. FErrorMessage := FLastResponse;
  741. end;
  742. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  743. procedure Register;
  744. begin
  745. RegisterComponents('FPiette', [TPop3Client]);
  746. end;
  747. {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
  748. end.