* Copyright (c) 1999-2011 CDI (cdi@thewebmasters.net) All Rights Reserved
* Modified by Philippe Mingo 2001-2009 mingo@rotedic.com
* An RFC 1939 compliant wrapper class for the POP3 protocol.
* Licensed under the GNU GPL. For full terms see the file COPYING.
* @copyright 1999-2011 The SquirrelMail Project Team
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
var $ERROR = ''; // Error string.
var $TIMEOUT = 60; // Default timeout before giving up on a
var $COUNT = -1; // Mailbox msg count
var $BUFFER = 512; // Socket buffer for socket fgets() calls.
// Per RFC 1939 the returned line a POP3
// server can send is 512 bytes.
var $FP = ''; // The connection to the server's
var $MAILSERVER = ''; // Set this to hard code the server name
var $DEBUG = FALSE; // set to true to echo pop3
// commands and responses to error_log
// this WILL log passwords!
var $BANNER = ''; // Holds the banner returned by the
// pop server - used for apop()
var $ALLOWAPOP = FALSE; // Allow or disallow apop()
// This must be set to true
function __construct ( $server = '', $timeout = '' ) {
settype($this->BUFFER,"integer");
// Do not allow programs to alter MAILSERVER
// if it is already specified. They can get around
// this if they -really- want to, so don't count on it.
if(empty($this->MAILSERVER))
$this->MAILSERVER = $server;
settype($timeout,"integer");
$this->TIMEOUT = $timeout;
set_time_limit($timeout);
public function POP3( $server = '', $timeout = '' ) {
self::__construct( $server, $timeout );
function update_timer () {
set_time_limit($this->TIMEOUT);
function connect ($server, $port = 110) {
// Opens a socket to the specified server. Unless overridden,
// port defaults to 110. Returns true on success, false on fail
// If MAILSERVER is set, override $server with its value.
if (!isset($port) || !$port) {$port = 110;}
if(!empty($this->MAILSERVER))
$server = $this->MAILSERVER;
$this->ERROR = "POP3 connect: " . _("No server specified");
$fp = @fsockopen("$server", $port, $errno, $errstr);
$this->ERROR = "POP3 connect: " . _("Error ") . "[$errno] [$errstr]";
socket_set_blocking($fp,-1);
$reply = fgets($fp,$this->BUFFER);
$reply = $this->strip_clf($reply);
error_log("POP3 SEND [connect: $server] GOT [$reply]",0);
if(!$this->is_ok($reply)) {
$this->ERROR = "POP3 connect: " . _("Error ") . "[$reply]";
$this->BANNER = $this->parse_banner($reply);
function user ($user = "") {
// Sends the USER command, returns true or false
$this->ERROR = "POP3 user: " . _("no login ID submitted");
} elseif(!isset($this->FP)) {
$this->ERROR = "POP3 user: " . _("connection not established");
$reply = $this->send_cmd("USER $user");
if(!$this->is_ok($reply)) {
$this->ERROR = "POP3 user: " . _("Error ") . "[$reply]";
function pass ($pass = "") {
// Sends the PASS command, returns # of msgs in mailbox,
// returns false (undef) on Auth failure
$this->ERROR = "POP3 pass: " . _("No password submitted");
} elseif(!isset($this->FP)) {
$this->ERROR = "POP3 pass: " . _("connection not established");
$reply = $this->send_cmd("PASS $pass");
if(!$this->is_ok($reply)) {
$this->ERROR = "POP3 pass: " . _("Authentication failed") . " [$reply]";
$count = $this->last("count");
function apop ($login,$pass) {
// Attempts an APOP login. If this fails, it'll
// try a standard login. YOUR SERVER MUST SUPPORT
// THE USE OF THE APOP COMMAND!
// (apop is optional per rfc1939)
$this->ERROR = "POP3 apop: " . _("No connection to server");
} elseif(!$this->ALLOWAPOP) {
$retVal = $this->login($login,$pass);
} elseif(empty($login)) {
$this->ERROR = "POP3 apop: " . _("No login ID submitted");
$this->ERROR = "POP3 apop: " . _("No password submitted");
if( (!$banner) or (empty($banner)) ) {
$this->ERROR = "POP3 apop: " . _("No server banner") . ' - ' . _("abort");
$retVal = $this->login($login,$pass);
$APOPString = md5($AuthString);
$cmd = "APOP $login $APOPString";
$reply = $this->send_cmd($cmd);
if(!$this->is_ok($reply)) {
$this->ERROR = "POP3 apop: " . _("apop authentication failed") . ' - ' . _("abort");
$retVal = $this->login($login,$pass);
$count = $this->last("count");
function login ($login = "", $pass = "") {
// Sends both user and pass. Returns # of msgs in mailbox or
// false on failure (or -1, if the error occurs while getting
// the number of messages.)
if( !isset($this->FP) ) {
$this->ERROR = "POP3 login: " . _("No connection to server");
if( !$this->user( $login ) ) {
// Preserve the error generated by user()
$count = $this->pass($pass);
if( (!$count) || ($count == -1) ) {
// Preserve the error generated by last() and pass()
function top ($msgNum, $numLines = "0") {
// Gets the header and first $numLines of the msg body
// returns data in an array with each returned line being
// an array element. If $numLines is empty, returns
// only the header information, and none of the body.
$this->ERROR = "POP3 top: " . _("No connection to server");
$cmd = "TOP $msgNum $numLines";
fwrite($fp, "TOP $msgNum $numLines\r\n");
$reply = fgets($fp, $buffer);
$reply = $this->strip_clf($reply);
@error_log("POP3 SEND [$cmd] GOT [$reply]",0);
if(!$this->is_ok($reply))
$this->ERROR = "POP3 top: " . _("Error ") . "[$reply]";
$line = fgets($fp,$buffer);
while ( !preg_match('/^\.\r\n/',$line))
$MsgArray[$count] = $line;
$line = fgets($fp,$buffer);
if(empty($line)) { break; }
function pop_list ($msgNum = "") {
// If called with an argument, returns that msgs' size in octets
// No argument returns an associative array of undeleted
// msg numbers and their sizes in octets
$this->ERROR = "POP3 pop_list: " . _("No connection to server");
if( (!$Total) or ($Total == -1) )
// return -1; // mailbox empty
$reply = fgets($fp,$this->BUFFER);
$reply = $this->strip_clf($reply);
@error_log("POP3 SEND [$cmd] GOT [$reply]",0);
if(!$this->is_ok($reply))
$this->ERROR = "POP3 pop_list: " . _("Error ") . "[$reply]";
list($junk,$num,$size) = preg_split('/\s+/',$reply);
$reply = $this->send_cmd($cmd);
if(!$this->is_ok($reply))
$reply = $this->strip_clf($reply);
$this->ERROR = "POP3 pop_list: " . _("Error ") . "[$reply]";
for($msgC=1;$msgC <= $Total; $msgC++)
if($msgC > $Total) { break; }
$line = fgets($fp,$this->BUFFER);
$line = $this->strip_clf($line);
if(strpos($line, '.') === 0)
$this->ERROR = "POP3 pop_list: " . _("Premature end of list");
list($thisMsg,$msgSize) = preg_split('/\s+/',$line);
settype($thisMsg,"integer");
$MsgArray[$msgC] = "deleted";
$MsgArray[$msgC] = $msgSize;
// Retrieve the specified msg number. Returns an array
// where each line of the msg is an array element.
$this->ERROR = "POP3 get: " . _("No connection to server");
$reply = $this->send_cmd($cmd);
if(!$this->is_ok($reply))
$this->ERROR = "POP3 get: " . _("Error ") . "[$reply]";
$line = fgets($fp,$buffer);
while ( !preg_match('/^\.\r\n/',$line))
if ( $line[0] == '.' ) { $line = substr($line,1); }
$MsgArray[$count] = $line;
$line = fgets($fp,$buffer);
if(empty($line)) { break; }
function last ( $type = "count" ) {
// Returns the highest msg number in the mailbox.
// returns -1 on error, 0+ on success, if type != count
// results in a popstat() call (2 element array returned)
$this->ERROR = "POP3 last: " . _("No connection to server");
$reply = $this->send_cmd("STAT");
if(!$this->is_ok($reply))
$this->ERROR = "POP3 last: " . _("Error ") . "[$reply]";
$Vars = preg_split('/\s+/',$reply);
settype($count,"integer");
settype($size,"integer");
return array($count,$size);
// Resets the status of the remote server. This includes
// resetting the status of ALL msgs to not be deleted.
// This method automatically closes the connection to the server.
$this->ERROR = "POP3 reset: " . _("No connection to server");
$reply = $this->send_cmd("RSET");
if(!$this->is_ok($reply))
// The POP3 RSET command -never- gives a -ERR
// response - if it ever does, something truly
$this->ERROR = "POP3 reset: " . _("Error ") . "[$reply]";
@error_log("POP3 reset: ERROR [$reply]",0);
function send_cmd ( $cmd = "" )
// Sends a user defined command string to the
// POP server and returns the results. Useful for
// non-compliant or custom POP servers.
// Do NOT includ the \r\n as part of your command
// string - it will be appended automatically.
// The return value is a standard fgets() call, which
// will read up to $this->BUFFER bytes of data, until it
// encounters a new line, or EOF, whichever happens first.
// This method works best if $cmd responds with only
$this->ERROR = "POP3 send_cmd: " . _("No connection to server");
$this->ERROR = "POP3 send_cmd: " . _("Empty command string");
$reply = fgets($fp,$buffer);
$reply = $this->strip_clf($reply);
if($this->DEBUG) { @error_log("POP3 SEND [$cmd] GOT [$reply]",0); }
// Closes the connection to the POP3 server, deleting
// any msgs marked as deleted.
$this->ERROR = "POP3 quit: " . _("connection does not exist");
$reply = fgets($fp,$this->BUFFER);
$reply = $this->strip_clf($reply);
if($this->DEBUG) { @error_log("POP3 SEND [$cmd] GOT [$reply]",0); }
// Returns an array of 2 elements. The number of undeleted
// msgs in the mailbox, and the size of the mbox in octets.