357 lines
14 KiB
Rust
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()
|
|
}
|
|
}
|