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}