Move pop3 to the resources directory.

This commit is contained in:
Mark Crane
2013-06-09 04:35:27 +00:00
parent 6c6e4cdc26
commit f1812c1bf1
4 changed files with 0 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
<?php
/*
* parse_message.php
*
* @(#) $Header: /home/mlemos/cvsroot/pop3/parse_message.php,v 1.4 2008/01/09 07:32:19 mlemos Exp $
*
*/
?><html>
<head>
<title>Parsing a message with Manuel Lemos' PHP POP3 and MIME Parser classes</title>
</head>
<body>
<center><h1>Parsing a message with Manuel Lemos' PHP POP3 and MIME Parser classes</h1></center>
<hr />
<?php
require('mime_parser.php');
require('rfc822_addresses.php');
require('pop3.php');
stream_wrapper_register('pop3', 'pop3_stream'); /* Register the pop3 stream handler class */
$user=UrlEncode("");
$password=UrlEncode("");
$realm=UrlEncode(""); /* Authentication realm or domain */
$workstation=UrlEncode(""); /* Workstation for NTLM authentication */
$apop=0; /* Use APOP authentication */
$authentication_mechanism=UrlEncode("USER"); /* SASL authentication mechanism */
$debug=1; /* Output debug information */
$html_debug=1; /* Debug information is in HTML */
$message=1;
$message_file='pop3://'.$user.':'.$password.'@localhost/'.$message.
'?debug='.$debug.'&html_debug='.$html_debug.'&realm='.$realm.'&workstation='.$workstation.
'&apop='.$apop.'&authentication_mechanism='.$authentication_mechanism;
/*
* Access Gmail POP account
*/
/*
$message_file='pop3://'.$user.':'.$password.'@pop.gmail.com:995/1?tls=1&debug=1&html_debug=1';
*/
$mime=new mime_parser_class;
/*
* Set to 0 for not decoding the message bodies
*/
$mime->decode_bodies = 1;
$parameters=array(
'File'=>$message_file,
/* Read a message from a string instead of a file */
/* 'Data'=>'My message data string', */
/* Save the message body parts to a directory */
/* 'SaveBody'=>'/tmp', */
/* Do not retrieve or save message body parts */
'SkipBody'=>1,
);
$success=$mime->Decode($parameters, $decoded);
if(!$success)
echo '<h2>MIME message decoding error: '.HtmlSpecialChars($mime->error)."</h2>\n";
else
{
echo '<h2>MIME message decoding successful</h2>'."\n";
echo '<h2>Message structure</h2>'."\n";
echo '<pre>';
var_dump($decoded[0]);
echo '</pre>';
if($mime->Analyze($decoded[0], $results))
{
echo '<h2>Message analysis</h2>'."\n";
echo '<pre>';
var_dump($results);
echo '</pre>';
}
else
echo 'MIME message analyse error: '.$mime->error."\n";
}
?>
<hr />
</body>
</html>

796
resources/pop3/pop3.php Normal file
View File

