some improvements
[banana.git] / include / NetNNTP.inc.php
CommitLineData
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 */
13class 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?>