deprecated.rs 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // Copyright 2013-2014 The rust-url developers.
  2. //
  3. // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
  4. // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
  5. // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
  6. // option. This file may not be copied, modified, or distributed
  7. // except according to those terms.
  8. //! Deprecated API for [*Unicode IDNA Compatibility Processing*
  9. //! (Unicode Technical Standard #46)](http://www.unicode.org/reports/tr46/)
  10. #![allow(deprecated)]
  11. use alloc::borrow::Cow;
  12. use alloc::string::String;
  13. use crate::uts46::*;
  14. use crate::Errors;
  15. /// Performs preprocessing equivalent to UTS 46 transitional processing
  16. /// if `transitional` is `true`. If `transitional` is `false`, merely
  17. /// lets the input pass through as-is (for call site convenience).
  18. ///
  19. /// The output of this function is to be passed to [`Uts46::process`].
  20. fn map_transitional(domain: &str, transitional: bool) -> Cow<'_, str> {
  21. if !transitional {
  22. return Cow::Borrowed(domain);
  23. }
  24. let mut chars = domain.chars();
  25. loop {
  26. let prev = chars.clone();
  27. if let Some(c) = chars.next() {
  28. match c {
  29. 'ß' | 'ẞ' | 'ς' | '\u{200C}' | '\u{200D}' => {
  30. let mut s = String::with_capacity(domain.len());
  31. let tail = prev.as_str();
  32. let head = &domain[..domain.len() - tail.len()];
  33. s.push_str(head);
  34. for c in tail.chars() {
  35. match c {
  36. 'ß' | 'ẞ' => {
  37. s.push_str("ss");
  38. }
  39. 'ς' => {
  40. s.push('σ');
  41. }
  42. '\u{200C}' | '\u{200D}' => {}
  43. _ => {
  44. s.push(c);
  45. }
  46. }
  47. }
  48. return Cow::Owned(s);
  49. }
  50. _ => {}
  51. }
  52. } else {
  53. break;
  54. }
  55. }
  56. Cow::Borrowed(domain)
  57. }
  58. /// Deprecated. Use the crate-top-level functions or [`Uts46`].
  59. #[derive(Default)]
  60. #[deprecated]
  61. pub struct Idna {
  62. config: Config,
  63. }
  64. impl Idna {
  65. pub fn new(config: Config) -> Self {
  66. Self { config }
  67. }
  68. /// [UTS 46 ToASCII](http://www.unicode.org/reports/tr46/#ToASCII)
  69. #[allow(clippy::wrong_self_convention)] // Retain old weirdness in deprecated API
  70. pub fn to_ascii(&mut self, domain: &str, out: &mut String) -> Result<(), Errors> {
  71. let mapped = map_transitional(domain, self.config.transitional_processing);
  72. match Uts46::new().process(
  73. mapped.as_bytes(),
  74. self.config.deny_list(),
  75. self.config.hyphens(),
  76. ErrorPolicy::FailFast, // Old code did not appear to expect the output to be useful in the error case.
  77. |_, _, _| false,
  78. out,
  79. None,
  80. ) {
  81. Ok(ProcessingSuccess::Passthrough) => {
  82. if self.config.verify_dns_length && !verify_dns_length(&mapped, true) {
  83. return Err(crate::Errors::default());
  84. }
  85. out.push_str(&mapped);
  86. Ok(())
  87. }
  88. Ok(ProcessingSuccess::WroteToSink) => {
  89. if self.config.verify_dns_length && !verify_dns_length(out, true) {
  90. return Err(crate::Errors::default());
  91. }
  92. Ok(())
  93. }
  94. Err(ProcessingError::ValidityError) => Err(crate::Errors::default()),
  95. Err(ProcessingError::SinkError) => unreachable!(),
  96. }
  97. }
  98. /// [UTS 46 ToUnicode](http://www.unicode.org/reports/tr46/#ToUnicode)
  99. #[allow(clippy::wrong_self_convention)] // Retain old weirdness in deprecated API
  100. pub fn to_unicode(&mut self, domain: &str, out: &mut String) -> Result<(), Errors> {
  101. let mapped = map_transitional(domain, self.config.transitional_processing);
  102. match Uts46::new().process(
  103. mapped.as_bytes(),
  104. self.config.deny_list(),
  105. self.config.hyphens(),
  106. ErrorPolicy::MarkErrors,
  107. |_, _, _| true,
  108. out,
  109. None,
  110. ) {
  111. Ok(ProcessingSuccess::Passthrough) => {
  112. out.push_str(&mapped);
  113. Ok(())
  114. }
  115. Ok(ProcessingSuccess::WroteToSink) => Ok(()),
  116. Err(ProcessingError::ValidityError) => Err(crate::Errors::default()),
  117. Err(ProcessingError::SinkError) => unreachable!(),
  118. }
  119. }
  120. }
  121. /// Deprecated configuration API.
  122. #[derive(Clone, Copy)]
  123. #[must_use]
  124. #[deprecated]
  125. pub struct Config {
  126. use_std3_ascii_rules: bool,
  127. transitional_processing: bool,
  128. verify_dns_length: bool,
  129. check_hyphens: bool,
  130. }
  131. /// The defaults are that of _beStrict=false_ in the [WHATWG URL Standard](https://url.spec.whatwg.org/#idna)
  132. impl Default for Config {
  133. fn default() -> Self {
  134. Self {
  135. use_std3_ascii_rules: false,
  136. transitional_processing: false,
  137. check_hyphens: false,
  138. // Only use for to_ascii, not to_unicode
  139. verify_dns_length: false,
  140. }
  141. }
  142. }
  143. impl Config {
  144. /// Whether to enforce STD3 or WHATWG URL Standard ASCII deny list.
  145. ///
  146. /// `true` for STD3, `false` for no deny list.
  147. ///
  148. /// Note that `true` rejects pseudo-hosts used by various TXT record-based protocols.
  149. #[inline]
  150. pub fn use_std3_ascii_rules(mut self, value: bool) -> Self {
  151. self.use_std3_ascii_rules = value;
  152. self
  153. }
  154. /// Whether to enable (deprecated) transitional processing.
  155. ///
  156. /// Note that Firefox, Safari, and Chrome do not use transitional
  157. /// processing.
  158. #[inline]
  159. pub fn transitional_processing(mut self, value: bool) -> Self {
  160. self.transitional_processing = value;
  161. self
  162. }
  163. /// Whether the _VerifyDNSLength_ operation should be performed
  164. /// by `to_ascii`.
  165. ///
  166. /// For compatibility with previous behavior, even when set to `true`,
  167. /// the trailing root label dot is allowed contrary to the spec.
  168. #[inline]
  169. pub fn verify_dns_length(mut self, value: bool) -> Self {
  170. self.verify_dns_length = value;
  171. self
  172. }
  173. /// Whether to enforce STD3 rules for hyphen placement.
  174. ///
  175. /// `true` to deny hyphens in the first and last positions.
  176. /// `false` to not enforce hyphen placement.
  177. ///
  178. /// Note that for backward compatibility this is not the same as
  179. /// UTS 46 _CheckHyphens_, which also disallows hyphens in the
  180. /// third and fourth positions.
  181. ///
  182. /// Note that `true` rejects real-world names, including some GitHub user pages.
  183. #[inline]
  184. pub fn check_hyphens(mut self, value: bool) -> Self {
  185. self.check_hyphens = value;
  186. self
  187. }
  188. /// Obsolete method retained to ease migration. The argument must be `false`.
  189. ///
  190. /// Panics
  191. ///
  192. /// If the argument is `true`.
  193. #[inline]
  194. #[allow(unused_mut)]
  195. pub fn use_idna_2008_rules(mut self, value: bool) -> Self {
  196. assert!(!value, "IDNA 2008 rules are no longer supported");
  197. self
  198. }
  199. /// Compute the deny list
  200. fn deny_list(&self) -> AsciiDenyList {
  201. if self.use_std3_ascii_rules {
  202. AsciiDenyList::STD3
  203. } else {
  204. AsciiDenyList::EMPTY
  205. }
  206. }
  207. /// Compute the hyphen mode
  208. fn hyphens(&self) -> Hyphens {
  209. if self.check_hyphens {
  210. Hyphens::CheckFirstLast
  211. } else {
  212. Hyphens::Allow
  213. }
  214. }
  215. /// [UTS 46 ToASCII](http://www.unicode.org/reports/tr46/#ToASCII)
  216. pub fn to_ascii(self, domain: &str) -> Result<String, Errors> {
  217. let mut result = String::with_capacity(domain.len());
  218. let mut codec = Idna::new(self);
  219. codec.to_ascii(domain, &mut result).map(|()| result)
  220. }
  221. /// [UTS 46 ToUnicode](http://www.unicode.org/reports/tr46/#ToUnicode)
  222. pub fn to_unicode(self, domain: &str) -> (String, Result<(), Errors>) {
  223. let mut codec = Idna::new(self);
  224. let mut out = String::with_capacity(domain.len());
  225. let result = codec.to_unicode(domain, &mut out);
  226. (out, result)
  227. }
  228. }