deltachat/headerdef.rs
1//! # List of email headers.
2
3use mailparse::{MailHeader, MailHeaderMap};
4
5#[derive(Debug, Display, Clone, PartialEq, Eq, IntoStaticStr)]
6#[strum(serialize_all = "kebab_case")]
7#[allow(missing_docs)]
8pub enum HeaderDef {
9 MessageId,
10 Subject,
11 Date,
12 From_,
13 To,
14 AutoSubmitted,
15
16 /// Carbon copy.
17 Cc,
18 Disposition,
19
20 /// Used in the "Body Part Header" of MDNs as of RFC 8098.
21 /// Indicates the Message-ID of the message for which the MDN is being issued.
22 OriginalMessageId,
23
24 /// Delta Chat extension for message IDs in combined MDNs
25 AdditionalMessageIds,
26
27 /// Outlook-SMTP-server replace the `Message-ID:`-header
28 /// and write the original ID to `X-Microsoft-Original-Message-ID`.
29 /// To sort things correctly and to not show outgoing messages twice,
30 /// we need to check that header as well.
31 XMicrosoftOriginalMessageId,
32
33 /// Thunderbird header used to store Draft information.
34 ///
35 /// Thunderbird 78.11.0 does not set \Draft flag on messages saved as "Template", but sets this
36 /// header, so it can be used to ignore such messages.
37 XMozillaDraftInfo,
38
39 /// Mailing list ID defined in [RFC 2919](https://tools.ietf.org/html/rfc2919).
40 ListId,
41 ListPost,
42 /// Mailing list id, belonging to a broadcast channel created by Delta Chat
43 ChatListId,
44
45 /// List-Help header defined in [RFC 2369](https://datatracker.ietf.org/doc/html/rfc2369).
46 ListHelp,
47 References,
48
49 /// In-Reply-To header containing Message-ID of the parent message.
50 InReplyTo,
51
52 /// Used to detect mailing lists if contains "list" value
53 /// as described in [RFC 3834](https://tools.ietf.org/html/rfc3834)
54 Precedence,
55
56 ContentType,
57 ContentId,
58 ChatVersion,
59 ChatGroupId,
60 ChatGroupName,
61 ChatGroupNameChanged,
62 ChatGroupNameTimestamp,
63 ChatGroupDescription,
64 ChatGroupDescriptionChanged,
65 ChatGroupDescriptionTimestamp,
66 ChatVerified,
67 ChatGroupAvatar,
68 ChatUserAvatar,
69 ChatVoiceMessage,
70 ChatGroupMemberRemoved,
71 ChatGroupMemberRemovedFpr,
72 ChatGroupMemberAdded,
73 ChatGroupMemberAddedFpr,
74 ChatContent,
75
76 /// Past members of the group.
77 ChatGroupPastMembers,
78
79 /// Space-separated timestamps of member addition
80 /// for members listed in the `To` field
81 /// followed by timestamps of member removal
82 /// for members listed in the `Chat-Group-Past-Members` field.
83 ChatGroupMemberTimestamps,
84
85 /// Space-separated PGP key fingerprints
86 /// of group members listed in the `To` field
87 /// followed by fingerprints
88 /// of past members listed in the `Chat-Group-Past-Members` field.
89 ChatGroupMemberFpr,
90
91 /// Duration of the attached media file.
92 ChatDuration,
93
94 ChatDispositionNotificationTo,
95 ChatWebrtcRoom,
96 ChatWebrtcAccepted,
97 ChatWebrtcHasVideoInitially,
98
99 /// This message deletes the messages listed in the value by rfc724_mid.
100 ChatDelete,
101
102 /// This message obsoletes the text of the message defined here by rfc724_mid.
103 ChatEdit,
104
105 /// The secret shared amongst all recipients of this broadcast channel,
106 /// used to encrypt and decrypt messages.
107 /// This secret is sent to a new member in the member-addition message.
108 ChatBroadcastSecret,
109 /// A message with a large attachment is split into two messages:
110 /// A pre-message, which contains everything but the attachment,
111 /// and a Post-Message.
112 /// The Pre-Message gets a `Chat-Post-Message-Id` header
113 /// referencing the Post-Message's rfc724_mid.
114 ChatPostMessageId,
115
116 /// Announces Post-Message metadata in a Pre-Message.
117 /// Contains a serialized `PostMsgMetadata` struct.
118 ChatPostMessageMetadata,
119
120 /// This message is preceded by a Pre-Message
121 /// and thus this message can be skipped while fetching messages.
122 /// This is an unprotected header.
123 ChatIsPostMessage,
124
125 /// [Autocrypt](https://autocrypt.org/) header.
126 Autocrypt,
127 AutocryptGossip,
128 AutocryptSetupMessage,
129 SecureJoin,
130
131 /// Deprecated header containing Group-ID in `vg-request-with-auth` message.
132 ///
133 /// It is not used by Alice as Alice knows the group corresponding to the AUTH token.
134 /// Bob still sends it for backwards compatibility.
135 SecureJoinGroup,
136 SecureJoinFingerprint,
137 SecureJoinInvitenumber,
138 SecureJoinAuth,
139 Sender,
140
141 /// Ephemeral message timer.
142 EphemeralTimer,
143 Received,
144
145 /// A header that includes the results of the DKIM, SPF and DMARC checks.
146 /// See <https://datatracker.ietf.org/doc/html/rfc8601>
147 AuthenticationResults,
148
149 /// Node address from iroh where direct addresses have been removed.
150 ///
151 /// The node address sent in this header must have
152 /// a non-null relay URL as contacting home relay
153 /// is the only way to reach the node without
154 /// direct addresses and global discovery.
155 IrohNodeAddr,
156
157 /// Advertised gossip topic for one webxdc.
158 IrohGossipTopic,
159
160 /// See <https://www.rfc-editor.org/rfc/rfc9788.html#name-hp-outer-header-field>.
161 HpOuter,
162
163 #[cfg(test)]
164 TestHeader,
165}
166
167impl HeaderDef {
168 /// Returns the corresponding header string.
169 ///
170 /// Format is lower-kebab-case for easy comparisons.
171 /// This method is used in message receiving and testing.
172 pub fn get_headername(&self) -> &'static str {
173 self.into()
174 }
175}
176
177#[allow(missing_docs)]
178pub trait HeaderDefMap {
179 /// Returns requested header value if it exists.
180 fn get_header_value(&self, headerdef: HeaderDef) -> Option<String>;
181
182 /// Returns requested header if it exists.
183 fn get_header(&self, headerdef: HeaderDef) -> Option<&MailHeader<'_>>;
184}
185
186impl HeaderDefMap for [MailHeader<'_>] {
187 fn get_header_value(&self, headerdef: HeaderDef) -> Option<String> {
188 self.get_first_value(headerdef.get_headername())
189 }
190 fn get_header(&self, headerdef: HeaderDef) -> Option<&MailHeader<'_>> {
191 self.get_first_header(headerdef.get_headername())
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 #[test]
200 /// Test that kebab_case serialization works as expected
201 fn kebab_test() {
202 assert_eq!(HeaderDef::From_.get_headername(), "from");
203
204 assert_eq!(HeaderDef::TestHeader.get_headername(), "test-header");
205 }
206
207 #[test]
208 /// Test that headers are parsed case-insensitively
209 fn test_get_header_value_case() {
210 let (headers, _) =
211 mailparse::parse_headers(b"fRoM: Bob\naUtoCryPt-SeTup-MessAge: v99").unwrap();
212 assert_eq!(
213 headers.get_header_value(HeaderDef::AutocryptSetupMessage),
214 Some("v99".to_string())
215 );
216 assert_eq!(
217 headers.get_header_value(HeaderDef::From_),
218 Some("Bob".to_string())
219 );
220 assert_eq!(headers.get_header_value(HeaderDef::Autocrypt), None);
221 }
222}