use bytes::Bytes;
use crate::error::HeaderError;
use crate::headers::address::Address;
use crate::headers::grammar;
use crate::message::{Headers, TypedHeader};
use crate::name::HeaderName;
use crate::uri::Scheme;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum IgnoredIdentityReason {
UnexpectedScheme,
DuplicateSip,
DuplicateSips,
DuplicateTel,
SipAfterSips,
SipsAfterSip,
}
#[derive(Debug, Clone)]
pub struct IgnoredIdentity {
index: usize,
address: Address,
reason: IgnoredIdentityReason,
}
impl IgnoredIdentity {
#[must_use]
pub fn index(&self) -> usize {
self.index
}
#[must_use]
pub fn address(&self) -> &Address {
&self.address
}
#[must_use]
pub fn reason(&self) -> IgnoredIdentityReason {
self.reason
}
}
fn validate_common(address: &Address, header: &'static str) -> Result<(), HeaderError> {
if !address.params.is_empty() {
return Err(HeaderError::Syntax { header });
}
if let Some(display_name) = &address.display_name {
let valid_ascii = display_name.iter().all(|byte| {
byte.is_ascii_graphic() || matches!(byte, b' ' | b'\t') || !byte.is_ascii()
});
if !valid_ascii || std::str::from_utf8(display_name).is_err() {
return Err(HeaderError::Syntax { header });
}
}
Ok(())
}
fn validate_address(address: &Address, header: &'static str) -> Result<(), HeaderError> {
validate_common(address, header)?;
if !matches!(
address.uri.scheme(),
Scheme::Sip | Scheme::Sips | Scheme::Tel
) {
return Err(HeaderError::Syntax { header });
}
Ok(())
}
fn parse_received_value(value: &[u8], header: &'static str) -> Result<Address, HeaderError> {
let address = Address::parse(value, header)?;
validate_common(&address, header)?;
Ok(address)
}
fn parse_value(value: &[u8], header: &'static str) -> Result<Address, HeaderError> {
let address = parse_received_value(value, header)?;
validate_address(&address, header)?;
Ok(address)
}
fn parse_list(value: &[u8], header: &'static str) -> Result<Vec<Address>, HeaderError> {
grammar::split_list(value, header)?
.into_iter()
.map(|part| parse_value(part, header))
.collect()
}
fn validate_list<'a>(
values: impl IntoIterator<Item = &'a Address>,
header: &'static str,
) -> Result<(), HeaderError> {
let mut values = values.into_iter();
let first = values.next().ok_or(HeaderError::Syntax { header })?;
let second = values.next();
if values.next().is_some() {
return Err(HeaderError::Syntax { header });
}
match second {
None => Ok(()),
Some(second)
if is_sip_family(first) != is_sip_family(second) && is_tel(first) != is_tel(second) =>
{
Ok(())
}
Some(_) => Err(HeaderError::Syntax { header }),
}
}
#[must_use]
fn is_sip_family(address: &Address) -> bool {
matches!(address.uri.scheme(), Scheme::Sip | Scheme::Sips)
}
#[must_use]
fn is_tel(address: &Address) -> bool {
matches!(address.uri.scheme(), Scheme::Tel)
}
fn serialize(address: &Address) -> Bytes {
let mut out = Vec::new();
if let Some(display_name) = &address.display_name {
out.push(b'"');
for &byte in display_name {
if matches!(byte, b'"' | b'\\') {
out.push(b'\\');
}
out.push(byte);
}
out.extend_from_slice(b"\" ");
}
out.push(b'<');
address.uri.write_to(&mut out);
out.push(b'>');
Bytes::from(out)
}
#[derive(Default)]
struct SeenSchemes {
sip: bool,
sips: bool,
tel: bool,
}
impl SeenSchemes {
fn classify(&mut self, address: &Address) -> Option<IgnoredIdentityReason> {
match address.uri.scheme() {
Scheme::Sip if self.sip => Some(IgnoredIdentityReason::DuplicateSip),
Scheme::Sip => {
self.sip = true;
self.sips.then_some(IgnoredIdentityReason::SipAfterSips)
}
Scheme::Sips if self.sips => Some(IgnoredIdentityReason::DuplicateSips),
Scheme::Sips => {
self.sips = true;
self.sip.then_some(IgnoredIdentityReason::SipsAfterSip)
}
Scheme::Tel if self.tel => Some(IgnoredIdentityReason::DuplicateTel),
Scheme::Tel => {
self.tel = true;
None
}
Scheme::Other(_) => Some(IgnoredIdentityReason::UnexpectedScheme),
}
}
}
struct ReceivedIdentityList {
values: Vec<Address>,
ignored: Vec<IgnoredIdentity>,
}
fn receive_list(
headers: &Headers,
name: &HeaderName,
label: &'static str,
) -> Result<Option<ReceivedIdentityList>, HeaderError> {
let mut present = false;
let mut index = 0usize;
let mut seen = SeenSchemes::default();
let mut values = Vec::new();
let mut ignored = Vec::new();
for row in headers.get_all(name) {
present = true;
let value = row.value();
for part in grammar::split_list(value.as_ref(), label)? {
let address = parse_received_value(part, label)?;
if let Some(reason) = seen.classify(&address) {
ignored.push(IgnoredIdentity {
index,
address,
reason,
});
} else {
values.push(address);
}
index += 1;
}
}
Ok(present.then_some(ReceivedIdentityList { values, ignored }))
}
macro_rules! identity_header {
(
$(#[$meta:meta])*
$type:ident, $list:ident => $variant:ident, $label:literal
) => {
$(#[$meta])*
#[derive(Debug, Clone)]
pub struct $type(Address);
impl $type {
pub fn new(address: Address) -> Result<Self, HeaderError> {
validate_address(&address, $label)?;
Ok(Self(address))
}
#[must_use]
pub fn address(&self) -> &Address {
&self.0
}
#[must_use]
pub fn to_bytes(&self) -> Bytes {
serialize(&self.0)
}
}
impl std::ops::Deref for $type {
type Target = Address;
fn deref(&self) -> &Address {
&self.0
}
}
impl TypedHeader for $type {
const NAME: HeaderName = HeaderName::$variant;
const VALIDATE_LIST: bool = true;
fn decode(value: &[u8]) -> Result<Self, HeaderError> {
parse_value(value, $label).map(Self)
}
fn decode_list(value: &[u8]) -> Result<Vec<Self>, HeaderError> {
parse_list(value, $label).map(|values| values.into_iter().map(Self).collect())
}
fn validate_list(values: &[&Self]) -> Result<(), HeaderError> {
validate_list(values.iter().map(|value| &value.0), $label)
}
}
#[derive(Debug, Clone)]
pub struct $list {
values: Vec<$type>,
ignored: Vec<IgnoredIdentity>,
}
impl $list {
pub fn new(
values: impl IntoIterator<Item = $type>,
) -> Result<Self, HeaderError> {
let values = values.into_iter().collect::<Vec<_>>();
validate_list(values.iter().map(|value| &value.0), $label)?;
Ok(Self {
values,
ignored: Vec::new(),
})
}
pub fn from_headers(headers: &Headers) -> Result<Option<Self>, HeaderError> {
receive_list(headers, &HeaderName::$variant, $label).map(|received| {
received.map(|received| Self {
values: received.values.into_iter().map($type).collect(),
ignored: received.ignored,
})
})
}
#[must_use]
pub fn values(&self) -> &[$type] {
&self.values
}
#[must_use]
pub fn ignored(&self) -> &[IgnoredIdentity] {
&self.ignored
}
#[must_use]
pub fn requires_rewrite(&self) -> bool {
!self.ignored.is_empty()
}
#[must_use]
pub fn into_parts(self) -> (Vec<$type>, Vec<IgnoredIdentity>) {
(self.values, self.ignored)
}
#[must_use]
pub fn to_bytes(&self) -> Option<Bytes> {
if self.values.is_empty() {
return None;
}
let mut out = Vec::new();
for (position, value) in self.values.iter().enumerate() {
if position != 0 {
out.extend_from_slice(b", ");
}
out.extend_from_slice(&value.to_bytes());
}
Some(Bytes::from(out))
}
}
};
}
identity_header!(
PAssertedIdentity, PAssertedIdentityList => PAssertedIdentity, "P-Asserted-Identity"
);
identity_header!(
PPreferredIdentity, PPreferredIdentityList => PPreferredIdentity, "P-Preferred-Identity"
);