Skip to content

特殊邮箱(Gmail 代理访问 / 运营商邮箱) ​

本文档整理两类「特殊邮箱」的接入要点:

  • Gmail 等需要代理才能访问的海外邮箱:协议原理、可行路线与已知限制(见第一至三节);
  • 189.cn 等运营商邮箱:服务端对 IMAP 可见范围的限制与变通方案(见第四节)。

以下场景说明针对 Gmail / 代理路线:

  • 服务器处于中国大陆等需要代理才能访问 Google 服务的网络环境;
  • 不想为 Gmail 注册 Google 开发者、不想走 OAuth2 授权流程;
  • 希望通过「应用专用密码 + 代理」的方式接入 Gmail。

一、协议原理:IMAP 并不通过 HTTP 通信 ​

很多人误以为项目里的「HTTP 代理」是把 IMAP 请求伪装成 HTTP 请求发出去。实际上:

IMAP 是独立于 HTTP 的应用层协议,本项目使用 HTTP 代理的 CONNECT 隧道 能力打一条 TCP 隧道,而不是把 IMAP 变成 HTTP。

  • Gmail 的收信地址为 imap.gmail.com:993(TLS 之上的 IMAP);
  • 发信地址为 smtp.gmail.com:587(STARTTLS);

代理流程如下:

  1. 客户端先与代理服务器建立连接;
  2. 发送 CONNECT imap.gmail.com:993 请求;
  3. 代理返回 200 后,后续连接是一条透明的 TCP 字节流隧道;
  4. 客户端在这条隧道之上再包一层 TLS,然后跑纯 IMAP 命令——与直连完全一致。

对应到代码(server/proxy/proxy.go):

  • httpProxyDialer:通过 HTTP CONNECT 方法建立 TCP 隧道;
  • socks5Dialer:标准 SOCKS5 握手,是通用 TCP 代理,连 HTTP 都不沾边;
  • IMAP / SMTP / POP3 三个协议都会调用同一个 proxy.Dialer(),先打隧道再包 TLS。

结论

IMAP ≠ HTTP,它只是「借 HTTP 代理的 CONNECT 能力打 TCP 隧道」。本项目对 IMAP/SMTP/POP3 的数据通道实现是正确的。


二、可行路线:应用专用密码 + 代理(不注册开发者) ​

Gmail 现在基本要求 OAuth2 或 应用专用密码(App Password)。本项目未实现 Google 的 OAuth2 Provider,因此推荐走「应用专用密码」路线,正好绕开了 OAuth2 相关限制(无需注册开发者、无需设备码/token 刷新走代理)。

为什么代码层面没问题 ​

  • 账号默认 AuthType = password,对应 IMAP 客户端的传统 LOGIN 命令,完全不会触发 OAuth2 流程;
  • 因此服务端「缺少 Google provider」「OAuth2 token 刷新不走代理」这两个缺陷都与此路线无关;
  • Gmail 预设(web/src/data/providers.js)已内置 imap.gmail.com:993 + smtp.gmail.com:587,无需手填。

具体配置步骤 ​

  1. 在 Google 账号中开启 两步验证(2-Step Verification);
  2. 进入「安全 → 应用专用密码」,生成一个 16 位的应用密码(这是 IMAP/SMTP 的「密码」,不是你的 Google 登录密码);
  3. 在 Magicmail 添加账号:邮箱填完整 Gmail 地址,AuthType 保持 password(默认),密码填 16 位应用密码;
  4. 打开「启用代理」开关,填写代理地址,例如:
    • HTTP 代理:http://user:pass@host:port
    • SOCKS5 代理:socks5://user:pass@host:port

代理是「按账号」配置的,不是全局的

本项目没有全局代理,代理完全由每个账号的 ProxyEnabled / ProxyURL 控制。Gmail 账号需要单独打开它自己的代理开关,配置一次全局代理并不会对所有账号生效。


三、已知限制与注意事项 ​

以下不是代码 bug,而是环境、协议与风控层面的现实约束。

1. 代理必须放行 993 和 587 端口(最容易踩坑) ​

代码发起的是 CONNECT imap.gmail.com:993 与 CONNECT smtp.gmail.com:587。而很多 HTTP 代理只允许 CONNECT 到 443,587 ≠ 443、993 ≠ 443,这类代理会直接拒绝隧道建立,导致:

  • SMTP 发信失败(587 被拒);
  • IMAP 收信失败(993 被拒)。

