~cytrogen/blog-public

blog-public/posts/4420.html -rw-r--r-- 34.9 KiB
88eebf3dCytrogen Deploy 2026-02-19 08:34:27 3 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
<!DOCTYPE html><html lang="zh" data-theme="dark"><head><meta charset="utf-8"><meta name="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Emacs 里配置邮箱服务 · Cytrogen 的个人博客</title><meta name="description" content="我开始在我的笔记本电脑上使用 EndeavourOS+i3WM 以及 Emacs 了。刚开始使用 Emacs,实在是被它的能力上限所折服,巴不得所有事情都让它来做 —— 例如邮件收发。"><link rel="icon" href="../favicon.png"><link rel="canonical" href="https://cytrogen.icu/posts/4420.html"><link rel="webmention" href="https://webmention.io/cytrogen.icu/webmention"><link rel="me" href="https://m.otter.homes/@Cytrogen"><link rel="me" href="https://github.com/cytrogen"><meta name="fediverse:creator" content="@Cytrogen@m.otter.homes"><link rel="preload" href="../fonts/opensans-regular-latin.woff2" as="font" type="font/woff2" crossorigin="anonymous"><style>@font-face {
  font-family: 'Open Sans';
  src: url('../fonts/opensans-regular-latin.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
  size-adjust: 107%;
  ascent-override: 97%;
  descent-override: 25%;
  line-gap-override: 0%;
}
</style><script>(function() {
  try {
    // 优先级:用户选择 > 系统偏好 > 默认浅色
    const saved = localStorage.getItem('theme');
    const theme = saved || 
      (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
    
    document.documentElement.setAttribute('data-theme', theme);
    document.documentElement.style.colorScheme = theme;
  } catch (error) {
    // 失败时使用默认主题,不阻塞渲染
    document.documentElement.setAttribute('data-theme', 'light');
  }
})();
</script><link rel="stylesheet" href="../css/ares.css"><script data-netlify-skip-bundle="true">(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const theme = document.documentElement.getAttribute('data-theme');
    const pageWrapper = document.getElementById('page-wrapper');
    if (pageWrapper && theme) {
      pageWrapper.setAttribute('data-theme', theme);
    }
  });
})();

