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