1use alloc::borrow::{Cow, ToOwned};
2use alloc::string::{String, ToString};
3use alloc::vec::Vec;
4use core::borrow::Borrow;
5use core::fmt;
6use core::mem;
7use core::ops::Deref;
8use core::str::FromStr;
9
10#[cfg(feature = "serde")]
11use serde::{Deserialize, Serialize};
12
13use crate::{domain_check, node_check, resource_check};
14use crate::{BareJid, Error, Jid};
15
16macro_rules! def_part_parse_doc {
17 ($name:ident, $other:ident, $more:expr) => {
18 concat!(
19 "Parse a [`",
20 stringify!($name),
21 "`] from a `",
22 stringify!($other),
23 "`, copying its contents.\n",
24 "\n",
25 "If the given `",
26 stringify!($other),
27 "` does not conform to the restrictions imposed by `",
28 stringify!($name),
29 "`, an error is returned.\n",
30 $more,
31 )
32 };
33}
34
35macro_rules! def_part_into_inner_doc {
36 ($name:ident, $other:ident, $more:expr) => {
37 concat!(
38 "Consume the `",
39 stringify!($name),
40 "` and return the inner `",
41 stringify!($other),
42 "`.\n",
43 $more,
44 )
45 };
46}
47
48#[cfg(feature = "serde")]
49#[derive(Deserialize)]
50struct NodeDeserializer<'a>(Cow<'a, str>);
51
52#[cfg(feature = "serde")]
53impl TryFrom<NodeDeserializer<'_>> for NodePart {
54 type Error = Error;
55
56 fn try_from(deserializer: NodeDeserializer) -> Result<NodePart, Self::Error> {
57 Ok(NodePart::new(&deserializer.0)?.into_owned())
58 }
59}
60
61#[cfg(feature = "serde")]
62#[derive(Deserialize)]
63struct DomainDeserializer<'a>(Cow<'a, str>);
64
65#[cfg(feature = "serde")]
66impl TryFrom<DomainDeserializer<'_>> for DomainPart {
67 type Error = Error;
68
69 fn try_from(deserializer: DomainDeserializer) -> Result<DomainPart, Self::Error> {
70 Ok(DomainPart::new(&deserializer.0)?.into_owned())
71 }
72}
73
74#[cfg(feature = "serde")]
75#[derive(Deserialize)]
76struct ResourceDeserializer<'a>(Cow<'a, str>);
77
78#[cfg(feature = "serde")]
79impl TryFrom<ResourceDeserializer<'_>> for ResourcePart {
80 type Error = Error;
81
82 fn try_from(deserializer: ResourceDeserializer) -> Result<ResourcePart, Self::Error> {
83 Ok(ResourcePart::new(&deserializer.0)?.into_owned())
84 }
85}
86
87macro_rules! def_part_types {
88 (
89 $(#[$mainmeta:meta])*
90 pub struct $name:ident(String) use $check_fn:ident();
91
92 $(#[$refmeta:meta])*
93 pub struct ref $borrowed:ident(str);
94 ) => {
95 $(#[$mainmeta])*
96 #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
97 #[repr(transparent)]
98 pub struct $name(pub(crate) String);
99
100 impl $name {
101 #[doc = def_part_parse_doc!($name, str, "Depending on whether the contents are changed by normalisation operations, this function either returns a copy or a reference to the original data.")]
102 #[allow(clippy::new_ret_no_self)]
103 pub fn new(s: &str) -> Result<Cow<'_, $borrowed>, Error> {
104 let part = $check_fn(s)?;
105 match part {
106 Cow::Borrowed(v) => Ok(Cow::Borrowed($borrowed::from_str_unchecked(v))),
107 Cow::Owned(v) => Ok(Cow::Owned(Self(v))),
108 }
109 }
110
111 #[doc = def_part_into_inner_doc!($name, String, "")]
112 pub fn into_inner(self) -> String {
113 self.0
114 }
115 }
116
117 impl FromStr for $name {
118 type Err = Error;
119
120 fn from_str(s: &str) -> Result<Self, Error> {
121 Ok(Self::new(s)?.into_owned())
122 }
123 }
124
125 impl fmt::Display for $name {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 <$borrowed as fmt::Display>::fmt(Borrow::<$borrowed>::borrow(self), f)
128 }
129 }
130
131 impl Deref for $name {
132 type Target = $borrowed;
133
134 fn deref(&self) -> &Self::Target {
135 Borrow::<$borrowed>::borrow(self)
136 }
137 }
138
139 impl AsRef<$borrowed> for $name {
140 fn as_ref(&self) -> &$borrowed {
141 Borrow::<$borrowed>::borrow(self)
142 }
143 }
144
145 impl AsRef<String> for $name {
146 fn as_ref(&self) -> &String {
147 &self.0
148 }
149 }
150
151 impl Borrow<$borrowed> for $name {
152 fn borrow(&self) -> &$borrowed {
153 $borrowed::from_str_unchecked(self.0.as_str())
154 }
155 }
156
157 impl Borrow<String> for $name {
159 fn borrow(&self) -> &String {
160 &self.0
161 }
162 }
163
164 impl Borrow<str> for $name {
166 fn borrow(&self) -> &str {
167 self.0.as_str()
168 }
169 }
170
171 impl<'x> TryFrom<&'x str> for $name {
172 type Error = Error;
173
174 fn try_from(s: &str) -> Result<Self, Error> {
175 Self::from_str(s)
176 }
177 }
178
179 impl From<&$borrowed> for $name {
180 fn from(other: &$borrowed) -> Self {
181 other.to_owned()
182 }
183 }
184
185 impl<'x> From<Cow<'x, $borrowed>> for $name {
186 fn from(other: Cow<'x, $borrowed>) -> Self {
187 other.into_owned()
188 }
189 }
190
191 $(#[$refmeta])*
192 #[repr(transparent)]
193 #[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
194 pub struct $borrowed(pub(crate) str);
195
196 impl $borrowed {
197 pub(crate) fn from_str_unchecked(s: &str) -> &Self {
198 unsafe { mem::transmute(s) }
201 }
202
203 pub fn as_str(&self) -> &str {
205 &self.0
206 }
207 }
208
209 impl Deref for $borrowed {
210 type Target = str;
211
212 fn deref(&self) -> &Self::Target {
213 &self.0
214 }
215 }
216
217 impl ToOwned for $borrowed {
218 type Owned = $name;
219
220 fn to_owned(&self) -> Self::Owned {
221 $name(self.0.to_string())
222 }
223 }
224
225 impl AsRef<str> for $borrowed {
226 fn as_ref(&self) -> &str {
227 &self.0
228 }
229 }
230
231 impl fmt::Display for $borrowed {
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233 write!(f, "{}", &self.0)
234 }
235 }
236 }
237}
238
239def_part_types! {
240 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
246 #[cfg_attr(feature = "serde", serde(try_from = "NodeDeserializer"))]
247 pub struct NodePart(String) use node_check();
248
249 #[cfg_attr(feature = "serde", derive(Serialize))]
253 pub struct ref NodeRef(str);
254}
255
256def_part_types! {
257 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
261 #[cfg_attr(feature = "serde", serde(try_from = "DomainDeserializer"))]
262 pub struct DomainPart(String) use domain_check();
263
264 #[cfg_attr(feature = "serde", derive(Serialize))]
268 pub struct ref DomainRef(str);
269}
270
271def_part_types! {
272 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
275 #[cfg_attr(feature = "serde", serde(try_from = "ResourceDeserializer"))]
276 pub struct ResourcePart(String) use resource_check();
277
278 #[cfg_attr(feature = "serde", derive(Serialize))]
283 pub struct ref ResourceRef(str);
284}
285
286impl DomainRef {
287 pub fn with_node(&self, node: &NodeRef) -> BareJid {
290 BareJid::from_parts(Some(node), self)
291 }
292}
293
294impl From<DomainPart> for BareJid {
295 fn from(other: DomainPart) -> Self {
296 BareJid {
297 inner: other.into(),
298 }
299 }
300}
301
302impl From<DomainPart> for Jid {
303 fn from(other: DomainPart) -> Self {
304 Jid {
305 normalized: other.0,
306 at: None,
307 slash: None,
308 }
309 }
310}
311
312impl<'x> From<&'x DomainRef> for BareJid {
313 fn from(other: &'x DomainRef) -> Self {
314 Self::from_parts(None, other)
315 }
316}
317
318impl NodeRef {
319 pub fn with_domain(&self, domain: &DomainRef) -> BareJid {
322 BareJid::from_parts(Some(self), domain)
323 }
324
325 pub fn unescape(&self) -> Result<Cow<'_, str>, Error> {
327 fn hex_to_char(bytes: [u8; 2]) -> Result<u8, ()> {
328 Ok(match &[bytes[0], bytes[1]] {
329 b"20" => b' ',
330 b"22" => b'"',
331 b"26" => b'&',
332 b"27" => b'\'',
333 b"2f" => b'/',
334 b"3a" => b':',
335 b"3c" => b'<',
336 b"3e" => b'>',
337 b"40" => b'@',
338 b"5c" => b'\\',
339 _ => return Err(()),
340 })
341 }
342
343 let bytes = self.0.as_bytes();
344 let mut iter = memchr::memchr_iter(b'\\', bytes);
345 Ok(match iter.next() {
346 None => Cow::Borrowed(self),
348
349 Some(mut index) => {
350 let mut acc = Vec::from(&bytes[..index]);
351 match hex_to_char([bytes[index + 1], bytes[index + 2]]) {
352 Ok(char) => acc.push(char),
353 Err(()) => index -= 3,
355 }
356 loop {
357 match iter.next() {
358 None => {
359 acc.extend_from_slice(&bytes[index + 3..]);
360 break;
361 }
362 Some(index2) => {
363 acc.extend_from_slice(&bytes[index + 3..index2]);
364 index = index2;
365 match hex_to_char([bytes[index + 1], bytes[index + 2]]) {
366 Ok(char) => acc.push(char),
367 Err(()) => index -= 3,
369 }
370 }
371 }
372 }
373 if acc.starts_with(b" ") || acc.ends_with(b" ") {
374 return Err(Error::NodePrep);
375 }
376
377 Cow::Owned(String::try_from(acc).unwrap())
379 }
380 })
381 }
382}
383
384#[cfg(test)]
385mod tests {
386 use super::*;
387
388 #[test]
389 fn nodepart_comparison() {
390 let n1 = NodePart::new("foo").unwrap();
391 let n2 = NodePart::new("bar").unwrap();
392 let n3 = NodePart::new("foo").unwrap();
393 assert_eq!(n1, n3);
394 assert_ne!(n1, n2);
395 }
396
397 #[cfg(feature = "serde")]
398 #[test]
399 fn nodepart_serde() {
400 serde_test::assert_de_tokens(
401 &NodePart(String::from("test")),
402 &[
403 serde_test::Token::TupleStruct {
404 name: "NodePart",
405 len: 1,
406 },
407 serde_test::Token::BorrowedStr("test"),
408 serde_test::Token::TupleStructEnd,
409 ],
410 );
411
412 serde_test::assert_de_tokens(
413 &NodePart(String::from("test")),
414 &[
415 serde_test::Token::TupleStruct {
416 name: "NodePart",
417 len: 1,
418 },
419 serde_test::Token::String("test"),
420 serde_test::Token::TupleStructEnd,
421 ],
422 );
423
424 serde_test::assert_de_tokens_error::<NodePart>(
425 &[
426 serde_test::Token::TupleStruct {
427 name: "NodePart",
428 len: 1,
429 },
430 serde_test::Token::BorrowedStr("invalid@domain"),
431 serde_test::Token::TupleStructEnd,
432 ],
433 "localpart doesn’t pass nodeprep validation",
434 );
435 }
436
437 #[cfg(feature = "serde")]
438 #[test]
439 fn domainpart_serde() {
440 serde_test::assert_de_tokens(
441 &DomainPart(String::from("[::1]")),
442 &[
443 serde_test::Token::TupleStruct {
444 name: "DomainPart",
445 len: 1,
446 },
447 serde_test::Token::BorrowedStr("[::1]"),
448 serde_test::Token::TupleStructEnd,
449 ],
450 );
451
452 serde_test::assert_de_tokens(
453 &DomainPart(String::from("[::1]")),
454 &[
455 serde_test::Token::TupleStruct {
456 name: "DomainPart",
457 len: 1,
458 },
459 serde_test::Token::String("[::1]"),
460 serde_test::Token::TupleStructEnd,
461 ],
462 );
463
464 serde_test::assert_de_tokens(
465 &DomainPart(String::from("domain.example")),
466 &[
467 serde_test::Token::TupleStruct {
468 name: "DomainPart",
469 len: 1,
470 },
471 serde_test::Token::BorrowedStr("domain.example"),
472 serde_test::Token::TupleStructEnd,
473 ],
474 );
475
476 serde_test::assert_de_tokens_error::<DomainPart>(
477 &[
478 serde_test::Token::TupleStruct {
479 name: "DomainPart",
480 len: 1,
481 },
482 serde_test::Token::BorrowedStr("invalid@domain"),
483 serde_test::Token::TupleStructEnd,
484 ],
485 "domain doesn’t pass idna validation",
486 );
487 }
488
489 #[cfg(feature = "serde")]
490 #[test]
491 fn resourcepart_serde() {
492 serde_test::assert_de_tokens(
493 &ResourcePart(String::from("test")),
494 &[
495 serde_test::Token::TupleStruct {
496 name: "ResourcePart",
497 len: 1,
498 },
499 serde_test::Token::BorrowedStr("test"),
500 serde_test::Token::TupleStructEnd,
501 ],
502 );
503
504 serde_test::assert_de_tokens(
505 &ResourcePart(String::from("test")),
506 &[
507 serde_test::Token::TupleStruct {
508 name: "ResourcePart",
509 len: 1,
510 },
511 serde_test::Token::String("test"),
512 serde_test::Token::TupleStructEnd,
513 ],
514 );
515
516 serde_test::assert_de_tokens_error::<ResourcePart>(
517 &[
518 serde_test::Token::TupleStruct {
519 name: "ResourcePart",
520 len: 1,
521 },
522 serde_test::Token::BorrowedStr("🤖"),
523 serde_test::Token::TupleStructEnd,
524 ],
525 "resource doesn’t pass resourceprep validation",
526 );
527 }
528
529 #[test]
530 fn unescape() {
531 let node = NodePart::new("foo\\40bar").unwrap();
532 assert_eq!(node.unescape().unwrap(), "foo@bar");
533
534 let node = NodePart::new("\\22\\26\\27\\2f\\20\\3a\\3c\\3e\\40\\5c").unwrap();
535 assert_eq!(node.unescape().unwrap(), "\"&'/ :<>@\\");
536
537 let node = NodePart::new("\\20foo").unwrap();
538 node.unescape().unwrap_err();
539
540 let node = NodePart::new("foo\\20").unwrap();
541 node.unescape().unwrap_err();
542
543 let jid = BareJid::new("tréville\\40musketeers.lit@smtp.gascon.fr").unwrap();
544 let node = jid.node().unwrap();
545 assert_eq!(node.unescape().unwrap(), "tréville@musketeers.lit");
546
547 let data = [
549 ("space cadet@example.com", "space\\20cadet@example.com"),
550 (
551 "call me \"ishmael\"@example.com",
552 "call\\20me\\20\\22ishmael\\22@example.com",
553 ),
554 ("at&t guy@example.com", "at\\26t\\20guy@example.com"),
555 ("d'artagnan@example.com", "d\\27artagnan@example.com"),
556 ("/.fanboy@example.com", "\\2f.fanboy@example.com"),
557 ("::foo::@example.com", "\\3a\\3afoo\\3a\\3a@example.com"),
558 ("<foo>@example.com", "\\3cfoo\\3e@example.com"),
559 ("user@host@example.com", "user\\40host@example.com"),
560 ("c:\\net@example.com", "c\\3a\\net@example.com"),
561 ("c:\\\\net@example.com", "c\\3a\\\\net@example.com"),
562 (
563 "c:\\cool stuff@example.com",
564 "c\\3a\\cool\\20stuff@example.com",
565 ),
566 ("c:\\5commas@example.com", "c\\3a\\5c5commas@example.com"),
567 ];
568 for (unescaped, escaped) in data {
569 let jid = BareJid::new(escaped).unwrap();
570 let node = jid.node().unwrap();
571 assert_eq!(
572 alloc::format!("{}@example.com", node.unescape().unwrap()),
573 unescaped
574 );
575 }
576 }
577}