Partial rollback of CompactString after benchmarking (or 'premature optimization is the root of all evil')

This commit is contained in:
mdecimus 2025-04-16 11:15:43 +02:00
parent d6dc6ee8c5
commit c5596fb656
189 changed files with 1318 additions and 1332 deletions

View file

@ -4,10 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use std::borrow::Cow;
use compact_str::CompactString;
use directory::{Directory, backend::RcptType};
use std::borrow::Cow;
use utils::config::{Config, utils::AsKey};
use crate::{
@ -100,7 +98,7 @@ impl Server {
directory: &Directory,
address: &str,
session_id: u64,
) -> trc::Result<Vec<CompactString>> {
) -> trc::Result<Vec<String>> {
directory
.vrfy(
self.core
@ -120,7 +118,7 @@ impl Server {
directory: &Directory,
address: &str,
session_id: u64,
) -> trc::Result<Vec<CompactString>> {
) -> trc::Result<Vec<String>> {
directory
.expn(
self.core
@ -196,7 +194,7 @@ impl AddressMapping {
}
AddressMapping::Custom(if_block) => {
if let Some(result) = core
.eval_if::<CompactString, _>(if_block, &Address(address), session_id)
.eval_if::<String, _>(if_block, &Address(address), session_id)
.await
{
return result.into();
@ -220,9 +218,9 @@ impl AddressMapping {
.map(|(_, domain_part)| format!("@{}", domain_part))
.map(Cow::Owned),
AddressMapping::Custom(if_block) => core
.eval_if::<CompactString, _>(if_block, &Address(address), session_id)
.eval_if::<String, _>(if_block, &Address(address), session_id)
.await
.map(|s| Cow::Owned(s.into())),
.map(Cow::Owned),
AddressMapping::Disable => None,
}
}

View file

@ -6,7 +6,7 @@
use std::{net::IpAddr, sync::Arc};
use compact_str::CompactString;
use directory::{
Directory, Permission, Permissions, Principal, QueryBy, core::secret::verify_secret_hash,
};
@ -31,9 +31,9 @@ pub struct AccessToken {
pub primary_id: u32,
pub member_of: Vec<u32>,
pub access_to: VecMap<u32, Bitmap<Collection>>,
pub name: CompactString,
pub description: Option<CompactString>,
pub emails: Vec<CompactString>,
pub name: String,
pub description: Option<String>,
pub emails: Vec<String>,
pub quota: u64,
pub permissions: Permissions,
pub tenant: Option<TenantInfo>,

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use serde::{Deserialize, Serialize};
use trc::{AddContext, AuthEvent, EventType};
@ -17,19 +17,19 @@ pub struct OAuthIntrospect {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub scope: Option<CompactString>,
pub scope: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub client_id: Option<CompactString>,
pub client_id: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub username: Option<CompactString>,
pub username: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub token_type: Option<CompactString>,
pub token_type: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
@ -45,7 +45,7 @@ pub struct OAuthIntrospect {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub sub: Option<CompactString>,
pub sub: Option<String>,
}
impl Server {

View file

@ -7,7 +7,7 @@
use std::fmt;
use biscuit::{ClaimsSet, JWT, RegisteredClaims, SingleOrMultiple, jws::RegisteredHeader};
use compact_str::CompactString;
use serde::{
Deserialize, Deserializer, Serialize,
de::{self, Visitor},
@ -20,47 +20,47 @@ use crate::Server;
pub struct Userinfo {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub sub: Option<CompactString>,
pub sub: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<CompactString>,
pub name: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub given_name: Option<CompactString>,
pub given_name: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub family_name: Option<CompactString>,
pub family_name: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub middle_name: Option<CompactString>,
pub middle_name: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub nickname: Option<CompactString>,
pub nickname: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub preferred_username: Option<CompactString>,
pub preferred_username: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub profile: Option<CompactString>,
pub profile: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub picture: Option<CompactString>,
pub picture: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub website: Option<CompactString>,
pub website: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<CompactString>,
pub email: Option<String>,
#[serde(default, deserialize_with = "any_bool")]
#[serde(skip_serializing_if = "std::ops::Not::not")]
@ -68,11 +68,11 @@ pub struct Userinfo {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub zoneinfo: Option<CompactString>,
pub zoneinfo: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub locale: Option<CompactString>,
pub locale: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
@ -83,15 +83,15 @@ pub struct Userinfo {
pub struct StandardClaims {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub nonce: Option<CompactString>,
pub nonce: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub preferred_username: Option<CompactString>,
pub preferred_username: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub email: Option<CompactString>,
pub email: Option<String>,
}
impl Server {

View file

@ -4,22 +4,22 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(rename_all = "snake_case")]
pub struct ClientRegistrationRequest {
pub redirect_uris: Vec<CompactString>,
pub redirect_uris: Vec<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub response_types: Vec<CompactString>,
pub response_types: Vec<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub grant_types: Vec<CompactString>,
pub grant_types: Vec<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
@ -27,31 +27,31 @@ pub struct ClientRegistrationRequest {
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub contacts: Vec<CompactString>,
pub contacts: Vec<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub client_name: Option<CompactString>,
pub client_name: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub logo_uri: Option<CompactString>,
pub logo_uri: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub client_uri: Option<CompactString>,
pub client_uri: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub policy_uri: Option<CompactString>,
pub policy_uri: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub tos_uri: Option<CompactString>,
pub tos_uri: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub jwks_uri: Option<CompactString>,
pub jwks_uri: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
@ -59,7 +59,7 @@ pub struct ClientRegistrationRequest {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub sector_identifier_uri: Option<CompactString>,
pub sector_identifier_uri: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
@ -67,39 +67,39 @@ pub struct ClientRegistrationRequest {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub id_token_signed_response_alg: Option<CompactString>,
pub id_token_signed_response_alg: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub id_token_encrypted_response_alg: Option<CompactString>,
pub id_token_encrypted_response_alg: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub id_token_encrypted_response_enc: Option<CompactString>,
pub id_token_encrypted_response_enc: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub userinfo_signed_response_alg: Option<CompactString>,
pub userinfo_signed_response_alg: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub userinfo_encrypted_response_alg: Option<CompactString>,
pub userinfo_encrypted_response_alg: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub userinfo_encrypted_response_enc: Option<CompactString>,
pub userinfo_encrypted_response_enc: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub request_object_signing_alg: Option<CompactString>,
pub request_object_signing_alg: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub request_object_encryption_alg: Option<CompactString>,
pub request_object_encryption_alg: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub request_object_encryption_enc: Option<CompactString>,
pub request_object_encryption_enc: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
@ -107,7 +107,7 @@ pub struct ClientRegistrationRequest {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub token_endpoint_auth_signing_alg: Option<CompactString>,
pub token_endpoint_auth_signing_alg: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
@ -119,34 +119,34 @@ pub struct ClientRegistrationRequest {
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub default_acr_values: Vec<CompactString>,
pub default_acr_values: Vec<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub initiate_login_uri: Option<CompactString>,
pub initiate_login_uri: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub request_uris: Vec<CompactString>,
pub request_uris: Vec<String>,
#[serde(flatten)]
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub additional_fields: HashMap<CompactString, serde_json::Value>,
pub additional_fields: HashMap<String, serde_json::Value>,
}
#[derive(Serialize, Deserialize, Debug, Default)]
#[serde(rename_all = "snake_case")]
pub struct ClientRegistrationResponse {
// Required fields
pub client_id: CompactString,
pub client_id: String,
// Optional fields specific to the response
#[serde(skip_serializing_if = "Option::is_none")]
pub client_secret: Option<CompactString>,
pub client_secret: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub registration_access_token: Option<CompactString>,
pub registration_access_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub registration_client_uri: Option<CompactString>,
pub registration_client_uri: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub client_id_issued_at: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]

View file

@ -6,7 +6,7 @@
use std::time::SystemTime;
use compact_str::CompactString;
use directory::QueryBy;
use mail_builder::encoders::base64::base64_encode;
use mail_parser::decoders::base64::base64_decode;
@ -24,7 +24,7 @@ use super::{CLIENT_ID_MAX_LEN, GrantType, RANDOM_CODE_LEN, crypto::SymmetricEncr
pub struct TokenInfo {
pub grant_type: GrantType,
pub account_id: u32,
pub client_id: CompactString,
pub client_id: String,
pub expiry: u64,
pub issued_at: u64,
pub expires_in: u64,
@ -128,7 +128,7 @@ impl Server {
GrantType::from_id(bytes.next().copied()?)?,
bytes.next_leb128::<u64>()?,
bytes.next_leb128::<u64>()?,
bytes.copied().map(char::from).collect::<CompactString>(),
bytes.copied().map(char::from).collect::<String>(),
)
.into()
})
@ -216,7 +216,7 @@ impl Server {
})
}
pub async fn password_hash(&self, account_id: u32) -> trc::Result<CompactString> {
pub async fn password_hash(&self, account_id: u32) -> trc::Result<String> {
if account_id != u32::MAX {
self.core
.storage

View file

@ -8,7 +8,7 @@ use std::time::Duration;
use crate::expr::{if_block::IfBlock, tokenizer::TokenMap};
use ahash::AHashSet;
use compact_str::CompactString;
use utils::config::{Config, Rate};
use super::*;
@ -17,8 +17,8 @@ use super::*;
pub struct Network {
pub node_id: u64,
pub roles: ClusterRoles,
pub server_name: CompactString,
pub report_domain: CompactString,
pub server_name: String,
pub report_domain: String,
pub security: Security,
pub contact_form: Option<ContactForm>,
pub http_response_url: IfBlock,
@ -28,14 +28,14 @@ pub struct Network {
#[derive(Clone)]
pub struct ContactForm {
pub rcpt_to: Vec<CompactString>,
pub rcpt_to: Vec<String>,
pub max_size: usize,
pub rate: Option<Rate>,
pub validate_domain: bool,
pub from_email: FieldOrDefault,
pub from_subject: FieldOrDefault,
pub from_name: FieldOrDefault,
pub field_honey_pot: Option<CompactString>,
pub field_honey_pot: Option<String>,
}
#[derive(Clone)]
@ -129,7 +129,7 @@ impl ContactForm {
.values("form.deliver-to")
.filter_map(|(_, addr)| {
if addr.contains('@') && addr.contains('.') {
Some(CompactString::from_str_to_lowercase(addr.trim()))
Some(addr.trim().to_lowercase())
} else {
None
}
@ -200,8 +200,8 @@ impl Network {
let mut network = Network {
node_id: config.property("cluster.node-id").unwrap_or(1),
report_domain: report_domain.into(),
server_name: server_name.into(),
report_domain,
server_name,
security: Security::parse(config),
contact_form: ContactForm::parse(config),
asn_geo_lookup: AsnGeoLookupConfig::parse(config).unwrap_or_default(),

View file

@ -12,7 +12,7 @@ use std::{
use ahash::AHashSet;
use base64::{Engine, engine::general_purpose::STANDARD};
use compact_str::CompactString;
use hyper::{
HeaderMap,
header::{AUTHORIZATION, CONTENT_TYPE, HeaderName, HeaderValue},
@ -143,9 +143,9 @@ pub struct Data {
#[derive(Clone)]
pub struct Milter {
pub enable: IfBlock,
pub id: Arc<CompactString>,
pub id: Arc<String>,
pub addrs: Vec<SocketAddr>,
pub hostname: CompactString,
pub hostname: String,
pub port: u16,
pub timeout_connect: Duration,
pub timeout_command: Duration,
@ -473,7 +473,7 @@ fn parse_milter(config: &mut Config, id: &str, token_map: &TokenMap) -> Option<M
})
.ok()?
.collect(),
hostname: hostname.into(),
hostname,
port,
timeout_connect: config
.property_or_default(("session.milter", id, "timeout.connect"), "30s")

View file

@ -17,7 +17,7 @@ pub mod undelete;
use std::{sync::Arc, time::Duration};
use ahash::{AHashMap, AHashSet};
use compact_str::CompactString;
use directory::{QueryBy, Type, backend::internal::lookup::DirectoryStore};
use license::LicenseKey;
use llm::AiApiConfig;
@ -233,7 +233,7 @@ impl Server {
}
}
fn default_logo_url(&self) -> Option<CompactString> {
fn default_logo_url(&self) -> Option<String> {
self.core
.enterprise
.as_ref()

View file

@ -701,6 +701,21 @@ impl<'x> TryFrom<Variable<'x>> for CompactString {
}
}
impl<'x> TryFrom<Variable<'x>> for String {
type Error = ();
fn try_from(value: Variable<'x>) -> Result<Self, Self::Error> {
if let Variable::String(s) = value {
Ok(match s {
StringCow::Borrowed(v) => v.to_string(),
StringCow::Owned(v) => v.into_string(),
})
} else {
Err(())
}
}
}
impl<'x> From<Variable<'x>> for bool {
fn from(val: Variable<'x>) -> Self {
val.to_bool()

View file

@ -23,14 +23,14 @@ pub struct IfThen {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IfBlock {
pub key: CompactString,
pub key: String,
pub if_then: Vec<IfThen>,
pub default: Expression,
}
impl IfBlock {
pub fn new<T: ConstantValue>(
key: impl Into<CompactString>,
key: impl Into<String>,
if_thens: impl IntoIterator<Item = (&'static str, &'static str)>,
default: impl AsRef<str>,
) -> Self {
@ -51,7 +51,7 @@ impl IfBlock {
}
}
pub fn empty(key: impl Into<CompactString>) -> Self {
pub fn empty(key: impl Into<String>) -> Self {
Self {
key: key.into(),
if_then: Default::default(),
@ -102,7 +102,7 @@ impl IfBlock {
// Parse conditions
let mut if_block = IfBlock {
key: key.into(),
key,
if_then: Default::default(),
default: Expression {
items: Default::default(),
@ -214,7 +214,7 @@ impl IfBlock {
}
}
pub fn into_default(self, key: impl Into<CompactString>) -> IfBlock {
pub fn into_default(self, key: impl Into<String>) -> IfBlock {
IfBlock {
key: key.into(),
if_then: Default::default(),

View file

@ -7,7 +7,6 @@
use std::{sync::Arc, time::Instant};
use ahash::RandomState;
use compact_str::CompactString;
use jmap_proto::types::{state::StateChange, type_state::DataType};
use mail_auth::{
dmarc::Dmarc,
@ -70,8 +69,8 @@ pub enum StateEvent {
pub enum UpdateSubscription {
Unverified {
id: u32,
url: CompactString,
code: CompactString,
url: String,
code: String,
keys: Option<EncryptionKeys>,
},
Verified(PushSubscription),
@ -80,7 +79,7 @@ pub enum UpdateSubscription {
#[derive(Debug)]
pub struct PushSubscription {
pub id: u32,
pub url: CompactString,
pub url: String,
pub expires: u64,
pub types: Bitmap<DataType>,
pub keys: Option<EncryptionKeys>,

View file

@ -19,7 +19,6 @@ use std::{
use ahash::{AHashMap, AHashSet};
use arc_swap::ArcSwap;
use auth::{AccessToken, oauth::config::OAuthConfig, roles::RolePermissions};
use compact_str::CompactString;
use config::{
dav::DavConfig,
imap::ImapConfig,
@ -180,7 +179,7 @@ pub struct MessageStoreCache {
pub change_id: u64,
pub items: Vec<MessageCache>,
pub index: AHashMap<u32, u32>,
pub keywords: Vec<CompactString>,
pub keywords: Vec<String>,
pub update_lock: Arc<Semaphore>,
pub size: u64,
}
@ -203,8 +202,8 @@ pub struct MessageUidCache {
#[derive(Debug, Clone)]
pub struct MailboxCache {
pub document_id: u32,
pub name: CompactString,
pub path: CompactString,
pub name: String,
pub path: String,
pub role: SpecialUse,
pub parent_id: u32,
pub sort_order: u32,

View file

@ -1,6 +1,7 @@
// Adapted from rustls-acme (https://github.com/FlorianUekermann/rustls-acme), licensed under MIT/Apache-2.0.
use chrono::{DateTime, TimeZone, Utc};
use compact_str::CompactString;
use dns_update::DnsRecord;
use futures::future::try_join_all;

View file

@ -6,7 +6,7 @@
use std::sync::Arc;
use compact_str::CompactString;
use sieve::{Envelope, runtime::Variable};
use store::Value;
use unicode_security::mixed_script::AugmentedScriptSet;
@ -22,7 +22,7 @@ pub mod plugins;
pub enum ScriptModification {
SetEnvelope {
name: Envelope,
value: CompactString,
value: String,
},
AddHeader {
name: Arc<String>,

View file

@ -101,7 +101,7 @@ impl Server {
.unwrap_or_default()
{
acl_obj.append(
Property::_T(name.into()),
Property::_T(name),
item.grants
.map(|acl_item| Value::Text(acl_item.to_string()))
.collect::<Vec<_>>(),

View file

@ -493,7 +493,7 @@ impl DavAclHandler for Server {
.get_principal_name(grant_account_id)
.await
.caused_by(trc::location!())?
.unwrap_or_else(|| format_compact!("_{grant_account_id}"));
.unwrap_or_else(|| format!("_{grant_account_id}"));
aces.push(Ace::new(
Principal::Href(Href(format!(

View file

@ -294,7 +294,7 @@ impl PrincipalPropFind for Server {
.get_principal_name(account_id)
.await
.caused_by(trc::location!())?
.unwrap_or_else(|| format_compact!("_{account_id}"));
.unwrap_or_else(|| format!("_{account_id}"));
Ok(Href(format!(
"{}/{}",
DavResource::Principal.base_path(),

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use mail_send::Credentials;
use smtp_proto::{AUTH_CRAM_MD5, AUTH_LOGIN, AUTH_OAUTHBEARER, AUTH_PLAIN, AUTH_XOAUTH2};
@ -69,11 +69,11 @@ impl ImapDirectory {
Err(trc::StoreEvent::NotSupported.caused_by(trc::location!()))
}
pub async fn vrfy(&self, _address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn vrfy(&self, _address: &str) -> trc::Result<Vec<String>> {
Err(trc::StoreEvent::NotSupported.caused_by(trc::location!()))
}
pub async fn expn(&self, _address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn expn(&self, _address: &str) -> trc::Result<Vec<String>> {
Err(trc::StoreEvent::NotSupported.caused_by(trc::location!()))
}

View file

@ -6,7 +6,7 @@
use super::{PrincipalInfo, manage::ManageDirectory};
use crate::{Principal, PrincipalData, QueryBy, Type, backend::RcptType};
use compact_str::CompactString;
use mail_send::Credentials;
use store::{
Deserialize, IterateParams, Store, ValueKey,
@ -24,9 +24,9 @@ pub trait DirectoryStore: Sync + Send {
async fn email_to_id(&self, address: &str) -> trc::Result<Option<u32>>;
async fn is_local_domain(&self, domain: &str) -> trc::Result<bool>;
async fn rcpt(&self, address: &str) -> trc::Result<RcptType>;
async fn vrfy(&self, address: &str) -> trc::Result<Vec<CompactString>>;
async fn expn(&self, address: &str) -> trc::Result<Vec<CompactString>>;
async fn expn_by_id(&self, id: u32) -> trc::Result<Vec<CompactString>>;
async fn vrfy(&self, address: &str) -> trc::Result<Vec<String>>;
async fn expn(&self, address: &str) -> trc::Result<Vec<String>>;
async fn expn_by_id(&self, id: u32) -> trc::Result<Vec<String>>;
}
impl DirectoryStore for Store {
@ -123,7 +123,7 @@ impl DirectoryStore for Store {
}
}
async fn vrfy(&self, address: &str) -> trc::Result<Vec<CompactString>> {
async fn vrfy(&self, address: &str) -> trc::Result<Vec<String>> {
let mut results = Vec::new();
let address = address.split('@').next().unwrap_or(address);
if address.len() > 3 {
@ -155,7 +155,7 @@ impl DirectoryStore for Store {
Ok(results)
}
async fn expn(&self, address: &str) -> trc::Result<Vec<CompactString>> {
async fn expn(&self, address: &str) -> trc::Result<Vec<String>> {
if let Some(ptype) = self
.get_value::<PrincipalInfo>(ValueKey::from(ValueClass::Directory(
DirectoryClass::EmailToId(address.as_bytes().to_vec()),
@ -169,7 +169,7 @@ impl DirectoryStore for Store {
}
}
async fn expn_by_id(&self, list_id: u32) -> trc::Result<Vec<CompactString>> {
async fn expn_by_id(&self, list_id: u32) -> trc::Result<Vec<String>> {
let mut results = Vec::new();
for account_id in self.get_members(list_id).await? {
if let Some(email) = self

View file

@ -13,6 +13,7 @@ use crate::{
QueryBy, ROLE_ADMIN, ROLE_TENANT_ADMIN, ROLE_USER, Type, backend::RcptType,
};
use ahash::{AHashMap, AHashSet};
use compact_str::CompactString;
use jmap_proto::types::collection::Collection;
use store::{
@ -61,7 +62,7 @@ pub trait ManageDirectory: Sized {
async fn get_principal_info(&self, name: &str) -> trc::Result<Option<PrincipalInfo>>;
async fn get_or_create_principal_id(&self, name: &str, typ: Type) -> trc::Result<u32>;
async fn get_principal(&self, principal_id: u32) -> trc::Result<Option<Principal>>;
async fn get_principal_name(&self, principal_id: u32) -> trc::Result<Option<CompactString>>;
async fn get_principal_name(&self, principal_id: u32) -> trc::Result<Option<String>>;
async fn get_member_of(&self, principal_id: u32) -> trc::Result<Vec<MemberOf>>;
async fn get_members(&self, principal_id: u32) -> trc::Result<Vec<u32>>;
async fn create_principal(
@ -125,7 +126,7 @@ impl ManageDirectory for Store {
}
}
async fn get_principal_name(&self, principal_id: u32) -> trc::Result<Option<CompactString>> {
async fn get_principal_name(&self, principal_id: u32) -> trc::Result<Option<String>> {
let archive = self
.get_value::<Archive<AlignedBytes>>(ValueKey::from(ValueClass::Directory(
DirectoryClass::Principal(principal_id),
@ -248,11 +249,11 @@ impl ManageDirectory for Store {
allowed_permissions: Option<&Permissions>,
) -> trc::Result<CreatedPrincipal> {
// Make sure the principal has a name
let name = CompactString::from_str_to_lowercase(principal_set.name());
let name = principal_set.name().to_lowercase();
if name.is_empty() {
return Err(err_missing(PrincipalField::Name));
}
let mut valid_domains: AHashSet<CompactString> = AHashSet::new();
let mut valid_domains: AHashSet<String> = AHashSet::new();
// SPDX-SnippetBegin
// SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
@ -1271,7 +1272,7 @@ impl ManageDirectory for Store {
PrincipalValue::String(email),
) => {
let email = email.to_lowercase();
if let Some(idx) = principal.emails.iter().position(|v| v == email) {
if let Some(idx) = principal.emails.iter().position(|v| v == &email) {
principal.emails.remove(idx);
batch.clear(ValueClass::Directory(DirectoryClass::EmailToId(
email.as_bytes().to_vec(),
@ -1899,7 +1900,7 @@ impl ManageDirectory for Store {
{
let mut principal = Principal::new(pt.id, pt.typ);
principal.name =
CompactString::from_utf8_lossy(key.get(1..).unwrap_or_default());
String::from_utf8_lossy(key.get(1..).unwrap_or_default()).into_owned();
results.push(principal);
}

View file

@ -9,7 +9,7 @@ pub mod manage;
use crate::Type;
use ahash::AHashMap;
use compact_str::CompactString;
use std::fmt::Display;
use store::{Deserialize, SerializeInfallible, U32_LEN, write::key::KeySerializer};
use utils::codec::leb128::Leb128Iterator;
@ -140,8 +140,8 @@ pub enum PrincipalAction {
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize)]
#[serde(untagged)]
pub enum PrincipalValue {
String(CompactString),
StringList(Vec<CompactString>),
String(String),
StringList(Vec<String>),
Integer(u64),
IntegerList(Vec<u64>),
}

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use ldap3::{Ldap, LdapConnAsync, ResultEntry, Scope, SearchEntry};
use mail_send::Credentials;
use store::xxhash_rust;
@ -298,11 +297,11 @@ impl LdapDirectory {
}
}
pub async fn vrfy(&self, address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn vrfy(&self, address: &str) -> trc::Result<Vec<String>> {
self.data_store.vrfy(address).await
}
pub async fn expn(&self, address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn expn(&self, address: &str) -> trc::Result<Vec<String>> {
self.data_store.expn(address).await
}
@ -351,44 +350,39 @@ impl LdapMappings {
for (attr, value) in entry.attrs {
if self.attr_name.contains(&attr) {
if !self.attr_email_address.contains(&attr) {
principal.name = value.into_iter().next().unwrap_or_default().into();
principal.name = value.into_iter().next().unwrap_or_default();
} else {
for (idx, item) in value.into_iter().enumerate() {
principal
.emails
.insert(0, CompactString::from_str_to_lowercase(&item));
principal.emails.insert(0, item.to_lowercase());
if idx == 0 {
principal.name = item.into();
principal.name = item;
}
}
}
} else if self.attr_secret.contains(&attr) {
for item in value {
principal.secrets.push(item.into());
principal.secrets.push(item);
}
} else if self.attr_secret_changed.contains(&attr) {
// Create a disabled AppPassword, used to indicate that the password has been changed
// but cannot be used for authentication.
for item in value {
principal.secrets.push(
format!("$app${}$", xxhash_rust::xxh3::xxh3_64(item.as_bytes())).into(),
);
principal.secrets.push(format!(
"$app${}$",
xxhash_rust::xxh3::xxh3_64(item.as_bytes())
));
}
} else if self.attr_email_address.contains(&attr) {
for item in value {
principal
.emails
.insert(0, CompactString::from_str_to_lowercase(&item));
principal.emails.insert(0, item.to_lowercase());
}
} else if self.attr_email_alias.contains(&attr) {
for item in value {
principal
.emails
.push(CompactString::from_str_to_lowercase(&item));
principal.emails.push(item.to_lowercase());
}
} else if let Some(idx) = self.attr_description.iter().position(|a| a == &attr) {
if principal.description.is_none() || idx == 0 {
principal.description = value.into_iter().next().map(Into::into);
principal.description = value.into_iter().next();
}
} else if self.attr_groups.contains(&attr) {
member_of.extend(value);

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use store::Store;
use utils::config::{Config, utils::AsKey};
@ -119,7 +119,7 @@ impl MemoryDirectory {
principal
.emails
.push(CompactString::from_str_to_lowercase(email));
.push(email.to_lowercase());
}
// Parse mailing lists

View file

@ -6,7 +6,7 @@
use super::{EmailType, MemoryDirectory};
use crate::{Principal, QueryBy, backend::RcptType};
use compact_str::CompactString;
use mail_send::Credentials;
impl MemoryDirectory {
@ -62,7 +62,7 @@ impl MemoryDirectory {
Ok(self.emails_to_ids.contains_key(address).into())
}
pub async fn vrfy(&self, address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn vrfy(&self, address: &str) -> trc::Result<Vec<String>> {
let mut result = Vec::new();
for (key, value) in &self.emails_to_ids {
if key.contains(address) && value.iter().any(|t| matches!(t, EmailType::Primary(_))) {
@ -72,7 +72,7 @@ impl MemoryDirectory {
Ok(result)
}
pub async fn expn(&self, address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn expn(&self, address: &str) -> trc::Result<Vec<String>> {
let mut result = Vec::new();
for (key, value) in &self.emails_to_ids {
if key == address {

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
pub mod imap;
pub mod internal;
@ -17,7 +17,7 @@ pub mod sql;
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
pub enum RcptType {
Mailbox,
List(Vec<CompactString>),
List(Vec<String>),
#[default]
Invalid,
}

View file

@ -5,7 +5,7 @@
*/
use ahash::HashMap;
use compact_str::CompactString;
use mail_send::Credentials;
use reqwest::{StatusCode, header::AUTHORIZATION};
use trc::{AddContext, AuthEvent};
@ -145,11 +145,11 @@ impl OpenIdDirectory {
self.data_store.rcpt(address).await
}
pub async fn vrfy(&self, address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn vrfy(&self, address: &str) -> trc::Result<Vec<String>> {
self.data_store.vrfy(address).await
}
pub async fn expn(&self, address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn expn(&self, address: &str) -> trc::Result<Vec<String>> {
self.data_store.expn(address).await
}
@ -188,10 +188,10 @@ impl BuildPrincipal for OpenIdResponse {
Ok(Principal {
id: u32::MAX,
typ: Type::Individual,
name: username.into(),
description: full_name.map(Into::into),
name: username,
description: full_name,
secrets: Default::default(),
emails: vec![email.into()],
emails: vec![email],
quota: Default::default(),
tenant: Default::default(),
data: vec![PrincipalData::Roles(vec![ROLE_USER])],

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use mail_send::{Credentials, smtp::AssertReply};
use smtp_proto::Severity;
@ -67,7 +67,7 @@ impl SmtpDirectory {
}
}
pub async fn vrfy(&self, address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn vrfy(&self, address: &str) -> trc::Result<Vec<String>> {
self.pool
.get()
.await
@ -76,7 +76,7 @@ impl SmtpDirectory {
.await
}
pub async fn expn(&self, address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn expn(&self, address: &str) -> trc::Result<Vec<String>> {
self.pool
.get()
.await
@ -111,7 +111,7 @@ impl SmtpClient {
}
}
async fn expand(&mut self, command: &str) -> trc::Result<Vec<CompactString>> {
async fn expand(&mut self, command: &str) -> trc::Result<Vec<String>> {
let reply = self
.client
.cmd(command.as_bytes())
@ -122,7 +122,7 @@ impl SmtpClient {
.message()
.split('\n')
.map(|p| p.into())
.collect::<Vec<CompactString>>()),
.collect::<Vec<String>>()),
code @ (550 | 551 | 553 | 500 | 502) => {
Err(trc::StoreEvent::NotSupported.ctx(trc::Key::Code, code))
}

View file

@ -15,7 +15,7 @@ use crate::{
},
},
};
use compact_str::CompactString;
use mail_send::Credentials;
use store::{NamedRows, Rows, Value};
use trc::AddContext;
@ -250,11 +250,11 @@ impl SqlDirectory {
}
}
pub async fn vrfy(&self, address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn vrfy(&self, address: &str) -> trc::Result<Vec<String>> {
self.data_store.vrfy(address).await
}
pub async fn expn(&self, address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn expn(&self, address: &str) -> trc::Result<Vec<String>> {
self.data_store.expn(address).await
}
@ -298,7 +298,7 @@ impl SqlMappings {
if let Value::Text(text) = value {
principal
.emails
.push(CompactString::from_str_to_lowercase(text.as_ref()));
.push(text.to_lowercase());
}
} else if name.eq_ignore_ascii_case(&self.column_quota) {
if let Value::Integer(quota) = value {

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use trc::AddContext;
use crate::{
@ -97,7 +97,7 @@ impl Directory {
Ok(result)
}
pub async fn vrfy(&self, address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn vrfy(&self, address: &str) -> trc::Result<Vec<String>> {
match &self.store {
DirectoryInner::Internal(store) => store.vrfy(address).await,
DirectoryInner::Ldap(store) => store.vrfy(address).await,
@ -110,7 +110,7 @@ impl Directory {
.caused_by(trc::location!())
}
pub async fn expn(&self, address: &str) -> trc::Result<Vec<CompactString>> {
pub async fn expn(&self, address: &str) -> trc::Result<Vec<String>> {
match &self.store {
DirectoryInner::Internal(store) => store.expn(address).await,
DirectoryInner::Ldap(store) => store.expn(address).await,

View file

@ -6,7 +6,6 @@
use std::{collections::hash_map::Entry, fmt, str::FromStr};
use compact_str::CompactString;
use serde::{
Deserializer, Serializer,
de::{self, IgnoredAny, Visitor},
@ -76,7 +75,7 @@ impl Principal {
// SPDX-SnippetEnd
pub fn description(&self) -> Option<&str> {
self.description.as_ref().map(|d| d.as_str())
self.description.as_deref()
}
pub fn member_of(&self) -> &[u32] {
@ -128,7 +127,7 @@ impl Principal {
.unwrap_or_default()
}
pub fn urls(&self) -> &[CompactString] {
pub fn urls(&self) -> &[String] {
self.data
.iter()
.find_map(|item| {
@ -164,7 +163,7 @@ impl Principal {
.unwrap_or_default()
}
pub fn picture(&self) -> Option<&CompactString> {
pub fn picture(&self) -> Option<&String> {
self.data.iter().find_map(|item| {
if let PrincipalData::Picture(picture) = item {
picture.into()
@ -174,7 +173,7 @@ impl Principal {
})
}
pub fn picture_mut(&mut self) -> Option<&mut CompactString> {
pub fn picture_mut(&mut self) -> Option<&mut String> {
self.data.iter_mut().find_map(|item| {
if let PrincipalData::Picture(picture) = item {
picture.into()
@ -299,7 +298,7 @@ impl Principal {
updates
}
pub fn fallback_admin(fallback_pass: impl Into<CompactString>) -> Self {
pub fn fallback_admin(fallback_pass: impl Into<String>) -> Self {
Principal {
id: u32::MAX,
typ: Type::Individual,
@ -363,7 +362,7 @@ impl PrincipalSet {
self.fields.get(&key).and_then(|v| v.as_int())
}
pub fn get_str_array(&self, key: PrincipalField) -> Option<&[CompactString]> {
pub fn get_str_array(&self, key: PrincipalField) -> Option<&[String]> {
self.fields.get(&key).and_then(|v| match v {
PrincipalValue::StringList(v) => Some(v.as_slice()),
PrincipalValue::String(v) => Some(std::slice::from_ref(v)),
@ -383,12 +382,12 @@ impl PrincipalSet {
self.fields.remove(&key)
}
pub fn take_str(&mut self, key: PrincipalField) -> Option<CompactString> {
pub fn take_str(&mut self, key: PrincipalField) -> Option<String> {
self.take(key).and_then(|v| match v {
PrincipalValue::String(s) => Some(s),
PrincipalValue::StringList(l) => l.into_iter().next(),
PrincipalValue::Integer(i) => Some(i.to_string().into()),
PrincipalValue::IntegerList(l) => l.into_iter().next().map(|i| i.to_string().into()),
PrincipalValue::Integer(i) => Some(i.to_string()),
PrincipalValue::IntegerList(l) => l.into_iter().next().map(|i| i.to_string()),
})
}
@ -401,7 +400,7 @@ impl PrincipalSet {
})
}
pub fn take_str_array(&mut self, key: PrincipalField) -> Option<Vec<CompactString>> {
pub fn take_str_array(&mut self, key: PrincipalField) -> Option<Vec<String>> {
self.take(key).map(|v| v.into_str_array())
}
@ -412,7 +411,7 @@ impl PrincipalSet {
pub fn iter_str(
&self,
key: PrincipalField,
) -> Box<dyn Iterator<Item = &CompactString> + Sync + Send + '_> {
) -> Box<dyn Iterator<Item = &String> + Sync + Send + '_> {
self.fields
.get(&key)
.map(|v| v.iter_str())
@ -422,7 +421,7 @@ impl PrincipalSet {
pub fn iter_mut_str(
&mut self,
key: PrincipalField,
) -> Box<dyn Iterator<Item = &mut CompactString> + Sync + Send + '_> {
) -> Box<dyn Iterator<Item = &mut String> + Sync + Send + '_> {
self.fields
.get_mut(&key)
.map(|v| v.iter_mut_str())
@ -488,11 +487,7 @@ impl PrincipalSet {
self
}
pub fn append_str(
&mut self,
key: PrincipalField,
value: impl Into<CompactString>,
) -> &mut Self {
pub fn append_str(&mut self, key: PrincipalField, value: impl Into<String>) -> &mut Self {
let value = value.into();
match self.fields.entry(key) {
Entry::Occupied(v) => {
@ -510,12 +505,12 @@ impl PrincipalSet {
}
}
PrincipalValue::Integer(i) => {
*v = PrincipalValue::StringList(vec![i.to_string().into(), value]);
*v = PrincipalValue::StringList(vec![i.to_string(), value]);
}
PrincipalValue::IntegerList(l) => {
*v = PrincipalValue::StringList(
l.iter()
.map(|i| i.to_string().into())
.map(|i| i.to_string())
.chain(std::iter::once(value))
.collect(),
);
@ -529,11 +524,7 @@ impl PrincipalSet {
self
}
pub fn prepend_str(
&mut self,
key: PrincipalField,
value: impl Into<CompactString>,
) -> &mut Self {
pub fn prepend_str(&mut self, key: PrincipalField, value: impl Into<String>) -> &mut Self {
let value = value.into();
match self.fields.entry(key) {
Entry::Occupied(v) => {
@ -551,12 +542,12 @@ impl PrincipalSet {
}
}
PrincipalValue::Integer(i) => {
*v = PrincipalValue::StringList(vec![value, i.to_string().into()]);
*v = PrincipalValue::StringList(vec![value, i.to_string()]);
}
PrincipalValue::IntegerList(l) => {
*v = PrincipalValue::StringList(
std::iter::once(value)
.chain(l.iter().map(|i| i.to_string().into()))
.chain(l.iter().map(|i| i.to_string()))
.collect(),
);
}
@ -629,7 +620,7 @@ impl PrincipalSet {
pub fn retain_str<F>(&mut self, key: PrincipalField, mut f: F)
where
F: FnMut(&CompactString) -> bool,
F: FnMut(&String) -> bool,
{
if let Some(value) = self.fields.get_mut(&key) {
match value {
@ -689,7 +680,7 @@ impl PrincipalValue {
}
}
pub fn iter_str(&self) -> Box<dyn Iterator<Item = &CompactString> + Sync + Send + '_> {
pub fn iter_str(&self) -> Box<dyn Iterator<Item = &String> + Sync + Send + '_> {
match self {
PrincipalValue::String(v) => Box::new(std::iter::once(v)),
PrincipalValue::StringList(v) => Box::new(v.iter()),
@ -697,9 +688,7 @@ impl PrincipalValue {
}
}
pub fn iter_mut_str(
&mut self,
) -> Box<dyn Iterator<Item = &mut CompactString> + Sync + Send + '_> {
pub fn iter_mut_str(&mut self) -> Box<dyn Iterator<Item = &mut String> + Sync + Send + '_> {
match self {
PrincipalValue::String(v) => Box::new(std::iter::once(v)),
PrincipalValue::StringList(v) => Box::new(v.iter_mut()),
@ -731,12 +720,12 @@ impl PrincipalValue {
}
}
pub fn into_str_array(self) -> Vec<CompactString> {
pub fn into_str_array(self) -> Vec<String> {
match self {
PrincipalValue::StringList(v) => v,
PrincipalValue::String(v) => vec![v],
PrincipalValue::Integer(v) => vec![v.to_string().into()],
PrincipalValue::IntegerList(v) => v.into_iter().map(|v| v.to_string().into()).collect(),
PrincipalValue::Integer(v) => vec![v.to_string()],
PrincipalValue::IntegerList(v) => v.into_iter().map(|v| v.to_string()).collect(),
}
}
@ -776,8 +765,8 @@ impl From<u64> for PrincipalValue {
}
}
impl From<CompactString> for PrincipalValue {
fn from(v: CompactString) -> Self {
impl From<String> for PrincipalValue {
fn from(v: String) -> Self {
Self::String(v)
}
}
@ -788,8 +777,8 @@ impl From<&str> for PrincipalValue {
}
}
impl From<Vec<CompactString>> for PrincipalValue {
fn from(v: Vec<CompactString>) -> Self {
impl From<Vec<String>> for PrincipalValue {
fn from(v: Vec<String>) -> Self {
Self::StringList(v)
}
}
@ -956,7 +945,7 @@ impl<'de> serde::Deserialize<'de> for PrincipalValue {
E: de::Error,
{
if value.len() <= MAX_STRING_LEN {
Ok(PrincipalValue::String(value.into()))
Ok(PrincipalValue::String(value))
} else {
Err(serde::de::Error::custom("string too long"))
}
@ -984,7 +973,7 @@ impl<'de> serde::Deserialize<'de> for PrincipalValue {
match value {
StringOrU64::String(s) => {
if s.len() <= MAX_STRING_LEN {
vec_string.push(s.into());
vec_string.push(s);
} else {
return Err(serde::de::Error::custom("string too long"));
}
@ -1042,19 +1031,19 @@ impl<'de> serde::Deserialize<'de> for PrincipalSet {
})?;
let value = match key {
PrincipalField::Name => PrincipalValue::String(
map.next_value::<CompactString>().and_then(|v| {
PrincipalField::Name => {
PrincipalValue::String(map.next_value::<String>().and_then(|v| {
if v.len() <= MAX_STRING_LEN {
Ok(v)
} else {
Err(serde::de::Error::custom("string too long"))
}
})?,
),
})?)
}
PrincipalField::Description
| PrincipalField::Tenant
| PrincipalField::Picture => {
if let Some(v) = map.next_value::<Option<CompactString>>()? {
if let Some(v) = map.next_value::<Option<String>>()? {
if v.len() <= MAX_STRING_LEN {
PrincipalValue::String(v)
} else {
@ -1166,8 +1155,8 @@ impl<'de> serde::Deserialize<'de> for StringOrU64 {
#[derive(Debug)]
enum StringOrMany {
One(CompactString),
Many(Vec<CompactString>),
One(String),
Many(Vec<String>),
}
impl<'de> serde::Deserialize<'de> for StringOrMany {
@ -1200,7 +1189,7 @@ impl<'de> serde::Deserialize<'de> for StringOrMany {
E: de::Error,
{
if v.len() <= MAX_STRING_LEN {
Ok(StringOrMany::One(v.into()))
Ok(StringOrMany::One(v))
} else {
Err(serde::de::Error::custom("string too long"))
}
@ -1212,7 +1201,7 @@ impl<'de> serde::Deserialize<'de> for StringOrMany {
{
let mut vec = Vec::new();
while let Some(value) = seq.next_element::<CompactString>()? {
while let Some(value) = seq.next_element::<String>()? {
vec.push(value);
}

View file

@ -5,6 +5,7 @@
*/
use argon2::Argon2;
use compact_str::ToCompactString;
use mail_builder::encoders::base64::base64_encode;
use mail_parser::decoders::base64::base64_decode;
use password_hash::PasswordHash;
@ -57,7 +58,7 @@ impl Principal {
.map_err(|err| {
trc::AuthEvent::Error
.reason(err)
.details(secret.to_string())
.details(secret.to_compact_string())
})?
.check_current(totp_token)
.unwrap_or(false);

View file

@ -17,7 +17,7 @@ use backend::{
smtp::SmtpDirectory,
sql::SqlDirectory,
};
use compact_str::CompactString;
use deadpool::managed::PoolError;
use ldap3::LdapError;
use mail_send::Credentials;
@ -37,10 +37,10 @@ pub struct Directory {
pub struct Principal {
pub id: u32,
pub typ: Type,
pub name: CompactString,
pub description: Option<CompactString>,
pub secrets: Vec<CompactString>,
pub emails: Vec<CompactString>,
pub name: String,
pub description: Option<String>,
pub secrets: Vec<String>,
pub emails: Vec<String>,
pub quota: Option<u64>,
pub tenant: Option<u32>,
pub data: Vec<PrincipalData>,
@ -58,9 +58,9 @@ pub enum PrincipalData {
Roles(Vec<u32>),
Lists(Vec<u32>),
Permissions(Vec<PermissionGrant>),
Picture(CompactString),
ExternalMembers(Vec<CompactString>),
Urls(Vec<CompactString>),
Picture(String),
ExternalMembers(Vec<String>),
Urls(Vec<String>),
PrincipalQuota(Vec<PrincipalQuota>),
}

View file

@ -6,25 +6,25 @@
pub mod index;
use compact_str::CompactString;
use store::{SERIALIZE_IDENTITY_V1, SerializedVersion};
#[derive(
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,
)]
pub struct Identity {
pub name: CompactString,
pub email: CompactString,
pub name: String,
pub email: String,
pub reply_to: Option<Vec<EmailAddress>>,
pub bcc: Option<Vec<EmailAddress>>,
pub text_signature: CompactString,
pub html_signature: CompactString,
pub text_signature: String,
pub html_signature: String,
}
#[derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Clone, PartialEq, Eq)]
pub struct EmailAddress {
pub name: Option<CompactString>,
pub email: CompactString,
pub name: Option<String>,
pub email: String,
}
impl SerializedVersion for Identity {

View file

@ -10,7 +10,7 @@ use common::{
CacheSwap, MailboxCache, MailboxStoreCache, Server, auth::AccessToken,
config::jmap::settings::SpecialUse, sharing::EffectiveAcl,
};
use compact_str::CompactString;
use jmap_proto::types::{acl::Acl, collection::Collection, value::AclGrant};
use std::future::Future;
use store::{
@ -242,7 +242,7 @@ fn build_tree(cache: &mut MailboxStoreCache) {
mailbox.path = if matches!(mailbox.role, SpecialUse::Inbox) {
"INBOX".into()
} else if mailbox.is_root() && mailbox.name.as_str().eq_ignore_ascii_case("inbox") {
format!("INBOX {}", idx + 1).into()
format!("INBOX {}", idx + 1)
} else {
mailbox.name.clone()
};
@ -264,7 +264,7 @@ fn build_tree(cache: &mut MailboxStoreCache) {
cache.by_id(&parent_id).map(|folder| (path, &folder.path))
})
{
let mut new_path = CompactString::with_capacity(parent_path.len() + path.len() + 1);
let mut new_path = String::with_capacity(parent_path.len() + path.len() + 1);
new_path.push_str(parent_path.as_str());
new_path.push('/');
new_path.push_str(path.as_str());

View file

@ -5,7 +5,7 @@
*/
use common::config::jmap::settings::SpecialUse;
use compact_str::CompactString;
use jmap_proto::types::value::AclGrant;
use store::{SERIALIZE_MAILBOX_V1, SerializedVersion};
@ -25,7 +25,7 @@ pub const TOMBSTONE_ID: u32 = u32::MAX - 1;
#[derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Clone, PartialEq, Eq)]
#[rkyv(derive(Debug))]
pub struct Mailbox {
pub name: CompactString,
pub name: String,
pub role: SpecialUse,
pub parent_id: u32,
pub sort_order: Option<u32>,
@ -48,7 +48,7 @@ impl SerializedVersion for Mailbox {
}
impl Mailbox {
pub fn new(name: impl Into<CompactString>) -> Self {
pub fn new(name: impl Into<String>) -> Self {
Mailbox {
name: name.into(),
role: SpecialUse::None,

View file

@ -9,7 +9,7 @@ use common::{
CacheSwap, MailboxStoreCache, MessageCache, MessageStoreCache, MessageUidCache, Server,
auth::AccessToken, sharing::EffectiveAcl,
};
use compact_str::CompactString;
use jmap_proto::types::{
acl::Acl,
collection::Collection,
@ -227,7 +227,7 @@ fn insert_item(cache: &mut MessageStoreCache, document_id: u32, message: &Archiv
if let Some(idx) = cache.keywords.iter().position(|k| k == custom) {
item.keywords |= 1 << (OTHER + idx);
} else if cache.keywords.len() < (128 - OTHER) {
cache.keywords.push(CompactString::from(custom));
cache.keywords.push(String::from(custom));
item.keywords |= 1 << (OTHER + cache.keywords.len() - 1);
}
}

View file

@ -5,7 +5,7 @@
*/
use common::Server;
use compact_str::CompactString;
use directory::Permission;
use jmap_proto::types::{state::StateChange, type_state::DataType};
use mail_parser::MessageParser;
@ -19,8 +19,8 @@ use super::ingest::{EmailIngest, IngestEmail, IngestSource};
#[derive(Debug)]
pub struct IngestMessage {
pub sender_address: CompactString,
pub recipients: Vec<CompactString>,
pub sender_address: String,
pub recipients: Vec<String>,
pub message_blob: BlobHash,
pub message_size: u64,
pub session_id: u64,
@ -44,8 +44,8 @@ pub struct LocalDeliveryResult {
}
pub struct AutogeneratedMessage {
pub sender_address: CompactString,
pub recipients: Vec<CompactString>,
pub sender_address: String,
pub recipients: Vec<String>,
pub message: Vec<u8>,
}

View file

@ -4,7 +4,6 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use jmap_proto::types::type_state::DataType;
use store::{SERIALIZE_PUSH_V1, SerializedVersion};
use utils::map::bitmap::Bitmap;
@ -13,10 +12,10 @@ use utils::map::bitmap::Bitmap;
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Default, Debug, Clone, PartialEq, Eq,
)]
pub struct PushSubscription {
pub url: CompactString,
pub device_client_id: CompactString,
pub url: String,
pub device_client_id: String,
pub expires: u64,
pub verification_code: CompactString,
pub verification_code: String,
pub verified: bool,
pub types: Bitmap<DataType>,
pub keys: Option<Keys>,

View file

@ -20,7 +20,7 @@ use crate::{
use common::{
Server, auth::AccessToken, config::jmap::settings::SpecialUse, scripts::plugins::PluginContext,
};
use compact_str::CompactString;
use directory::{Permission, QueryBy};
use jmap_proto::types::{collection::Collection, id::Id, keyword::Keyword, property::Property};
use mail_parser::MessageParser;
@ -377,11 +377,9 @@ impl SieveScriptIngest for Server {
} => {
input = true.into();
if let Some(message) = messages.get(message_id) {
let recipients: Vec<CompactString> = match recipient {
Recipient::Address(rcpt) => vec![rcpt.into()],
Recipient::Group(rcpts) => {
rcpts.into_iter().map(CompactString::from).collect()
}
let recipients: Vec<String> = match recipient {
Recipient::Address(rcpt) => vec![rcpt],
Recipient::Group(rcpts) => rcpts,
Recipient::List(_) => {
// Not yet implemented
continue;
@ -394,7 +392,7 @@ impl SieveScriptIngest for Server {
From = mail_from.clone(),
To = recipients
.iter()
.map(|r| trc::Value::String(r.clone()))
.map(|r| trc::Value::String(r.as_str().into()))
.collect::<Vec<_>>(),
Size = message.raw_message.len(),
SpanId = session_id
@ -411,7 +409,7 @@ impl SieveScriptIngest for Server {
From = mail_from.clone(),
To = recipients
.iter()
.map(|r| trc::Value::String(r.clone()))
.map(|r| trc::Value::String(r.as_str().into()))
.collect::<Vec<_>>(),
Size = message.raw_message.len(),
Limit = self.core.jmap.mail_max_size,
@ -722,6 +720,6 @@ impl SieveScriptIngest for Server {
pub struct CompiledScript {
pub script: Sieve,
pub name: CompactString,
pub name: String,
pub hash: u32,
}

View file

@ -7,7 +7,7 @@
use std::sync::Arc;
use common::KV_SIEVE_ID;
use compact_str::CompactString;
use sieve::Sieve;
use store::{SERIALIZE_SIEVE_V1, SerializedVersion, blake3};
use utils::BlobHash;
@ -21,7 +21,7 @@ pub mod ingest;
pub struct ActiveScript {
pub document_id: u32,
pub hash: u32,
pub script_name: CompactString,
pub script_name: String,
pub script: Arc<Sieve>,
}
@ -30,7 +30,7 @@ pub struct ActiveScript {
)]
#[rkyv(derive(Debug))]
pub struct SieveScript {
pub name: CompactString,
pub name: String,
pub is_active: bool,
pub blob_hash: BlobHash,
pub size: u32,
@ -50,13 +50,13 @@ impl SerializedVersion for SieveScript {
pub struct VacationResponse {
pub from_date: Option<u64>,
pub to_date: Option<u64>,
pub subject: Option<CompactString>,
pub text_body: Option<CompactString>,
pub html_body: Option<CompactString>,
pub subject: Option<String>,
pub text_body: Option<String>,
pub html_body: Option<String>,
}
impl SieveScript {
pub fn new(name: impl Into<CompactString>, blob_hash: BlobHash) -> Self {
pub fn new(name: impl Into<String>, blob_hash: BlobHash) -> Self {
SieveScript {
name: name.into(),
is_active: false,
@ -66,7 +66,7 @@ impl SieveScript {
}
}
pub fn with_name(mut self, name: impl Into<CompactString>) -> Self {
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = name.into();
self
}

View file

@ -7,7 +7,7 @@
use std::collections::HashMap;
use calcard::icalendar::ICalendar;
use compact_str::CompactString;
use dav_proto::schema::request::DeadProperty;
use jmap_proto::types::{acl::Acl, value::AclGrant};
use store::{SERIALIZE_CALENDAR_V1, SERIALIZE_CALENDAREVENT_V1, SerializedVersion, ahash};
@ -19,7 +19,7 @@ use crate::DavName;
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,
)]
pub struct Calendar {
pub name: CompactString,
pub name: String,
pub preferences: HashMap<u32, CalendarPreferences, ahash::RandomState>,
pub acls: Vec<AclGrant>,
pub dead_properties: DeadProperty,
@ -31,16 +31,16 @@ pub struct Calendar {
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,
)]
pub struct CalendarPreferences {
pub name: CompactString,
pub description: Option<CompactString>,
pub name: String,
pub description: Option<String>,
pub sort_order: u32,
pub color: Option<CompactString>,
pub color: Option<String>,
pub is_subscribed: bool,
pub is_default: bool,
pub is_visible: bool,
pub include_in_availability: IncludeInAvailability,
pub default_alerts_with_time: HashMap<CompactString, ICalendar, ahash::RandomState>,
pub default_alerts_without_time: HashMap<CompactString, ICalendar, ahash::RandomState>,
pub default_alerts_with_time: HashMap<String, ICalendar, ahash::RandomState>,
pub default_alerts_without_time: HashMap<String, ICalendar, ahash::RandomState>,
pub time_zone: Timezone,
}
@ -49,7 +49,7 @@ pub struct CalendarPreferences {
)]
pub struct CalendarEvent {
pub names: Vec<DavName>,
pub display_name: Option<CompactString>,
pub display_name: Option<String>,
pub event: ICalendar,
pub user_properties: VecMap<u32, ICalendar>,
pub may_invite_self: bool,
@ -66,7 +66,7 @@ pub struct CalendarEvent {
rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Debug, Default, Clone, PartialEq, Eq,
)]
pub enum Timezone {
IANA(CompactString),
IANA(String),
Custom(ICalendar),
#[default]
Default,

View file

@ -7,6 +7,7 @@
pub mod index;
use calcard::vcard::VCard;
use dav_proto::schema::request::DeadProperty;
use jmap_proto::types::{acl::Acl, value::AclGrant};
use store::{SERIALIZE_ADDRESSBOOK_V1, SERIALIZE_CALENDAREVENT_V1, SerializedVersion};

View file

@ -6,6 +6,7 @@
pub mod index;
use dav_proto::schema::request::DeadProperty;
use jmap_proto::types::value::AclGrant;
use store::{SERIALIZE_FILENODE_V1, SerializedVersion};

View file

@ -64,7 +64,7 @@ impl Deserialize for DavName {
.caused_by(trc::location!())
.ctx(trc::Key::Value, bytes)
})?
.to_string();
.into();
Ok(DavName { name, parent_id })
}

View file

@ -8,7 +8,7 @@ use common::{
Server,
expr::{functions::ResolveVariable, *},
};
use compact_str::{CompactString, ToCompactString, format_compact};
use compact_str::{ToCompactString, format_compact};
use hyper::StatusCode;
use crate::{HttpContext, HttpRequest, HttpSessionData};
@ -18,7 +18,7 @@ impl<'x> HttpContext<'x> {
Self { session, req }
}
pub async fn resolve_response_url(&self, server: &Server) -> CompactString {
pub async fn resolve_response_url(&self, server: &Server) -> String {
server
.eval_if(
&server.core.network.http_response_url,
@ -27,7 +27,7 @@ impl<'x> HttpContext<'x> {
)
.await
.unwrap_or_else(|| {
format_compact!(
format!(
"http{}://{}:{}",
if self.session.is_tls { "s" } else { "" },
self.session.local_ip,
@ -67,12 +67,8 @@ impl ResolveVariable for HttpContext<'_> {
.iter()
.map(|(h, v)| {
Variable::String(
CompactString::new(format_compact!(
"{}: {}",
h.as_str(),
v.to_str().unwrap_or_default()
))
.into(),
format_compact!("{}: {}", h.as_str(), v.to_str().unwrap_or_default())
.into(),
)
})
.collect::<Vec<_>>()

View file

@ -10,25 +10,24 @@ use common::{
Server,
auth::{AccessToken, oauth::oidc::Userinfo},
};
use compact_str::{CompactString, ToCompactString, format_compact};
use serde::{Deserialize, Serialize};
use http_proto::*;
#[derive(Debug, Serialize, Deserialize)]
pub struct OpenIdMetadata {
pub issuer: CompactString,
pub authorization_endpoint: CompactString,
pub token_endpoint: CompactString,
pub userinfo_endpoint: CompactString,
pub jwks_uri: CompactString,
pub registration_endpoint: CompactString,
pub scopes_supported: Vec<CompactString>,
pub response_types_supported: Vec<CompactString>,
pub subject_types_supported: Vec<CompactString>,
pub grant_types_supported: Vec<CompactString>,
pub id_token_signing_alg_values_supported: Vec<CompactString>,
pub claims_supported: Vec<CompactString>,
pub issuer: String,
pub authorization_endpoint: String,
pub token_endpoint: String,
pub userinfo_endpoint: String,
pub jwks_uri: String,
pub registration_endpoint: String,
pub scopes_supported: Vec<String>,
pub response_types_supported: Vec<String>,
pub subject_types_supported: Vec<String>,
pub grant_types_supported: Vec<String>,
pub id_token_signing_alg_values_supported: Vec<String>,
pub claims_supported: Vec<String>,
}
pub trait OpenIdHandler: Sync + Send {
@ -50,7 +49,7 @@ impl OpenIdHandler for Server {
access_token: &AccessToken,
) -> trc::Result<HttpResponse> {
Ok(JsonResponse::new(Userinfo {
sub: Some(access_token.primary_id.to_compact_string()),
sub: Some(access_token.primary_id.to_string()),
name: access_token.description.clone(),
preferred_username: Some(access_token.name.clone()),
email: access_token.emails.first().cloned(),
@ -71,11 +70,11 @@ impl OpenIdHandler for Server {
.await;
Ok(JsonResponse::new(OpenIdMetadata {
authorization_endpoint: format_compact!("{base_url}/authorize/code",),
token_endpoint: format_compact!("{base_url}/auth/token"),
userinfo_endpoint: format_compact!("{base_url}/auth/userinfo"),
jwks_uri: format_compact!("{base_url}/auth/jwks.json"),
registration_endpoint: format_compact!("{base_url}/auth/register"),
authorization_endpoint: format!("{base_url}/authorize/code",),
token_endpoint: format!("{base_url}/auth/token"),
userinfo_endpoint: format!("{base_url}/auth/userinfo"),
jwks_uri: format!("{base_url}/auth/jwks.json"),
registration_endpoint: format!("{base_url}/auth/register"),
response_types_supported: vec![
"code".into(),
"id_token".into(),

View file

@ -10,7 +10,7 @@ use common::{
Server,
auth::oauth::registration::{ClientRegistrationRequest, ClientRegistrationResponse},
};
use compact_str::CompactString;
use directory::{
Permission, QueryBy, Type,
backend::internal::{
@ -70,7 +70,7 @@ impl ClientRegistrationHandler for Server {
.sample_iter(Alphanumeric)
.take(20)
.map(|ch| char::from(ch.to_ascii_lowercase()))
.collect::<CompactString>();
.collect::<String>();
self.store()
.create_principal(
PrincipalSet::new(u32::MAX, Type::OauthClient)

View file

@ -11,7 +11,7 @@ use common::{
oauth::{GrantType, oidc::StandardClaims},
},
};
use compact_str::CompactString;
use hyper::StatusCode;
use std::future::Future;
use store::{
@ -45,8 +45,8 @@ pub trait TokenHandler: Sync + Send {
&self,
account_id: u32,
client_id: &str,
issuer: CompactString,
nonce: Option<CompactString>,
issuer: String,
nonce: Option<String>,
with_refresh_token: bool,
with_id_token: bool,
) -> impl Future<Output = trc::Result<OAuthResponse>> + Send;
@ -289,8 +289,8 @@ impl TokenHandler for Server {
&self,
account_id: u32,
client_id: &str,
issuer: CompactString,
nonce: Option<CompactString>,
issuer: String,
nonce: Option<String>,
with_refresh_token: bool,
with_id_token: bool,
) -> trc::Result<OAuthResponse> {

View file

@ -7,7 +7,7 @@
use std::fmt::Write;
use common::{Server, manager::webadmin::Resource};
use compact_str::CompactString;
use directory::QueryBy;
use quick_xml::Reader;
use quick_xml::events::Event;
@ -29,7 +29,7 @@ pub trait Autoconfig: Sync + Send {
fn autoconfig_parameters<'x>(
&self,
emailaddress: &'x str,
) -> impl Future<Output = trc::Result<(CompactString, CompactString, &'x str)>> + Send;
) -> impl Future<Output = trc::Result<(String, String, &'x str)>> + Send;
}
impl Autoconfig for Server {
@ -177,7 +177,7 @@ impl Autoconfig for Server {
async fn autoconfig_parameters<'x>(
&self,
emailaddress: &'x str,
) -> trc::Result<(CompactString, CompactString, &'x str)> {
) -> trc::Result<(String, String, &'x str)> {
let (_, domain) = emailaddress.rsplit_once('@').ok_or_else(|| {
trc::ResourceEvent::BadParameters
.into_err()

View file

@ -12,7 +12,7 @@ use common::{
config::network::{ContactForm, FieldOrDefault},
ip_to_bytes, psl,
};
use compact_str::CompactString;
use email::message::delivery::{IngestMessage, LocalDeliveryStatus, MailDelivery};
use hyper::StatusCode;
use mail_auth::common::cache::NoCache;
@ -86,8 +86,10 @@ impl FormHandler for Server {
}
// Obtain fields
let from_email =
CompactString::from_str_to_lowercase(form_data.get_or_default(&form.from_email).trim());
let from_email = form_data
.get_or_default(&form.from_email)
.trim()
.to_lowercase();
let from_subject = form_data.get_or_default(&form.from_subject).trim();
let from_name = form_data.get_or_default(&form.from_name).trim();

View file

@ -4,10 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use std::sync::Arc;
use common::{KV_BAYES_MODEL_USER, Server, auth::AccessToken};
use compact_str::{CompactString, format_compact};
use directory::{
DirectoryInner, Permission, QueryBy, Type,
backend::internal::{
@ -19,35 +16,23 @@ use directory::{
},
},
};
use http_proto::{request::decode_path_element, *};
use hyper::{Method, header};
use serde_json::json;
use std::future::Future;
use std::sync::Arc;
use trc::AddContext;
use utils::url_params::UrlParams;
use http_proto::{request::decode_path_element, *};
use std::future::Future;
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(tag = "type")]
#[serde(rename_all = "camelCase")]
pub enum AccountAuthRequest {
SetPassword {
password: CompactString,
},
EnableOtpAuth {
url: CompactString,
},
DisableOtpAuth {
url: Option<CompactString>,
},
AddAppPassword {
name: CompactString,
password: CompactString,
},
RemoveAppPassword {
name: Option<CompactString>,
},
SetPassword { password: String },
EnableOtpAuth { url: String },
DisableOtpAuth { url: Option<String> },
AddAppPassword { name: String, password: String },
RemoveAppPassword { name: Option<String> },
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
@ -55,7 +40,7 @@ pub struct AccountAuthResponse {
#[serde(rename = "otpEnabled")]
pub otp_auth: bool,
#[serde(rename = "appPasswords")]
pub app_passwords: Vec<CompactString>,
pub app_passwords: Vec<String>,
}
pub trait PrincipalManager: Sync + Send {
@ -818,7 +803,7 @@ impl PrincipalManager for Server {
actions.push(PrincipalUpdate {
action: PrincipalAction::RemoveItem,
field: PrincipalField::Secrets,
value: PrincipalValue::String(CompactString::new("")),
value: PrincipalValue::String(String::new()),
});
(PrincipalAction::AddItem, password)
@ -828,13 +813,12 @@ impl PrincipalManager for Server {
PrincipalAction::RemoveItem,
url.unwrap_or_else(|| "otpauth://".into()),
),
AccountAuthRequest::AddAppPassword { name, password } => (
PrincipalAction::AddItem,
format_compact!("$app${name}${password}"),
),
AccountAuthRequest::AddAppPassword { name, password } => {
(PrincipalAction::AddItem, format!("$app${name}${password}"))
}
AccountAuthRequest::RemoveAppPassword { name } => (
PrincipalAction::RemoveItem,
format_compact!("$app${}", name.unwrap_or_default()),
format!("$app${}", name.unwrap_or_default()),
),
};

View file

@ -8,7 +8,7 @@ use std::{future::Future, sync::atomic::Ordering};
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
use common::{Server, auth::AccessToken, ipc::QueueEvent};
use compact_str::CompactString;
use directory::{Permission, Type, backend::internal::manage::ManageDirectory};
use hyper::Method;
use mail_auth::{
@ -134,7 +134,7 @@ impl QueueManagement for Server {
// SPDX-License-Identifier: LicenseRef-SEL
// Limit to tenant domains
let mut tenant_domains: Option<Vec<CompactString>> = None;
let mut tenant_domains: Option<Vec<String>> = None;
#[cfg(feature = "enterprise")]
if self.core.is_enterprise_edition() {
if let Some(tenant) = access_token.tenant {
@ -466,7 +466,7 @@ impl QueueManagement for Server {
match report_id {
QueueClass::DmarcReportHeader(event)
if tenant_domains.as_ref().is_none_or(|domains| {
domains.iter().any(|dd| dd == event.domain)
domains.iter().any(|dd| dd == &event.domain)
}) =>
{
let mut rua = Vec::new();
@ -479,7 +479,7 @@ impl QueueManagement for Server {
}
QueueClass::TlsReportHeader(event)
if tenant_domains.as_ref().is_none_or(|domains| {
domains.iter().any(|dd| dd == event.domain)
domains.iter().any(|dd| dd == &event.domain)
}) =>
{
let mut rua = Vec::new();
@ -539,7 +539,7 @@ impl QueueManagement for Server {
let result = match report_id {
QueueClass::DmarcReportHeader(event)
if tenant_domains.as_ref().is_none_or(|domains| {
domains.iter().any(|dd| dd == event.domain)
domains.iter().any(|dd| dd == &event.domain)
}) =>
{
self.delete_dmarc_report(event).await;
@ -547,7 +547,7 @@ impl QueueManagement for Server {
}
QueueClass::TlsReportHeader(event)
if tenant_domains.as_ref().is_none_or(|domains| {
domains.iter().any(|dd| dd == event.domain)
domains.iter().any(|dd| dd == &event.domain)
}) =>
{
self.delete_tls_report(vec![event]).await;
@ -668,7 +668,7 @@ struct QueuedMessages {
async fn fetch_queued_messages(
server: &Server,
params: &UrlParams<'_>,
tenant_domains: &Option<Vec<CompactString>>,
tenant_domains: &Option<Vec<String>>,
) -> trc::Result<QueuedMessages> {
let text = params.get("text");
let from = params.get("from");
@ -774,7 +774,7 @@ struct QueuedReports {
async fn fetch_queued_reports(
server: &Server,
params: &UrlParams<'_>,
tenant_domains: &Option<Vec<CompactString>>,
tenant_domains: &Option<Vec<String>>,
) -> trc::Result<QueuedReports> {
let domain = params.get("domain").map(|d| d.to_lowercase());
let type_ = params.get("type").and_then(|t| match t {
@ -823,7 +823,7 @@ async fn fetch_queued_reports(
let event = ReportEvent::deserialize(key)?;
if tenant_domains
.as_ref()
.is_none_or(|domains| domains.iter().any(|dd| dd == event.domain))
.is_none_or(|domains| domains.iter().any(|dd| dd == &event.domain))
&& event.seq_id != 0
&& domain.as_ref().is_none_or(|d| event.domain.contains(d))
{
@ -964,10 +964,10 @@ fn is_zero(num: &i16) -> bool {
}
trait IsTenantDomain {
fn is_tenant_domain(&self, tenant_domains: &Option<Vec<CompactString>>) -> bool;
fn is_tenant_domain(&self, tenant_domains: &Option<Vec<String>>) -> bool;
}
impl IsTenantDomain for ArchivedMessage {
fn is_tenant_domain(&self, tenant_domains: &Option<Vec<CompactString>>) -> bool {
fn is_tenant_domain(&self, tenant_domains: &Option<Vec<String>>) -> bool {
tenant_domains
.as_ref()
.is_none_or(|domains| self.has_domain(domains))

View file

@ -7,7 +7,7 @@
use std::future::Future;
use common::{Server, auth::AccessToken};
use compact_str::CompactString;
use directory::{Permission, Type, backend::internal::manage::ManageDirectory};
use hyper::Method;
use mail_auth::report::{
@ -52,7 +52,7 @@ impl ManageReports for Server {
// SPDX-License-Identifier: LicenseRef-SEL
// Limit to tenant domains
let mut tenant_domains: Option<Vec<CompactString>> = None;
let mut tenant_domains: Option<Vec<String>> = None;
#[cfg(feature = "enterprise")]
if self.core.is_enterprise_edition() {
if let Some(tenant) = access_token.tenant {
@ -297,7 +297,7 @@ async fn fetch_incoming_reports(
server: &Server,
class: &str,
params: &UrlParams<'_>,
tenant_domains: &Option<Vec<CompactString>>,
tenant_domains: &Option<Vec<String>>,
) -> trc::Result<IncomingReports> {
let filter = params.get("text");
let page: usize = params.parse::<usize>("page").unwrap_or_default();

View file

@ -7,6 +7,7 @@
use std::net::IpAddr;
use common::{Server, auth::AccessToken, config::spamfilter::SpamFilterAction, psl};
use compact_str::CompactString;
use directory::{
Permission,

View file

@ -233,10 +233,7 @@ impl ParseHttp for Server {
self.authenticate_headers(&req, &session, false).await?;
return self
.handle_session_resource(
ctx.resolve_response_url(self).await.into(),
access_token,
)
.handle_session_resource(ctx.resolve_response_url(self).await, access_token)
.await
.map(|s| s.into_http_response());
}

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::ToCompactString;
use crate::{
Command,
protocol::{
@ -43,17 +45,17 @@ impl Request<Command> {
let mailbox_name = utf7_maybe_decode(
tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing mailbox name."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing mailbox name."))?
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
version,
);
let identifier = if has_identifier {
tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing identifier."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing identifier."))?
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?
.map_err(|v| bad(self.tag.to_compact_string(), v))?
.into()
} else {
None
@ -62,10 +64,10 @@ impl Request<Command> {
ModRights::parse(
&tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing rights."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing rights."))?
.unwrap_bytes(),
)
.map_err(|v| bad(self.tag.to_string(), v))?
.map_err(|v| bad(self.tag.to_compact_string(), v))?
.into()
} else {
None

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::ToCompactString;
use crate::{
Command,
protocol::{
@ -35,7 +37,7 @@ impl Request<Command> {
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
version,
);
let mut messages = Vec::new();
@ -61,7 +63,7 @@ impl Request<Command> {
State::UTF8 => State::UTF8Data,
_ => {
return Err(bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Invalid opening parenthesis found.",
));
}
@ -70,7 +72,7 @@ impl Request<Command> {
Token::ParenthesisClose => match state {
State::None | State::UTF8 => {
return Err(bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Invalid closing parenthesis found.",
));
}
@ -93,7 +95,7 @@ impl Request<Command> {
message.received_at = Some(date_time);
} else {
return Err(bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Failed to parse received time.",
));
}
@ -105,12 +107,12 @@ impl Request<Command> {
State::Flags => {
message.flags.push(
Flag::parse_imap(value)
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
);
}
State::UTF8 => {
return Err(bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Expected parenthesis after UTF8.",
));
}
@ -119,13 +121,13 @@ impl Request<Command> {
message.message = value;
} else {
return Err(bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Invalid parameter after message literal.",
));
}
}
},
_ => return Err(bad(self.tag.to_string(), "Invalid arguments.")),
_ => return Err(bad(self.tag.to_compact_string(), "Invalid arguments.")),
}
}

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::ToCompactString;
use crate::{
Command,
protocol::authenticate::{self, Mechanism},
@ -16,7 +18,7 @@ impl Request<Command> {
let mut tokens = self.tokens.into_iter();
Ok(authenticate::Arguments {
mechanism: Mechanism::parse(&tokens.next().unwrap().unwrap_bytes())
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
params: tokens
.filter_map(|token| token.unwrap_string().ok())
.collect(),

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::ToCompactString;
use crate::{
Command,
protocol::{ProtocolVersion, copy_move},
@ -22,16 +24,16 @@ impl Request<Command> {
sequence_set: parse_sequence_set(
&tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing sequence set."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing sequence set."))?
.unwrap_bytes(),
)
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
mailbox_name: utf7_maybe_decode(
tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing mailbox name."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing mailbox name."))?
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
version,
),
tag: self.tag,

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::{CompactString, ToCompactString, format_compact};
use crate::{
Command,
protocol::{ProtocolVersion, create, list::Attribute},
@ -20,21 +22,27 @@ impl Request<Command> {
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.clone(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
version,
);
let mailbox_role = if let Some(Token::ParenthesisOpen) = tokens.next() {
match tokens.next() {
Some(Token::Argument(param)) if param.eq_ignore_ascii_case(b"USE") => (),
_ => {
return Err(bad(self.tag, "Failed to parse, expected 'USE'."));
return Err(bad(
CompactString::from_string_buffer(self.tag),
"Failed to parse, expected 'USE'.",
));
}
}
if tokens
.next()
.is_none_or(|token| !token.is_parenthesis_open())
{
return Err(bad(self.tag, "Expected '(' after 'USE'."));
return Err(bad(
CompactString::from_string_buffer(self.tag),
"Expected '(' after 'USE'.",
));
}
match tokens.next() {
Some(Token::Argument(value)) => {
@ -52,14 +60,14 @@ impl Request<Command> {
Some(Some(tag)) => Some(tag),
Some(None) => {
return Err(bad(
self.tag,
CompactString::from_string_buffer(self.tag),
"A mailbox with the \"\\All\" attribute already exists.",
));
}
None => {
return Err(bad(
self.tag,
format!(
CompactString::from_string_buffer(self.tag),
format_compact!(
"Special use attribute {:?} is not supported.",
String::from_utf8_lossy(&value)
),
@ -68,7 +76,10 @@ impl Request<Command> {
}
}
_ => {
return Err(bad(self.tag, "Invalid SPECIAL-USE attribute."));
return Err(bad(
CompactString::from_string_buffer(self.tag),
"Invalid SPECIAL-USE attribute.",
));
}
}
} else {

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::ToCompactString;
use crate::{
Command,
protocol::{ProtocolVersion, delete},
@ -21,7 +23,7 @@ impl Request<Command> {
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.clone(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
version,
),
tag: self.tag,

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::ToCompactString;
use crate::{
Command,
protocol::{capability::Capability, enable},
@ -18,7 +20,7 @@ impl Request<Command> {
for capability in self.tokens {
capabilities.push(
Capability::parse(&capability.unwrap_bytes())
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
);
}
Ok(enable::Arguments {

View file

@ -8,7 +8,7 @@ use std::borrow::Cow;
use std::iter::Peekable;
use std::vec::IntoIter;
use compact_str::CompactString;
use compact_str::{CompactString, ToCompactString, format_compact};
use crate::{
Command,
@ -30,10 +30,10 @@ impl Request<Command> {
let sequence_set = parse_sequence_set(
&tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing sequence set."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing sequence set."))?
.unwrap_bytes(),
)
.map_err(|v| bad(self.tag.to_string(), v))?;
.map_err(|v| bad(self.tag.to_compact_string(), v))?;
let mut in_parentheses = false;
@ -90,7 +90,7 @@ impl Request<Command> {
let rfc822 = tokens
.next()
.ok_or_else(|| {
bad(self.tag.to_string(), "Missing RFC822 parameter.")
bad(self.tag.to_compact_string(), "Missing RFC822 parameter.")
})?
.unwrap_bytes();
if rfc822.eq_ignore_ascii_case(b"HEADER") {
@ -101,8 +101,8 @@ impl Request<Command> {
Attribute::Rfc822Text
} else {
return Err(bad(
self.tag,
format!(
CompactString::from_string_buffer(self.tag),
format_compact!(
"Invalid RFC822 parameter {:?}.",
String::from_utf8_lossy(&rfc822)
),
@ -126,13 +126,13 @@ impl Request<Command> {
.is_none_or( |token| !token.eq_ignore_ascii_case(b"PEEK"))
{
return Err(bad(
self.tag.clone(),
self.tag.to_compact_string(),
"Expected 'PEEK' after '.'.",
));
}
if tokens.next().is_none_or( |token| !token.is_bracket_open()) {
return Err(bad(
self.tag.clone(),
self.tag.to_compact_string(),
"Expected '[' after 'BODY.PEEK'",
));
}
@ -162,7 +162,7 @@ impl Request<Command> {
!token.eq_ignore_ascii_case(b"FIELDS")
}) {
return Err(bad(
self.tag,
CompactString::from_string_buffer(self.tag),
"Expected 'FIELDS' after 'HEADER.'.",
));
}
@ -172,7 +172,7 @@ impl Request<Command> {
!token.eq_ignore_ascii_case(b"NOT")
}) {
return Err(bad(
self.tag,
CompactString::from_string_buffer(self.tag),
"Expected 'NOT' after 'HEADER.FIELDS.'.",
));
}
@ -185,7 +185,7 @@ impl Request<Command> {
.is_none_or( |token| !token.is_parenthesis_open())
{
return Err(bad(
self.tag,
CompactString::from_string_buffer(self.tag),
"Expected '(' after 'HEADER.FIELDS'.",
));
}
@ -194,13 +194,13 @@ impl Request<Command> {
match token {
Token::ParenthesisClose => break,
Token::Argument(value) => {
fields.push(CompactString::from_utf8(value).map_err(
|_| bad(self.tag.clone(), "Invalid UTF-8 in header field name."),
fields.push(String::from_utf8(value).map_err(
|_| bad(self.tag.to_compact_string(),"Invalid UTF-8 in header field name."),
)?);
}
_ => {
return Err(bad(
self.tag,
CompactString::from_string_buffer(self.tag),
"Expected field name.",
))
}
@ -220,7 +220,7 @@ impl Request<Command> {
} else {
Section::Part {
num: parse_number::<u32>(&value)
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
}
};
sections.push(section);
@ -228,8 +228,8 @@ impl Request<Command> {
Token::Dot => (),
_ => {
return Err(bad(
self.tag,
format!(
CompactString::from_string_buffer(self.tag),
format_compact!(
"Invalid token {:?} found in section-spect.",
token
),
@ -242,7 +242,7 @@ impl Request<Command> {
peek: is_peek,
sections,
partial: parse_partial(&mut tokens)
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
});
},
"BINARY" => {
@ -251,7 +251,7 @@ impl Request<Command> {
let param = tokens
.next()
.ok_or({
bad(self.tag.clone(), "Missing parameter after 'BINARY.'.")
bad(self.tag.to_compact_string(),"Missing parameter after 'BINARY.'.")
})?
.unwrap_bytes();
if param.eq_ignore_ascii_case(b"PEEK") {
@ -260,7 +260,7 @@ impl Request<Command> {
(false, true)
} else {
return Err(bad(
self.tag,
CompactString::from_string_buffer(self.tag),
"Expected 'PEEK' or 'SIZE' after 'BINARY.'.",
));
}
@ -270,7 +270,7 @@ impl Request<Command> {
// Parse section-part
if tokens.next().is_none_or( |token| !token.is_bracket_open()) {
return Err(bad(self.tag.to_string(), "Expected '[' after 'BINARY'."));
return Err(bad(self.tag.to_compact_string(), "Expected '[' after 'BINARY'."));
}
let mut sections = Vec::new();
while let Some(token) = tokens.next() {
@ -278,15 +278,15 @@ impl Request<Command> {
Token::Argument(value) => {
sections.push(
parse_number::<u32>(&value)
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
);
}
Token::Dot => (),
Token::BracketClose => break,
_ => {
return Err(bad(
self.tag,
format!(
CompactString::from_string_buffer(self.tag),
format_compact!(
"Expected part section integer, got {:?}.",
token.to_string()
),
@ -299,7 +299,7 @@ impl Request<Command> {
peek: is_peek,
sections,
partial: parse_partial(&mut tokens)
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
}
} else {
Attribute::BinarySize { sections }
@ -338,8 +338,8 @@ impl Request<Command> {
},
_ => {
return Err(bad(
self.tag,
format!("Invalid attribute {:?}", String::from_utf8_lossy(&value)),
CompactString::from_string_buffer(self.tag),
format_compact!("Invalid attribute {:?}", String::from_utf8_lossy(&value)),
));
}
);
@ -352,20 +352,26 @@ impl Request<Command> {
if !in_parentheses {
in_parentheses = true;
} else {
return Err(bad(self.tag.to_string(), "Unexpected parenthesis open."));
return Err(bad(
self.tag.to_compact_string(),
"Unexpected parenthesis open.",
));
}
}
Token::ParenthesisClose => {
if in_parentheses {
break;
} else {
return Err(bad(self.tag.to_string(), "Unexpected parenthesis close."));
return Err(bad(
self.tag.to_compact_string(),
"Unexpected parenthesis close.",
));
}
}
_ => {
return Err(bad(
self.tag,
format!("Invalid fetch argument {:?}.", token.to_string()),
CompactString::from_string_buffer(self.tag),
format_compact!("Invalid fetch argument {:?}.", token.to_string()),
));
}
}
@ -383,11 +389,14 @@ impl Request<Command> {
&tokens
.next()
.ok_or_else(|| {
bad(self.tag.to_string(), "Missing CHANGEDSINCE parameter.")
bad(
self.tag.to_compact_string(),
"Missing CHANGEDSINCE parameter.",
)
})?
.unwrap_bytes(),
)
.map_err(|v| bad(self.tag.to_string(), v))?
.map_err(|v| bad(self.tag.to_compact_string(), v))?
.into();
}
Token::Argument(param) if param.eq_ignore_ascii_case(b"VANISHED") => {
@ -398,8 +407,8 @@ impl Request<Command> {
}
_ => {
return Err(bad(
self.tag.clone(),
format!("Unsupported parameter '{}'.", token.to_string()),
self.tag.to_compact_string(),
format_compact!("Unsupported parameter '{}'.", token),
));
}
}
@ -415,7 +424,10 @@ impl Request<Command> {
include_vanished,
})
} else {
Err(bad(self.tag, "No data items to fetch specified."))
Err(bad(
CompactString::from_string_buffer(self.tag),
"No data items to fetch specified.",
))
}
}
}

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::{CompactString, ToCompactString};
use crate::{
Command,
protocol::{
@ -27,13 +29,13 @@ impl Request<Command> {
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
mailbox_name: utf7_maybe_decode(
tokens
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
version,
),
tag: self.tag,
@ -53,12 +55,12 @@ impl Request<Command> {
Token::Argument(value) => {
selection_options.push(
SelectionOption::parse(&value)
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
);
}
_ => {
return Err(bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Invalid selection option argument.",
));
}
@ -66,18 +68,20 @@ impl Request<Command> {
}
tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing reference name."))?
.ok_or_else(|| {
bad(self.tag.to_compact_string(), "Missing reference name.")
})?
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?
.map_err(|v| bad(self.tag.to_compact_string(), v))?
}
token => token
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
};
match tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing mailbox name."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing mailbox name."))?
{
Token::ParenthesisOpen => {
while let Some(token) = tokens.next() {
@ -87,7 +91,7 @@ impl Request<Command> {
mailbox_name.push(
token
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
);
}
}
@ -97,7 +101,7 @@ impl Request<Command> {
mailbox_name.push(utf7_maybe_decode(
token
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
version,
));
}
@ -112,7 +116,7 @@ impl Request<Command> {
.is_none_or(|token| !token.is_parenthesis_open())
{
return Err(bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Invalid return option, expected parenthesis.",
));
}
@ -122,14 +126,14 @@ impl Request<Command> {
Token::ParenthesisClose => break,
Token::Argument(value) => {
let mut return_option = ReturnOption::parse(&value)
.map_err(|v| bad(self.tag.to_string(), v))?;
.map_err(|v| bad(self.tag.to_compact_string(), v))?;
if let ReturnOption::Status(status) = &mut return_option {
if tokens
.next()
.is_none_or(|token| !token.is_parenthesis_open())
{
return Err(bad(
self.tag,
CompactString::from_string_buffer(self.tag),
"Invalid return option, expected parenthesis after STATUS.",
));
}
@ -137,15 +141,13 @@ impl Request<Command> {
match token {
Token::ParenthesisClose => break,
Token::Argument(value) => {
status.push(
Status::parse(&value).map_err(|v| {
bad(self.tag.to_string(), v)
})?,
);
status.push(Status::parse(&value).map_err(
|v| bad(self.tag.to_compact_string(), v),
)?);
}
_ => {
return Err(bad(
self.tag,
CompactString::from_string_buffer(self.tag),
"Invalid status return option argument.",
));
}
@ -156,7 +158,7 @@ impl Request<Command> {
}
_ => {
return Err(bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Invalid return option argument.",
));
}

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::ToCompactString;
use crate::{
Command,
protocol::login,
@ -20,12 +22,12 @@ impl Request<Command> {
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
password: tokens
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
tag: self.tag,
})
}

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::ToCompactString;
use crate::{
Command,
protocol::{
@ -22,15 +24,15 @@ impl Request<Command> {
Ok(list::Arguments::Extended {
reference_name: tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing reference name."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing reference name."))?
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
mailbox_name: vec![utf7_maybe_decode(
tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing mailbox name."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing mailbox name."))?
.unwrap_string()
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
ProtocolVersion::Rev1,
)],
selection_options: vec![SelectionOption::Subscribed],

View file

@ -28,7 +28,6 @@ pub mod thread;
use std::{borrow::Cow, str::FromStr};
use chrono::{DateTime, NaiveDate};
use compact_str::CompactString;
use crate::{
Command,
@ -112,7 +111,7 @@ impl Flag {
if let Some(flag) = flag {
Ok(flag)
} else {
CompactString::from_utf8(value)
String::from_utf8(value)
.map_err(|_| Cow::from("Invalid UTF-8."))
.map(Flag::Keyword)
}
@ -137,9 +136,9 @@ impl Flag {
"$forwarded" => Flag::Forwarded,
"$mdnsent" => Flag::MDNSent,
)
.unwrap_or_else(|| Flag::Keyword(value.into()))
.unwrap_or_else(|| Flag::Keyword(value))
} else {
let mut keyword = CompactString::with_capacity(value.len());
let mut keyword = String::with_capacity(value.len());
for c in value.chars() {
if c.is_ascii_alphanumeric() {
keyword.push(c);

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::ToCompactString;
use crate::{
Command,
protocol::{ProtocolVersion, quota},
@ -21,7 +23,7 @@ impl Request<Command> {
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.clone(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
version,
),
tag: self.tag,
@ -40,7 +42,7 @@ impl Request<Command> {
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.clone(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
tag: self.tag,
}),
0 => Err(self.into_error("Missing quota root.")),

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::ToCompactString;
use crate::{
Command,
protocol::{ProtocolVersion, rename},
@ -22,7 +24,7 @@ impl Request<Command> {
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.clone(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
version,
),
new_mailbox_name: utf7_maybe_decode(
@ -30,7 +32,7 @@ impl Request<Command> {
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.clone(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
version,
),
tag: self.tag,

View file

@ -8,7 +8,7 @@ use std::borrow::Cow;
use std::iter::Peekable;
use std::vec::IntoIter;
use compact_str::CompactString;
use compact_str::ToCompactString;
use mail_parser::decoders::charsets::DecoderFnc;
use mail_parser::decoders::charsets::map::charset_decoder;
@ -38,14 +38,14 @@ impl Request<Command> {
tokens.next();
is_esearch = true;
result_options = parse_result_options(&mut tokens)
.map_err(|v| bad(self.tag.to_string(), v))?;
.map_err(|v| bad(self.tag.to_compact_string(), v))?;
}
Some(Token::Argument(value)) if value.eq_ignore_ascii_case(b"charset") => {
tokens.next();
decoder = charset_decoder(
&tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing charset."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing charset."))?
.unwrap_bytes(),
);
}
@ -53,11 +53,14 @@ impl Request<Command> {
}
}
let filter =
parse_filters(&mut tokens, decoder).map_err(|v| bad(self.tag.to_string(), v))?;
let filter = parse_filters(&mut tokens, decoder)
.map_err(|v| bad(self.tag.to_compact_string(), v))?;
match filter.len() {
0 => Err(bad(self.tag.to_string(), "No filters found in command.")),
0 => Err(bad(
self.tag.to_compact_string(),
"No filters found in command.",
)),
_ => Ok(search::Arguments {
tag: self.tag,
result_options,
@ -374,7 +377,7 @@ pub fn parse_filters(
}
Some(token) => {
return Err(
format!("Unsupported MODSEQ parameter '{}'.", token.to_string()).into()
format!("Unsupported MODSEQ parameter '{}'.", token).into()
);
}
None => {
@ -500,16 +503,16 @@ pub fn parse_filters(
pub fn decode_argument(
tokens: &mut Peekable<IntoIter<Token>>,
decoder: Option<DecoderFnc>,
) -> super::Result<CompactString> {
) -> super::Result<String> {
let argument = tokens
.next()
.ok_or_else(|| Cow::from("Expected string."))?
.unwrap_bytes();
if let Some(decoder) = decoder {
Ok(decoder(&argument).into())
Ok(decoder(&argument))
} else {
Ok(CompactString::from_utf8(argument).map_err(|_| Cow::from("Invalid UTF-8 argument."))?)
Ok(String::from_utf8(argument).map_err(|_| Cow::from("Invalid UTF-8 argument."))?)
}
}

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::{CompactString, ToCompactString, format_compact};
use crate::{
Command,
protocol::{
@ -27,7 +29,7 @@ impl Request<Command> {
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.clone(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
version,
);
@ -46,7 +48,10 @@ impl Request<Command> {
.next()
.is_none_or(|token| !token.is_parenthesis_open())
{
return Err(bad(self.tag, "Expected '(' after 'QRESYNC'."));
return Err(bad(
CompactString::from_string_buffer(self.tag),
"Expected '(' after 'QRESYNC'.",
));
}
let uid_validity = parse_number::<u32>(
@ -54,32 +59,32 @@ impl Request<Command> {
.next()
.ok_or_else(|| {
bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Missing uidvalidity parameter for QRESYNC.",
)
})?
.unwrap_bytes(),
)
.map_err(|v| bad(self.tag.to_string(), v))?;
.map_err(|v| bad(self.tag.to_compact_string(), v))?;
let modseq = parse_number::<u64>(
&tokens
.next()
.ok_or_else(|| {
bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Missing modseq parameter for QRESYNC.",
)
})?
.unwrap_bytes(),
)
.map_err(|v| bad(self.tag.to_string(), v))?;
.map_err(|v| bad(self.tag.to_compact_string(), v))?;
let mut known_uids = None;
let mut seq_match = None;
let has_seq_match = match tokens.peek() {
Some(Token::Argument(value)) => {
known_uids = parse_sequence_set(value)
.map_err(|v| bad(self.tag.to_string(), v))?
.map_err(|v| bad(self.tag.to_compact_string(), v))?
.into();
tokens.next();
if matches!(tokens.peek(), Some(Token::ParenthesisOpen)) {
@ -103,31 +108,34 @@ impl Request<Command> {
.next()
.ok_or_else(|| {
bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Missing known-sequence-set parameter for QRESYNC.",
)
})?
.unwrap_bytes(),
)
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
parse_sequence_set(
&tokens
.next()
.ok_or_else(|| {
bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Missing known-uid-set parameter for QRESYNC.",
)
})?
.unwrap_bytes(),
)
.map_err(|v| bad(self.tag.to_string(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
));
if tokens
.next()
.is_none_or(|token| !token.is_parenthesis_close())
{
return Err(bad(self.tag, "Missing ')' for 'QRESYNC'."));
return Err(bad(
CompactString::from_string_buffer(self.tag),
"Missing ')' for 'QRESYNC'.",
));
}
}
@ -135,7 +143,10 @@ impl Request<Command> {
.next()
.is_none_or(|token| !token.is_parenthesis_close())
{
return Err(bad(self.tag, "Missing ')' for 'QRESYNC'."));
return Err(bad(
CompactString::from_string_buffer(self.tag),
"Missing ')' for 'QRESYNC'.",
));
}
qresync = QResync {
@ -151,8 +162,8 @@ impl Request<Command> {
}
_ => {
return Err(bad(
self.tag,
format!("Unexpected value '{}'.", token.to_string()),
CompactString::from_string_buffer(self.tag),
format_compact!("Unexpected value '{}'.", token),
));
}
}
@ -160,8 +171,8 @@ impl Request<Command> {
}
Some(token) => {
return Err(bad(
self.tag,
format!("Unexpected value '{}'.", token.to_string()),
CompactString::from_string_buffer(self.tag),
format_compact!("Unexpected value '{}'.", token),
));
}
None => (),

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::ToCompactString;
use mail_parser::decoders::charsets::map::charset_decoder;
use crate::{
@ -28,7 +29,8 @@ impl Request<Command> {
Some(Token::Argument(value)) if value.eq_ignore_ascii_case(b"return") => {
tokens.next();
(
parse_result_options(&mut tokens).map_err(|v| bad(self.tag.to_string(), v))?,
parse_result_options(&mut tokens)
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
true,
)
}
@ -40,7 +42,7 @@ impl Request<Command> {
.is_none_or(|token| !token.is_parenthesis_open())
{
return Err(bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Expected sort criteria between parentheses.",
));
}
@ -54,31 +56,40 @@ impl Request<Command> {
is_ascending = false;
} else {
sort.push(Comparator {
sort: Sort::parse(&value).map_err(|v| bad(self.tag.to_string(), v))?,
sort: Sort::parse(&value)
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
ascending: is_ascending,
});
is_ascending = true;
}
}
_ => return Err(bad(self.tag.to_string(), "Invalid result option argument.")),
_ => {
return Err(bad(
self.tag.to_compact_string(),
"Invalid result option argument.",
));
}
}
}
if sort.is_empty() {
return Err(bad(self.tag.to_string(), "Missing sort criteria."));
return Err(bad(self.tag.to_compact_string(), "Missing sort criteria."));
}
let decoder = charset_decoder(
&tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing charset."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing charset."))?
.unwrap_bytes(),
);
let filter =
parse_filters(&mut tokens, decoder).map_err(|v| bad(self.tag.to_string(), v))?;
let filter = parse_filters(&mut tokens, decoder)
.map_err(|v| bad(self.tag.to_compact_string(), v))?;
match filter.len() {
0 => Err(bad(self.tag.to_string(), "No filters found in command.")),
0 => Err(bad(
self.tag.to_compact_string(),
"No filters found in command.",
)),
_ => Ok(Arguments {
sort: sort.into(),
result_options,

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::{CompactString, ToCompactString};
use crate::Command;
use crate::protocol::status::Status;
use crate::protocol::{ProtocolVersion, status};
@ -21,7 +23,7 @@ impl Request<Command> {
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.clone(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
version,
);
let mut items = Vec::with_capacity(len - 2);
@ -31,7 +33,7 @@ impl Request<Command> {
.is_none_or(|token| !token.is_parenthesis_open())
{
return Err(bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Expected parenthesis after mailbox name.",
));
}
@ -42,12 +44,13 @@ impl Request<Command> {
Token::ParenthesisClose => break,
Token::Argument(value) => {
items.push(
Status::parse(&value).map_err(|v| bad(self.tag.to_string(), v))?,
Status::parse(&value)
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
);
}
_ => {
return Err(bad(
self.tag.to_string(),
self.tag.to_compact_string(),
"Invalid status return option argument.",
));
}
@ -61,7 +64,10 @@ impl Request<Command> {
items,
})
} else {
Err(bad(self.tag, "At least one status item is required."))
Err(bad(
CompactString::from_string_buffer(self.tag),
"At least one status item is required.",
))
}
}
}

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::{CompactString, ToCompactString, format_compact};
use crate::{
Command,
protocol::{
@ -23,10 +25,10 @@ impl Request<Command> {
let sequence_set = parse_sequence_set(
&tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing sequence set."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing sequence set."))?
.unwrap_bytes(),
)
.map_err(|v| bad(self.tag.to_string(), v))?;
.map_err(|v| bad(self.tag.to_compact_string(), v))?;
let mut unchanged_since = None;
// CONDSTORE parameters
@ -39,11 +41,14 @@ impl Request<Command> {
&tokens
.next()
.ok_or_else(|| {
bad(self.tag.to_string(), "Missing UNCHANGEDSINCE parameter.")
bad(
self.tag.to_compact_string(),
"Missing UNCHANGEDSINCE parameter.",
)
})?
.unwrap_bytes(),
)
.map_err(|v| bad(self.tag.to_string(), v))?
.map_err(|v| bad(self.tag.to_compact_string(), v))?
.into();
}
Token::ParenthesisClose => {
@ -51,8 +56,8 @@ impl Request<Command> {
}
_ => {
return Err(bad(
self.tag.to_string(),
format!("Unsupported parameter '{}'.", token.to_string()),
self.tag.to_compact_string(),
format_compact!("Unsupported parameter '{}'.", token),
));
}
}
@ -62,7 +67,12 @@ impl Request<Command> {
// Operation
let operation = tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing message data item name."))?
.ok_or_else(|| {
bad(
self.tag.to_compact_string(),
"Missing message data item name.",
)
})?
.unwrap_bytes();
let (is_silent, operation) = hashify::tiny_map_ignore_case!(operation.as_slice(),
"FLAGS" => (false, Operation::Set),
@ -74,8 +84,8 @@ impl Request<Command> {
)
.ok_or_else(|| {
bad(
self.tag.to_string(),
format!(
self.tag.to_compact_string(),
format_compact!(
"Unsupported message data item name: {:?}",
String::from_utf8_lossy(&operation)
),
@ -86,30 +96,36 @@ impl Request<Command> {
let mut keywords = Vec::new();
match tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing flags to set."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing flags to set."))?
{
Token::ParenthesisOpen => {
for token in tokens {
match token {
Token::Argument(flag) => {
keywords.push(
Flag::parse_imap(flag).map_err(|v| bad(self.tag.to_string(), v))?,
Flag::parse_imap(flag)
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
);
}
Token::ParenthesisClose => {
break;
}
_ => {
return Err(bad(self.tag.to_string(), "Unsupported flag."));
return Err(bad(self.tag.to_compact_string(), "Unsupported flag."));
}
}
}
}
Token::Argument(flag) => {
keywords.push(Flag::parse_imap(flag).map_err(|v| bad(self.tag.to_string(), v))?);
keywords.push(
Flag::parse_imap(flag).map_err(|v| bad(self.tag.to_compact_string(), v))?,
);
}
_ => {
return Err(bad(self.tag, "Invalid flags parameter."));
return Err(bad(
CompactString::from_string_buffer(self.tag),
"Invalid flags parameter.",
));
}
}
@ -123,7 +139,7 @@ impl Request<Command> {
unchanged_since,
})
} else {
Err(bad(self.tag.to_string(), "Missing flags to set."))
Err(bad(self.tag.to_compact_string(), "Missing flags to set."))
}
}
}

View file

@ -4,6 +4,8 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::ToCompactString;
use crate::{
Command,
protocol::{ProtocolVersion, subscribe},
@ -21,7 +23,7 @@ impl Request<Command> {
.next()
.unwrap()
.unwrap_string()
.map_err(|v| bad(self.tag.clone(), v))?,
.map_err(|v| bad(self.tag.to_compact_string(), v))?,
version,
),
tag: self.tag,

View file

@ -4,6 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::ToCompactString;
use mail_parser::decoders::charsets::map::charset_decoder;
use crate::{
@ -25,22 +26,25 @@ impl Request<Command> {
let algorithm = Algorithm::parse(
&tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing threading algorithm."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing threading algorithm."))?
.unwrap_bytes(),
)
.map_err(|v| bad(self.tag.to_string(), v))?;
.map_err(|v| bad(self.tag.to_compact_string(), v))?;
let decoder = charset_decoder(
&tokens
.next()
.ok_or_else(|| bad(self.tag.to_string(), "Missing charset."))?
.ok_or_else(|| bad(self.tag.to_compact_string(), "Missing charset."))?
.unwrap_bytes(),
);
let filter =
parse_filters(&mut tokens, decoder).map_err(|v| bad(self.tag.to_string(), v))?;
let filter = parse_filters(&mut tokens, decoder)
.map_err(|v| bad(self.tag.to_compact_string(), v))?;
match filter.len() {
0 => Err(bad(self.tag.to_string(), "No filters found in command.")),
0 => Err(bad(
self.tag.to_compact_string(),
"No filters found in command.",
)),
_ => Ok(thread::Arguments {
algorithm,
filter,

View file

@ -35,7 +35,7 @@
use std::fmt::Display;
use compact_str::CompactString;
use jmap_proto::types::acl::Acl;
use crate::utf7::utf7_encode;
@ -72,28 +72,28 @@ pub enum ModRightsOp {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub mailbox_name: CompactString,
pub identifier: Option<CompactString>,
pub tag: String,
pub mailbox_name: String,
pub identifier: Option<String>,
pub mod_rights: Option<ModRights>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GetAclResponse {
pub mailbox_name: CompactString,
pub permissions: Vec<(CompactString, Vec<Rights>)>,
pub mailbox_name: String,
pub permissions: Vec<(String, Vec<Rights>)>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ListRightsResponse {
pub mailbox_name: CompactString,
pub identifier: CompactString,
pub mailbox_name: String,
pub identifier: String,
pub permissions: Vec<Vec<Rights>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MyRightsResponse {
pub mailbox_name: CompactString,
pub mailbox_name: String,
pub rights: Vec<Rights>,
}
@ -249,14 +249,14 @@ impl From<Rights> for Acl {
#[cfg(test)]
mod tests {
use compact_str::CompactString;
use crate::protocol::acl::{GetAclResponse, ListRightsResponse, MyRightsResponse, Rights};
#[test]
fn serialize_acl() {
assert_eq!(
CompactString::from_utf8(
String::from_utf8(
GetAclResponse {
mailbox_name: "INBOX".into(),
permissions: vec![
@ -290,7 +290,7 @@ mod tests {
);
assert_eq!(
CompactString::from_utf8(
String::from_utf8(
ListRightsResponse {
mailbox_name: "Deleted Items".into(),
identifier: "Fred".into(),
@ -307,7 +307,7 @@ mod tests {
);
assert_eq!(
CompactString::from_utf8(
String::from_utf8(
MyRightsResponse {
mailbox_name: "Important".into(),
rights: vec![Rights::Lookup, Rights::Read, Rights::DeleteMailbox]

View file

@ -4,14 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use super::Flag;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub mailbox_name: CompactString,
pub tag: String,
pub mailbox_name: String,
pub messages: Vec<Message>,
}

View file

@ -4,13 +4,13 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub tag: String,
pub mechanism: Mechanism,
pub params: Vec<CompactString>,
pub params: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]

View file

@ -4,13 +4,13 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use super::Sequence;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub tag: String,
pub sequence_set: Sequence,
pub mailbox_name: CompactString,
pub mailbox_name: String,
}

View file

@ -4,13 +4,13 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use super::list::Attribute;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub mailbox_name: CompactString,
pub tag: String,
pub mailbox_name: String,
pub mailbox_role: Option<Attribute>,
}

View file

@ -4,10 +4,10 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub mailbox_name: CompactString,
pub tag: String,
pub mailbox_name: String,
}

View file

@ -4,13 +4,13 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use super::{ImapResponse, capability::Capability};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub tag: String,
pub capabilities: Vec<Capability>,
}

View file

@ -6,7 +6,7 @@
use std::borrow::Cow;
use compact_str::CompactString;
use mail_parser::DateTime;
use super::{
@ -16,7 +16,7 @@ use super::{
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub tag: String,
pub sequence_set: Sequence,
pub attributes: Vec<Attribute>,
pub changed_since: Option<u64>,
@ -69,14 +69,9 @@ pub enum Attribute {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Section {
Part {
num: u32,
},
Part { num: u32 },
Header,
HeaderFields {
not: bool,
fields: Vec<CompactString>,
},
HeaderFields { not: bool, fields: Vec<String> },
Text,
Mime,
}
@ -134,10 +129,10 @@ pub enum DataItem<'x> {
modseq: u64,
},
EmailId {
email_id: CompactString,
email_id: String,
},
ThreadId {
thread_id: CompactString,
thread_id: String,
},
}
@ -922,7 +917,7 @@ impl ImapResponse for Response<'_> {
#[cfg(test)]
mod tests {
use compact_str::CompactString;
use mail_parser::DateTime;
use crate::protocol::{Flag, ImapResponse};
@ -1370,7 +1365,7 @@ mod tests {
item.serialize(&mut buf);
assert_eq!(CompactString::from_utf8(buf).unwrap(), expected_response);
assert_eq!(String::from_utf8(buf).unwrap(), expected_response);
}
}

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use crate::utf7::utf7_encode;
@ -16,14 +16,14 @@ use super::{
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Arguments {
Basic {
tag: CompactString,
reference_name: CompactString,
mailbox_name: CompactString,
tag: String,
reference_name: String,
mailbox_name: String,
},
Extended {
tag: CompactString,
reference_name: CompactString,
mailbox_name: Vec<CompactString>,
tag: String,
reference_name: String,
mailbox_name: Vec<String>,
selection_options: Vec<SelectionOption>,
return_options: Vec<ReturnOption>,
},
@ -82,12 +82,12 @@ pub enum ChildInfo {
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Tag {
ChildInfo(Vec<ChildInfo>),
OldName(CompactString),
OldName(String),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ListItem {
pub mailbox_name: CompactString,
pub mailbox_name: String,
pub attributes: Vec<Attribute>,
pub tags: Vec<Tag>,
}
@ -108,7 +108,7 @@ impl Arguments {
}
}
pub fn unwrap_tag(self) -> CompactString {
pub fn unwrap_tag(self) -> String {
match self {
Arguments::Basic { tag, .. } => tag,
Arguments::Extended { tag, .. } => tag,
@ -196,7 +196,7 @@ impl Tag {
}
impl ListItem {
pub fn new(name: impl Into<CompactString>) -> Self {
pub fn new(name: impl Into<String>) -> Self {
ListItem {
mailbox_name: name.into(),
attributes: Vec::new(),
@ -277,7 +277,7 @@ impl ImapResponse for Response {
#[cfg(test)]
mod tests {
use compact_str::CompactString;
use crate::protocol::{
ImapResponse,
@ -341,8 +341,8 @@ mod tests {
response.serialize(&mut buf_1, false, false);
response.serialize(&mut buf_2, true, false);
let response_v1 = CompactString::from_utf8(buf_1).unwrap();
let response_v2 = CompactString::from_utf8(buf_2).unwrap();
let response_v1 = String::from_utf8(buf_1).unwrap();
let response_v2 = String::from_utf8(buf_2).unwrap();
assert_eq!(response_v2, expected_v2);
assert_eq!(response_v1, expected_v1);
@ -391,11 +391,11 @@ mod tests {
"* LSUB () \"/\" \"foo\" (\"CHILDINFO\" (\"SUBSCRIBED\"))\r\n",
);
let response_v2 = CompactString::from_utf8(response.clone().serialize()).unwrap();
let response_v2 = String::from_utf8(response.clone().serialize()).unwrap();
response.is_rev2 = false;
response.is_lsub = true;
response.status_items.clear();
let response_v1 = CompactString::from_utf8(response.serialize()).unwrap();
let response_v1 = String::from_utf8(response.serialize()).unwrap();
assert_eq!(response_v2, expected_v2);
assert_eq!(response_v1, expected_v1);

View file

@ -4,11 +4,11 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub username: CompactString,
pub password: CompactString,
pub tag: String,
pub username: String,
pub password: String,
}

View file

@ -8,6 +8,7 @@ use std::{cmp::Ordering, fmt::Display};
use ahash::AHashSet;
use chrono::{DateTime, Utc};
use compact_str::CompactString;
use jmap_proto::types::keyword::{ArchivedKeyword, Keyword};
@ -240,7 +241,7 @@ pub enum Flag {
Deleted,
Forwarded,
MDNSent,
Keyword(CompactString),
Keyword(String),
}
impl Flag {

View file

@ -4,12 +4,12 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use super::{ImapResponse, quoted_string};
pub struct Response {
pub shared_prefix: Option<CompactString>,
pub shared_prefix: Option<String>,
}
impl ImapResponse for Response {

View file

@ -4,18 +4,18 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use super::{ImapResponse, capability::QuotaResourceName, quoted_string};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub name: CompactString,
pub tag: String,
pub name: String,
}
pub struct QuotaItem {
pub name: CompactString,
pub name: String,
pub resources: Vec<QuotaResource>,
}
@ -26,7 +26,7 @@ pub struct QuotaResource {
}
pub struct Response {
pub quota_root_items: Vec<CompactString>,
pub quota_root_items: Vec<String>,
pub quota_items: Vec<QuotaItem>,
}

View file

@ -4,11 +4,11 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub mailbox_name: CompactString,
pub new_mailbox_name: CompactString,
pub tag: String,
pub mailbox_name: String,
pub new_mailbox_name: String,
}

View file

@ -4,14 +4,14 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use store::fts::{FilterItem, FilterType};
use super::{Flag, Sequence, quoted_string, serialize_sequence};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub tag: String,
pub is_esearch: bool,
pub sort: Option<Vec<Comparator>>,
pub result_options: Vec<ResultOption>,
@ -64,15 +64,15 @@ pub enum Filter {
Sequence(Sequence, bool),
All,
Answered,
Bcc(CompactString),
Bcc(String),
Before(i64),
Body(CompactString),
Cc(CompactString),
Body(String),
Cc(String),
Deleted,
Draft,
Flagged,
From(CompactString),
Header(CompactString, CompactString),
From(String),
Header(String, String),
Keyword(Flag),
Larger(u32),
On(i64),
@ -82,9 +82,9 @@ pub enum Filter {
SentSince(i64),
Since(i64),
Smaller(u32),
Subject(CompactString),
Text(CompactString),
To(CompactString),
Subject(String),
Text(String),
To(String),
Unanswered,
Undeleted,
Undraft,
@ -111,8 +111,8 @@ pub enum Filter {
ModSeq((u64, ModSeqEntry)),
// RFC 8474 - ObjectID
EmailId(CompactString),
ThreadId(CompactString),
EmailId(String),
ThreadId(String),
}
impl FilterItem for Filter {

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use crate::{ResponseCode, StatusResponse};
@ -12,8 +12,8 @@ use super::{ImapResponse, Sequence, list::ListItem};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub mailbox_name: CompactString,
pub tag: String,
pub mailbox_name: String,
pub condstore: bool,
pub qresync: Option<QResync>,
}
@ -40,7 +40,7 @@ pub struct Response {
pub is_rev2: bool,
pub closed_previous: bool,
pub highest_modseq: Option<HighestModSeq>,
pub mailbox_id: CompactString,
pub mailbox_id: String,
}
#[derive(Debug, Clone)]

View file

@ -4,7 +4,7 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use crate::utf7::utf7_encode;
@ -12,8 +12,8 @@ use super::quoted_string;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub mailbox_name: CompactString,
pub tag: String,
pub mailbox_name: String,
pub items: Vec<Status>,
}
@ -33,14 +33,14 @@ pub enum Status {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StatusItem {
pub mailbox_name: CompactString,
pub mailbox_name: String,
pub items: Vec<(Status, StatusItemType)>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StatusItemType {
Number(u64),
String(CompactString),
String(String),
}
impl StatusItem {

View file

@ -4,13 +4,13 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use super::{Flag, ImapResponse, Sequence, fetch::FetchItem};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub tag: String,
pub sequence_set: Sequence,
pub operation: Operation,
pub is_silent: bool,

View file

@ -4,10 +4,10 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub mailbox_name: CompactString,
pub tag: String,
pub mailbox_name: String,
}

View file

@ -4,13 +4,13 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use super::{ImapResponse, search::Filter};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Arguments {
pub tag: CompactString,
pub tag: String,
pub filter: Vec<Filter>,
pub algorithm: Algorithm,
}

View file

@ -4,7 +4,9 @@
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use compact_str::CompactString;
use std::fmt::Display;
use compact_str::{CompactString, format_compact};
use super::{ResponseCode, ResponseType};
@ -17,7 +19,7 @@ pub enum Error {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Request<T: CommandParser> {
pub tag: CompactString,
pub tag: String,
pub command: T,
pub tokens: Vec<Token>,
}
@ -43,7 +45,7 @@ pub enum Token {
impl<T: CommandParser> Default for Request<T> {
fn default() -> Self {
Self {
tag: CompactString::new(""),
tag: String::new(),
command: T::default(),
tokens: Vec::new(),
}
@ -112,7 +114,7 @@ impl<T: CommandParser> Receiver<T> {
if !self.buf.is_empty() {
self.current_request_size += self.buf.len();
if self.current_request_size > self.max_request_size {
return Err(self.error_reset(format!(
return Err(self.error_reset(format_compact!(
"Request exceeds maximum limit of {} bytes.",
self.max_request_size
)));
@ -128,7 +130,7 @@ impl<T: CommandParser> Receiver<T> {
fn push_token(&mut self, token: Token) -> Result<(), Error> {
self.current_request_size += 1;
if self.current_request_size > self.max_request_size {
return Err(self.error_reset(format!(
return Err(self.error_reset(format_compact!(
"Request exceeds maximum limit of {} bytes.",
self.max_request_size
)));
@ -150,7 +152,7 @@ impl<T: CommandParser> Receiver<T> {
State::Tag => match ch {
b' ' => {
if !self.buf.is_empty() {
self.request.tag = CompactString::from_utf8(std::mem::replace(
self.request.tag = String::from_utf8(std::mem::replace(
&mut self.buf,
Vec::with_capacity(10),
))
@ -160,7 +162,7 @@ impl<T: CommandParser> Receiver<T> {
}
b'\t' | b'\r' => {}
b'\n' => {
return Err(self.error_reset(format!(
return Err(self.error_reset(format_compact!(
"Missing command after tag {:?}, found CRLF instead.",
std::str::from_utf8(&self.buf).unwrap_or_default()
)));
@ -187,7 +189,7 @@ impl<T: CommandParser> Receiver<T> {
T::parse(&self.buf, is_uid).ok_or_else(|| {
let command =
String::from_utf8_lossy(&self.buf).into_owned();
self.error_reset(format!(
self.error_reset(format_compact!(
"Unrecognized command '{}'.",
command
))
@ -206,7 +208,7 @@ impl<T: CommandParser> Receiver<T> {
}
}
} else {
return Err(self.error_reset(format!(
return Err(self.error_reset(format_compact!(
"Invalid character {:?} in command name.",
ch as char
)));
@ -315,7 +317,7 @@ impl<T: CommandParser> Receiver<T> {
})?;
if self.current_request_size + size as usize > self.max_request_size
{
return Err(self.error_reset(format!(
return Err(self.error_reset(format_compact!(
"Literal exceeds the maximum request size of {} bytes.",
self.max_request_size
)));
@ -343,7 +345,7 @@ impl<T: CommandParser> Receiver<T> {
}
}
_ => {
return Err(self.error_reset(format!(
return Err(self.error_reset(format_compact!(
"Invalid character {:?} in literal.",
ch as char
)));
@ -386,10 +388,10 @@ impl<T: CommandParser> Receiver<T> {
}
impl Token {
pub fn unwrap_string(self) -> crate::parser::Result<CompactString> {
pub fn unwrap_string(self) -> crate::parser::Result<String> {
match self {
Token::Argument(value) => {
CompactString::from_utf8(value).map_err(|_| "Invalid UTF-8 in argument.".into())
String::from_utf8(value).map_err(|_| "Invalid UTF-8 in argument.".into())
}
other => Ok(other.to_string()),
}
@ -445,6 +447,12 @@ impl Token {
}
}
impl Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(&String::from_utf8_lossy(self.as_bytes()))
}
}
impl Token {
pub fn as_bytes(&self) -> &[u8] {
match self {
@ -459,17 +467,14 @@ impl Token {
Token::Nil => b"",
}
}
pub fn to_string(&self) -> CompactString {
CompactString::from_utf8_lossy(self.as_bytes())
}
}
impl Error {
pub fn err(tag: Option<CompactString>, message: impl Into<trc::Value>) -> Self {
pub fn err(tag: Option<impl Into<CompactString>>, message: impl Into<trc::Value>) -> Self {
Error::Error {
response: trc::ImapEvent::Error
.ctx(trc::Key::Details, message)
.ctx_opt(trc::Key::Id, tag)
.ctx_opt(trc::Key::Id, tag.map(Into::into))
.ctx(trc::Key::Type, ResponseType::Bad)
.code(ResponseCode::Parse),
}
@ -493,13 +498,13 @@ impl<T: CommandParser> Request<T> {
pub fn into_error(self, message: impl Into<trc::Value>) -> trc::Error {
trc::ImapEvent::Error
.ctx(trc::Key::Details, message)
.ctx(trc::Key::Id, self.tag)
.ctx(trc::Key::Id, CompactString::from_string_buffer(self.tag))
}
pub fn into_parse_error(self, message: impl Into<trc::Value>) -> trc::Error {
trc::ImapEvent::Error
.ctx(trc::Key::Details, message)
.ctx(trc::Key::Id, self.tag)
.ctx(trc::Key::Id, CompactString::from_string_buffer(self.tag))
.ctx(trc::Key::Code, ResponseCode::Parse)
.ctx(trc::Key::Type, ResponseType::Bad)
}

View file

@ -7,8 +7,6 @@
// Ported from https://github.com/jstedfast/MailKit/blob/master/MailKit/Net/Imap/ImapEncoding.cs
// Author: Jeffrey Stedfast <jestedfa@microsoft.com>
use compact_str::CompactString;
use crate::protocol::ProtocolVersion;
static UTF_7_RANK: &[u8] = &[
@ -22,7 +20,7 @@ static UTF_7_RANK: &[u8] = &[
static UTF_7_MAP: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+,";
pub fn utf7_decode(text: &str) -> Option<CompactString> {
pub fn utf7_decode(text: &str) -> Option<String> {
let mut bytes: Vec<u16> = Vec::with_capacity(text.len());
let mut bits = 0;
let mut v: u32 = 0;
@ -70,11 +68,11 @@ pub fn utf7_decode(text: &str) -> Option<CompactString> {
}
}
CompactString::from_utf16(&bytes).ok()
String::from_utf16(&bytes).ok()
}
pub fn utf7_encode(text: &str) -> CompactString {
let mut result = CompactString::with_capacity(text.len());
pub fn utf7_encode(text: &str) -> String {
let mut result = String::with_capacity(text.len());
let mut shifted = false;
let mut bits = 0;
let mut u: u32 = 0;
@ -124,7 +122,7 @@ pub fn utf7_encode(text: &str) -> CompactString {
}
#[inline(always)]
pub fn utf7_maybe_decode(text: CompactString, version: ProtocolVersion) -> CompactString {
pub fn utf7_maybe_decode(text: String, version: ProtocolVersion) -> String {
if version.is_rev2() {
text
} else {

View file

@ -13,7 +13,7 @@ use common::{
listener::{SessionStream, limiter::InFlight},
sharing::EffectiveAcl,
};
use compact_str::CompactString;
use directory::backend::internal::manage::ManageDirectory;
use email::{
mailbox::{
@ -60,7 +60,7 @@ impl<T: SessionStream> SessionData<T> {
// Fetch shared mailboxes
for &account_id in access_token.shared_accounts(Collection::Mailbox) {
let prefix: CompactString = format!(
let prefix: String = format!(
"{}/{}",
session.server.core.jmap.shared_folder,
session
@ -69,9 +69,8 @@ impl<T: SessionStream> SessionData<T> {
.get_principal_name(account_id)
.await
.caused_by(trc::location!())?
.unwrap_or_else(|| Id::from(account_id).to_string().into())
)
.into();
.unwrap_or_else(|| Id::from(account_id).to_string())
);
mailboxes.push(
session
.fetch_account_mailboxes(account_id, prefix.into(), &access_token, None)
@ -89,7 +88,7 @@ impl<T: SessionStream> SessionData<T> {
async fn fetch_account_mailboxes(
&self,
account_id: u32,
mailbox_prefix: Option<CompactString>,
mailbox_prefix: Option<String>,
access_token: &AccessToken,
current_state: Option<AccountState>,
) -> trc::Result<Option<Account>> {
@ -153,7 +152,7 @@ impl<T: SessionStream> SessionData<T> {
// Build mailbox path and map it to its effective id
let mailbox_name = if let Some(prefix) = &account.prefix {
let mut name = CompactString::with_capacity(prefix.len() + mailbox.path.len() + 1);
let mut name = String::with_capacity(prefix.len() + mailbox.path.len() + 1);
name.push_str(prefix.as_str());
name.push('/');
name.push_str(mailbox.path.as_str());
@ -167,7 +166,7 @@ impl<T: SessionStream> SessionData<T> {
.jmap
.default_folders
.iter()
.find(|f| f.name == mailbox_name || f.aliases.iter().any(|a| a == mailbox_name))
.find(|f| f.name == mailbox_name || f.aliases.iter().any(|a| a == &mailbox_name))
.and_then(|f| special_uses.get(&f.special_use))
.copied()
.unwrap_or(mailbox.document_id);
@ -275,7 +274,7 @@ impl<T: SessionStream> SessionData<T> {
// Fetch mailboxes for each new shared account
for account_id in added_account_ids {
let prefix: CompactString = format!(
let prefix: String = format!(
"{}/{}",
self.server.core.jmap.shared_folder,
self.server
@ -283,9 +282,8 @@ impl<T: SessionStream> SessionData<T> {
.get_principal_name(account_id)
.await
.caused_by(trc::location!())?
.unwrap_or_else(|| Id::from(account_id).to_string().into())
)
.into();
.unwrap_or_else(|| Id::from(account_id).to_string())
);
added_accounts.push(
self.fetch_account_mailboxes(account_id, prefix.into(), &access_token, None)
.await?

Some files were not shown because too many files have changed in this diff Show more