bugfix (spool)
[banana.git] / include / 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 /** 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;
24
25 # Socket functions
26
27 /** get a line from server
28 * @return STRING
29 */
30
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;
37 }
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));
50 }
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 && (substr($result,0,1)=="2")) {
79 $this->pline("MODE READER\r\n");
80 $result = $this->gline();
81 $this->posting = (substr($result,0,3)=="200");
82 }
83 return (substr($result,0,1)=="2");
84 }
85
86 # strict NNTP Functions [RFC 977]
87 # see http://www.faqs.org/rfcs/rfc977.html
88
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 (substr($result,0,1)!="2") {
103 $this->lasterrorcode = substr($result,0,3);
104 $this->lasterrortext = substr($result,4);
105 return false;
106 }
107 return true;
108 }
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 (substr($result,0,1)!="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;
136 }
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 (substr($result,0,1)!="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 (substr($result,0,1)!="2") {
160 $this->lasterrorcode = substr($result,0,3);
161 $this->lasterrortext = substr($result,4);
162 return false;
163 }
164 if (substr($result,0,1)=="2") {
165 if (preg_match("/(<[^@>]+@[^@>]+>)/",$result,$regs)) {
166 return $regs[0];
167 } else {
168 return true;
169 }
170 }
171 return false;
172 }
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 (substr($result,0,1)!="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;
197 }
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 (substr($result,0,1)!="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;
222 }
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 (substr($line,0,1)!="2") {
234 $this->lasterrorcode = substr($result,0,3);
235 $this->lasterrortext = substr($result,4);
236 return false;
237 }
238 if (preg_match("/^2\d{2} (\d+) (\d+) (\d+) ([^ ]+)/",
239 $line,$regs)) {
240 return array($regs[1],$regs[2],$regs[3],$regs[4]);
241 }
242 return $false;
243 }
244
245 /** set the article pointer to the previous article in current group
246 * @return STRING MSGID of article
247 * @see next
248 */
249
250 function last() {
251 $this->pline("LAST \r\n");
252 $line = $this->gline();
253 if (substr($line,0,1)!="2") {
254 $this->lasterrorcode = substr($result,0,3);
255 $this->lasterrortext = substr($result,4);
256 return false;
257 }
258 if (preg_match("/^2\d{2} \d+ <([^>]+)>/",$line,$regs)) {
259 return "<{$regs[1]}>";
260 }
261 return $false;
262 }
263
264 /** set the article pointer to the next article in current group
265 * @return STRING MSGID of article
266 * @see last
267 */
268
269 function next() {
270 $this->pline("NEXT \r\n");
271 $line = $this->gline();
272 if (substr($line,0,1)!="2") {
273 $this->lasterrorcode = substr($result,0,3);
274 $this->lasterrortext = substr($result,4);
275 return false;
276 }
277 if (preg_match("/^2\d{2} \d+ <([^>]+)>/",$line,$regs)) {
278 return "<{$regs[1]}>";
279 }
280 return $false;
281 }
282
283 /** set the current article pointer
284 * @param $_msgid STRING MSGID or MSGNUM of article
285 * @return BOOLEAN true if authentication was successful, error code otherwise
286 * @see article
287 * @see body
288 */
289
290 function nntpstat($_msgid) {
291 $msgid = preg_replace("/(\r|\n)/","",$_msgid);
292 $this->pline("STAT $msgid\r\n");
293 $line = $this->gline();
294 if (substr($line,0,1)!="2") {
295 $this->lasterrorcode = substr($result,0,3);
296 $this->lasterrortext = substr($result,4);
297 return false;
298 }
299 if (preg_match("/^2\d{2} \d+ <([^>]+)>/",$line,$regs)) {
300 return "<{$regs[1]}>";
301 }
302 return false;
303 }
304
305 /** returns true if posting is allowed
306 * @return BOOLEAN true if posting is allowed lines
307 */
308
309 function postok() {
310 return ($this->posting);
311 }
312
313 /** gets information about all active newsgroups
314 * @return ARRAY group name => (MSGNUM of first article, MSGNUM of last article, NNTP flags)
315 * @see newgroups
316 */
317
318 function liste() {
319 $this->pline("LIST\r\n");
320 if (substr($this->gline(),0,1)!="2") return false;
321 $result = $this->gline();
322 while ($result != ".") {
323 preg_match("/([^ ]+) (\d+) (\d+) (.)/",$result,$regs);
324 $array[$regs[1]]=array(intval($regs[2]),intval($regs[3]),
325 intval($regs[4]));
326 $result = $this->gline();
327 }
328 return $array;
329 }
330
331 /** get information about recent newsgroups
332 * same as list, but information are limited to newgroups created after $_since
333 * @param $_since INTEGER unix timestamp
334 * @param $_distributions STRING distributions
335 * @return ARRAY same format as liste
336 * @see liste
337 */
338
339 function newgroups($_since,$_distributions="") {
340 #assume $_since is a unix timestamp
341 $distributions = preg_replace("/(\r|\n)/","",$_distributions);
342 $this->pline("NEWGROUPS ".gmdate("ymd His",$_since)
343 ." GMT $distributions\r\n");
344 if (substr($this->gline(),0,1)!="2") return false;
345 $result = $this->gline();
346 while ($result != ".") {
347 preg_match("/([^ ]+) (\d+) (\d+) ./",$result,$regs);
348 $array[$regs[1]]=array(intval($regs[2]),intval($regs[3]),
349 intval($regs[4]));
350 $result = $this->gline();
351 }
352 return $array;
353 }
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)
367 ." GMT $distributions\r\n");
368 if (substr($this->gline(),0,1)!="2") return false;
369 $result = $this->gline();
370 while ($result != ".") {
371 $array[] = $result;
372 $result = $this->gline();
373 }
374 return $array;
375 }
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
381 function slave() {
382 $this->pline("SLAVE \r\n");
383 return (substr($this->gline(),0,1)=="2");
384 }
385
386 /** implements IHAVE method
387 * @param $_msgid STRING MSGID of article
388 * @param $_message STRING article
389 * @return BOOLEAN
390 */
391
392 function ihave($_msgid,$_message=false) {
393 $msgid = preg_replace("/(\r|\n)/","",$_msgid);
394 if (is_array($message))
395 $message=join("\n",$_message);
396 else
397 $message=$_message;
398 $this->pline("IHAVE $msgid \r\n");
399 $result=$this->gline();
400 if ($message && (substr($result,0,1)=="3")) {
401 $this->pline("$message\r\n.\r\n");
402 $result=$this->gline();
403 }
404 return (substr($result,0,1)=="2");
405 }
406
407 /** closes connection to server
408 */
409
410 function quit() {
411 $this->pline("QUIT\r\n");
412 $this->gline();
413 fclose($this->ns);
414 }
415
416 # NNTP Extensions [RFC 2980]
417
418 /** Returns the date on the remote server
419 * @return INTEGER timestamp
420 */
421
422 function date() {
423 $this->pline("DATE \r\n");
424 $result = $this->gline();
425 if (preg_match("/^111 (\d{4})(\d{2})(\d{2})"
426 ."(\d{2})(\d{2})(\d{2})$/",$result,$r)) {
427 return gmmktime($r[4],$r[5],$r[6],$r[2],$r[3],$r[1]);
428 }
429 return false;
430 }
431
432 /** returns group descriptions
433 * @param $_pattern STRING pattern of intersting groups
434 * @return ARRAY group name => description
435 */
436
437 function xgtitle($_pattern="*") {
438 $pattern = preg_replace("/(\r|\n)/","",$_pattern);
439 $this->pline("XGTITLE $pattern \r\n");
440 if (substr($this->gline(),0,1)!="2") return false;
441 $result = $this->gline();
442 while ($result != ".") {
443 preg_match("/([^ \t]+)( |\t)+(.+)$/",$result,$regs);
444 $array[$regs[1]]=$regs[3];
445 $result = $this->gline();
446 }
447 return $array;
448 }
449
450 /** obtain the header field $hdr for all the messages specified
451 * @param $_hdr STRING name of the header (eg: 'From')
452 * @param $_range STRING range of articles
453 * @return ARRAY MSGNUM => header value
454 */
455
456 function xhdr($_hdr,$_range="") {
457 $hdr = preg_replace("/(\r|\n)/","",$_hdr);
458 $range = preg_replace("/(\r|\n)/","",$_range);
459 $this->pline("XHDR $hdr $range \r\n");
460 if (substr($this->gline(),0,1)!="2") return false;
461 $result = $this->gline();
462 while ($result != ".") {
463 preg_match("/([^ \t]+) (.*)$/",$result,$regs);
464 $array[$regs[1]]=$regs[2];
465 $result = $this->gline();
466 }
467 return $array;
468 }
469
470 }
471
472 ?>