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

developerWorks 中国  >  Linux | Open source  >

功能丰富的 Perl: 通过 Perl 使用 IMAP,第 2 部分

检查 Maildir 并使用 ifrom.pl 脚本和 Mail::IMAPClient 建立隧道

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

Teodor Zlatanov (tzz@bu.edu), 程序员, Gold Software Systems

2005 年 6 月 06 日

Ted 回到了使用 Mail::IMAPClient 来访问 IMAP 的主题,他将 ifrom.pl 当作是另外一个检查 IMAP 和 POP3 邮件的替代工具。这次 Ted 会介绍使用隧道(有时称为“端口转发”),并将这个脚本应用于 Maildir 邮件存储格式。

本文是“通过 Perl 使用 IMAP,第 1 部分” 的续篇,再次讨论了 ifrom.pl 工具。在阅读本文之前,请首先阅读上一篇文章中的一些介绍材料,从而能够很好地理解 ifrom.ipl 的一些特有机制。本文中的新主题包括 Maildir 邮件存储格式和隧道(有时也称为端口转发)。

对 ifrom.pl 的扩展已经满足了我作为一个 IMAP 用户的需求,因此我希望能证明它对您来说也非常有用。

Stunnel,通用的 SSL 封装程序

Stunnel 是一个程序,它允许您使用 SSL 对任意的 TCP 连接进行加密,这在 UNIX® 和 Windows® 上都可以使用。使用 Stunnel,您可以通过让 Stunnel 提供加密来增强没有使用 SSL 的守护进程和协议(POP、IMAP、LDAP)的安全性,而不需要对守护进程的代码进行任何修改。源代码(遵守 GNU GPL)并不是一个完整的产品;您将仍然需要一个 SSL 函数库来编译 stunnel(这就是说 stunnel 只能支持您的 SSL 库,除非您希望对源代码进行修改)。

OpenSSH 对您的报告进行加密

OpenSSH 是一个免费的 SSH 协议网络连接工具包;它对所有的网络通信(包括密码)进行加密,从而有效地消除网络窃听、连接窃取以及其他类型的网络攻击,这些攻击在通过不加密的 Internet(例如 telnet、rlogin、ftp 以及其他程序)传输用户密码时都会发生。OpenSSH 还提供了很多加密隧道能力,以及很多认证方法。OpenSSH 主要是由 OpenBSD Project 开发的,支持 SSH 协议 1.3、1.5 和 2.0,包括 ssh (取代 rlogin 和 telnet)、scp(取代 rcp)、sftp(取代 ftp)、sshd(该包的服务器端),以及一些基本的工具,例如 ssh-add、ssh-agent、ssh-keysign、ssh-keyscan、ssh-keygen 和 sftp-server。

隧道,也称为端口转发

网络连接的 隧道(也称为 端口转发,不过从技术上来讲,二者并不完全相同)是计算环境中的一种常见技术。要对一个连接使用隧道,就是要将其重定向到一个或多个目的地(这并不需要是远程机器)。隧道应用程序通常并不知道自己所携带的是什么数据。使用隧道的程序也不知道。常见的 UNIX 隧道应用程序有 stunnel 和 OpenSSH 等。(OpenSSH 也有一些其他的用法,但是隧道非常适合它。)任何使用一个网络端口的程序都可以再使用一个隧道网络端口。您可以认为它就像是一个可以到达下一个门的电话连接,这就像它可以到达世界上的各个角落一样简单。

除 OpenSSH 之外,其他 SSH 版本也都支持隧道,但是由于 OpenSSH 的广泛流行和无限制的可用性,本文中我们就使用 OpenSSH 进行讨论。

在限制性环境中,隧道会非常有用。假设不允许您连接到一个外部 IMAP 服务器,因为防火墙配置限制了这种行为。那么您可以以端口转发模式运行 OpenSSH,这样它就可以将外部 IMAP 服务器的端口 143(IMAP)转发到您的本地机器上的 XYZ 端口上。您甚至并不需要登录到 IMAP 服务器上就可以实现这种功能!实际上,您只需要一个中间跳板,它可以接受 SSH 连接,然后连接到 IMAP 服务器。当然,如果您可以直接 SSH 到 IMAP 服务器上,那么连接的速度会更快,也更可靠,因为这样就消除了这个中间跳板的影响。

您也可以对整个连接过程进行反向,从 IMAP 服务器使用隧道连接到您自己的机器。

