明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

用PHP完成POP3郵件的解碼(二)

[摘要]MIME 編碼方式簡(jiǎn)介(作者:陳俊清 2000年10月24日 15:09)  MIME 編碼方式簡(jiǎn)介   Subject: =?gb2312?B?xOO6w6Oh?=   這里是郵件的主題,可是因?yàn)?..
MIME 編碼方式簡(jiǎn)介
(作者:陳俊清 2000年10月24日 15:09)

  MIME 編碼方式簡(jiǎn)介

  Subject: =?gb2312?B?xOO6w6Oh?=

  這里是郵件的主題,可是因?yàn)榫幋a了,我們看不出是什么內(nèi)容,其原來(lái)的文本是:“你好!”我們先看看 MIME 編碼的兩種方法。

  對(duì)郵件進(jìn)行編碼最初的原因是因?yàn)?Internet 上的很多網(wǎng)關(guān)不能正確傳輸8 bit 內(nèi)碼的字符,比如漢字等。編碼的原理就是把 8 bit 的內(nèi)容轉(zhuǎn)換成 7 bit 的形式以能正確傳輸,在接收方收到之后,再將其還原成 8 bit 的內(nèi)容。

  MIME 是“多用途網(wǎng)際郵件擴(kuò)充協(xié)議”的縮寫(xiě),在 MIME 協(xié)議之前,郵件的編碼曾經(jīng)有過(guò) UUENCODE 等編碼方式 ,但是由于 MIME 協(xié)議算法簡(jiǎn)單,并且易于擴(kuò)展,現(xiàn)在已經(jīng)成為郵件編碼方式的主流,不僅是用來(lái)傳輸 8 bit 的字符,也可以用來(lái)傳送二進(jìn)制的文件 ,如郵件附件中的圖像、音頻等信息,而且擴(kuò)展了很多基于MIME 的應(yīng)用。從編碼方式來(lái)說(shuō),MIME 定義了兩種編碼方法Base64與QP(Quote-Printable) :

  Base 64 是一種通用的方法,其原理很簡(jiǎn)單,就是把三個(gè)Byte的數(shù)據(jù)用 4 個(gè)Byte表示,這樣,這四個(gè)Byte 中,實(shí)際用到的都只有前面6 bit,這樣就不存在只能傳輸 7bit 的字符的問(wèn)題了。Base 64的縮寫(xiě)一般是“B”,像這封信中的Subject 就是用的 Base64 編碼。

  另一種方法是QP(Quote-Printable) 方法,通常縮寫(xiě)為“Q”方法,其原理是把一個(gè) 8 bit 的字符用兩個(gè)16進(jìn)制數(shù)值表示,然后在前面加“=”。所以我們看到經(jīng)過(guò)QP編碼后的文件通常是這個(gè)樣子:=B3=C2=BF=A1=C7=E5=A3=AC=C4=FA=BA=C3=A3=A1。

  在 PHP 里,系統(tǒng)有兩個(gè)函數(shù)可以很方便地實(shí)現(xiàn)解碼:base64_decode()與quoted_printable_decode(),前者可用于base64 編碼的解碼,后者是用于 QP 編碼方法的解碼。

  現(xiàn)在我們?cè)賮?lái)看看Subject: =?gb2312?B?xOO6w6Oh?= 這一主題的內(nèi)容,這不是一段完整的編碼,只有部分是編碼了的,這個(gè)部分用 =? ?= 兩個(gè)標(biāo)記括起來(lái),=? 后面說(shuō)明的是這段文字的字符集是 GB2312 ,然后一個(gè) ? 后面的一個(gè) B 表示的是用的 Base64 編碼。通過(guò)這段分析,我們來(lái)看一下這個(gè) MIME 解碼的函數(shù):(該函數(shù)由 PHPX.COM 站長(zhǎng) Sadly 提供,本人將其放入一個(gè)類中,并做了少量的修改,在此致謝)

  function decode_mime($string) {

   $pos = strpos($string, '=?');

   if (!is_int($pos)) {

     return $string;

   }

   $preceding = substr($string, 0, $pos); // save any preceding text

   $search = substr($string, $pos+2); /* the mime header spec says this is the longest a single encoded word can be */

   $d1 = strpos($search, '?');

   if (!is_int($d1)) {

     return $string;

   }

   $charset = substr($string, $pos+2, $d1); //取出字符集的定義部分

   $search = substr($search, $d1+1); //字符集定義以后的部分=>$search;

   $d2 = strpos($search, '?');

   if (!is_int($d2)) {

     return $string;

   }

   $encoding = substr($search, 0, $d2); ////兩個(gè)? 之間的部分編碼方式。海瘛』颉。狻

   $search = substr($search, $d2+1);

   $end = strpos($search, '?='); //$d2+1 與 $end 之間是編碼了 的內(nèi)容:=> $endcoded_text;

   if (!is_int($end)) {

     return $string;

   }

   $encoded_text = substr($search, 0, $end);

   $rest = substr($string, (strlen($preceding . $charset . $encoding . $encoded_text)+6)); //+6 是前面去掉的 =????= 六個(gè)字符

   switch ($encoding) {

   case 'Q':

   case 'q':

     //$encoded_text = str_replace('_', '%20', $encoded_text);

     //$encoded_text = str_replace('=', '%', $encoded_text);

     //$decoded = urldecode($encoded_text);

   $decoded=quoted_printable_decode($encoded_text);

     if (strtolower($charset) == 'windows-1251') {

     $decoded = convert_cyr_string($decoded, 'w', 'k');

     }

     break;

   case 'B':

   case 'b':

     $decoded = base64_decode($encoded_text);

     if (strtolower($charset) == 'windows-1251') {

     $decoded = convert_cyr_string($decoded, 'w', 'k');

     }

     break;

   default:

     $decoded = '=?' . $charset . '?' . $encoding . '?' . $encoded_text . '?=';

     break;

   }

   return $preceding . $decoded . $this->decode_mime($rest);

  }

  這個(gè)函數(shù)用了遞歸的方法來(lái)實(shí)現(xiàn)一段包含有如上的 Subject 段的字符的解碼。程序中已經(jīng)加上了注釋。相信有點(diǎn)PHP 編程基礎(chǔ)的人都能夠看得明白。該函數(shù)也是調(diào)用的base64_decode()與quoted_printable_decode()兩個(gè)系統(tǒng)函數(shù)實(shí)現(xiàn)的解碼,但是需要對(duì)郵件源文件進(jìn)行大量的字符串的分析。不過(guò),PHP 的字符串操作可以算是所有語(yǔ)言里最為方便自由的。函數(shù)的最后return $preceding . $decoded . $this->decode_mime($rest); 實(shí)現(xiàn)遞歸解碼,因?yàn)檫@個(gè)函數(shù)實(shí)際上是放在后面要介紹的一個(gè) MIME解碼的類中的,所以用了 $this->decode_mime($rest)這種形式的調(diào)用方法。

  下面我們來(lái)看正文。這里關(guān)系到 MIME 的一些頭信息,我們先做一個(gè)簡(jiǎn)單的介紹(如果讀者有興趣了解更多的內(nèi)容,請(qǐng)參考 MIME 的官方文檔)。

  MIME-Version: 1.0

  表示使用的 MIME 的版本號(hào),一般是1.0;

  Content-Type: 定義了正文的類型,我們實(shí)際上是通過(guò)這個(gè)標(biāo)識(shí)來(lái)知道正文內(nèi)是什么類型的文件,比如:

