maildir是三层子目录结构(tmp、new、cur),邮件为独立文件,需用二进制模式读取;头尾以双换行分隔,应跳过tmp目录并校验文件可读性与大小。

Maildir目录结构怎么看懂
Maildir不是单个文件,而是一套约定俗成的三层子目录结构:tmp、new、cur。真正可读的邮件只在 new 和 cur 下,每个邮件是独立文件,无锁、无索引、无格式封装——这意味着你不需要解析 mbox 的换行转义或 From 行,直接用 C++ 读文件内容就行。
但注意:cur 下的文件名带后缀(如 1712345678.12345.host:2,S),冒号之后是 Maildir 标志位(,S 表示已读,,T 表示标记,,F 表示已删除),这些后缀不影响邮件正文,可忽略或按需过滤。
- 先
opendir()或 C++17std::filesystem::directory_iterator遍历new/和cur/ - 跳过以
.开头的隐藏文件(如.subscriptions)和非普通文件(如符号链接) - 不依赖文件扩展名——Maildir 没有统一后缀,纯文本即可
用 std::ifstream 读取单封邮件要注意什么
邮件正文是纯文本(RFC 5322 格式),但可能含二进制附件(base64 / quoted-printable 编码)。C++ 默认以 text 模式打开文件会触发换行符转换(比如 Windows 下 \r\n → \n),破坏 base64 编码或签名块(如 PGP ASCII armor),所以必须用 binary 模式读取。
- 始终用
std::ifstream file(path, std::ios::binary) - 用
file.seekg(0, std::ios::end); size = file.tellg(); file.seekg(0);获取长度,再std::vector<char> buf(size); file.read(buf.data(), size);</char> - 不要用
std::getline()逐行读——遇到空行(消息头与正文分隔符)就该停,但有些邮件头里有折叠字段(续行以空格开头),简单按行切会破坏结构
如何安全提取邮件头与正文分界
Maildir 不改邮件内容,所以 RFC 分隔规则依然有效:第一个连续空行(\r\n\r\n 或 \n\n)就是头尾分界点。但不能假设换行符统一——原始邮件可能混用 \n 和 \r\n,尤其来自不同 MUA 的邮件。
- 在
std::vector<char></char>缓冲区中搜索双换行序列,优先匹配\r\n\r\n,再 fallback 到\n\n - 注意:有些邮件头字段值本身含
\n(折叠行),但折叠行前一行结尾必有\r\n或\n,且下一行以 WSP(空格或制表符)开头;真正的空行是前后都无非空白字符的行 - 若找不到空行,整封当 header 处理(极少数损坏邮件),避免越界访问
实际读取时容易漏掉的边界情况
Maildir 目录本身不保证原子性,tmp/ 下正在写入的文件可能被误读;cur/ 中已加标志但未同步的文件可能处于中间状态;某些 MDA(如 Dovecot)会在 cur/ 文件名后加 :2, 后缀并附带自定义标志(如 :2,ARW),这些都不影响内容,但若用正则过滤文件名,容易把合法邮件排除。
- 永远跳过
tmp/目录——它只供写入,不是读取目标 - 检查文件是否可读且大小 > 0,避免读取零字节或权限不足的文件
- 对
cur/文件,不要因含:2,就跳过;对new/文件,不因无后缀就认为“更干净”——两者内容等价 - Linux 上注意 st_size 可能不准(NFS、overlayfs),保险起见仍用 read() 实际读取并检查结果
真正麻烦的从来不是读文件,而是后续怎么解析 header 字段、处理编码、提取 MIME part——那得靠专用库(如 mimetic 或 mailio),纯 C++ 标准库只管把字节流拿进来,而且得拿得稳、不丢、不乱码。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











