2008年10月15日星期三

邮件解析框架FastMail库使用简介


邮件解析库API完全使用面向对象技术设计,使用C++语言开发的用于邮件解析和组装的库。它提供了一些类用来解析和组装Internet邮件,如MimeMessageMimeBodyPart,用于Internet邮件协议实现并且遵循RFC822RFC2045规范。这些API库用于应用程序的开发。



RFCRequest For Comments, 请求注解, Internet标准(草案)


MIMEMultipurpose Internet Mail Extension protocol, 多用途的网际邮件扩充协议



以下是一个邮件解析库的简单程序,说明使用面向对象设计的API解析邮件的方法:


void main()


{


    char *emaildata = loademailfile(“helloworld.eml”);


    MimeMessage email(emaildata);


    FastString subject, from, bodytext;


    InternetAddress addr;


    email.getSubject(subject);


    email.getFrom(addr);  addr.toString(From);


    email.getTextPlain(bodytext);


    printf(“Subject: %s\nFrom: %s\nBody: %s\n”,


subject.c_str(), from.c_str(), bodytext.c_str());


    free(emaildata);


}


 


邮件解析库包含一系列的类,主要有MimeMessage(邮件实现类)、MimeBodyPart(邮件正文段体类)、MimeMultipart(邮件多部段体类)、InternetHeaders(邮件头类)、InternetAddress(邮件地址类)和ContentType(段体类型类)等。解析和组装邮件主要使用这些类进行组装和分解。


现在介绍一下最主要的也是提供主要的调用接口API的类MimeMessage


MimeMessage提供了一系列的方法供调用者使用,如定义了获取地址信息和获取邮件正文内容的结构(可以为具体的数据也可以为一个MimeMultipart对象),用来实现RFC822MIME规范。


一个MimeMessage对象里保存了一个邮件内容数据(Content),以及一些记录特定的邮件地址信息(如发件人(Sender)和收件人(recipients))的属性(InternetHeaders)。还有关于这封邮件的结构信息(structural information),以及它的邮件主体(body)的段体类型(Content-Type)。


下面用图来描述一个MimeMessage对象内部可能的结构:





下面的例子详细说明如何用邮件解析库API解析一封邮件:


/**


* 邮件源文数据通过参数传递


* @param msg   指向邮件源文的字符串指针


* @param len   邮件源文的长度


*/


void parseMessage(const char *msg, const int len)


