IDNA.cpp 2.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. /*
  2. * Copyright (c) 2023, Simon Wanner <simon@skyrising.xyz>
  3. * Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #define AK_DONT_REPLACE_STD
  8. #include <LibLocale/ICU.h>
  9. #include <LibUnicode/IDNA.h>
  10. #include <unicode/idna.h>
  11. namespace Unicode::IDNA {
  12. // https://www.unicode.org/reports/tr46/#ToASCII
  13. ErrorOr<String> to_ascii(Utf8View domain_name, ToAsciiOptions const& options)
  14. {
  15. u32 icu_options = UIDNA_DEFAULT;
  16. if (options.check_bidi == CheckBidi::Yes)
  17. icu_options |= UIDNA_CHECK_BIDI;
  18. if (options.check_joiners == CheckJoiners::Yes)
  19. icu_options |= UIDNA_CHECK_CONTEXTJ;
  20. if (options.use_std3_ascii_rules == UseStd3AsciiRules::Yes)
  21. icu_options |= UIDNA_USE_STD3_RULES;
  22. if (options.transitional_processing == TransitionalProcessing::No)
  23. icu_options |= UIDNA_NONTRANSITIONAL_TO_ASCII | UIDNA_NONTRANSITIONAL_TO_UNICODE;
  24. UErrorCode status = U_ZERO_ERROR;
  25. auto idna = adopt_own_if_nonnull(icu::IDNA::createUTS46Instance(icu_options, status));
  26. if (Locale::icu_failure(status))
  27. return Error::from_string_literal("Unable to create an IDNA instance");
  28. StringBuilder builder { domain_name.as_string().length() };
  29. icu::StringByteSink sink { &builder };
  30. icu::IDNAInfo info;
  31. idna->nameToASCII_UTF8(Locale::icu_string_piece(domain_name.as_string()), sink, info, status);
  32. auto errors = info.getErrors();
  33. if (options.check_hyphens == CheckHyphens::No) {
  34. errors &= ~UIDNA_ERROR_HYPHEN_3_4;
  35. errors &= ~UIDNA_ERROR_LEADING_HYPHEN;
  36. errors &= ~UIDNA_ERROR_TRAILING_HYPHEN;
  37. }
  38. if (options.verify_dns_length == VerifyDnsLength::No) {
  39. errors &= ~UIDNA_ERROR_EMPTY_LABEL;
  40. errors &= ~UIDNA_ERROR_LABEL_TOO_LONG;
  41. errors &= ~UIDNA_ERROR_DOMAIN_NAME_TOO_LONG;
  42. }
  43. if (Locale::icu_failure(status) || errors != 0)
  44. return Error::from_string_literal("Unable to convert domain to ASCII");
  45. return builder.to_string();
  46. }
  47. }