</script><!-- hexo injector head_end start -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.12.0/dist/katex.min.css">

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/hexo-math@4.0.0/dist/style.css">
<!-- hexo injector head_end end --><meta name="generator" content="Hexo 8.1.1"><link rel="alternate" href="atom.xml" title="Cytrogen 的个人博客" type="application/atom+xml">
</head><body><div id="page-wrapper"><a class="skip-link" href="#main-content">跳到主要内容</a><div class="wrap"><header><a class="logo-link" href="../index.html"><img src="../favicon.png" alt="logo"></a><div class="h-card visually-hidden"><img class="u-photo" src="https://cytrogen.icu/favicon.png" alt="Cytrogen"><a class="p-name u-url u-uid" href="https://cytrogen.icu">Cytrogen</a><p class="p-note">Cytrogen 的个人博客,Cytrogen's Blog</p><a class="u-url" rel="me noopener" target="_blank" href="https://m.otter.homes/@Cytrogen">Mastodon</a><a class="u-url" rel="me noopener" target="_blank" href="https://github.com/cytrogen">GitHub</a></div><nav class="site-nav"><div class="nav-main"><div class="nav-primary"><ul class="nav-list hidden-mobile"><li class="nav-item"><a class="nav-link" href="../index.html">首页</a></li></ul><div class="nav-tools"><div class="language-menu"><button class="language-toggle" type="button"><svg class="icon icon-globe" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M0 8a8 8 0 1 1 16 0A8 8 0 0 1 0 8zm7.5-6.923c-.67.204-1.335.82-1.887 1.855A7.97 7.97 0 0 0 5.145 4H7.5V1.077zM4.09 4a9.267 9.267 0 0 1 .64-1.539 6.7 6.7 0 0 1 .597-.933A7.025 7.025 0 0 0 2.255 4H4.09zm-.582 3.5c.03-.877.138-1.718.312-2.5H1.674a6.958 6.958 0 0 0-.656 2.5h2.49zM4.847 5a12.5 12.5 0 0 0-.338 2.5H7.5V5H4.847zM8.5 5v2.5h2.99a12.495 12.495 0 0 0-.337-2.5H8.5zM4.51 8.5a12.5 12.5 0 0 0 .337 2.5H7.5V8.5H4.51zm3.99 0V11h2.653c.187-.765.306-1.608.338-2.5H8.5zM5.145 12c.138.386.295.744.468 1.068.552 1.035 1.218 1.65 1.887 1.855V12H5.145zm.182 2.472a6.696 6.696 0 0 1-.597-.933A9.268 9.268 0 0 1 4.09 12H2.255a7.024 7.024 0 0 0 3.072 2.472zM3.82 11a13.652 13.652 0 0 1-.312-2.5h-2.49c.062.89.291 1.733.656 2.5H3.82zm6.853 3.472A7.024 7.024 0 0 0 13.745 12H11.91a9.27 9.27 0 0 1-.64 1.539 6.688 6.688 0 0 1-.597.933zM8.5 12v2.923c.67-.204 1.335-.82 1.887-1.855A7.97 7.97 0 0 0 10.855 12H8.5zm3.68-1h2.146c.365-.767.594-1.61.656-2.5h-2.49a13.65 13.65 0 0 1-.312 2.5zm2.802-3.5a6.959 6.959 0 0 0-.656-2.5H12.18c.174.782.282 1.623.312 2.5h2.49zM11.27 2.461c.247.464.462.98.64 1.539h1.835a7.024 7.024 0 0 0-3.072-2.472c.218.284.418.598.597.933zM10.855 4a7.966 7.966 0 0 0-.468-1.068C9.835 1.897 9.17 1.282 8.5 1.077V4h2.355z"></path></svg><span>中文</span></button><div class="language-dropdown"></div></div></div><div class="nav-controls"><div class="more-menu hidden-mobile"><button class="more-toggle" type="button"><span>更多</span><svg class="icon icon-chevron-down" width="12" height="12" viewBox="0 0 12 12" fill="currentColor" aria-hidden="true" focusable="false"><path d="M6 8.825c-.2 0-.4-.1-.5-.2l-3.3-3.3c-.3-.3-.3-.8 0-1.1s.8-.3 1.1 0l2.7 2.7 2.7-2.7c.3-.3.8-.3 1.1 0s.3.8 0 1.1l-3.3 3.3c-.1.1-.3.2-.5.2z"></path></svg></button><div class="more-dropdown"><ul class="dropdown-list"><li class="dropdown-item"><a class="nav-link" href="../archives/index.html">归档</a></li><li class="dropdown-item"><a class="nav-link" href="../categories/index.html">分类</a></li><li class="dropdown-item"><a class="nav-link" href="../tags/index.html">标签</a></li><li class="dropdown-item"><a class="nav-link" href="../about/index.html">关于</a></li><li class="dropdown-item"><a class="nav-link" href="../sitemap/index.html">领地地图</a></li></ul></div></div><div class="theme-switcher"><button class="theme-toggle" type="button" role="switch" aria-pressed="false" aria-label="切换主题"><div class="theme-icon moon-icon"><svg class="icon icon-moon" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z"></path></svg></div><div class="theme-icon sun-icon"><svg class="icon icon-sun" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M8 11a3 3 0 1 1 0-6 3 3 0 0 1 0 6zm0 1a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z"></path></svg></div></button></div><details class="mobile-menu-details hidden-desktop"><summary class="hamburger-menu" aria-label="nav.menu"><svg class="icon icon-bars" width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" focusable="false"><path d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5z"></path></svg><span class="menu-text">nav.menu</span></summary><div class="mobile-menu-dropdown"><ul class="mobile-nav-list"><li class="mobile-nav-item"><a class="mobile-nav-link" href="../index.html">首页</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../archives/index.html">归档</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../categories/index.html">分类</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../tags/index.html">标签</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../about/index.html">关于</a></li><li class="mobile-nav-item"><a class="mobile-nav-link" href="../sitemap/index.html">领地地图</a></li></ul></div></details></div></div></div></nav></header><main class="container" id="main-content" tabindex="-1"><div class="post"><article class="post-block h-entry"><div class="post-meta p-author h-card visually-hidden"><img class="author-avatar u-photo" src="../favicon.png" alt="Cytrogen"><span class="p-name">Cytrogen</span><a class="u-url" href="https://cytrogen.icu">https://cytrogen.icu</a></div><a class="post-permalink u-url u-uid visually-hidden" href="https://cytrogen.icu/posts/4420.html">永久链接</a><div class="p-summary visually-hidden"><p>我开始在我的笔记本电脑上使用 EndeavourOS+i3WM 以及 Emacs 了。刚开始使用 Emacs,实在是被它的能力上限所折服,巴不得所有事情都让它来做 —— 例如邮件收发。</p></div><div class="visually-hidden"><a class="p-category" href="../categories/%E7%BC%96%E7%A8%8B%E7%AC%94%E8%AE%B0/">编程笔记</a><a class="p-category" href="../tags/Emacs/">Emacs</a><a class="p-category" href="../tags/Lisp/">Lisp</a></div><h1 class="post-title p-name">Emacs 里配置邮箱服务</h1><div class="post-info"><time class="post-date dt-published" datetime="2026-01-11T05:00:00.000Z">1/11/2026</time><time class="dt-updated visually-hidden" datetime="2026-02-09T17:16:55.009Z"></time></div><div class="post-content e-content"><html><head></head><body><p>我开始在我的笔记本电脑上使用 EndeavourOS+i3WM 以及 Emacs 了。刚开始使用 Emacs,实在是被它的能力上限所折服,巴不得所有事情都让它来做 —— 例如邮件收发。</p>
<span id="more"></span>
<p>关于邮箱收发,我最终决定使用这样的邮件客户端流:Mu4e 客户端+mbsync。</p>
<p>首先需要安装以下内容:</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> pacman -S isync msmtp msmtp-mta pass</span><br><span class="line">yay -S mu</span><br></pre></td></tr></tbody></table></figure>
<p>其中,isync 用于 IMAP 同步,mu 是邮件索引器,msmtp 是 SMTP 客户端用于发送,pass 则是密钥管理器。</p>
<p>配置邮件同步,需要创建 <code>~/.mbsyncrc</code></p>
<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">Create Both</span><br><span class="line">Expunge Both</span><br><span class="line">SyncState *</span><br><span class="line"></span><br><span class="line"># === Gmail ===</span><br><span class="line">IMAPAccount gmail</span><br><span class="line">HOST imap.gmail.com</span><br><span class="line">User 邮箱地址</span><br><span class="line">PassCmd "pass show email/gmail"</span><br><span class="line">TLSType IMAPS</span><br><span class="line">AuthMechs LOGIN</span><br><span class="line"></span><br><span class="line">IMAPStore gmail-remote</span><br><span class="line">Account gmail</span><br><span class="line"></span><br><span class="line">MaildirStore gmail-local</span><br><span class="line">Path ~/Mail/Gmail/</span><br><span class="line">Inbox ~/Mail/Gmail/Inbox</span><br><span class="line">SubFolders Verbatim</span><br><span class="line"></span><br><span class="line">Channel gmail</span><br><span class="line">Far :gmail-remote:</span><br><span class="line">Near :gmail-local:</span><br><span class="line">Patterns *</span><br></pre></td></tr></tbody></table></figure>
<p>在终端初始化目录: <code>mkdir -p ~*Mail*Gmail</code></p>
<p>同步前需要生成 GPG 密钥:</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">gpg --full-generate-key</span><br><span class="line">gpg --list-keys</span><br><span class="line">pass init &lt;GPG ID 或者最后 8 位&gt;</span><br><span class="line">pass insert email/gmail</span><br></pre></td></tr></tbody></table></figure>
<p>最后一个命令是交互式的,需要自行粘贴邮箱的密码。对于主流的邮箱,需要输入应用密码,Gmail 和 iCloud 都是这样。</p>
<p>使用 <code>mbsync gmail</code> 来测试单个邮箱的连接。全部一起连接用 <code>mbsync -a</code></p>
<h3 id="初始化-mu"><a class="markdownIt-Anchor" href="#初始化-mu"></a> 初始化 mu</h3>
<p>我们需要初始化 mu,告诉它邮件都存放在哪里、哪些是我们的邮箱地址:</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">mu init --maildir=~/Mail \</span><br><span class="line">   --my-address=邮箱地址 \</span><br><span class="line">   --my-address=另一个邮箱地址</span><br></pre></td></tr></tbody></table></figure>
<p>最后建立索引:</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mu index</span><br></pre></td></tr></tbody></table></figure>
<h3 id="将我校邮箱加进来"><a class="markdownIt-Anchor" href="#将我校邮箱加进来"></a> 将我校邮箱加进来</h3>
<p>CUNY 用的是微软的服务,并且关闭了基本认证,必须使用 OAuth2。</p>
<p>我最初的方案是,弄一个脚本处理 OAuth2 握手并自动刷新令牌,也就是 <code>mutt_oauth2.py</code> 脚本(见 neomutt)。这个脚本需要被伪装成 Mozilla Thunderbird,来绕过应用注册审批。</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p ~/.mbsync</span><br><span class="line">curl -L -o ~/.mbsync/mutt_oauth2.py https://raw.githubusercontent.com/neomutt/neomutt/master/contrib/oauth2/mutt_oauth2.py</span><br><span class="line"><span class="built_in">chmod</span> -x ~/.mbsync/mutt_oauth2.py</span><br></pre></td></tr></tbody></table></figure>
<p>然后这么调用:</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python ~/.mbsync/mutt_oauth2.py ~/.mbsync/cuny_token --verbose --authorize --provider microsoft --client-id 9e5f94bc-e8a4-4e73-b8be-63364c29d753 --authflow authcode --email &lt;你的邮箱地址&gt;</span><br></pre></td></tr></tbody></table></figure>
<p>出现 Client Secret 时直接按回车,复制终端里给的 URL,最好是在无痕浏览器里登录。登录完会跳转到一个页面,赶紧复制 URL 下来,否则过几秒会跳转到警告页面。这个 URL 里有个 <code>code</code> 参数,复制下来(注意后面的 <code>session_state</code> 参数,删掉),输入到终端里……</p>
<p>然后你会收到 <em>User is authorized but not connected</em> 的消息。</p>
<p>为什么会这样呢?我们的 OAuth2 配置是没有问题的,但是 CUNY IT 部门把邮箱的 IMAP 和 POP 协议都禁用、不让用在应用上。</p>
<p>解决方法是,用 DavMail 在本地机器开启一个 IMAP 端口,mbsync 会连接到它,发送的请求会被转换成 Microsoft EWS 协议,也是 Outlook 网页版和官方客户端使用的协议。DavMail 会向微软的服务器请求数据,并伪装成 IMAP 返回给 mbsync。</p>
<p>创建 <code>~/.davmail.properties</code></p>
<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">davmail.mode=O365Manual</span><br><span class="line">davmail.uri=https://outlook.office365.com/EWS/Exchange.asmx</span><br><span class="line">davmail.imapPort=1143</span><br><span class="line">davmail.smtpPort=1025</span><br><span class="line">davmail.ldapPort=1389</span><br><span class="line">davmail.popPort=1110</span><br><span class="line">davmail.bindAddress=127.0.0.1</span><br><span class="line">davmail.oauth.clientId=d3590ed6-52b3-4102-aeff-aad2292ab01c</span><br><span class="line">davmail.oauth.redirectUri=urn:ietf:wg:oauth:2.0:oob</span><br><span class="line">davmail.tokenFilePath=davmail.properties</span><br><span class="line">davmail.ssl.nosecurecaldav=false</span><br><span class="line">log4j.rootLogger=WARN</span><br><span class="line">davmail.logFilePath=/var/log/davmail.log</span><br><span class="line">davmail.keepDelay=30</span><br></pre></td></tr></tbody></table></figure>
<p><code>davmail.oauth.clientId</code> 里的内容是微软 Outlook 官方客户端的 ID。<br><code>davmail ~/.davmail.properties</code> 启动,它会出现一个 URL,也是建议用无痕浏览器使用。唯一的区别是,这次登录后会一直卡着,一定要在登录前打开 Inspect 的 Network 标签页,里面会有一个 302 状态码的请求,location 值里的 URL 有个 <code>code</code> 参数,取出来粘贴到 DavMail 里。</p>
<p>在另一个终端里运行 mbsync:</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mbsync cuny</span><br></pre></td></tr></tbody></table></figure>
<h3 id="发送邮件"><a class="markdownIt-Anchor" href="#发送邮件"></a> 发送邮件</h3>
<p>我们需要使用 msmtp 这个外部程序来发信。</p>
<p>如果你有着需要使用 DavMail 才能连接上的邮箱地址,msmtp 也需要通过 DavMail 发送邮件。创建 <code>~/.msmtprc</code></p>
<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">defaults</span><br><span class="line">auth            on</span><br><span class="line">tls             off</span><br><span class="line">tls_starttls    off</span><br><span class="line">logfile         ~/.msmtp.log</span><br><span class="line"></span><br><span class="line">account         cuny</span><br><span class="line">host            127.0.0.1</span><br><span class="line">port            1025</span><br><span class="line">from            邮箱地址</span><br><span class="line">user            邮箱地址</span><br><span class="line">password        "随便写"</span><br><span class="line"></span><br><span class="line">account         gmail</span><br><span class="line">host            smtp.gmail.com</span><br><span class="line">port            587</span><br><span class="line">auth            on</span><br><span class="line">tls             on</span><br><span class="line">tls_starttls    on</span><br><span class="line">user            邮箱地址</span><br><span class="line">from            邮箱地址</span><br><span class="line">passwordeval    "pass show email/gmail"</span><br><span class="line">tls_trust_file  /etc/ssl/certs/ca-certificates.crt</span><br></pre></td></tr></tbody></table></figure>
<p>设置文件的权限:</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">chmod</span> 600 ~/.msmtprc</span><br></pre></td></tr></tbody></table></figure>
<p>你可以这样测试发送:</p>
<figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">"This is a test email from msmtp via DavMail."</span> | msmtp --debug --account=发的账户 另一个收的邮箱</span><br></pre></td></tr></tbody></table></figure>
<h3 id="配置-emacs"><a class="markdownIt-Anchor" href="#配置-emacs"></a> 配置 Emacs</h3>
<p>现在我们的邮件系统由三个组件协同工作:</p>
<ul>
<li>mbsync 负责收</li>
<li>mu4e 负责界面和索引</li>
<li>msmtp 负责发</li>
</ul>
<p>我的方案是,在一个配置文件内写好所有的邮箱账号信息,例如这样:</p>
<figure class="highlight lisp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">(<span class="symbol">:name</span>     <span class="string">"CUNY"</span>                           <span class="comment">; 显示名称</span></span><br><span class="line">	   <span class="symbol">:email</span>    <span class="string">"邮箱地址"</span></span><br><span class="line">	   <span class="symbol">:fullname</span> <span class="string">"blabla"</span>                         <span class="comment">; 发件人名</span></span><br><span class="line">	   <span class="symbol">:maildir</span>  <span class="string">"CUNY"</span>                           <span class="comment">; ~/Mail/ 下的文件夹</span></span><br><span class="line">	   <span class="symbol">:inbox</span>    <span class="string">"Inbox"</span>                          <span class="comment">; 收件箱路径</span></span><br><span class="line">	   <span class="symbol">:sent</span>     <span class="string">"Sent"</span>                           <span class="comment">; 已发送</span></span><br><span class="line">	   <span class="symbol">:drafts</span>   <span class="string">"Drafts"</span>                         <span class="comment">; 草稿</span></span><br><span class="line">	   <span class="symbol">:trash</span>    <span class="string">"Trash"</span>                          <span class="comment">; 垃圾箱</span></span><br><span class="line">	   <span class="symbol">:archive</span>  <span class="string">"Archive"</span>)                       <span class="comment">; 归档</span></span><br></pre></td></tr></tbody></table></figure>
<p>多账号就是一个列表: <code>(账号1 账号2 ...)</code></p>
<p>为了让 mu4e 支持多账号,需要写一个函数自动生成 context。</p>
<figure class="highlight lisp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">(<span class="name">defun</span> my/email--make-context (<span class="name">account</span>)</span><br><span class="line">  (<span class="name">make-mu4e-context</span></span><br><span class="line">   <span class="symbol">:name</span> (<span class="name">plist-get</span> account <span class="symbol">:name</span>)</span><br><span class="line">   <span class="symbol">:match-func</span> (<span class="name">lambda</span> (<span class="name">msg</span>)</span><br><span class="line">		 (<span class="name">string-prefix-p</span> <span class="string">"/CUNY"</span> (<span class="name">mu4e-message-field</span> msg <span class="symbol">:maildir</span>)))</span><br><span class="line">   <span class="symbol">:vars</span> `((user-mail-address . ,(plist-get account :email))</span><br><span class="line">      	   (mu4e-sent-folder . <span class="string">"/CUNY/Sent"</span>)</span><br><span class="line">           (mu4e-drafts-folder . <span class="string">"/CUNY/Drafts"</span>)</span><br><span class="line">  	   ...)))</span><br></pre></td></tr></tbody></table></figure>
<p>mu4e 很大,建议用 <code>with-eval-after-load</code> 延迟配置:</p>
<figure class="highlight lisp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">(<span class="name">with-eval-after-load</span> 'mu4e</span><br><span class="line">  (<span class="name">setq</span> mu4e-maildir <span class="string">"~/Mail"</span>)</span><br><span class="line">  (<span class="name">setq</span> mu4e-get-mail-command <span class="string">"mbsync -a"</span>))</span><br></pre></td></tr></tbody></table></figure>
<p>也可以使用 <code>--read-envelope-from</code> 让 msmtp 自动从邮件头的 <code>From:</code> 字段选择正确的账号发送:</p>
<figure class="highlight lisp"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">(<span class="name">setq</span> sendmail-program <span class="string">"/usr/bin/msmtp"</span>)</span><br><span class="line">(<span class="name">setq</span> message-send-mail-function 'message-send-mail-with-sendmail)</span><br><span class="line">(<span class="name">setq</span> message-sendmail-extra-arguments '(<span class="string">"--read-envelope-from"</span>))</span><br></pre></td></tr></tbody></table></figure>
</body></html></div></article></div></main><footer><div class="paginator"><a class="prev" href="9412.html">上一篇</a><a class="next" href="b211.html">下一篇</a></div><!-- Webmention 显示区域--><div class="webmention-section webmention-empty" data-page-url="posts/4420.html" data-full-url="https://cytrogen.icu/posts/4420.html" data-mode="static">
              <h3 class="webmention-title">Webmentions (<span class="webmention-count">0</span>)</h3>
              <div class="webmention-list"></div>
              <span>暂无 Webmentions</span>
            </div><div class="copyright"><p class="footer-links"><a href="../friends/index.html">友链</a><span class="footer-separator"> ·</span><a href="../links/index.html">邻邦</a><span class="footer-separator"> ·</span><a href="../contact/index.html">联络</a><span class="footer-separator"> ·</span><a href="../colophon/index.html">营造记</a><span class="footer-separator"> ·</span><a href="../atom.xml">RSS订阅</a></p><p>© 2025 - 2026 <a href="https://cytrogen.icu">Cytrogen</a>, powered by <a href="https://hexo.io/" target="_blank">Hexo</a> and <a href="https://github.com/cytrogen/hexo-theme-ares" target="_blank">hexo-theme-ares</a>.</p><p><a href="https://blogscn.fun" target="_blank" rel="noopener">BLOGS·CN</a></p></div></footer></div></div><a class="back-to-top" href="#top" aria-label="返回顶部"><svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M3.293 9.707a1 1 0 010-1.414L9.586 2a2 2 0 012.828 0l6.293 6.293a1 1 0 01-1.414 1.414L11 3.414V17a1 1 0 11-2 0V3.414L2.707 9.707a1 1 0 01-1.414 0z"></path></svg></a><script>document.addEventListener('DOMContentLoaded', function() {
  const codeBlocks = document.querySelectorAll('figure.highlight');
  
  codeBlocks.forEach(block => {
    let caption = block.querySelector('figcaption');
    if (!caption) {
      caption = document.createElement('figcaption');
      block.insertBefore(caption, block.firstChild);
    }

    const info = document.createElement('div');
    info.className = 'info';
    
    const filename = caption.querySelector('span');
    if (filename) {
      filename.className = 'filename';
      info.appendChild(filename);
    }
    
    const lang = block.className.split(' ')[1];
    if (lang) {
      const langSpan = document.createElement('span');
      langSpan.className = 'lang-name';
      langSpan.textContent = lang;
      info.appendChild(langSpan);
    }

    const sourceLink = caption.querySelector('a');
    if (sourceLink) {
      sourceLink.className = 'source-link';
      info.appendChild(sourceLink);
    }

    const actions = document.createElement('div');
    actions.className = 'actions';

    const codeHeight = block.scrollHeight;
    const threshold = 300;

    if (codeHeight > threshold) {
      block.classList.add('folded');
      
      const toggleBtn = document.createElement('button');
      toggleBtn.textContent = '展开';
      toggleBtn.addEventListener('click', () => {
        block.classList.toggle('folded');
        toggleBtn.textContent = block.classList.contains('folded') ? '展开' : '折叠';
      });
      actions.appendChild(toggleBtn);
    }

    const copyBtn = document.createElement('button');
    copyBtn.textContent = '复制';
    copyBtn.addEventListener('click', async () => {
      const codeLines = block.querySelectorAll('.code .line');
      const code = Array.from(codeLines)
        .map(line => line.textContent)
        .join('\n')
        .replace(/\n\n/g, '\n');
      
      try {
        await navigator.clipboard.writeText(code);
        copyBtn.textContent = '已复制';
        copyBtn.classList.add('copied');
        
        setTimeout(() => {
          copyBtn.textContent = '复制';
          copyBtn.classList.remove('copied');
        }, 3000);
      } catch (err) {
        console.error('复制失败:', err);
        copyBtn.textContent = '复制失败';
        
        setTimeout(() => {
          copyBtn.textContent = '复制';
        }, 3000);
      }
    });
    actions.appendChild(copyBtn);

    caption.innerHTML = '';
    caption.appendChild(info);
    caption.appendChild(actions);

    const markedLines = block.getAttribute('data-marked-lines');
    if (markedLines) {
      const lines = markedLines.split(',');
      lines.forEach(range => {
        if (range.includes('-')) {
          const [start, end] = range.split('-').map(Number);
          for (let i = start; i <= end; i++) {
            const line = block.querySelector(`.line-${i}`);
            if (line) line.classList.add('marked');
          }
        } else {
          const line = block.querySelector(`.line-${range}`);
          if (line) line.classList.add('marked');
        }
      });
    }
  });
});</script><script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js" id="MathJax-script"></script><script>(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const themeToggle = document.querySelector('.theme-toggle');
    
    if (!themeToggle) return;
    
    const getCurrentTheme = () => {
      return document.documentElement.getAttribute('data-theme') || 'light';
    };
    
    const updateUI = (theme) => {
      const isDark = theme === 'dark';
      themeToggle.setAttribute('aria-pressed', isDark.toString());
    };
    
    const setTheme = (theme) => {
      document.documentElement.setAttribute('data-theme', theme);
      document.documentElement.style.colorScheme = theme;
      
      const pageWrapper = document.getElementById('page-wrapper');
      if (pageWrapper) {
        pageWrapper.setAttribute('data-theme', theme);
      }
      
      // Find and remove the temporary anti-flicker style tag if it exists.
      // This ensures the main stylesheet takes full control after the initial load.
      const antiFlickerStyle = document.getElementById('anti-flicker-style');
      if (antiFlickerStyle) {
        antiFlickerStyle.remove();
      }
      
      localStorage.setItem('theme', theme);
      updateUI(theme);
    };
    
    const toggleTheme = () => {
      const current = getCurrentTheme();
      const newTheme = current === 'light' ? 'dark' : 'light';
      setTheme(newTheme);
    };
    
    updateUI(getCurrentTheme());
    
    themeToggle.addEventListener('click', toggleTheme);
    
    if (window.matchMedia) {
      const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
      mediaQuery.addEventListener('change', function(e) {
        if (!localStorage.getItem('theme')) {
          const theme = e.matches ? 'dark' : 'light';
          setTheme(theme);
        }
      });
    }
  });
})();
</script><script src="../js/details-toggle.js" defer></script><script>(function() {
  document.addEventListener('DOMContentLoaded', function() {
    const backToTopBtn = document.querySelector('.back-to-top');
    
    if (!backToTopBtn) return;
    
    const toggleButtonVisibility = () => {
      const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
      const shouldShow = scrollTop > 200;
      
      if (shouldShow) {
        backToTopBtn.classList.add('is-visible');
      } else {
        backToTopBtn.classList.remove('is-visible');
      }
    };
    
    let ticking = false;
    const handleScroll = () => {
      if (!ticking) {
        requestAnimationFrame(() => {
          toggleButtonVisibility();
          ticking = false;
        });
        ticking = true;
      }
    };
    
    const scrollToTop = (event) => {
      event.preventDefault();
      window.scrollTo({
        top: 0,
        behavior: 'smooth'
      });
    };
    
    window.addEventListener('scroll', handleScroll);
    backToTopBtn.addEventListener('click', scrollToTop);
    
    toggleButtonVisibility();
  });
})();</script></body></html>