bb3b9f8e |
1 | <?php |
2 | /******************************************************************************** |
3 | * include/NetNNTP.inc.php : NNTP subroutines |
4 | * ------------------------- |
5 | * |
6 | * This file is part of the banana distribution |
7 | * Copyright: See COPYING files that comes with this distribution |
8 | ********************************************************************************/ |
9 | |
10 | /** Class NNTP |
11 | * implements some basic functions for NNTP protocol |
12 | */ |
13 | class nntp { |
14 | /** socket filehandle */ |
15 | var $ns; |
16 | /** debug mode */ |
17 | var $debug; |
18 | /** posting allowed */ |
19 | var $posting; |
20 | /** last NNTP error code */ |
21 | var $lasterrorcode; |
22 | /** last NNTP error text */ |
23 | var $lasterrortext; |
24 | |
25 | # Socket functions |
26 | |
27 | /** get a line from server |
28 | * @return STRING |
29 | */ |
30 | |
31 | function gline() { |
32 | $line = preg_replace("/(\r|\n)/","",fgets($this->ns,1200)); |
33 | if ($this->debug) { |
34 | print "NNTP >>>> $line \n"; |
35 | } |
36 | return $line; |
37 | } |
38 | |
39 | /** puts a line on server |
40 | * @param STRING $_line line to put |
41 | */ |
42 | |
43 | function pline($_line) { |
44 | if ($this->debug) { |
45 | $dline = preg_replace("/\r\n$/","",$_line); |
46 | $dline = preg_replace("/(\r|\n|\r\n)/","\nNNTP <<<< ",$dline); |
47 | print "NNTP <<<< $dline \n"; |
48 | } |
49 | return fputs($this->ns,$_line,strlen($_line)); |
50 | } |
51 | |
52 | /** constructor |
53 | * @param $_host STRING NNTP host |
54 | * @param $_timeout INTEGER socket timeout |
55 | * @param $_debug BOOLEAN debug flag (generates verbose output if on) |
56 | * @param $_reader BOOLEAN sends a "MODE READER" at connection if true |
57 | */ |
58 | |
59 | function nntp($_host,$_timeout=120,$_debug=0,$_reader=true) { |
60 | if (preg_match("/([^:]+):([^:]+)/",$_host,$regs)) { |
61 | $host = $regs[1]; |
62 | $port = $regs[2]; |
63 | } else { |
64 | $host = $_host; |
65 | $port = 119; |
66 | } |
94219412 |
67 | $this->ns = fsockopen($host, $port, $errno, $errstr, $_timeout); |
bb3b9f8e |
68 | $this->debug = $_debug; |
69 | if (!$this->ns) { |
70 | if ($this->debug) { |
71 | echo "ERREUR: $errno - $errstr <br />\n"; |
72 | } |
73 | $this = false; |
74 | return false; |
75 | } |
76 | $result = $this->gline(); |
77 | $this->posting = (substr($result,0,3)=="200"); |
78 | if ($_reader && (substr($result,0,1)=="2")) { |
79 | $this->pline("MODE READER\r\n"); |
80 | $result = $this->gline(); |
81 | $this->posting = (substr($result,0,3)=="200"); |
82 | } |
83 | return (substr($result,0,1)=="2"); |
84 | } |
85 | |
86 | # strict NNTP Functions [RFC 977] |
87 | # see http://www.faqs.org/rfcs/rfc977.html |
88 | |
89 | /** authentification |
90 | * @param $_user STRING login |
91 | * @param $_pass INTEGER password |
92 | * @return BOOLEAN true if authentication was successful |
93 | */ |
94 | |
95 | function authinfo($_user,$_pass) { |
96 | $user = preg_replace("/(\r|\n)/","",$_user); |
97 | $pass = preg_replace("/(\r|\n)/","",$_pass); |
98 | $this->pline("AUTHINFO USER $user\r\n"); |
99 | $this->gline(); |
100 | $this->pline("AUTHINFO PASS $pass\r\n"); |
101 | $result=$this->gline(); |
102 | if (substr($result,0,1)!="2") { |
103 | $this->lasterrorcode = substr($result,0,3); |
104 | $this->lasterrortext = substr($result,4); |
105 | return false; |
106 | } |
107 | return true; |
108 | } |
109 | |
110 | /** retrieves an article |
111 | * MSGID is a numeric ID a shown in article's headers. MSGNUM is a |
112 | * server-dependent ID (see X-Ref on many servers) and retriving |
113 | * an article by this way will change the current article pointer. |
114 | * If an error occur, false is returned. |
115 | * @param $_msgid STRING MSGID or MSGNUM of article |
116 | * @return ARRAY lines of the article |
117 | * @see body |
118 | * @see head |
119 | */ |
120 | |
121 | function article($_msgid="") { |
122 | $msgid = preg_replace("/(\r|\n)/","",$_msgid); |
123 | $this->pline("ARTICLE $msgid\r\n"); |
124 | $result = $this->gline(); |
125 | if (substr($result,0,1)!="2") { |
126 | $this->lasterrorcode = substr($result,0,3); |
127 | $this->lasterrortext = substr($result,4); |
128 | return false; |
129 | } |
130 | $result = $this->gline(); |
131 | while ($result != ".") { |
132 | $array[] = $result; |
133 | $result = $this->gline(); |
134 | } |
135 | return $array; |
136 | } |
137 | |
138 | /** post a message |
139 | * if an error occur, false is returned |
140 | * @param $_message STRING message to post |
141 | * @return STRING MSGID of article |
142 | */ |
143 | |
144 | function post($_message) { |
145 | if (is_array($_message)) { |
146 | $message=join("\n",$_message); |
147 | } else { |
148 | $message=$_message; |
149 | } |
150 | $this->pline("POST \r\n"); |
151 | $result=$this->gline(); |
152 | if (substr($result,0,1)!="3") { |
153 | $this->lasterrorcode = substr($result,0,3); |
154 | $this->lasterrortext = substr($result,4); |
155 | return false; |
156 | } |
157 | $this->pline($message."\r\n.\r\n"); |
158 | $result = $this->gline(); |
159 | if (substr($result,0,1)!="2") { |
160 | $this->lasterrorcode = substr($result,0,3); |
161 | $this->lasterrortext = substr($result,4); |
162 | return false; |
163 | } |
164 | if (substr($result,0,1)=="2") { |
165 | if (preg_match("/(<[^@>]+@[^@>]+>)/",$result,$regs)) { |
166 | return $regs[0]; |
167 | } else { |
168 | return true; |
169 | } |
170 | } |
171 | return false; |
172 | } |
173 | |
174 | /** fetches the body of an article |
175 | * params are the same as article |
176 | * @param $_msgid STRING MSGID or MSGNUM of article |
177 | * @return ARRAY lines of the article |
178 | * @see article |
179 | * @see head |
180 | */ |
181 | |
182 | function body($_msgid="") { |
183 | $msgid = preg_replace("/(\r|\n)/","",$_msgid); |
184 | $this->pline("BODY $msgid\r\n"); |
185 | $result = $this->gline(); |
186 | if (substr($result,0,1)!="2") { |
187 | $this->lasterrorcode = substr($result,0,3); |
188 | $this->lasterrortext = substr($result,4); |
189 | return false; |
190 | } |
191 | $result = $this->gline(); |
192 | while ($result != ".") { |
193 | $array[] = $result; |
194 | $result = $this->gline(); |
195 | } |
196 | return $array; |
197 | } |
198 | |
199 | /** fetches the headers of an article |
200 | * params are the same as article |
201 | * @param $_msgid STRING MSGID or MSGNUM of article |
202 | * @return ARRAY lines of the article |
203 | * @see article |
204 | * @see body |
205 | */ |
206 | |
207 | function head($_msgid="") { |
208 | $msgid = preg_replace("/(\r|\n)/","",$_msgid); |
209 | $this->pline("HEAD $msgid\r\n"); |
210 | $result = $this->gline(); |
211 | if (substr($result,0,1)!="2") { |
212 | $this->lasterrorcode = substr($result,0,3); |
213 | $this->lasterrortext = substr($result,4); |
214 | return false; |
215 | } |
216 | $result = $this->gline(); |
217 | while ($result != ".") { |
218 | $array[] = $result; |
219 | $result = $this->gline(); |
220 | } |
221 | return $array; |
222 | } |
223 | |
224 | /** set current group |
225 | * @param $_group STRING |
226 | * @return ARRAY array : nb of articles in group, MSGNUM of first article, MSGNUM of last article, and group name |
227 | */ |
228 | |
229 | function group($_group) { |
230 | $group = preg_replace("/(\r|\n)/","",$_group); |
231 | $this->pline("GROUP $group\r\n"); |
232 | $line = $this->gline(); |
233 | if (substr($line,0,1)!="2") { |
761fc9a6 |
234 | $this->lasterrorcode = substr($line,0,3); |
235 | $this->lasterrortext = substr($line,4); |
bb3b9f8e |
236 | return false; |
237 | } |
238 | if (preg_match("/^2\d{2} (\d+) (\d+) (\d+) ([^ ]+)/", |
239 | $line,$regs)) { |
240 | return array($regs[1],$regs[2],$regs[3],$regs[4]); |
241 | } |
242 | return $false; |
243 | } |
244 | |
245 | /** set the article pointer to the previous article in current group |
246 | * @return STRING MSGID of article |
247 | * @see next |
248 | */ |
249 | |
250 | function last() { |
251 | $this->pline("LAST \r\n"); |
252 | $line = $this->gline(); |
253 | if (substr($line,0,1)!="2") { |
254 | $this->lasterrorcode = substr($result,0,3); |
255 | $this->lasterrortext = substr($result,4); |
256 | return false; |
257 | } |
258 | if (preg_match("/^2\d{2} \d+ <([^>]+)>/",$line,$regs)) { |
259 | return "<{$regs[1]}>"; |
260 | } |
261 | return $false; |
262 | } |
263 | |
264 | /** set the article pointer to the next article in current group |
265 | * @return STRING MSGID of article |
266 | * @see last |
267 | */ |
268 | |
269 | function next() { |
270 | $this->pline("NEXT \r\n"); |
271 | $line = $this->gline(); |
272 | if (substr($line,0,1)!="2") { |
273 | $this->lasterrorcode = substr($result,0,3); |
274 | $this->lasterrortext = substr($result,4); |
275 | return false; |
276 | } |
277 | if (preg_match("/^2\d{2} \d+ <([^>]+)>/",$line,$regs)) { |
278 | return "<{$regs[1]}>"; |
279 | } |
280 | return $false; |
281 | } |
282 | |
283 | /** set the current article pointer |
284 | * @param $_msgid STRING MSGID or MSGNUM of article |
285 | * @return BOOLEAN true if authentication was successful, error code otherwise |
286 | * @see article |
287 | * @see body |
288 | */ |
289 | |
290 | function nntpstat($_msgid) { |
291 | $msgid = preg_replace("/(\r|\n)/","",$_msgid); |
292 | $this->pline("STAT $msgid\r\n"); |
293 | $line = $this->gline(); |
294 | if (substr($line,0,1)!="2") { |
295 | $this->lasterrorcode = substr($result,0,3); |
296 | $this->lasterrortext = substr($result,4); |
297 | return false; |
298 | } |
299 | if (preg_match("/^2\d{2} \d+ <([^>]+)>/",$line,$regs)) { |
300 | return "<{$regs[1]}>"; |
301 | } |
302 | return false; |
303 | } |
304 | |
305 | /** returns true if posting is allowed |
306 | * @return BOOLEAN true if posting is allowed lines |
307 | */ |
308 | |
309 | function postok() { |
310 | return ($this->posting); |
311 | } |
312 | |
313 | /** gets information about all active newsgroups |
314 | * @return ARRAY group name => (MSGNUM of first article, MSGNUM of last article, NNTP flags) |
315 | * @see newgroups |
316 | */ |
317 | |
318 | function liste() { |
319 | $this->pline("LIST\r\n"); |
320 | if (substr($this->gline(),0,1)!="2") return false; |
321 | $result = $this->gline(); |
322 | while ($result != ".") { |
94219412 |
323 | preg_match("/([^ ]+) (\d+) (\d+) (.)/",$result,$regs); |
bb3b9f8e |
324 | $array[$regs[1]]=array(intval($regs[2]),intval($regs[3]), |
325 | intval($regs[4])); |
326 | $result = $this->gline(); |
327 | } |
328 | return $array; |
329 | } |
330 | |
331 | /** get information about recent newsgroups |
332 | * same as list, but information are limited to newgroups created after $_since |
333 | * @param $_since INTEGER unix timestamp |
334 | * @param $_distributions STRING distributions |
335 | * @return ARRAY same format as liste |
336 | * @see liste |
337 | */ |
338 | |
339 | function newgroups($_since,$_distributions="") { |
340 | #assume $_since is a unix timestamp |
341 | $distributions = preg_replace("/(\r|\n)/","",$_distributions); |
342 | $this->pline("NEWGROUPS ".gmdate("ymd His",$_since) |
343 | ." GMT $distributions\r\n"); |
344 | if (substr($this->gline(),0,1)!="2") return false; |
345 | $result = $this->gline(); |
8a1d7dee |
346 | $array=array(); |
bb3b9f8e |
347 | while ($result != ".") { |
8a1d7dee |
348 | preg_match("/([^ ]+) (\d+) (\d+) (.)/",$result,$regs); |
bb3b9f8e |
349 | $array[$regs[1]]=array(intval($regs[2]),intval($regs[3]), |
350 | intval($regs[4])); |
351 | $result = $this->gline(); |
352 | } |
353 | return $array; |
354 | } |
355 | |
356 | /** gets a list of new articles |
357 | * @param $_since INTEGER unix timestamp |
358 | * @parma $_groups STRING pattern of intersting groups |
359 | * @return ARRAY MSGID of new articles |
360 | */ |
361 | |
362 | function newnews($_since,$_groups="*",$_distributions="") { |
363 | $distributions = preg_replace("/(\r|\n)/","",$_distributions); |
364 | $groups = preg_replace("/(\r|\n)/","",$_groups); |
5c33c608 |
365 | $array=array(); |
bb3b9f8e |
366 | #assume $since is a unix timestamp |
367 | $this->pline("NEWNEWS $_groups ".gmdate("ymd His",$_since) |
368 | ." GMT $distributions\r\n"); |
369 | if (substr($this->gline(),0,1)!="2") return false; |
370 | $result = $this->gline(); |
371 | while ($result != ".") { |
372 | $array[] = $result; |
373 | $result = $this->gline(); |
374 | } |
375 | return $array; |
376 | } |
377 | |
378 | /** Tell the remote server that I am not a user client, but probably another news server |
379 | * @return BOOLEAN true if sucessful |
380 | */ |
381 | |
382 | function slave() { |
383 | $this->pline("SLAVE \r\n"); |
384 | return (substr($this->gline(),0,1)=="2"); |
385 | } |
386 | |
387 | /** implements IHAVE method |
388 | * @param $_msgid STRING MSGID of article |
389 | * @param $_message STRING article |
390 | * @return BOOLEAN |
391 | */ |
392 | |
393 | function ihave($_msgid,$_message=false) { |
394 | $msgid = preg_replace("/(\r|\n)/","",$_msgid); |
395 | if (is_array($message)) |
396 | $message=join("\n",$_message); |
397 | else |
398 | $message=$_message; |
399 | $this->pline("IHAVE $msgid \r\n"); |
400 | $result=$this->gline(); |
401 | if ($message && (substr($result,0,1)=="3")) { |
402 | $this->pline("$message\r\n.\r\n"); |
403 | $result=$this->gline(); |
404 | } |
405 | return (substr($result,0,1)=="2"); |
406 | } |
407 | |
408 | /** closes connection to server |
409 | */ |
410 | |
411 | function quit() { |
412 | $this->pline("QUIT\r\n"); |
413 | $this->gline(); |
414 | fclose($this->ns); |
415 | } |
416 | |
417 | # NNTP Extensions [RFC 2980] |
418 | |
419 | /** Returns the date on the remote server |
420 | * @return INTEGER timestamp |
421 | */ |
422 | |
423 | function date() { |
424 | $this->pline("DATE \r\n"); |
425 | $result = $this->gline(); |
426 | if (preg_match("/^111 (\d{4})(\d{2})(\d{2})" |
427 | ."(\d{2})(\d{2})(\d{2})$/",$result,$r)) { |
428 | return gmmktime($r[4],$r[5],$r[6],$r[2],$r[3],$r[1]); |
429 | } |
430 | return false; |
431 | } |
432 | |
433 | /** returns group descriptions |
434 | * @param $_pattern STRING pattern of intersting groups |
435 | * @return ARRAY group name => description |
436 | */ |
437 | |
438 | function xgtitle($_pattern="*") { |
439 | $pattern = preg_replace("/(\r|\n)/","",$_pattern); |
440 | $this->pline("XGTITLE $pattern \r\n"); |
441 | if (substr($this->gline(),0,1)!="2") return false; |
442 | $result = $this->gline(); |
443 | while ($result != ".") { |
444 | preg_match("/([^ \t]+)( |\t)+(.+)$/",$result,$regs); |
445 | $array[$regs[1]]=$regs[3]; |
446 | $result = $this->gline(); |
447 | } |
448 | return $array; |
449 | } |
450 | |
451 | /** obtain the header field $hdr for all the messages specified |
452 | * @param $_hdr STRING name of the header (eg: 'From') |
453 | * @param $_range STRING range of articles |
454 | * @return ARRAY MSGNUM => header value |
455 | */ |
456 | |
457 | function xhdr($_hdr,$_range="") { |
458 | $hdr = preg_replace("/(\r|\n)/","",$_hdr); |
459 | $range = preg_replace("/(\r|\n)/","",$_range); |
460 | $this->pline("XHDR $hdr $range \r\n"); |
461 | if (substr($this->gline(),0,1)!="2") return false; |
462 | $result = $this->gline(); |
5d502f47 |
463 | $array=array(); |
bb3b9f8e |
464 | while ($result != ".") { |
465 | preg_match("/([^ \t]+) (.*)$/",$result,$regs); |
466 | $array[$regs[1]]=$regs[2]; |
467 | $result = $this->gline(); |
468 | } |
469 | return $array; |
470 | } |
471 | |
5d502f47 |
472 | /** obtain the header field $_hdr matching $_pat for all the messages specified |
473 | * @param $_hdr STRING name of the header (eg: 'From') |
474 | * @param $_range STRING range of articles |
475 | * @param $_pat STRING pattern |
476 | * @return ARRAY MSGNUM => header value |
477 | */ |
478 | |
479 | function xpat($_hdr,$_range,$_pat) { |
480 | $hdr = preg_replace("/(\r|\n)/","",$_hdr); |
481 | $range = preg_replace("/(\r|\n)/","",$_range); |
482 | $pat = preg_replace("/(\r|\n)/","",$_pat); |
483 | $this->pline("XPAT $hdr $range $pat\r\n"); |
484 | if (substr($this->gline(),0,1)!="2") return false; |
485 | $result = $this->gline(); |
486 | while ($result != ".") { |
487 | preg_match("/([^ \t]+) (.*)$/",$result,$regs); |
488 | $array[$regs[1]]=$regs[2]; |
489 | $result = $this->gline(); |
490 | } |
491 | return $array; |
492 | } |
bb3b9f8e |
493 | } |
494 | |
495 | ?> |