{


    // 定义一个MimeMessage邮件对象用于解析


    // 邮件对象使用指向邮件源文的字符串指针和长度的参数构造


    // 也可使用 MimeMessage email(msg)构造,传入len参数的目的是为了节省再做一次


    // strlen()的时间,因为有些邮件源文比较大。


    // 备注:如果只获取邮件头,MimeMessage就只解析邮件头数据,不会解析邮件正文。


    MimeMessage Email(msg, len);


 


    // 获取发信时间,此时间UTC时间


    // Coordinated Universal Time (UTC, formerly referred to as "Greenwich Mean Time")


    time_t  senttm = email.getSentDate();


 


    // 定义存储邮件主题的字符串变量,邮件解析库均使用FastString做为字符串处理


    FastString subject;


    // 调用MimeMessage类的getSubject()方法获取邮件主题,内容放进subject变量里


Email.getSubject(subject);


// 打印输出主题,c_str()方法是标准的获取字符串内容指针的方法


printf(“Subject: %s\n”, subject.c_str());


 


// 定义存储发信人地址的变量,这里InternetAddress是处理邮件地址的类


InternetAddress from;


// 调用MimeMessage类的getFrom()方法获取邮件发信人地址


Email.getFrom(from);


// 输出地址发信人地址,personal是邮件地址的名字,address是地址


printf(“From: \”%s\” <%s>\n”, from.personal(), from.address());


 


// 定义存储发信人地址的变量,这里用InternetAddressArray是因为收件人可能有多个


InternetAddressArray toAddrs;


// 调用MimeMessage类的getTo()方法获取所有的收件人地址信息


// 获取其他地址如 抄送者用getCc() 密送者用getBcc() 参考后面的MimeMessage方法列表


Email.getTo(toAddrs);


// 由于InternetAddressArray是一个FastArray数组类,所以采用以下方式逐个输出


// 定义遍历数组的迭代器(这是面向对象的设计,类似STL库容器的迭代器用法)


InternetAddressArrayIterator it(toAddrs);


// 判断迭代器是否走到数组的末尾,否则进入循环


while( !it.done() ) {


    // 输出邮件地址,迭代器相当于指向InternetAddress的指针


    printf(“To: \”%s\” <%s>\n”, it->personal(), it->address());


    // 跌打器向前移动一位


    it.advance();


}


// 数组的遍历也可采用如下传统方式


for( int i = 0; i < toAddrs.size(); i ++ ) {


    // 由于[]操作符不计算数组范围,所以不建议如此使用。尽量使用迭代器,


// 除非是想直接取得第n个地址


    printf(“To: \”%s\” <%s>\n”, toAddrs[i].personal(),toAddrs[i].address());


}


 


// 获取其他邮件Header行的内容


FastString xline;


Email.getHeader(“X-Priority”, xline);


Printf(“X-Priority: %s\n”, xline.c_str());


 


// 获取邮件纯文本正文。由于每一封邮件都可能同时包含一个纯文本正文体和一个


// HTML正文体,所以它们单独获取


FastString textplain;


Email.getTextPlain(textplain);


printf(“BodyTextPlain: %s\n”, textplain.c_str());


// 也可以这样同时获取纯文本正文的字符集编码方式,以供调用者根据它来


// 选择不同的字符集显示给用户。getTextHtml()也类似。


FastString charset;


Email.getTextPlain(textplain, charset);


 


// 获取邮件HTML正文内容。


FastString texthtml;


Email.getTextHtml(texthtml);


printf(“BodyTextHtml: %s\n”, texthtml.c_str());


 


// 获取邮件所有附件的名字。


FastStringArray filenames;


Email.getAllAttachmentFilenames(filenames);


// 遍历查找名字跟其他Array用法一样


// 获取指定附件文件名的附件内容,如果有重复的名字的附件将只返回第一个相同


// 名字的附件数据。要获取其他所有附件,请参考下面的方法。


FastString filename(“attr1.jpg”), content;


Email.getAttachment(filename, content);


 


// 获取邮件所有附件。


AttachmentPtrArray attachments;


Email. getAllAttachments(attachments);


// 附件总数


int attnum = attachments.size();


// 遍历所有附件


for( size_t i = 0; i < attachments.size(); i ++ )


{


    // 获得此附件PART的指针,注意:不能free或其他直接修改指针内容的操作。


MimeBodyPart *part = attachments[i];


if( part == NULL )


continue;


        FastString filename, content;


        // 获取此附件文件名


        part->getFileName(filename);


        // 获取此附件内容,已解码


        part->getContent(content);


}


 


// 获取邮件的内联资源附件的名字及内容


// 方法与获取普通附件一样,只不过调用getRelatedAttachment()等。


// filename参数换成cid Content-ID


// 获取邮件所有内联资源附件的名字。


FastStringArray cids;


Email.getAllRelatedAttachmentCIDs(cids);


// 遍历查找名字跟其他Array用法一样


// 获取指定内联资源附件文件名的附件内容,如果有重复的名字的附件将只返回第一个相同


// 名字的附件数据。要获取其他所有附件,请参考下面的方法。


FastString cid(“3334776372$1097735850$0600030@local”), content;


Email.getRelatedAttachment(cid, content);


 


// 获取邮件所有内联资源附件。


AttachmentPtrArray attachments;


Email. getAllRelatedAttachments(attachments);


// 内联资源附件总数


int attnum = attachments.size();


// 遍历所有内联资源附件


for( size_t i = 0; i < attachments.size(); i ++ )


{


    // 获得此附件PART的指针,注意:不能free或其他直接修改指针内容的操作。


MimeBodyPart *part = attachments[i];


if( part == NULL )


continue;


        FastString filename, cid, content;


        // 获取此内联资源附件CID


        Part->getContentID(cid);


        // 获取此内联资源附件文件名


        part->getFileName(filename);


        // 获取此内联资源附件内容,已解码


        part->getContent(content);


}


 


}



下面的例子详细说明如何用邮件解析库API组装一封邮件:


/**


* 邮件源文数据通过参数传递


* @param emaildata 存储组装好的邮件源文的字符串


*/


void createMessage(FastString &emaildata)


