基于gmime2.0库的邮件解码程序实现

Gmime 库是一套非常强大MIME(Multipurpose Internet Mail Extension)工具库,用来创建、编辑、分解MIME消息和结构。Gmime本身基于Glib2.0的Gobject,具有良好的扩展性。GMIME遵循GPL许可,源代码公开。本文通过一个最简单的程序,讲解了如何利用该库去实现对一封邮件进行内容还原。

 

一、测试邮件生成

在outlook收件箱中新建一邮件,仅包含正文内容,没有附件等其他东西。

标题为: test mail

发件人为: [email protected]

收件人为:[email protected]

正文内容是:

hello billy.

 

这是一封测试信.

 

石冬雪

然后,另存为testmail.eml。这就是我们将要分析的邮件,通过INTERNET捕包或者邮件代理程序接收到的邮件原始信息跟它是一样的。它的内容如下:

From: "billy shi" <[email protected]>

To: "[email protected]"

Subject: test mail

Date: Mon, 9 May 2005 15:18:53 +0800

MIME-Version: 1.0

Content-Type: multipart/alternative;

    boundary="----=_NextPart_000_000C_01C554AA.7B713F40"

X-Priority: 3

X-MSMail-Priority:

Normal

X-Unsent: 1

X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2800.1441

 

This is a multi-part message in MIME format.

 

------=_NextPart_000_000C_01C554AA.7B713F40

Content-Type: text/plain;

    charset="gb2312"

Content-Transfer-Encoding: base64

 

aGVsbG8gYmlsbHkuDQoNCtXiysfSu7fisuLK1NDFLg0KDQrKr7as0ak=

 

------=_NextPart_000_000C_01C554AA.7B713F40

Content-Type: text/html;

    charset="gb2312"

Content-Transfer-Encoding: base64

 

PCFET0NUWVBFIEhUTUwgUFVCTElDICItLy9XM0MvL0RURCBIVE1MIDQuMCBUcmFuc2l0aW9uYWwv

L0VOIj4NCjxIVE1MPjxIRUFEPg0KPE1FVEEgaHR0cC1lcXVpdj1Db250ZW50LVR5cGUgY29udGVu

dD0idGV4dC9odG1sOyBjaGFyc2V0PWdiMjMxMiI+DQo8TUVUQSBjb250ZW50PSJNU0hUTUwgNi4w

MC4yODAwLjE0OTgiIG5hbWU9R0VORVJBVE9SPg0KPFNUWUxFPjwvU1RZTEU+DQo8L0hFQUQ+DQo8

Qk9EWSBiZ0NvbG9yPSNmZmZmZmY+DQo8RElWPjxGT05UIHNpemU9Mj5oZWxsbyBiaWxseS48L0ZP

TlQ+PC9ESVY+DQo8RElWPjxGT05UIHNpemU9Mj48L0ZPTlQ+Jm5ic3A7PC9ESVY+DQo8RElWPjxG

T05UIHNpemU9Mj7V4srH0ru34rLiytTQxS48L0ZPTlQ+PC9ESVY+DQo8RElWPjxGT05UIHNpemU9

Mj48L0ZPTlQ+Jm5ic3A7PC9ESVY+DQo8RElWPjxGT05UIHNpemU9Mj7Kr7as0ak8L0ZPTlQ+PC9E

SVY+PC9CT0RZPjwvSFRNTD4NCg==

 

------=_NextPart_000_000C_01C554AA.7B713F40--

我们看到,经编码过之后,邮件标题和发信人/收信人还可读,但邮件内容变得不可读。

二、邮件原始结构分析

l         此邮件是一个混合体,混合的两部分是等价的,其中一部分是采用PLAIN明文方式,另一部分是采用HTML网页方式,任一部分内容分析出来都可以;

l         每部分都采用了BASE64来编码;

l         各部分均采使用了gb2312字符集;

l         各部分的分界线为:”----=_NextPart_000_000C_01C554AA.7B713F40”;

另外,将该邮件上传到linux服务器可看出,它每行是以\r\n结尾的,跟LINUX下的邮件内容有区别,需要对CRLF进行特别处理。

三、Gmime2.0简介

a)         框架

                         i.              GMime Streams

GMimeContentType

    GMimeDisposition

    GMimeHeader

    GMimeParam

    GObject

        GMimeDataWrapper

        GMimeObject

            GMimeMessage

            GMimeMessagePart

            GMimeMessagePartial

            GMimeMultipart

                GMimeMultipartEncrypted

                GMimeMultipartSigned

            GMimePart

        GMimeParser

        GMimeStream

            GMimeStreamBuffer

            GMimeStreamCat

            GMimeStreamFile

            GMimeStreamFilter

            GMimeStreamFs

            GMimeStreamMem

            GMimeStreamMmap

            GMimeStreamNull

        GMimeFilter

            GMimeFilterBasic

            GMimeFilterBest

            GMimeFilterCharset

            GMimeFilterCRLF

            GMimeFilterFrom

            GMimeFilterHTML

            GMimeFilterMd5

            GMimeFilterStrip

            GMimeFilterYenc

        GMimeCipherContext

            GMimeGpgContext

        GMimeSession

    InternetAddress

 

 

       各对象细节详见《GMime Reference Manual》。

四、解码程序开发

