imap/types/capabilities.rs
1use imap_proto::types::Capability;
2use std::collections::hash_set::Iter;
3use std::collections::HashSet;
4
5const IMAP4REV1_CAPABILITY: &str = "IMAP4rev1";
6const AUTH_CAPABILITY_PREFIX: &str = "AUTH=";
7
8/// From [section 7.2.1 of RFC 3501](https://tools.ietf.org/html/rfc3501#section-7.2.1).
9///
10/// A list of capabilities that the server supports.
11/// The capability list will include the atom "IMAP4rev1".
12///
13/// In addition, all servers implement the `STARTTLS`, `LOGINDISABLED`, and `AUTH=PLAIN` (described
14/// in [IMAP-TLS](https://tools.ietf.org/html/rfc2595)) capabilities. See the [Security
15/// Considerations section of the RFC](https://tools.ietf.org/html/rfc3501#section-11) for
16/// important information.
17///
18/// A capability name which begins with `AUTH=` indicates that the server supports that particular
19/// authentication mechanism.
20///
21/// The `LOGINDISABLED` capability indicates that the `LOGIN` command is disabled, and that the
22/// server will respond with a [`super::Error::No`] response to any attempt to use the `LOGIN`
23/// command even if the user name and password are valid. An IMAP client MUST NOT issue the
24/// `LOGIN` command if the server advertises the `LOGINDISABLED` capability.
25///
26/// Other capability names indicate that the server supports an extension, revision, or amendment
27/// to the IMAP4rev1 protocol. Capability names either begin with `X` or they are standard or
28/// standards-track [RFC 3501](https://tools.ietf.org/html/rfc3501) extensions, revisions, or
29/// amendments registered with IANA.
30///
31/// Client implementations SHOULD NOT require any capability name other than `IMAP4rev1`, and MUST
32/// ignore any unknown capability names.
33pub struct Capabilities(
34 // Note that this field isn't *actually* 'static.
35 // Rather, it is tied to the lifetime of the `ZeroCopy` that contains this `Name`.
36 pub(crate) HashSet<Capability<'static>>,
37);
38
39impl Capabilities {
40 /// Check if the server has the given capability.
41 pub fn has<'a>(&self, cap: &Capability<'a>) -> bool {
42 self.0.contains(cap)
43 }
44
45 /// Check if the server has the given capability via str.
46 pub fn has_str<S: AsRef<str>>(&self, cap: S) -> bool {
47 let s = cap.as_ref();
48 if s.eq_ignore_ascii_case(IMAP4REV1_CAPABILITY) {
49 return self.has(&Capability::Imap4rev1);
50 }
51 if s.len() > AUTH_CAPABILITY_PREFIX.len() {
52 let (pre, val) = s.split_at(AUTH_CAPABILITY_PREFIX.len());
53 if pre.eq_ignore_ascii_case(AUTH_CAPABILITY_PREFIX) {
54 return self.has(&Capability::Auth(val));
55 }
56 }
57 self.has(&Capability::Atom(s))
58 }
59
60 /// Iterate over all the server's capabilities
61 pub fn iter(&self) -> Iter<'_, Capability<'_>> {
62 self.0.iter()
63 }
64
65 /// Returns how many capabilities the server has.
66 pub fn len(&self) -> usize {
67 self.0.len()
68 }
69
70 /// Returns true if the server purports to have no capabilities.
71 pub fn is_empty(&self) -> bool {
72 self.0.is_empty()
73 }
74}