nntpsend.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. {==============================================================================|
  2. | Project : Ararat Synapse | 001.005.001 |
  3. |==============================================================================|
  4. | Content: NNTP client |
  5. |==============================================================================|
  6. | Copyright (c)1999-2007, Lukas Gebauer |
  7. | All rights reserved. |
  8. | |
  9. | Redistribution and use in source and binary forms, with or without |
  10. | modification, are permitted provided that the following conditions are met: |
  11. | |
  12. | Redistributions of source code must retain the above copyright notice, this |
  13. | list of conditions and the following disclaimer. |
  14. | |
  15. | Redistributions in binary form must reproduce the above copyright notice, |
  16. | this list of conditions and the following disclaimer in the documentation |
  17. | and/or other materials provided with the distribution. |
  18. | |
  19. | Neither the name of Lukas Gebauer nor the names of its contributors may |
  20. | be used to endorse or promote products derived from this software without |
  21. | specific prior written permission. |
  22. | |
  23. | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
  24. | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
  25. | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
  26. | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR |
  27. | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
  28. | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
  29. | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
  30. | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
  31. | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
  32. | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
  33. | DAMAGE. |
  34. |==============================================================================|
  35. | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
  36. | Portions created by Lukas Gebauer are Copyright (c) 1999-2007. |
  37. | All Rights Reserved. |
  38. |==============================================================================|
  39. | Contributor(s): |
  40. |==============================================================================|
  41. | History: see HISTORY.HTM from distribution package |
  42. | (Found at URL: http://www.ararat.cz/synapse/) |
  43. |==============================================================================}
  44. {:@abstract(NNTP client)
  45. NNTP (network news transfer protocol)
  46. Used RFC: RFC-977, RFC-2980
  47. }
  48. {$IFDEF FPC}
  49. {$MODE DELPHI}
  50. {$ENDIF}
  51. {$H+}
  52. unit nntpsend;
  53. interface
  54. uses
  55. SysUtils, Classes,
  56. blcksock, synautil;
  57. const
  58. cNNTPProtocol = '119';
  59. type
  60. {:abstract(Implementation of Network News Transfer Protocol.
  61. Note: Are you missing properties for setting Username and Password? Look to
  62. parent @link(TSynaClient) object!
  63. Are you missing properties for specify server address and port? Look to
  64. parent @link(TSynaClient) too!}
  65. TNNTPSend = class(TSynaClient)
  66. private
  67. FSock: TTCPBlockSocket;
  68. FResultCode: Integer;
  69. FResultString: string;
  70. FData: TStringList;
  71. FDataToSend: TStringList;
  72. FAutoTLS: Boolean;
  73. FFullSSL: Boolean;
  74. FNNTPcap: TStringList;
  75. function ReadResult: Integer;
  76. function ReadData: boolean;
  77. function SendData: boolean;
  78. function Connect: Boolean;
  79. public
  80. constructor Create;
  81. destructor Destroy; override;
  82. {:Connects to NNTP server and begin session.}
  83. function Login: Boolean;
  84. {:Logout from NNTP server and terminate session.}
  85. function Logout: Boolean;
  86. {:By this you can call any NNTP command.}
  87. function DoCommand(const Command: string): boolean;
  88. {:by this you can call any NNTP command. This variant is used for commands
  89. for download information from server.}
  90. function DoCommandRead(const Command: string): boolean;
  91. {:by this you can call any NNTP command. This variant is used for commands
  92. for upload information to server.}
  93. function DoCommandWrite(const Command: string): boolean;
  94. {:Download full message to @link(data) property. Value can be number of
  95. message or message-id (in brackets).}
  96. function GetArticle(const Value: string): Boolean;
  97. {:Download only body of message to @link(data) property. Value can be number
  98. of message or message-id (in brackets).}
  99. function GetBody(const Value: string): Boolean;
  100. {:Download only headers of message to @link(data) property. Value can be
  101. number of message or message-id (in brackets).}
  102. function GetHead(const Value: string): Boolean;
  103. {:Get message status. Value can be number of message or message-id
  104. (in brackets).}
  105. function GetStat(const Value: string): Boolean;
  106. {:Select given group.}
  107. function SelectGroup(const Value: string): Boolean;
  108. {:Tell to server 'I have mesage with given message-ID.' If server need this
  109. message, message is uploaded to server.}
  110. function IHave(const MessID: string): Boolean;
  111. {:Move message pointer to last item in group.}
  112. function GotoLast: Boolean;
  113. {:Move message pointer to next item in group.}
  114. function GotoNext: Boolean;
  115. {:Download to @link(data) property list of all groups on NNTP server.}
  116. function ListGroups: Boolean;
  117. {:Download to @link(data) property list of all groups created after given time.}
  118. function ListNewGroups(Since: TDateTime): Boolean;
  119. {:Download to @link(data) property list of message-ids in given group since
  120. given time.}
  121. function NewArticles(const Group: string; Since: TDateTime): Boolean;
  122. {:Upload new article to server. (for new messages by you)}
  123. function PostArticle: Boolean;
  124. {:Tells to remote NNTP server 'I am not NNTP client, but I am another NNTP
  125. server'.}
  126. function SwitchToSlave: Boolean;
  127. {:Call NNTP XOVER command.}
  128. function Xover(xoStart, xoEnd: string): boolean;
  129. {:Call STARTTLS command for upgrade connection to SSL/TLS mode.}
  130. function StartTLS: Boolean;
  131. {:Try to find given capability in extension list. This list is getted after
  132. successful login to NNTP server. If extension capability is not found,
  133. then return is empty string.}
  134. function FindCap(const Value: string): string;
  135. {:Try get list of server extensions. List is returned in @link(data) property.}
  136. function ListExtensions: Boolean;
  137. published
  138. {:Result code number of last operation.}
  139. property ResultCode: Integer read FResultCode;
  140. {:String description of last result code from NNTP server.}
  141. property ResultString: string read FResultString;
  142. {:Readed data. (message, etc.)}
  143. property Data: TStringList read FData;
  144. {:If is set to @true, then upgrade to SSL/TLS mode after login if remote
  145. server support it.}
  146. property AutoTLS: Boolean read FAutoTLS Write FAutoTLS;
  147. {:SSL/TLS mode is used from first contact to server. Servers with full
  148. SSL/TLS mode usualy using non-standard TCP port!}
  149. property FullSSL: Boolean read FFullSSL Write FFullSSL;
  150. {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.}
  151. property Sock: TTCPBlockSocket read FSock;
  152. end;
  153. implementation
  154. constructor TNNTPSend.Create;
  155. begin
  156. inherited Create;
  157. FSock := TTCPBlockSocket.Create;
  158. FData := TStringList.Create;
  159. FDataToSend := TStringList.Create;
  160. FNNTPcap := TStringList.Create;
  161. FSock.ConvertLineEnd := True;
  162. FTimeout := 60000;
  163. FTargetPort := cNNTPProtocol;
  164. FAutoTLS := False;
  165. FFullSSL := False;
  166. end;
  167. destructor TNNTPSend.Destroy;
  168. begin
  169. FSock.Free;
  170. FDataToSend.Free;
  171. FData.Free;
  172. FNNTPcap.Free;
  173. inherited Destroy;
  174. end;
  175. function TNNTPSend.ReadResult: Integer;
  176. var
  177. s: string;
  178. begin
  179. Result := 0;
  180. FData.Clear;
  181. s := FSock.RecvString(FTimeout);
  182. FResultString := Copy(s, 5, Length(s) - 4);
  183. if FSock.LastError <> 0 then
  184. Exit;
  185. if Length(s) >= 3 then
  186. Result := StrToIntDef(Copy(s, 1, 3), 0);
  187. FResultCode := Result;
  188. end;
  189. function TNNTPSend.ReadData: boolean;
  190. var
  191. s: string;
  192. begin
  193. repeat
  194. s := FSock.RecvString(FTimeout);
  195. if s = '.' then
  196. break;
  197. if (s <> '') and (s[1] = '.') then
  198. s := Copy(s, 2, Length(s) - 1);
  199. FData.Add(s);
  200. until FSock.LastError <> 0;
  201. Result := FSock.LastError = 0;
  202. end;
  203. function TNNTPSend.SendData: boolean;
  204. var
  205. s: string;
  206. n: integer;
  207. begin
  208. for n := 0 to FDataToSend.Count - 1 do
  209. begin
  210. s := FDataToSend[n];
  211. if (s <> '') and (s[1] = '.') then
  212. s := s + '.';
  213. FSock.SendString(s + CRLF);
  214. if FSock.LastError <> 0 then
  215. break;
  216. end;
  217. if FDataToSend.Count = 0 then
  218. FSock.SendString(CRLF);
  219. if FSock.LastError = 0 then
  220. FSock.SendString('.' + CRLF);
  221. FDataToSend.Clear;
  222. Result := FSock.LastError = 0;
  223. end;
  224. function TNNTPSend.Connect: Boolean;
  225. begin
  226. FSock.CloseSocket;
  227. FSock.Bind(FIPInterface, cAnyPort);
  228. if FSock.LastError = 0 then
  229. FSock.Connect(FTargetHost, FTargetPort);
  230. if FSock.LastError = 0 then
  231. if FFullSSL then
  232. FSock.SSLDoConnect;
  233. Result := FSock.LastError = 0;
  234. end;
  235. function TNNTPSend.Login: Boolean;
  236. begin
  237. Result := False;
  238. FNNTPcap.Clear;
  239. if not Connect then
  240. Exit;
  241. Result := (ReadResult div 100) = 2;
  242. ListExtensions;
  243. FNNTPcap.Assign(Fdata);
  244. if Result then
  245. if (not FullSSL) and FAutoTLS and (FindCap('STARTTLS') <> '') then
  246. Result := StartTLS;
  247. if (FUsername <> '') and Result then
  248. begin
  249. FSock.SendString('AUTHINFO USER ' + FUsername + CRLF);
  250. if (ReadResult div 100) = 3 then
  251. begin
  252. FSock.SendString('AUTHINFO PASS ' + FPassword + CRLF);
  253. Result := (ReadResult div 100) = 2;
  254. end;
  255. end;
  256. end;
  257. function TNNTPSend.Logout: Boolean;
  258. begin
  259. FSock.SendString('QUIT' + CRLF);
  260. Result := (ReadResult div 100) = 2;
  261. FSock.CloseSocket;
  262. end;
  263. function TNNTPSend.DoCommand(const Command: string): Boolean;
  264. begin
  265. FSock.SendString(Command + CRLF);
  266. Result := (ReadResult div 100) = 2;
  267. Result := Result and (FSock.LastError = 0);
  268. end;
  269. function TNNTPSend.DoCommandRead(const Command: string): Boolean;
  270. begin
  271. Result := DoCommand(Command);
  272. if Result then
  273. begin
  274. Result := ReadData;
  275. Result := Result and (FSock.LastError = 0);
  276. end;
  277. end;
  278. function TNNTPSend.DoCommandWrite(const Command: string): Boolean;
  279. var
  280. x: integer;
  281. begin
  282. FDataToSend.Assign(FData);
  283. FSock.SendString(Command + CRLF);
  284. x := (ReadResult div 100);
  285. if x = 3 then
  286. begin
  287. SendData;
  288. x := (ReadResult div 100);
  289. end;
  290. Result := x = 2;
  291. Result := Result and (FSock.LastError = 0);
  292. end;
  293. function TNNTPSend.GetArticle(const Value: string): Boolean;
  294. var
  295. s: string;
  296. begin
  297. s := 'ARTICLE';
  298. if Value <> '' then
  299. s := s + ' ' + Value;
  300. Result := DoCommandRead(s);
  301. end;
  302. function TNNTPSend.GetBody(const Value: string): Boolean;
  303. var
  304. s: string;
  305. begin
  306. s := 'BODY';
  307. if Value <> '' then
  308. s := s + ' ' + Value;
  309. Result := DoCommandRead(s);
  310. end;
  311. function TNNTPSend.GetHead(const Value: string): Boolean;
  312. var
  313. s: string;
  314. begin
  315. s := 'HEAD';
  316. if Value <> '' then
  317. s := s + ' ' + Value;
  318. Result := DoCommandRead(s);
  319. end;
  320. function TNNTPSend.GetStat(const Value: string): Boolean;
  321. var
  322. s: string;
  323. begin
  324. s := 'STAT';
  325. if Value <> '' then
  326. s := s + ' ' + Value;
  327. Result := DoCommand(s);
  328. end;
  329. function TNNTPSend.SelectGroup(const Value: string): Boolean;
  330. begin
  331. Result := DoCommand('GROUP ' + Value);
  332. end;
  333. function TNNTPSend.IHave(const MessID: string): Boolean;
  334. begin
  335. Result := DoCommandWrite('IHAVE ' + MessID);
  336. end;
  337. function TNNTPSend.GotoLast: Boolean;
  338. begin
  339. Result := DoCommand('LAST');
  340. end;
  341. function TNNTPSend.GotoNext: Boolean;
  342. begin
  343. Result := DoCommand('NEXT');
  344. end;
  345. function TNNTPSend.ListGroups: Boolean;
  346. begin
  347. Result := DoCommandRead('LIST');
  348. end;
  349. function TNNTPSend.ListNewGroups(Since: TDateTime): Boolean;
  350. begin
  351. Result := DoCommandRead('NEWGROUPS ' + SimpleDateTime(Since) + ' GMT');
  352. end;
  353. function TNNTPSend.NewArticles(const Group: string; Since: TDateTime): Boolean;
  354. begin
  355. Result := DoCommandRead('NEWNEWS ' + Group + ' ' + SimpleDateTime(Since) + ' GMT');
  356. end;
  357. function TNNTPSend.PostArticle: Boolean;
  358. begin
  359. Result := DoCommandWrite('POST');
  360. end;
  361. function TNNTPSend.SwitchToSlave: Boolean;
  362. begin
  363. Result := DoCommand('SLAVE');
  364. end;
  365. function TNNTPSend.Xover(xoStart, xoEnd: string): Boolean;
  366. var
  367. s: string;
  368. begin
  369. s := 'XOVER ' + xoStart;
  370. if xoEnd <> xoStart then
  371. s := s + '-' + xoEnd;
  372. Result := DoCommandRead(s);
  373. end;
  374. function TNNTPSend.StartTLS: Boolean;
  375. begin
  376. Result := False;
  377. if FindCap('STARTTLS') <> '' then
  378. begin
  379. if DoCommand('STARTTLS') then
  380. begin
  381. Fsock.SSLDoConnect;
  382. Result := FSock.LastError = 0;
  383. end;
  384. end;
  385. end;
  386. function TNNTPSend.ListExtensions: Boolean;
  387. begin
  388. Result := DoCommandRead('LIST EXTENSIONS');
  389. end;
  390. function TNNTPSend.FindCap(const Value: string): string;
  391. var
  392. n: Integer;
  393. s: string;
  394. begin
  395. s := UpperCase(Value);
  396. Result := '';
  397. for n := 0 to FNNTPcap.Count - 1 do
  398. if Pos(s, UpperCase(FNNTPcap[n])) = 1 then
  399. begin
  400. Result := FNNTPcap[n];
  401. Break;
  402. end;
  403. end;
  404. {==============================================================================}
  405. end.