Files
inbuxa-server/crates/imap-proto/src/lib.rs
T
jcoffey-dev 7dae9b29fd Import upstream v0.16.22, stripped
Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f
Enterprise-only files removed or emptied: 63
Enterprise-only snippets removed: 117 in 50 files
Dangling module declarations removed: 5
Cargo edits turning enterprise off: 14
Verification: clean
Enterprise feature gates left for rebuilt features: 19 in 18 files

Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
2026-09-18 10:21:56 -07:00

277 lines
5.2 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use protocol::ObjectId;
use protocol::capability::Capability;
use std::borrow::Cow;
pub mod parser;
pub mod protocol;
pub mod receiver;
pub mod utf7;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Command {
// Client Commands - Any State
Capability,
#[default]
Noop,
Logout,
// Client Commands - Not Authenticated State
StartTls,
Authenticate,
Login,
// Client Commands - Authenticated State
Enable,
Select,
Examine,
Create,
Delete,
Rename,
Subscribe,
Unsubscribe,
List,
Namespace,
Status,
Append,
Idle,
// Client Commands - Selected State
Close,
Unselect,
Expunge(bool),
Search(bool),
Fetch(bool),
Store(bool),
Copy(bool),
Move(bool),
// IMAP4rev1
Lsub,
Check,
// RFC 5256
Sort(bool),
Thread(bool),
// RFC 4314
SetAcl,
DeleteAcl,
GetAcl,
ListRights,
MyRights,
// RFC 8437
Unauthenticate,
// RFC 2971
Id,
// RFC 9208
GetQuota,
GetQuotaRoot,
// RFC 9698
GetJmapAccess,
// RFC 10022
UidBatches,
}
impl Command {
pub fn is_uid(&self) -> bool {
matches!(
self,
Command::Fetch(true)
| Command::Search(true)
| Command::Copy(true)
| Command::Move(true)
| Command::Store(true)
| Command::Expunge(true)
| Command::Sort(true)
| Command::Thread(true)
)
}
pub fn requires_uid(&self) -> bool {
matches!(
self,
Command::Fetch(false)
| Command::Search(false)
| Command::Copy(false)
| Command::Move(false)
| Command::Store(false)
| Command::Sort(false)
| Command::Thread(false)
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResponseCode {
Alert,
AlreadyExists,
AppendUid {
uid_validity: u32,
uids: Vec<u32>,
},
AuthenticationFailed,
AuthorizationFailed,
BadCharset,
Cannot,
Capability {
capabilities: Vec<Capability>,
},
ClientBug,
Closed,
ContactAdmin,
CopyUid {
uid_validity: u32,
src_uids: Vec<u32>,
dest_uids: Vec<u32>,
},
Corruption,
Expired,
ExpungeIssued,
HasChildren,
InUse,
Limit,
NonExistent,
NoPerm,
OverQuota,
Parse,
PermanentFlags,
PrivacyRequired,
ReadOnly,
ReadWrite,
ServerBug,
TryCreate,
UidNext,
UidNotSticky,
UidValidity,
Unavailable,
UnknownCte,
// CONDSTORE
Modified {
ids: Vec<u32>,
},
HighestModseq {
modseq: u64,
},
// ObjectID
ObjectId(ObjectId),
// USEATTR
UseAttr,
// UIDONLY
UidRequired,
// UIDBATCHES
TooFew,
TooMany,
// MESSAGELIMIT
MessageLimit {
limit: u32,
uid: Option<u32>,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StatusResponse {
pub tag: Option<String>,
pub code: Option<ResponseCode>,
pub message: Cow<'static, str>,
pub rtype: ResponseType,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResponseType {
Ok,
No,
Bad,
PreAuth,
Bye,
}
impl ResponseCode {
pub fn highest_modseq(modseq: u64) -> Self {
ResponseCode::HighestModseq {
modseq: if modseq > 0 { modseq + 1 } else { 0 },
}
}
}
impl StatusResponse {
pub fn bad(message: impl Into<Cow<'static, str>>) -> Self {
StatusResponse {
tag: None,
code: None,
message: message.into(),
rtype: ResponseType::Bad,
}
}
pub fn parse_error(message: impl Into<Cow<'static, str>>) -> Self {
StatusResponse {
tag: None,
code: ResponseCode::Parse.into(),
message: message.into(),
rtype: ResponseType::Bad,
}
}
pub fn database_failure() -> Self {
StatusResponse::no("Database failure.").with_code(ResponseCode::ContactAdmin)
}
pub fn completed(command: Command) -> Self {
StatusResponse::ok(format!("{} completed", command))
}
pub fn with_code(mut self, code: ResponseCode) -> Self {
self.code = Some(code);
self
}
pub fn with_tag(mut self, tag: impl Into<String>) -> Self {
self.tag = Some(tag.into());
self
}
pub fn no(message: impl Into<Cow<'static, str>>) -> Self {
StatusResponse {
tag: None,
code: None,
message: message.into(),
rtype: ResponseType::No,
}
}
pub fn ok(message: impl Into<Cow<'static, str>>) -> Self {
StatusResponse {
tag: None,
code: None,
message: message.into(),
rtype: ResponseType::Ok,
}
}
pub fn bye(message: impl Into<Cow<'static, str>>) -> Self {
StatusResponse {
tag: None,
code: None,
message: message.into(),
rtype: ResponseType::Bye,
}
}
}