source: trunk/Packages/synapse/source/lib/nntpsend.pas

Last change on this file was 2, checked in by chronos, 12 years ago
  • Přidáno: Základní kostra projektu.
  • Přidáno: Knihovna synapse.
File size: 14.5 KB
Line 
1{==============================================================================|
2| Project : Ararat Synapse | 001.005.003 |
3|==============================================================================|
4| Content: NNTP client |
5|==============================================================================|
6| Copyright (c)1999-2011, 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-2011. |
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
45{:@abstract(NNTP client)
46NNTP (network news transfer protocol)
47
48Used RFC: RFC-977, RFC-2980
49}
50
51{$IFDEF FPC}
52 {$MODE DELPHI}
53{$ENDIF}
54{$H+}
55
56{$IFDEF UNICODE}
57 {$WARN IMPLICIT_STRING_CAST OFF}
58 {$WARN IMPLICIT_STRING_CAST_LOSS OFF}
59 {$WARN SUSPICIOUS_TYPECAST OFF}
60{$ENDIF}
61
62unit nntpsend;
63
64interface
65
66uses
67 SysUtils, Classes,
68 blcksock, synautil;
69
70const
71 cNNTPProtocol = '119';
72
73type
74
75 {:abstract(Implementation of Network News Transfer Protocol.
76
77 Note: Are you missing properties for setting Username and Password? Look to
78 parent @link(TSynaClient) object!
79
80 Are you missing properties for specify server address and port? Look to
81 parent @link(TSynaClient) too!}
82 TNNTPSend = class(TSynaClient)
83 private
84 FSock: TTCPBlockSocket;
85 FResultCode: Integer;
86 FResultString: string;
87 FData: TStringList;
88 FDataToSend: TStringList;
89 FAutoTLS: Boolean;
90 FFullSSL: Boolean;
91 FNNTPcap: TStringList;
92 function ReadResult: Integer;
93 function ReadData: boolean;
94 function SendData: boolean;
95 function Connect: Boolean;
96 public
97 constructor Create;
98 destructor Destroy; override;
99
100 {:Connects to NNTP server and begin session.}
101 function Login: Boolean;
102
103 {:Logout from NNTP server and terminate session.}
104 function Logout: Boolean;
105
106 {:By this you can call any NNTP command.}
107 function DoCommand(const Command: string): boolean;
108
109 {:by this you can call any NNTP command. This variant is used for commands
110 for download information from server.}
111 function DoCommandRead(const Command: string): boolean;
112
113 {:by this you can call any NNTP command. This variant is used for commands
114 for upload information to server.}
115 function DoCommandWrite(const Command: string): boolean;
116
117 {:Download full message to @link(data) property. Value can be number of
118 message or message-id (in brackets).}
119 function GetArticle(const Value: string): Boolean;
120
121 {:Download only body of message to @link(data) property. Value can be number
122 of message or message-id (in brackets).}
123 function GetBody(const Value: string): Boolean;
124
125 {:Download only headers of message to @link(data) property. Value can be
126 number of message or message-id (in brackets).}
127 function GetHead(const Value: string): Boolean;
128
129 {:Get message status. Value can be number of message or message-id
130 (in brackets).}
131 function GetStat(const Value: string): Boolean;
132
133 {:Select given group.}
134 function SelectGroup(const Value: string): Boolean;
135
136 {:Tell to server 'I have mesage with given message-ID.' If server need this
137 message, message is uploaded to server.}
138 function IHave(const MessID: string): Boolean;
139
140 {:Move message pointer to last item in group.}
141 function GotoLast: Boolean;
142
143 {:Move message pointer to next item in group.}
144 function GotoNext: Boolean;
145
146 {:Download to @link(data) property list of all groups on NNTP server.}
147 function ListGroups: Boolean;
148
149 {:Download to @link(data) property list of all groups created after given time.}
150 function ListNewGroups(Since: TDateTime): Boolean;
151
152 {:Download to @link(data) property list of message-ids in given group since
153 given time.}
154 function NewArticles(const Group: string; Since: TDateTime): Boolean;
155
156 {:Upload new article to server. (for new messages by you)}
157 function PostArticle: Boolean;
158
159 {:Tells to remote NNTP server 'I am not NNTP client, but I am another NNTP
160 server'.}
161 function SwitchToSlave: Boolean;
162
163 {:Call NNTP XOVER command.}
164 function Xover(xoStart, xoEnd: string): boolean;
165
166 {:Call STARTTLS command for upgrade connection to SSL/TLS mode.}
167 function StartTLS: Boolean;
168
169 {:Try to find given capability in extension list. This list is getted after
170 successful login to NNTP server. If extension capability is not found,
171 then return is empty string.}
172 function FindCap(const Value: string): string;
173
174 {:Try get list of server extensions. List is returned in @link(data) property.}
175 function ListExtensions: Boolean;
176 published
177 {:Result code number of last operation.}
178 property ResultCode: Integer read FResultCode;
179
180 {:String description of last result code from NNTP server.}
181 property ResultString: string read FResultString;
182
183 {:Readed data. (message, etc.)}
184 property Data: TStringList read FData;
185
186 {:If is set to @true, then upgrade to SSL/TLS mode after login if remote
187 server support it.}
188 property AutoTLS: Boolean read FAutoTLS Write FAutoTLS;
189
190 {:SSL/TLS mode is used from first contact to server. Servers with full
191 SSL/TLS mode usualy using non-standard TCP port!}
192 property FullSSL: Boolean read FFullSSL Write FFullSSL;
193
194 {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.}
195 property Sock: TTCPBlockSocket read FSock;
196 end;
197
198implementation
199
200constructor TNNTPSend.Create;
201begin
202 inherited Create;
203 FSock := TTCPBlockSocket.Create;
204 FSock.Owner := self;
205 FData := TStringList.Create;
206 FDataToSend := TStringList.Create;
207 FNNTPcap := TStringList.Create;
208 FSock.ConvertLineEnd := True;
209 FTimeout := 60000;
210 FTargetPort := cNNTPProtocol;
211 FAutoTLS := False;
212 FFullSSL := False;
213end;
214
215destructor TNNTPSend.Destroy;
216begin
217 FSock.Free;
218 FDataToSend.Free;
219 FData.Free;
220 FNNTPcap.Free;
221 inherited Destroy;
222end;
223
224function TNNTPSend.ReadResult: Integer;
225var
226 s: string;
227begin
228 Result := 0;
229 FData.Clear;
230 s := FSock.RecvString(FTimeout);
231 FResultString := Copy(s, 5, Length(s) - 4);
232 if FSock.LastError <> 0 then
233 Exit;
234 if Length(s) >= 3 then
235 Result := StrToIntDef(Copy(s, 1, 3), 0);
236 FResultCode := Result;
237end;
238
239function TNNTPSend.ReadData: boolean;
240var
241 s: string;
242begin
243 repeat
244 s := FSock.RecvString(FTimeout);
245 if s = '.' then
246 break;
247 if (s <> '') and (s[1] = '.') then
248 s := Copy(s, 2, Length(s) - 1);
249 FData.Add(s);
250 until FSock.LastError <> 0;
251 Result := FSock.LastError = 0;
252end;
253
254function TNNTPSend.SendData: boolean;
255var
256 s: string;
257 n: integer;
258begin
259 for n := 0 to FDataToSend.Count - 1 do
260 begin
261 s := FDataToSend[n];
262 if (s <> '') and (s[1] = '.') then
263 s := s + '.';
264 FSock.SendString(s + CRLF);
265 if FSock.LastError <> 0 then
266 break;
267 end;
268 if FDataToSend.Count = 0 then
269 FSock.SendString(CRLF);
270 if FSock.LastError = 0 then
271 FSock.SendString('.' + CRLF);
272 FDataToSend.Clear;
273 Result := FSock.LastError = 0;
274end;
275
276function TNNTPSend.Connect: Boolean;
277begin
278 FSock.CloseSocket;
279 FSock.Bind(FIPInterface, cAnyPort);
280 if FSock.LastError = 0 then
281 FSock.Connect(FTargetHost, FTargetPort);
282 if FSock.LastError = 0 then
283 if FFullSSL then
284 FSock.SSLDoConnect;
285 Result := FSock.LastError = 0;
286end;
287
288function TNNTPSend.Login: Boolean;
289begin
290 Result := False;
291 FNNTPcap.Clear;
292 if not Connect then
293 Exit;
294 Result := (ReadResult div 100) = 2;
295 if Result then
296 begin
297 ListExtensions;
298 FNNTPcap.Assign(Fdata);
299 if (not FullSSL) and FAutoTLS and (FindCap('STARTTLS') <> '') then
300 Result := StartTLS;
301 end;
302 if (FUsername <> '') and Result then
303 begin
304 FSock.SendString('AUTHINFO USER ' + FUsername + CRLF);
305 if (ReadResult div 100) = 3 then
306 begin
307 FSock.SendString('AUTHINFO PASS ' + FPassword + CRLF);
308 Result := (ReadResult div 100) = 2;
309 end;
310 end;
311end;
312
313function TNNTPSend.Logout: Boolean;
314begin
315 FSock.SendString('QUIT' + CRLF);
316 Result := (ReadResult div 100) = 2;
317 FSock.CloseSocket;
318end;
319
320function TNNTPSend.DoCommand(const Command: string): Boolean;
321begin
322 FSock.SendString(Command + CRLF);
323 Result := (ReadResult div 100) = 2;
324 Result := Result and (FSock.LastError = 0);
325end;
326
327function TNNTPSend.DoCommandRead(const Command: string): Boolean;
328begin
329 Result := DoCommand(Command);
330 if Result then
331 begin
332 Result := ReadData;
333 Result := Result and (FSock.LastError = 0);
334 end;
335end;
336
337function TNNTPSend.DoCommandWrite(const Command: string): Boolean;
338var
339 x: integer;
340begin
341 FDataToSend.Assign(FData);
342 FSock.SendString(Command + CRLF);
343 x := (ReadResult div 100);
344 if x = 3 then
345 begin
346 SendData;
347 x := (ReadResult div 100);
348 end;
349 Result := x = 2;
350 Result := Result and (FSock.LastError = 0);
351end;
352
353function TNNTPSend.GetArticle(const Value: string): Boolean;
354var
355 s: string;
356begin
357 s := 'ARTICLE';
358 if Value <> '' then
359 s := s + ' ' + Value;
360 Result := DoCommandRead(s);
361end;
362
363function TNNTPSend.GetBody(const Value: string): Boolean;
364var
365 s: string;
366begin
367 s := 'BODY';
368 if Value <> '' then
369 s := s + ' ' + Value;
370 Result := DoCommandRead(s);
371end;
372
373function TNNTPSend.GetHead(const Value: string): Boolean;
374var
375 s: string;
376begin
377 s := 'HEAD';
378 if Value <> '' then
379 s := s + ' ' + Value;
380 Result := DoCommandRead(s);
381end;
382
383function TNNTPSend.GetStat(const Value: string): Boolean;
384var
385 s: string;
386begin
387 s := 'STAT';
388 if Value <> '' then
389 s := s + ' ' + Value;
390 Result := DoCommand(s);
391end;
392
393function TNNTPSend.SelectGroup(const Value: string): Boolean;
394begin
395 Result := DoCommand('GROUP ' + Value);
396end;
397
398function TNNTPSend.IHave(const MessID: string): Boolean;
399begin
400 Result := DoCommandWrite('IHAVE ' + MessID);
401end;
402
403function TNNTPSend.GotoLast: Boolean;
404begin
405 Result := DoCommand('LAST');
406end;
407
408function TNNTPSend.GotoNext: Boolean;
409begin
410 Result := DoCommand('NEXT');
411end;
412
413function TNNTPSend.ListGroups: Boolean;
414begin
415 Result := DoCommandRead('LIST');
416end;
417
418function TNNTPSend.ListNewGroups(Since: TDateTime): Boolean;
419begin
420 Result := DoCommandRead('NEWGROUPS ' + SimpleDateTime(Since) + ' GMT');
421end;
422
423function TNNTPSend.NewArticles(const Group: string; Since: TDateTime): Boolean;
424begin
425 Result := DoCommandRead('NEWNEWS ' + Group + ' ' + SimpleDateTime(Since) + ' GMT');
426end;
427
428function TNNTPSend.PostArticle: Boolean;
429begin
430 Result := DoCommandWrite('POST');
431end;
432
433function TNNTPSend.SwitchToSlave: Boolean;
434begin
435 Result := DoCommand('SLAVE');
436end;
437
438function TNNTPSend.Xover(xoStart, xoEnd: string): Boolean;
439var
440 s: string;
441begin
442 s := 'XOVER ' + xoStart;
443 if xoEnd <> xoStart then
444 s := s + '-' + xoEnd;
445 Result := DoCommandRead(s);
446end;
447
448function TNNTPSend.StartTLS: Boolean;
449begin
450 Result := False;
451 if FindCap('STARTTLS') <> '' then
452 begin
453 if DoCommand('STARTTLS') then
454 begin
455 Fsock.SSLDoConnect;
456 Result := FSock.LastError = 0;
457 end;
458 end;
459end;
460
461function TNNTPSend.ListExtensions: Boolean;
462begin
463 Result := DoCommandRead('LIST EXTENSIONS');
464end;
465
466function TNNTPSend.FindCap(const Value: string): string;
467var
468 n: Integer;
469 s: string;
470begin
471 s := UpperCase(Value);
472 Result := '';
473 for n := 0 to FNNTPcap.Count - 1 do
474 if Pos(s, UpperCase(FNNTPcap[n])) = 1 then
475 begin
476 Result := FNNTPcap[n];
477 Break;
478 end;
479end;
480
481{==============================================================================}
482
483end.
Note: See TracBrowser for help on using the repository browser.