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    SecureJoin,
129
130    /// Deprecated header containing Group-ID in `vg-request-with-auth` message.
131    ///
132    /// It is not used by Alice as Alice knows the group corresponding to the AUTH token.
133    /// Bob still sends it for backwards compatibility.
134    SecureJoinGroup,
135    SecureJoinFingerprint,
136    SecureJoinInvitenumber,
137    SecureJoinAuth,
138    Sender,
139
140    /// Ephemeral message timer.
141    EphemeralTimer,
142    Received,
143
144    /// A header that includes the results of the DKIM, SPF and DMARC checks.
145    /// See <https://datatracker.ietf.org/doc/html/rfc8601>
146    AuthenticationResults,
147
148    /// Node address from iroh where direct addresses have been removed.
149    ///
150    /// The node address sent in this header must have
151    /// a non-null relay URL as contacting home relay
152    /// is the only way to reach the node without
153    /// direct addresses and global discovery.
154    IrohNodeAddr,
155
156    /// Advertised gossip topic for one webxdc.
157    IrohGossipTopic,
158
159    /// See <https://www.rfc-editor.org/rfc/rfc9788.html#name-hp-outer-header-field>.
160    HpOuter,
161
162    #[cfg(test)]
163    TestHeader,
164}
165
166impl HeaderDef {
167    /// Returns the corresponding header string.
168    ///
169    /// Format is lower-kebab-case for easy comparisons.
170    /// This method is used in message receiving and testing.
171    pub fn get_headername(&self) -> &'static str {
172        self.into()
173    }
174}
175
176#[allow(missing_docs)]
177pub trait HeaderDefMap {
178    /// Returns requested header value if it exists.
179    fn get_header_value(&self, headerdef: HeaderDef) -> Option<String>;
180
181    /// Returns requested header if it exists.
182    fn get_header(&self, headerdef: HeaderDef) -> Option<&MailHeader<'_>>;
183}
184
185impl HeaderDefMap for [MailHeader<'_>] {
186    fn get_header_value(&self, headerdef: HeaderDef) -> Option<String> {
187        self.get_first_value(headerdef.get_headername())
188    }
189    fn get_header(&self, headerdef: HeaderDef) -> Option<&MailHeader<'_>> {
190        self.get_first_header(headerdef.get_headername())
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use super::*;
197
198    #[test]
199    /// Test that kebab_case serialization works as expected
200    fn kebab_test() {
201        assert_eq!(HeaderDef::From_.get_headername(), "from");
202
203        assert_eq!(HeaderDef::TestHeader.get_headername(), "test-header");
204    }
205
206    #[test]
207    /// Test that headers are parsed case-insensitively
208    fn test_get_header_value_case() {
209        let (headers, _) =
210            mailparse::parse_headers(b"fRoM: Bob\naUtoCryPt-GoSsIp: fooBaR").unwrap();
211        assert_eq!(
212            headers.get_header_value(HeaderDef::AutocryptGossip),
213            Some("fooBaR".to_string())
214        );
215        assert_eq!(
216            headers.get_header_value(HeaderDef::From_),
217            Some("Bob".to_string())
218        );
219        assert_eq!(headers.get_header_value(HeaderDef::Autocrypt), None);
220    }
221}