// -------------------------------------------------------------
// Publié par The Maxg Network, http://maxg.info
// -------------------------------------------------------------
// De l'aide sur ces 3 classe peut etre trouvée sur
// http://forum.maxg.info (phpWebmail)
// NOTE: 750 lignes de codes, séparez plutot en 3 classes !
// -------------------------------------------------------------
/*
Exemple :
=========
// Création de la classe, l'URL sert aux images "inline",
// le timestamp au format de date, le 1 pour afficher en HTML,
// 150 le wordwrap et 0 pour tout afficher
$mime = new MiMail("/scripts/mail.php", "d/m/Y h:i", 1, 150, 0);
// Création de l'objet POP3
$POP3 = new POP3;
// Création d'un objet SMTP
$SMTP = new SMTP;
// Connection au serveur localhost
$con_smtp = $SMTP->smtp_connect();
// Connection au serveur POP3 localhost sur le port 110
$socket = $POP3->pop_connect("localhost",110);
$POP3->pop_login($socket,"*** USER ***","*** PASSWORD ***");
// Annalyse du message 1 sur le serveur
$mime->parse_mail($POP3, $socket, 1);
// Affichage de toutes les infos sur le message
print_r($mail);
// Affichage du message analysé
print("
".$mime->fullbody);
// Envoi d'un message
$SMTP->smtp_mail($con_smtp, '"Moi" ', Array('"toi" '), "Salut", "", "Salut !!");
?>
*/
/**
* SMTP Class v1.3
* Send mails to SMTP server easly and fastly
* Go to http://forum.maxg.info for help & info
*/
class SMTP
{
var $t = Array();
var $v = "1.3";
/**
* SMTP connect function
*
* @Access public
* @Param str $str_Host = SMTP Server
* @Param int $int_Port = SMTP Port
* @Param str $str_Sendscript = Banner to send on connect
* @Param int $int_Auth = User SMTP Authentification
* @Param str $str_User = SMTP User for the auth (no encoding)
* @Param str $str_Pass = SMTP Password for the auth (no encoding)
* @Return obj $fp = Connection pointer for the commands
*/
function smtp_connect($str_Host="localhost",$int_Port=25,$str_Sendscript="phpsmtplib@maxg.info",$int_Auth=0,$str_User="",$str_Pass="")
{
$fp = fsockopen ("$str_Host", $int_Port, $int_Err, $str_Err,1);
if (!$fp)
{
die("Impossible de se connecter au serveur $str_host :
$int_Err : $str_Err
");
}
$this->t[] = "<- ".fgets ($fp, 200);
$qu = "HELO $str_Sendscript\r\n";
fputs ($fp, $qu, strlen($qu));
$this->t[] = "-> ".trim($qu);
$this->t[] = "<- ".fgets($fp,100);
$qu = "NOOP Librairie PHP-SMTP ".$this->v." (http://www.maxg.info)\r\n";
fputs ($fp, $qu, strlen($qu));
$this->t[] = "-> ".trim($qu);
$this->t[] = "<- ".fgets($fp,100);
if($int_Auth==1)
{
$qu = "AUTH LOGIN\r\n";
fputs ($fp, $qu, strlen($qu));
$this->t[] = "-> ".trim($qu);
$this->t[] = "<- ".fgets($fp, 200);
fputs ($fp, base64_encode($str_User)."\r\n");
$this->t[] = "<- ".fgets($fp, 200);
fputs ($fp, base64_encode($str_Pass)."\r\n");
$this->t[] = "<- ".fgets($fp, 200);
}
return $fp;
}
/**
* SMTP Mail function - send a SMTP mail
*
* @Access public
* @Param obj $fp = Connection pointer
* @Param str $str_From = Mail sender (format = )
* @Param arr $ar_To = Array of the recipients (format = ["Name" ] )
* @Param str $str_Subject = Subject of the mail
* @Param str $str_Headers = Headers to add (Mime-Version,X-Priority...)
* @Param str $str_Body = Body of your mail (can be MIME multipart)
* @Return bool = Success if true, Fail if false
*/
function smtp_mail ($fp, $str_From, $ar_To, $str_Subject, $str_Headers, $str_Body)
{
if (!$fp) return FALSE;
$qu = "MAIL FROM:<$str_From>\r\n";
fputs($fp, $qu, strlen($qu));
$this->t[] = "-> ".trim($qu);
$this->t[] = "<- ".fgets($fp,100);
for($z=0;isset($ar_To[$z]);$z++){
$qu = "RCPT TO:".trim($ar_To[$z])."\r\n";
fputs($fp, $qu);
$this->t[] = "-> ".trim($qu);
$this->t[] = "<- ".fgets($fp,300);
}
$all = implode("; ",$ar_To);
$qu = "DATA\r\n";
fputs($fp, $qu, strlen($qu));
$this->t[] = "-> ".trim($qu);
$this->t[] = "<- ".fgets($fp, 100);
$qu = "From: $str_From\r\n";
fputs($fp, $qu, strlen($qu));
if ($str_Headers)
{
$qu = $str_Headers;
fputs($fp, $qu, strlen($qu));
}
$qu = "To: $all\r\nSubject: $str_Subject\r\n";
fputs($fp, $qu, strlen($qu));
$qu = "\r\n$str_Body\r\n";
fputs($fp, $qu, strlen($qu));
$qu = "\r\n.\r\n";
fputs($fp, $qu, strlen($qu)); usleep(10);
$this->t[] = "<- ".fgets($fp, 100);
$qu = "RSET\r\n";
fputs ($fp, $qu, strlen($qu));
$this->t[] = "-> ".trim($qu);
$this->t[] = "<- ".fgets($fp, 100);
return TRUE;
}
/**
* SMTP Close function
*
* @Param obj $fp = Connection pointer
* @Return arr = IN/OUT commands (begins with "<-" / "->" means IN/OUT)
*/
function smtp_close ($fp)
{
if (!$fp) return 0;
$qu = "QUIT\r\n";
fputs($fp,$qu,strlen($qu));
$this->t[] = "-> ".trim($qu);
$this->t[] = "<- ".fgets($fp,200);
fclose($fp);
return $this->t;
}
}
// Classe POP3
class POP3
{
var $totaille = 0 ; // Total Mailbox size
var $banner = ""; // POP3 banner
var $dial = Array(); // Server replies
var $sizes = Array(); // Messages sizes
/**
* Connects to server
*
* @Param str &$str_Host = POP3 server to connect
* @Param int &$int_Port = Port to connect (110 by default)
* @Return {
* Succes: obj $soket = Connection object for the server
* Error : bol FALSE = Unable to connect.
* also create $int_Error (err num) and $str_Err
* Note : str $POP3->banner is created (POP3 banner)
* }
*/
function pop_connect($str_Host = "localhost", $int_Port = 110)
{
$soket = fsockopen ($str_Host, $int_Port, $int_Err, $str_Err);
if (!$soket)
{
global $str_Err,$int_Err;
return FALSE;
}
$this->banner = ($this->dial[]=fgets($soket,100));
return $soket;
}
/**
* Login
*
* @Param obj $soket = Connection pointer created with pop_connect()
* @Param str $str_User = Username to send
* @Param str $str_Pass = Password to send
* @Return {
* Succes: int $int_nMsg = Number of messages on the mailbox
* Error : -1 = invalid connection pointer
* -2 = invalid username
* -3 = bad password
* -4 = STAT failed
* Note : int $POP3->totaille is created (Mailbox size)
* }
*/
function pop_login($soket, $str_User, $str_Pass)
{
if (!$soket)
{
return -1;
}
$flag = md5(trim(substr($this->banner,strpos($this->banner,"<"),strlen($this->banner)-1)).$str_Pass);
$qu = "APOP $str_User $flag\r\n";
fputs($soket,$qu);
$this->dial[] = "-> ".$qu;
if(eregi("-ERR",($this->dial[]="<- ".fgets($soket,100))))
{
$qu = "USER $str_User\r\n";
fputs($soket,$qu);
$this->dial[] = "-> ".$qu;
if(eregi("-ERR",($this->dial[]="<- ".fgets($soket,100))))
return -2;
$qu = "PASS $str_Pass\r\n";
fputs($soket,$qu);
$this->dial[] = "-> ".str_replace($str_Pass,"[Pass]",$qu);
if(eregi("-ERR",($this->dial[]="<- ".fgets($soket,100))))
return -3;
}
$qu = "STAT\r\n";
fputs($soket, $qu);
$this->dial[] = "-> ".$qu;
$r = split(" ",($this->dial[]="<- ".fgets($soket,100)));
if($r[1]=="-ERR") return -4;
$this->totaille = $r[3];
$qu = "NOOP POP3 Librairie, dev.maxg.info\r\n";
fputs($soket,$qu);
$this->dial[]="-> ".$qu;
$this->dial[]="<- ".fgets($soket,200);
return $r[2];
}
/**
* Remove a message from a mailbox
*
* @Param obj $soket = Connection pointer
* @Param int $int_Msgid = Message number
* @Return {
* Succes: TRUE;
* Error : -1 : Bad connection pointer
* -2 : Unable to delete
* }
*/
function pop_delete($soket, $int_Msgid)
{
if (!$soket)
{
return -1;
}
$qu = "DELE $int_Msgid\r\n";
fputs($soket, $qu);
$this->dial[] = "-> ".$qu;
if(substr(($this->dial[]="<- ".fgets($soket,100)),3,6)=="+OK")
return true;
else return -2;
}
/**
* Gets message list
*
* @Param obj $soket = Connection pointer
* @Param int $count = Number of mails (see pop_login())
* @Return {
* Succes: Array of mails
* Error : -1 : bad connection pointer
* -2 : LIST command failed
* }
*/
function pop_list($soket,$count)
{
if (!$soket)
{
return -1;
}
$qu = "LIST\r\n";
fputs($soket,$qu);
$this->dial[] = "-> ".$qu;
if(substr(($this->dial[]="<- ".fgets($soket,200)),3,7)=="-ERR") return -2;
$rep[0] = $count;
for($j=1;$j<=$count; $j++)
{
if($j > $count) { break; }
$line = fgets($soket,500);
$line = split(" ",$line);
if($line[0] != $j)
{
$rep[$j] = "deleted";
}
else
{
$rep[$j] = $line[1];
}
unset($line);
}
$this->sizes=$rep;
return $rep;
}
/**
* Gets the headers of a mail
*
* @Param obj $soket = Connection pointer
* @Param int $int_Id = Message ID (see pop_list())
* @Return {
* Succes: Array of the headers
* Error : -1 : bad connection pointer
* -2 : TOP command failed
* }
*/
function pop_top($soket, $int_Id)
{
if (!$soket) return -1;
$all=""; $qu = "TOP $int_Id 0\r\n";
fputs($soket, $qu);
$this->dial[] = "-> ".$qu;
if(eregi("-ERR", ($this->dial[]="<- ".fgets($soket, 200))))
return -2;
while (!ereg("^\.\r\n",$line=fgets($soket,200)))
{
if(!trim($line)) continue;
$all.=$line;
if($line[0]!="\t"){
if($data&&$hname){
$ar_Headers[strtolower($hname)].=$data;
}
$line=explode(":",$line,2);
$hname = trim($line[0]);
$data = trim($line[1]);
}else{
$data.="\r\n\t".trim($line);
}
}
if($data&&$hname)
$ar_Headers["$hname"]=$data;
$ar_Headers["all"] = $all;
return $ar_Headers;
}
/**
* Download a mail
*
* @Param obj $soket = Connection pointer
* @Param int $int_Id = Message ID (see pop_list())
* @Return {
* Succes: Array of the lines
* Error : -1 : bad connection pointer
* -2 : RETR command failed
* }
*/
function pop_get($soket, $int_Id)
{
if (!$soket) return -1;
$qu = "TOP $int_Id 9999\r\n";
fputs($soket,$qu);
$this->dial[] = "-> ".$qu;
if(eregi("-ERR",($this->dial[]="<- ".fgets($soket, 200)))) return -2;
$line = fgets($soket, 2000);
for($h=0; !ereg("^\.\r\n",$line); $h++)
{
$ar_Rep[$h] = $line;
$line = fgets($soket,2000);
if(empty($line)){ break; }
}
return $ar_Rep;
}
/**
* Close connection to server
*
* @Param obj $soket = Connection pointer
* @Return {
* Succes: TRUE
* Error : -1 : bad connection pointer
* Note : $POP3->dial is created (the server dialog)
* }
*/
function pop_quit($soket)
{
if(!$soket) return -1;
$qu = "QUIT\r\n";
fputs($soket,$qu);
$this->dial[]="-> ".$qu;
$this->dial[]="<- ".fgets($soket,200);
fclose($soket);
return TRUE;
}
}
/*
* Mail Parsing Function *
=========================================================
Il est beaucoup plus difficile que l'on pourrait
le croire d'annalyser les en-têtes d'un message.
Cette fonction doit pouvoir le faire sans IMAP !
A noter que la classe fait 300 lignes PILE (huhu :-)
==========================================================
TODO : explicitement indiqué (## NOT IMPLEMENTD ##)
ADD : Gestion de IMAP 4 possible mais sans utilisation
de la librairie de PHP...
===========================================================
Id:$parse_mail.inc.php;class£04/03§bouchon@maxg.info$
Todo:$addlines:#{12,13,14,}|trim(@each)$
===========================================================
*/
class MiMail
{
var $fromname = "", // Nom expéditeur
$fromaddr = "", // Adresse expéditeur
$to = "", // Nom Destinataire
$date = "", // Date d'envoi
$subject = "", // Sujet
$priority = 3, // Priorité du message
$size = "", // Taille du mail
$unit = "", // Unités (By,Kb,Mb)
$ctype = "", // Type de contenu
$reply_to = "", // Adresse de réponse
$decodeattach = 0, // Annalyse ou pas des pièces (performances)
$attachments = Array(), // Pièces jointes
$mime = 0, // Message multipart ou simple ?
$otherversion = 0, // Si le message comporte une version texte.
$body = "", // Texte du message
$fullbody = ""; // Message intégral non décodé
var $timestamp = "", // Format de date
$html = 1, // Préférer le HTML au texte (alternative)
$wordwrap = 150, // Mots par ligne
$url = ""; // URL du script
// Fonction de construction
function MiMail($url, $timestamp = "d/m/Y h:i", $html = 1, $ww=150, $decatt=0 ){
$this->timestamp = $timestamp; $this->wordwrap=$ww;
$this->html = $html; $this->url=$url; $this->decodeattach=$decatt;
}
function decode_mime($string) {
if(($pos = strpos($string,"=?")) === false) return $string;
while(!($pos === false)) {
$newresult .= substr($string,0,$pos);
$string = substr($string,$pos+2,strlen($string));
$intpos = strpos($string,"?");
$charset = substr($string,0,$intpos);
$enctype = strtolower(substr($string,$intpos+1,1));
$string = substr($string,$intpos+3,strlen($string));
$endpos = strpos($string,"?=");
$mystring = substr($string,0,$endpos);
$string = substr($string,$endpos+2,strlen($string));
if($enctype == "q") {
$mystring = $this->decode_quoted_printable($mystring);
} else if ($enctype == "b")
$mystring = $this->decode_base64($mystring);
$newresult .= $mystring;
$pos = strpos($string,"=?");
}
return $newresult.$string;
}
function decode_quoted_printable($body){
$body = preg_replace("/=([0-9A-F]{2})/e","chr(hexdec('\\1'))",$body);
return str_replace("=\r\n","",$body);
}
function decode_base64($body){
$body = eregi_replace("\r|\n","",$body);
return base64_decode($body);
}
function decode_7bits($body){
## Not implemented ##
return $body;
}
function decode_8bits($body){
## Not implemented ##
return $body;
}
function decode_encoding($headers,$body){
$cte = "Content-Transfer-Encoding:";
if(eregi("$cte base64",$headers))
$body = $this->decode_base64($body);
elseif(eregi("$cte 8bits",$headers))
$body = $this->decode_8bits($body);
elseif(eregi("$cte 7bits",$headers))
$body = $this->decode_7bits($body);
else $body = $this->decode_quoted_printable($body);
return $body;
}
function decode_disposition($headers,$ctype){
$in=eregi("inline",$headers);
if($in||($ctype=="alternative")) $dispo="i";
elseif(eregi("attachment",$headers)) $dispo="a";
else{
switch($ctype){
case "related": $dispo="i"; break;
case "mixed": $dispo="i"; break;
default: $dispo="a";
}
}
if(eregi('name="',$headers)){
$fname=substr($headers,strpos($headers,'name="')+6,50);
$fname=substr($fname,0,strpos($fname,'"'));
$id=substr($headers,strpos($headers,"Content-ID: <")+13,50);
$id=substr($id,0,strpos($id,">"));
}else{ $fname=""; $id="";}
return Array($dispo,$fname,$id);
}
/**
* Analyse du content-type
* Permet d'analyser récursivement les parties de message
*
* @Param str $tag = Tag ou headers entiers à analyser
*
* @Return {
* Array(
* str $ctype = Content-Type, ou si multipart
* retourne le type de multipart,
* str $bound = Retourne le Boundary si présent
* }
*/
function content_type($tag){
if(!trim($tag)) return Array("text/plain","");
if(eregi('multipart',$tag)){
$ctype = eregi_replace("(.*)multipart/([a-z]*)(.*)","\\2",$tag);
if(!$ctype) $ctype="related";
$bound = substr($tag,strpos($tag,'boundary="')+10,strlen($tag));
$bound = substr($bound,0,strpos($bound,'"'));
}else{
$ctype = substr($tag,strpos($tag,"Content-Type: ")+14,35);
$ctype = trim(eregi_replace("(([a-z]|-)*/([a-z]|-)*)(.*)","\\1",$ctype));
$bound = "";
}
return(Array($ctype,$bound));
}
/**
* Analyse de structur d'un message
* Cette fonction récursive a été de loin la plus chiante à faire :
* imaginez que j'ai du lire 30 RFC (si si !) alors que je n'en
* respecte que la moitié ... HYPER HARD
*
* Pour ceux qui ont le malheur de lire ceci, je peux les consoler en disant
* que je faire une classe pour IMAP4 -- ARGGG !
* Rien qu'à voir le nombre de commandes on peut se suicider !!
*
* Je tiens à ajouter que cette classe est plutot optimisée, et que la plupart
* des messages compatibles RFC sont correctement analysés.
*
* @Param str $msg = Message à analyser
* @Param str $bound = Boundary du Content-Type
* @Param str $dtype = Type de multipart (alternative, mixed, related)
* @Param str $tenc = Type d'encodage
*
* @Return {
* None
* }
*/
function fetch_structure( $msg, $bound, $dtype, $tenc = "7bits" ){
if($bound){
if(!eregi($bound,$msg)) return $msg;
$parts = spliti("(\-*)$bound(--)?\r\n",$msg);
foreach($parts as $p){
if(!eregi("content-type",$p)) continue;
$headers = substr($p,0,strpos($p,"\r\n\r\n"));
list($ctype,$bound)=$this->content_type($headers);
$body = substr($p,strpos($p,"\r\n\r\n")+4,strlen($p));
if($bound) $this->fetch_structure($body,$bound,$ctype);
else{
list($dispo,$fname,$id)=$this->decode_disposition($headers,$dtype);
if($dtype=="alternative"){
if($this->html){
if($ctype=="text/html") $disp=1; else{ $disp=0; $this->otherversion=1;}
}else{
if($ctype=="text/plain") $disp=1; else{ $disp=0; $this->otherversion=1;}
}
}else{
if($dispo=="i") $disp=1; else $disp=0;
}
if($disp){
if($ctype=="text/plain") $this->body.="".$this->decode_encoding($headers,$body)."
";
else $this->body.=$this->decode_encoding($headers,$body);
}
else{
if($id) $index = $id; else $index = count($this->attachments)+1;
if($fname)
$this->attachments[$index]=Array($fname,($this->decodeattach?$this->decode_encoding($headers,$body):"[NOT PARSED]"),$ctype);
}
}
}
}else{
$this->body.=wordwrap($this->decode_encoding("Content-Transfert-Encoding: ".$tenc,$msg),$this->wordwrap);
}
}
/**
* Analyse totale d'un message
* Supporte pas trop mal les différents types de mail
* On dirait pas comme ca mais c'est C-H-I-A-N-T
*
* @Param obj $POP3 = Objet créé par la classe pop3
* @Param ptr $soket = Pointeur de connection
* @Param int $msgid = ID du message à annalyser
*
* @Return {
* int TRUE on succes.
* }
*/
function parse_mail($POP3,$soket,$msgid,$justheaders=0)
{
$mail = $POP3->pop_top($soket, $msgid);
if($mail<0) return(-1);
$to = $this->decode_mime($mail['to']);
$this->to = htmlentities(trim($to));
$this->priority = $mail['x-priority'];
$sujet = $mail[subject];
$this->subject = trim($this->decode_mime($sujet));
$from = $mail[from];
if($imap_ok){
$tmpfrom = imap_mime_header_decode ( $from );
for($r=0,$from="";$tmpfrom[$r];$r++)
$from .= $tmpfrom[$r]->text;
}else{
$from = $this->decode_mime($from);
}
if(ereg("<",$from)){
$froms = explode('<',$from,2);
$froms[0]=str_replace('"',"",trim($froms[0]));
$froms[1]=substr(trim($froms[1]),0,strlen(trim($froms[1]))-1);
$from = $froms[0]; $addr_from = $froms[1];
}else{
$addr_from=$from; $from="";
}
if(!$addr_from)
$addr_from=substr($mail['return-path'],1,strlen($mail['eturn-path'])-1);
$this->fromname = trim($from); $this->fromaddr = trim($addr_from);
$this->reply_to=($mail['return-path']?$mail['return-path']:$addr_from);
$this->date=@date($this->timestamp,@strtotime($mail['date']));
$this->mime=(@eregi("multipart",$mail['content-type'])?1:0);
if($justheaders) return(1);
$ar_Msg=$POP3->pop_get($soket, $msgid);
$this->fullbody = implode('',$ar_Msg);
$size = strlen(implode('',$ar_Msg));
$un = Array("By","Kb","Mb","Gb","Tb");
for($u=0;$size>=1024;$u++) $size=round($size/1024,1);
$this->size=$size; $this->unit=$un[$u];
for($i=0,$ok=0;isset($ar_Msg[$i]); $i++){
$k = $ar_Msg[$i];if($k=="\r\n") $ok=1;
if($ok) $body .= $k;
}
list($ctype,$bound)=$this->content_type("Content-Type: ".$mail['content-type']);
$this->ctype = $ctype;
$this->fetch_structure($body,$bound,$ctype,$mail['content-transfert-encoding']);
if($this->html){
foreach($this->attachments as $k=>$v){
$this->body = str_replace("cid:$k",$this->url."&ac=read&msgNum=$msgid&attach=$k&name=".$v[0],$this->body);
}
}
}}
?>