快速便捷的旅行邮件:OfflineIMAP

作者:John Goerzen

对于许多人来说,电子邮件是互联网最重要的一项功能。我们在家、在工作场所、在旅行途中以及在许多不同的计算机上阅读电子邮件。但是,从所有这些地方查看相同的邮件很困难。如果您在家中删除一条消息,当您从工作场所查看同一帐户时,它可能不会显示为已删除。更糟糕的是,您可能只能在一台机器上查看给定的消息。如果您有时想将邮件下载到笔记本电脑并在没有任何互联网连接的情况下阅读,情况会变得更加复杂。

有些人尝试通过在其邮件客户端中使用 IMAP 来解决这些问题。但是 IMAP 可能很慢且支持不佳;尤其是在连接速度较慢的情况下,它往往会使邮件阅读变得不愉快。我最近就面临着这种情况——我是一个非常恼火的程序员。许多程序之所以出现,是因为某处的程序员感到恼火。因此,我编写了 OfflineIMAP。

关于 OfflineIMAP

OfflineIMAP 旨在让您从许多不同的计算机上读取相同的邮箱,无论是否有活动的 Internet 连接。它执行双向同步,这意味着您所做的任何更改最终都会反映在您的所有机器上。在其最常见的形式中,OfflineIMAP 的工作原理是连接到 IMAP 服务器并将您的文件夹同步到本地机器上的一系列 Maildir 文件夹。尽管名称如此,但即使您从不离线阅读邮件,OfflineIMAP 也很有用。

安装 OfflineIMAP

OfflineIMAP 的安装非常简单。访问 OfflineIMAP 主页:quux.org/devel/offlineimap,然后下载 .deb 或 tar.gz 文件。Debian 用户只需运行dpkg -i offlineimap.deb即可安装它,然后使用apt-get -f install来修复任何缺失的依赖项。如果您没有运行 Debian,请确保您已安装 Python 2.2 或更高版本。如果您尚未安装 Python,请咨询您的发行版或访问 www.python.org 下载它。

当您准备好安装 OfflineIMAP 时,运行tar -zxvf offlineimap_4.0.2.tar.gz解压缩源代码。更改到新目录,然后以 root 用户身份运行python setup.py install。如果您遇到困难,OfflineIMAP 手册包含更多安装提示。

基本配置

OfflineIMAP 配置在 ~/.offlineimaprc 文件中完成。该文件有三个不同的部分:general(常规),它控制 OfflineIMAP 的整体行为;repository(存储库),它描述邮件的存储位置;以及 account(帐户),它描述了如何将两个存储库同步在一起。一个基本的、简单的设置只需要一个小的配置文件。这是一个示例

[general]
accounts = MyMail

[Account MyMail]
localrepository = MyMailLocal
remoterepository = MyMailRemote

[Repository MyMailLocal]
type = Maildir
localfolders = ~/MyMail

[Repository MyMailRemote]
type = IMAP
remotehost = hostname.example.com
remoteuser = my-username-goes-here
ssl = yes

此示例定义了一个帐户 MyMail。MyMail 帐户从 hostname.example.com 服务器同步到本地机器上的 ~/MyMail 目录。所有远程文件夹都会被复制。如果您的 IMAP 提供商不支持 SSL 加密,请删除ssl = yes行。现在,运行offlineimap。系统会要求您输入密码,然后它会同步您的邮箱一次并退出。

持续同步

如果您在阅读邮件时连接到互联网,您可以让 OfflineIMAP 持续保持您的本地树与服务器同步。为此,只需添加一个autorefresh行到您的帐户部分。例如,您可以修改您的帐户部分,使其如下所示

[Account MyMail]
localrepository = MyMailLocal
remoterepository = MyMailRemote
autorefresh = 5

当您现在运行 OfflineIMAP 时,它会像以前一样同步您的邮箱。但是当它完成时,它不会退出,而是继续运行,每五分钟同步一次您的邮件。

同步多个帐户

OfflineIMAP 完全能够同步多个帐户。例如,您可能希望能够阅读来自您的工作电子邮件和家庭电子邮件的邮件。为此,为每个帐户添加一个帐户和两个存储库部分,确保使用唯一的名称。然后,将帐户添加到 general 部分的 accounts 列表中。用逗号分隔名称。

