Mise à jour du Changelog
[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 return null;
37 }
38
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");
45 }
46 if ($result{0}=="2" && $url['user'] && $url['user']!='anonymous') {
47 return $this->authinfo($url['user'], $url['pass']);
48 }
49 return ($result{0}=="2");
50 }
51
52 # Socket functions
53
54 /** get a line from server
55 * @return STRING
56 */
57
58 function gline()
59 {
60 return rtrim(fgets($this->ns, 1200));
61 }
62
63 /** puts a line on server
64 * @param STRING $_line line to put
65 */
66
67 function pline($_line)
68 {
69 return fputs($this->ns, $_line, strlen($_line));
70 }
71
72 # strict NNTP Functions [RFC 977]
73 # see http://www.faqs.org/rfcs/rfc977.html
74
75 /** authentification
76 * @param $_user STRING login
77 * @param $_pass INTEGER password
78 * @return BOOLEAN true if authentication was successful
79 */
80
81 function authinfo($_user, $_pass)
82 {
83 $user = preg_replace("/(\r|\n)/", "", $_user);
84 $pass = preg_replace("/(\r|\n)/", "", $_pass);
85 $this->pline("AUTHINFO USER $user\r\n");
86 $this->gline();
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);
92 return false;
93 }
94 return true;
95 }
96
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
104 * @see body
105 * @see head
106 */
107
108 function article($_msgid="")
109 {
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);
116 return false;
117 }
118 $result = $this->gline();
119 while ($result != ".") {
120 $array[] = $result;
121 $result = $this->gline();
122 }
123 return $array;
124 }
125
126 /** post a message
127 * if an error occur, false is returned
128 * @param $_message STRING message to post
129 * @return STRING MSGID of article
130 */
131
132 function post($_message)
133 {
134 if (is_array($_message)) {
135 $message=join("\n", $_message);
136 } else {
137 $message=$_message;
138 }
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);
144 return false;
145 }
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);
151 return false;
152 }
153 if ($result{0} == '2') {
154 if (preg_match("/(<[^@>]+@[^@>]+>)/", $result, $regs)) {
155 return $regs[0];
156 } else {
157 return true;
158 }
159 }
160 return false;
161 }
162
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
167 * @see article
168 * @see head
169 */
170
171 function body($_msgid="")
172 {
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);
179 return false;
180 }
181 $array = Array();
182 while (($result = $this->gline()) != ".") {
183 $array[] = $result;
184 }
185 return $array;
186 }
187
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
192 * @see article
193 * @see body
194 */
195
196 function head($_msgid="")
197 {
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);
204 return false;
205 }
206 $result = $this->gline();
207 while ($result != ".") {
208 $array[] = $result;
209 $result = $this->gline();
210 }
211 return $array;
212 }
213
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
217 */
218
219 function group($_group)
220 {
221 $group = preg_replace("/(\r|\n)/", "", $_group);
222 $this->pline("GROUP $group\r\n");
223 $line = $this->gline();
224 if ($line{0}!="2") {
225 $this->lasterrorcode = substr($line, 0, 3);
226 $this->lasterrortext = substr($line, 4);
227 return false;
228 }
229 if (preg_match("/^2\d{2} (\d+) (\d+) (\d+) ([^ ]+)/", $line, $regs)) {
230 return array($regs[1], $regs[2], $regs[3], $regs[4]);
231 }
232 return false;
233 }
234
235 /** set the article pointer to the previous article in current group
236 * @return STRING MSGID of article
237 * @see next
238 */
239
240 function last()
241 {
242 $this->pline("LAST \r\n");
243 $line = $this->gline();
244 if ($line{0}!="2") {
245 $this->lasterrorcode = substr($result, 0, 3);
246 $this->lasterrortext = substr($result, 4);
247 return false;
248 }
249 if (preg_match("/^2\d{2} \d+ <([^>]+)>/", $line, $regs)) {
250 return "<{$regs[1]}>";
251 }
252 return false;
253 }
254
255 /** set the article pointer to the next article in current group
256 * @return STRING MSGID of article
257 * @see last
258 */
259
260 function next()
261 {
262 $this->pline("NEXT \r\n");
263 $line = $this->gline();
264 if ($line{0}!="2") {
265 $this->lasterrorcode = substr($result, 0, 3);
266 $this->lasterrortext = substr($result, 4);
267 return false;
268 }
269 if (preg_match("/^2\d{2} \d+ <([^>]+)>/", $line, $regs)) {
270 return "<{$regs[1]}>";
271 }
272 return false;
273 }
274
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
278 * @see article
279 * @see body
280 */
281
282 function nntpstat($_msgid)
283 {
284 $msgid = preg_replace("/(\r|\n)/", "", $_msgid);
285 $this->pline("STAT $msgid\r\n");
286 $line = $this->gline();
287 if ($line{0}!="2") {
288 $this->lasterrorcode = substr($result, 0, 3);
289 $this->lasterrortext = substr($result, 4);
290 return false;
291 }
292 if (preg_match("/^2\d{2} \d+ <([^>]+)>/", $line, $regs)) {
293 return "<{$regs[1]}>";
294 }
295 return false;
296 }
297
298 /** returns true if posting is allowed
299 * @return BOOLEAN true if posting is allowed lines
300 */
301
302 function postok()
303 {
304 return ($this->posting);
305 }
306
307 /** retreive the group list
308 * @return ARRAY group name => (MSGNUM of first article, MSGNUM of last article, NNTP flags)
309 * @see newgroups, liste
310 */
311 function _grouplist()
312 {
313 global $banana;
314
315 if (substr($this->gline(), 0, 1)!="2") {
316 return false;
317 }
318 $result = $this->gline();
319 $array = Array();
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]));
324 }
325 $result = $this->gline();
326 }
327 return $array;
328 }
329
330 /** gets information about all active newsgroups
331 * @return ARRAY group name => (MSGNUM of first article, MSGNUM of last article, NNTP flags)
332 * @see newgroups
333 */
334
335 function liste()
336 {
337 $this->pline("LIST\r\n");
338 return $this->_grouplist();
339 }
340
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
346 * @see liste
347 */
348
349 function newgroups($_since, $_distributions="")
350 {
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();
356 }
357
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
362 */
363
364 function newnews($_since, $_groups="*", $_distributions="")
365 {
366 $distributions = preg_replace("/(\r|\n)/", "", $_distributions);
367 $groups = preg_replace("/(\r|\n)/", "", $_groups);
368 $array = array();
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") {
372 return false;
373 }
374 while (($result = $this->gline()) != ".") {
375 $array[] = $result;
376 }
377 return $array;
378 }
379
380 /** Tell the remote server that I am not a user client, but probably another news server
381 * @return BOOLEAN true if sucessful
382 */
383
384 function slave()
385 {
386 $this->pline("SLAVE \r\n");
387 return (substr($this->gline(), 0, 1)=="2");
388 }
389
390 /** implements IHAVE method
391 * @param $_msgid STRING MSGID of article
392 * @param $_message STRING article
393 * @return BOOLEAN
394 */
395
396 function ihave($_msgid, $_message=false)
397 {
398 $msgid = preg_replace("/(\r|\n)/", "", $_msgid);
399 if (is_array($message)) {
400 $message = join("\n", $_message);
401 } else {
402 $message = $_message;
403 }
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();
409 }
410 return ($result{0}=="2");
411 }
412
413 /** closes connection to server
414 */
415
416 function quit()
417 {
418 $this->pline("QUIT\r\n");
419 $this->gline();
420 fclose($this->ns);
421 }
422
423 # NNTP Extensions [RFC 2980]
424
425 /** Returns the date on the remote server
426 * @return INTEGER timestamp
427 */
428
429 function date()
430 {
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]);
435 }
436 return false;
437 }
438
439 /** returns group descriptions
440 * @param $_pattern STRING pattern of intersting groups
441 * @return ARRAY group name => description
442 */
443
444 function xgtitle($_pattern="*")
445 {
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();
454 }
455 return $array;
456 }
457
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
462 */
463
464 function xhdr($_hdr, $_range="")
465 {
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") {
470 return false;
471 }
472
473 $array = array();
474 while (($result = $this->gline()) != '.') {
475 preg_match("/([^ \t]+) (.*)$/", $result, $regs);
476 $array[$regs[1]] = $regs[2];
477 }
478 return $array;
479 }
480
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
486 */
487
488 function xpat($_hdr, $_range, $_pat)
489 {
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") {
495 return false;
496 }
497 $result = $this->gline();
498 while ($result != ".") {
499 preg_match("/([^ \t]+) (.*)$/", $result, $regs);
500 $array[$regs[1]] = $regs[2];
501 $result = $this->gline();
502 }
503 return $array;
504 }
505 }
506
507 ?>