用PHP實現POP3郵件的解碼(三)_PHP

來源:互聯網
上載者:User
關鍵字 郵件 實現 content if reg 本文 ec
POP3

實現 MIME 解碼的類
(作者:陳俊清 2000年10月24日 15:11)

  一個實現 MIME 解碼的類

  該類實現解碼的方法是 decode($head=null,$body=null,$content_num=-1),為了處理上的方便,要求輸入的是兩個字元數組,在我們的上篇中,所用到的POP類所收取得到的就是兩個這樣的數組,一個是郵件標頭內容,一個是郵件的本文內容。限於篇幅,不對其做詳細的說明,其實現思想跟本文上篇中所介紹的POP類類似。請參考其中的注釋。

  該類中用到了大量的Regex的操作,對此不熟悉的讀者,請參考Regex的有關資料。

  class decode_mail

  {

  var $from_name;var $to_name;var $mail_time;var $from_mail;var $to_mail;

  var $reply_to;var $cc_to;var $subject;

  // 解碼後的郵件標頭部分的資訊:

  var $body;

  // 解碼後得到的本文資料,為一個數組。

  var $body_type; // 本文類型

  var $tem_num=0;

  var $get_content_num=0;

  var $body_temp=array();

  var $body_code_type;

  var $boundary;

  // 以上是一些方法中用到的一些全域性的臨時變數,由於 PHP不能做到良好的封裝,所以只能放在這裡定義

  var $err_str; // 錯誤資訊

  var $debug=0; // 調試標記

  var $month_num=array("Jan"=>1,"Feb"=>2,"Mar"=>3,"Apr"=>4,"May"=>5,"Jun"=>6,"Jul"=>7,

  "Aug"=>8,"Sep"=>9,"Oct"=>10,"Nov"=>11,"Dec"=>12); // 把英文月份轉換成數字表示的月份

  function decode($head=null,$body=null,$content_num=-1) // 調用的主方法,$head 與 $body 是兩個數組,$content_num 表示的是當本文有多個部分的時候,只取出指定部分的內容以提高效率,預設為 -1 ,表示解碼全部內容,如果解碼成功,該 方法返回 true

  {

   if (!$head and !$body)

   {

   $this->err_str="沒有指定郵件的頭與內容!!";

   return false;

   }

  if (gettype($head)=="array")

   {

   $have_decode=true;

   $this->decode_head($head);

   }

  if (gettype($body)=="array")

   {

   $this->get_content_num=$content_num;

   $this->body_temp=$body;

   $have_decode=true;

   $this->decode_body();

   unset($this->body_temp);

   }

  if (!$have_decode)

   {

   $this->err_str="傳遞的參數不對,用法:new decode_mail(head,body) 兩個參數都是數組";

   return false;

   }

  }

  function decode_head($head) // 郵件標頭內容 的解碼,取出郵件標頭中有意義的內容

  {

   $i=0;

   $this->from_name=$this->to_name=$this->mail_time=$this->from_mail=$this->

   to_mail=$this->reply_to=$this->cc_to=$this->subject="";

   $this->body_type=$Sthis->boundary=$this->body_code_type="";

   while ($head[$i])

   {

   if (strpos($head[$i],"=?"))

   $head[$i]=$this->decode_mime($head[$i]);  //如果有編碼的內容,則進行解碼,解碼函數是上文所介紹的 decode_mime()

   $pos=strpos($head[$i],":");

   $summ=substr($head[$i],0,$pos);

   $content=substr($head[$i],$pos+1);  //將郵件標頭資訊的標識與內容分開

   if ($this->debug) echo $summ.":----:".$content."
";

   switch (strtoupper($summ))

   {

   case "FROM": // 寄件者地址及姓名(可能沒有姓名,只有地址資訊)

   if ($left_tag_pos=strpos($content,"<"))

   {

   $mail_lenth=strrpos($content,">")-$left_tag_pos-1;

   $this->from_name=substr($content,0,$left_tag_pos);

   $this->from_mail=substr($content,$left_tag_pos+1,$mail_lenth);

   if (trim($this->from_name)=="") $this->from_name=$this->from_mail;

   else

   if (ereg("[\"|\']([^\'\"]+)[\'|\"]",$this->from_name,$reg))

   $this->from_name=$reg[1];

   }

   else

   {

   $this->from_name=$content;

   $this->from_mail=$content;

   //沒有寄件者的郵件地址

   }

   break;

   case "TO": //收件者地址及姓名(可能 沒有姓名)

   if ($left_tag_pos=strpos($content,"<"))

   {

   $mail_lenth=strrpos($content,">")-$left_tag_pos-1;

   $this->to_name=substr($content,0,$left_tag_pos);

   $this->to_mail=substr($content,$left_tag_pos+1,$mail_lenth);

   if (trim($this->to_name)=="") $this->to_name=$this->to_mail;

   else

   if (ereg("[\"|\']([^\'\"]+)[\'|\"]",$this->to_name,$reg))

   $this->to_name=$reg[1];

   }

   else

   {

   $this->to_name=$content;

   $this->to_mail=$content;

   //沒有分開收件者的郵件地址

   }

   break;

   case "DATE" : //發送日期,為了處理方便,這裡返回的是一個 Unix 時間戳記,可以用 date("Y-m-d",$this->mail_time) 來得到一般格式的日期

   $content=trim($content);

   $day=strtok($content," ");

   $day=substr($day,0,strlen($day)-1);

   $date=strtok(" ");

   $month=$this->month_num[strtok(" ")];

   $year=strtok(" ");

   $time=strtok(" ");

   $time=split(":",$time);

   $this->mail_time=mktime($time[0],$time[1],$time[2],$month,$date,$year);

   break;

   case "SUBJECT":  //郵件主題

   $this->subject=$content;

   break;

   case "REPLY_TO": // 回複地址(可能沒有)

   if (ereg("<([^>]+)>",$content,$reg))

   $this->reply_to=$reg[1];

   else $this->reply_to=$content;

   break;

   case "CONTENT-TYPE": // 整個郵件的 Content類型, eregi("([^;]*);",$content,$reg);

   $this->body_type=trim($reg[1]);

   if (eregi("multipart",$content)) // 如果是 multipart 類型,取得 分隔字元

   {

   while (!eregi('boundary=\"(.*)\"',$head[$i],$reg) and $head[$i])

   $i++;

   $this->boundary=$reg[1];

   }

   else //對於一般的本文類型,直接取得其編碼方法

   {

   while (!eregi("charset=[\"|\'](.*)[\'|\"]",$head[$i],$reg))

   $i++;

   $this->body_char_set=$reg[1];

   while (!eregi("Content-Transfer-Encoding:(.*)",$head[$i],$reg))

   $i++;

   $this->body_code_type=trim($reg[1]);

   }

   break;

   case "CC": //抄送到。。

   if (ereg("<([^>]+)>",$content,$reg))

   $this->cc_to=$reg[1];

   else

   $this->cc_to=$content;

   default:

   break;

   } // end switch

  

   $i++;

   } // end while

  

   if (trim($this->reply_to)=="")  //如果沒有指定回複地址,則回複地址為發送人地址

   $this->reply_to=$this->from_mail;

  }// end function define

  function decode_body() //本文的解碼,其中用到了不少郵件標頭解碼所得來的資訊

  {

  $i=0;

  if (!eregi("multipart",$this->body_type)) // 如果不是複合類型,可以直接解碼

   {

   $tem_body=implode($this->body_temp,"\r\n");

   switch (strtolower($this->body_code_type)) // body_code_type ,本文的編碼方式,由郵件標頭資訊中取得

   {case "base64":

   $tem_body=base64_decode($tem_body);

   break;

   case "quoted-printable":

   $tem_body=quoted_printable_decode($tem_body);

   break;

   }

   $this->tem_num=0;

   $this->body=array();

   $this->body[$this->tem_num][content_id]="";

   $this->body[$this->tem_num][type]=$this->body_type;

   switch (strtolower($this->body_type))

   {

   case "text/html":

   $this->body[$this->tem_num][name]="超文本本文";

   break;

   case "text/plain":

   $this->body[$this->tem_num][name]="文本本文";

   break;

   default:

   $this->body[$this->tem_num][name]="未知本文";

   }

  

   $this->body[$this->tem_num][size]=strlen($tem_body);

   $this->body[$this->tem_num][content]=$tem_body;

   unset($tem_body);

   }

   else // 如果是複合類型的

   {

   $this->body=array();

   $this->tem_num=0;

   $this->decode_mult($this->body_type,$this->boundary,0);  //調用複合類型的解碼方法

   }

  }

  function decode_mult($type,$boundary,$begin_row) // 該方法用遞迴的方法實現 複合類型郵件內文的解碼,郵件源檔案取自於 body_temp 數組,調用時給出該複合類型的類型、分隔字元及 在 body_temp 數組中的開始指標

  {

  $i=$begin_row;

  $lines=count($this->body_temp);

  while ($i<$lines) // 這是一個部分的結束標識;

   {

   while (!eregi($boundary,$this->body_temp[$i]))//找到一個開始標識

   $i++;

   if (eregi($boundary."--",$this->body_temp[$i]))

   {

   return $i;

   }

   while (!eregi("Content-Type:([^;]*);",$this->body_temp[$i],$reg ) and $this->body_temp[$i])

   $i++;

   $sub_type=trim($reg[1]); // 取得這一個部分的 類型是milt or text ....

   if (eregi("multipart",$sub_type))// 該子部分又是有多個部分的;

   {

   while (!eregi('boundary=\"([^\"]*)\"',$this->body_temp[$i],$reg) and $this->body_temp[$i])

   $i++;

   $sub_boundary=$reg[1];// 子部分的分隔字元;

   $i++;

   $last_row=$this->decode_mult($sub_type,$sub_boundary,$i);

   $i=$last_row;

   }

   else

   {

   $comm="";

   while (trim($this->body_temp[$i])!="")

   {

   if (strpos($this->body_temp[$i],"=?"))

   $this->body_temp[$i]=$this->decode_mime($this->body_temp[$i]);

   if (eregi("Content-Transfer-Encoding:(.*)",$this->body_temp[$i],$reg))

   $code_type=strtolower(trim($reg[1])); // 編碼方式

   $comm.=$this->body_temp[$i]."\r\n";

   $i++;

   } // comm 是編碼的說明部分

   if (eregi('name=[\"]([^\"]*)[\"]',$comm,$reg))

   $name=$reg[1];

   if (eregi("Content-Disposition:(.*);",$comm,$reg))

   $disp=$reg[1];

   if (eregi("charset=[\"|\'](.*)[\'|\"]",$comm,$reg))

   $char_set=$reg[1];

   if (eregi("Content-ID:[ ]*\<(.*)\>",$comm,$reg)) // 圖片的標識符。

   $content_id=$reg[1];

  

   $this->body[$this->tem_num][type]=$sub_type;

   $this->body[$this->tem_num][content_id]=$content_id;

   $this->body[$this->tem_num][char_set]=$char_set;

   if ($name)

   $this->body[$this->tem_num][name]=$name;

   else

   switch (strtolower($sub_type))

   {

   case "text/html":

   $this->body[$this->tem_num][name]="超文本本文";

   break;

   case "text/plain":

   $this->body[$this->tem_num][name]="文本本文";

   break;

   default:

   $this->body[$this->tem_num][name]="未知本文";

   }

  

  

   // 下一行開始取回本文

   if ($this->get_content_num==-1 or $this->get_content_num==$this->tem_num) // 判斷這個部分是否是需要的。-1 表示全部

   {

   $content="";

   while (!ereg($boundary,$this->body_temp[$i]))

   {

   //$content[]=$this->body_temp[$i];

   $content.=$this->body_temp[$i]."\r\n";

   $i++;

   }

   //$content=implode("\r\n",$content);

   switch ($code_type)

   {

   case "base64":

   $content=base64_decode($content);

   break;

   case "quoted-printable":

   $content=str_replace("\n","\r\n",quoted_printable_decode($content));

   break;

   }

   $this->body[$this->tem_num][size]=strlen($content);

   $this->body[$this->tem_num][content]=$content;

   }

   else

   {

   while (!ereg($boundary,$this->body_temp[$i]))

   $i++;

   }

   $this->tem_num++;

   }

   // end else

  } // end while;

  } // end function define

  

  function decode_mime($string) {

  //decode_mime 已在上文中給出,這裡略過。

  }

  } // end class define

  在這裡要特別說明一點的是html本文裡所用圖片的解碼。發送html格式的本文時,都會碰到圖片如何傳送的問題。圖片在 html 文檔裡是一個的標籤,關鍵是這個源檔案從何來的。很多郵件的處理方法是用一個絕對的 url 標識,就是在郵件的html本文裡用之類的標籤,這樣,在閱讀郵件時,郵件閱讀器(通常是用內嵌的瀏覽器)會自動從網上下載圖片,但是如果郵件收下來之後,與 Internet 的串連斷了,圖片也就不能正常顯示。

  所以更好的方法是把圖片放在郵件中一起發送出去。在 MIME 編碼裡,描述圖片與本文的關係,除了上面所提到的multipart/related MIME頭資訊之外,還用到了一個 Content-ID: 的屬性來使圖片與 html 本文之間建立關係。html 文檔中的圖片在編碼時,其MIME頭中加入一個 Content-ID:122223443556dsdf@ntsever 之類的屬性,122223443556dsdf@ntsever是一個唯一的標識,在 html 文檔裡,標籤被修改成,在解碼的時候,實際上,還需要把 html 本文中的這些標籤進行修改,使之指向解碼後的圖片的具體路徑。但是考慮到具體的解碼程式中對圖片會有不同的處理,所以在這個解碼的類中,沒有對 hmtl 本文中的標籤進行修改。所以在實際使用這個類時,對於有圖片的 html 本文,還需要一定的處理。本文中的圖片,可以用臨時檔案來儲存,也可以用資料庫來儲存。

  現在我們已經介紹了POP3 收取郵件並進行 MIME 解碼的原理。下面給出一個使用這兩個類的一段小程式:

  
  include("pop3.inc.php");

  include("mime.inc.php");

  $host="pop.china.com";

  $user="boss_ch";

  $pass="mypassword";

  $rec=new pop3($host,110,2);

  $decoder=new decode_mail();

  if (!$rec->open()) die($rec->err_str);

  if (!$rec->login($user,$pass)) die($rec->err_str);

  if (!$rec->stat()) die($rec->err_str);

  echo "共有".$rec->messages."封信件,共".$rec->size."位元組大小
