Wordpress 2.9-scripts
[autoinstalls/wordpress.git] / wp-includes / class-pop3.php
1 <?php
2 /**
3  * mail_fetch/setup.php
4  *
5  * @package SquirrelMail
6  *
7  * @copyright (c) 1999-2006 The SquirrelMail Project Team
8  *
9  * @copyright (c) 1999 CDI (cdi@thewebmasters.net) All Rights Reserved
10  * Modified by Philippe Mingo 2001 mingo@rotedic.com
11  * An RFC 1939 compliant wrapper class for the POP3 protocol.
12  *
13  * Licensed under the GNU GPL. For full terms see the file COPYING.
14  *
15  * pop3 class
16  *
17  * $Id: class-pop3.php 9503 2008-11-03 23:25:11Z ryan $
18  */
19
20 class POP3 {
21     var $ERROR      = '';       //  Error string.
22
23     var $TIMEOUT    = 60;       //  Default timeout before giving up on a
24                                 //  network operation.
25
26     var $COUNT      = -1;       //  Mailbox msg count
27
28     var $BUFFER     = 512;      //  Socket buffer for socket fgets() calls.
29                                 //  Per RFC 1939 the returned line a POP3
30                                 //  server can send is 512 bytes.
31
32     var $FP         = '';       //  The connection to the server's
33                                 //  file descriptor
34
35     var $MAILSERVER = '';       // Set this to hard code the server name
36
37     var $DEBUG      = FALSE;    // set to true to echo pop3
38                                 // commands and responses to error_log
39                                 // this WILL log passwords!
40
41     var $BANNER     = '';       //  Holds the banner returned by the
42                                 //  pop server - used for apop()
43
44     var $ALLOWAPOP  = FALSE;    //  Allow or disallow apop()
45                                 //  This must be set to true
46                                 //  manually
47
48     function POP3 ( $server = '', $timeout = '' ) {
49         settype($this->BUFFER,"integer");
50         if( !empty($server) ) {
51             // Do not allow programs to alter MAILSERVER
52             // if it is already specified. They can get around
53             // this if they -really- want to, so don't count on it.
54             if(empty($this->MAILSERVER))
55                 $this->MAILSERVER = $server;
56         }
57         if(!empty($timeout)) {
58             settype($timeout,"integer");
59             $this->TIMEOUT = $timeout;
60             if (!ini_get('safe_mode'))
61                 set_time_limit($timeout);
62         }
63         return true;
64     }
65
66     function update_timer () {
67         if (!ini_get('safe_mode'))
68             set_time_limit($this->TIMEOUT);
69         return true;
70     }
71
72     function connect ($server, $port = 110)  {
73         //  Opens a socket to the specified server. Unless overridden,
74         //  port defaults to 110. Returns true on success, false on fail
75
76         // If MAILSERVER is set, override $server with it's value
77
78         if (!isset($port) || !$port) {$port = 110;}
79         if(!empty($this->MAILSERVER))
80             $server = $this->MAILSERVER;
81
82         if(empty($server)){
83             $this->ERROR = "POP3 connect: " . _("No server specified");
84             unset($this->FP);
85             return false;
86         }
87
88         $fp = @fsockopen("$server", $port, $errno, $errstr);
89
90         if(!$fp) {
91             $this->ERROR = "POP3 connect: " . _("Error ") . "[$errno] [$errstr]";
92             unset($this->FP);
93             return false;
94         }
95
96         socket_set_blocking($fp,-1);
97         $this->update_timer();
98         $reply = fgets($fp,$this->BUFFER);
99         $reply = $this->strip_clf($reply);
100         if($this->DEBUG)
101             error_log("POP3 SEND [connect: $server] GOT [$reply]",0);
102         if(!$this->is_ok($reply)) {
103             $this->ERROR = "POP3 connect: " . _("Error ") . "[$reply]";
104             unset($this->FP);
105             return false;
106         }
107         $this->FP = $fp;
108         $this->BANNER = $this->parse_banner($reply);
109         return true;
110     }
111
112     function user ($user = "") {
113         // Sends the USER command, returns true or false
114
115         if( empty($user) ) {
116             $this->ERROR = "POP3 user: " . _("no login ID submitted");
117             return false;
118         } elseif(!isset($this->FP)) {
119             $this->ERROR = "POP3 user: " . _("connection not established");
120             return false;
121         } else {
122             $reply = $this->send_cmd("USER $user");
123             if(!$this->is_ok($reply)) {
124                 $this->ERROR = "POP3 user: " . _("Error ") . "[$reply]";
125                 return false;
126             } else
127                 return true;
128         }
129     }
130
131     function pass ($pass = "")     {
132         // Sends the PASS command, returns # of msgs in mailbox,
133         // returns false (undef) on Auth failure
134
135         if(empty($pass)) {
136             $this->ERROR = "POP3 pass: " . _("No password submitted");
137             return false;
138         } elseif(!isset($this->FP)) {
139             $this->ERROR = "POP3 pass: " . _("connection not established");
140             return false;
141         } else {
142             $reply = $this->send_cmd("PASS $pass");
143             if(!$this->is_ok($reply)) {
144                 $this->ERROR = "POP3 pass: " . _("Authentication failed") . " [$reply]";
145                 $this->quit();
146                 return false;
147             } else {
148                 //  Auth successful.
149                 $count = $this->last("count");
150                 $this->COUNT = $count;
151                 return $count;
152             }
153         }
154     }
155
156     function apop ($login,$pass) {
157         //  Attempts an APOP login. If this fails, it'll
158         //  try a standard login. YOUR SERVER MUST SUPPORT
159         //  THE USE OF THE APOP COMMAND!
160         //  (apop is optional per rfc1939)
161
162         if(!isset($this->FP)) {
163             $this->ERROR = "POP3 apop: " . _("No connection to server");
164             return false;
165         } elseif(!$this->ALLOWAPOP) {
166             $retVal = $this->login($login,$pass);
167             return $retVal;
168         } elseif(empty($login)) {
169             $this->ERROR = "POP3 apop: " . _("No login ID submitted");
170             return false;
171         } elseif(empty($pass)) {
172             $this->ERROR = "POP3 apop: " . _("No password submitted");
173             return false;
174         } else {
175             $banner = $this->BANNER;
176             if( (!$banner) or (empty($banner)) ) {
177                 $this->ERROR = "POP3 apop: " . _("No server banner") . ' - ' . _("abort");
178                 $retVal = $this->login($login,$pass);
179                 return $retVal;
180             } else {
181                 $AuthString = $banner;
182                 $AuthString .= $pass;
183                 $APOPString = md5($AuthString);
184                 $cmd = "APOP $login $APOPString";
185                 $reply = $this->send_cmd($cmd);
186                 if(!$this->is_ok($reply)) {
187                     $this->ERROR = "POP3 apop: " . _("apop authentication failed") . ' - ' . _("abort");
188                     $retVal = $this->login($login,$pass);
189                     return $retVal;
190                 } else {
191                     //  Auth successful.
192                     $count = $this->last("count");
193                     $this->COUNT = $count;
194                     return $count;
195                 }
196             }
197         }
198     }
199
200     function login ($login = "", $pass = "") {
201         // Sends both user and pass. Returns # of msgs in mailbox or
202         // false on failure (or -1, if the error occurs while getting
203         // the number of messages.)
204
205         if( !isset($this->FP) ) {
206             $this->ERROR = "POP3 login: " . _("No connection to server");
207             return false;
208         } else {
209             $fp = $this->FP;
210             if( !$this->user( $login ) ) {
211                 //  Preserve the error generated by user()
212                 return false;
213             } else {
214                 $count = $this->pass($pass);
215                 if( (!$count) || ($count == -1) ) {
216                     //  Preserve the error generated by last() and pass()
217                     return false;
218                 } else
219                     return $count;
220             }
221         }
222     }
223
224     function top ($msgNum, $numLines = "0") {
225         //  Gets the header and first $numLines of the msg body
226         //  returns data in an array with each returned line being
227         //  an array element. If $numLines is empty, returns
228         //  only the header information, and none of the body.
229
230         if(!isset($this->FP)) {
231             $this->ERROR = "POP3 top: " . _("No connection to server");
232             return false;
233         }
234         $this->update_timer();
235
236         $fp = $this->FP;
237         $buffer = $this->BUFFER;
238         $cmd = "TOP $msgNum $numLines";
239         fwrite($fp, "TOP $msgNum $numLines\r\n");
240         $reply = fgets($fp, $buffer);
241         $reply = $this->strip_clf($reply);
242         if($this->DEBUG) {
243             @error_log("POP3 SEND [$cmd] GOT [$reply]",0);
244         }
245         if(!$this->is_ok($reply))
246         {
247             $this->ERROR = "POP3 top: " . _("Error ") . "[$reply]";
248             return false;
249         }
250
251         $count = 0;
252         $MsgArray = array();
253
254         $line = fgets($fp,$buffer);
255         while ( !ereg("^\.\r\n",$line))
256         {
257             $MsgArray[$count] = $line;
258             $count++;
259             $line = fgets($fp,$buffer);
260             if(empty($line))    { break; }
261         }
262
263         return $MsgArray;
264     }
265
266     function pop_list ($msgNum = "") {
267         //  If called with an argument, returns that msgs' size in octets
268         //  No argument returns an associative array of undeleted
269         //  msg numbers and their sizes in octets
270
271         if(!isset($this->FP))
272         {
273             $this->ERROR = "POP3 pop_list: " . _("No connection to server");
274             return false;
275         }
276         $fp = $this->FP;
277         $Total = $this->COUNT;
278         if( (!$Total) or ($Total == -1) )
279         {
280             return false;
281         }
282         if($Total == 0)
283         {
284             return array("0","0");
285             // return -1;   // mailbox empty
286         }
287
288         $this->update_timer();
289
290         if(!empty($msgNum))
291         {
292             $cmd = "LIST $msgNum";
293             fwrite($fp,"$cmd\r\n");
294             $reply = fgets($fp,$this->BUFFER);
295             $reply = $this->strip_clf($reply);
296             if($this->DEBUG) {
297                 @error_log("POP3 SEND [$cmd] GOT [$reply]",0);
298             }
299             if(!$this->is_ok($reply))
300             {
301                 $this->ERROR = "POP3 pop_list: " . _("Error ") . "[$reply]";
302                 return false;
303             }
304             list($junk,$num,$size) = preg_split('/\s+/',$reply);
305             return $size;
306         }
307         $cmd = "LIST";
308         $reply = $this->send_cmd($cmd);
309         if(!$this->is_ok($reply))
310         {
311             $reply = $this->strip_clf($reply);
312             $this->ERROR = "POP3 pop_list: " . _("Error ") .  "[$reply]";
313             return false;
314         }
315         $MsgArray = array();
316         $MsgArray[0] = $Total;
317         for($msgC=1;$msgC <= $Total; $msgC++)
318         {
319             if($msgC > $Total) { break; }
320             $line = fgets($fp,$this->BUFFER);
321             $line = $this->strip_clf($line);
322             if(ereg("^\.",$line))
323             {
324                 $this->ERROR = "POP3 pop_list: " . _("Premature end of list");
325                 return false;
326             }
327             list($thisMsg,$msgSize) = preg_split('/\s+/',$line);
328             settype($thisMsg,"integer");
329             if($thisMsg != $msgC)
330             {
331                 $MsgArray[$msgC] = "deleted";
332             }
333             else
334             {
335                 $MsgArray[$msgC] = $msgSize;
336             }
337         }
338         return $MsgArray;
339     }
340
341     function get ($msgNum) {
342         //  Retrieve the specified msg number. Returns an array
343         //  where each line of the msg is an array element.
344
345         if(!isset($this->FP))
346         {
347             $this->ERROR = "POP3 get: " . _("No connection to server");
348             return false;
349         }
350
351         $this->update_timer();
352
353         $fp = $this->FP;
354         $buffer = $this->BUFFER;
355         $cmd = "RETR $msgNum";
356         $reply = $this->send_cmd($cmd);
357
358         if(!$this->is_ok($reply))
359         {
360             $this->ERROR = "POP3 get: " . _("Error ") . "[$reply]";
361             return false;
362         }
363
364         $count = 0;
365         $MsgArray = array();
366
367         $line = fgets($fp,$buffer);
368         while ( !ereg("^\.\r\n",$line))
369         {
370             if ( $line{0} == '.' ) { $line = substr($line,1); }
371             $MsgArray[$count] = $line;
372             $count++;
373             $line = fgets($fp,$buffer);
374             if(empty($line))    { break; }
375         }
376         return $MsgArray;
377     }
378
379     function last ( $type = "count" ) {
380         //  Returns the highest msg number in the mailbox.
381         //  returns -1 on error, 0+ on success, if type != count
382         //  results in a popstat() call (2 element array returned)
383
384         $last = -1;
385         if(!isset($this->FP))
386         {
387             $this->ERROR = "POP3 last: " . _("No connection to server");
388             return $last;
389         }
390
391         $reply = $this->send_cmd("STAT");
392         if(!$this->is_ok($reply))
393         {
394             $this->ERROR = "POP3 last: " . _("Error ") . "[$reply]";
395             return $last;
396         }
397
398         $Vars = preg_split('/\s+/',$reply);
399         $count = $Vars[1];
400         $size = $Vars[2];
401         settype($count,"integer");
402         settype($size,"integer");
403         if($type != "count")
404         {
405             return array($count,$size);
406         }
407         return $count;
408     }
409
410     function reset () {
411         //  Resets the status of the remote server. This includes
412         //  resetting the status of ALL msgs to not be deleted.
413         //  This method automatically closes the connection to the server.
414
415         if(!isset($this->FP))
416         {
417             $this->ERROR = "POP3 reset: " . _("No connection to server");
418             return false;
419         }
420         $reply = $this->send_cmd("RSET");
421         if(!$this->is_ok($reply))
422         {
423             //  The POP3 RSET command -never- gives a -ERR
424             //  response - if it ever does, something truely
425             //  wild is going on.
426
427             $this->ERROR = "POP3 reset: " . _("Error ") . "[$reply]";
428             @error_log("POP3 reset: ERROR [$reply]",0);
429         }
430         $this->quit();
431         return true;
432     }
433
434     function send_cmd ( $cmd = "" )
435     {
436         //  Sends a user defined command string to the
437         //  POP server and returns the results. Useful for
438         //  non-compliant or custom POP servers.
439         //  Do NOT includ the \r\n as part of your command
440         //  string - it will be appended automatically.
441
442         //  The return value is a standard fgets() call, which
443         //  will read up to $this->BUFFER bytes of data, until it
444         //  encounters a new line, or EOF, whichever happens first.
445
446         //  This method works best if $cmd responds with only
447         //  one line of data.
448
449         if(!isset($this->FP))
450         {
451             $this->ERROR = "POP3 send_cmd: " . _("No connection to server");
452             return false;
453         }
454
455         if(empty($cmd))
456         {
457             $this->ERROR = "POP3 send_cmd: " . _("Empty command string");
458             return "";
459         }
460
461         $fp = $this->FP;
462         $buffer = $this->BUFFER;
463         $this->update_timer();
464         fwrite($fp,"$cmd\r\n");
465         $reply = fgets($fp,$buffer);
466         $reply = $this->strip_clf($reply);
467         if($this->DEBUG) { @error_log("POP3 SEND [$cmd] GOT [$reply]",0); }
468         return $reply;
469     }
470
471     function quit() {
472         //  Closes the connection to the POP3 server, deleting
473         //  any msgs marked as deleted.
474
475         if(!isset($this->FP))
476         {
477             $this->ERROR = "POP3 quit: " . _("connection does not exist");
478             return false;
479         }
480         $fp = $this->FP;
481         $cmd = "QUIT";
482         fwrite($fp,"$cmd\r\n");
483         $reply = fgets($fp,$this->BUFFER);
484         $reply = $this->strip_clf($reply);
485         if($this->DEBUG) { @error_log("POP3 SEND [$cmd] GOT [$reply]",0); }
486         fclose($fp);
487         unset($this->FP);
488         return true;
489     }
490
491     function popstat () {
492         //  Returns an array of 2 elements. The number of undeleted
493         //  msgs in the mailbox, and the size of the mbox in octets.
494
495         $PopArray = $this->last("array");
496
497         if($PopArray == -1) { return false; }
498
499         if( (!$PopArray) or (empty($PopArray)) )
500         {
501             return false;
502         }
503         return $PopArray;
504     }
505
506     function uidl ($msgNum = "")
507     {
508         //  Returns the UIDL of the msg specified. If called with
509         //  no arguments, returns an associative array where each
510         //  undeleted msg num is a key, and the msg's uidl is the element
511         //  Array element 0 will contain the total number of msgs
512
513         if(!isset($this->FP)) {
514             $this->ERROR = "POP3 uidl: " . _("No connection to server");
515             return false;
516         }
517
518         $fp = $this->FP;
519         $buffer = $this->BUFFER;
520
521         if(!empty($msgNum)) {
522             $cmd = "UIDL $msgNum";
523             $reply = $this->send_cmd($cmd);
524             if(!$this->is_ok($reply))
525             {
526                 $this->ERROR = "POP3 uidl: " . _("Error ") . "[$reply]";
527                 return false;
528             }
529             list ($ok,$num,$myUidl) = preg_split('/\s+/',$reply);
530             return $myUidl;
531         } else {
532             $this->update_timer();
533
534             $UIDLArray = array();
535             $Total = $this->COUNT;
536             $UIDLArray[0] = $Total;
537
538             if ($Total < 1)
539             {
540                 return $UIDLArray;
541             }
542             $cmd = "UIDL";
543             fwrite($fp, "UIDL\r\n");
544             $reply = fgets($fp, $buffer);
545             $reply = $this->strip_clf($reply);
546             if($this->DEBUG) { @error_log("POP3 SEND [$cmd] GOT [$reply]",0); }
547             if(!$this->is_ok($reply))
548             {
549                 $this->ERROR = "POP3 uidl: " . _("Error ") . "[$reply]";
550                 return false;
551             }
552
553             $line = "";
554             $count = 1;
555             $line = fgets($fp,$buffer);
556             while ( !ereg("^\.\r\n",$line)) {
557                 if(ereg("^\.\r\n",$line)) {
558                     break;
559                 }
560                 list ($msg,$msgUidl) = preg_split('/\s+/',$line);
561                 $msgUidl = $this->strip_clf($msgUidl);
562                 if($count == $msg) {
563                     $UIDLArray[$msg] = $msgUidl;
564                 }
565                 else
566                 {
567                     $UIDLArray[$count] = 'deleted';
568                 }
569                 $count++;
570                 $line = fgets($fp,$buffer);
571             }
572         }
573         return $UIDLArray;
574     }
575
576     function delete ($msgNum = "") {
577         //  Flags a specified msg as deleted. The msg will not
578         //  be deleted until a quit() method is called.
579
580         if(!isset($this->FP))
581         {
582             $this->ERROR = "POP3 delete: " . _("No connection to server");
583             return false;
584         }
585         if(empty($msgNum))
586         {
587             $this->ERROR = "POP3 delete: " . _("No msg number submitted");
588             return false;
589         }
590         $reply = $this->send_cmd("DELE $msgNum");
591         if(!$this->is_ok($reply))
592         {
593             $this->ERROR = "POP3 delete: " . _("Command failed ") . "[$reply]";
594             return false;
595         }
596         return true;
597     }
598
599     //  *********************************************************
600
601     //  The following methods are internal to the class.
602
603     function is_ok ($cmd = "") {
604         //  Return true or false on +OK or -ERR
605
606         if( empty($cmd) )
607             return false;
608         else
609             return( ereg ("^\+OK", $cmd ) );
610     }
611
612     function strip_clf ($text = "") {
613         // Strips \r\n from server responses
614
615         if(empty($text))
616             return $text;
617         else {
618             $stripped = str_replace("\r",'',$text);
619             $stripped = str_replace("\n",'',$stripped);
620             return $stripped;
621         }
622     }
623
624     function parse_banner ( $server_text ) {
625         $outside = true;
626         $banner = "";
627         $length = strlen($server_text);
628         for($count =0; $count < $length; $count++)
629         {
630             $digit = substr($server_text,$count,1);
631             if(!empty($digit))             {
632                 if( (!$outside) && ($digit != '<') && ($digit != '>') )
633                 {
634                     $banner .= $digit;
635                 }
636                 if ($digit == '<')
637                 {
638                     $outside = false;
639                 }
640                 if($digit == '>')
641                 {
642                     $outside = true;
643                 }
644             }
645         }
646         $banner = $this->strip_clf($banner);    // Just in case
647         return "<$banner>";
648     }
649
650 }   // End class
651 ?>