Emacs 里配置邮箱服务
我开始在我的笔记本电脑上使用 EndeavourOS+i3WM 以及 Emacs 了。刚开始使用 Emacs,实在是被它的能力上限所折服,巴不得所有事情都让它来做 —— 例如邮件收发。
关于邮箱收发,我最终决定使用这样的邮件客户端流:Mu4e 客户端+mbsync。
首先需要安装以下内容:
1 | sudo pacman -S isync msmtp msmtp-mta pass |
其中,isync 用于 IMAP 同步,mu 是邮件索引器,msmtp 是 SMTP 客户端用于发送,pass 则是密钥管理器。
配置邮件同步,需要创建 ~/.mbsyncrc:
1 | Create Both |
在终端初始化目录: mkdir -p ~*Mail*Gmail。
同步前需要生成 GPG 密钥:
1 | gpg --full-generate-key |
最后一个命令是交互式的,需要自行粘贴邮箱的密码。对于主流的邮箱,需要输入应用密码,Gmail 和 iCloud 都是这样。
使用 mbsync gmail 来测试单个邮箱的连接。全部一起连接用 mbsync -a。
初始化 mu
我们需要初始化 mu,告诉它邮件都存放在哪里、哪些是我们的邮箱地址:
1 | mu init --maildir=~/Mail \ |
最后建立索引:
1 | mu index |
将我校邮箱加进来
CUNY 用的是微软的服务,并且关闭了基本认证,必须使用 OAuth2。
我最初的方案是,弄一个脚本处理 OAuth2 握手并自动刷新令牌,也就是 mutt_oauth2.py 脚本(见 neomutt)。这个脚本需要被伪装成 Mozilla Thunderbird,来绕过应用注册审批。
1 | mkdir -p ~/.mbsync |
然后这么调用:
1 | python ~/.mbsync/mutt_oauth2.py ~/.mbsync/cuny_token --verbose --authorize --provider microsoft --client-id 9e5f94bc-e8a4-4e73-b8be-63364c29d753 --authflow authcode --email <你的邮箱地址> |
出现 Client Secret 时直接按回车,复制终端里给的 URL,最好是在无痕浏览器里登录。登录完会跳转到一个页面,赶紧复制 URL 下来,否则过几秒会跳转到警告页面。这个 URL 里有个 code 参数,复制下来(注意后面的 session_state 参数,删掉),输入到终端里……
然后你会收到 User is authorized but not connected 的消息。
为什么会这样呢?我们的 OAuth2 配置是没有问题的,但是 CUNY IT 部门把邮箱的 IMAP 和 POP 协议都禁用、不让用在应用上。
解决方法是,用 DavMail 在本地机器开启一个 IMAP 端口,mbsync 会连接到它,发送的请求会被转换成 Microsoft EWS 协议,也是 Outlook 网页版和官方客户端使用的协议。DavMail 会向微软的服务器请求数据,并伪装成 IMAP 返回给 mbsync。
创建 ~/.davmail.properties:
1 | davmail.mode=O365Manual |
davmail.oauth.clientId 里的内容是微软 Outlook 官方客户端的 ID。
用 davmail ~/.davmail.properties 启动,它会出现一个 URL,也是建议用无痕浏览器使用。唯一的区别是,这次登录后会一直卡着,一定要在登录前打开 Inspect 的 Network 标签页,里面会有一个 302 状态码的请求,location 值里的 URL 有个 code 参数,取出来粘贴到 DavMail 里。
在另一个终端里运行 mbsync:
1 | mbsync cuny |
发送邮件
我们需要使用 msmtp 这个外部程序来发信。
如果你有着需要使用 DavMail 才能连接上的邮箱地址,msmtp 也需要通过 DavMail 发送邮件。创建 ~/.msmtprc:
1 | defaults |
设置文件的权限:
1 | chmod 600 ~/.msmtprc |
你可以这样测试发送:
1 | echo "This is a test email from msmtp via DavMail." | msmtp --debug --account=发的账户 另一个收的邮箱 |
配置 Emacs
现在我们的邮件系统由三个组件协同工作:
- mbsync 负责收
- mu4e 负责界面和索引
- msmtp 负责发
我的方案是,在一个配置文件内写好所有的邮箱账号信息,例如这样:
1 | (:name "CUNY" ; 显示名称 |
多账号就是一个列表: (账号1 账号2 ...)。
为了让 mu4e 支持多账号,需要写一个函数自动生成 context。
1 | (defun my/email--make-context (account) |
mu4e 很大,建议用 with-eval-after-load 延迟配置:
1 | (with-eval-after-load 'mu4e |
也可以使用 --read-envelope-from 让 msmtp 自动从邮件头的 From: 字段选择正确的账号发送:
1 | (setq sendmail-program "/usr/bin/msmtp") |