推荐优先使用 SOCKS5 代理

SOCKS5 是通用 TCP 转发,没有端口限制,能彻底规避「只允许 443」的问题。如果只能使用 HTTP 代理,请确认该代理允许 CONNECT 到 993 / 587 端口。

2. Google 会对代理 IP 做风控 ​

应用专用密码相比 OAuth2 容忍度更高,但从数据中心 / 机房 IP、匿名代理 IP 首次登录时,Google 安全引擎仍可能:

  • 触发「阻止了一次登录尝试」的安全提醒;
  • 临时拦截并要求二次验证或验证码。

这是代理方案绕不开的现实风险,无法用代码解决。缓解建议:

  • 使用稳定、最好是住宅类型的代理 IP;
  • 触发拦截后,前往 Google 账号的安全页面确认是本人操作即可恢复。

3. 仅按账号代理,无全局兜底 ​

如前所述,当前没有全局代理配置入口。如果你有多个 Gmail 账号,需要逐个账号开启代理开关;系统也不会在未配置代理时自动回落到某个全局代理。

4. CONNECT 响应解析较为脆弱(代码健壮性瑕疵) ​

当前 httpProxyDialer 仅读取 1024 字节,并固定取响应字节 resp[9:12] 判断是否 200。当代理返回的响应头特别长(例如带较多头字段)时,可能读取不完整导致误判。

  • 影响:极少数情况下,连接本应成功却被误判为失败;
  • 现状:属于健壮性优化点,不影响大多数正常代理环境;
  • 可改进方向:改为按 \r\n\r\n 切分响应后再解析状态码。

四、另一类特殊邮箱:运营商邮箱(189.cn) ​

天翼邮箱(189.cn,Coremail / 21cn 实现)是另一种「特殊」——它不需要代理,但在协议层面限制了可见范围。

现象 ​

账号添加成功、认证成功,应用内却一直为空;而网页端能看到大量历史邮件(例如中国电信的账单邮件)。

原因 ​

IMAP / POP3 只投递「开通服务之后」新到达的邮件,历史邮件不对第三方客户端开放。

这是运营商邮箱的常见策略,属服务端限制,客户端在协议层无法绕过。该服务器同时也不支持 IMAP IDLE(能力集仅 IMAP4 IMAP4rev1 ID XLIST XAPPLEPUSHSERVICE),应用会自动降级为轮询。

如何自行确认 ​

用诊断工具看两条关键证据(完整用法见 IMAP 诊断工具):

bash
go run ./cmd/imapdiag -host imap.189.cn -user <账号> -pass <授权码> -all
  1. 三条独立路径计数一致为 0:STATUS 的 MESSAGES、SELECT 的 EXISTS、UID SEARCH ALL 全为 0,且 -all 扫描所有目录也都是 0 —— 可排除协议解析问题;
  2. UID 跳号:从外部邮箱发一封测试邮件后立刻复查,若新邮件能读到、且其 UID 远大于 1(实测首次即 UID=115),说明服务器上存在过 114 个更早的 UID 却不暴露 —— 这就是服务端限制的确证。

变通方案 ​

在网页端把需要的历史邮件转发给自己,转发件会作为全新邮件进入 IMAP,应用即可正常收取。仅限个别重要邮件,批量操作不现实。


五、小结 ​

维度结论
IMAP 是否走 HTTP否,仅借 HTTP CONNECT 打 TCP 隧道,协议实现正确
不注册开发者能否用 Gmail能,走「应用专用密码 + 代理」即可,无需 OAuth2 / Google Provider
主要技术拦路虎代理端口放行(993/587),优先使用 SOCKS5
主要现实拦路虎Google 对代理 IP 的风控,触发安全提醒/拦截
代理作用范围仅「按账号」生效,无全局代理兜底
运营商邮箱(189.cn)历史邮件不对 IMAP 开放,仅能收取开通后的新邮件;且不支持 IDLE,自动走轮询

一句话:用应用专用密码 + 代理可以正常使用 Gmail;代码层面的数据通道没有问题,唯一现实拦路虎是代理端口放行(993/587)和 Google 对代理 IP 的风控。运营商邮箱则是另一类限制——协议层只给你看开通后的新邮件。

相关文档:

基于 AGPLv3 协议开源