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    ChatVerified,
64    ChatGroupAvatar,
65    ChatUserAvatar,
66    ChatVoiceMessage,
67    ChatGroupMemberRemoved,
68    ChatGroupMemberRemovedFpr,
69    ChatGroupMemberAdded,
70    ChatGroupMemberAddedFpr,
71    ChatContent,
72
73    /// Past members of the group.
74    ChatGroupPastMembers,
75
76    /// Space-separated timestamps of member addition
77    /// for members listed in the `To` field
78    /// followed by timestamps of member removal
79    /// for members listed in the `Chat-Group-Past-Members` field.
80    ChatGroupMemberTimestamps,
81
82    /// Space-separated PGP key fingerprints
83    /// of group members listed in the `To` field
84    /// followed by fingerprints
85    /// of past members listed in the `Chat-Group-Past-Members` field.
86    ChatGroupMemberFpr,
87
88    /// Duration of the attached media file.
89    ChatDuration,
90
91    ChatDispositionNotificationTo,
92    ChatWebrtcRoom,
93    ChatWebrtcAccepted,
94
95    /// This message deletes the messages listed in the value by rfc724_mid.
96    ChatDelete,
97
98    /// This message obsoletes the text of the message defined here by rfc724_mid.
99    ChatEdit,
100
101    /// The secret shared amongst all recipients of this broadcast channel,
102    /// used to encrypt and decrypt messages.
103    /// This secret is sent to a new member in the member-addition message.
104    ChatBroadcastSecret,
105    /// A message with a large attachment is split into two messages:
106    /// A pre-message, which contains everything but the attachment,
107    /// and a Post-Message.
108    /// The Pre-Message gets a `Chat-Post-Message-Id` header
109    /// referencing the Post-Message's rfc724_mid.
110    ChatPostMessageId,
111
112    /// Announces Post-Message metadata in a Pre-Message.
113    /// Contains a serialized `PostMsgMetadata` struct.
114    ChatPostMessageMetadata,
115
116    /// This message is preceded by a Pre-Message
117    /// and thus this message can be skipped while fetching messages.
118    /// This is an unprotected header.
119    ChatIsPostMessage,
120
121    /// [Autocrypt](https://autocrypt.org/) header.
122    Autocrypt,
123    AutocryptGossip,
124    AutocryptSetupMessage,
125    SecureJoin,
126
127    /// Deprecated header containing Group-ID in `vg-request-with-auth` message.
128    ///
129    /// It is not used by Alice as Alice knows the group corresponding to the AUTH token.
130    /// Bob still sends it for backwards compatibility.
131    SecureJoinGroup,
132    SecureJoinFingerprint,
133    SecureJoinInvitenumber,
134    SecureJoinAuth,
135    Sender,
136
137    /// Ephemeral message timer.
138    EphemeralTimer,
139    Received,
140
141    /// A header that includes the results of the DKIM, SPF and DMARC checks.
142    /// See <https://datatracker.ietf.org/doc/html/rfc8601>
143    AuthenticationResults,
144
145    /// Node address from iroh where direct addresses have been removed.
146    ///
147    /// The node address sent in this header must have
148    /// a non-null relay URL as contacting home relay
149    /// is the only way to reach the node without
150    /// direct addresses and global discovery.
151    IrohNodeAddr,
152
153    /// Advertised gossip topic for one webxdc.
154    IrohGossipTopic,
155
156    /// See <https://www.rfc-editor.org/rfc/rfc9788.html#name-hp-outer-header-field>.
157    HpOuter,
158
159    #[cfg(test)]
160    TestHeader,
161}
162
163impl HeaderDef {
164    /// Returns the corresponding header string.
165    ///
166    /// Format is lower-kebab-case for easy comparisons.
167    /// This method is used in message receiving and testing.
168    pub fn get_headername(&self) -> &'static str {
169        self.into()
170    }
171}
172
173#[allow(missing_docs)]
174pub trait HeaderDefMap {
175    /// Returns requested header value if it exists.
176    fn get_header_value(&self, headerdef: HeaderDef) -> Option<String>;
177
178    /// Returns requested header if it exists.
179    fn get_header(&self, headerdef: HeaderDef) -> Option<&MailHeader<'_>>;
180}
181
182impl HeaderDefMap for [MailHeader<'_>] {
183    fn get_header_value(&self, headerdef: HeaderDef) -> Option<String> {
184        self.get_first_value(headerdef.get_headername())
185    }
186    fn get_header(&self, headerdef: HeaderDef) -> Option<&MailHeader<'_>> {
187        self.get_first_header(headerdef.get_headername())
188    }
189}
190
191#[cfg(test)]
192mod tests {
193    use super::*;
194
195    #[test]
196    /// Test that kebab_case serialization works as expected
197    fn kebab_test() {
198        assert_eq!(HeaderDef::From_.get_headername(), "from");
199
200        assert_eq!(HeaderDef::TestHeader.get_headername(), "test-header");
201    }
202
203    #[test]
204    /// Test that headers are parsed case-insensitively
205    fn test_get_header_value_case() {
206        let (headers, _) =
207            mailparse::parse_headers(b"fRoM: Bob\naUtoCryPt-SeTup-MessAge: v99").unwrap();
208        assert_eq!(
209            headers.get_header_value(HeaderDef::AutocryptSetupMessage),
210            Some("v99".to_string())
211        );
212        assert_eq!(
213            headers.get_header_value(HeaderDef::From_),
214            Some("Bob".to_string())
215        );
216        assert_eq!(headers.get_header_value(HeaderDef::Autocrypt), None);
217    }
218}