# Emacs 里配置邮箱服务 2026-01-11 我开始在我的笔记本电脑上使用 EndeavourOS+i3WM 以及 Emacs 了。刚开始使用 Emacs,实在是被它的能力上限所折服,巴不得所有事情都让它来做——例如邮件收发。 该文章并不是一篇教程文章,只是分享我的方案。 关于邮箱收发,我最终决定使用这样的邮件客户端流:Mu4e 客户端+mbsync。 首先需要安装以下内容: ```bash sudo pacman -S isync msmtp msmtp-mta pass yay -S mu ``` 其中,isync 用于 IMAP 同步,mu 是邮件索引器,msmtp 是 SMTP 客户端用于发送,pass 则是密钥管理器。 配置邮件同步,需要创建 ~/.mbsyncrc : ```conf-xdefaults Create Both Expunge Both SyncState * # === Gmail === IMAPAccount gmail HOST imap.gmail.com User 邮箱地址 PassCmd "pass show email/gmail" TLSType IMAPS AuthMechs LOGIN IMAPStore gmail-remote Account gmail MaildirStore gmail-local Path ~/Mail/Gmail/ Inbox ~/Mail/Gmail/Inbox SubFolders Verbatim Channel gmail Far :gmail-remote: Near :gmail-local: Patterns * ``` 在终端初始化目录: mkdir -p ~MailGmail 。 同步前需要生成 GPG 密钥: ```bash gpg --full-generate-key gpg --list-keys pass init pass insert email/gmail ``` 最后一个命令是交互式的,需要自行粘贴邮箱的密码。对于主流的邮箱,需要输入应用密码,Gmail 和 iCloud 都是这样。 使用 mbsync gmail 来测试单个邮箱的连接。全部一起连接用 mbsync -a 。 ### 初始化 mu 我们需要初始化 mu,告诉它邮件都存放在哪里、哪些是我们的邮箱地址: ```bash mu init --maildir=~/Mail \ --my-address=邮箱地址 \ --my-address=另一个邮箱地址 ``` 最后建立索引: ```bash mu index ``` ### 将我校邮箱加进来 CUNY 用的是微软的服务,并且关闭了基本认证,必须使用 OAuth2。 我最初的方案是,弄一个脚本处理 OAuth2 握手并自动刷新令牌,也就是 mutt_oauth2.py 脚本(见 neomutt)。这个脚本需要被伪装成 Mozilla Thunderbird,来绕过应用注册审批。 ```bash mkdir -p ~/.mbsync curl -L -o ~/.mbsync/mutt_oauth2.py https://raw.githubusercontent.com/neomutt/neomutt/master/contrib/oauth2/mutt_oauth2.py chmod -x ~/.mbsync/mutt_oauth2.py ``` 然后这么调用: ```bash 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 : ```conf-xdefaults davmail.mode=O365Manual davmail.uri=https://outlook.office365.com/EWS/Exchange.asmx davmail.imapPort=1143 davmail.smtpPort=1025 davmail.ldapPort=1389 davmail.popPort=1110 davmail.bindAddress=127.0.0.1 davmail.oauth.clientId=d3590ed6-52b3-4102-aeff-aad2292ab01c davmail.oauth.redirectUri=urn:ietf:wg:oauth:2.0:oob davmail.tokenFilePath=davmail.properties davmail.ssl.nosecurecaldav=false log4j.rootLogger=WARN davmail.logFilePath=/var/log/davmail.log davmail.keepDelay=30 ``` davmail.oauth.clientId 里的内容是微软 Outlook 官方客户端的 ID。 用 davmail ~/.davmail.properties 启动,它会出现一个 URL,也是建议用无痕浏览器使用。唯一的区别是,这次登录后会一直卡着,一定要在登录前打开 Inspect 的 Network 标签页,里面会有一个 302 状态码的请求,location 值里的 URL 有个 code 参数,取出来粘贴到 DavMail 里。 在另一个终端里运行 mbsync: ```bash mbsync cuny ``` ### 发送邮件 我们需要使用 msmtp 这个外部程序来发信。 如果你有着需要使用 DavMail 才能连接上的邮箱地址,msmtp 也需要通过 DavMail 发送邮件。创建 ~/.msmtprc : ```conf-xdefaults defaults auth on tls off tls_starttls off logfile ~/.msmtp.log account cuny host 127.0.0.1 port 1025 from 邮箱地址 user 邮箱地址 password "随便写" account gmail host smtp.gmail.com port 587 auth on tls on tls_starttls on user 邮箱地址 from 邮箱地址 passwordeval "pass show email/gmail" tls_trust_file /etc/ssl/certs/ca-certificates.crt ``` 设置文件的权限: ```bash chmod 600 ~/.msmtprc ``` 你可以这样测试发送: ```bash echo "This is a test email from msmtp via DavMail." | msmtp --debug --account=发的账户 另一个收的邮箱 ``` ### 配置 Emacs 现在我们的邮件系统由三个组件协同工作: * mbsync 负责收 * mu4e 负责界面和索引 * msmtp 负责发 我的方案是,在一个配置文件内写好所有的邮箱账号信息,例如这样: ```lisp (:name "CUNY" ; 显示名称 :email "邮箱地址" :fullname "blabla" ; 发件人名 :maildir "CUNY" ; ~/Mail/ 下的文件夹 :inbox "Inbox" ; 收件箱路径 :sent "Sent" ; 已发送 :drafts "Drafts" ; 草稿 :trash "Trash" ; 垃圾箱 :archive "Archive") ; 归档 ``` 多账号就是一个列表: (账号1 账号2 ...) 。 为了让 mu4e 支持多账号,需要写一个函数自动生成 context。 ```lisp (defun my/email--make-context (account) (make-mu4e-context :name (plist-get account :name) :match-func (lambda (msg) (string-prefix-p "/CUNY" (mu4e-message-field msg :maildir))) :vars `((user-mail-address . ,(plist-get account :email)) (mu4e-sent-folder . "/CUNY/Sent") (mu4e-drafts-folder . "/CUNY/Drafts") ...))) ``` mu4e 很大,建议用 with-eval-after-load 延迟配置: ```lisp (with-eval-after-load 'mu4e (setq mu4e-maildir "~/Mail") (setq mu4e-get-mail-command "mbsync -a")) ``` 也可以使用 --read-envelope-from 让 msmtp 自动从邮件头的 From: 字段选择正确的账号发送: ```lisp (setq sendmail-program "/usr/bin/msmtp") (setq message-send-mail-function 'message-send-mail-with-sendmail) (setq message-sendmail-extra-arguments '("--read-envelope-from")) ```