隧道的另外一种用途是对连接进行加密。当您使用 OpenSSH 启用隧道时,如果 IMAP 服务器没有提供安全连接,那么您可以使用它的加密能力来对 IMAP 报文进行加密。如果 IMAP 服务器早已提供了安全连接,那您仍然可以这样做,这样就对连接加密了两次。有些疯狂,不是吗?

为了提供隧道的功能,我在 ifrom.pl 中增加了 TUNNEL 选项。这非常简单。实际上,我 10 个月大的女儿在一半时间内就可以编写一个更好的程序(如果她不先啃键盘的话)。我只是在 ifrom.pl 中添加了一个 -tunnel 选项,在这个选项出现时,我会使用 system() 来运行它。


清单 1. 在 ifrom.pl 中使用 TUNNEL 选项
                
# use -tunnel like so with OpenSSH:
# ifrom.pl -tunnel "ssh REMOTEHOST.COM -N -T -n -L 2002:127.0.0.1:143 &"
#          [other options to follow...]
# This forwards the REMOTE port 143 on REMOTEHOST.COM to the LOCAL
# port 2002.  See the OpenSSH documentation for details and more
# examples.  There is no intermediate jump point.
if ($config->TUNNEL())
{
 system($config->TUNNEL());
}

然后,我实现了 ifrom.pl 应该完成的事情。也就是说,我连接到 IMAP 服务器上并检查邮件。这就是为什么我们会说隧道非常方便 —— 使用隧道的程序并不要做什么不常见的事情,因为所有的魔力都来自于数据传输层。在我的例子中,ifrom.pl 必须要稍加修改,但是整个程序的主要逻辑并没有发生变化。





回页首


Maildir 支持

Maildir 是一种邮件存储格式。Courier IMAP、qmail 以及其他一些服务器都使用这种格式来存储用户的邮件。Maildir 包含了一个目录,其中有子目录 cur、new 和 tmp。可能还有其他一些子目录,不过都可以忽略。为了简单起见,我将 Maildir 目录称为“Maildir”。

在上一段中所提到的 3 个子目录中,只有“new”目录直接与我们有关。在每个子目录中,Maildir 都会存储一些信息,每个文件一条。这些文件的名字都是惟一的。

“new”子目录中包含了 Maildir 中的新消息。何为“新”这一语义要取决于特定的邮件传输代理(MDA)和其他使用 Maildir 的程序。例如,Courier IMAP 通过将消息移出 Maildir 的 “new” 子目录而将这些消息标记为已查看的。

首先,我在 ifrom.pl 中添加了 -maildir 开关。这只是一个标量,告诉我不要尝试 IMAP 连接,而是执行 Maildir 逻辑。

