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, NonEmptyVec},
    fetch::MessageDataItem,
    response::{Data, Response},
};

let fetch = {
    let data = Data::Fetch {
        seq: NonZeroU32::new(42).unwrap(),
        items: NonEmptyVec::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)
IMAP MOVE Extension (RFC 6851)
IMAP UNSELECT command (RFC 3691)
IMAP Extension for 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_condstore_qresyncQuick Flag Changes Resynchronization and Quick Mailbox Resynchronization (RFC 7162)Unfinished
ext_login_referralsIMAP4 Login Referrals (RFC 2221)Unfinished
ext_mailbox_referralsIMAP4 Mailbox Referrals (RFC 2193)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 implementations.No
bounded-staticDerive ToStatic/IntoStatic implementations.No
serdeDerive serdes Serialize and Deserialize implementations.No
unvalidatedUnlock unvalidated constructors.No

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 using bounded-static, all types provide a to_static and into_static method that converts a type into its “owned” variant. This is useful when you want to pass objects around, e.g., into other threads, a vector, etc. When the serde feature is used, all types implement Serde’s Serialize and Deserialize traits. (Try running cargo run --example serde_json.)

Re-exports

Modules