a)         例程

      1 #include <stdio.h>

      2 #include <gmime/gmime.h>

      3 void Walk(GMimeObject* pPart,int nDepth)

      4 {

      5         printf("Geting %d part content type...\n",nDepth);

      6         const GMimeContentType* pContentType = g_mime_object_get_content_type(pPart) ;

      7         char* szContentType = g_mime_content_type_to_string(pContentType);

      8         printf("The content type is: %s or direct get from the object(%s/%s)\n",szContentType,pContentType->type,pCont        entType->subtype);

      9         g_free(szContentType);

     10

     11         char* szObject = g_mime_object_to_string(pPart);

     12 //      printf("The object is :%s\n",szObject);

     13         g_free(szObject);

     14

     15         const char* szHeader = g_mime_object_get_headers(pPart);

     16         printf("The object's header is:%s\n",szHeader);

     17         const char* szContentId = g_mime_object_get_content_id (pPart);

     18         printf("The object's content id is:%s\n",szContentId);

     19

     20         if(GMIME_IS_MULTIPART(pPart))

     21         {

     22                 GMimeMultipart* pMultipart = GMIME_MULTIPART(pPart);

     23                 printf("The GMimeObject can convert to GMimeMultiPart object...\n");

     24                 printf("The multipart preface is: %s\n",g_mime_multipart_get_preface (GMIME_MULTIPART(pPart)));

     25                 printf("The multipart number is %d\n",g_mime_multipart_get_number (pMultipart));

     26                 printf("The multipart boundary is:%s\n",g_mime_multipart_get_boundary (pMultipart));

     27

     28                 GList* pSubParts =  pMultipart->subparts;

     29

     30                 if(pSubParts==NULL)

     31                         printf("The multipart has no subparts, wrong mail????\n");

     32                 int j=0;

     33                 while(pSubParts)

     34                 {

     35                         pSubParts = pSubParts->next;

     36                         printf("multi part no%d\n",j++);

     37                 }

     38

     39         }

     40 }

     41 void Analyze(GMimeMessage* pMessage)

     42 {

     43         printf("analyze the message begin...\n");

44         Walk(pMessage->mime_part,0);

     45 }

     46 int main(int argc,char** argv)

     47 {

     48         g_mime_init(0);

     49         if(argc<2)

     50         {

     51                 printf("error open file to parser. hello <file>\n");

     52                 return 0;

     53         }

     54         printf("hello, gmime! please\n");

     55

     56         FILE* fp = fopen(argv[1],"rb");

     57         GMimeStream* pStream = g_mime_stream_file_new(fp);

     58

     59

     60         printf("[main] g_mime_stream_file_new success!\n");

     61

     62         GMimeFilter* pCrlfFilter = g_mime_filter_crlf_new (GMIME_FILTER_CRLF_DECODE,GMIME_FILTER_CRLF_MODE_CRLF_ONLY);

     63         printf("[main] new crlf filter success!\n");

     64

     65         GMimeStream* pFilterStream = g_mime_stream_filter_new_with_stream (pStream);

     66         printf("[main] create filter stream with file stream success!\n");

     67         printf("unref the stream object.\n");

     68         g_mime_stream_unref(pStream);

     69

     70         g_mime_stream_filter_add (GMIME_STREAM_FILTER (pFilterStream), pCrlfFilter);

     71         g_object_unref (pCrlfFilter);

     72         printf("[main] add crlf filter to decode success!\n");

     73

     74         GMimeParser* pParser = g_mime_parser_new();

     75         if(!pParser)

     76         {

     77                 printf("error new parser.\n");

     78         }

     79         printf("new parser success!\n");

     80

     81

     82

     83         g_mime_parser_init_with_stream(pParser,pFilterStream);

     84         GMimeMessage* pMessage = g_mime_parser_construct_message(pParser);

     85         if(!pMessage)

     86         {

     87                 printf("error construct the message!\n");

    88                 return 0;

     89         }

     90         printf("construct message with filter stream success!\n");

     91

     92         printf("unref the filter stream.\n");

     93         g_mime_stream_unref(pFilterStream);

     94         printf("unref the parser object.\n");

     95         g_object_unref(pParser);

     96

     97         printf("sender:%s\n",g_mime_message_get_sender(pMessage));

     98         printf("rcpt to:%s\n",g_mime_message_get_reply_to (pMessage));

     99         printf("subject:%s\n",g_mime_message_get_subject (pMessage));

    100         gboolean is_html;

    101         printf("body:%s\n",g_mime_message_get_body (pMessage,true,&is_html));

    102

    103         Analyze(pMessage);

    104         printf("unref the pMessage object.\n");

    105         g_object_unref(GMIME_OBJECT(pMessage));

    106         return 1;

    107 }

 

b)        编译方法:

c++ `pkg-config --cflags --libs gmime-2.0` hello.cpp -o hello

c)        代码介绍

第2行,必须包含<gmime/gmime.h>,才能使用GMIME库的接口。

第46行,main开始

该程序要求带一个参数,作为要分析的邮件原始文件名。

48行,g_mime_init(0) 必须在所有GMIME函数调用之前调用。

57行,创建一个基于文件的流对象,流对象见上述。

62行,创建一个CRLF过滤器,过滤器介绍见前述。一般而言,MTA之间的通信每行都是以\n结尾的,但因为我们要处理以CRLF结尾的邮件体,所以必须附加一个CRLF过滤器来处理\r\n.

65行,根据初始文件流和过滤器创建一个具备过滤功能的流对象。

74行,创建一个MIME解析器,分析器可对一封邮件进行解析,生成消息对象。在消息对象对邮件消息进行结构化的存储。通过消息对象可以遍历邮件各部分内容。

83行,根据输入流对象对MIME解析器进行初始化。

84行,利用MIME解析器生成消息对象。

97-99行,获取消息对象中的“发信人”、“收信人”和“主题”。

101行,从消息对象中获取邮件体。

28行, 对一个混合型邮件体,获取其混合对象链表。利用该链表对象可遍历混合体中的各部分。

33-37行,遍历组成混合体的各部分邮件体对象

你可能感兴趣的:(基于gmime2.0库的邮件解码程序实现)