IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Linux  >

功能丰富的 Perl: 通过 Perl 使用 IMAP

用 Mail::IMAPClient CPAN 模块构建的 IMAP 客户机

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

Teodor Zlatanov (tzz@iglou.com), 程序员, Gold Software Systems

2004 年 3 月 09 日

Ted 向您介绍如何使用 Mail::IMAPClient CPAN 模块访问 IMAP。通过使用 Mail::IMAPClient,他构建了可替代其它 IMAP 和 POP3 邮件检查器的 ifrom 实用程序。ifrom 有其独一无二的优点,可利用这些优点列出、打印和移动 IMAP 服务器上的消息,并备份 IMAP 邮件。

IMAP(因特网消息访问协议,Internet Message Access Protocol)及其广为使用的同类协议 POP3(邮局协议,Post Office Protocol),都是得以广泛实现的协议,它们允许对电子邮件进行访问。尽管 POP 普遍存在,但 IMAP 在许多情况下是处理邮件的更佳选择。例如,POP3 的单一邮箱模型会导致数千条大型消息都处于一个位置,从而使得对邮箱的访问和控制成了件麻烦事。而 IMAP 则专门设计用于在服务器上按不同的邮箱存放消息。另外,IMAP 可以在现有的 IMAP 连接上检查新邮件,而 POP3 每次必须打开新的连接。(有关这两个协议的技术细节的链接,请查阅本文的 参考资料一节。POP3 RFC 在“Scaling and Operational Considerations”一节中专门介绍了连接。)

在本文中,我将主要讨论 IMAP,并介绍我自己管理电子邮件的 ifrom 实用程序(请参阅 参考资料以下载 ifrom.pl)。我本人使用 ifrom 列出、打印和移动 IMAP 服务器上的消息。我还每晚运行它以备份我的 IMAP 邮件,因为我对 IMAP 服务器不放心。(并且要记住,仅仅因为您不放心并不表示它们 不会给您找麻烦。)

列出消息

ifrom 最简单的调用就是如此,没有任何选项。它将访问您在 IMAP 服务器上的 INBOX 邮箱,然后用有编号的列表列出消息。 -host 选项告诉 ifrom 联系哪个 IMAP 服务器。

请注意, ifrom 使用 AppConfig 模块,该模块提供缺省值以及其它很好的功能。

ifrom 中的认证是用明文 LOGIN 进行的(意味着您用明文向 IMAP 服务器指定密码和用户名)。根据 参考资料中所提供的链接信息,了解 Mail::IMAPClient 包的认证机制能力。您可以使用带 -crammd5 开关的 CRAM-MD5 认证; Mail::IMAPClient 不支持别的机制,因此我不必建立一个通用的 -authentication 开关。请注意,您可以实现自己的认证;请参阅 Mail::IMAPClient 文档。


清单 1. 至 IMAP 服务器的认证
if ($config->CRAMMD5())
{
 my $authmech = "CRAM-MD5";
 if ($imap->has_capability($authmech))
 {
  print "Switching to $authmech authentication\n";
  $imap->Authmechanism($authmech);
 }
}

您可以在命令行用 -user-password 开关或通过 authinfo(也称为 netrc)文件指定名称和密码。authinfo 文件存储了一列认证选项,如:


清单 2. ifrom 的 authinfo 文件格式
machine imap.yourserver.here login joe password JoeSecret
machine imap.yourserver.there password FredSecret port 244

authinfo 文件供其它程序使用,其格式随那些程序的需要而变化; ifrom 解析该文件以寻找 machine、login、password 和 port 关键字。如果其中任何一个关键字未被指定,则使用缺省值。authinfo 文件覆盖 -user-password 命令行开关。请确保您的 authinfo 文件只对您可读!

机器(machine)名称是您的 IMAP 服务器名称。authinfo 文件中的机器名称必须与通过 -host 开关向 ifrom 给出的名称完全匹配。

ifrom 与服务器连接时,它将 Peek 变量设为 1,以便我们检查的消息没有标记为已读(read)。

接着, ifrom 打开 -mailbox 开关指定的邮箱。它在缺省情况下是 INBOX:标准 IMAP 主邮箱。

完成这些操作以后, ifrom 用以下格式打印出消息:


清单 3. 用 printf 打印消息头
printf "%5d %-35.35s %s\n", $count, $address,
   ((defined $data->{Subject}->[0]) ? $data->{Subject}->[0] : '');

$address 中发件人的地址会尽可能从完整的用户名抽取出来,使用的是简单的正则表达式匹配。

如果对 ifrom 使用 -dump 开关,它还将在消息头之后打印消息的内容。如果您没有时间启动电子邮件应用程序来查看一封紧急消息的内容,那么这个选项很有用。可以在 ifrom 打印大型消息时中断它;消息不会被删除或受影响,因为我们打开服务器时将 Peek 选项设为 1。

请注意, -dump 开关使用 body_string() 函数,而稍后我们将看到的 -backup 选项使用 message_string() 函数。





回页首


移动消息

有时候,您希望将消息从一个邮箱移到另一个邮箱。如果有许多消息,而您的邮件客户机在移动它们时与 IMAP 服务器的连接断开(在慢速网络链接上常常发生这种情况),那么可以使用这一能力。

只需使用 -mailbox-to 选项。非常简单。照下面那样运行 ifrom


清单 4. 用 ifrom 移动消息
ifrom -mailbox newmail -to archive

您还可以用 -n 开关告诉 ifrom 在移动了某一数量的消息之后停止移动消息。

