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    /// Space-separated PGP key fingerprints
79    /// of group members listed in the `To` field
80    /// followed by fingerprints
81    /// of past members listed in the `Chat-Group-Past-Members` field.
82    ChatGroupMemberFpr,
83
84    /// Duration of the attached media file.
85    ChatDuration,
86
87    ChatDispositionNotificationTo,
88    ChatWebrtcRoom,
89
90    /// This message deletes the messages listed in the value by rfc724_mid.
91    ChatDelete,
92
93    /// This message obsoletes the text of the message defined here by rfc724_mid.
94    ChatEdit,
95
96    /// [Autocrypt](https://autocrypt.org/) header.
97    Autocrypt,
98    AutocryptGossip,
99    AutocryptSetupMessage,
100    SecureJoin,
101
102    /// Deprecated header containing Group-ID in `vg-request-with-auth` message.
103    ///
104    /// It is not used by Alice as Alice knows the group corresponding to the AUTH token.
105    /// Bob still sends it for backwards compatibility.
106    SecureJoinGroup,
107    SecureJoinFingerprint,
108    SecureJoinInvitenumber,
109    SecureJoinAuth,
110    Sender,
111
112    /// Ephemeral message timer.
113    EphemeralTimer,
114    Received,
115
116    /// A header that includes the results of the DKIM, SPF and DMARC checks.
117    /// See <https://datatracker.ietf.org/doc/html/rfc8601>
118    AuthenticationResults,
119
120    /// Node address from iroh where direct addresses have been removed.
121    IrohNodeAddr,
122
123    /// Advertised gossip topic for one webxdc.
124    IrohGossipTopic,
125
126    #[cfg(test)]
127    TestHeader,
128}
129
130impl HeaderDef {
131    /// Returns the corresponding header string.
132    pub fn get_headername(&self) -> &'static str {
133        self.into()
134    }
135}
136
137#[allow(missing_docs)]
138pub trait HeaderDefMap {
139    /// Returns requested header value if it exists.
140    fn get_header_value(&self, headerdef: HeaderDef) -> Option<String>;
141
142    /// Returns requested header if it exists.
143    fn get_header(&self, headerdef: HeaderDef) -> Option<&MailHeader<'_>>;
144}
145
146impl HeaderDefMap for [MailHeader<'_>] {
147    fn get_header_value(&self, headerdef: HeaderDef) -> Option<String> {
148        self.get_first_value(headerdef.get_headername())
149    }
150    fn get_header(&self, headerdef: HeaderDef) -> Option<&MailHeader<'_>> {
151        self.get_first_header(headerdef.get_headername())
152    }
153}
154
155#[cfg(test)]
156mod tests {
157    use super::*;
158
159    #[test]
160    /// Test that kebab_case serialization works as expected
161    fn kebab_test() {
162        assert_eq!(HeaderDef::From_.get_headername(), "from");
163
164        assert_eq!(HeaderDef::TestHeader.get_headername(), "test-header");
165    }
166
167    #[test]
168    /// Test that headers are parsed case-insensitively
169    fn test_get_header_value_case() {
170        let (headers, _) =
171            mailparse::parse_headers(b"fRoM: Bob\naUtoCryPt-SeTup-MessAge: v99").unwrap();
172        assert_eq!(
173            headers.get_header_value(HeaderDef::AutocryptSetupMessage),
174            Some("v99".to_string())
175        );
176        assert_eq!(
177            headers.get_header_value(HeaderDef::From_),
178            Some("Bob".to_string())
179        );
180        assert_eq!(headers.get_header_value(HeaderDef::Autocrypt), None);
181    }
182}