mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2025-12-19 05:49:21 +08:00
Always use rsplit to extract the domain part from email addresses
This commit is contained in:
parent
d00d3dd013
commit
5fcf73f070
14 changed files with 87 additions and 96 deletions
|
|
@ -13,6 +13,7 @@ use store::{
|
||||||
write::{DirectoryClass, ValueClass},
|
write::{DirectoryClass, ValueClass},
|
||||||
};
|
};
|
||||||
use trc::AddContext;
|
use trc::AddContext;
|
||||||
|
use utils::DomainPart;
|
||||||
|
|
||||||
#[allow(async_fn_in_trait)]
|
#[allow(async_fn_in_trait)]
|
||||||
pub trait DirectoryStore: Sync + Send {
|
pub trait DirectoryStore: Sync + Send {
|
||||||
|
|
@ -117,7 +118,7 @@ impl DirectoryStore for Store {
|
||||||
|
|
||||||
async fn vrfy(&self, address: &str) -> trc::Result<Vec<String>> {
|
async fn vrfy(&self, address: &str) -> trc::Result<Vec<String>> {
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
let address = address.split('@').next().unwrap_or(address);
|
let address = address.try_local_part().unwrap_or(address);
|
||||||
if address.len() > 3 {
|
if address.len() > 3 {
|
||||||
self.iterate(
|
self.iterate(
|
||||||
IterateParams::new(
|
IterateParams::new(
|
||||||
|
|
@ -129,7 +130,7 @@ impl DirectoryStore for Store {
|
||||||
|key, value| {
|
|key, value| {
|
||||||
let key =
|
let key =
|
||||||
std::str::from_utf8(key.get(1..).unwrap_or_default()).unwrap_or_default();
|
std::str::from_utf8(key.get(1..).unwrap_or_default()).unwrap_or_default();
|
||||||
if key.split('@').next().unwrap_or(key).contains(address)
|
if key.try_local_part().unwrap_or(key).contains(address)
|
||||||
&& PrincipalInfo::deserialize(value)
|
&& PrincipalInfo::deserialize(value)
|
||||||
.caused_by(trc::location!())?
|
.caused_by(trc::location!())?
|
||||||
.typ
|
.typ
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ use store::{
|
||||||
};
|
};
|
||||||
use trc::AddContext;
|
use trc::AddContext;
|
||||||
use types::collection::Collection;
|
use types::collection::Collection;
|
||||||
use utils::sanitize_email;
|
use utils::{DomainPart, sanitize_email};
|
||||||
|
|
||||||
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct PrincipalList<T> {
|
pub struct PrincipalList<T> {
|
||||||
|
|
@ -354,7 +354,7 @@ impl ManageDirectory for Store {
|
||||||
principal_create.tenant = tenant_id.into();
|
principal_create.tenant = tenant_id.into();
|
||||||
|
|
||||||
if !matches!(principal_create.typ, Type::Tenant | Type::Domain) {
|
if !matches!(principal_create.typ, Type::Tenant | Type::Domain) {
|
||||||
if let Some(domain) = name.split('@').nth(1)
|
if let Some(domain) = name.try_domain_part()
|
||||||
&& self
|
&& self
|
||||||
.get_principal_info(domain)
|
.get_principal_info(domain)
|
||||||
.await
|
.await
|
||||||
|
|
@ -523,7 +523,7 @@ impl ManageDirectory for Store {
|
||||||
if self.rcpt(&email).await.caused_by(trc::location!())? != RcptType::Invalid {
|
if self.rcpt(&email).await.caused_by(trc::location!())? != RcptType::Invalid {
|
||||||
return Err(err_exists(PrincipalField::Emails, email.to_string()));
|
return Err(err_exists(PrincipalField::Emails, email.to_string()));
|
||||||
}
|
}
|
||||||
if let Some(domain) = email.split('@').nth(1)
|
if let Some(domain) = email.try_domain_part()
|
||||||
&& valid_domains.insert(domain.into())
|
&& valid_domains.insert(domain.into())
|
||||||
{
|
{
|
||||||
self.get_principal_info(domain)
|
self.get_principal_info(domain)
|
||||||
|
|
@ -1003,7 +1003,7 @@ impl ManageDirectory for Store {
|
||||||
if tenant_id.is_some()
|
if tenant_id.is_some()
|
||||||
&& !matches!(principal_type, Type::Tenant | Type::Domain)
|
&& !matches!(principal_type, Type::Tenant | Type::Domain)
|
||||||
{
|
{
|
||||||
if let Some(domain) = new_name.split('@').nth(1)
|
if let Some(domain) = new_name.try_domain_part()
|
||||||
&& self
|
&& self
|
||||||
.get_principal_info(domain)
|
.get_principal_info(domain)
|
||||||
.await
|
.await
|
||||||
|
|
@ -2401,7 +2401,7 @@ impl ValidateDirectory for Store {
|
||||||
) -> trc::Result<()> {
|
) -> trc::Result<()> {
|
||||||
if self.rcpt(email).await.caused_by(trc::location!())? != RcptType::Invalid {
|
if self.rcpt(email).await.caused_by(trc::location!())? != RcptType::Invalid {
|
||||||
Err(err_exists(PrincipalField::Emails, email.to_string()))
|
Err(err_exists(PrincipalField::Emails, email.to_string()))
|
||||||
} else if let Some(domain) = email.split('@').nth(1) {
|
} else if let Some(domain) = email.try_domain_part() {
|
||||||
match self
|
match self
|
||||||
.get_principal_info(domain)
|
.get_principal_info(domain)
|
||||||
.await
|
.await
|
||||||
|
|
|
||||||
|
|
@ -261,7 +261,7 @@ impl EmailAddress<'_> {
|
||||||
&self.address
|
&self.address
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((local, host)) = addr.split_once('@') {
|
if let Some((local, host)) = addr.rsplit_once('@') {
|
||||||
quoted_or_literal_string(buf, local);
|
quoted_or_literal_string(buf, local);
|
||||||
buf.push(b' ');
|
buf.push(b' ');
|
||||||
quoted_or_literal_string(buf, host);
|
quoted_or_literal_string(buf, host);
|
||||||
|
|
|
||||||
|
|
@ -4,31 +4,26 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::{
|
use crate::{inbound::auth::SaslToken, queue::QueueId};
|
||||||
hash::Hash,
|
|
||||||
net::IpAddr,
|
|
||||||
sync::Arc,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
|
|
||||||
use common::{
|
use common::{
|
||||||
Inner, Server,
|
Inner, Server,
|
||||||
auth::AccessToken,
|
auth::AccessToken,
|
||||||
config::smtp::auth::VerifyStrategy,
|
config::smtp::auth::VerifyStrategy,
|
||||||
listener::{ServerInstance, asn::AsnGeoLookupResult},
|
listener::{ServerInstance, asn::AsnGeoLookupResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
use directory::Directory;
|
use directory::Directory;
|
||||||
use mail_auth::{IprevOutput, SpfOutput};
|
use mail_auth::{IprevOutput, SpfOutput};
|
||||||
use smtp_proto::request::receiver::{
|
use smtp_proto::request::receiver::{
|
||||||
BdatReceiver, DataReceiver, DummyDataReceiver, DummyLineReceiver, LineReceiver, RequestReceiver,
|
BdatReceiver, DataReceiver, DummyDataReceiver, DummyLineReceiver, LineReceiver, RequestReceiver,
|
||||||
};
|
};
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use std::{
|
||||||
|
hash::Hash,
|
||||||
use crate::{
|
net::IpAddr,
|
||||||
inbound::auth::SaslToken,
|
sync::Arc,
|
||||||
queue::{DomainPart, QueueId},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
|
use utils::DomainPart;
|
||||||
|
|
||||||
pub mod params;
|
pub mod params;
|
||||||
pub mod throttle;
|
pub mod throttle;
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,7 @@ use super::{ArcSeal, AuthResult, DkimSign};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{Session, SessionAddress, State},
|
core::{Session, SessionAddress, State},
|
||||||
inbound::milter::Modification,
|
inbound::milter::Modification,
|
||||||
queue::{
|
queue::{self, Message, MessageSource, MessageWrapper, QueueEnvelope, quota::HasQueueQuota},
|
||||||
self, DomainPart, Message, MessageSource, MessageWrapper, QueueEnvelope,
|
|
||||||
quota::HasQueueQuota,
|
|
||||||
},
|
|
||||||
reporting::analysis::AnalyzeReport,
|
reporting::analysis::AnalyzeReport,
|
||||||
scripts::ScriptResult,
|
scripts::ScriptResult,
|
||||||
};
|
};
|
||||||
|
|
@ -44,7 +41,7 @@ use std::{
|
||||||
time::{Instant, SystemTime},
|
time::{Instant, SystemTime},
|
||||||
};
|
};
|
||||||
use trc::SmtpEvent;
|
use trc::SmtpEvent;
|
||||||
use utils::config::Rate;
|
use utils::{DomainPart, config::Rate};
|
||||||
|
|
||||||
impl<T: SessionStream> Session<T> {
|
impl<T: SessionStream> Session<T> {
|
||||||
pub async fn queue_message(&mut self) -> Cow<'static, [u8]> {
|
pub async fn queue_message(&mut self) -> Cow<'static, [u8]> {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{Session, SessionAddress},
|
core::{Session, SessionAddress},
|
||||||
queue::DomainPart,
|
|
||||||
scripts::ScriptResult,
|
scripts::ScriptResult,
|
||||||
};
|
};
|
||||||
use common::{config::smtp::session::Stage, listener::SessionStream, scripts::ScriptModification};
|
use common::{config::smtp::session::Stage, listener::SessionStream, scripts::ScriptModification};
|
||||||
|
|
@ -17,7 +16,7 @@ use std::{
|
||||||
time::{Duration, Instant, SystemTime},
|
time::{Duration, Instant, SystemTime},
|
||||||
};
|
};
|
||||||
use trc::SmtpEvent;
|
use trc::SmtpEvent;
|
||||||
use utils::config::Rate;
|
use utils::{DomainPart, config::Rate};
|
||||||
|
|
||||||
impl<T: SessionStream> Session<T> {
|
impl<T: SessionStream> Session<T> {
|
||||||
pub async fn handle_mail_from(&mut self, from: MailFrom<Cow<'_, str>>) -> Result<(), ()> {
|
pub async fn handle_mail_from(&mut self, from: MailFrom<Cow<'_, str>>) -> Result<(), ()> {
|
||||||
|
|
|
||||||
|
|
@ -4,26 +4,22 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::{borrow::Cow, time::Instant};
|
use super::{Action, Error, Macros, Modification};
|
||||||
|
use crate::{
|
||||||
|
core::{Session, SessionAddress, SessionData},
|
||||||
|
inbound::{FilterResponse, milter::MilterClient},
|
||||||
|
};
|
||||||
use common::{
|
use common::{
|
||||||
DAEMON_NAME,
|
DAEMON_NAME,
|
||||||
config::smtp::session::{Milter, Stage},
|
config::smtp::session::{Milter, Stage},
|
||||||
listener::SessionStream,
|
listener::SessionStream,
|
||||||
};
|
};
|
||||||
|
|
||||||
use mail_auth::AuthenticatedMessage;
|
use mail_auth::AuthenticatedMessage;
|
||||||
use smtp_proto::{IntoString, request::parser::Rfc5321Parser};
|
use smtp_proto::{IntoString, request::parser::Rfc5321Parser};
|
||||||
|
use std::{borrow::Cow, time::Instant};
|
||||||
use tokio::io::{AsyncRead, AsyncWrite};
|
use tokio::io::{AsyncRead, AsyncWrite};
|
||||||
use trc::MilterEvent;
|
use trc::MilterEvent;
|
||||||
|
use utils::DomainPart;
|
||||||
use crate::{
|
|
||||||
core::{Session, SessionAddress, SessionData},
|
|
||||||
inbound::{FilterResponse, milter::MilterClient},
|
|
||||||
queue::DomainPart,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{Action, Error, Macros, Modification};
|
|
||||||
|
|
||||||
enum Rejection {
|
enum Rejection {
|
||||||
Action(Action),
|
Action(Action),
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,21 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use crate::{
|
||||||
|
core::{Session, SessionAddress},
|
||||||
|
scripts::ScriptResult,
|
||||||
|
};
|
||||||
use common::{
|
use common::{
|
||||||
KV_GREYLIST, config::smtp::session::Stage, listener::SessionStream, scripts::ScriptModification,
|
KV_GREYLIST, config::smtp::session::Stage, listener::SessionStream, scripts::ScriptModification,
|
||||||
};
|
};
|
||||||
|
|
||||||
use directory::backend::RcptType;
|
use directory::backend::RcptType;
|
||||||
use smtp_proto::{
|
use smtp_proto::{
|
||||||
RCPT_NOTIFY_DELAY, RCPT_NOTIFY_FAILURE, RCPT_NOTIFY_NEVER, RCPT_NOTIFY_SUCCESS, RcptTo,
|
RCPT_NOTIFY_DELAY, RCPT_NOTIFY_FAILURE, RCPT_NOTIFY_NEVER, RCPT_NOTIFY_SUCCESS, RcptTo,
|
||||||
};
|
};
|
||||||
|
use std::borrow::Cow;
|
||||||
use store::dispatch::lookup::KeyValue;
|
use store::dispatch::lookup::KeyValue;
|
||||||
use trc::{SecurityEvent, SmtpEvent};
|
use trc::{SecurityEvent, SmtpEvent};
|
||||||
|
use utils::DomainPart;
|
||||||
use crate::{
|
|
||||||
core::{Session, SessionAddress},
|
|
||||||
queue::DomainPart,
|
|
||||||
scripts::ScriptResult,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl<T: SessionStream> Session<T> {
|
impl<T: SessionStream> Session<T> {
|
||||||
pub async fn handle_rcpt_to(&mut self, to: RcptTo<Cow<'_, str>>) -> Result<(), ()> {
|
pub async fn handle_rcpt_to(&mut self, to: RcptTo<Cow<'_, str>>) -> Result<(), ()> {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ use std::{
|
||||||
};
|
};
|
||||||
use store::write::now;
|
use store::write::now;
|
||||||
use types::blob_hash::BlobHash;
|
use types::blob_hash::BlobHash;
|
||||||
|
use utils::DomainPart;
|
||||||
|
|
||||||
pub mod dsn;
|
pub mod dsn;
|
||||||
pub mod manager;
|
pub mod manager;
|
||||||
|
|
@ -460,38 +461,6 @@ impl InstantFromTimestamp for u64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DomainPart {
|
|
||||||
fn to_lowercase_domain(&self) -> String;
|
|
||||||
fn domain_part(&self) -> &str;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsRef<str>> DomainPart for T {
|
|
||||||
fn to_lowercase_domain(&self) -> String {
|
|
||||||
let address = self.as_ref();
|
|
||||||
if let Some((local, domain)) = address.rsplit_once('@') {
|
|
||||||
let mut address = String::with_capacity(address.len());
|
|
||||||
address.push_str(local);
|
|
||||||
address.push('@');
|
|
||||||
for ch in domain.chars() {
|
|
||||||
for ch in ch.to_lowercase() {
|
|
||||||
address.push(ch);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
address
|
|
||||||
} else {
|
|
||||||
address.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn domain_part(&self) -> &str {
|
|
||||||
self.as_ref()
|
|
||||||
.rsplit_once('@')
|
|
||||||
.map(|(_, d)| d)
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Error {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::{QueueEnvelope, QuotaKey, Status};
|
use super::{QueueEnvelope, QuotaKey, Status};
|
||||||
use crate::{
|
use crate::{core::throttle::NewKey, queue::MessageWrapper};
|
||||||
core::throttle::NewKey,
|
|
||||||
queue::{DomainPart, MessageWrapper},
|
|
||||||
};
|
|
||||||
use ahash::AHashSet;
|
use ahash::AHashSet;
|
||||||
use common::{Server, config::smtp::queue::QueueQuota, expr::functions::ResolveVariable};
|
use common::{Server, config::smtp::queue::QueueQuota, expr::functions::ResolveVariable};
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
|
@ -17,6 +14,7 @@ use store::{
|
||||||
write::{BatchBuilder, QueueClass, ValueClass},
|
write::{BatchBuilder, QueueClass, ValueClass},
|
||||||
};
|
};
|
||||||
use trc::QueueEvent;
|
use trc::QueueEvent;
|
||||||
|
use utils::DomainPart;
|
||||||
|
|
||||||
pub trait HasQueueQuota: Sync + Send {
|
pub trait HasQueueQuota: Sync + Send {
|
||||||
fn has_quota(&self, message: &mut MessageWrapper) -> impl Future<Output = bool> + Send;
|
fn has_quota(&self, message: &mut MessageWrapper) -> impl Future<Output = bool> + Send;
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::queue::manager::{LockedMessage, Queue};
|
use crate::queue::manager::{LockedMessage, Queue};
|
||||||
use crate::queue::{
|
use crate::queue::{
|
||||||
DomainPart, FROM_AUTHENTICATED, FROM_AUTOGENERATED, FROM_DSN, FROM_REPORT,
|
FROM_AUTHENTICATED, FROM_AUTOGENERATED, FROM_DSN, FROM_REPORT, FROM_UNAUTHENTICATED,
|
||||||
FROM_UNAUTHENTICATED, FROM_UNAUTHENTICATED_DMARC, MessageWrapper,
|
FROM_UNAUTHENTICATED_DMARC, MessageWrapper,
|
||||||
};
|
};
|
||||||
use common::config::smtp::queue::QueueName;
|
use common::config::smtp::queue::QueueName;
|
||||||
use common::ipc::QueueEvent;
|
use common::ipc::QueueEvent;
|
||||||
|
|
@ -28,6 +28,7 @@ use store::write::{
|
||||||
use store::{Deserialize, IterateParams, Serialize, SerializeInfallible, U64_LEN, ValueKey};
|
use store::{Deserialize, IterateParams, Serialize, SerializeInfallible, U64_LEN, ValueKey};
|
||||||
use trc::{AddContext, ServerEvent};
|
use trc::{AddContext, ServerEvent};
|
||||||
use types::blob_hash::BlobHash;
|
use types::blob_hash::BlobHash;
|
||||||
|
use utils::DomainPart;
|
||||||
|
|
||||||
pub const LOCK_EXPIRY: u64 = 10 * 60; // 10 minutes
|
pub const LOCK_EXPIRY: u64 = 10 * 60; // 10 minutes
|
||||||
pub const QUEUE_REFRESH: u64 = 5 * 60; // 5 minutes
|
pub const QUEUE_REFRESH: u64 = 5 * 60; // 5 minutes
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::{AggregateTimestamp, SerializedSize};
|
use super::{AggregateTimestamp, SerializedSize};
|
||||||
use crate::{
|
use crate::{core::Session, queue::RecipientDomain, reporting::SmtpReporting};
|
||||||
core::Session,
|
|
||||||
queue::{DomainPart, RecipientDomain},
|
|
||||||
reporting::SmtpReporting,
|
|
||||||
};
|
|
||||||
use ahash::AHashMap;
|
use ahash::AHashMap;
|
||||||
use common::{
|
use common::{
|
||||||
Server,
|
Server,
|
||||||
|
|
@ -31,7 +27,7 @@ use store::{
|
||||||
write::{AlignedBytes, Archive, Archiver, BatchBuilder, QueueClass, ReportEvent, ValueClass},
|
write::{AlignedBytes, Archive, Archiver, BatchBuilder, QueueClass, ReportEvent, ValueClass},
|
||||||
};
|
};
|
||||||
use trc::{AddContext, OutgoingReportEvent};
|
use trc::{AddContext, OutgoingReportEvent};
|
||||||
use utils::config::Rate;
|
use utils::{DomainPart, config::Rate};
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Debug,
|
Debug,
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,9 @@ use smtp_proto::{
|
||||||
MAIL_BY_NOTIFY, MAIL_BY_RETURN, MAIL_BY_TRACE, MAIL_RET_FULL, MAIL_RET_HDRS, RCPT_NOTIFY_DELAY,
|
MAIL_BY_NOTIFY, MAIL_BY_RETURN, MAIL_BY_TRACE, MAIL_RET_FULL, MAIL_RET_HDRS, RCPT_NOTIFY_DELAY,
|
||||||
RCPT_NOTIFY_FAILURE, RCPT_NOTIFY_NEVER, RCPT_NOTIFY_SUCCESS,
|
RCPT_NOTIFY_FAILURE, RCPT_NOTIFY_NEVER, RCPT_NOTIFY_SUCCESS,
|
||||||
};
|
};
|
||||||
|
use utils::DomainPart;
|
||||||
|
|
||||||
use crate::{
|
use crate::core::{SessionAddress, SessionData};
|
||||||
core::{SessionAddress, SessionData},
|
|
||||||
queue::DomainPart,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl SessionData {
|
impl SessionData {
|
||||||
pub fn apply_envelope_modification(&mut self, envelope: Envelope, value: String) {
|
pub fn apply_envelope_modification(&mut self, envelope: Envelope, value: String) {
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,50 @@ pub fn rustls_client_config(allow_invalid_certs: bool) -> ClientConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait DomainPart {
|
||||||
|
fn to_lowercase_domain(&self) -> String;
|
||||||
|
fn domain_part(&self) -> &str;
|
||||||
|
fn try_domain_part(&self) -> Option<&str>;
|
||||||
|
fn try_local_part(&self) -> Option<&str>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: AsRef<str>> DomainPart for T {
|
||||||
|
fn to_lowercase_domain(&self) -> String {
|
||||||
|
let address = self.as_ref();
|
||||||
|
if let Some((local, domain)) = address.rsplit_once('@') {
|
||||||
|
let mut address = String::with_capacity(address.len());
|
||||||
|
address.push_str(local);
|
||||||
|
address.push('@');
|
||||||
|
for ch in domain.chars() {
|
||||||
|
for ch in ch.to_lowercase() {
|
||||||
|
address.push(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
address
|
||||||
|
} else {
|
||||||
|
address.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_domain_part(&self) -> Option<&str> {
|
||||||
|
self.as_ref().rsplit_once('@').map(|(_, d)| d)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn try_local_part(&self) -> Option<&str> {
|
||||||
|
self.as_ref().rsplit_once('@').map(|(l, _)| l)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn domain_part(&self) -> &str {
|
||||||
|
self.as_ref()
|
||||||
|
.rsplit_once('@')
|
||||||
|
.map(|(_, d)| d)
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct DummyVerifier;
|
struct DummyVerifier;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue