source: Network/synapse/nntpsend.pas

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