Remote Mail Drive Code
Brought to you by:
xamlo_ru
--- a +++ b/includes/pop3.php @@ -0,0 +1,752 @@ +<?php +/* + * pop3.php + * + * @(#) $Header: /home/mlemos/cvsroot/pop3/pop3.php,v 1.19 2006/09/28 05:00:00 mlemos Exp $ + * + */ + +class pop3_class +{ + var $hostname=""; + var $port=110; + var $tls=0; + var $quit_handshake=1; + var $error=""; + var $authentication_mechanism="USER"; + var $realm=""; + var $workstation=""; + var $join_continuation_header_lines=1; + + /* Private variables - DO NOT ACCESS */ + + var $connection=0; + var $state="DISCONNECTED"; + var $greeting=""; + var $must_update=0; + var $debug=0; + var $html_debug=0; + var $next_token=""; + var $message_buffer=""; + + /* Private methods - DO NOT CALL */ + + Function Tokenize($string,$separator="") + { + if(!strcmp($separator,"")) + { + $separator=$string; + $string=$this->next_token; + } + for($character=0;$character<strlen($separator);$character++) + { + if(GetType($position=strpos($string,$separator[$character]))=="integer") + $found=(IsSet($found) ? min($found,$position) : $position); + } + if(IsSet($found)) + { + $this->next_token=substr($string,$found+1); + return(substr($string,0,$found)); + } + else + { + $this->next_token=""; + return($string); + } + } + + Function SetError($error) + { + return($this->error=$error); + } + + Function OutputDebug($message) + { + $message.="\n"; + if($this->html_debug) + $message=str_replace("\n","<br />\n",HtmlSpecialChars($message)); + echo $message; + flush(); + } + + Function GetLine() + { + for($line="";;) + { + if(feof($this->connection)) + return(0); + $line.=fgets($this->connection,100); + $length=strlen($line); + if($length>=2 + && substr($line,$length-2,2)=="\r\n") + { + $line=substr($line,0,$length-2); + if($this->debug) + $this->OutputDebug("S $line"); + return($line); + } + } + } + + Function PutLine($line) + { + if($this->debug) + $this->OutputDebug("C $line"); + return(fputs($this->connection,"$line\r\n")); + } + + Function OpenConnection() + { + if($this->tls) + { + $version=explode(".",function_exists("phpversion") ? phpversion() : "3.0.7"); + $php_version=intval($version[0])*1000000+intval($version[1])*1000+intval($version[2]); + if($php_version<4003000) + return("establishing TLS connections requires at least PHP version 4.3.0"); + if(!function_exists("extension_loaded") + || !extension_loaded("openssl")) + return("establishing TLS connections requires the OpenSSL extension enabled"); + } + if($this->hostname=="") + return($this->SetError("2 it was not specified a valid hostname")); + if($this->debug) + $this->OutputDebug("Connecting to ".$this->hostname." ..."); + if(($this->connection=@fsockopen(($this->tls ? "tls://" : "").$this->hostname,$this->port,$error))==0) + { + switch($error) + { + case -3: + return($this->SetError("-3 socket could not be created")); + case -4: + return($this->SetError("-4 dns lookup on hostname \"$hostname\" failed")); + case -5: + return($this->SetError("-5 connection refused or timed out")); + case -6: + return($this->SetError("-6 fdopen() call failed")); + case -7: + return($this->SetError("-7 setvbuf() call failed")); + default: + return($this->SetError($error." could not connect to the host \"".$this->hostname."\"")); + } + } + return(""); + } + + Function CloseConnection() + { + if($this->debug) + $this->OutputDebug("Closing connection."); + if($this->connection!=0) + { + fclose($this->connection); + $this->connection=0; + } + } + + /* Public methods */ + + /* Open method - set the object variable $hostname to the POP3 server address. */ + + Function Open() + { + if($this->state!="DISCONNECTED") + return($this->SetError("1 a connection is already opened")); + if(($error=$this->OpenConnection())!="") + return($error); + $this->greeting=$this->GetLine(); + if(GetType($this->greeting)!="string" + || $this->Tokenize($this->greeting," ")!="+OK") + { + $this->CloseConnection(); + return($this->SetError("3 POP3 server greeting was not found")); + } + $this->Tokenize("<"); + $this->must_update=0; + $this->state="AUTHORIZATION"; + return(""); + } + + /* Close method - this method must be called at least if there are any + messages to be deleted */ + + Function Close() + { + if($this->state=="DISCONNECTED") + return($this->SetError("no connection was opened")); + if($this->must_update + || $this->quit_handshake) + { + if($this->PutLine("QUIT")==0) + return($this->SetError("Could not send the QUIT command")); + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not get quit command response")); + if($this->Tokenize($response," ")!="+OK") + return($this->SetError("Could not quit the connection: ".$this->Tokenize("\r\n"))); + } + $this->CloseConnection(); + $this->state="DISCONNECTED"; + return(""); + } + + /* Login method - pass the user name and password of POP account. Set + $apop to 1 or 0 wether you want to login using APOP method or not. */ + + Function Login($user,$password,$apop=0) + { + if($this->state!="AUTHORIZATION") + return($this->SetError("connection is not in AUTHORIZATION state")); + if($apop) + { + if(!strcmp($this->greeting,"")) + return($this->SetError("Server does not seem to support APOP authentication")); + if($this->PutLine("APOP $user ".md5("<".$this->greeting.">".$password))==0) + return($this->SetError("Could not send the APOP command")); + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not get APOP login command response")); + if($this->Tokenize($response," ")!="+OK") + return($this->SetError("APOP login failed: ".$this->Tokenize("\r\n"))); + } + else + { + $authenticated=0; + if(strcmp($this->authentication_mechanism,"USER") + && function_exists("class_exists") + && class_exists("sasl_client_class")) + { + if(strlen($this->authentication_mechanism)) + $mechanisms=array($this->authentication_mechanism); + else + { + $mechanisms=array(); + if($this->PutLine("CAPA")==0) + return($this->SetError("Could not send the CAPA command")); + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not get CAPA command response")); + if(!strcmp($this->Tokenize($response," "),"+OK")) + { + for(;;) + { + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not retrieve the supported authentication methods")); + switch($this->Tokenize($response," ")) + { + case ".": + break 2; + case "SASL": + for($method=1;strlen($mechanism=$this->Tokenize(" "));$method++) + $mechanisms[]=$mechanism; + break; + } + } + } + } + $sasl=new sasl_client_class; + $sasl->SetCredential("user",$user); + $sasl->SetCredential("password",$password); + if(strlen($this->realm)) + $sasl->SetCredential("realm",$this->realm); + if(strlen($this->workstation)) + $sasl->SetCredential("workstation",$this->workstation); + do + { + $status=$sasl->Start($mechanisms,$message,$interactions); + } + while($status==SASL_INTERACT); + switch($status) + { + case SASL_CONTINUE: + break; + case SASL_NOMECH: + if(strlen($this->authentication_mechanism)) + return($this->SetError("authenticated mechanism ".$this->authentication_mechanism." may not be used: ".$sasl->error)); + break; + default: + return($this->SetError("Could not start the SASL authentication client: ".$sasl->error)); + } + if(strlen($sasl->mechanism)) + { + if($this->PutLine("AUTH ".$sasl->mechanism.(IsSet($message) ? " ".base64_encode($message) : ""))==0) + return("Could not send the AUTH command"); + $response=$this->GetLine(); + if(GetType($response)!="string") + return("Could not get AUTH command response"); + switch($this->Tokenize($response," ")) + { + case "+OK": + $response=""; + break; + case "+": + $response=base64_decode($this->Tokenize("\r\n")); + break; + default: + return($this->SetError("Authentication error: ".$this->Tokenize("\r\n"))); + } + for(;!$authenticated;) + { + do + { + $status=$sasl->Step($response,$message,$interactions); + } + while($status==SASL_INTERACT); + switch($status) + { + case SASL_CONTINUE: + if($this->PutLine(base64_encode($message))==0) + return("Could not send message authentication step message"); + $response=$this->GetLine(); + if(GetType($response)!="string") + return("Could not get authentication step message response"); + switch($this->Tokenize($response," ")) + { + case "+OK": + $authenticated=1; + break; + case "+": + $response=base64_decode($this->Tokenize("\r\n")); + break; + default: + return($this->SetError("Authentication error: ".$this->Tokenize("\r\n"))); + } + break; + default: + return($this->SetError("Could not process the SASL authentication step: ".$sasl->error)); + } + } + } + } + if(!$authenticated) + { + if($this->PutLine("USER $user")==0) + return($this->SetError("Could not send the USER command")); + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not get user login entry response")); + if($this->Tokenize($response," ")!="+OK") + return($this->SetError("User error: ".$this->Tokenize("\r\n"))); + if($this->PutLine("PASS $password")==0) + return($this->SetError("Could not send the PASS command")); + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not get login password entry response")); + if($this->Tokenize($response," ")!="+OK") + return($this->SetError("Password error: ".$this->Tokenize("\r\n"))); + } + } + $this->state="TRANSACTION"; + return(""); + } + + /* Statistics method - pass references to variables to hold the number of + messages in the mail box and the size that they take in bytes. */ + + Function Statistics(&$messages,&$size) + { + if($this->state!="TRANSACTION") + return($this->SetError("connection is not in TRANSACTION state")); + if($this->PutLine("STAT")==0) + return($this->SetError("Could not send the STAT command")); + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not get the statistics command response")); + if($this->Tokenize($response," ")!="+OK") + return($this->SetError("Could not get the statistics: ".$this->Tokenize("\r\n"))); + $messages=$this->Tokenize(" "); + $size=$this->Tokenize(" "); + return(""); + } + + /* ListMessages method - the $message argument indicates the number of a + message to be listed. If you specify an empty string it will list all + messages in the mail box. The $unique_id flag indicates if you want + to list the each message unique identifier, otherwise it will + return the size of each message listed. If you list all messages the + result will be returned in an array. */ + + Function ListMessages($message,$unique_id) + { + if($this->state!="TRANSACTION") + return($this->SetError("connection is not in TRANSACTION state")); + if($unique_id) + $list_command="UIDL"; + else + $list_command="LIST"; + if($this->PutLine("$list_command".($message ? " ".$message : ""))==0) + return($this->SetError("Could not send the $list_command command")); + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not get message list command response")); + if($this->Tokenize($response," ")!="+OK") + return($this->SetError("Could not get the message listing: ".$this->Tokenize("\r\n"))); + if($message=="") + { + for($messages=array();;) + { + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not get message list response")); + if($response==".") + break; + $message=intval($this->Tokenize($response," ")); + if($unique_id) + $messages[$this->Tokenize(" ")]=$message; + else + $messages[$message]=intval($this->Tokenize(" ")); + } + return($messages); + } + else + { + $message=intval($this->Tokenize(" ")); + $value=$this->Tokenize(" "); + return($unique_id ? $value : intval($value)); + } + } + + /* RetrieveMessage method - the $message argument indicates the number of + a message to be listed. Pass a reference variables that will hold the + arrays of the $header and $body lines. The $lines argument tells how + many lines of the message are to be retrieved. Pass a negative number + if you want to retrieve the whole message. */ + + Function RetrieveMessage($message,&$headers,&$body,$lines) + { + if($this->state!="TRANSACTION") + return($this->SetError("connection is not in TRANSACTION state")); + if($lines<0) + { + $command="RETR"; + $arguments="$message"; + } + else + { + $command="TOP"; + $arguments="$message $lines"; + } + $boundary=''; + $startfile=false; + $searchstart=0; + $body=''; + if($this->PutLine("$command $arguments")==0) + return($this->SetError("Could not send the $command command")); + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not get message retrieval command response")); + if($this->Tokenize($response," ")!="+OK") + return($this->SetError("Could not retrieve the message: ".$this->Tokenize("\r\n"))); + for($headers=array(),$line=0;;) + { + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not retrieve the message")); + switch($response) + { + case ".": + return(""); + case "": + break 2; + default: + if(substr($response,0,1)==".") + $response=substr($response,1,strlen($response)-1); + break; + } + if(!$startfile && ($boundary=='')){ + if(strpos($response,'boundary=')!==false){ + $boundary=trim(str_replace(array('boundary=','"'),array('',''),$response)); + //echo "HH:".$boundary; + } + } + if($this->join_continuation_header_lines + && $line>0 + && ($response[0]=="\t" + || $response[0]==" ")) + $headers[$line-1].=$response; + else + { + $headers[$line]=$response; + $line++; + } + } + for($line=0;;$line++) + { + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not retrieve the message")); + switch($response) + { + case ".": + return(""); + default: + if(substr($response,0,1)==".") + $response=substr($response,1,strlen($response)-1); + break; + } + if(($boundary!='')){ + if(strpos($response,$boundary)!==false){ + //$boundary=trim(str_replace(array('boundary=','"'),array('',''),$response)); + //echo "HH:".$boundary; + $searchstart++; + } + } + if($searchstart==2 && !$startfile){ + if(trim($response)==''){ + $startfile=true; + continue; + } + } + if($searchstart==2 && $startfile){ + $body.=base64_decode($response); + } + } + return(""); + } + + /* OpenMessage method - the $message argument indicates the number of + a message to be opened. The $lines argument tells how many lines of + the message are to be retrieved. Pass a negative number if you want + to retrieve the whole message. */ + + Function OpenMessage($message, $lines=-1) + { + if($this->state!="TRANSACTION") + return($this->SetError("connection is not in TRANSACTION state")); + if($lines<0) + { + $command="RETR"; + $arguments="$message"; + } + else + { + $command="TOP"; + $arguments="$message $lines"; + } + if($this->PutLine("$command $arguments")==0) + return($this->SetError("Could not send the $command command")); + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not get message retrieval command response")); + if($this->Tokenize($response," ")!="+OK") + return($this->SetError("Could not retrieve the message: ".$this->Tokenize("\r\n"))); + $this->state="GETMESSAGE"; + $this->message_buffer=""; + return(""); + } + + /* GetMessage method - the $count argument indicates the number of bytes + to be read from an opened message. The $message returns by reference + the data read from the message. The $end_of_message argument returns + by reference a boolean value indicated whether it was reached the end + of the message. */ + + Function GetMessage($count, &$message, &$end_of_message) + { + if($this->state!="GETMESSAGE") + return($this->SetError("connection is not in GETMESSAGE state")); + $message=""; + $end_of_message=0; + while($count>strlen($this->message_buffer) + && !$end_of_message) + { + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not retrieve the message headers")); + if(!strcmp($response,".")) + { + $end_of_message=1; + $this->state="TRANSACTION"; + break; + } + else + { + if(substr($response,0,1)==".") + $response=substr($response,1,strlen($response)-1); + $this->message_buffer.=$response."\r\n"; + } + } + if($end_of_message + || $count>=strlen($this->message_buffer)) + { + $message=$this->message_buffer; + $this->message_buffer=""; + } + else + { + $message=substr($this->message_buffer, 0, $count); + $this->message_buffer=substr($this->message_buffer, $count); + } + return(""); + } + + /* DeleteMessage method - the $message argument indicates the number of + a message to be marked as deleted. Messages will only be effectively + deleted upon a successful call to the Close method. */ + + Function DeleteMessage($message) + { + if($this->state!="TRANSACTION") + return($this->SetError("connection is not in TRANSACTION state")); + if($this->PutLine("DELE $message")==0) + return($this->SetError("Could not send the DELE command")); + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not get message delete command response")); + if($this->Tokenize($response," ")!="+OK") + return($this->SetError("Could not delete the message: ".$this->Tokenize("\r\n"))); + $this->must_update=1; + return(""); + } + + /* ResetDeletedMessages method - Reset the list of marked to be deleted + messages. No messages will be marked to be deleted upon a successful + call to this method. */ + + Function ResetDeletedMessages() + { + if($this->state!="TRANSACTION") + return($this->SetError("connection is not in TRANSACTION state")); + if($this->PutLine("RSET")==0) + return($this->SetError("Could not send the RSET command")); + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not get reset deleted messages command response")); + if($this->Tokenize($response," ")!="+OK") + return($this->SetError("Could not reset deleted messages: ".$this->Tokenize("\r\n"))); + $this->must_update=0; + return(""); + } + + /* IssueNOOP method - Just pings the server to prevent it auto-close the + connection after an idle timeout (tipically 10 minutes). Not very + useful for most likely uses of this class. It's just here for + protocol support completeness. */ + + Function IssueNOOP() + { + if($this->state!="TRANSACTION") + return($this->SetError("connection is not in TRANSACTION state")); + if($this->PutLine("NOOP")==0) + return($this->SetError("Could not send the NOOP command")); + $response=$this->GetLine(); + if(GetType($response)!="string") + return($this->SetError("Could not NOOP command response")); + if($this->Tokenize($response," ")!="+OK") + return($this->SetError("Could not issue the NOOP command: ".$this->Tokenize("\r\n"))); + return(""); + } +}; + +class pop3_stream +{ + var $opened = 0; + var $report_errors = 1; + var $read = 0; + var $buffer = ""; + var $end_of_message=0; + + Function SetError($error) + { + if($this->report_errors) + trigger_error($error); + return(FALSE); + } + + Function ParsePath($path, &$url) + { + $url=parse_url($path); + if(IsSet($url["host"])) + $this->pop3->hostname=$url["host"]; + if(IsSet($url["port"])) + $this->pop3->port=intval($url["port"]); + if(IsSet($url["scheme"]) + && !strcmp($url["scheme"],"pop3s")) + $this->pop3->tls=1; + if(!IsSet($url["user"])) + return($this->SetError("it was not specified a valid POP3 user")); + if(!IsSet($url["pass"])) + return($this->SetError("it was not specified a valid POP3 password")); + if(!IsSet($url["path"])) + return($this->SetError("it was not specified a valid mailbox path")); + if(IsSet($url["query"])) + { + parse_str($url["query"],$query); + if(IsSet($query["debug"])) + $this->pop3->debug = intval($query["debug"]); + if(IsSet($query["html_debug"])) + $this->pop3->html_debug = intval($query["html_debug"]); + if(IsSet($query["tls"])) + $this->pop3->tls = intval($query["tls"]); + if(IsSet($query["realm"])) + $this->pop3->realm = UrlDecode($query["realm"]); + if(IsSet($query["workstation"])) + $this->pop3->workstation = UrlDecode($query["workstation"]); + if(IsSet($query["authentication_mechanism"])) + $this->pop3->realm = UrlDecode($query["authentication_mechanism"]); + if(IsSet($query["quit_handshake"])) + $this->pop3->quit_handshake = intval($query["quit_handshake"]); + } + return(TRUE); + } + + Function stream_open($path, $mode, $options, &$opened_path) + { + $this->report_errors = (($options & STREAM_REPORT_ERRORS) !=0); + if(strcmp($mode, "r")) + return($this->SetError("the message can only be opened for reading")); + $this->pop3=new pop3_class; + if(!$this->ParsePath($path, $url)) + return(FALSE); + $message=substr($url["path"],1); + if(strcmp(intval($message), $message) + || $message<=0) + return($this->SetError("it was not specified a valid message to retrieve")); + if(strlen($error=$this->pop3->Open())) + return($this->SetError($error)); + $this->opened = 1; + $apop = (IsSet($url["query"]["apop"]) ? intval($url["query"]["apop"]) : 0); + if(strlen($error=$this->pop3->Login(UrlDecode($url["user"]), UrlDecode($url["pass"]),$apop)) + || strlen($error=$this->pop3->OpenMessage($message,-1))) + { + $this->stream_close(); + return($this->SetError($error)); + } + $this->end_of_message=FALSE; + if($options & STREAM_USE_PATH) + $opened_path=$path; + $this->read = 0; + $this->buffer = ""; + return(TRUE); + } + + Function stream_eof() + { + if($this->read==0) + return(FALSE); + return($this->end_of_message); + } + + Function stream_read($count) + { + if($count<=0) + return($this->SetError("it was not specified a valid length of the message to read")); + if($this->end_of_message) + return(""); + if(strlen($error=$this->pop3->GetMessage($count, $read, $this->end_of_message))) + return($this->SetError($error)); + $this->read += strlen($read); + return($read); + } + + Function stream_close() + { + if($this->opened) + { + $this->pop3->Close(); + $this->opened = 0; + } + } +}; + +?> \ No newline at end of file