Chinaunix首页 | 论坛 | 博客
  • 博客访问: 640075
  • 博文数量: 263
  • 博客积分: 6000
  • 博客等级: 准将
  • 技术积分: 2555
  • 用 户 组: 普通用户
  • 注册时间: 2008-02-26 11:20
文章分类

全部博文(263)

文章存档

2011年(10)

2010年(19)

2009年(170)

2008年(64)

我的朋友

分类: 系统运维

2008-10-23 12:08:12

MIME   编码方式简介    
   
    Subject:   =?gb2312?B?xOO6w6Oh?=    
   
    这里是邮件的主题,可是因为编码了,我们看不出是什么内容,其原来的文本是:“你好!”我们先看看   MIME   编码的两种方法。    
   
    对邮件进行编码最初的原因是因为   Internet   上的很多网关不能正确传输8   bit   内码的字符,比如汉字等。编码的原理就是把   8   bit   的内容转换成   7   bit   的形式以能正确传输,在接收方收到之后,再将其还原成   8   bit   的内容。    
   
    MIME   是“多用途网际邮件扩充协议”的缩写,在   MIME   协议之前,邮件的编码曾经有过   UUENCODE   等编码方式   ,但是由于   MIME   协议算法简单,并且易于扩展,现在已经成为邮件编码方式的主流,不仅是用来传输   8   bit   的字符,也可以用来传送二进制的文件   ,如邮件附件中的图像、音频等信息,而且扩展了很多基于MIME   的应用。从编码方式来说,MIME   定义了两种编码方法Base64与QP(Quote-Printable)   :    
   
    Base   64   是一种通用的方法,其原理很简单,就是把三个Byte的数据用   4   个Byte表示,这样,这四个Byte   中,实际用到的都只有前面6   bit,这样就不存在只能传输   7bit   的字符的问题了。Base   64的缩写一般是“B”,像这封信中的Subject   就是用的   Base64   编码。    
   
    另一种方法是QP(Quote-Printable)   方法,通常缩写为“Q”方法,其原理是把一个   8   bit   的字符用两个16进制数值表示,然后在前面加“=”。所以我们看到经过QP编码后的文件通常是这个样子:=B3=C2=BF=A1=C7=E5=A3=AC=C4=FA=BA=C3=A3=A1。    
   
    在   PHP   里,系统有两个函数可以很方便地实现解码:base64_decode()与quoted_printable_decode(),前者可用于base64   编码的解码,后者是用于   QP   编码方法的解码。    
   
    现在我们再来看看Subject:   =?gb2312?B?xOO6w6Oh?=   这一主题的内容,这不是一段完整的编码,只有部分是编码了的,这个部分用   =?   ?=   两个标记括起来,=?   后面说明的是这段文字的字符集是   GB2312   ,然后一个   ?   后面的一个   B   表示的是用的   Base64   编码。通过这段分析,我们来看一下这个   MIME   解码的函数:(该函数由   PHPX.COM   站长   Sadly   提供,本人将其放入一个类中,并做了少量的修改,在此致谢)    
   
    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);   ////两个? 之间的部分编码方式 :q 或 b     
   
       $search   =   substr($search,   $d2+1);    
   
       $end   =   strpos($search,   '?=');   //$d2+1   与   $end   之间是编码了 的内容:=>   $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   是前面去掉的 =????= 六个字符    
   
       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);    
   
    }    
   
    这个函数用了递归的方法来实现一段包含有如上的   Subject   段的字符的解码。程序中已经加上了注释。相信有点PHP   编程基础的人都能够看得明白。该函数也是调用的base64_decode()与quoted_printable_decode()两个系统函数实现的解码,但是需要对邮件源文件进行大量的字符串的分析。不过,PHP   的字符串操作可以算是所有语言里最为方便自由的。函数的最后return   $preceding   .   $decoded   .   $this->decode_mime($rest);   实现递归解码,因为这个函数实际上是放在后面要介绍的一个   MIME解码的类中的,所以用了   $this->decode_mime($rest)这种形式的调用方法。    
   
    下面我们来看正文。这里关系到   MIME   的一些头信息,我们先做一个简单的介绍(如果读者有兴趣了解更多的内容,请参考   MIME   的官方文档)。    
   
    MIME-Version:   1.0    
   
    表示使用的   MIME   的版本号,一般是1.0;    
   
    Content-Type:   定义了正文的类型,我们实际上是通过这个标识来知道正文内是什么类型的文件,比如:text/plain   表示的是无格式的文本正文,text/html   表示的   Html   文档,image/gif   表示的是   gif   格式的图片等等。在本文中特别要说明一下的是邮件中常用到的复合类型。multipart   类型表示正文是由多个部分组成的,后面的子类型说明的是这些部分之间的关系,邮件中用到的三个类型有,multipart/alternative:表示正文由两个部分组成,可以选择其中的任意一个。主要作用是在征文同时有   text   格式和   html   格式时,可以在两个正文中选择一个来显示,支持   html   格式的邮件客户端软件一般会显示其   HTML   正文,而不支持的则会显示其   Text   正文;multipart/mixed   :表示文档的多个部分是混合的,指正文与附件的关系。如果邮件的   MIME   类型是multipart/mixed,即表示邮件带有附件;multipart/related   :表示文档的多个部分是相关的,一般用来描述   Html   正文与其相关的图片。    
   
    这些复合类型又是可以嵌套使用的,比如说一个带有附件的邮件,同时有   html   与   text   两种格式的正文,则邮件的结构是:    
   
    Content-Type:   multipart/mixed    
   
       部分一:    
   
       Content   Type   :   multipart/alternative:    
   
       Text   正文;    
   
       Html   格式的正文     
   
    部分二:    
   
       附件    
   
    邮件结束符;    
   
    由于复合类型由多个部分组成,因此,需要一个分隔符来分隔这多个部分,这就是上面的邮件源文件中的 boundary="----=_NextPart_000_0007_01C03166.5B1E9510"所描述的,对于每一个Contect   type   :multipart/*   的内容,都会有这么一个说明,表示多个部分之间的分隔,这个分隔符是正文中不可能出现的一串古字符的组合,在文档中,以 "--"   加上这个boundary   来表示一个部分的开始,在文档的结束,以"--"加boundary再在最后加上   "--"   来表示文档的结束。由于复合类型是可以嵌套使用的,因此,邮件中可能会多个 boundary 。    
   
    还有一个最重要的   MIME   头标签:    
   
    Content-Transfer-Encoding:   base64   它表示了这个部分文档的编码方式,也就是我们上面所介绍的Base64或QP(Quote-Printable)。我们只有识别了这个说明,才能用正确的解码方式实现对其解码。
 
 
如果想了解更多的关于MIME的协议内容,请参看
阅读(1219) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~