一、测试邮件生成
在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行,遍历组成混合体的各部分邮件体对象