26次发布
0.9.3 | 2024年3月28日 |
---|---|
0.9.2 | 2023年11月27日 |
0.9.1 | 2023年9月22日 |
0.8.2 | 2023年2月22日 |
0.3.0 | 2021年11月8日 |
#34 in 解析器实现
20,758 每月下载量
在 35 个crate(24个直接) 中使用
555KB
12K SLoC
mail-parser
mail-parser 是一个用Rust编写的 电子邮件解析库,完全符合互联网消息格式标准(《RFC 5322》),多用途互联网邮件扩展(MIME;《RFC 2045 - 2049》)以及许多其他互联网消息RFC。
它还支持解码41种不同的字符集,包括过时的格式,如UTF-7。所有Unicode(UTF-*)和单字节字符集都由库内部处理,而支持中文和日语等语言的旧多字节编码,如BIG5或ISO-2022-JP,则由可选依赖项encoding_rs提供。
通常,这个库遵循Postel定律或鲁棒性原则,即实现必须在发送行为上保守,在接收行为上自由。这意味着只要这些行为不太偏离标准,mail-parser 会尽力解析不符合规范的电子邮件消息。
与其他电子邮件解析库不同,该库遵循RFC 8621,第4.1.4节,提供了一种更符合人类友好的消息内容表示,包括仅由文本主体部分、HTML主体部分和附件组成。此外,当缺少alternative版本时,会自动将HTML和纯文本内联主体部分进行转换。
性能和内存安全性是在设计mail-parser时考虑的两个重要因素
- 零拷贝:此库返回的几乎所有字符串都是对输入原始消息的
Cow<str>
引用。 - 基于Chromium解码器的高性能Base64解码。
- 使用完美哈希函数快速解析消息头字段、字符集名称和HTML实体。
- 使用100%安全的Rust语言编写,无外部依赖。
- 库中的每个函数都经过模糊测试和彻底的MIRI测试。
- 经过实战考验,使用自1995年至今数百万条真实电子邮件消息。
- 由全球的Stalwart邮件服务器在生产环境中使用。
使用示例
let input = br#"From: Art Vandelay <[email protected]> (Vandelay Industries)
To: "Colleagues": "James Smythe" <[email protected]>; Friends:
[email protected], =?UTF-8?Q?John_Sm=C3=AEth?= <[email protected]>;
Date: Sat, 20 Nov 2021 14:22:01 -0800
Subject: Why not both importing AND exporting? =?utf-8?b?4pi6?=
Content-Type: multipart/mixed; boundary="festivus";
--festivus
Content-Type: text/html; charset="us-ascii"
Content-Transfer-Encoding: base64
PGh0bWw+PHA+SSB3YXMgdGhpbmtpbmcgYWJvdXQgcXVpdHRpbmcgdGhlICZsZHF1bztle
HBvcnRpbmcmcmRxdW87IHRvIGZvY3VzIGp1c3Qgb24gdGhlICZsZHF1bztpbXBvcnRpbm
cmcmRxdW87LDwvcD48cD5idXQgdGhlbiBJIHRob3VnaHQsIHdoeSBub3QgZG8gYm90aD8
gJiN4MjYzQTs8L3A+PC9odG1sPg==
--festivus
Content-Type: message/rfc822
From: "Cosmo Kramer" <[email protected]>
Subject: Exporting my book about coffee tables
Content-Type: multipart/mixed; boundary="giddyup";
--giddyup
Content-Type: text/plain; charset="utf-16"
Content-Transfer-Encoding: quoted-printable
=FF=FE=0C!5=D8"=DD5=D8)=DD5=D8-=DD =005=D8*=DD5=D8"=DD =005=D8"=
=DD5=D85=DD5=D8-=DD5=D8,=DD5=D8/=DD5=D81=DD =005=D8*=DD5=D86=DD =
=005=D8=1F=DD5=D8,=DD5=D8,=DD5=D8(=DD =005=D8-=DD5=D8)=DD5=D8"=
=DD5=D8=1E=DD5=D80=DD5=D8"=DD!=00
--giddyup
Content-Type: image/gif; name*1="about "; name*0="Book ";
name*2*=utf-8''%e2%98%95 tables.gif
Content-Transfer-Encoding: Base64
Content-Disposition: attachment
R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7
--giddyup--
--festivus--
"#;
let message = MessageParser::default().parse(input).unwrap();
// Parses addresses (including comments), lists and groups
assert_eq!(
message.from().unwrap().first().unwrap(),
&Addr::new(
"Art Vandelay (Vandelay Industries)".into(),
"[email protected]"
)
);
assert_eq!(
message.to().unwrap().as_group().unwrap(),
&[
Group::new(
"Colleagues",
vec![Addr::new("James Smythe".into(), "[email protected]")]
),
Group::new(
"Friends",
vec![
Addr::new(None, "[email protected]"),
Addr::new("John Smîth".into(), "[email protected]"),
]
)
]
);
assert_eq!(
message.date().unwrap().to_rfc3339(),
"2021-11-20T14:22:01-08:00"
);
// RFC2047 support for encoded text in message readers
assert_eq!(
message.subject().unwrap(),
"Why not both importing AND exporting? ☺"
);
// HTML and text body parts are returned conforming to RFC8621, Section 4.1.4
assert_eq!(
message.body_html(0).unwrap(),
concat!(
"<html><p>I was thinking about quitting the “exporting” to ",
"focus just on the “importing”,</p><p>but then I thought,",
" why not do both? ☺</p></html>"
)
);
// HTML parts are converted to plain text (and viceversa) when missing
assert_eq!(
message.body_text(0).unwrap(),
concat!(
"I was thinking about quitting the “exporting” to focus just on the",
" “importing”,\nbut then I thought, why not do both? ☺\n"
)
);
// Supports nested messages as well as multipart/digest
let nested_message = message
.attachment(0)
.unwrap()
.message();
.unwrap();
assert_eq!(
nested_message.subject().unwrap(),
"Exporting my book about coffee tables"
);
// Handles UTF-* as well as many legacy encodings
assert_eq!(
nested_message.body_text(0).unwrap(),
"ℌ𝔢𝔩𝔭 𝔪𝔢 𝔢𝔵𝔭𝔬𝔯𝔱 𝔪𝔶 𝔟𝔬𝔬𝔨 𝔭𝔩𝔢𝔞𝔰𝔢!"
);
assert_eq!(
nested_message.body_html(0).unwrap(),
"<html><body>ℌ𝔢𝔩𝔭 𝔪𝔢 𝔢𝔵𝔭𝔬𝔯𝔱 𝔪𝔶 𝔟𝔬𝔬𝔨 𝔭𝔩𝔢𝔞𝔰𝔢!</body></html>"
);
let nested_attachment = nested_message.attachment(0).unwrap();
assert_eq!(nested_attachment.len(), 42);
// Full RFC2231 support for continuations and character sets
assert_eq!(
nested_attachment.attachment_name().unwrap(),
"Book about ☕ tables.gif"
);
// Integrates with Serde
println!("{}", serde_json::to_string_pretty(&message).unwrap());
更多示例在示例目录下。请注意,此库不支持构建电子邮件消息,因为该功能由mail-builder
crate单独提供。
测试、模糊测试和基准测试
要运行测试套件
$ cargo test --all-features
或者,要使用MIRI运行测试套件
$ cargo +nightly miri test --all-features
使用cargo-fuzz
模糊库
$ cargo +nightly fuzz run mail_parser
并,运行基准测试
$ cargo +nightly bench --all-features
符合RFC
- RFC 822 - ARPA互联网文本消息标准
- RFC 5322 - 互联网消息格式
- RFC 2045 - 多用途互联网邮件扩展(MIME)第一部分:互联网消息主体的格式
- RFC 2046 - 多用途互联网邮件扩展(MIME)第二部分:媒体类型
- RFC 2047 - MIME(多用途互联网邮件扩展)第三部分:非ASCII文本的消息头扩展
- RFC 2048 - 多用途互联网邮件扩展(MIME)第四部分:注册程序
- RFC 2049 - 多用途互联网邮件扩展(MIME)第五部分:符合性准则和示例
- RFC 2231 - MIME参数值和编码词扩展:字符集、语言和延续
- RFC 2557 - MIME封装的聚合文档,如HTML(MHTML)
- RFC 2183 - 在互联网消息中传达展示信息:Content-Disposition头字段
- RFC 2392 - Content-ID和Message-ID统一资源定位符
- RFC 3282 - 内容语言头
- RFC 6532 - 国际化电子邮件头
- RFC 2152 - UTF-7 - Unicode的安全转换格式
- RFC 2369 - 将URL作为核心邮件列表命令的元语法及其通过消息头字段传输的使用
- RFC 2919 - List-Id:用于标识邮件列表的结构化字段和命名空间
- RFC 3339 - 互联网上的日期和时间:时间戳
- RFC 8621 - 邮件使用的JSON元应用协议(JMAP)(第4.1.4节)
- RFC 5957 - 互联网消息访问协议 - SORT和THREAD扩展(第2.1节)
支持的字符集
- UTF-8
- UTF-16, UTF-16BE, UTF-16LE
- UTF-7
- US-ASCII
- ISO-8859-1
- ISO-8859-2
- ISO-8859-3
- ISO-8859-4
- ISO-8859-5
- ISO-8859-6
- ISO-8859-7
- ISO-8859-8
- ISO-8859-9
- ISO-8859-10
- ISO-8859-13
- ISO-8859-14
- ISO-8859-15
- ISO-8859-16
- CP1250
- CP1251
- CP1252
- CP1253
- CP1254
- CP1255
- CP1256
- CP1257
- CP1258
- KOI8-R
- KOI8_U
- MACINTOSH
- IBM850
- TIS-620
通过可选依赖项encoding_rs支持字符集
- SHIFT_JIS
- BIG5
- EUC-JP
- EUC-KR
- GB18030
- GBK
- ISO-2022-JP
- WINDOWS-874
- IBM-866
许可证
许可方式任选其一
- Apache License,版本2.0(LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT许可证(LICENSE-MIT 或 http://opensource.org/licenses/MIT)
由您选择。
版权
版权(C)2020-2022,Stalwart Labs Ltd.
依赖关系
~0–1MB
~34K SLoC