第六部分 fastmail的使用
C++通用框架的设计 作者:naven
1 fastmail介绍
邮件解析库API完全使用面向对象技术设计,使用C++语言开发的用于邮件解析和组装的库。它提供了一些类用来解析和组装Internet邮件,如MimeMessage和MimeBodyPart,用于Internet邮件协议实现并且遵循RFC822和RFC2045规范。这些API库用于应用程序的开发。
RFC:Request For Comments, 请求注解, Internet标准(草案)
MIME:Multipurpose Internet Mail Extension protocol, 多用途的网际邮件扩充协议
邮件解析库包含一系列的类,主要有MimeMessage(邮件实现类)、MimeBodyPart(邮件正文段体类)、MimeMultipart(邮件多部段体类)、InternetHeaders(邮件头类)、InternetAddress(邮件地址类)和ContentType(段体类型类)等。解析和组装邮件主要使用这些类进行组装和分解。
fastmail主要有如下一些类
ContentDisposition MIME邮件头 实现MIME邮件头的ContentDisposition
ContentID MIME邮件头 实现MIME邮件头的ContentID
ContentType MIME邮件头 实现MIME邮件头的ContentType
MimeType MIME类型类 实现MIME的类型,记录在ContentType里,如text/plain
hdr MIME邮件头 实现MIME邮件头HEADER行
HeaderTokenizer 邮件头分解类 实现分解MIME邮件头的各元素,如Content-Type的mimetype和各个参数。
IMimePart MIME邮件段体接口类 邮件段体类的基类
MimeBodyPart MIME邮件段体类 实现MIME邮件各个段体的类
MimeMessage MIME邮件类 MIME邮件主类
IMultipart 多部分接口类 多部分类的基类
MimeMultipart 多部分实现类 保存段体类对象的多部分容器类
InternetAddress MIME邮件地址类 实现MIME邮件地址的类
InternetHeaders MIME邮件头部分类 实现保存MIME各邮件头的容器类
MimeInitialization 邮件解析全局初始化类 实现初始化MIME邮件用到的各个全局变量
MimetypesFileTypeMap 邮件Mime类型映射类 实现邮件MimeType和FileType类型的映射表,用于查询
MimeUtility MIME邮件分析工具类 实现邮件解析用到的各个解析工具函数
ParameterList 参数列表类 实现ContentType和ContentDisposition用到的参数列表
UniqueValue 邮件唯一值生成类 实现生成MIME邮件用到的唯一值的类,如boundary等
MimeMessage类
现在介绍一下最主要的也是提供主要的调用接口API的类MimeMessage。
MimeMessage提供了一系列的方法供调用者使用,如定义了获取地址信息和获取邮件正文内容的结构(可以为具体的数据也可以为一个MimeMultipart对象),用来实现RFC822和MIME规范。
一个MimeMessage对象里保存了一个邮件内容数据(Content),以及一些记录特定的邮件地址信息(如发件人(Sender)和收件人(recipients))的属性(InternetHeaders)。还有关于这封邮件的结构信息(structural information),以及它的邮件主体(body)的段体类型(Content-Type)。
下面用图来描述一个MimeMessage对象内部可能的结构:
1 Hello World!
以下是一个邮件解析库的简单程序,说明使用面向对象设计的API解析邮件的方法:
void main() {
char *emaildata = loademailfile("helloworld.eml");
MimeMessage email(emaildata);
String 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);
1 解析邮件
下面的例子详细说明如何用邮件解析库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();
// 定义存储邮件主题的字符串变量,邮件解析库均使用String做为字符串处理
String 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行的内容
String xline;
email.getHeader("X-Priority", xline);
Printf("X-Priority: %s\n", xline.c_str());
// 获取邮件纯文本正文。由于每一封邮件都可能同时包含一个纯文本正文体和一个
// HTML正文体,所以它们单独获取
String textplain;
email.getTextPlain(textplain);
printf("BodyTextPlain: %s\n", textplain.c_str());
// 也可以这样同时获取纯文本正文的字符集编码方式,以供调用者根据它来
// 选择不同的字符集显示给用户。getTextHtml()也类似。
String charset;
email.getTextPlain(textplain, charset);
// 获取邮件HTML正文内容。
String texthtml;
email.getTextHtml(texthtml);
printf("BodyTextHtml: %s\n", texthtml.c_str());
// 获取邮件所有附件的名字。
StringArray filenames;
email.getAllAttachmentFilenames(filenames);
// 遍历查找名字跟其他Array用法一样
// 获取指定附件文件名的附件内容,如果有重复的名字的附件将只返回第一个相同
// 名字的附件数据。要获取其他所有附件,请参考下面的方法。
String 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;
String filename, content;
// 获取此附件文件名
part->getFileName(filename);
// 获取此附件内容,已解码
part->getContent(content);
}
// 获取邮件的内联资源附件的名字及内容
// 方法与获取普通附件一样,只不过调用getRelatedAttachment()等。
// filename参数换成cid (Content-ID)
// 获取邮件所有内联资源附件的名字。
StringArray cids;
email.getAllRelatedAttachmentCIDs(cids);
// 遍历查找名字跟其他Array用法一样
// 获取指定内联资源附件文件名的附件内容,如果有重复的名字的附件将只返回第一个相同
// 名字的附件数据。要获取其他所有附件,请参考下面的方法。
String 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;
String filename, cid, content;
// 获取此内联资源附件CID
Part->getContentID(cid);
// 获取此内联资源附件文件名
part->getFileName(filename);
// 获取此内联资源附件内容,已解码
part->getContent(content);
}
}
1 组装邮件
下面的例子详细说明如何用邮件解析库API组装一封邮件:
/**
* 邮件源文数据通过参数传递
* @param emaildata 存储组装好的邮件源文的字符串
*/
void createMessage(String &emaildata)
{
// 定义一个MimeMessage邮件对象用于组装
MimeMessage email;
// 设置标题
email.setSubject("test mail");
// 设置发件人
email.setSender("test@test.net");
// 也可以,后面是地址的名字
email.setSender("test@test.net", "测试帐号");
// 添加收件人
email.addTo("test1@test.net");
email.addTo("test2@test.net", "收件人2");
// 添加抄送者地址
email.addCc("test3@test.net");
email.addCc("test4@test.net", "收件人4");
// 添加密送者地址
email.addBcc("test5@test.net");
email.addBcc("test6@test.net", "收件人6");
// 设置特殊的邮件头
email.addHeader("X-Mailer", "jmail 1.0");
// 设置纯文本正文,缺省为gb2312(环境变量控制,后面会讲到如何配置邮件解析环境)
email.setTextPlain("This is a test mail created by xmail");
// 也可以这样指定编码方式
email.setTextPlain("This is a mail encoded by gbk", "gbk");
// 当然也可以这样
String 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");
// 当然也可以这样
String bodyhtml;
// bodyhtml可以从其他地方读取
email.setTextHtml(bodyhtml);
// 或者这样
email.setTextHtml(bodyhtml, "gbk");
// 添加附件
String 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里引用的个数
String 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将会自动遍历所有资源附件,删除没有再引用的资源附件。
}
1 修改邮件
下面的例子详细说明如何用邮件解析库API修改一封邮件:
/**
* 邮件源文数据通过参数传递
* @param emaildata 存储组装好的邮件源文的字符串
*/
void createMessage(String &emaildata)
{
// 定义一个MimeMessage邮s件对象用于修改
// 方法与上面的解析和组装类似,解析和组装调用的方法都可以调用
MimeMessage email(emaildata);
// 下面介绍一下删除的功能
// 清除邮件纯文本正文
email.removeTextPlain();
// 清楚邮件HTML正文
email.removeTextHtml();
// 删除指定文件名的附件
String filename("attr1.jpg");
email.removeAttachment(filename);
email.removeAttachment("attr1.jpg");
// 删除指定位置的附件,getAllAttachments()获取的数组中的位置,从0开
email.removeAttachment(2);
// 删除所有附件
email.removeAllAttachments();
// 删除所有内嵌资源附件
email.removeAllRelatedAttachments();
// 还可以调用上面的组装方法更新指定的数据
// 更新邮件源文数据
// 注意:一定这样重新定义变量存储新源文数据
String newdata;
email.toString(newdata);
// 更新返回值字符串
emaildata = newdata;
}
1 高级功能
邮件解析引擎库API还有更复杂更强大的高级功能,可以组装和解析出任何符合RFC822和RFC2045的邮件。请参考解析库里的测试程序mimeutils.cpp和相关的文档。