@@ -0,0 +1,796 @@
<?php
/*
* pop3.php
*
* @(#) $Header: /home/mlemos/cvsroot/pop3/pop3.php,v 1.23 2009/01/31 04:06:12 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="";
var $connection_name = '';
/* 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, $error_message))==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."\": ".$error_message));
}
}
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);
$greeting=$this->GetLine();
if(GetType($greeting)!="string"
|| $this->Tokenize($greeting," ")!="+OK")
{
$this->CloseConnection();
return($this->SetError("3 POP3 server greeting was not found"));
}
$this->Tokenize("<");
$this->greeting = $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"));
while($this->state=='GETMESSAGE')
{
if(strlen($error=$this->GetMessage(8000, $message, $end_of_message)))
return($error);
}
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";
pop3_class::SetConnection(-1, $this->connection_name, $this);
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[$message]=$this->Tokenize(" ");
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";
}
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=$body=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($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;
}
$body[$line]=$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("");
}
Function &SetConnection($set, &$current_name, &$pop3)
{
static $connections = array();
if($set>0)
{
$current_name = strval(count($connections));
$connections[$current_name] = &$pop3;
}
elseif($set<0)
{
$connections[$current_name] = '';
$current_name = '';
}
elseif(IsSet($connections[$current_name])
&& GetType($connections[$current_name])!='string')
{
$connection = &$connections[$current_name];
return($connection);
}
return($pop3);
}
/* GetConnectionName method - Retrieve the name associated to an
established POP3 server connection to use as virtual host name for
use in POP3 stream wrapper URLs. */
Function GetConnectionName(&$connection_name)
{
if($this->state!="TRANSACTION")
return($this->SetError("cannot get the name of a POP3 connection that was not established and the user has logged in"));
if(strlen($this->connection_name) == 0)
pop3_class::SetConnection(1, $this->connection_name, $this);
$connection_name = $this->connection_name;
return('');
}
};
class pop3_stream
{
var $opened = 0;
var $report_errors = 1;
var $read = 0;
var $buffer = "";
var $end_of_message=1;
var $previous_connection = 0;
var $pop3;
Function SetError($error)
{
if($this->report_errors)
trigger_error($error);
return(FALSE);
}
Function ParsePath($path, &$url)
{
if(!$this->previous_connection)
{
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(!$this->previous_connection)
{
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"));
$url=parse_url($path);
$host = $url['host'];
$pop3 = &pop3_class::SetConnection(0, $host, $this->pop3);
if(IsSet($pop3))
{
$this->pop3 = &$pop3;
$this->previous_connection = 1;
}
else
$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(!$this->previous_connection)
{
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)))
{
$this->stream_close();
return($this->SetError($error));
}
}
if(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()
{
while(!$this->end_of_message)
$this->stream_read(8000);
if($this->opened)
{
$this->pop3->Close();
$this->opened = 0;
}
}
};
?>

View File

@@ -0,0 +1,903 @@
<?php
/*
* rfc822_addresses.php
*
* @(#) $Id: rfc822_addresses.php,v 1.13 2010/04/08 20:09:23 mlemos Exp $
*
*/
/*
{metadocument}<?xml version="1.0" encoding="ISO-8859-1" ?>
<class>
<package>net.manuellemos.mimeparser</package>
<version>@(#) $Id: rfc822_addresses.php,v 1.13 2010/04/08 20:09:23 mlemos Exp $</version>
<copyright>Copyright © (C) Manuel Lemos 2006 - 2008</copyright>
<title>RFC 822 e-mail addresses parser</title>
<author>Manuel Lemos</author>
<authoraddress>mlemos-at-acm.org</authoraddress>
<documentation>
<idiom>en</idiom>
<purpose>Parse e-mail addresses from headers of <link>
<url>http://www.ietf.org/rfc/rfc822.txt</url>
<data>RFC 822</data>
</link> compliant e-mail messages.</purpose>
<usage>Use the function <functionlink>ParseAddressList</functionlink>
function to retrieve the list of e-mail addresses contained in
e-mail message headers like <tt>From</tt>, <tt>To</tt>, <tt>Cc</tt>
or <tt>Bcc</tt>.</usage>
</documentation>
{/metadocument}
*/
class rfc822_addresses_class
{
/* Private variables */
var $v = '';
/* Public variables */
/*
{metadocument}
<variable>
<name>error</name>
<type>STRING</type>
<value></value>
<documentation>
<purpose>Store the message that is returned when an error
occurs.</purpose>
<usage>Check this variable to understand what happened when a call to
any of the class functions has failed.<paragraphbreak />
This class uses cumulative error handling. This means that if one
class functions that may fail is called and this variable was
already set to an error message due to a failure in a previous call
to the same or other function, the function will also fail and does
not do anything.<paragraphbreak />
This allows programs using this class to safely call several
functions that may fail and only check the failure condition after
the last function call.<paragraphbreak />
Just set this variable to an empty string to clear the error
condition.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $error = '';
/*
{metadocument}
<variable>
<name>error_position</name>
<type>INTEGER</type>
<value>-1</value>
<documentation>
<purpose>Point to the position of the message data or file that
refers to the last error that occurred.</purpose>
<usage>Check this variable to determine the relevant position of the
message when a parsing error occurs.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $error_position = -1;
/*
{metadocument}
<variable>
<name>ignore_syntax_errors</name>
<type>BOOLEAN</type>
<value>1</value>
<documentation>
<purpose>Specify whether the class should ignore syntax errors in
malformed addresses.</purpose>
<usage>Set this variable to <booleanvalue>0</booleanvalue> if it is
necessary to verify whether message data may be corrupted due to
to eventual bugs in the program that generated the
message.<paragraphbreak />
Currently the class only ignores some types of syntax errors.
Other syntax errors may still cause the
<functionlink>ParseAddressList</functionlink> to fail.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $ignore_syntax_errors=1;
/*
{metadocument}
<variable>
<name>warnings</name>
<type>HASH</type>
<value></value>
<documentation>
<purpose>Return a list of positions of the original message that
contain syntax errors.</purpose>
<usage>Check this variable to retrieve eventual message syntax
errors that were ignored when the
<variablelink>ignore_syntax_errors</variablelink> is set to
<booleanvalue>1</booleanvalue>.<paragraphbreak />
The indexes of this array are the positions of the errors. The
array values are the corresponding syntax error messages.</usage>
</documentation>
</variable>
{/metadocument}
*/
var $warnings=array();
/* Private functions */
Function SetError($error)
{
$this->error = $error;
return(0);
}
Function SetPositionedError($error, $position)
{
$this->error_position = $position;
return($this->SetError($error));
}
Function SetWarning($warning, $position)
{
$this->warnings[$position]=$warning;
return(1);
}
Function SetPositionedWarning($error, $position)
{
if(!$this->ignore_syntax_errors)
return($this->SetPositionedError($error, $position));
return($this->SetWarning($error, $position));
}
Function QDecode($p, &$value, &$encoding)
{
$encoding = $charset = null;
$s = 0;
$decoded = '';
$l = strlen($value);
while($s < $l)
{
if(GetType($q = strpos($value, '=?', $s)) != 'integer')
{
if($s == 0)
return(1);
if($s < $l)
$decoded .= substr($value, $s);
break;
}
if($s < $q)
$decoded .= substr($value, $s, $q - $s);
$q += 2;
if(GetType($c = strpos($value, '?', $q)) != 'integer'
|| $q == $c)
return($this->SetPositionedWarning('invalid Q-encoding character set', $p + $q));
if(IsSet($charset))
{
$another_charset = strtolower(substr($value, $q, $c - $q));
if(strcmp($charset, $another_charset)
&& strcmp($another_charset, 'ascii'))
return($this->SetWarning('it is not possible to decode an encoded value using mixed character sets into a single value', $p + $q));
}
else
{
$charset = strtolower(substr($value, $q, $c - $q));
if(!strcmp($charset, 'ascii'))
$charset = null;
}
++$c;
if(GetType($t = strpos($value, '?', $c)) != 'integer'
|| $c==$t)
return($this->SetPositionedWarning('invalid Q-encoding type', $p + $c));
$type = strtolower(substr($value, $c, $t - $c));
++$t;
if(GetType($e = strpos($value, '?=', $t)) != 'integer')
return($this->SetPositionedWarning('invalid Q-encoding encoded data', $p + $e));
switch($type)
{
case 'q':
for($s = $t; $s<$e;)
{
switch($b = $value[$s])
{
case '=':
$h = HexDec($hex = strtolower(substr($value, $s + 1, 2)));
if($s + 3 > $e
|| strcmp(sprintf('%02x', $h), $hex))
return($this->SetPositionedWarning('invalid Q-encoding q encoded data', $p + $s));
$decoded .= chr($h);
$s += 3;
break;
case '_':
$decoded .= ' ';
++$s;
break;
default:
$decoded .= $b;
++$s;
}
}
break;
case 'b':
if($e <= $t
|| strlen($binary = base64_decode($data = substr($value, $t, $e - $t))) == 0
|| GetType($binary) != 'string')
return($this->SetPositionedWarning('invalid Q-encoding b encoded data', $p + $t));
$decoded .= $binary;
$s = $e;
break;
default:
return($this->SetPositionedWarning('Q-encoding '.$type.' is not yet supported', $p + $c));
}
$s += 2;
}
$value = $decoded;
$encoding = $charset;
return(1);
}
Function ParseCText(&$p, &$c_text)
{
$c_text = null;
$v = $this->v;
if($p<strlen($v)
&& GetType(strchr("\t\r\n ()\\\0", $c = $v[$p])) != 'string'
&& Ord($c)<128)
{
$c_text = $c;
++$p;
}
return(1);
}
Function ParseQText(&$p, &$q_text)
{
$q_text = null;
$v = $this->v;
if($p>strlen($v)
|| GetType(strchr("\t\r\n \"\\\0", $c = $v[$p])) == 'string')
return(1);
if(Ord($c) >= 128)
{
if(!$this->ignore_syntax_errors)
return(1);
$this->SetPositionedWarning('it was used an unencoded 8 bit character', $p);
}
$q_text = $c;
++$p;
return(1);
}
Function ParseQuotedPair(&$p, &$quoted_pair)
{
$quoted_pair = null;
$v = $this->v;
$l = strlen($v);
if($p+1 < $l
&& !strcmp($v[$p], '\\')
&& GetType(strchr("\r\n\0", $c = $v[$p + 1])) != 'string'
&& Ord($c)<128)
{
$quoted_pair = $c;
$p += 2;
}
return(1);
}
Function ParseCContent(&$p, &$c_content)
{
$c_content = null;
$c = $p;
if(!$this->ParseQuotedPair($c, $content))
return(0);
if(!IsSet($content))
{
if(!$this->ParseCText($c, $content))
return(0);
if(!IsSet($content))
{
if(!$this->ParseComment($c, $content))
return(0);
if(!IsSet($content))
return(1);
}
}
$c_content = $content;
$p = $c;
return(1);
}
Function SkipWhiteSpace(&$p)
{
$v = $this->v;
$l = strlen($v);
for(;$p<$l; ++$p)
{
switch($v[$p])
{
case ' ':
case "\n":
case "\r":
case "\t":
break;
default:
return(1);
}
}
return(1);
}
Function ParseComment(&$p, &$comment)
{
$comment = null;
$v = $this->v;
$l = strlen($v);
$c = $p;
if($c >= $l
|| strcmp($v[$c], '('))
return(1);
++$c;
for(; $c < $l;)
{
if(!$this->SkipWhiteSpace($c))
return(0);
if(!$this->ParseCContent($c, $c_content))
return(0);
if(!IsSet($c_content))
break;
}
if(!$this->SkipWhiteSpace($c))
return(0);
if($c >= $l
|| strcmp($v[$c], ')'))
return(1);
++$c;
$comment = substr($v, $p, $c - $p);
$p = $c;
return(1);
}
Function SkipCommentGetWhiteSpace(&$p, &$space)
{
$v = $this->v;
$l = strlen($v);
for($space = '';$p<$l;)
{
switch($w = $v[$p])
{
case ' ':
case "\n":
case "\r":
case "\t":
++$p;
$space .= $w;
break;
case '(':
if(!$this->ParseComment($p, $comment))
return(0);
default:
return(1);
}
}
return(1);
}
Function SkipCommentWhiteSpace(&$p)
{
$v = $this->v;
$l = strlen($v);
for(;$p<$l;)
{
switch($w = $v[$p])
{
case ' ':
case "\n":
case "\r":
case "\t":
++$p;
break;
case '(':
if(!$this->ParseComment($p, $comment))
return(0);
default:
return(1);
}
}
return(1);
}
Function ParseQContent(&$p, &$q_content)
{
$q_content = null;
$q = $p;
if(!$this->ParseQuotedPair($q, $content))
return(0);
if(!IsSet($content))
{
if(!$this->ParseQText($q, $content))
return(0);
if(!IsSet($content))
return(1);
}
$q_content = $content;
$p = $q;
return(1);
}
Function ParseAtom(&$p, &$atom, $dot)
{
$atom = null;
$v = $this->v;
$l = strlen($v);
$a = $p;
if(!$this->SkipCommentGetWhiteSpace($a, $space))
return(0);
$match = '/^([-'.($dot ? '.' : '').'A-Za-z0-9!#$&\'*+\\/=?^_{|}~]+)/';
for($s = $a;$a < $l;)
{
if(preg_match($match, substr($this->v, $a), $m))
$a += strlen($m[1]);
elseif(Ord($v[$a]) < 128)
break;
elseif(!$this->SetPositionedWarning('it was used an unencoded 8 bit character', $a))
return(0);
else
++$a;
}
if($s == $a)
return(1);
$atom = $space.substr($this->v, $s, $a - $s);
if(!$this->SkipCommentGetWhiteSpace($a, $space))
return(0);
$atom .= $space;
$p = $a;
return(1);
}
Function ParseQuotedString(&$p, &$quoted_string)
{
$quoted_string = null;
$v = $this->v;
$l = strlen($v);
$s = $p;
if(!$this->SkipCommentWhiteSpace($s))
return(0);
if($s >= $l
|| strcmp($v[$s], '"'))
return(1);
++$s;
for($string = '';$s < $l;)
{
$w = $s;
if(!$this->SkipWhiteSpace($s))
return(0);
if($w != $s)
$string .= substr($v, $w, $s - $w);
if(!$this->ParseQContent($s, $q_content))
return(0);
if(!IsSet($q_content))
break;
$string .= $q_content;
}
$w = $s;
if(!$this->SkipWhiteSpace($s))
return(0);
if($w != $s)
$string .= substr($v, $w, $s - $w);
if($s >= $l
|| strcmp($v[$s], '"'))
return(1);
++$s;
if(!$this->SkipCommentWhiteSpace($s))
return(0);
$quoted_string = $string;
$p = $s;
return(1);
}
Function ParseWord(&$p, &$word)
{
$word = null;
if(!$this->ParseQuotedString($p, $word))
return(0);
if(IsSet($word))
return(1);
if(!$this->ParseAtom($p, $word, 0))
return(0);
return(1);
}
Function ParseObsPhrase(&$p, &$obs_phrase)
{
$obs_phrase = null;
$v = $this->v;
$l = strlen($v);
$ph = $p;
if(!$this->ParseWord($ph, $word))
return(0);
$string = $word;
for(;;)
{
if(!$this->ParseWord($ph, $word))
return(0);
if(IsSet($word))
{
$string .= $word;
continue;
}
$w = $ph;
if(!$this->SkipCommentGetWhiteSpace($ph, $space))
return(0);
if($w != $ph)
{
$string .= $space;
continue;
}
if($ph >= $l
|| strcmp($v[$ph], '.'))
break;
$string .= '.';
++$ph;
}
$obs_phrase = $string;
$p = $ph;
return(1);
}
Function ParsePhrase(&$p, &$phrase)
{
$phrase = null;
if(!$this->ParseObsPhrase($p, $phrase))
return(0);
if(IsSet($phrase))
return(1);
$ph = $p;
if(!$this->ParseWord($ph, $word))
return(0);
$string = $word;
for(;;)
{
if(!$this->ParseWord($ph, $word))
return(0);
if(!IsSet($word))
break;
$string .= $word;
}
$phrase = $string;
$p = $ph;
return(1);
}
Function ParseAddrSpec(&$p, &$addr_spec)
{
$addr_spec = null;
$v = $this->v;
$l = strlen($v);
$a = $p;
if(!$this->ParseQuotedString($a, $local_part))
return(0);
if(!IsSet($local_part))
{
if(!$this->ParseAtom($a, $local_part, 1))
return(0);
$local_part = trim($local_part);
}
if($a >= $l
|| strcmp($v[$a], '@'))
return(1);
++$a;
if(!$this->ParseAtom($a, $domain, 1))
return(0);
if(!IsSet($domain))
return(1);
$addr_spec = $local_part.'@'.trim($domain);
$p = $a;
return(1);
}
Function ParseAngleAddr(&$p, &$addr)
{
$addr = null;
$v = $this->v;
$l = strlen($v);
$a = $p;
if(!$this->SkipCommentWhiteSpace($a))
return(0);
if($a >= $l
|| strcmp($v[$a], '<'))
return(1);
++$a;
if(!$this->ParseAddrSpec($a, $addr_spec))
return(0);
if($a >= $l
|| strcmp($v[$a], '>'))
return(1);
++$a;
if(!$this->SkipCommentWhiteSpace($a))
return(0);
$addr = $addr_spec;
$p = $a;
return(1);
}
Function ParseName(&$p, &$address)
{
$address = null;
$a = $p;
if(!$this->ParsePhrase($a, $display_name))
return(0);
if(IsSet($display_name))
{
if(!$this->QDecode($p, $display_name, $encoding))
return(0);
$address['name'] = trim($display_name);
if(IsSet($encoding))
$address['encoding'] = $encoding;
}
$p = $a;
return(1);
}
Function ParseNameAddr(&$p, &$address)
{
$address = null;
$a = $p;
if(!$this->ParsePhrase($a, $display_name))
return(0);
if(!$this->ParseAngleAddr($a, $addr))
return(0);
if(!IsSet($addr))
return(1);
$address = array('address'=>$addr);
if(IsSet($display_name))
{
if(!$this->QDecode($p, $display_name, $encoding))
return(0);
$address['name'] = trim($display_name);
if(IsSet($encoding))
$address['encoding'] = $encoding;
}
$p = $a;
return(1);
}
Function ParseAddrNameAddr(&$p, &$address)
{
$address = null;
$a = $p;
if(!$this->ParseAddrSpec($a, $display_name))
return(0);
if(!IsSet($display_name))
return(1);
if(!$this->ParseAngleAddr($a, $addr))
return(0);
if(!IsSet($addr))
return(1);
if(!$this->QDecode($p, $display_name, $encoding))
return(0);
$address = array(
'address'=>$addr,
'name' => trim($display_name)
);
if(IsSet($encoding))
$address['encoding'] = $encoding;
$p = $a;
return(1);
}
Function ParseMailbox(&$p, &$address)
{
$address = null;
if($this->ignore_syntax_errors)
{
$a = $p;
if(!$this->ParseAddrNameAddr($p, $address))
return(0);
if(IsSet($address))
return($this->SetPositionedWarning('it was specified an unquoted address as name', $a));
}
if(!$this->ParseNameAddr($p, $address))
return(0);
if(IsSet($address))
return(1);
if(!$this->ParseAddrSpec($p, $addr_spec))
return(0);
if(IsSet($addr_spec))
{
$address = array('address'=>$addr_spec);
return(1);
}
$a = $p;
if($this->ignore_syntax_errors
&& $this->ParseName($p, $address)
&& IsSet($address))
return($this->SetPositionedWarning('it was specified a name without an address', $a));
return(1);
}
Function ParseMailboxGroup(&$p, &$mailbox_group)
{
$v = $this->v;
$l = strlen($v);
$g = $p;
if(!$this->ParseMailbox($g, $address))
return(0);
if(!IsSet($address))
return(1);
$addresses = array($address);
for(;$g < $l;)
{
if(strcmp($v[$g], ','))
break;
++$g;
if(!$this->ParseMailbox($g, $address))
return(0);
if(!IsSet($address))
return(1);
$addresses[] = $address;
}
$mailbox_group = $addresses;
$p = $g;
return(1);
}
Function ParseGroup(&$p, &$address)
{
$address = null;
$v = $this->v;
$l = strlen($v);
$g = $p;
if(!$this->ParsePhrase($g, $display_name))
return(0);
if(!IsSet($display_name)
|| $g >= $l
|| strcmp($v[$g], ':'))
return(1);
++$g;
if(!$this->ParseMailboxGroup($g, $mailbox_group))
return(0);
if(!IsSet($mailbox_group))
{
if(!$this->SkipCommentWhiteSpace($g))
return(0);
$mailbox_group = array();
}
if($g >= $l
|| strcmp($v[$g], ';'))
return(1);
$c = ++$g;
if($this->SkipCommentWhiteSpace($g)
&& $g > $c
&& !$this->SetPositionedWarning('it were used invalid comments after a group of addresses', $c))
return(0);
if(!$this->QDecode($p, $display_name, $encoding))
return(0);
$address = array(
'name'=>$display_name,
'group'=>$mailbox_group
);
if(IsSet($encoding))
$address['encoding'] = $encoding;
$p = $g;
return(1);
}
Function ParseAddress(&$p, &$address)
{
$address = null;
if(!$this->ParseGroup($p, $address))
return(0);
if(!IsSet($address))
{
if(!$this->ParseMailbox($p, $address))
return(0);
}
return(1);
}
/* Public functions */
/*
{metadocument}
<function>
<name>ParseAddressList</name>
<type>BOOLEAN</type>
<documentation>
<purpose>Parse and extract e-mail addresses eventually from headers
of an e-mail message.</purpose>
<usage>Pass a string value with a list of e-mail addresses to the
<argumentlink>
<function>ParseAddressList</function>
<argument>value</argument>
</argumentlink>. The <argumentlink>
<function>ParseAddressList</function>
<argument>addresses</argument>
</argumentlink> returns the list of e-mail addresses found.</usage>
<returnvalue>This function returns <booleanvalue>1</booleanvalue> if
the specified value is parsed successfully. Otherwise,
check the variables <variablelink>error</variablelink> and
<variablelink>error_position</variablelink> to determine what
error occurred and the relevant value position.</returnvalue>
</documentation>
<argument>
<name>value</name>
<type>STRING</type>
<documentation>
<purpose>String with a list of e-mail addresses to parse.</purpose>
</documentation>
</argument>
<argument>
<name>addresses</name>
<type>ARRAY</type>
<out />
<documentation>
<purpose>Return the list of parsed e-mail addresses.
Each entry in the list is an associative array.<paragraphbreak />
For normal addresses, this associative array has the entry
<stringvalue>address</stringvalue> set to the e-mail address.
If the address has an associated name, it is stored in the
entry <stringvalue>name</stringvalue>.<paragraphbreak />
For address groups, there is the entry
<stringvalue>name</stringvalue>.
The group addresses list are stored in the entry
<stringvalue>group</stringvalue> as an array. The structure of
the group addresses list array is the same as this addresses
list array argument.</purpose>
</documentation>
</argument>
<do>
{/metadocument}
*/
Function ParseAddressList($value, &$addresses)
{
$this->warnings = array();
$addresses = array();
$this->v = $v = $value;
$l = strlen($v);
$p = 0;
if(!$this->ParseAddress($p, $address))
return(0);
if(!IsSet($address))
return($this->SetPositionedError('it was not specified a valid address', $p));
$addresses[] = $address;
while($p < $l)
{
if(strcmp($v[$p], ',')
&& !$this->SetPositionedWarning('multiple addresses must be separated by commas: ', $p))
return(0);
++$p;
if(!$this->ParseAddress($p, $address))
return(0);
if(!IsSet($address))
return($this->SetPositionedError('it was not specified a valid address after comma', $p));
$addresses[] = $address;
}
return(1);
}
/*
{metadocument}
</do>
</function>
{/metadocument}
*/
};
/*
{metadocument}
</class>
{/metadocument}
*/
?>