Possibilité de filtrer la liste des newsgroups à afficher à partir d'une regexp
[banana.git] / banana / NetNNTP.inc.php
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
14 {
15 /** socket filehandle */
16 var $ns;
17 /** posting allowed */
18 var $posting;
19 /** last NNTP error code */
20 var $lasterrorcode;
21 /** last NNTP error text */
22 var $lasterrortext;
23
24 /** constructor
25 * @param $_host STRING NNTP host
26 * @param $_timeout INTEGER socket timeout
27 * @param $_reader BOOLEAN sends a "MODE READER" at connection if true
28 */
29
30 function nntp($_url, $_timeout=120, $_reader=true)
31 {
32 $url['port'] = 119;
33 $url = parse_url($_url);
34 $this->ns = fsockopen($url['host'], $url['port'], $errno, $errstr, $_timeout);
35 if (!$this->ns) {
36 $this = false;
37 return false;
38 }
39
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 }
47 if ($result{0}=="2" && $url['user'] && $url['user']!='anonymous') {
48 return $this->authinfo($url['user'], $url['pass']);
49 }
50 return ($result{0}=="2");
51 }
52
53 # Socket functions
54
55 /** get a line from server
56 * @return STRING
57 */
58
59 function gline()
60 {
61 return rtrim(fgets($this->ns, 1200));
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
73 # strict NNTP Functions [RFC 977]
74 # see http://www.faqs.org/rfcs/rfc977.html
75
76 /** authentification
77 * @param $_user STRING login
78 * @param $_pass INTEGER password
79 * @return BOOLEAN true if authentication was successful
80 */
81
82 function authinfo($_user, $_pass)
83 {
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 }
95 return true;
96 }
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
109 function article($_msgid="")
110 {
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;
125 }
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
133 function post($_message)
134 {
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;
162 }
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
172 function body($_msgid="")
173 {
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 $array = Array();
183 while (($result = $this->gline()) != ".") {
184 $array[] = $result;
185 }
186 return $array;
187 }
188
189 /** fetches the headers of an article
190 * params are the same as article
191 * @param $_msgid STRING MSGID or MSGNUM of article
192 * @return ARRAY lines of the article
193 * @see article
194 * @see body
195 */
196
197 function head($_msgid="")
198 {
199 $msgid = preg_replace("/(\r|\n)/", "", $_msgid);
200 $this->pline("HEAD $msgid\r\n");
201 $result = $this->gline();
202 if ($result{0}!="2") {
203 $this->lasterrorcode = substr($result, 0, 3);
204 $this->lasterrortext = substr($result, 4);
205 return false;
206 }
207 $result = $this->gline();
208 while ($result != ".") {
209 $array[] = $result;
210 $result = $this->gline();
211 }
212 return $array;
213 }
214
215 /** set current group
216 * @param $_group STRING
217 * @return ARRAY array : nb of articles in group, MSGNUM of first article, MSGNUM of last article, and group name
218 */
219
220 function group($_group)
221 {
222 $group = preg_replace("/(\r|\n)/", "", $_group);
223 $this->pline("GROUP $group\r\n");
224 $line = $this->gline();
225 if ($line{0}!="2") {
226 $this->lasterrorcode = substr($line, 0, 3);
227 $this->lasterrortext = substr($line, 4);
228 return false;
229 }
230 if (preg_match("/^2\d{2} (\d+) (\d+) (\d+) ([^ ]+)/", $line, $regs)) {
231 return array($regs[1], $regs[2], $regs[3], $regs[4]);
232 }
233 return false;
234 }
235
236 /** set the article pointer to the previous article in current group
237 * @return STRING MSGID of article
238 * @see next
239 */
240
241 function last()
242 {
243 $this->pline("LAST \r\n");
244 $line = $this->gline();
245 if ($line{0}!="2") {
246 $this->lasterrorcode = substr($result, 0, 3);
247 $this->lasterrortext = substr($result, 4);
248 return false;
249 }
250 if (preg_match("/^2\d{2} \d+ <([^>]+)>/", $line, $regs)) {
251 return "<{$regs[1]}>";
252 }
253 return false;
254 }
255
256 /** set the article pointer to the next article in current group
257 * @return STRING MSGID of article
258 * @see last
259 */
260
261 function next()
262 {
263 $this->pline("NEXT \r\n");
264 $line = $this->gline();
265 if ($line{0}!="2") {
266 $this->lasterrorcode = substr($result, 0, 3);
267 $this->lasterrortext = substr($result, 4);
268 return false;
269 }
270 if (preg_match("/^2\d{2} \d+ <([^>]+)>/", $line, $regs)) {
271 return "<{$regs[1]}>";
272 }
273 return false;
274 }
275
276 /** set the current article pointer
277 * @param $_msgid STRING MSGID or MSGNUM of article
278 * @return BOOLEAN true if authentication was successful, error code otherwise
279 * @see article
280 * @see body
281 */
282
283 function nntpstat($_msgid)
284 {
285 $msgid = preg_replace("/(\r|\n)/", "", $_msgid);
286 $this->pline("STAT $msgid\r\n");
287 $line = $this->gline();
288 if ($line{0}!="2") {
289 $this->lasterrorcode = substr($result, 0, 3);
290 $this->lasterrortext = substr($result, 4);
291 return false;
292 }
293 if (preg_match("/^2\d{2} \d+ <([^>]+)>/", $line, $regs)) {
294 return "<{$regs[1]}>";
295 }
296 return false;
297 }
298
299 /** returns true if posting is allowed
300 * @return BOOLEAN true if posting is allowed lines
301 */
302
303 function postok()
304 {
305 return ($this->posting);
306 }
307
308 /** retreive the group list
309 * @return ARRAY group name => (MSGNUM of first article, MSGNUM of last article, NNTP flags)
310 * @see newgroups, liste
311 */
312 function _grouplist()
313 {
314 global $banana;
315
316 if (substr($this->gline(), 0, 1)!="2") {
317 return false;
318 }
319 $result = $this->gline();
320 $array = Array();
321 while ($result != ".") {
322 preg_match("/([^ ]+) (\d+) (\d+) (.)/", $result, $regs);
323 if (!isset($banana->grp_pattern) || preg_match('@'.$banana->grp_pattern.'@', $regs[1])) {
324 $array[$regs[1]] = array(intval($regs[2]), intval($regs[3]), intval($regs[4]));
325 }
326 $result = $this->gline();
327 }
328 return $array;
329 }
330
331 /** gets information about all active newsgroups
332 * @return ARRAY group name => (MSGNUM of first article, MSGNUM of last article, NNTP flags)
333 * @see newgroups
334 */
335
336 function liste()
337 {
338 $this->pline("LIST\r\n");
339 return $this->_grouplist();
340 }
341
342 /** get information about recent newsgroups
343 * same as list, but information are limited to newgroups created after $_since
344 * @param $_since INTEGER unix timestamp
345 * @param $_distributions STRING distributions
346 * @return ARRAY same format as liste
347 * @see liste
348 */
349
350 function newgroups($_since, $_distributions="")
351 {
352 #assume $_since is a unix timestamp
353 $distributions = preg_replace("/(\r|\n)/", "", $_distributions);
354 $this->pline("NEWGROUPS ".gmdate("ymd His", $_since)
355 ." GMT $distributions\r\n");
356 return $this->_grouplist();
357 }
358
359 /** gets a list of new articles
360 * @param $_since INTEGER unix timestamp
361 * @parma $_groups STRING pattern of intersting groups
362 * @return ARRAY MSGID of new articles
363 */
364
365 function newnews($_since, $_groups="*", $_distributions="")
366 {
367 $distributions = preg_replace("/(\r|\n)/", "", $_distributions);
368 $groups = preg_replace("/(\r|\n)/", "", $_groups);
369 $array = array();
370 #assume $since is a unix timestamp
371 $this->pline("NEWNEWS $_groups ".gmdate("ymd His", $_since)." GMT $distributions\r\n");
372 if (substr($this->gline(), 0, 1)!="2") {
373 return false;
374 }
375 while (($result = $this->gline()) != ".") {
376 $array[] = $result;
377 }
378 return $array;
379 }
380
381 /** Tell the remote server that I am not a user client, but probably another news server
382 * @return BOOLEAN true if sucessful
383 */
384
385 function slave()
386 {
387 $this->pline("SLAVE \r\n");
388 return (substr($this->gline(), 0, 1)=="2");
389 }
390
391 /** implements IHAVE method
392 * @param $_msgid STRING MSGID of article
393 * @param $_message STRING article
394 * @return BOOLEAN
395 */
396
397 function ihave($_msgid, $_message=false)
398 {
399 $msgid = preg_replace("/(\r|\n)/", "", $_msgid);
400 if (is_array($message)) {
401 $message = join("\n", $_message);
402 } else {
403 $message = $_message;
404 }
405 $this->pline("IHAVE $msgid \r\n");
406 $result = $this->gline();
407 if ($message && ($result{0}=="3")) {
408 $this->pline("$message\r\n.\r\n");
409 $result = $this->gline();
410 }
411 return ($result{0}=="2");
412 }
413
414 /** closes connection to server
415 */
416
417 function quit()
418 {
419 $this->pline("QUIT\r\n");
420 $this->gline();
421 fclose($this->ns);
422 }
423
424 # NNTP Extensions [RFC 2980]
425
426 /** Returns the date on the remote server
427 * @return INTEGER timestamp
428 */
429
430 function date()
431 {
432 $this->pline("DATE \r\n");
433 $result = $this->gline();
434 if (preg_match("/^111 (\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})$/", $result, $r)) {
435 return gmmktime($r[4], $r[5], $r[6], $r[2], $r[3], $r[1]);
436 }
437 return false;
438 }
439
440 /** returns group descriptions
441 * @param $_pattern STRING pattern of intersting groups
442 * @return ARRAY group name => description
443 */
444
445 function xgtitle($_pattern="*")
446 {
447 $pattern = preg_replace("/[\r\n]/", "", $_pattern);
448 $this->pline("XGTITLE $pattern \r\n");
449 if (substr($this->gline(), 0, 1)!="2") return false;
450 $result = $this->gline();
451 while ($result != ".") {
452 preg_match("/([^ \t]+)[ \t]+(.+)$/", $result, $regs);
453 $array[$regs[1]] = $regs[2];
454 $result = $this->gline();
455 }
456 return $array;
457 }
458
459 /** obtain the header field $hdr for all the messages specified
460 * @param $_hdr STRING name of the header (eg: 'From')
461 * @param $_range STRING range of articles
462 * @return ARRAY MSGNUM => header value
463 */
464
465 function xhdr($_hdr, $_range="")
466 {
467 $hdr = preg_replace("/(\r|\n)/", "", $_hdr);
468 $range = preg_replace("/(\r|\n)/", "", $_range);
469 $this->pline("XHDR $hdr $range \r\n");
470 if (substr($this->gline(), 0, 1)!="2") {
471 return false;
472 }
473
474 $array = array();
475 while (($result = $this->gline()) != '.') {
476 preg_match("/([^ \t]+) (.*)$/", $result, $regs);
477 $array[$regs[1]] = $regs[2];
478 }
479 return $array;
480 }
481
482 /** obtain the header field $_hdr matching $_pat for all the messages specified
483 * @param $_hdr STRING name of the header (eg: 'From')
484 * @param $_range STRING range of articles
485 * @param $_pat STRING pattern
486 * @return ARRAY MSGNUM => header value
487 */
488
489 function xpat($_hdr, $_range, $_pat)
490 {
491 $hdr = preg_replace("/(\r|\n)/", "", $_hdr);
492 $range = preg_replace("/(\r|\n)/", "", $_range);
493 $pat = preg_replace("/(\r|\n)/", "", $_pat);
494 $this->pline("XPAT $hdr $range $pat\r\n");
495 if (substr($this->gline(), 0, 1)!="2") {
496 return false;
497 }
498 $result = $this->gline();
499 while ($result != ".") {
500 preg_match("/([^ \t]+) (.*)$/", $result, $regs);
501 $array[$regs[1]] = $regs[2];
502 $result = $this->gline();
503 }
504 return $array;
505 }
506 }
507
508 ?>