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