sipx_sip/headers/
privacy.rs1use std::collections::HashSet;
7
8use bytes::Bytes;
9
10use crate::error::HeaderError;
11use crate::headers::grammar::{self, is_token_char, trim};
12use crate::message::TypedHeader;
13use crate::name::HeaderName;
14
15const LABEL: &str = "Privacy";
16
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
23pub enum PrivacyValue {
24 User,
26 Header,
28 Session,
30 None,
32 Critical,
34 Id,
36 History,
38 Extension(Vec<u8>),
40}
41
42impl PrivacyValue {
43 #[must_use]
45 pub fn as_bytes(&self) -> &[u8] {
46 match self {
47 Self::User => b"user",
48 Self::Header => b"header",
49 Self::Session => b"session",
50 Self::None => b"none",
51 Self::Critical => b"critical",
52 Self::Id => b"id",
53 Self::History => b"history",
54 Self::Extension(value) => value,
55 }
56 }
57
58 fn decode(value: &[u8]) -> Result<Self, HeaderError> {
59 if value.is_empty() || !value.iter().all(|&octet| is_token_char(octet)) {
60 return Err(syntax());
61 }
62 Ok(known(value).unwrap_or_else(|| Self::Extension(value.to_vec())))
63 }
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
71pub struct Privacy(PrivacyValue);
72
73impl Privacy {
74 pub fn new(value: PrivacyValue) -> Result<Self, HeaderError> {
76 validate_value(&value)?;
77 Ok(Self(value))
78 }
79
80 #[must_use]
82 pub fn value(&self) -> &PrivacyValue {
83 &self.0
84 }
85
86 #[must_use]
88 pub fn is(&self, wanted: &PrivacyValue) -> bool {
89 self.0.as_bytes().eq_ignore_ascii_case(wanted.as_bytes())
90 }
91
92 #[must_use]
94 pub fn to_bytes(&self) -> Bytes {
95 Bytes::copy_from_slice(self.0.as_bytes())
96 }
97}
98
99#[derive(Debug, Clone, PartialEq, Eq)]
101pub struct PrivacyList(Vec<Privacy>);
102
103impl PrivacyList {
104 pub fn new(values: impl IntoIterator<Item = PrivacyValue>) -> Result<Self, HeaderError> {
106 let values = values
107 .into_iter()
108 .map(Privacy::new)
109 .collect::<Result<Vec<_>, _>>()?;
110 validate(values.iter().map(|privacy| &privacy.0))?;
111 Ok(Self(values))
112 }
113
114 #[must_use]
116 pub fn values(&self) -> &[Privacy] {
117 &self.0
118 }
119
120 #[must_use]
122 pub fn contains(&self, wanted: &PrivacyValue) -> bool {
123 self.0.iter().any(|value| value.is(wanted))
124 }
125
126 #[must_use]
128 pub fn to_bytes(&self) -> Bytes {
129 let mut out = Vec::new();
130 for (position, value) in self.0.iter().enumerate() {
131 if position != 0 {
132 out.push(b',');
133 }
134 out.extend_from_slice(value.0.as_bytes());
135 }
136 Bytes::from(out)
137 }
138}
139
140impl TypedHeader for Privacy {
141 const NAME: HeaderName = HeaderName::Privacy;
142 const VALIDATE_LIST: bool = true;
143
144 fn decode(value: &[u8]) -> Result<Self, HeaderError> {
145 PrivacyValue::decode(trim(value)).map(Self)
146 }
147
148 fn decode_list(value: &[u8]) -> Result<Vec<Self>, HeaderError> {
149 grammar::split_list(value, LABEL)?
150 .into_iter()
151 .map(Self::decode)
152 .collect()
153 }
154
155 fn validate_list(values: &[&Self]) -> Result<(), HeaderError> {
156 validate(values.iter().map(|privacy| &privacy.0))
157 }
158}
159
160fn validate_value(value: &PrivacyValue) -> Result<(), HeaderError> {
161 let token = value.as_bytes();
162 if token.is_empty() || !token.iter().all(|&octet| is_token_char(octet)) {
163 return Err(syntax());
164 }
165 if matches!(value, PrivacyValue::Extension(_)) && known(token).is_some() {
166 return Err(syntax());
167 }
168 Ok(())
169}
170
171fn validate<'a>(values: impl IntoIterator<Item = &'a PrivacyValue>) -> Result<(), HeaderError> {
172 let values = values.into_iter().collect::<Vec<_>>();
173 if values.is_empty() {
174 return Err(syntax());
175 }
176
177 let mut seen = HashSet::with_capacity(values.len());
178 let mut none = false;
179 let mut critical = None;
180 for (position, value) in values.iter().enumerate() {
181 validate_value(value)?;
182 let token = value.as_bytes();
183 if !seen.insert(token.to_ascii_lowercase()) {
184 return Err(syntax());
185 }
186 match *value {
187 PrivacyValue::None => none = true,
188 PrivacyValue::Critical => critical = Some(position),
189 _ => {}
190 }
191 }
192
193 if none && values.len() != 1 {
194 return Err(syntax());
195 }
196 if let Some(position) = critical
197 && (position == 0 || position + 1 != values.len())
198 {
199 return Err(syntax());
200 }
201 Ok(())
202}
203
204fn known(value: &[u8]) -> Option<PrivacyValue> {
205 if value.eq_ignore_ascii_case(b"user") {
206 Some(PrivacyValue::User)
207 } else if value.eq_ignore_ascii_case(b"header") {
208 Some(PrivacyValue::Header)
209 } else if value.eq_ignore_ascii_case(b"session") {
210 Some(PrivacyValue::Session)
211 } else if value.eq_ignore_ascii_case(b"none") {
212 Some(PrivacyValue::None)
213 } else if value.eq_ignore_ascii_case(b"critical") {
214 Some(PrivacyValue::Critical)
215 } else if value.eq_ignore_ascii_case(b"id") {
216 Some(PrivacyValue::Id)
217 } else if value.eq_ignore_ascii_case(b"history") {
218 Some(PrivacyValue::History)
219 } else {
220 None
221 }
222}
223
224fn syntax() -> HeaderError {
225 HeaderError::Syntax { header: LABEL }
226}