2 /********************************************************************************
3 * include/NetNNTP.inc.php : NNTP subroutines
4 * -------------------------
6 * This file is part of the banana distribution
7 * Copyright: See COPYING files that comes with this distribution
8 ********************************************************************************/
11 * implements some basic functions for NNTP protocol
15 /** socket filehandle */
17 /** posting allowed */
19 /** last NNTP error code */
21 /** last NNTP error text */
25 * @param $_host STRING NNTP host
26 * @param $_timeout INTEGER socket timeout
27 * @param $_reader BOOLEAN sends a "MODE READER" at connection if true
30 function nntp($_url, $_timeout=120, $_reader=true
)
33 $url = parse_url($_url);
34 $this->ns
= fsockopen($url['host'], $url['port'], $errno, $errstr, $_timeout);
39 $result = $this->gline();
40 $this->posting
= (substr($result, 0, 3)=="200");
41 if ($_reader && ($result{0}=="2")) {
42 $this->pline("MODE READER\r\n");
43 $result = $this->gline();
44 $this->posting
= ($result{0}=="200");
46 if ($result{0}=="2" && $url['user'] && $url['user']!='anonymous') {
47 return $this->authinfo($url['user'], $url['pass']);
49 return ($result{0}=="2");
54 /** get a line from server
60 return rtrim(fgets($this->ns
, 1200));
63 /** puts a line on server
64 * @param STRING $_line line to put
67 function pline($_line)
69 return fputs($this->ns
, $_line, strlen($_line));
72 # strict NNTP Functions [RFC 977]
73 # see http://www.faqs.org/rfcs/rfc977.html
76 * @param $_user STRING login
77 * @param $_pass INTEGER password
78 * @return BOOLEAN true if authentication was successful
81 function authinfo($_user, $_pass)
83 $user = preg_replace("/(\r|\n)/", "", $_user);
84 $pass = preg_replace("/(\r|\n)/", "", $_pass);
85 $this->pline("AUTHINFO USER $user\r\n");
87 $this->pline("AUTHINFO PASS $pass\r\n");
88 $result=$this->gline();
89 if ($result{0}!="2") {
90 $this->lasterrorcode
= substr($result, 0, 3);
91 $this->lasterrortext
= substr($result, 4);
97 /** retrieves an article
98 * MSGID is a numeric ID a shown in article's headers. MSGNUM is a
99 * server-dependent ID (see X-Ref on many servers) and retriving
100 * an article by this way will change the current article pointer.
101 * If an error occur, false is returned.
102 * @param $_msgid STRING MSGID or MSGNUM of article
103 * @return ARRAY lines of the article
108 function article($_msgid="")
110 $msgid = preg_replace("/(\r|\n)/", "", $_msgid);
111 $this->pline("ARTICLE $msgid\r\n");
112 $result = $this->gline();
113 if ($result{0} != '2') {
114 $this->lasterrorcode
= substr($result, 0, 3);
115 $this->lasterrortext
= substr($result, 4);
118 $result = $this->gline();
119 while ($result != ".") {
121 $result = $this->gline();
127 * if an error occur, false is returned
128 * @param $_message STRING message to post
129 * @return STRING MSGID of article
132 function post($_message)
134 if (is_array($_message)) {
135 $message=join("\n", $_message);
139 $this->pline("POST \r\n");
140 $result=$this->gline();
141 if ($result{0} != '3') {
142 $this->lasterrorcode
= substr($result, 0, 3);
143 $this->lasterrortext
= substr($result, 4);
146 $this->pline($message."\r\n.\r\n");
147 $result = $this->gline();
148 if ($result{0} != '2') {
149 $this->lasterrorcode
= substr($result, 0, 3);
150 $this->lasterrortext
= substr($result, 4);
153 if ($result{0} == '2') {
154 if (preg_match("/(<[^@>]+@[^@>]+>)/", $result, $regs)) {
163 /** fetches the body of an article
164 * params are the same as article
165 * @param $_msgid STRING MSGID or MSGNUM of article
166 * @return ARRAY lines of the article
171 function body($_msgid="")
173 $msgid = preg_replace("/(\r|\n)/", "", $_msgid);
174 $this->pline("BODY $msgid\r\n");
175 $result = $this->gline();
176 if ($result{0} != '2') {
177 $this->lasterrorcode
= substr($result, 0, 3);
178 $this->lasterrortext
= substr($result, 4);
182 while (($result = $this->gline()) != ".") {
188 /** fetches the headers of an article
189 * params are the same as article
190 * @param $_msgid STRING MSGID or MSGNUM of article
191 * @return ARRAY lines of the article
196 function head($_msgid="")
198 $msgid = preg_replace("/(\r|\n)/", "", $_msgid);
199 $this->pline("HEAD $msgid\r\n");
200 $result = $this->gline();
201 if ($result{0}!="2") {
202 $this->lasterrorcode
= substr($result, 0, 3);
203 $this->lasterrortext
= substr($result, 4);
206 $result = $this->gline();
207 while ($result != ".") {
209 $result = $this->gline();
214 /** set current group
215 * @param $_group STRING
216 * @return ARRAY array : nb of articles in group, MSGNUM of first article, MSGNUM of last article, and group name
219 function group($_group)
221 $group = preg_replace("/(\r|\n)/", "", $_group);
222 $this->pline("GROUP $group\r\n");
223 $line = $this->gline();
225 $this->lasterrorcode
= substr($line, 0, 3);
226 $this->lasterrortext
= substr($line, 4);
229 if (preg_match("/^2\d{2} (\d+) (\d+) (\d+) ([^ ]+)/", $line, $regs)) {
230 return array($regs[1], $regs[2], $regs[3], $regs[4]);
235 /** set the article pointer to the previous article in current group
236 * @return STRING MSGID of article
242 $this->pline("LAST \r\n");
243 $line = $this->gline();
245 $this->lasterrorcode
= substr($result, 0, 3);
246 $this->lasterrortext
= substr($result, 4);
249 if (preg_match("/^2\d{2} \d+ <([^>]+)>/", $line, $regs)) {
250 return "<{$regs[1]}>";
255 /** set the article pointer to the next article in current group
256 * @return STRING MSGID of article
262 $this->pline("NEXT \r\n");
263 $line = $this->gline();
265 $this->lasterrorcode
= substr($result, 0, 3);
266 $this->lasterrortext
= substr($result, 4);
269 if (preg_match("/^2\d{2} \d+ <([^>]+)>/", $line, $regs)) {
270 return "<{$regs[1]}>";
275 /** set the current article pointer
276 * @param $_msgid STRING MSGID or MSGNUM of article
277 * @return BOOLEAN true if authentication was successful, error code otherwise
282 function nntpstat($_msgid)
284 $msgid = preg_replace("/(\r|\n)/", "", $_msgid);
285 $this->pline("STAT $msgid\r\n");
286 $line = $this->gline();
288 $this->lasterrorcode
= substr($result, 0, 3);
289 $this->lasterrortext
= substr($result, 4);
292 if (preg_match("/^2\d{2} \d+ <([^>]+)>/", $line, $regs)) {
293 return "<{$regs[1]}>";
298 /** returns true if posting is allowed
299 * @return BOOLEAN true if posting is allowed lines
304 return ($this->posting
);
307 /** retreive the group list
308 * @return ARRAY group name => (MSGNUM of first article, MSGNUM of last article, NNTP flags)
309 * @see newgroups, liste
311 function _grouplist()
315 if (substr($this->gline(), 0, 1)!="2") {
318 $result = $this->gline();
320 while ($result != ".") {
321 preg_match("/([^ ]+) (\d+) (\d+) (.)/", $result, $regs);
322 if (!isset($banana->grp_pattern
) ||
preg_match('@'.$banana->grp_pattern
.'@', $regs[1])) {
323 $array[$regs[1]] = array(intval($regs[2]), intval($regs[3]), intval($regs[4]));
325 $result = $this->gline();
330 /** gets information about all active newsgroups
331 * @return ARRAY group name => (MSGNUM of first article, MSGNUM of last article, NNTP flags)
337 $this->pline("LIST\r\n");
338 return $this->_grouplist();
341 /** get information about recent newsgroups
342 * same as list, but information are limited to newgroups created after $_since
343 * @param $_since INTEGER unix timestamp
344 * @param $_distributions STRING distributions
345 * @return ARRAY same format as liste
349 function newgroups($_since, $_distributions="")
351 #assume $_since is a unix timestamp
352 $distributions = preg_replace("/(\r|\n)/", "", $_distributions);
353 $this->pline("NEWGROUPS ".gmdate("ymd His", $_since)
354 ." GMT $distributions\r\n");
355 return $this->_grouplist();
358 /** gets a list of new articles
359 * @param $_since INTEGER unix timestamp
360 * @parma $_groups STRING pattern of intersting groups
361 * @return ARRAY MSGID of new articles
364 function newnews($_since, $_groups="*", $_distributions="")
366 $distributions = preg_replace("/(\r|\n)/", "", $_distributions);
367 $groups = preg_replace("/(\r|\n)/", "", $_groups);
369 #assume $since is a unix timestamp
370 $this->pline("NEWNEWS $_groups ".gmdate("ymd His", $_since)." GMT $distributions\r\n");
371 if (substr($this->gline(), 0, 1)!="2") {
374 while (($result = $this->gline()) != ".") {
380 /** Tell the remote server that I am not a user client, but probably another news server
381 * @return BOOLEAN true if sucessful
386 $this->pline("SLAVE \r\n");
387 return (substr($this->gline(), 0, 1)=="2");
390 /** implements IHAVE method
391 * @param $_msgid STRING MSGID of article
392 * @param $_message STRING article
396 function ihave($_msgid, $_message=false
)
398 $msgid = preg_replace("/(\r|\n)/", "", $_msgid);
399 if (is_array($message)) {
400 $message = join("\n", $_message);
402 $message = $_message;
404 $this->pline("IHAVE $msgid \r\n");
405 $result = $this->gline();
406 if ($message && ($result{0}=="3")) {
407 $this->pline("$message\r\n.\r\n");
408 $result = $this->gline();
410 return ($result{0}=="2");
413 /** closes connection to server
418 $this->pline("QUIT\r\n");
423 # NNTP Extensions [RFC 2980]
425 /** Returns the date on the remote server
426 * @return INTEGER timestamp
431 $this->pline("DATE \r\n");
432 $result = $this->gline();
433 if (preg_match("/^111 (\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/", $result, $r)) {
434 return gmmktime($r[4], $r[5], $r[6], $r[2], $r[3], $r[1]);
439 /** returns group descriptions
440 * @param $_pattern STRING pattern of intersting groups
441 * @return ARRAY group name => description
444 function xgtitle($_pattern="*")
446 $pattern = preg_replace("/[\r\n]/", "", $_pattern);
447 $this->pline("XGTITLE $pattern \r\n");
448 if (substr($this->gline(), 0, 1)!="2") return false
;
449 $result = $this->gline();
450 while ($result != ".") {
451 preg_match("/([^ \t]+)[ \t]+(.+)$/", $result, $regs);
452 $array[$regs[1]] = $regs[2];
453 $result = $this->gline();
458 /** obtain the header field $hdr for all the messages specified
459 * @param $_hdr STRING name of the header (eg: 'From')
460 * @param $_range STRING range of articles
461 * @return ARRAY MSGNUM => header value
464 function xhdr($_hdr, $_range="")
466 $hdr = preg_replace("/(\r|\n)/", "", $_hdr);
467 $range = preg_replace("/(\r|\n)/", "", $_range);
468 $this->pline("XHDR $hdr $range \r\n");
469 if (substr($this->gline(), 0, 1)!="2") {
474 while (($result = $this->gline()) != '.') {
475 preg_match("/([^ \t]+) (.*)$/", $result, $regs);
476 $array[$regs[1]] = $regs[2];
481 /** obtain the header field $_hdr matching $_pat for all the messages specified
482 * @param $_hdr STRING name of the header (eg: 'From')
483 * @param $_range STRING range of articles
484 * @param $_pat STRING pattern
485 * @return ARRAY MSGNUM => header value
488 function xpat($_hdr, $_range, $_pat)
490 $hdr = preg_replace("/(\r|\n)/", "", $_hdr);
491 $range = preg_replace("/(\r|\n)/", "", $_range);
492 $pat = preg_replace("/(\r|\n)/", "", $_pat);
493 $this->pline("XPAT $hdr $range $pat\r\n");
494 if (substr($this->gline(), 0, 1)!="2") {
497 $result = $this->gline();
498 while ($result != ".") {
499 preg_match("/([^ \t]+) (.*)$/", $result, $regs);
500 $array[$regs[1]] = $regs[2];
501 $result = $this->gline();