";

  if ($rec->messages>0)

   {

   if (!$rec->listmail()) die($rec->err_str);

   echo "以下是信件內容:
";

   for ($i=1;$i<=count($rec->mail_list);$i++)

   {

   echo "信件".$rec->mail_list[$i][num].",大小:".$rec->mail_list[$i][size]."
";

   $rec->getmail($rec->mail_list[$i][num]);

   $decoder->decode($rec->head,$rec->body);

   echo "

郵件標頭的內容:


";

   echo $decoder->from_name."(".$decoder->from_mail.") 於".date("Y-m-d H:i:s",$decoder->mail_time)." 發給".$decoder->to_name."(".$decoder->to_mail.")";

   echo "\n
抄送:";

   if ($decoder->cc_to) echo $decoder->cc_to;else echo "無";

   echo "\n
主題:".$decoder->subject;

   echo "\n
回複到:".$decoder->reply_to;

  

   echo "

郵件內文 :


";

   echo "本文類型:".$decoder->body_type;

   echo "
本文各內容:";

   for ($j=0;$jbody);$j++)

   {

   echo "\n
類型:".$decoder->body[$j][type];

   echo "\n
名稱:".$decoder->body[$j][name];

   echo "\n
大小:".$decoder->body[$j][size];

   echo "\n
content_id:".$decoder->body[$j][content_id];

   echo "\n
本文字元集".$decoder->body[$j][char_set];

   echo "
"; 

   echo "本文內容:".$decoder->body[$j][content];

   echo "
";

   }

  $rec->dele($i);

   }

  }

  $rec->close();

  ?>

  如有想要取得完整原始碼的朋友,請與本人聯絡: boss_ch@netease.com



<全文完>
  • 相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.