在本地端,您应确保每个帐户同步到不同的目录。否则,可能会发生混乱和损坏。

提升性能

OfflineIMAP 的默认设置(如以上示例所示)非常保守。它试图尽可能多地与 IMAP 服务器开箱即用,因此偶尔会引起麻烦的高级功能默认情况下处于禁用状态。

如果您有很多邮件文件夹或每个文件夹中收到大量邮件,则同步过程可能会很慢。如果您使用的是高延迟互联网连接(例如调制解调器或卫星),则尤其如此。为了加快速度,OfflineIMAP 能够同时建立与服务器的多个连接。然后,它能够并行执行任务。例如,OfflineIMAP 可能会同时下载三条消息并同步两个文件夹。

OfflineIMAP 提供了几个配置选项。首先,您应该在 general 部分中添加如下行maxsyncaccounts = 5这使 OfflineIMAP 能够同时同步多个帐户,这几乎总是一件好事。其次,在每个帐户远程部分的存储库部分中,您可以控制要使用的并行度。例如,您可以在我们的示例中添加一行说明maxconnections = 3到 MyMailRemote 存储库部分。这允许 OfflineIMAP 建立最多三个与服务器的连接。

如果您正在使用上述 autorefresh 选项执行连续同步,则还有另一个延迟来源。每次 OfflineIMAP 开始同步帐户时,它都会连接到服务器。当它完成特定同步时,它会断开连接。在许多情况下,建立这些连接可能很慢。OfflineIMAP 提供了一个选项,即使在同步之间也保持连接打开。问题是一些服务器会断开长时间处于空闲状态的客户端的连接。为了解决这个问题,OfflineIMAP 还可以每隔一段时间发送少量流量,以确保计时器不会过期。要利用这些功能,请在远程存储库部分添加如下行

holdconnectionopen = true
keepalive = 60

Keepalive 以秒为单位给出,而 autorefresh 以分钟为单位给出。

用户界面

OfflineIMAP 附带了许多不同的用户界面。最常见的两个是 Tk.Blinkenlights 和 Curses.Blinkenlights。前者在您的 X 桌面上呈现一个小的图形窗口,显示 OfflineIMAP 的进度。后者在终端中运行,并提供了一个不错的进度监视器(参见图 1 和图 2)。

Fast Convenient Mail for Travel: OfflineIMAP

图 1. OfflineIMAP 的 Tk.Blinkenlights GUI 界面

Fast Convenient Mail for Travel: OfflineIMAP

图 2. 在终端窗口中运行的 Curses.Blinkenlights 界面

使用 Tk.Blinkenlights 界面,您可以单击“立即同步”按钮来强制帐户立即同步。您可以通过按帐户名称旁边的数字在 Curses.Blinkenlights 界面中执行相同的操作。这两个界面都显示当前活动的日志。您还可以获得令人着迷的闪烁状态灯显示,这样您在观看同步发生时就不会感到无聊。

TTY.TTYUI 界面无需任何 Curses 支持即可运行——它不使用任何颜色或终端控件。它可以读取密码输入,但它不提供其他交互功能。

Noninteractive.Basic 是一种用户界面,旨在永不接收来自用户的任何输入。但是,它可以显示状态消息。如果您需要密码才能登录到远程服务器,请添加如下行remotepass = mypassword到配置文件的远程存储库部分。

最后,Noninteractive.Quiet 更进一步,不输出状态消息。有些人喜欢从 cron 作业运行 OfflineIMAP,而 Noninteractive.Quiet 非常适合这样做。

您可以通过两种方式之一指定应使用哪个用户界面。首先,您可以使用 OfflineIMAP 命令行上的 -u 选项。例如,您可以运行offlineimap -u Curses.Blinkenlights。或者,您可以将 ui 行添加到您的 general 部分,如下所示

ui = Tk.Blinkenlights, Curses.Blinkenlights,
     TTY.TTYUI

通过此配置,OfflineIMAP 首先尝试 Tk.Blinkenlights 界面。如果您的 Python 不支持 Tk,或者您未在 X 下运行,则它会尝试 Curses.Blinkenlights 界面。如果这也失败了,则尝试 TTY.TTYUI 界面。如果即使这也不起作用,OfflineIMAP 将中止并显示错误。

选择文件夹

