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() { |
32 | $line = preg_replace("/(\r|\n)/", "", fgets($this->ns, 1200)); |
33 | if ($this->debug) { |
34 | print "NNTP >>>> $line \n"; |
35 | } |
36 | return $line; |
bb3b9f8e |
37 | } |
e785d91c |
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)); |
bb3b9f8e |
50 | } |
e785d91c |
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 | } |
67 | $this->ns = fsockopen($host, $port, $errno, $errstr, $_timeout); |
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 && ($result{0}=="2")) { |
79 | $this->pline("MODE READER\r\n"); |
80 | $result = $this->gline(); |
81 | $this->posting = ($result{0}=="200"); |
82 | } |
83 | return ($result{0}=="2"); |
bb3b9f8e |
84 | } |
bb3b9f8e |
85 | |
86 | # strict NNTP Functions [RFC 977] |
87 | # see http://www.faqs.org/rfcs/rfc977.html |
88 | |
e785d91c |
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 ($result{0}!="2") { |
103 | $this->lasterrorcode = substr($result, 0, 3); |
104 | $this->lasterrortext = substr($result, 4); |
105 | return false; |
106 | } |
bb3b9f8e |
107 | return true; |
bb3b9f8e |
108 | } |
e785d91c |
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 ($result{0} != '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; |
bb3b9f8e |
136 | } |
e785d91c |
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 ($result{0} != '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 ($result{0} != '2') { |
160 | $this->lasterrorcode = substr($result, 0, 3); |
161 | $this->lasterrortext = substr($result, 4); |
162 | return false; |
163 | } |
164 | if ($result{0} == '2') { |
165 | if (preg_match("/(<[^@>]+@[^@>]+>)/", $result, $regs)) { |
166 | return $regs[0]; |
167 | } else { |
168 | return true; |
169 | } |
170 | } |
171 | return false; |
bb3b9f8e |
172 | } |
e785d91c |
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 ($result{0} != '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; |
bb3b9f8e |
197 | } |
e785d91c |
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 ($result{0}!="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; |
bb3b9f8e |
222 | } |
e785d91c |
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 ($line{0}!="2") { |
234 | $this->lasterrorcode = substr($line, 0, 3); |
235 | $this->lasterrortext = substr($line, 4); |
236 | return false; |
237 | } |
238 | if (preg_match("/^2\d{2} (\d+) (\d+) (\d+) ([^ ]+)/", $line, $regs)) { |
239 | return array($regs[1], $regs[2], $regs[3], $regs[4]); |
240 | } |
241 | return $false; |
bb3b9f8e |
242 | } |
e785d91c |
243 | |
244 | /** set the article pointer to the previous article in current group |
245 | * @return STRING MSGID of article |
246 | * @see next |
247 | */ |
248 | |
249 | function last() { |
250 | $this->pline("LAST \r\n"); |
251 | $line = $this->gline(); |
252 | if ($line{0}!="2") { |
253 | $this->lasterrorcode = substr($result, 0, 3); |
254 | $this->lasterrortext = substr($result, 4); |
255 | return false; |
256 | } |
257 | if (preg_match("/^2\d{2} \d+ <([^>]+)>/", $line, $regs)) { |
258 | return "<{$regs[1]}>"; |
259 | } |
260 | return $false; |
bb3b9f8e |
261 | } |
e785d91c |
262 | |
263 | /** set the article pointer to the next article in current group |
264 | * @return STRING MSGID of article |
265 | * @see last |
266 | */ |
267 | |
268 | function next() { |
269 | $this->pline("NEXT \r\n"); |
270 | $line = $this->gline(); |
271 | if ($line{0}!="2") { |
272 | $this->lasterrorcode = substr($result, 0, 3); |
273 | $this->lasterrortext = substr($result, 4); |
274 | return false; |
275 | } |
276 | if (preg_match("/^2\d{2} \d+ <([^>]+)>/", $line, $regs)) { |
277 | return "<{$regs[1]}>"; |
278 | } |
279 | return $false; |
bb3b9f8e |
280 | } |
e785d91c |
281 | |
282 | /** set the current article pointer |
283 | * @param $_msgid STRING MSGID or MSGNUM of article |
284 | * @return BOOLEAN true if authentication was successful, error code otherwise |
285 | * @see article |
286 | * @see body |
287 | */ |
288 | |
289 | function nntpstat($_msgid) { |
290 | $msgid = preg_replace("/(\r|\n)/", "", $_msgid); |
291 | $this->pline("STAT $msgid\r\n"); |
292 | $line = $this->gline(); |
293 | if ($line{0}!="2") { |
294 | $this->lasterrorcode = substr($result, 0, 3); |
295 | $this->lasterrortext = substr($result, 4); |
296 | return false; |
297 | } |
298 | if (preg_match("/^2\d{2} \d+ <([^>]+)>/", $line, $regs)) { |
299 | return "<{$regs[1]}>"; |
300 | } |
301 | return false; |
bb3b9f8e |
302 | } |
e785d91c |
303 | |
304 | /** returns true if posting is allowed |
305 | * @return BOOLEAN true if posting is allowed lines |
306 | */ |
307 | |
308 | function postok() { |
309 | return ($this->posting); |
bb3b9f8e |
310 | } |
e785d91c |
311 | |
312 | /** gets information about all active newsgroups |
313 | * @return ARRAY group name => (MSGNUM of first article, MSGNUM of last article, NNTP flags) |
314 | * @see newgroups |
315 | */ |
316 | |
317 | function liste() { |
318 | $this->pline("LIST\r\n"); |
319 | if (substr($this->gline(), 0, 1)!="2") return false; |
320 | $result = $this->gline(); |
321 | while ($result != ".") { |
322 | preg_match("/([^ ]+) (\d+) (\d+) (.)/", $result, $regs); |
323 | $array[$regs[1]] = array(intval($regs[2]), intval($regs[3]), intval($regs[4])); |
324 | $result = $this->gline(); |
325 | } |
326 | return $array; |
bb3b9f8e |
327 | } |
e785d91c |
328 | |
329 | /** get information about recent newsgroups |
330 | * same as list, but information are limited to newgroups created after $_since |
331 | * @param $_since INTEGER unix timestamp |
332 | * @param $_distributions STRING distributions |
333 | * @return ARRAY same format as liste |
334 | * @see liste |
335 | */ |
336 | |
337 | function newgroups($_since, $_distributions="") { |
338 | #assume $_since is a unix timestamp |
339 | $distributions = preg_replace("/(\r|\n)/", "", $_distributions); |
340 | $this->pline("NEWGROUPS ".gmdate("ymd His", $_since) |
341 | ." GMT $distributions\r\n"); |
342 | if (substr($this->gline(), 0, 1)!="2") { |
343 | return false; |
344 | } |
345 | $result = $this->gline(); |
346 | $array = array(); |
347 | while ($result != ".") { |
348 | preg_match("/([^ ]+) (\d+) (\d+) (.)/", $result, $regs); |
349 | $array[$regs[1]] = array(intval($regs[2]), intval($regs[3]), intval($regs[4])); |
350 | $result = $this->gline(); |
351 | } |
352 | return $array; |
bb3b9f8e |
353 | } |
e785d91c |
354 | |
355 | /** gets a list of new articles |
356 | * @param $_since INTEGER unix timestamp |
357 | * @parma $_groups STRING pattern of intersting groups |
358 | * @return ARRAY MSGID of new articles |
359 | */ |
360 | |
361 | function newnews($_since, $_groups="*", $_distributions="") { |
362 | $distributions = preg_replace("/(\r|\n)/", "", $_distributions); |
363 | $groups = preg_replace("/(\r|\n)/", "", $_groups); |
364 | $array = array(); |
365 | #assume $since is a unix timestamp |
366 | $this->pline("NEWNEWS $_groups ".gmdate("ymd His", $_since)." GMT $distributions\r\n"); |
367 | if (substr($this->gline(), 0, 1)!="2") { |
368 | return false; |
369 | } |
370 | $result = $this->gline(); |
371 | while ($result != ".") { |
372 | $array[] = $result; |
373 | $result = $this->gline(); |
374 | } |
375 | return $array; |
bb3b9f8e |
376 | } |
e785d91c |
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"); |
bb3b9f8e |
385 | } |
e785d91c |
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 | } |
400 | $this->pline("IHAVE $msgid \r\n"); |
401 | $result = $this->gline(); |
402 | if ($message && ($result{0}=="3")) { |
403 | $this->pline("$message\r\n.\r\n"); |
404 | $result = $this->gline(); |
405 | } |
406 | return ($result{0}=="2"); |
bb3b9f8e |
407 | } |
e785d91c |
408 | |
409 | /** closes connection to server |
410 | */ |
411 | |
412 | function quit() { |
413 | $this->pline("QUIT\r\n"); |
414 | $this->gline(); |
415 | fclose($this->ns); |
bb3b9f8e |
416 | } |
bb3b9f8e |
417 | |
418 | # NNTP Extensions [RFC 2980] |
419 | |
e785d91c |
420 | /** Returns the date on the remote server |
421 | * @return INTEGER timestamp |
422 | */ |
423 | |
424 | function date() { |
425 | $this->pline("DATE \r\n"); |
426 | $result = $this->gline(); |
427 | if (preg_match("/^111 (\d{4})(\d{2})(\d{2})(\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; |
bb3b9f8e |
431 | } |
e785d91c |
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; |
bb3b9f8e |
449 | } |
e785d91c |
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") { |
462 | return false; |
463 | } |
464 | $result = $this->gline(); |
465 | $array = array(); |
466 | while ($result != ".") { |
467 | preg_match("/([^ \t]+) (.*)$/", $result, $regs); |
468 | $array[$regs[1]] = $regs[2]; |
469 | $result = $this->gline(); |
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 | ?> |