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
/*
* SPDX-FileCopyrightText: 2026 Stalwart Labs LLC <hello@stalw.art>
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*/
//! Quoted-printable (RFC 2045 section 6.7), the "Q" encoding of RFC 2047
//! encoded words and dkim-quoted-printable (RFC 6376 section 2.11).
//!
//! Every operation is a method on a [`QuotedPrintable`] engine:
//!
//! ```
//! use encodify::qp;
//!
//! # fn main() -> Result<(), encodify::Error> {
//! let body = qp::BODY.encode("Grüße,\nJürgen\n");
//! assert_eq!(body, "Gr=C3=BC=C3=9Fe,\r\nJ=C3=BCrgen\r\n");
//! assert_eq!(qp::BODY.decode(&body)?, "Grüße,\r\nJürgen\r\n".as_bytes());
//!
//! assert_eq!(qp::Q_PHRASE.encode("Keld Jørn"), "Keld_J=C3=B8rn");
//! let (name, used) = qp::Q_TEXT.decode_word("Andr=E9?= Pirard")?;
//! assert_eq!((name.as_slice(), used), (&b"Andr\xe9"[..], 9));
//!
//! assert_eq!(qp::DKIM.encode("a=b; c"), "a=3Db=3B=20c");
//! assert_eq!(qp::DKIM.decode("a=3Db=3B\r\n\t=20c")?, &b"a=b; c"[..]);
//! # Ok(())
//! # }
//! ```
//!
//! Decoding of `BODY` and `BINARY` is lenient by default, as RFC 2045
//! recommends for robust implementations; [`QuotedPrintable::strict`] turns
//! malformed escapes into errors.
pub
/// A quoted-printable configuration. Engines are `Copy` and cheap to pass
/// around; all the constants in this module are engines.
/// Text bodies (`Content-Transfer-Encoding: quoted-printable`).
///
/// Encoding turns LF and CRLF into CRLF hard line breaks, inserts `=` soft
/// breaks so that no line exceeds 76 characters, never splits an escape,
/// and escapes `=`, bytes from 0x7F up, control characters other than TAB,
/// and a space or tab that would end a line.
///
/// Decoding keeps CRLF and bare LF line breaks as they are, drops bare CR,
/// joins soft breaks (`=`, optional spaces or tabs, then a line break or the
/// end of the input), deletes the spaces and tabs that end a line, accepts
/// lowercase hex digits and keeps a malformed escape (`=` and the byte after
/// it) literally.
pub const BODY: QuotedPrintable = QuotedPrintable ;
/// Binary content such as attachments: like [`BODY`] except that encoding
/// escapes CR and LF too, so the output has soft line breaks only.
pub const BINARY: QuotedPrintable = QuotedPrintable ;
/// RFC 2047 "Q" encoding for unstructured header text such as `Subject`:
/// a space becomes `_`, and `=`, `?`, `_`, control characters and bytes from
/// 0x7F up are escaped.
///
/// Decoding turns `_` into a space and `=XX` escapes (either case) into
/// bytes and rejects any other `=`. Spaces and tabs, which RFC 2047 does
/// not allow in an encoded word but some encoders leave in, are kept; CR,
/// and LF with the spaces and tabs after it, are dropped as left over from
/// folding.
pub const Q_TEXT: QuotedPrintable = QuotedPrintable ;
/// RFC 2047 "Q" encoding for words in a phrase, such as display names
/// (section 5, rule 3): only letters, digits and `! * + - /` stay literal, a
/// space becomes `_` and every other byte is escaped. Decoding is the same
/// as [`Q_TEXT`].
pub const Q_PHRASE: QuotedPrintable = QuotedPrintable ;
/// dkim-quoted-printable (RFC 6376 section 2.11), used by the DKIM `i=`,
/// `z=` and ARC tags.
///
/// Encoding escapes every byte outside `dkim-safe-char`, including `=`, `;`,
/// spaces and 8-bit bytes, and `|`, which separates the items of `z=` (RFC
/// 6376 section 3.5). It does not wrap lines: callers fold between escapes,
/// or use [`QuotedPrintable::encode_folded`]. Decoding removes folding
/// whitespace, accepts lowercase hex digits, `|` and 8-bit bytes (RFC 8616)
/// and rejects anything else that is not `dkim-safe-char` or a complete
/// escape.
pub const DKIM: QuotedPrintable = QuotedPrintable ;