Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403]

   1  <?php
   2  /**
   3   * Copyright 2014-2017 Horde LLC (http://www.horde.org/)
   4   *
   5   * See the enclosed file LICENSE for license information (BSD). If you
   6   * did not receive this file, see http://www.horde.org/licenses/bsd.
   7   *
   8   * @author   Michael Slusarz <slusarz@horde.org>
   9   * @category Horde
  10   * @license  http://www.horde.org/licenses/bsd BSD
  11   * @package  Idna
  12   */
  13  
  14  /**
  15   * Provide normalized encoding/decoding support for IDNA strings.
  16   *
  17   * @author    Michael Slusarz <slusarz@horde.org>
  18   * @category  Horde
  19   * @copyright 2014-2017 Horde LLC
  20   * @license   http://www.horde.org/licenses/bsd BSD
  21   * @package   Idna
  22   */
  23  class Horde_Idna
  24  {
  25      /**
  26       * The backend to use.
  27       *
  28       * @var mixed
  29       */
  30      protected static $_backend;
  31  
  32      /**
  33       * @throws Horde_Idna_Exception
  34       */
  35      public static function encode($data)
  36      {
  37          switch ($backend = static::_getBackend()) {
  38          case 'INTL':
  39              if ($data === null) {
  40                  return false;
  41              }
  42              return idn_to_ascii($data);
  43  
  44          case 'INTL_UTS46':
  45              if ($data === null) {
  46                  return false;
  47              }
  48              $result = idn_to_ascii($data, 0, INTL_IDNA_VARIANT_UTS46, $info);
  49              self::_checkForError($info);
  50              return $result;
  51  
  52          default:
  53              return $backend->encode($data);
  54          }
  55      }
  56  
  57      /**
  58       * @throws Horde_Idna_Exception
  59       */
  60      public static function decode($data)
  61      {
  62          switch ($backend = static::_getBackend()) {
  63          case 'INTL':
  64          case 'INTL_UTS46':
  65              $parts = explode('.', $data);
  66              foreach ($parts as &$part) {
  67                  if (strpos($part, 'xn--') === 0) {
  68                      switch ($backend) {
  69                      case 'INTL':
  70                          $part = idn_to_utf8($part);
  71                          break;
  72  
  73                      case 'INTL_UTS46':
  74                          $part = idn_to_utf8($part, 0, INTL_IDNA_VARIANT_UTS46, $info);
  75                          self::_checkForError($info);
  76                          break;
  77                      }
  78                  }
  79              }
  80              return implode('.', $parts);
  81  
  82          default:
  83              return $backend->decode($data);
  84          }
  85      }
  86  
  87      /**
  88       * Checks if the $idna_info parameter of idn_to_ascii() or idn_to_utf8()
  89       * contains errors.
  90       *
  91       * @param array $info  Fourth parameter to idn_to_ascii() or idn_to_utf8().
  92       *
  93       * @throws Horde_Idna_Exception
  94       */
  95      protected static function _checkForError($info)
  96      {
  97          if (!isset($info['errors'])) {
  98              return;
  99          }
 100          switch (true) {
 101          case $info['errors'] & IDNA_ERROR_EMPTY_LABEL:
 102              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 103                  "Domain name is empty"
 104              ));
 105          case $info['errors'] & IDNA_ERROR_LABEL_TOO_LONG:
 106          case $info['errors'] & IDNA_ERROR_DOMAIN_NAME_TOO_LONG:
 107              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 108                  "Domain name is too long"
 109              ));
 110          case $info['errors'] & IDNA_ERROR_LEADING_HYPHEN:
 111              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 112                  "Starts with a hyphen"
 113              ));
 114          case $info['errors'] & IDNA_ERROR_TRAILING_HYPHEN:
 115              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 116                  "Ends with a hyphen"
 117              ));
 118          case $info['errors'] & IDNA_ERROR_HYPHEN_3_4:
 119              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 120                  "Contains hyphen in the third and fourth positions"
 121              ));
 122          case $info['errors'] & IDNA_ERROR_LEADING_COMBINING_MARK:
 123              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 124                  "Starts with a combining mark"
 125              ));
 126          case $info['errors'] & IDNA_ERROR_DISALLOWED:
 127              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 128                  "Contains disallowed characters"
 129              ));
 130          case $info['errors'] & IDNA_ERROR_PUNYCODE:
 131              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 132                  "Starts with \"xn--\" but does not contain valid Punycode"
 133              ));
 134          case $info['errors'] & IDNA_ERROR_LABEL_HAS_DOT:
 135              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 136                  "Contains a dot"
 137              ));
 138          case $info['errors'] & IDNA_ERROR_INVALID_ACE_LABEL:
 139              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 140                  "ACE label does not contain a valid label string"
 141              ));
 142          case $info['errors'] & IDNA_ERROR_BIDI:
 143              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 144                  "Does not meet the IDNA BiDi requirements (for right-to-left characters)"
 145              ));
 146          case $info['errors'] & IDNA_ERROR_CONTEXTJ:
 147              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 148                  "Does not meet the IDNA CONTEXTJ requirements"
 149              ));
 150          case $info['errors']:
 151              throw new Horde_Idna_Exception(Horde_Idna_Translation::t(
 152                  "Unknown error"
 153              ));
 154          }
 155      }
 156  
 157      /**
 158       * Return the IDNA backend.
 159       *
 160       * @return mixed  IDNA backend (false if none available).
 161       */
 162      protected static function _getBackend()
 163      {
 164          if (!isset(self::$_backend)) {
 165              if (extension_loaded('intl')) {
 166                  /* Only available in PHP > 5.4.0 */
 167                  self::$_backend = defined('INTL_IDNA_VARIANT_UTS46')
 168                      ? 'INTL_UTS46'
 169                      : 'INTL';
 170              } else {
 171                  self::$_backend = new Horde_Idna_Punycode();
 172              }
 173          }
 174  
 175          return self::$_backend;
 176      }
 177  
 178  }