- 浏览: 514539 次
- 性别:
- 来自: 惠州
文章分类
- 全部博客 (255)
- ant (1)
- springMVC (2)
- ajax (4)
- oracle (12)
- SSH (13)
- struts1 (2)
- Hibernate (14)
- spring (5)
- jstl (1)
- 连接池 (1)
- acegi (4)
- java (17)
- jquery (11)
- div+css (4)
- drupal (1)
- php (8)
- freemaker调模板生成静态页面 (1)
- xml (1)
- json (2)
- javascript (9)
- 正则表达式 (4)
- Ext (8)
- jdbc (1)
- sql server (2)
- perl (5)
- db4o (1)
- webservice (4)
- flex (13)
- it资讯 (1)
- joomla (0)
- 设计模式 (1)
- struts2 (4)
- s2sh (8)
- linux (3)
- ejb (2)
- android旅途 (24)
- android (36)
- C/C++ (16)
- mysql (1)
最新评论
-
fengyuxing168:
IBelyService bs = IBelyService. ...
为 Android 添加 Java 层服务也就是添加自定义的aidl服务到serviceManager 通过ServiceManager.getService取 -
dengzhangtao:
"由于ActivityManagerService是 ...
binder理解 -
yzyspy:
ActivityManagerService:startHom ...
Android的Launcher成为系统中第一个启动的,也是唯一的 -
Matchstick:
使用SELECT DISTINCT alias FROM Po ...
hibernate 一对多表查询时fetchMode.join 生成left outer join 出来数据重复问题 -
dlheart:
没看懂你什么意思啊,我遇到的问题是一对多,设了fetch = ...
hibernate 一对多表查询时fetchMode.join 生成left outer join 出来数据重复问题
邮件消息的处理
- 博客分类:
- php
译者/作者:chunzi
出处:中国Perl协会 FPC(Foundation of Perlchina)
原名:The Evolution of Perl Email Handling
作者:Simon Cozens
原文:http://www.perl.com/pub/a/2004/06/10/email.html
发表:June 10, 2004
请保护作者的著作权,维护作者劳动的结晶。
每天我都要花费大量的时间在电子邮件相关的工作上,或者通过邮件来和其他工作伙伴联系,或者饶有兴致地分析,索引,重新组织以及挖掘邮件内容。很自然的,Perl 协助我做这些事情。
在 CPAN 上有很多现成的模块可以用来处理电子邮件,我们将介绍其中几个主要的。同时我们也将关注由我和 Richard Clamp,Simon Wistow 以及其他伙伴所致力的 Perl 电子邮件项目(Perl Email Project),该项目的目标是提供一系列简单的,有效的,精准的邮件处理模块。
邮件消息的处理
我们从一些比较简单的,用来描绘一封单独邮件,提供对邮件头和邮件体的访问,甚至修改它们的信息的那些模块开始介绍。
所有的这些模块的曾祖父都是 Mail::Internet ,由 Graham Barr 创建,目前 Mark Overmeer 在维护。该模块提供了通过数组(元素为字符串行)或者文件句柄来读取信件内容的构造器,并通过它返回一个描述该信件的 Mail::Internet 对象。在下面的例子中,我们使用变量 $rfc2822 来表示字符串形式的邮件信息内容。
my $obj = Mail::Internet->new( [ split /\n/, $rfc2822 ] );
Mail::Internet 从信件中提取构造出一个邮件头对象,并连带邮件体信息。邮件头对象的类为 Mail::Header 。你可以通过该对象获取或者设置邮件头的信息:
my $subject = $obj->head->get("Subject"); $obj->head->replace("Subject", "New subject");
而读取或者编辑邮件体内容的操作,则可以使用 body 方法:
my $old_body = $obj->body; $obj->body("Wasnt worth reading anyway.");
到现在为止我还没有提到过任何关于 MIME 的东西。对于简单的任务来说,Mail::Internet 确实非常方便,不过它并不完全支持对 MIME 的处理。谢天谢地,MIME::Entity 作为一个为 MIME 而考虑设计的 Mail::Internet 子类,允许你读取 MIME 消息的每一个独立的部分(part):
my $num_parts = $obj->parts; for (0..$num_parts) { my $part = $obj->parts($_); ... }
如果 Mail::Internet 和 MIME::Entity 都不适合你,你可以试试 Mark Overmeer 自己的 Mail::Message 模块,该模块是令人印象深刻的 Mail::Box 模块中的一部分。Mail::Message 是个极富特色的、功能全面的模块,但这些优点并不总意味着褒扬。
Mail::Message 对象通常都是在 Mail::Box 读取一个电子邮件文件夹的时候,在内部构建的。当然它也可以通过 read 方法来读取一封信件:
$obj = Mail::Message->read($rfc2822);
就像 Mail::Internet 一样,邮件消息被分割为邮件头和邮件体,而与 Mail::Internet 不同的是,邮件体也是一个对象。我们如此读取邮件头:
$obj->head->get("Subject");
或者,如果是 Subject 头信息以及其他常见的邮件头信息,可以如此读取:
$obj->subject;
我找不到直接设置头信息的方法,所以最终可能需要这样做:
$obj->head->delete($header); $obj->head->add($header, $_) for @data;
读取邮件体内容作为字符串形式表达也仅有一点麻烦:
$obj->decoded->string
而设置邮件体内容的操作则绝对是恶梦 -- 我们不得不构建一个 Mail::Message::Body 对象来覆盖现有的。
$obj->body(Mail::Message::Body->new(data => [split /\n/, $body]));
Mail::Message 处理邮件的时候可能有点慢,也着实难用。它的体系也非常复杂,上面我们所看到的这些操作就已经用到了 16 种类 (Mail::Address, Mail::Box::Parser, Mail::Box::Parser::Perl, Mail::Message, Mail::Message::Body, Mail::Message::Body::File, Mail::Message::Body::Lines, Mail::Message::Body::Multipart, Mail::Message::Body::Nested, Mail::Message::Construct, Mail::Message::Field, Mail::Message::Field::Fast, Mail::Message::Head, Mail::Message::Head::Complete, Mail::Message::Part, 以及 Mail::Reporter)和 4400 多行的代码。尽管它确实拥有很多功能,我还是傻傻的觉得邮件的分析处理应该更为简洁。所以我坐下来决定自己着手编写尽可能简洁的邮件处理函数库,结果就有了 Email::Simple 模块,它的交互界面如下所示:
my $obj = Email::Simple->new($rfc2822); my $subject = $obj->header("Subject"); $obj->header_set("Subject", "A new subject"); my $old_body = $obj->body; $obj->body_set("A new body\n"); print $obj->as_string;
它做的事情并不多,但却非常简单和高效。如果你需要 MIME 处理,可以使用它的子类 Email::MIME, 该类增加了 parts 方法。
实际上,选择哪一种邮件处理函数库完全取决于你,最终用户,不过并不总是这样的。有许多辅助性的模块,帮助你在更高的应用层上处理邮件信息的,可能要求你提供特定的邮件表达对象。比如最近的 Mail::ListDetector 模块(稍后我们将解析),需要传给它的邮件为 Mail::Internet 对象,因为该对象的操作界面(API)是已知的。而我不想用 Mail::Internet 对象,但我又需要 Mail::ListDetector 的一些功能,那我可以做些什么呢?
为了让用户也能够有这样的选择,我写了一个用于表达上面各个模块操作界面的抽象层,叫做 Email::Abstract 。给出上面任何一种类型的对象,我们都可以说:
my $subject = Email::Abstract->get_header($obj, "Subject"); Email::Abstract->set_header($obj, "Subject", "My new subject"); my $body = Email::Abstract->get_body($obj); Email::Abstract->set_body($message, "Hello\nTest message\n"); $rfc2822 = Email::Abstract->as_string($obj);
Email::Abstract 知道如何在这些主要的邮件表达对象上作相应的操作。它也抽象了构造邮件消息的过程,并允许你通过类方法 cast 来改变邮件消息对象的操作界面:
my $obj = Email::Abstract->cast($rfc2822, "Mail::Internet");
my $mm = Email::Abstract->cast($obj, "Mail::Message"); 这样使得模块的作者得以使用“接口预先未知(interface-agnostic)”的方式来撰写邮件处理函数库。我很感谢 Michael Stevens 立即在 Mail::ListDetector 中使用了 Email::Abstract 。现在我可以将 Email::Simple 对象传递给 Mail::ListDetector 了,而且它工作的非常好。
Email::Abstract 也给了我们对上面所有这些模块作基准测试(benchmarks)的机会。这里是我使用的测试代码:
use Email::Abstract; my $message = do { local $/; ; }; my @classes = qw(Email::MIME Email::Simple MIME::Entity Mail::Internet Mail::Message); eval "require $_" or die $@ for @classes; use Benchmark; my %h; for my $class (@classes) { $h{$class} = sub { my $obj = Email::Abstract->cast($message, $class); Email::Abstract->get_header($obj, "Subject"); Email::Abstract->get_body($obj); Email::Abstract->set_header($obj, "Subject", "New Subject"); Email::Abstract->set_body($obj, "A completely new body"); Email::Abstract->as_string($obj); } } timethese(1000, \%h); __DATA__ ...
我把一封短小的邮件放到 DATA 部分中,并运行相同的操作一千次:构造一个新的消息对象,读取邮件头,读取邮件体,并将消息内容作为字符串返回。
Benchmark: timing 1000 iterations of Email::MIME, Email::Simple, MIME::Entity, Mail::Internet, Mail::Message... Email::MIME: 10 wallclock secs ( 7.97 usr + 0.24 sys = 8.21 CPU) @ 121.80/s (n=1000) Email::Simple: 9 wallclock secs ( 7.49 usr + 0.05 sys = 7.54 CPU) @ 132.63/s (n=1000) MIME::Entity: 33 wallclock secs (23.76 usr + 0.35 sys = 24.11 CPU) @ 41.48/s (n=1000) Mail::Internet: 24 wallclock secs (17.34 usr + 0.30 sys = 17.64 CPU) @ 56.69/s (n=1000) Mail::Message: 20 wallclock secs (17.12 usr + 0.27 sys = 17.39 CPU) @ 57.50/s (n=1000)
Perl 电子邮件项目确实是成功的:Email::MIME 和 Email::Simple 的运行速度差不多是对手的两倍。然而,我们要强调一点,这里所做的测试都是非常低级的,如果你要做任何比这里看到的更加复杂的操作,你该考虑哪些老的 Mail:: 模块。
邮箱的处理
对于单独信件的处理已经谈了很多了,让我们来看看对一组邮件或者存放邮件的文件夹该如何处理。我们提到过 Mail::Box ,它绝对是处理邮件文件夹的老大,它支持本地和远程的文件夹处理,可以编辑文件夹,以及作相应的排序操作等等。要使用它,我们首先需要 Mail::Box::Manager 模块,它是用来构建 Mail::Box 对象的工厂对象。
use Mail::Box::Manager my $mgr = Mail::Box::Manager->new;
接下来,我们通过管理器来打开文件夹:
my $folder = $mgr->open(folder => $folder_file);
而现在,我们可以获取各个独立的邮件表达对象(Mail::Message):
for ($folder->messages) { print $_->subject,"\n"; }
与此最为相近的,我喜欢用的邮箱管理器还是 Mail::Util 的 read_mbox 函数。把 Unix 中 mbox 文件路径传递给它,然后返回一系列的匿名数组,每个匿名数组都表示一个邮件消息,其元素为该消息的每一行。如此一来,它非常适合 Mail::Internet->new 或者相近的:
for (read_mbox($folder_file)) { my $obj = Mail::Internet->new($_); print $_->head->get("Subject"),"\n"; }
这两种做法都非常容易,不过似乎在 Mail::Util 的简洁性和 Mail::Box 的功能上还有些简化的余地,于是电子邮件项目再次停滞下来,这次的焦点集中在 Email::Folder 和 Email::LocalDelivery 上面。 Email::Folder 可以处理 mbox 和 maildir 格式的邮件文件夹,以及计划中更多其他格式,并且它有非常简洁的操作界面:
my $folder = Email::Folder->new($folder_file); for ($folder->messages) { print $_->header("Subject"),"\n"; }
默认情况,它返回一系列 Email::Simple 对象用以表达每封邮件,不过这可以通过派生一个子类来改变。例如,如果我们想要原始的 RFC2822 格式的字符串,我们可以这样做:
package Email::Folder::Raw; use base Email::Folder; sub bless_message { my ($self, $rfc2822) = @_; return $rfc2822; }
可能将来我们不用再派生一个子类,然后 bless_message ,而改用 Email::Abstract->cast 来更容易的改变对邮件消息的表达方式。
处理文件夹的另一方面就是如何写数据了。或者说如何本地投递。Email::LocalDelivery 模块的出现是为了辅助 Email::Filter 。问题比听起来要更难些,因为它必须处理锁定,跳开邮件体,以及由 mailbox 和 maildir 等不同格式而引发的问题。而 LocalDelivery 则通过简单的界面把所有这些都隐藏起来:
Email::LocalDelivery->deliver($rfc2822, @mailboxes);
Email::LocalDelivery 和 Email::Folder 都使用了 Email::FolderType 模块来帮助确定是哪种类型的邮件文件夹(通过文件名来判断)。
邮件地址的处理
我们再次从抽象层面回到低级的处理,有大量的模块可用于对邮件地址的处理。我很喜欢老的 Mail::Address 模块。邮件地址可以分割为各种字段,诸如:实际的邮件地址,名称短语,注释信息。例如:
Example user (Not a real user)
Mail::Address 解析这些邮件地址,并将名称短语和注释分离出来,以便获取各个独立的部分:
for (Mail::Address->parse($from_line)) { print $_->name, "\t", $_->address, "\n"; }
不幸的是,和其他很多邮件模块一样,并不真的那么有用。
my ($addr) = Mail::Address->parse("eBay, Inc." ); print $addr->name # Inc. eBay
得到的结果仍然难以让人接受,虽然它比之间的版本所返回的 "Inc Ebay" 要好些。于是 Casey West 加入我们并创造了 Email::Address 模块。它和 Mail::Address 使用一致的交互界面,并且运行地更加快速,差不多两到三倍。(译注:上面的例子中,Email::Address 返回 "eBay, Inc." 。看来在作者眼里,Mail::Address 的作者画蛇添足了。)
还有一件我们经常需要做的事情就是校验邮件地址是否合法。比如,某个用户在站点上注册,我们就需要对他所提供的邮件地址是否能够接收邮件作检查。Email::Valid 模块是在我们这帮叛逆的人冲进来之前,就已有的 Email:: 名字空间的原住民,这个模块就是用来做这件事情的。在它最简约的用法中,我们可以说:
if (not Email::Valid->address(test@example.com)) { die "Not a valid address" }
你也可以打开其他检查的选项,比如确定它的域名拥有一个合法的 MX 记录,修正常见的 AOL 和 Compuserve 的邮件地址的一些错误,如下:
if (not Email::Valid->address(-address => test@example.com, -mxcheck => 1)) { die "Not a valid address" }
邮件数据转换
我们有了自己的信件,接下来会对它们做些什么呢?我发现大多是对邮件进行文本化分析,这里有三个模块可以协助我们:
首先是 Text::Quoted ,它获取邮件体的文本,实际上可以是任何其他文本,然后尝试找出某些引用其他邮件的文本部分,然后将之分离并保存到嵌套的数据结构中。例如,如果我们有
$message = < foo > # Bar > baz quux EOF
然后运行 extract($message) 就会返回如下的数据结构:
[ [ { text => foo, quoter => >, raw => > foo }, [ { text => Bar, quoter => > #, raw => > # Bar } ], { text => baz, quoter => >, raw => > baz } ], { empty => 1 }, { text => quux, quoter => , raw => quux } ];
当你显示邮件消息的内容时,准备用不同的颜色来区分不同的引用文本,那么这个模块就帮到你大忙了。类似概念的还有 Text::Original 模块,用于搜寻以原始文件内容开头,没有被引用的部分。它知道如何识别各种类型的属性行,所以有:
$message = < Why are there so many different mail modules? Theres more than one way to do it! Different modules have different focuses, and operate at different levels; some lower, some higher. EOF
那么 first_sentence($message) 将返回 Theres more than one way to do it!。Mariachi 邮件列表存档程序就使用了这项技术,为一个线索中的邮件给出它的提白。
说到邮件的线索化,Mail::Thread 模块实现了 Jamie Zawinski 的邮件线索化算法,该算法先是被 Mozilla 所用,继而许多其他邮件客户端也开始使用这种技术。当然 Mariachi 也使用了这项技术,最近它还作了更新,使用 Email::Abstract 来处理各种你扔过去的邮件表达对象:
my $threader = Mail::Thread->new(@mails); $threader->thread; # 计算线索 for ($threader->rootset) { # 在一个线索内的原始邮件 dump_thread($_); }
邮件过滤
经典的 Perl 的邮件过滤工具莫不就是 Mail::Audit 了,我还在这里写过关于如何使用 Mail::Audit 模块的文章(http://www.perl.com/pub/a/2001/07/17/mailfiltering.html),以及如何与 Mail::SpamAssassin (http://www.perl.com/pub/a/2002/03/06/spam.html)模块相结合使用。
我们已经提到过 Mail::ListDetector 模块好几次了。我把它和 Mail::Audit 结合在一起使用,帮助自己做了大量的自动邮件过滤工作。Mail::Audit::List 的插件使用 ListDetector 来查找信件中的邮件列表头信息,诸如 List-Id,X-Mailman-Version 等等类似的东西,这些头信息可以帮助判别该邮件是否来自于邮件列表。这意味着我有能力过滤所有来自邮件列表的信件到各自的文件夹中,就像这样:
my $list = Mail::ListDetector->new($obj); if ($list) { my $name = $list->listname; $item->accept("mail/$name.-$date"); }
然而,Mail::Audit 本身还有很长一段路要走,所以如果你新架设的系统的话,我们鼓励您使用电子邮件项目的 Email::Filter 模块作为替代,它们的大部分操作界面是一致的,尽管功能并不完全相同。为了追求简洁和速度,它使用了新式的 Email::Simple 作为邮件表达对象模块。
邮件信息挖掘
最后,我所做的比较高级的事情就是开发一个自动分类,组织,并索引邮件到数据库的应用框架,并尝试从中分析并提取有价值的信息。
我的第一个完成这个预期目标的模块是 Mail::Miner ,它由三个主要部分组成。第一个部分获取一封邮件后,去除各种附件,并分别存储到数据库。第二部分纵览这封邮件并运行一系列的识别(Recogniser)模块,如此搜寻邮件地址,电话号码,一些关键字和短语等等,并把它们存储到另一个独立的数据库表中。第三部分为命令行工具,用来查询数据库中的邮件以及相关的信息。
举个例子,如果我需要找 Tim OReilly 的邮政地址,我就会使用查询工具 mm ,从他发来的信中找出该地址:
% mm --from "Tim O" --address Address found in message 1835 from "Tim OReilly" : Tim OReilly @ OReilly %26amp; Associates, Inc. 1005 Gravenstein Highway North, Sebastopol, CA 95472
如果要获取完整的邮件,我可以说
% mm --id 1835
如果它原本包含一个附件,那么我们可能会看到类似下面的部分:
[ text/xml attachment something.xml detached - use mm --detach 208 to recover ]
我粘贴中间的那一行 mm --detach 208 到 shell 中,然后很快的,something.xml 写到了磁盘上。
现在 Mail::Miner 已经非常不错了,不过它把三种思想紧紧地捆绑在一个包中 -- 邮件的归档,邮件的数据挖掘以及查询数据库的命令行界面 -- 这使得很难单独开发或者扩展每块的功能。当然,它使用了老式的 Mail:: 名字空间。
这引领我们走到这次邮件模块旅程的最后一站,最新发布的:Email::Store 模块。这是个基于 Class::DBI 的应用框架,用来存储邮件到数据库并以各种方式索引:
use Email::Store dbi:SQLite:mail.db; Email::Store->setup; Email::Store::Mail->store($rfc2822);
紧接着...
my ($name) = Email::Store::Name->search( name => "Simon Cozens" ) @mails_from_simon = $name->addressings( role => "From" )->mails;
它可以用来构建类似 Mariachi 的邮件列表归档工具,或者类似 Mail::Miner 的数据挖掘。它仍然在初步的开发阶段,并在增强模块的扩展性方面使用了一些新的思想。
在我们使用 Email::Store 写出第一个邮件归档和搜索工具的时候,我会再次给大家作详细介绍的。这也是为了 perl.org 的新的 Perl 邮件列表处理接口而准备做的工作。
小结
我们已经看过了 CPAN 上的几个主要的邮件处理模块,当然还有更多。很明显的,我着实偏袒那些自己写的模块。特定的 Perl 电子邮件项目的模块则使用 Email::* 的名字空间。我们特别设计了这些简洁、高效的模块,而它们并不总是老式的 Mail::* 模块的优良替换方案,特别像 Mail::Box 之类。到此,我希望各位通过对本文的阅读,了解和认识更多的邮件处理工具模块,并在之后使用 Perl 来处理邮件时,胸中有丘壑。
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!
出处:中国Perl协会 FPC(Foundation of Perlchina)
原名:The Evolution of Perl Email Handling
作者:Simon Cozens
原文:http://www.perl.com/pub/a/2004/06/10/email.html
发表:June 10, 2004
请保护作者的著作权,维护作者劳动的结晶。
每天我都要花费大量的时间在电子邮件相关的工作上,或者通过邮件来和其他工作伙伴联系,或者饶有兴致地分析,索引,重新组织以及挖掘邮件内容。很自然的,Perl 协助我做这些事情。
在 CPAN 上有很多现成的模块可以用来处理电子邮件,我们将介绍其中几个主要的。同时我们也将关注由我和 Richard Clamp,Simon Wistow 以及其他伙伴所致力的 Perl 电子邮件项目(Perl Email Project),该项目的目标是提供一系列简单的,有效的,精准的邮件处理模块。
邮件消息的处理
我们从一些比较简单的,用来描绘一封单独邮件,提供对邮件头和邮件体的访问,甚至修改它们的信息的那些模块开始介绍。
所有的这些模块的曾祖父都是 Mail::Internet ,由 Graham Barr 创建,目前 Mark Overmeer 在维护。该模块提供了通过数组(元素为字符串行)或者文件句柄来读取信件内容的构造器,并通过它返回一个描述该信件的 Mail::Internet 对象。在下面的例子中,我们使用变量 $rfc2822 来表示字符串形式的邮件信息内容。
my $obj = Mail::Internet->new( [ split /\n/, $rfc2822 ] );
Mail::Internet 从信件中提取构造出一个邮件头对象,并连带邮件体信息。邮件头对象的类为 Mail::Header 。你可以通过该对象获取或者设置邮件头的信息:
my $subject = $obj->head->get("Subject"); $obj->head->replace("Subject", "New subject");
而读取或者编辑邮件体内容的操作,则可以使用 body 方法:
my $old_body = $obj->body; $obj->body("Wasnt worth reading anyway.");
到现在为止我还没有提到过任何关于 MIME 的东西。对于简单的任务来说,Mail::Internet 确实非常方便,不过它并不完全支持对 MIME 的处理。谢天谢地,MIME::Entity 作为一个为 MIME 而考虑设计的 Mail::Internet 子类,允许你读取 MIME 消息的每一个独立的部分(part):
my $num_parts = $obj->parts; for (0..$num_parts) { my $part = $obj->parts($_); ... }
如果 Mail::Internet 和 MIME::Entity 都不适合你,你可以试试 Mark Overmeer 自己的 Mail::Message 模块,该模块是令人印象深刻的 Mail::Box 模块中的一部分。Mail::Message 是个极富特色的、功能全面的模块,但这些优点并不总意味着褒扬。
Mail::Message 对象通常都是在 Mail::Box 读取一个电子邮件文件夹的时候,在内部构建的。当然它也可以通过 read 方法来读取一封信件:
$obj = Mail::Message->read($rfc2822);
就像 Mail::Internet 一样,邮件消息被分割为邮件头和邮件体,而与 Mail::Internet 不同的是,邮件体也是一个对象。我们如此读取邮件头:
$obj->head->get("Subject");
或者,如果是 Subject 头信息以及其他常见的邮件头信息,可以如此读取:
$obj->subject;
我找不到直接设置头信息的方法,所以最终可能需要这样做:
$obj->head->delete($header); $obj->head->add($header, $_) for @data;
读取邮件体内容作为字符串形式表达也仅有一点麻烦:
$obj->decoded->string
而设置邮件体内容的操作则绝对是恶梦 -- 我们不得不构建一个 Mail::Message::Body 对象来覆盖现有的。
$obj->body(Mail::Message::Body->new(data => [split /\n/, $body]));
Mail::Message 处理邮件的时候可能有点慢,也着实难用。它的体系也非常复杂,上面我们所看到的这些操作就已经用到了 16 种类 (Mail::Address, Mail::Box::Parser, Mail::Box::Parser::Perl, Mail::Message, Mail::Message::Body, Mail::Message::Body::File, Mail::Message::Body::Lines, Mail::Message::Body::Multipart, Mail::Message::Body::Nested, Mail::Message::Construct, Mail::Message::Field, Mail::Message::Field::Fast, Mail::Message::Head, Mail::Message::Head::Complete, Mail::Message::Part, 以及 Mail::Reporter)和 4400 多行的代码。尽管它确实拥有很多功能,我还是傻傻的觉得邮件的分析处理应该更为简洁。所以我坐下来决定自己着手编写尽可能简洁的邮件处理函数库,结果就有了 Email::Simple 模块,它的交互界面如下所示:
my $obj = Email::Simple->new($rfc2822); my $subject = $obj->header("Subject"); $obj->header_set("Subject", "A new subject"); my $old_body = $obj->body; $obj->body_set("A new body\n"); print $obj->as_string;
它做的事情并不多,但却非常简单和高效。如果你需要 MIME 处理,可以使用它的子类 Email::MIME, 该类增加了 parts 方法。
实际上,选择哪一种邮件处理函数库完全取决于你,最终用户,不过并不总是这样的。有许多辅助性的模块,帮助你在更高的应用层上处理邮件信息的,可能要求你提供特定的邮件表达对象。比如最近的 Mail::ListDetector 模块(稍后我们将解析),需要传给它的邮件为 Mail::Internet 对象,因为该对象的操作界面(API)是已知的。而我不想用 Mail::Internet 对象,但我又需要 Mail::ListDetector 的一些功能,那我可以做些什么呢?
为了让用户也能够有这样的选择,我写了一个用于表达上面各个模块操作界面的抽象层,叫做 Email::Abstract 。给出上面任何一种类型的对象,我们都可以说:
my $subject = Email::Abstract->get_header($obj, "Subject"); Email::Abstract->set_header($obj, "Subject", "My new subject"); my $body = Email::Abstract->get_body($obj); Email::Abstract->set_body($message, "Hello\nTest message\n"); $rfc2822 = Email::Abstract->as_string($obj);
Email::Abstract 知道如何在这些主要的邮件表达对象上作相应的操作。它也抽象了构造邮件消息的过程,并允许你通过类方法 cast 来改变邮件消息对象的操作界面:
my $obj = Email::Abstract->cast($rfc2822, "Mail::Internet");
my $mm = Email::Abstract->cast($obj, "Mail::Message"); 这样使得模块的作者得以使用“接口预先未知(interface-agnostic)”的方式来撰写邮件处理函数库。我很感谢 Michael Stevens 立即在 Mail::ListDetector 中使用了 Email::Abstract 。现在我可以将 Email::Simple 对象传递给 Mail::ListDetector 了,而且它工作的非常好。
Email::Abstract 也给了我们对上面所有这些模块作基准测试(benchmarks)的机会。这里是我使用的测试代码:
use Email::Abstract; my $message = do { local $/; ; }; my @classes = qw(Email::MIME Email::Simple MIME::Entity Mail::Internet Mail::Message); eval "require $_" or die $@ for @classes; use Benchmark; my %h; for my $class (@classes) { $h{$class} = sub { my $obj = Email::Abstract->cast($message, $class); Email::Abstract->get_header($obj, "Subject"); Email::Abstract->get_body($obj); Email::Abstract->set_header($obj, "Subject", "New Subject"); Email::Abstract->set_body($obj, "A completely new body"); Email::Abstract->as_string($obj); } } timethese(1000, \%h); __DATA__ ...
我把一封短小的邮件放到 DATA 部分中,并运行相同的操作一千次:构造一个新的消息对象,读取邮件头,读取邮件体,并将消息内容作为字符串返回。
Benchmark: timing 1000 iterations of Email::MIME, Email::Simple, MIME::Entity, Mail::Internet, Mail::Message... Email::MIME: 10 wallclock secs ( 7.97 usr + 0.24 sys = 8.21 CPU) @ 121.80/s (n=1000) Email::Simple: 9 wallclock secs ( 7.49 usr + 0.05 sys = 7.54 CPU) @ 132.63/s (n=1000) MIME::Entity: 33 wallclock secs (23.76 usr + 0.35 sys = 24.11 CPU) @ 41.48/s (n=1000) Mail::Internet: 24 wallclock secs (17.34 usr + 0.30 sys = 17.64 CPU) @ 56.69/s (n=1000) Mail::Message: 20 wallclock secs (17.12 usr + 0.27 sys = 17.39 CPU) @ 57.50/s (n=1000)
Perl 电子邮件项目确实是成功的:Email::MIME 和 Email::Simple 的运行速度差不多是对手的两倍。然而,我们要强调一点,这里所做的测试都是非常低级的,如果你要做任何比这里看到的更加复杂的操作,你该考虑哪些老的 Mail:: 模块。
邮箱的处理
对于单独信件的处理已经谈了很多了,让我们来看看对一组邮件或者存放邮件的文件夹该如何处理。我们提到过 Mail::Box ,它绝对是处理邮件文件夹的老大,它支持本地和远程的文件夹处理,可以编辑文件夹,以及作相应的排序操作等等。要使用它,我们首先需要 Mail::Box::Manager 模块,它是用来构建 Mail::Box 对象的工厂对象。
use Mail::Box::Manager my $mgr = Mail::Box::Manager->new;
接下来,我们通过管理器来打开文件夹:
my $folder = $mgr->open(folder => $folder_file);
而现在,我们可以获取各个独立的邮件表达对象(Mail::Message):
for ($folder->messages) { print $_->subject,"\n"; }
与此最为相近的,我喜欢用的邮箱管理器还是 Mail::Util 的 read_mbox 函数。把 Unix 中 mbox 文件路径传递给它,然后返回一系列的匿名数组,每个匿名数组都表示一个邮件消息,其元素为该消息的每一行。如此一来,它非常适合 Mail::Internet->new 或者相近的:
for (read_mbox($folder_file)) { my $obj = Mail::Internet->new($_); print $_->head->get("Subject"),"\n"; }
这两种做法都非常容易,不过似乎在 Mail::Util 的简洁性和 Mail::Box 的功能上还有些简化的余地,于是电子邮件项目再次停滞下来,这次的焦点集中在 Email::Folder 和 Email::LocalDelivery 上面。 Email::Folder 可以处理 mbox 和 maildir 格式的邮件文件夹,以及计划中更多其他格式,并且它有非常简洁的操作界面:
my $folder = Email::Folder->new($folder_file); for ($folder->messages) { print $_->header("Subject"),"\n"; }
默认情况,它返回一系列 Email::Simple 对象用以表达每封邮件,不过这可以通过派生一个子类来改变。例如,如果我们想要原始的 RFC2822 格式的字符串,我们可以这样做:
package Email::Folder::Raw; use base Email::Folder; sub bless_message { my ($self, $rfc2822) = @_; return $rfc2822; }
可能将来我们不用再派生一个子类,然后 bless_message ,而改用 Email::Abstract->cast 来更容易的改变对邮件消息的表达方式。
处理文件夹的另一方面就是如何写数据了。或者说如何本地投递。Email::LocalDelivery 模块的出现是为了辅助 Email::Filter 。问题比听起来要更难些,因为它必须处理锁定,跳开邮件体,以及由 mailbox 和 maildir 等不同格式而引发的问题。而 LocalDelivery 则通过简单的界面把所有这些都隐藏起来:
Email::LocalDelivery->deliver($rfc2822, @mailboxes);
Email::LocalDelivery 和 Email::Folder 都使用了 Email::FolderType 模块来帮助确定是哪种类型的邮件文件夹(通过文件名来判断)。
邮件地址的处理
我们再次从抽象层面回到低级的处理,有大量的模块可用于对邮件地址的处理。我很喜欢老的 Mail::Address 模块。邮件地址可以分割为各种字段,诸如:实际的邮件地址,名称短语,注释信息。例如:
Example user (Not a real user)
Mail::Address 解析这些邮件地址,并将名称短语和注释分离出来,以便获取各个独立的部分:
for (Mail::Address->parse($from_line)) { print $_->name, "\t", $_->address, "\n"; }
不幸的是,和其他很多邮件模块一样,并不真的那么有用。
my ($addr) = Mail::Address->parse("eBay, Inc." ); print $addr->name # Inc. eBay
得到的结果仍然难以让人接受,虽然它比之间的版本所返回的 "Inc Ebay" 要好些。于是 Casey West 加入我们并创造了 Email::Address 模块。它和 Mail::Address 使用一致的交互界面,并且运行地更加快速,差不多两到三倍。(译注:上面的例子中,Email::Address 返回 "eBay, Inc." 。看来在作者眼里,Mail::Address 的作者画蛇添足了。)
还有一件我们经常需要做的事情就是校验邮件地址是否合法。比如,某个用户在站点上注册,我们就需要对他所提供的邮件地址是否能够接收邮件作检查。Email::Valid 模块是在我们这帮叛逆的人冲进来之前,就已有的 Email:: 名字空间的原住民,这个模块就是用来做这件事情的。在它最简约的用法中,我们可以说:
if (not Email::Valid->address(test@example.com)) { die "Not a valid address" }
你也可以打开其他检查的选项,比如确定它的域名拥有一个合法的 MX 记录,修正常见的 AOL 和 Compuserve 的邮件地址的一些错误,如下:
if (not Email::Valid->address(-address => test@example.com, -mxcheck => 1)) { die "Not a valid address" }
邮件数据转换
我们有了自己的信件,接下来会对它们做些什么呢?我发现大多是对邮件进行文本化分析,这里有三个模块可以协助我们:
首先是 Text::Quoted ,它获取邮件体的文本,实际上可以是任何其他文本,然后尝试找出某些引用其他邮件的文本部分,然后将之分离并保存到嵌套的数据结构中。例如,如果我们有
$message = < foo > # Bar > baz quux EOF
然后运行 extract($message) 就会返回如下的数据结构:
[ [ { text => foo, quoter => >, raw => > foo }, [ { text => Bar, quoter => > #, raw => > # Bar } ], { text => baz, quoter => >, raw => > baz } ], { empty => 1 }, { text => quux, quoter => , raw => quux } ];
当你显示邮件消息的内容时,准备用不同的颜色来区分不同的引用文本,那么这个模块就帮到你大忙了。类似概念的还有 Text::Original 模块,用于搜寻以原始文件内容开头,没有被引用的部分。它知道如何识别各种类型的属性行,所以有:
$message = < Why are there so many different mail modules? Theres more than one way to do it! Different modules have different focuses, and operate at different levels; some lower, some higher. EOF
那么 first_sentence($message) 将返回 Theres more than one way to do it!。Mariachi 邮件列表存档程序就使用了这项技术,为一个线索中的邮件给出它的提白。
说到邮件的线索化,Mail::Thread 模块实现了 Jamie Zawinski 的邮件线索化算法,该算法先是被 Mozilla 所用,继而许多其他邮件客户端也开始使用这种技术。当然 Mariachi 也使用了这项技术,最近它还作了更新,使用 Email::Abstract 来处理各种你扔过去的邮件表达对象:
my $threader = Mail::Thread->new(@mails); $threader->thread; # 计算线索 for ($threader->rootset) { # 在一个线索内的原始邮件 dump_thread($_); }
邮件过滤
经典的 Perl 的邮件过滤工具莫不就是 Mail::Audit 了,我还在这里写过关于如何使用 Mail::Audit 模块的文章(http://www.perl.com/pub/a/2001/07/17/mailfiltering.html),以及如何与 Mail::SpamAssassin (http://www.perl.com/pub/a/2002/03/06/spam.html)模块相结合使用。
我们已经提到过 Mail::ListDetector 模块好几次了。我把它和 Mail::Audit 结合在一起使用,帮助自己做了大量的自动邮件过滤工作。Mail::Audit::List 的插件使用 ListDetector 来查找信件中的邮件列表头信息,诸如 List-Id,X-Mailman-Version 等等类似的东西,这些头信息可以帮助判别该邮件是否来自于邮件列表。这意味着我有能力过滤所有来自邮件列表的信件到各自的文件夹中,就像这样:
my $list = Mail::ListDetector->new($obj); if ($list) { my $name = $list->listname; $item->accept("mail/$name.-$date"); }
然而,Mail::Audit 本身还有很长一段路要走,所以如果你新架设的系统的话,我们鼓励您使用电子邮件项目的 Email::Filter 模块作为替代,它们的大部分操作界面是一致的,尽管功能并不完全相同。为了追求简洁和速度,它使用了新式的 Email::Simple 作为邮件表达对象模块。
邮件信息挖掘
最后,我所做的比较高级的事情就是开发一个自动分类,组织,并索引邮件到数据库的应用框架,并尝试从中分析并提取有价值的信息。
我的第一个完成这个预期目标的模块是 Mail::Miner ,它由三个主要部分组成。第一个部分获取一封邮件后,去除各种附件,并分别存储到数据库。第二部分纵览这封邮件并运行一系列的识别(Recogniser)模块,如此搜寻邮件地址,电话号码,一些关键字和短语等等,并把它们存储到另一个独立的数据库表中。第三部分为命令行工具,用来查询数据库中的邮件以及相关的信息。
举个例子,如果我需要找 Tim OReilly 的邮政地址,我就会使用查询工具 mm ,从他发来的信中找出该地址:
% mm --from "Tim O" --address Address found in message 1835 from "Tim OReilly" : Tim OReilly @ OReilly %26amp; Associates, Inc. 1005 Gravenstein Highway North, Sebastopol, CA 95472
如果要获取完整的邮件,我可以说
% mm --id 1835
如果它原本包含一个附件,那么我们可能会看到类似下面的部分:
[ text/xml attachment something.xml detached - use mm --detach 208 to recover ]
我粘贴中间的那一行 mm --detach 208 到 shell 中,然后很快的,something.xml 写到了磁盘上。
现在 Mail::Miner 已经非常不错了,不过它把三种思想紧紧地捆绑在一个包中 -- 邮件的归档,邮件的数据挖掘以及查询数据库的命令行界面 -- 这使得很难单独开发或者扩展每块的功能。当然,它使用了老式的 Mail:: 名字空间。
这引领我们走到这次邮件模块旅程的最后一站,最新发布的:Email::Store 模块。这是个基于 Class::DBI 的应用框架,用来存储邮件到数据库并以各种方式索引:
use Email::Store dbi:SQLite:mail.db; Email::Store->setup; Email::Store::Mail->store($rfc2822);
紧接着...
my ($name) = Email::Store::Name->search( name => "Simon Cozens" ) @mails_from_simon = $name->addressings( role => "From" )->mails;
它可以用来构建类似 Mariachi 的邮件列表归档工具,或者类似 Mail::Miner 的数据挖掘。它仍然在初步的开发阶段,并在增强模块的扩展性方面使用了一些新的思想。
在我们使用 Email::Store 写出第一个邮件归档和搜索工具的时候,我会再次给大家作详细介绍的。这也是为了 perl.org 的新的 Perl 邮件列表处理接口而准备做的工作。
小结
我们已经看过了 CPAN 上的几个主要的邮件处理模块,当然还有更多。很明显的,我着实偏袒那些自己写的模块。特定的 Perl 电子邮件项目的模块则使用 Email::* 的名字空间。我们特别设计了这些简洁、高效的模块,而它们并不总是老式的 Mail::* 模块的优良替换方案,特别像 Mail::Box 之类。到此,我希望各位通过对本文的阅读,了解和认识更多的邮件处理工具模块,并在之后使用 Perl 来处理邮件时,胸中有丘壑。
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!
发表评论
-
perl Base64 邮件解码 乱码 问题
2011-11-19 00:21 2001http://maclife.net/tools/base64 ... -
perl程序——文件的base64编码解码程序(文件操作)
2011-11-17 14:34 1442#!/usr/bin/perl use MIME::Base6 ... -
perl 里的<>和<STDIN>
2011-11-13 21:44 1552<>操作查看@ARGV来决定使用哪些文件。如果表为 ... -
php参考
2010-12-13 21:50 983<?php/** Author 墨龙* Tim ... -
浅谈 Joomla! 的数据库访问
2009-12-08 13:50 3680浅谈 Joomla! 的数据库访 ... -
php 邮件解析 主题解析
2009-05-31 17:32 1208$position=strpos($row["f_S ... -
PHP经典分页导航函数可自己修改成jsp的分页类似javaeye
2008-10-22 14:37 1721PHP经典分页导航函数 可自己修改成jsp的分页类似javae ...
相关推荐
本文将深入探讨“msg1.rar”这个压缩包中所涉及的VC消息机制,以及与“msg1”相关的邮件消息处理。 首先,“msg1”可能是指一个特定的消息ID或消息处理函数,这在Windows编程中非常常见。每个窗口都有一个消息队列...
1. 消息发送和接收:通过短信网关和短信猫完成短消息的收发和消息格式的转换,提供消息的发送、接收、重复消息的压缩、重要消息的升级、短信消息(SMS)、邮件消息(Email)、离线消息、消息群发、消息转发功能。...
4. **邮件消息处理**: - `Pop3Client`类:它提供了`Connect`、`Authenticate`方法来连接并验证邮件服务器,然后可以使用`GetMessageCount`获取邮件数量,`GetMessage`获取特定邮件,`Disconnect`断开连接。 - `...
5. `mailmsg.dll`: 邮件消息处理库,可能包含处理邮件格式、解析、编码等功能,是邮件系统的核心组件之一。 6. `ntfsdrct.h` 和 `ntfsdrct.ini`: 这些文件与NTFS(New Technology File System)驱动程序有关,NTFS是...
**知识点3:邮件消息处理流程** - **消息创建**: 用户通过邮件客户端创建邮件消息。 - **消息发送**: 通过Domino邮件服务器发送邮件。 - **消息路由**: 根据邮件收件人信息路由邮件。 - **消息接收**: 收件人通过...
2. **邮件创建**:开发者可以通过提供必要的参数,如收件人地址、邮件主题、正文内容和附件,来创建新的邮件消息。这在自动化发送邮件时特别实用。 3. **邮件编辑**:MsgApi 还允许修改已存在的邮件,例如更改主题...
JavaMail API 包括核心组件如 `javax.mail` 和 `javax.mail.internet` 包,它们提供了邮件传输和邮件消息处理的基本接口。 以下是一些使用 JavaMail 的关键知识点: 1. **JavaMail Session**: 这是 JavaMail 的...
Python作为一种强大且易学的编程语言,广泛应用于各种自动化场景,包括邮件和消息的发送。本篇将深入讲解如何利用Python的requests库发送钉钉消息以及如何使用email和smtplib库发送邮件,特别是与163邮箱服务的集成...
**综合邮件处理软件cryptoheaven** 是一款集成了多种功能的电子邮件管理工具,与Exchange和FOXMAIL等知名邮件客户端有类似的使用体验。它不仅提供基本的邮件收发服务,还包含了一些独特的特性,使得它在电子邮件处理...
5. **异步处理**:结合ActiveMQ,邮件发送可以通过消息队列异步执行,提高系统响应速度和稳定性。当一个请求触发邮件发送时,将邮件信息封装成消息发送到ActiveMQ,由消息监听器接收并实际执行发送操作。 **三、...
本文将详细讲解如何使用Java接口来实现消息邮件的发送。 首先,我们需要了解的是Java Mail API,这是一个开源的Java库,用于处理电子邮件。在Java中,发送邮件主要依赖于`javax.mail`和`javax.mail.internet`这两个...
MIME(Multipurpose Internet Mail Extensions)是一种扩展了电子邮件标准的协议,允许在邮件中包含非ASCII字符、多部分消息体(如文本、图片、附件等)。在`MIME邮件格式分析及信息提取.doc`文档中,你可以学习...
消息适配器(Matcher)则负责判断哪些邮件应由Mailet处理,实现了邮件处理的高度定制化。 James服务器的组件包括: 1. SMTP和POP3邮件服务器,分别负责邮件的发送和接收。 2. 远程管理服务器,便于管理员远程监控和...
8. **安全与隐私**:消息系统处理敏感的用户信息,如电话号码和电子邮件地址,因此必须遵循GDPR等法规,确保数据安全,如使用HTTPS通信、加密存储敏感数据。 9. **可扩展性与高可用性**:随着用户量的增长,系统应...
Avaya Modular Messaging...这一强大的前瞻性语音/传真或一体化(语音/传真/电子邮件) 消息处理系统专为单点或多点跨国企业设计,既允许您添加新的IP 消息处理功能,也能使您保留您当前的消息处理基础设施和资本投资。
本文将详细讲解如何使用Java发送邮件,并着重介绍消息格式化的概念及其重要性。 首先,发送邮件的核心类是JavaMail API提供的`javax.mail`包中的`Session`, `Message`, `Transport`等类。为了发送邮件,我们需要...
UIPath是一款强大的Robotic Process Automation (RPA) 工具,广泛应用于办公自动化,能够帮助用户自动执行重复性高的电子邮件处理任务。 首先,我们需要了解UIPath的工作流程。UIPath Studio是其主要的开发环境,它...
电子邮件系统是互联网上不可或缺的一部分,它使得用户能够方便地发送和接收消息。在这个系统中,邮件服务器和邮件客户端起着核心作用。让我们深入探讨这两个关键组件以及它们如何协同工作来实现简单邮件处理。 邮件...
本文将深入探讨“电子功用”在电子邮件系统中发送和处理组消息的方法,这些方法旨在提高效率,确保信息的有效传达,并简化群组沟通。 1. **群组邮件列表**:在电子邮件系统中,群组邮件列表是一种高效的方式,它...
消息中间件会接收到这个邮件消息,并根据配置的邮件服务器信息,利用JavaMail API实际发送邮件。由于发送邮件的过程是在消息中间件中完成的,因此不会直接影响到业务应用的性能。此外,如果邮件发送失败,消息中间件...