OfflineIMAP
OfflineIMAP 是一个 Python 实用程序,用于同步来自 IMAP 服务器的邮件。它不适用于 POP3 协议或 mbox,通常与 MUA(例如 Mutt)配对使用。
安装
安装 offlineimap 软件包。
配置
Offlineimap 发行版附带两个默认配置文件,都位于 /usr/share/offlineimap/
。offlineimap.conf
包含所有设置并有详细文档。或者,offlineimap.conf.minimal
没有注释,仅包含少量设置;请参阅:#Minimal。
将其中一个默认配置文件复制到 ~/.offlineimaprc
或 $XDG_CONFIG_HOME/offlineimap/config
。
极简
以下文件是 offlineimap.conf.minimal
的注释版本。
~/.offlineimaprc
[general] # List of accounts to be synced, separated by a comma. accounts = main [Account main] # Identifier for the local repository; e.g. the maildir to be synced via IMAP. localrepository = main-local # Identifier for the remote repository; i.e. the actual IMAP, usually non-local. remoterepository = main-remote [Repository main-local] # OfflineIMAP supports Maildir, GmailMaildir, and IMAP for local repositories. type = Maildir # Where should the mail be placed? localfolders = ~/mail [Repository main-remote] # Remote repos can be IMAP or Gmail, the latter being a preconfigured IMAP. # SSL and STARTTLS are enabled by default. type = IMAP remotehost = host.domain.tld remoteuser = username # Necessary for SSL connections, if using offlineimap version > 6.5.4 sslcacertfile = /etc/ssl/certs/ca-certificates.crt
选择性文件夹同步
要仅同步某些文件夹,您可以在 ~/.offlineimaprc
中帐户的 remote 部分使用 folderfilter。例如,以下配置将仅同步 Inbox
和 Sent
文件夹
~/.offlineimaprc
[Repository main-remote] # Synchronize only the folders Inbox and Sent: folderfilter = lambda foldername: foldername in ["Inbox", "Sent"] ...
有关更多选项,请参阅 官方文档。
自定义端口
某些 IMAP 服务器可能要求您连接到自定义端口,而不是默认的 993 端口。为此,请在 remote 部分的 ~/.offlineimaprc
中添加 remoteport 选项
~/.offlineimaprc
[Repository main-remote] remoteport=1234
用法
在运行 offlineimap 之前,创建分配给本地存储库的任何父目录
$ mkdir ~/mail
现在,运行程序
$ offlineimap
邮件帐户现在将被同步。如果出现任何问题,请仔细查看错误消息。OfflineIMAP 通常会非常详细地说明问题;部分原因是开发人员懒得从最终产品中删除回溯信息。
技巧与窍门
在后台运行 offlineimap
大多数其他邮件传输代理都假设用户将该工具用作 守护程序,默认情况下使程序定期同步。在 offlineimap 中,有一些设置可以控制后台任务。
令人困惑的是,它们分散在整个配置文件中
~/.offlineimaprc
# In the general section [general] # Controls how many accounts may be synced simultaneously maxsyncaccounts = 1 # In the account identifier [Account main] # Minutes between syncs autorefresh = 0.5 # Quick-syncs do not update if the only changes were to IMAP flags. # autorefresh=0.5 together with quick=10 yields # 10 quick refreshes between each full refresh, with 0.5 minutes between every # refresh, regardless of type. quick = 10 # In the remote repository identifier [Repository main-remote] # Instead of closing the connection once a sync is complete, offlineimap will # send empty data to the server to hold the connection open. A value of 60 # attempts to hold the connection for a minute between syncs (both quick and # autorefresh).This setting has no effect if autorefresh and holdconnectionopen # are not both set. keepalive = 60 # OfflineIMAP normally closes IMAP server connections between refreshes if # the global option autorefresh is specified. If you wish it to keep the # connection open, set this to true. This setting has no effect if autorefresh # is not set. holdconnectionopen = yes
要在登录时自动启动守护程序,请使用 --user
标志启动/启用 systemd/User 服务 offlineimap.service
。
如果您配置了多个帐户,建议使用 offlineimap@.service
而不是增加 maxsyncaccounts 参数[1]。只需 启动/启用 offlineimap@youraccountname.service
即可。
systemd 计时器
或者,可以使用 systemd-user 计时器 完全管理 OfflineIMAP,使用 --user
标志启动/启用 offlineimap-oneshot.timer
。
此计时器默认每 15 分钟运行 OfflineIMAP 一次。可以通过创建 drop-in 片段 轻松更改此设置。例如,以下内容将计时器修改为每 5 分钟检查一次
~/.config/systemd/user/offlineimap-oneshot.timer.d/timer.conf
[Timer] OnUnitInactiveSec=5m
为了更强大的解决方案,可以设置一个 watchdog,在 OfflineIMAP 冻结的情况下将其杀死
~/.config/systemd/user/offlineimap-oneshot.service.d/service.conf
[Service] WatchdogSec=300
为 mutt 自动生成邮箱
Mutt 不能简单地指向 IMAP 或 maildir 目录,并期望它猜测哪些子目录恰好是邮箱,但 offlineimap 可以生成一个 muttrc 片段,其中包含它同步的邮箱。
~/.offlineimaprc
[mbnames] enabled = yes filename = ~/.mutt/mailboxes header = "mailboxes " peritem = "+%(accountname)s/%(foldername)s" sep = " " footer = "\n"
然后将以下行添加到 ~/.mutt/muttrc
。
~/.mutt/muttrc
# IMAP: offlineimap set folder = "~/mail" source ~/.mutt/mailboxes set spoolfile = "+account/INBOX" set record = "+account/Sent\ Items" set postponed = "+account/Drafts"
account
是您在 ~/.offlineimaprc
中为您的 IMAP 帐户指定的名称。
Gmail 配置
此远程存储库专门为 Gmail 支持而配置,将文件夹名称中的大写字母替换为小写字母,以及其他一些小的添加。请记住,此配置不会同步所有邮件文件夹,因为它通常是不必要的,跳过它可以节省带宽成本
~/.offlineimaprc
[Repository gmail-remote] type = Gmail remoteuser = user@gmail.com remotepass = password nametrans = lambda foldername: re.sub ('^\[gmail\]', 'bak', re.sub ('sent_mail', 'sent', re.sub ('starred', 'flagged', re.sub (' ', '_', foldername.lower())))) folderfilter = lambda foldername: foldername not in ['[Gmail]/All Mail'] # Necessary as of OfflineIMAP 6.5.4 sslcacertfile = /etc/ssl/certs/ca-certificates.crt # Necessary to work around https://github.com/OfflineIMAP/offlineimap/issues/573 (versions 7.0.12, 7.2.1) ssl_version = tls1_2
- 如果您已将 Gmail 设置为另一种语言,则文件夹名称也可能显示为已翻译,例如“verzonden_berichten”而不是“sent_mail”。
- 在 6.3.5 版本之后,offlineimap 还会创建远程文件夹以匹配您的本地文件夹。因此,您可能还需要为本地存储库设置 nametrans 规则,以反转此 nametrans 规则的效果。如果您不想制定反向 nametrans 规则,则可以通过在远程配置中放置以下内容来禁用远程文件夹创建:
createfolders = False
- 截至 2012 年 10 月 1 日,gmail SSL 证书指纹并不总是相同的。这阻止了使用
cert_fingerprint
,并使sslcacertfile
成为 SSL 验证的更好解决方案(请参阅 #SSL 指纹不匹配)。
通过 oama 获取 OAuth2 访问令牌
oama (oama-binAUR) 是一个实用程序,为 IMAP/SMTP 客户端提供 OAuth2 凭据的续订功能和授权。
OfflineIMAP 可以从其配置中调用 Python 代码。因此,在开头的 [general]
部分中,添加以下行
~/.offlineimaprc
[general] pythonfile = ~/.offlineimap.py
在 Python 文件中,添加以下代码以通过 oama 检索 OAuth2 访问令牌
~/.offlineimap.py
import subprocess def get_token(email_address): return subprocess.run(["oama", "access", email_address], capture_output=True, text=True).stdout
现在回到配置文件,在 Gmail 帐户的存储库部分,添加以下内容
~/.offlineimaprc
auth_mechanisms = XOAUTH2 oauth2_client_id = YOUR_OAUTH2_CLIENT_ID oauth2_client_secret = YOUR_OAUTH2_CLIENT_SECRET oauth2_request_url = https://127.0.0.1/o/oauth2/token oauth2_access_token_eval = get_token("YOUR_EMAIL_ADDRESS_FOR_THIS_ACCOUNT")
密码管理
.netrc
将以下行添加到您的 ~/.netrc
machine hostname.tld login [your username] password [your password]
不要忘记给文件适当的权限,如 600 或 700
$ chmod 600 ~/.netrc
.netrc
文件中存储了多个帐户时,OfflineIMAP 不知道 如何检索密码。使用 GPG
GNU Privacy Guard 可用于将密码存储在加密文件中。首先设置 GnuPG,然后按照本节中的步骤操作。假设您可以一直使用您的 GPG 私钥 而无需输入密码。
首先在纯文本文件中键入电子邮件帐户的密码。在位于 tmpfs 上的具有 700 权限的安全目录中执行此操作,以避免将未加密的密码写入磁盘。然后使用 GnuPG 加密文件,并将自己设置为接收者。
删除不再需要的纯文本文件。将加密文件移动到最终位置,例如 ~/.offlineimappass.gpg
。
现在创建一个 python 函数,该函数将解密密码
~/.offlineimap.py
#! /usr/bin/env python from subprocess import check_output def get_pass(): return check_output("gpg -dq ~/.offlineimappass.gpg", shell=True).rstrip(b"\n")
从 ~/.offlineimaprc
加载此文件并指定定义的函数
~/.offlineimaprc
[general] # Path to file with arbitrary Python code to be loaded pythonfile = ~/.offlineimap.py ... [Repository example] # Decrypt and read the encrypted password remotepasseval = get_pass() ...
使用 pass
pass 是一个基于 GPG 的简单命令行密码管理器。
首先为您的电子邮件帐户创建密码
$ pass insert Mail/account
现在创建一个 python 函数,该函数将解密密码
~/.offlineimap.py
#! /usr/bin/env python from subprocess import check_output def get_pass(account): return check_output("pass Mail/" + account, shell=True).splitlines()[0]
这是多帐户设置的示例。您可以自定义 pass 的参数,如先前定义的那样。
从 ~/.offlineimaprc
加载此文件并指定定义的函数
~/.offlineimaprc
[general] # Path to file with arbitrary Python code to be loaded pythonfile = ~/.offlineimap.py ... [Repository Gmail] # Decrypt and read the encrypted password remotepasseval = get_pass("Gmail") ...
Gnome 密钥环
在远程存储库的配置中,remoteusereval/remotepasseval 字段可以设置为自定义 python 代码,该代码评估为用户名/密码。该代码可以是调用在 'pythonfile' 配置字段指向的 Python 脚本中定义的函数。根据以下小节创建 ~/.offlineimap.py
并在配置中使用它
[general] pythonfile = ~/.offlineimap.py [Repository examplerepo] type = IMAP remotehost = mail.example.com remoteusereval = get_username("examplerepo") remotepasseval = get_password("examplerepo")
gkgetsecret.py
确保已安装 gnome-keyring、python2AUR、python2-gobjectAUR 和 libsecret。然后使用以下内容创建 ~/.offlineimap.py
:gkgetsecret.py,并在 ~/.offlineimaprc
中设置 pythonfile = ~/.offlineimap.py
,如上所述。
如果您使用 seahorse 创建了密码,则可以从其描述中检索它。例如,存储在 gnome-keyring 中,描述为 _Password for me@myworkemail.com_ 的存储库 _Work_ 的密码可以通过将以下内容添加到 ~/.offlineimaprc
来检索
[Repository Work] ... remotepasseval = get_pw_from_desc("Password for me@myworkemail.com")
对于您希望也存储用户名的配置,最好使用 secret-tool 创建密码,因为这可以用于设置属性,例如用户名和存储库名称。考虑使用以下命令创建的密码
$ secret-tool store --label "Password for Work Email" username me@myworkemail.com repo Work
可以通过将以下内容添加到 ~/.offlineimaprc
来检索此帐户的用户名和密码
[Repository Work] ... remoteusereval = get_val_from_attrs("username", "repo", "Work") remotepasseval = get_pw_from_attrs("repo", "Work")
python-keyring
有一个通用解决方案,应该适用于任何密钥环。安装 python-keyring,然后更改您的 ~/.offlineimaprc
以说类似以下内容
[general] pythonfile = /home/user/offlineimap.py ... [Repository RemoteEmail] remoteuser = username@host.net remotepasseval = keyring.get_password("host","username") ...
并在 ~/offlineimap.py
中的某个位置添加 import keyring
。
现在您要做的就是设置您的密码,可以使用 python 脚本,如下所示
$ python >>> import keyring >>> keyring.set_password("host","username", "MYPASSWORD")
或者您可以使用 python-keyring 软件包提供的 keyring
命令
$ keyring --help $ keyring set host username Password for 'username' in 'host': $ keyring get host username password
它将从您的 (kwallet/gnome-) 密钥环中获取密码,而无需将其保留为纯文本或每次都输入。
Emacs EasyPG
请参阅 https://www.emacswiki.org/emacs/OfflineIMAP#toc2
KeePassXC 与 Freedesktop.org secret-service
安装 libsecret,在 KeepassXC 设置中启用 Freedesktop.org secret-service 集成,在 _数据库设置 > Secret Service 集成_ 中公开条目,并在 _编辑条目 > 高级_ 设置中添加带有 account@name.org
的附加属性 _Title_。现在命令 secret-tool lookup Title account@name.org
应该在控制台中打印密码。接下来创建一个 python 脚本
~/.script.py
#! /usr/bin/env python import os from subprocess import check_output def get_pass(account): return check_output("secret-tool lookup Title " + account, shell=True).splitlines()[0].decode("UTF-8")
一个等效的脚本依赖于 python-secretstorage,它是
~/.script.py
#! /usr/bin/env python import secretstorage from contextlib import closing def get_pass(title): with closing(secretstorage.dbus_init()) as conn: assert(secretstorage.check_service_availability(conn)) collection = secretstorage.get_default_collection(conn) if collection.is_locked(): collection.unlock() matches = collection.search_items({"Title": title}) entry = next(matches) if entry.is_locked(): entry.unlock() return(entry.get_secret())
从 ~/.offlineimaprc
加载此文件并指定定义的函数
~/.offlineimaprc
[general] # Path to file with arbitrary Python code to be loaded pythonfile = ~/.script.py ... [Repository Gmail] # Decrypt and read the encrypted password remotepasseval = get_pass("account@name.org") ...
故障排除
覆盖 UI 和自动刷新设置
为了方便故障排除,有时可以启动 offlineimap,使用更详细的 UI,不进行后台同步,甚至使用调试级别
$ offlineimap [ -o ] [ -d <debug_type> ] [ -u <ui> ]
- -o
- 禁用自动刷新、保持连接等。
- -d <debug_type>
- 其中 <debug_type> 是
imap
、maildir
或thread
之一。调试 imap 和 maildir 是目前为止最有用的。
- -u <ui>
- 其中 <ui> 是
CURSES.BLINKENLIGHTS
、TTY.TTYUI
、NONINTERACTIVE.BASIC
、NONINTERACTIVE.QUIET
或MACHINE.MACHINEUI
之一。TTY.TTYUI 对于调试目的来说已经足够了。
blinkenlights
、ttyui
、basic
、quiet
或 machineui
。文件夹无法创建
在 6.5.3 版本中,offlineimap 获得了在远程仓库中创建文件夹的能力,如此处所述。
当在远程仓库上使用 nametrans
时,这可能会导致以下形式的错误
ERROR: Creating folder bar on repository foo-remote Folder 'bar'[foo-remote] could not be created. Server responded: ('NO', ['[ALREADYEXISTS] Duplicate folder name bar (Failure)'])
解决方案是为本地仓库提供一个反向的 nametrans
lambda,例如
~/.offlineimaprc
[Repository foo-local] nametrans = lambda foldername: foldername.replace('bar', 'BAR') [Repository foo-remote] nametrans = lambda foldername: foldername.replace('BAR', 'bar')
- 为了找出正确的反向映射,
offlineimap --info
的输出应该有所帮助。 - 更新映射后,可能需要删除受影响帐户的
$HOME/.offlineimap/
下的所有文件夹。
SSL 指纹不匹配
ERROR: Server SSL fingerprint 'keykeykey' for hostname 'example.com' does not match configured fingerprint. Please verify and set 'cert_fingerprint' accordingly if not set yet.
要解决此问题,请添加到 ~/.offlineimaprc
(在与 ssl = yes
相同的节中) 以下内容之一
- 或者添加
cert_fingerprint
,以及远程服务器的证书指纹。这将检查远程服务器证书是否与给定的指纹匹配。cert_fingerprint = keykeykey
- 或者添加
sslcacertfile
,以及系统 CA 证书文件的路径。需要安装 ca-certificates。sslcacertfile = /etc/ssl/certs/ca-certificates.crt
复制消息,连接已关闭
ERROR: Copying message -2 [acc: email] connection closed Folder sent [acc: email]: ERROR: while syncing sent [account email] connection closed
造成这种情况的原因可能是本地和服务器上都创建了相同的消息。如果您的电子邮件提供商自动将已发送邮件保存到与本地客户端相同的文件夹,则会发生这种情况。如果遇到这种情况,请在本地客户端中禁用保存已发送邮件的功能。
另请参阅
- 官方 OfflineIMAP 邮件列表
- Mutt + Gmail + Offlineimap - brisbin 使用 cron 保持 offlineimap 同步的简单 gmail/mutt 设置的概述。