Gmail的OfflineIMAP XOAUTH2认证

Gmail现在似乎不再允许IMAP的AUTHENTICATE PLAIN了。2016年1月的OfflineIMAP 6.7.0-rc1对XOAUTH2支持较好,仍可访问Gmail IMAP。

Gmail网页版启用IMAP

https://mail.google.com中Settings->Forwarding and POP/IMAP,开启IMAP Access,When a message is marked as deleted and expunged from the last visible IMAP folder:最好选择默认的Archive the message,被删除的邮件在网页版中会丢失所有标签,但仍能在“[Gmail]/All Mail”中找到。

  • 访问Google Developers Console
  • 选择或新建一个project
  • 访问https://console.developers.google.com/apis/credentials,点击Create credentials->OAuth client ID,type选择other,记录client ID与client secret

Gmail的label大致被映射成IMAP的folder,以下是一些默认label的IMAP folder名称:

INBOX
[Gmail]/Trash
[Gmail]/Important
[Gmail]/Sent Mail
[Gmail]/Starred
[Gmail]/Spam
[Gmail]/All Mail
[Gmail]/Personal

OfflineIMAP XOAUTH2配置

编辑~/.offlineimaprc:

[general]
ui = ttyui
accounts = Ray
fsync = False

[Account Ray]
localrepository = Ray-Local
remoterepository = Ray-Remote
status_backend = sqlite
#要用代理的话得pip install pysocks
#proxy = HTTP:127.0.0.1:1111
#proxy = SOCKS5:127.0.0.1:1112

[Repository Ray-Local]
type = Maildir
localfolders = ~/Maildir

[Repository Ray-Remote]
type = Gmail
ssl = yes
sslcacertfile = /etc/ssl/certs/ca-certificates.crt
remoteuser = youremail@example.com
auth_mechanisms = XOAUTH2
oauth2_client_id = 7738xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com
oauth2_client_secret = xxxxxx-xxxxxxxxxxxxxxxxx
oauth2_refresh_token = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#oauth2_access_token = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

nametrans = lambda folder: {'[Gmail]/Drafts':    'Drafts',
                            '[Gmail]/Sent Mail': 'Sent',
                            '[Gmail]/Starred':   'Starred',
                            '[Gmail]/Trash':     'Trash',
                            '[Gmail]/All Mail':  'Archive',
                            }.get(folder, folder)
folderfilter = lambda folder: folder in ['INBOX', 'Label1', 'Label2', 'Work']

默认配置文件https://github.com/OfflineIMAP/offlineimap/blob/master/offlineimap.conf有详细的描述。

cd /tmp
git clone https://github.com/google/gmail-oauth2-tools
cd gmail-oauth2-tools
# 获取refresh token,填入~/.offlineimaprc的oauth2_refresh_token
# 这一步需要借由各种代理途径,比如proxychains等
python2 python/oauth2.py --generate_oauth2_token --client_id=xxx --client_secret=xxx

systemd user

OfflineIMAP的quick sync很不靠谱,SIGINT后也不能有效退出,往往要停滞很长时间,因此我用systemd user每个一段时间运行一次offlineimap,并设置较短的TimeoutStopSec(man 5 systemd.service)。编辑~/.config/systemd/user/offlineimap@.service:

[Unit]
Description=OfflineIMAP IMAP/Maildir Synchronization
Documentation=man:offlineimap

[Service]
ExecStart=/usr/bin/offlineimap -o -u quiet -a %i
Restart=always
RestartSec=600
TimeoutStopSec=10

[Install]
WantedBy=default.target
# Ray为~/.offlineimaprc中配置的账户名
systemctl --user enable offlineimap@Ray
systemctl --user start offlineimap@Ray

SMTP client

没找到支持XOAUTH2、可用作Mail Submission Agent的SMTP client,暂时仍用msmtp的SMTP AUTH PLAIN,用pass记录Gmail密码,用gpg的passphrase保护。编辑~/.msmtprc:

defaults
auth           on
#proxy_host     127.0.0.1
#proxy_port     1111
tls            on
tls_trust_file /etc/ssl/certs/ca-certificates.crt

account        Gmail
host           smtp.gmail.com
port           587
from           youremail@example.com
user           youremail@example.com
passwordeval   pass show Gmail

易犯错误

如果没有用全局代理,使用offlineimaprc中的proxy选项,得确保安装Python 2的PySocks包,不然代理设置会被忽略。

imapserver.py中__xoauth2handler用urllib.urlopen获取access token,2016年1月的实现没有用上proxy选项设置。offlineimap-6.7.0已经集成了该补丁。