Maildir 处理是通过一个 glob() 调用进行的。这意味着使用了本地 shell 的工具进行通配符“Maildir/new/*”的匹配。我还可以使用 opendir()readdir(),但是 glob() 调用要简单得多。

对于使用 glob() 调用找到的每个文件,我会从中搜索发送者和主题并打印这些信息。然后,如果还指定了 -dump-print 开关,就通过逐一打印相关的文件来打印所请求的消息。文件的顺序是由 shell 的 glob() 函数决定的。我并没有对日期进行排序,因为 shell 对 Maildir 文件的排序已经很好了(如果这些文件都是由 qmail 传输的,那么它们早就根据日期进行了排序)。在使用更多邮件传输代理时,需要对日期进行排序。


清单 2. ifrom.pl 对 Maildir 的支持
                
if ($config->MAILDIR())
{
 my $count = 0;
 foreach my $file (glob($config->MAILDIR() . '/new/*'))
 {
  $count++;
  open M, "<$file";
  my $address = 'UNKNOWN';
  my $subject = 'UNKNOWN';
  while (<M>)
  {
   $address = $1 if m/^From: (.*)/;	# the sender of the message
   $subject = $1 if m/^Subject: (.*)/;	# the subject of the message
   last if $_ eq "\n";
  }
  printf "%5d %-35.35s %s\n", $count, $address, $subject;
  if ($config->DUMP || grep {$_ == $count} @{$config->PRINT})
  {
   close M;
   open M, "<$file";
   print MARKER();
   print foreach <M>;
   print MARKER();
  }
 }
}





回页首


导入邮件

作为一个邮件服务器的管理员,我需要将用户的邮件从所谓的 mbox 格式(一个大文件,其中包含消息)转换成 IMAP 服务器格式。由于 ifrom.pl 早已具有了必需的 IMAP 逻辑,因此我向其中添加了 -import 开关,并使用 Mail::Box 模块来处理从 mbox 文件读取邮件的过程。

这是一个速度很慢的方法,因为每条消息都必须通过网络连接进行传输。为了真正能够批量导入很多用户的邮件,您可能需要考虑一种更为复杂的方法,它需要对您的站点进行一些定制和裁剪。例如,如果您的站点使用 Maildirs 来存储邮件,并且您现在正从 mbox 格式迁移过来,那就可以使用 mbox 到 Maildir 的转换程序,例如 safecat(顺便说一下,safecat 还有很多其他的用途)。有关 mbox 到 Maildir 的转换,请参阅 safecat 的 Web 站点(参阅 参考资料)。

-mailboxauto_mailbox 参数(保存在 MAILBOX_AUTO 常量中)会告诉 ifrom.pl 根据文件名使用邮箱名。与 PREFIX 参数一起,我在 -import 开关的值上使用 basename() 函数。如果没有指定 auto_mailbox,那么不论 -mailbox 说什么,新邮箱就叫什么。因此,-import A/B/C/FILE -mailbox auto_mailbox 会创建并生成一个名为“FILE”的 IMAP 文件夹,而 -import A/B/C/FILE -mailbox XYZ 则会创建一个名为“XYZ”的文件夹。

此外,如果看到一个文件名为“XYZ.msf”(XYZ 代表任意的名称,.msf 是 Mozilla 的 mbox 索引文件扩展名),那么就只有“XYZ”会被用作文件名。这会让您实现类似于 find DIRECTORY -name "*.msf" -exec ifrom.pl -import {} ... \; 的功能,这会将 find 的结果逐一传递给 ifrom.pl。它会发现所有的 Mozilla 索引文件,然后 ifrom.pl 会逐一导入相应的邮箱。

PREFIX 向 ifrom.pl 告知 IMAP 服务器的前缀。这个前缀在 IMAP 服务器上可以使用 namespace() 函数真正地得到,但是获得这个前缀的逻辑对于这样一个简单的 ifrom.pl 脚本来说太过复杂了。如果您需要 IMAP 前缀或分隔符,可以用 namespace() 得到。作为一个例子,通用的 UW IMAP 前缀和分隔符是 "" 和 "/",因此邮箱就类似于“a/b/c”(注意文件名和目录名是类似的)。Courier IMAP 服务器前缀和分隔符是“INBOX.”和“.”,因此邮箱就类似于“INBOX.a.b.c”(注意结构 —— Courier 文件夹全都位于顶层的 Maildir 之下,而没有子目录)。

ifrom.pl 的 -dryrun 选项非常有用。它让您可以在 ifrom.pl 导入邮件时看到详细的信息,除非实际的导入操作并不发生。


清单 3. 导入邮件
                
elsif ($config->IMPORT)
{
 eval { require Mail::Box::Manager; };
 die "You need to install the Mail::Box module, exiting" if $!;
 my $file = $config->IMPORT;
 if ($file =~ m/^(.*)\.msf$/i)
 {
  $file = $1;
  print "MSF file detected, using $file as the file name\n";
 }
 die "Can't access import file $file" unless -r $file;
 if ($config->MAILBOX eq MAILBOX_AUTO)
 {
  $box = $config->PREFIX . basename($file);
 }
 my $mgr    = Mail::Box::Manager->new;
 my $folder = $mgr->open(folder => $file);
 my $i;
 if ($config->DRYRUN)
 {
  print "Skipping folder check and creation because a dry run was requested\n";
 }
 else
 {
  if ($imap->select($box))
  {
   print "Selected folder successfully, ready to import.\n"
    if $config->VERBOSE;
  }
  else
  {
   print "Could not select folder $box, trying to create it...\n";
   $imap->create($box)
    or die "Could not create import folder: $@\n";
  }
 }
 # Iterate over the messages.
 foreach ($folder->messages)
 {
  printf "Appending to mailbox %s, message %d: ID %s, %d lines\n",
   $box, ++$i, $_->messageId, $_->nrLines;
  if ($config->DRYRUN)
  {
   print "Skipping import because a dry run was requested\n";
  }
  else
  {
   $imap->append($box, $_->string);
  }
 }
}





回页首


其他改进和注意事项

我编写了一些帮助信息。它非常简短,但却信息丰富,您可以使用 ifrom.pl -help 来查看这些信息。

我添加了一个 MARKER() 常量,因为会频繁用到“\n===\n\n”。

对于 Maildirs 来说,我并没有添加 IMAP 中所有的特有代码,例如 -backup-delete_mailbox_really 开关。因为 Maildir 是一个本地特性,这就是说您可以通过本地挂载的文件系统来访问 Maildir 中的数据,备份和邮箱的删除都是非常简单的一些 shell 操作。例如,要备份一个 Maildir,请参考下面的例子:


清单 4. 备份本地的 Maildir
                
rsync -avP --delete MaildirLocation Destination
# so, for example, to back up /var/qmail/maildirs/tzz
# to /home/tzz/backups
rsync -avP --delete /var/qmail/maildirs/tzz /home/tzz/backups

请阅读 rsync 的文档 —— 它是一个常见的很好的备份和目录同步工具。如果您不使用这个工具,就忽略了目前最好的一个 UNIX 工具。

Maildir 和 IMAP 循环的逻辑看起来非常类似,但是它们的相似度还不足以进行合并。如果有另外一个邮件源被加入 ifrom.pl 中,那么可能就有意义将所有的循环都合并成一个循环了。现在,我并不认为值得合并这两个循环,因为这会使得整个程序的逻辑更容易产生混淆,但却没有得到什么好处。

我还添加了调用 -print 时可以从命令行中只接受数字的能力,这样就不用使用 ifrom.pl -print 1 -print 2,而是只要使用 ifrom.pl 1 2 即可 —— 我认为这对于用户来说会更好。代码非常简单,您只需要首先调用 $config->args() 即可。


清单 5. 为 -print 开关传递命令行参数
                
# all non-switch command-line arguments are implied -print requests
if (scalar @ARGV)
{
 $config->print($_) foreach @ARGV;
}





回页首


结束语

ifrom.pl 是我曾经编写的工具中功能最为强大的工具之一。它非常简单,却速度很快,可以实现我需要的一些功能。我希望您对本文满意,并希望 ifrom.pl 可以很好地为您服务。而且,我还希望您从本文中获得的有关 IMAP 和 Maildirs 的知识将来会有用。

您可以自由地使用 ifrom.pl,并将您的改进建议发送给我。



参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文

  • 通过 Perl 使用 IMAP,第 1 部分”(developerWorks,2003 年 6 月)向您简介了使用 Mail::IMAPClient CPAN 模块和 ifrom 工具来访问 IMAP 的知识。

  • 下载本文中使用的 ifrom.pl 脚本。

  • 阅读 developerWorks 上 Ted 撰写的 功能丰富的 Perl 系列 文章。

  • 访问 CPAN,这是 CPAN 模块包。

  • Mail::IMAPClient 手册页中查找更多有关 Mail::IMAPClient 的信息。

  • IMAP4rev1 RFC 2060 文档中定义了当前版本的 IMAP 协议。这是一篇必读物。

  • TAoUPThe Art of Unix Programming,是由 Eric Raymond 撰写的一本非常好的书,它可以帮助您理解为什么 -tunnel 选项是如此简单,但在 UNIX 环境中的功能却是如此强大。

  • safecat 是一个优秀的工具,用于将 mboxes 转换为 Maildirs。

  • Stunnel 是一个程序,可以让您使用 SSL 来加密任意的 TCP 连接,这在 UNIX 和 Windows 上都可以使用。

  • OpenSSH 是一个免费的 SSH 协议的网路连接工具,它可以对所有的网络通信(包括密码)进行加密,从而有效的消除网络窃听、连接窃取以及其他类型的网络攻击。

  • developerWorks Linux 专区 中可以找到更多为 Linux 开发者准备的参考资料。

  • 通过参与 developerWorks blogs 加入 developerWorks 社区。

  • 在 Developer Bookstore 的 Linux 专区购买 Linux 的打折书籍

  • 定购免费的 SEK for Linux,这是两张 DVD,其中包含了 IBM 在 Linux 平台上的最新试用软件,包括 DB2®、Lotus®、Rational®、Tivoli® 以及 WebSphere®。

  • 在您的下一个 Linux 开发项目中使用 IBM 试用软件,它们可以从 developerWorks 的 下载目录中获得。


关于作者

Teodor Zlatanov

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




对本文的评价










回页首


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