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