text/plain 表示的是無(wú)格式的文本正文,
text/html 表示的 Html 文檔,
image/gif 表示的是 gif 格式的圖片等等。

  在本文中特別要說(shuō)明一下的是郵件中常用到的復(fù)合類型。multipart 類型表示正文是由多個(gè)部分組成的,后面的子類型說(shuō)明的是這些部分之間的關(guān)系,郵件中用到的三個(gè)類型有,

multipart/alternative:表示正文由兩個(gè)部分組成,可以選擇其中的任意一個(gè)。主要作用是在征文同時(shí)有 text 格式和 html 格式時(shí),可以在兩個(gè)正文中選擇一個(gè)來(lái)顯示,支持 html 格式的郵件客戶端軟件一般會(huì)顯示其 HTML 正文,而不支持的則會(huì)顯示其 Text 正文;

multipart/mixed :表示文檔的多個(gè)部分是混合的,指正文與附件的關(guān)系。如果郵件的 MIME 類型是

multipart/mixed,即表示郵件帶有附件;
multipart/related :表示文檔的多個(gè)部分是相關(guān)的,一般用來(lái)描述 Html 正文與其相關(guān)的圖片。

  這些復(fù)合類型又是可以嵌套使用的,比如說(shuō)一個(gè)帶有附件的郵件,同時(shí)有 html 與 text 兩種格式的正文,則郵件的結(jié)構(gòu)是:

  Content-Type: multipart/mixed

   部分一:

   Content Type : multipart/alternative:

   Text 正文;

   Html 格式的正文 

  部分二:

   附件

  郵件結(jié)束符;

  由于復(fù)合類型由多個(gè)部分組成,因此,需要一個(gè)分隔符來(lái)分隔這多個(gè)部分,這就是上面的郵件源文件中的boundary="----=_NextPart_000_0007_01C03166.5B1E9510"所描述的,對(duì)于每一個(gè)Contect type :multipart/* 的內(nèi)容,都會(huì)有這么一個(gè)說(shuō)明,表示多個(gè)部分之間的分隔,這個(gè)分隔符是正文中不可能出現(xiàn)的一串古字符的組合,在文檔中,以 "--" 加上這個(gè)boundary 來(lái)表示一個(gè)部分的開(kāi)始,在文檔的結(jié)束,以"--"加boundary再在最后加上 "--" 來(lái)表示文檔的結(jié)束。由于復(fù)合類型是可以嵌套使用的,因此,郵件中可能會(huì)多個(gè) boundary 。

  還有一個(gè)最重要的 MIME 頭標(biāo)簽:

  Content-Transfer-Encoding: base64 它表示了這個(gè)部分文檔的編碼方式,也就是我們上面所介紹的Base64或QP(Quote-Printable)。我們只有識(shí)別了這個(gè)說(shuō)明,才能用正確的解碼方式實(shí)現(xiàn)對(duì)其解碼。

  限于篇幅,對(duì)于 MIME 的介紹就只說(shuō)到這里。下面我將給出一個(gè)解碼MIME郵件的類,并對(duì)其做簡(jiǎn)要說(shuō)明。