默认情况下,OfflineIMAP 会询问您的远程 IMAP 服务器哪些文件夹可供您使用,并同步所有文件夹。可以将 folderfilter 选项添加到您的远程存储库部分,以限制要引入的内容。folderfilter 选项是一个非常强大的选项。与您迄今为止看到的其他选项不同,folderfilter 实际上期望被传递一个 Python 函数。该函数接受一个参数,如果应该包含该文件夹,则应返回 true。

Python 提供了一个名为 lambda 的功能,可让您动态创建简单函数。因此,您可以构建一些复杂的规则。以下是一些示例。您可以指定要同步的一组文件夹。您可以使用 Python 的 in 运算符来测试所讨论的文件夹是否在列表中,如下所示

folderfilter = lambda foldername: foldername in
      ['INBOX', 'Sent Mail',
       'Received']

此代码仅同步三个命名的文件夹。请注意第二行和第三行的缩进——如果您缩进它们,配置解析器会将它们视为单个语句的一部分。

您还可以指定要排除的文件夹

folderfilter = lambda foldername: foldername not in
      ['Spam', 'Junk']

在此示例中,除了 Spam 和 Junk 之外的所有文件夹都已同步。

您还可以使用正则表达式,例如

folderfilter = lambda foldername:
     not re.search('(^Trash$|Del)', foldername)

此输入导致名为 Trash 的文件夹以及所有包含文本 Del 的文件夹被排除。

更改文件夹名称

有时,您可能希望在将文件夹存储在本地端之前更改文件夹名称。OfflineIMAP 提供了一个名为 nametrans 的选项,也在远程存储库部分中指定,以完全做到这一点。某些 IMAP 服务器(例如 Courier)将“INBOX.”添加到所有文件夹的开头,这可能会很烦人。nametrans 功能让您可以摆脱它。这是一个例子

nametrans = lambda foldername:
   re.sub('^INBOX\.', '', foldername)

与 folderfilter 一样,nametrans 也接受 Python 表达式。此表达式接收文件夹名称作为参数,并应返回新的和改进的文件夹名称。在此示例中,任何名称以 INBOX. 开头的文件夹都会删除前导的 INBOX.。重要的是不仅要删除前导的 INBOX;文件夹 INBOX 本身确实存在,因此您最终会得到一个空文件夹名称(这是一件坏事)。

使用 nametrans 规则时也要小心。您必须确保 nametrans 为每个文件夹返回不同的值。如果它为两个不同的文件夹返回相同的内容,则可能会发生不好的事情。

如果您想知道,nametrans 不会更改 folderfilter。也就是说,您的 folderfilter 规则在 nametrans 生效之前对文件夹名称进行操作。

同步两个 IMAP 服务器

某些邮件阅读器不太支持 Maildir 层次结构。对于他们来说,OfflineIMAP 引入了一项新功能:直接同步两个 IMAP 服务器的能力。想法是这样的:您在本地机器上安装一个 IMAP 服务器。您的邮件阅读器可能已经具有缓慢的 IMAP 支持,但在访问位于您自己的机器上的 IMAP 服务器时速度相当快。邮件阅读器永远不需要知道 OfflineIMAP 将消息放在文件夹中。

要实现此目的,您需要对本地存储库部分进行一些简单的更改。首先,将类型从 Maildir 更改为 IMAP。其次,删除 localfolders 和其他 Maildir 信息,而是指定 IMAP 配置,例如 remotehost 和 remoteuser。最后,删除您的 ~/.offlineimap 目录以确保旧的状态信息不会残留。

某些选项仍然仅在远程部分中受支持——nametrans 和 folderfilter 是两个示例——但与连接本身相关的选项在两个位置都受支持。实际上,您可以将本地 IMAP 服务器放在远程于您的机器上。

结论

OfflineIMAP 是一个强大的邮件解决方案。在本文中,我向您介绍了 OfflineIMAP 的基础知识,但您仍然可以使用它做更多的事情。要了解更多信息,请查看 OfflineIMAP 主页和示例配置文件。如果您是 Python 程序员,您还会找到一些不错的 Python 代码钩子。

John Goerzen 自 1996 年以来一直为 Linux 编程,目前是 Software in the Public Interest, Inc. 的副总裁。欢迎您通过 jgoerzen@complete.org 提出您的意见。

加载 Disqus 评论