{


    // 定义一个MimeMessage邮件对象用于组装


    MimeMessage Email;


 


    // 设置标题


    Email.setSubject(“test mail”);


 


    // 设置发件人


    Email.setSender(“”);


    // 也可以,后面是地址的名字


    Email.setSender(“”, “测试帐号”);


 


    // 添加收件人


    Email.addTo(“”);


    Email.addTo(“”, “收件人2”);


    .. . .


 


    // 添加抄送者地址


    Email.addCc(“”);


    Email.addCc(“”, “收件人4”);


    .. . .


 


    // 添加密送者地址


    Email.addBcc(“”);


    Email.addBcc(“”, “收件人6”);


 


    // 设置特殊的邮件头


    Email.addHeader(“X-Mailer”, “xmail 2.0”);


 


    // 设置纯文本正文,缺省编码gb2312(环境变量控制,后面会讲到如何配置邮件解析环境)


    Email.setTextPlain(“This is a test mail created by xmail”);


    // 也可以这样指定编码方式


    Email.setTextPlain(“This is a mail encoded by gbk”, “gbk”);


    // 当然也可以这样


    FastString bodytext;


    .. .. .. // bodytext可以从其他地方读取


    Email.setTextPlain(bodytext);


    // 或者这样


    Email.setTextPlain(bodytext, “gbk”);


 


    // 设置HTML正文, 跟纯文本正文类似。


    // 备注:一封邮件可以同时包含一个纯文本正文和一个HTML正文供阅读器选择显示


    Email.setTextHtml(“<HTML><BODY>This is a test mail</BODY></HTML>”);


    // 也可以这样指定编码方式


    Email.setTextHtml(“<HTML><BODY>This is a mail encoded by gbk</BODY></HTML>”, “gbk”);


    // 当然也可以这样


    FastString bodyhtml;


    .. .. .. // bodyhtml可以从其他地方读取


    Email.setTextHtml(bodyhtml);


    // 或者这样


    Email.setTextHtml(bodyhtml, “gbk”);


 


    // 添加附件


    FastString filename(“attr1.jpg”), filedata;


    .. .. .. //filename filedata 可以从其他地方读取


    Email.addAttachment(filedata, filename, ”image/jpeg”);


    // 也可以这样


    Email.addAttachment(filedata, “attr1.jpg”, “image/jpeg”);


    // 注意:如果不指定后面第三个参数,即附件的MimeType类型


    // MimeMessage将根据filename的扩展名到MimeTypes数据映射表中查找。


    // 备注:MimeTypes映射表可以配置,参考后面的“配置邮件解析环境”


    // 所以也可以这样调用


    Email.addAttachment(filedata, filename);


    Email.addAttachment(filedata, “attr1.jpg”);


 


    // 添加内嵌资源附件,与添加普通附件类似。


    // 注意:必须要先设置邮件的HTML正文后才能添加内嵌资源附件,否则也添不进去


    // src是资源附件在HTML正文里的URL,包括路径和文件名


    // cid是添加成功后资源附件的CID


    // 返回值count是资源附件在HTML里引用的个数


    FastString src(“/images/attr1.jpg”), cid, filedata;


    .. .. .. //src filedata 可以从其他地方读取


    Int count = Email.addRelatedAttachment(filedata, src, cid, ”image/jpeg”);


    // 同样也可以这样


    Count=Email.addRelatedAttachment(filedata, “/images/attr1.jpg”, cid, “image/jpeg”);


Count = Email.addRelatedAttachment(filedata, “/images/attr1.jpg”, cid);


    Count = Email.addRelatedAttachment(filedata, “/images/attr1.jpg”, cid);


    // 注意:如果重新覆盖了邮件HTML正文即再次调用setTextHtml()


// MimeMessage将会自动遍历所有资源附件,删除没有再引用的资源附件。


 


}


 



下面的例子详细说明如何用邮件解析库API修改一封邮件:


/**


* 邮件源文数据通过参数传递


* @param emaildata 存储组装好的邮件源文的字符串


*/


void createMessage(FastString &emaildata)


{


    // 定义一个MimeMessages件对象用于修改


    // 方法与上面的解析和组装类似,解析和组装调用的方法都可以调用


    MimeMessage Email(emaildata);


 


    // 下面介绍一下删除的功能


    // 清除邮件纯文本正文


    Email.removeTextPlain();


    // 清楚邮件HTML正文


    Email.removeTextHtml();


    // 删除指定文件名的附件


    FastString filename(“attr1.jpg”);


    Email.removeAttachment(filename);


    Email.removeAttachment(“attr1.jpg”);


    // 删除指定位置的附件,getAllAttachments()获取的数组中的位置,从0开始


    Email.removeAttachment(2);


    // 删除所有附件


    Email.removeAllAttachments();


    // 删除所有内嵌资源附件


    Email.removeAllRelatedAttachments();


 


    // 还可以调用上面的组装方法更新指定的数据


 


    // 更新邮件源文数据


    // 注意:一定这样重新定义变量存储新源文数据


    FastString newdata;


    Email.toString(newdata);


    // 更新返回值字符串


    Emaildata = newdata;


}


 



邮件解析引擎库API还有更复杂更强大的高级功能,可以组装和解析出任何符合RFC822RFC2045的邮件。请参考解析库里的测试程序mimeutils.cpp和相关的文档。




--------------------next---------------------

0 条评论:

发表评论

订阅 博文评论 [Atom]

<< 主页