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
43    /// List-Help header defined in [RFC 2369](https://datatracker.ietf.org/doc/html/rfc2369).
44    ListHelp,
45    References,
46
47    /// In-Reply-To header containing Message-ID of the parent message.
48    InReplyTo,
49
50    /// Used to detect mailing lists if contains "list" value
51    /// as described in [RFC 3834](https://tools.ietf.org/html/rfc3834)
52    Precedence,
53
54    ContentType,
55    ContentId,
56    ChatVersion,
57    ChatGroupId,
58    ChatGroupName,
59    ChatGroupNameChanged,
60    ChatGroupNameTimestamp,
61    ChatVerified,
62    ChatGroupAvatar,
63    ChatUserAvatar,
64    ChatVoiceMessage,
65    ChatGroupMemberRemoved,
66    ChatGroupMemberAdded,
67    ChatContent,
68
69    /// Past members of the group.
70    ChatGroupPastMembers,
71
72    /// Space-separated timestamps of member addition
73    /// for members listed in the `To` field
74    /// followed by timestamps of member removal
75    /// for members listed in the `Chat-Group-Past-Members` field.
76    ChatGroupMemberTimestamps,
77
78    /// Duration of the attached media file.
79    ChatDuration,
80
81    ChatDispositionNotificationTo,
82    ChatWebrtcRoom,
83
84    /// This message deletes the messages listed in the value by rfc724_mid.
85    ChatDelete,
86
87    /// This message obsoletes the text of the message defined here by rfc724_mid.
88    ChatEdit,
89
90    /// [Autocrypt](https://autocrypt.org/) header.
91    Autocrypt,
92    AutocryptGossip,
93    AutocryptSetupMessage,
94    SecureJoin,
95
96    /// Deprecated header containing Group-ID in `vg-request-with-auth` message.
97    ///
98    /// It is not used by Alice as Alice knows the group corresponding to the AUTH token.
99    /// Bob still sends it for backwards compatibility.
100    SecureJoinGroup,
101    SecureJoinFingerprint,
102    SecureJoinInvitenumber,
103    SecureJoinAuth,
104    Sender,
105
106    /// Ephemeral message timer.
107    EphemeralTimer,
108    Received,
109
110    /// A header that includes the results of the DKIM, SPF and DMARC checks.
111    /// See <https://datatracker.ietf.org/doc/html/rfc8601>
112    AuthenticationResults,
113
114    /// Node address from iroh where direct addresses have been removed.
115    IrohNodeAddr,
116
117    /// Advertised gossip topic for one webxdc.
118    IrohGossipTopic,
119
120    #[cfg(test)]
121    TestHeader,
122}
123
124impl HeaderDef {
125    /// Returns the corresponding header string.
126    pub fn get_headername(&self) -> &'static str {
127        self.into()
128    }
129}
130
131#[allow(missing_docs)]
132pub trait HeaderDefMap {
133    /// Returns requested header value if it exists.
134    fn get_header_value(&self, headerdef: HeaderDef) -> Option<String>;
135
136    /// Returns requested header if it exists.
137    fn get_header(&self, headerdef: HeaderDef) -> Option<&MailHeader>;
138}
139
140impl HeaderDefMap for [MailHeader<'_>] {
141    fn get_header_value(&self, headerdef: HeaderDef) -> Option<String> {
142        self.get_first_value(headerdef.get_headername())
143    }
144    fn get_header(&self, headerdef: HeaderDef) -> Option<&MailHeader> {
145        self.get_first_header(headerdef.get_headername())
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    #[test]
154    /// Test that kebab_case serialization works as expected
155    fn kebab_test() {
156        assert_eq!(HeaderDef::From_.get_headername(), "from");
157
158        assert_eq!(HeaderDef::TestHeader.get_headername(), "test-header");
159    }
160
161    #[test]
162    /// Test that headers are parsed case-insensitively
163    fn test_get_header_value_case() {
164        let (headers, _) =
165            mailparse::parse_headers(b"fRoM: Bob\naUtoCryPt-SeTup-MessAge: v99").unwrap();
166        assert_eq!(
167            headers.get_header_value(HeaderDef::AutocryptSetupMessage),
168            Some("v99".to_string())
169        );
170        assert_eq!(
171            headers.get_header_value(HeaderDef::From_),
172            Some("Bob".to_string())
173        );
174        assert_eq!(headers.get_header_value(HeaderDef::Autocrypt), None);
175    }
176}