下面是进行移动的实际代码。IMAP 有内置的项目移动,因此客户机只需调用 move() 函数即可。


清单 5. Perl 如何移动消息
 foreach my $message (@msg_list)
 {
  $count++;
  if ($config->TO)
  {
   die "Could not move message $message: $!" unless $imap->move($config->TO, $message);
   print "Moved message $message to " . $config->TO, "\n";
   $imap->expunge() if $config->EXPUNGE_OFTEN;
   last if $count >= $config->N;
  }
 }
 if ($config->TO)
 {
  $imap->expunge();
 }

在 IMAP 中移动消息无需考虑 Peek 设置。即使它被设为 1 也无妨, Peek 只和查看消息的操作有关。移动消息时,总是在消息的副本被保存在目的地邮箱之后才删除原始消息,这一点和 Mail::IMAPClient 的实现相同。





回页首


清除和删除邮箱

在上面移动消息的代码中,您肯定已经注意到神秘的 expunge() 函数。它只是告诉 IMAP 服务器清理邮箱,除去所有标记为已删除(deleted)的消息。通常,在列出消息时,我们不把它们标记为“已删除”,因为我们将 Peek 设为 1。然而, move() 函数将象上面所说的那样把它们标记为已删除。为了真正删除那些消息,必须调用 expunge() 函数。

如果 IMAP 确实移动了消息,那么大可不必如此,但 IMAP 没有那样做,因此 Mail::IMAPClientmove() 实现为在 copy() 之后进行删除。

这为什么会有用呢?我们都会不小心删除邮件。IMAP 的这一特性允许您在执行最终清除之前取回已删除的消息。

当您使用不可靠的链接且连接随时会断开时,可使用 -expunge_often 标志来移动消息。它确保在移动每条消息后,将调用 expunge() (否则,消息仍将留在邮箱中)。然而,更好的方法是在移动所有消息之后直接使用 expunge() ,并加上 -n 标志。这样,将在每移动 10 条、15 条或您指定数目的消息之后调用 expunge()

我还为 ifrom 提供了 -delete_mailbox_really 选项。当使用这一选项时, ifrom 将删除由 -mailbox 开关命名的任何邮箱,因此不要对缺省的 INBOX 邮箱使用该选项!所涉及的 Perl 代码再简单不过了:


清单 6. 删除邮箱
if ($config->DELETE_MAILBOX_REALLY)
{
 $imap->delete($config->MAILBOX)
  or warn "Could not delete mailbox " . $config->MAILBOX . "\n";
}





回页首


备份 IMAP 邮件

我喜欢做备份;这多次挽救了我的工作。当涉及电子邮件时,备份似乎不那么重要,但实际上我们的日常交流有许多是通过电子邮件进行的。通常,我们很迟才发觉本不应该删掉关于周五穿短裤的备忘或一个月后全公司会议的提醒。如果不注意的话,那位应您要求找寻丢失备忘而工作过度的助手将告诉您的上司,您一直在办公桌里囤积可可饮料包。别让这事发生在您身上。

要使用 ifrom 备份邮件,只需对它使用 -backup 标志。 -savedir 开关也很重要 — 将它设置为您要保存消息的位置。其它所有选项 — authentication、host 和 port 等 — 都和常规 ifrom 一样工作。 -mailbox 参数不起作用,因为 ifrom 备份所有的邮箱。我本来可以有一个特殊的 -backup_mailbox 标志用来覆盖“所有邮箱”,但老实说我从不需要它。

IMAP 邮箱中的消息都有唯一的编号。我们在 ifrom 中利用这一事实,将每条消息保存在文件 savedir/mailbox/messageNumber 中,如果文件已经存在则跳过该消息。


清单 7. 备份消息
  foreach my $message (@msg_list)
  {
   my $filename = "$dir/$f/$message";
   next if -e $filename;
   print "saving message $f/$message to $filename\n" if $config->VERBOSE;
   my $data_fh = new IO::File $filename, "w";
   my $data = $imap->message_string($message);
   warn "Empty message data for $f/$message" unless defined $data && length $data;
   $data_fh->print($data);
  }

我们使用 message_string() 函数,因为它检索整个消息。先前 -dump 开关使用的 body_string() 函数会跳过消息头,这些消息头在您快速浏览电子邮件时通常没什么价值。





回页首


结束语

ifrom 实用程序是那些不断发展并吸收有用特性的小脚本中的一个。我不会对其他人编写的 ifrom 脚本感到惊讶。列出消息这一基本特性一直对我非常有帮助,因为当我在机房做系统维护或使用慢速连接时,检查 IMAP 邮件有时是不可能的。

我已经几次在不小心丢失重要邮件时使用了 ifrom 备份。现在我放心了,因为我知道即使运行我的 IMAP 服务器的 ISP 丢失了我的邮箱(我以前碰到过这种事情),我最多也就丢失一天的邮件。

我希望这篇文章让您有兴趣编写 Perl 代码与 IMAP 进行相互操作,并希望 ifrom 对您有用。如果您使用了 ifrom ,我希望了解您的使用情况。



参考资料



关于作者

Teodor Zlatanov

Teodor Zlatanov 1999 年毕业于波士顿大学计算机工程专业,获硕士学位。自从 1992 年以来,他一直从事编程工作,使用的语言包括 Perl、Java、C 和 C++。他的主要兴趣在于文本解析、三层客户机-服务器数据库体系结构、UNIX 系统管理、CORBA 以及项目管理方面的开放源码工作。可以通过 tzz-at-iglou.com与 Teodor 联系。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款