rust-imap/src/types/mod.rs
2021-04-07 20:02:34 -04:00

357 lines
14 KiB
Rust

//! This module contains types used throughout the IMAP protocol.
use std::borrow::Cow;
/// From section [2.3.1.1 of RFC 3501](https://tools.ietf.org/html/rfc3501#section-2.3.1.1).
///
/// A 32-bit value assigned to each message, which when used with the unique identifier validity
/// value (see below) forms a 64-bit value that will not refer to any other message in the mailbox
/// or any subsequent mailbox with the same name forever. Unique identifiers are assigned in a
/// strictly ascending fashion in the mailbox; as each message is added to the mailbox it is
/// assigned a higher UID than the message(s) which were added previously. Unlike message sequence
/// numbers, unique identifiers are not necessarily contiguous.
///
/// The unique identifier of a message will not change during the session, and will generally not
/// change between sessions. Any change of unique identifiers between sessions will be detectable
/// using the `UIDVALIDITY` mechanism discussed below. Persistent unique identifiers are required
/// for a client to resynchronize its state from a previous session with the server (e.g.,
/// disconnected or offline access clients); this is discussed further in
/// [`IMAP-DISC`](https://tools.ietf.org/html/rfc3501#ref-IMAP-DISC).
///
/// Associated with every mailbox are two values which aid in unique identifier handling: the next
/// unique identifier value and the unique identifier validity value.
///
/// The next unique identifier value is the predicted value that will be assigned to a new message
/// in the mailbox. Unless the unique identifier validity also changes (see below), the next
/// unique identifier value will have the following two characteristics. First, the next unique
/// identifier value will not change unless new messages are added to the mailbox; and second, the
/// next unique identifier value will change whenever new messages are added to the mailbox, even
/// if those new messages are subsequently expunged.
///
/// > Note: The next unique identifier value is intended to provide a means for a client to
/// > determine whether any messages have been delivered to the mailbox since the previous time it
/// > checked this value. It is not intended to provide any guarantee that any message will have
/// > this unique identifier. A client can only assume, at the time that it obtains the next
/// > unique identifier value, that messages arriving after that time will have a UID greater than
/// > or equal to that value.
///
/// The unique identifier validity value is sent in a `UIDVALIDITY` response code in an `OK`
/// untagged response at mailbox selection time. If unique identifiers from an earlier session fail
/// to persist in this session, the unique identifier validity value will be greater than the one
/// used in the earlier session.
///
/// > Note: Ideally, unique identifiers will persist at all
/// > times. Although this specification recognizes that failure
/// > to persist can be unavoidable in certain server
/// > environments, it STRONGLY ENCOURAGES message store
/// > implementation techniques that avoid this problem. For
/// > example:
/// >
/// > 1. Unique identifiers are strictly ascending in the
/// > mailbox at all times. If the physical message store is
/// > re-ordered by a non-IMAP agent, this requires that the
/// > unique identifiers in the mailbox be regenerated, since
/// > the former unique identifiers are no longer strictly
/// > ascending as a result of the re-ordering.
/// > 2. If the message store has no mechanism to store unique
/// > identifiers, it must regenerate unique identifiers at
/// > each session, and each session must have a unique
/// > `UIDVALIDITY` value.
/// > 3. If the mailbox is deleted and a new mailbox with the
/// > same name is created at a later date, the server must
/// > either keep track of unique identifiers from the
/// > previous instance of the mailbox, or it must assign a
/// > new `UIDVALIDITY` value to the new instance of the
/// > mailbox. A good `UIDVALIDITY` value to use in this case
/// > is a 32-bit representation of the creation date/time of
/// > the mailbox. It is alright to use a constant such as
/// > 1, but only if it guaranteed that unique identifiers
/// > will never be reused, even in the case of a mailbox
/// > being deleted (or renamed) and a new mailbox by the
/// > same name created at some future time.
/// > 4. The combination of mailbox name, `UIDVALIDITY`, and `UID`
/// > must refer to a single immutable message on that server
/// > forever. In particular, the internal date, [RFC 2822](https://tools.ietf.org/html/rfc2822)
/// > size, envelope, body structure, and message texts
/// > (RFC822, RFC822.HEADER, RFC822.TEXT, and all BODY[...]
/// > fetch data items) must never change. This does not
/// > include message numbers, nor does it include attributes
/// > that can be set by a `STORE` command (e.g., `FLAGS`).
pub type Uid = u32;
/// From section [2.3.1.2 of RFC 3501](https://tools.ietf.org/html/rfc3501#section-2.3.1.2).
///
/// A relative position from 1 to the number of messages in the mailbox.
/// This position is ordered by ascending unique identifier. As
/// each new message is added, it is assigned a message sequence number
/// that is 1 higher than the number of messages in the mailbox before
/// that new message was added.
///
/// Message sequence numbers can be reassigned during the session. For
/// example, when a message is permanently removed (expunged) from the
/// mailbox, the message sequence number for all subsequent messages is
/// decremented. The number of messages in the mailbox is also
/// decremented. Similarly, a new message can be assigned a message
/// sequence number that was once held by some other message prior to an
/// expunge.
///
/// In addition to accessing messages by relative position in the
/// mailbox, message sequence numbers can be used in mathematical
/// calculations. For example, if an untagged "11 EXISTS" is received,
/// and previously an untagged "8 EXISTS" was received, three new
/// messages have arrived with message sequence numbers of 9, 10, and 11.
/// Another example, if message 287 in a 523 message mailbox has UID
/// 12345, there are exactly 286 messages which have lesser UIDs and 236
/// messages which have greater UIDs.
pub type Seq = u32;
/// With the exception of [`Flag::Custom`], these flags are system flags that are pre-defined in
/// [RFC 3501 section 2.3.2](https://tools.ietf.org/html/rfc3501#section-2.3.2). All system flags
/// begin with `\` in the IMAP protocol. Certain system flags (`\Deleted` and `\Seen`) have
/// special semantics described elsewhere.
///
/// A flag can be permanent or session-only on a per-flag basis. Permanent flags are those which
/// the client can add or remove from the message flags permanently; that is, concurrent and
/// subsequent sessions will see any change in permanent flags. Changes to session flags are valid
/// only in that session.
///
/// > Note: The `\Recent` system flag is a special case of a session flag. `\Recent` can not be
/// > used as an argument in a `STORE` or `APPEND` command, and thus can not be changed at all.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
#[non_exhaustive]
pub enum Flag<'a> {
/// Message has been read
Seen,
/// Message has been answered
Answered,
/// Message is "flagged" for urgent/special attention
Flagged,
/// Message is "deleted" for removal by later EXPUNGE
Deleted,
/// Message has not completed composition (marked as a draft).
Draft,
/// Message is "recently" arrived in this mailbox. This session is the first session to have
/// been notified about this message; if the session is read-write, subsequent sessions will
/// not see `\Recent` set for this message. This flag can not be altered by the client.
///
/// If it is not possible to determine whether or not this session is the first session to be
/// notified about a message, then that message will generally be considered recent.
///
/// If multiple connections have the same mailbox selected simultaneously, it is undefined
/// which of these connections will see newly-arrived messages with `\Recent` set and which
/// will see it without `\Recent` set.
Recent,
/// The [`Mailbox::permanent_flags`] can include this special flag (`\*`), which indicates that
/// it is possible to create new keywords by attempting to store those flags in the mailbox.
MayCreate,
/// A non-standard user- or server-defined flag.
Custom(Cow<'a, str>),
}
impl Flag<'static> {
fn system(s: &str) -> Option<Self> {
match s {
"\\Seen" => Some(Flag::Seen),
"\\Answered" => Some(Flag::Answered),
"\\Flagged" => Some(Flag::Flagged),
"\\Deleted" => Some(Flag::Deleted),
"\\Draft" => Some(Flag::Draft),
"\\Recent" => Some(Flag::Recent),
"\\*" => Some(Flag::MayCreate),
_ => None,
}
}
/// Helper function to transform Strings into owned Flags
pub fn from_strs<S: ToString>(
v: impl IntoIterator<Item = S>,
) -> impl Iterator<Item = Flag<'static>> {
v.into_iter().map(|s| Flag::from(s.to_string()))
}
}
impl<'a> fmt::Display for Flag<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Flag::Seen => write!(f, "\\Seen"),
Flag::Answered => write!(f, "\\Answered"),
Flag::Flagged => write!(f, "\\Flagged"),
Flag::Deleted => write!(f, "\\Deleted"),
Flag::Draft => write!(f, "\\Draft"),
Flag::Recent => write!(f, "\\Recent"),
Flag::MayCreate => write!(f, "\\*"),
Flag::Custom(ref s) => write!(f, "{}", s),
}
}
}
impl<'a> From<String> for Flag<'a> {
fn from(s: String) -> Self {
if let Some(f) = Flag::system(&s) {
f
} else {
Flag::Custom(Cow::Owned(s))
}
}
}
impl<'a> From<&'a str> for Flag<'a> {
fn from(s: &'a str) -> Self {
if let Some(f) = Flag::system(s) {
f
} else {
Flag::Custom(Cow::Borrowed(s))
}
}
}
mod mailbox;
pub use self::mailbox::Mailbox;
mod fetch;
pub use self::fetch::Fetch;
mod name;
pub use self::name::{Name, NameAttribute};
mod capabilities;
pub use self::capabilities::Capabilities;
mod deleted;
pub use self::deleted::Deleted;
mod unsolicited_response;
pub use self::unsolicited_response::{AttributeValue, UnsolicitedResponse};
/// This type wraps an input stream and a type that was constructed by parsing that input stream,
/// which allows the parsed type to refer to data in the underlying stream instead of copying it.
///
/// Any references given out by a `ZeroCopy` should never be used after the `ZeroCopy` is dropped.
pub struct ZeroCopy<D> {
_owned: Box<[u8]>,
derived: D,
}
impl<D> ZeroCopy<D> {
/// Derive a new `ZeroCopy` view of the byte data stored in `owned`.
///
/// # Safety
///
/// The `derive` callback will be passed a `&'static [u8]`. However, this reference is not, in
/// fact `'static`. Instead, it is only valid for as long as the `ZeroCopy` lives. Therefore,
/// it is *only* safe to call this function if *every* accessor on `D` returns either a type
/// that does not contain any borrows, *or* where the return type is bound to the lifetime of
/// `&self`.
///
/// It is *not* safe for the error type `E` to borrow from the passed reference.
pub(crate) unsafe fn make<F, E>(owned: Vec<u8>, derive: F) -> Result<Self, E>
where
F: FnOnce(&'static [u8]) -> Result<D, E>,
{
use std::mem;
// the memory pointed to by `owned` now has a stable address (on the heap).
// even if we move the `Box` (i.e., into `ZeroCopy`), a slice to it will remain valid.
let _owned = owned.into_boxed_slice();
// this is the unsafe part -- the implementor of `derive` must be aware that the reference
// they are passed is not *really* 'static, but rather the lifetime of `&self`.
let static_owned_ref: &'static [u8] = mem::transmute(&*_owned);
Ok(ZeroCopy {
_owned,
derived: derive(static_owned_ref)?,
})
}
/// Take out the derived value of this `ZeroCopy`.
///
/// Only safe if `D` contains no references into the underlying input stream (i.e., the `owned`
/// passed to `ZeroCopy::new`).
#[allow(dead_code)]
pub(crate) unsafe fn take(self) -> D {
self.derived
}
}
use super::error::Error;
pub(crate) type ZeroCopyResult<T> = Result<ZeroCopy<T>, Error>;
use std::ops::Deref;
impl<D> Deref for ZeroCopy<D> {
type Target = D;
fn deref(&self) -> &Self::Target {
&self.derived
}
}
// re-implement standard traits
// basically copied from Rc
impl<D: PartialEq> PartialEq for ZeroCopy<D> {
fn eq(&self, other: &ZeroCopy<D>) -> bool {
**self == **other
}
}
impl<D: Eq> Eq for ZeroCopy<D> {}
use std::cmp::Ordering;
impl<D: PartialOrd> PartialOrd for ZeroCopy<D> {
fn partial_cmp(&self, other: &ZeroCopy<D>) -> Option<Ordering> {
(**self).partial_cmp(&**other)
}
fn lt(&self, other: &ZeroCopy<D>) -> bool {
**self < **other
}
fn le(&self, other: &ZeroCopy<D>) -> bool {
**self <= **other
}
fn gt(&self, other: &ZeroCopy<D>) -> bool {
**self > **other
}
fn ge(&self, other: &ZeroCopy<D>) -> bool {
**self >= **other
}
}
impl<D: Ord> Ord for ZeroCopy<D> {
fn cmp(&self, other: &ZeroCopy<D>) -> Ordering {
(**self).cmp(&**other)
}
}
use std::hash::{Hash, Hasher};
impl<D: Hash> Hash for ZeroCopy<D> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state);
}
}
use std::fmt;
impl<D: fmt::Display> fmt::Display for ZeroCopy<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl<D: fmt::Debug> fmt::Debug for ZeroCopy<D> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<'a, D> IntoIterator for &'a ZeroCopy<D>
where
&'a D: IntoIterator,
{
type Item = <&'a D as IntoIterator>::Item;
type IntoIter = <&'a D as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
(**self).into_iter()
}
}