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
106    /// [Autocrypt](https://autocrypt.org/) header.
107    Autocrypt,
108    AutocryptGossip,
109    AutocryptSetupMessage,
110    SecureJoin,
111
112    /// Deprecated header containing Group-ID in `vg-request-with-auth` message.
113    ///
114    /// It is not used by Alice as Alice knows the group corresponding to the AUTH token.
115    /// Bob still sends it for backwards compatibility.
116    SecureJoinGroup,
117    SecureJoinFingerprint,
118    SecureJoinInvitenumber,
119    SecureJoinAuth,
120    Sender,
121
122    /// Ephemeral message timer.
123    EphemeralTimer,
124    Received,
125
126    /// A header that includes the results of the DKIM, SPF and DMARC checks.
127    /// See <https://datatracker.ietf.org/doc/html/rfc8601>
128    AuthenticationResults,
129
130    /// Node address from iroh where direct addresses have been removed.
131    ///
132    /// The node address sent in this header must have
133    /// a non-null relay URL as contacting home relay
134    /// is the only way to reach the node without
135    /// direct addresses and global discovery.
136    IrohNodeAddr,
137
138    /// Advertised gossip topic for one webxdc.
139    IrohGossipTopic,
140
141    #[cfg(test)]
142    TestHeader,
143}
144
145impl HeaderDef {
146    /// Returns the corresponding header string.
147    pub fn get_headername(&self) -> &'static str {
148        self.into()
149    }
150}
151
152#[allow(missing_docs)]
153pub trait HeaderDefMap {
154    /// Returns requested header value if it exists.
155    fn get_header_value(&self, headerdef: HeaderDef) -> Option<String>;
156
157    /// Returns requested header if it exists.
158    fn get_header(&self, headerdef: HeaderDef) -> Option<&MailHeader<'_>>;
159}
160
161impl HeaderDefMap for [MailHeader<'_>] {
162    fn get_header_value(&self, headerdef: HeaderDef) -> Option<String> {
163        self.get_first_value(headerdef.get_headername())
164    }
165    fn get_header(&self, headerdef: HeaderDef) -> Option<&MailHeader<'_>> {
166        self.get_first_header(headerdef.get_headername())
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173
174    #[test]
175    /// Test that kebab_case serialization works as expected
176    fn kebab_test() {
177        assert_eq!(HeaderDef::From_.get_headername(), "from");
178
179        assert_eq!(HeaderDef::TestHeader.get_headername(), "test-header");
180    }
181
182    #[test]
183    /// Test that headers are parsed case-insensitively
184    fn test_get_header_value_case() {
185        let (headers, _) =
186            mailparse::parse_headers(b"fRoM: Bob\naUtoCryPt-SeTup-MessAge: v99").unwrap();
187        assert_eq!(
188            headers.get_header_value(HeaderDef::AutocryptSetupMessage),
189            Some("v99".to_string())
190        );
191        assert_eq!(
192            headers.get_header_value(HeaderDef::From_),
193            Some("Bob".to_string())
194        );
195        assert_eq!(headers.get_header_value(HeaderDef::Autocrypt), None);
196    }
197}