Crate imap_types

source ·
Expand description

§Misuse-resistant IMAP types

The most prominent types in imap-types are Greeting, Command, and Response, and we use the term “message” to refer to either of them. Messages can be created in different ways. However, what all ways have in common is, that the API does not allow the creation of invalid ones.

For example, all commands in IMAP are prefixed with a “tag”. Although IMAP’s tags are just strings, they have additional rules, such as that no whitespace is allowed. Thus, imap-types encapsulate them in Tag struct to ensure that invalid ones can’t be created.

§Understanding and using the core types

Similar to Tags, there are more “core types” (or “string types”), such as, Atom, Quoted, or Literal. Besides being used for correctness, these types play a crucial role in IMAP because they determine the IMAP protocol flow. Sending a password as a literal requires a different protocol flow than sending the password as an atom or a quoted string. So, even though imap-types can choose the most efficient representation for a datum automatically, it’s good to become familiar with the core module at some point to master the IMAP protocol.

§Construction of messages

imap-types relies a lot on the standard conversion traits, i.e., From, TryFrom, Into, and TryInto. Make good use of them. More convenient constructors are available for types that are more cumbersome to create.

Note: When you are sure that the thing you want to create is valid, you can unlock various unvalidated(...) functions through the unvalidated feature. This allows us to bypass certain checks in release builds.

§Example

use imap_types::{
    command::{Command, CommandBody},
    core::Tag,
};

// # Variant 1
// Create a `Command` with `tag` "A123" and `body` "NOOP".
// (Note: `Command::new()` returns `Err(...)` when the tag is invalid.)
let cmd = Command::new("A123", CommandBody::Noop).unwrap();

// # Variant 2
// Create a `CommandBody` first and finalize it into
// a `Command` by attaching a tag later.
let cmd = CommandBody::Noop.tag("A123").unwrap();

// # Variant 3
// Create a `Command` directly.
let cmd = Command {
    tag: Tag::try_from("A123").unwrap(),
    body: CommandBody::Noop,
};

§More complex messages

§Example

The following example is a server fetch response containing the size and MIME structure of a message with the sequence number (or UID) 42.

use std::{borrow::Cow, num::NonZeroU32};

use imap_types::{
    body::{BasicFields, Body, BodyStructure, SinglePartExtensionData, SpecificFields},
    core::{IString, NString, Vec1},
    fetch::MessageDataItem,
    response::{Data, Response},
};

let fetch = {
    let data = Data::Fetch {
        seq: NonZeroU32::new(42).unwrap(),
        items: Vec1::try_from(vec![
            MessageDataItem::Rfc822Size(1337),
            MessageDataItem::Body(BodyStructure::Single {
                body: Body {
                    basic: BasicFields {
                        parameter_list: vec![],
                        id: NString(None),
                        description: NString(Some(
                            IString::try_from("Important message.").unwrap(),
                        )),
                        content_transfer_encoding: IString::try_from("base64").unwrap(),
                        size: 512,
                    },
                    specific: SpecificFields::Basic {
                        r#type: IString::try_from("text").unwrap(),
                        subtype: IString::try_from("html").unwrap(),
                    },
                },
                extension_data: None,
            }),
        ])
        .unwrap(),
    };

    Response::Data(data)
};

§Supported IMAP extensions

Description
IMAP4 non-synchronizing literals (RFC 2088, RFC 7888)
Internet Message Access Protocol (IMAP) - MOVE Extension (RFC 6851)
Internet Message Access Protocol (IMAP) UNSELECT command (RFC 3691)
IMAP Extension for Simple Authentication and Security Layer (SASL) Initial Client Response (RFC 4959)
The IMAP COMPRESS Extension (RFC 4978)
The IMAP ENABLE Extension (RFC 5161)
IMAP4 IDLE command (RFC 2177)
IMAP QUOTA Extension (RFC 9208)

§Features

This crate uses the following features to enable experimental IMAP extensions:

FeatureDescriptionStatus
ext_idIMAP4 ID extension (RFC 2971)Unfinished
ext_sort_threadInternet Message Access Protocol - SORT and THREAD Extensions (RFC 5256, RFC 5957)Unfinished
ext_condstore_qresyncIMAP Extensions: Quick Flag Changes Resynchronization (CONDSTORE) and Quick Mailbox Resynchronization (QRESYNC) (RFC 7162)Unfinished
ext_login_referralsIMAP4 Login Referrals (RFC 2221)Unfinished
ext_mailbox_referralsIMAP4 Mailbox Referrals (RFC 2193)Unfinished
ext_binaryIMAP4 Binary Content Extension (RFC 3516)Unfinished
ext_metadataThe IMAP METADATA Extension (RFC 5464)Unfinished
ext_uidplusIMAP4 UIDPLUS extension (RFC 2359, RFC 4315)Unfinished
starttlsIMAP4rev1 (RFC 3501; section 6.2.1)

STARTTLS is not an IMAP extension but feature-gated because it should be avoided. For better performance and security, use “implicit TLS”, i.e., IMAP-over-TLS on port 993, and don’t use STARTTLS at all.

Furthermore, imap-types uses the following features to facilitate interoperability:

FeatureDescriptionEnabled by default
arbitraryDerive Arbitrary implementationsNo
serdeDerive serdes Serialize and Deserialize implementationsNo
unvalidatedUnlock unvalidated constructorsNo

When using arbitrary, all types defined in imap-types implement the Arbitrary trait to ease testing. This is used, for example, to generate instances during fuzz-testing. (See, e.g., imap-types/fuzz/fuzz_targets/to_static.rs) When the serde feature is used, all types implement Serde’s Serialize and Deserialize traits. (Try running cargo run --example serde_json.)

Modules§

Traits§

  • Create owned variant of object (consuming it).
  • Create owned variant of object.