DomainValidator.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <?php
  2. namespace Drupal\domain;
  3. use Drupal\Core\Config\ConfigFactoryInterface;
  4. use Drupal\Core\Entity\EntityTypeManagerInterface;
  5. use Drupal\Core\Extension\ModuleHandlerInterface;
  6. use Drupal\Core\StringTranslation\StringTranslationTrait;
  7. use GuzzleHttp\ClientInterface;
  8. use GuzzleHttp\Exception\RequestException;
  9. /**
  10. * Provides validation of domain strings against RFC standards for hostnames.
  11. */
  12. class DomainValidator implements DomainValidatorInterface {
  13. use StringTranslationTrait;
  14. /**
  15. * The module handler.
  16. *
  17. * @var \Drupal\Core\Extension\ModuleHandlerInterface
  18. */
  19. protected $moduleHandler;
  20. /**
  21. * The HTTP client.
  22. *
  23. * @var \GuzzleHttp\Client
  24. */
  25. protected $httpClient;
  26. /**
  27. * The config factory.
  28. *
  29. * @var \Drupal\Core\Config\ConfigFactoryInterface
  30. */
  31. protected $configFactory;
  32. /**
  33. * The entity type manager.
  34. *
  35. * @var \Drupal\Core\Entity\EntityTypeManagerInterface
  36. */
  37. protected $entityTypeManager;
  38. /**
  39. * Constructs a DomainValidator object.
  40. *
  41. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
  42. * The module handler.
  43. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
  44. * The config factory.
  45. * @param \GuzzleHttp\ClientInterface $http_client
  46. * A Guzzle client object.
  47. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
  48. * The entity type manager.
  49. */
  50. public function __construct(ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, ClientInterface $http_client, EntityTypeManagerInterface $entity_type_manager) {
  51. $this->moduleHandler = $module_handler;
  52. $this->configFactory = $config_factory;
  53. $this->httpClient = $http_client;
  54. $this->entityTypeManager = $entity_type_manager;
  55. }
  56. /**
  57. * {@inheritdoc}
  58. */
  59. public function validate($hostname) {
  60. $error_list = [];
  61. // Check for at least one dot or the use of 'localhost'.
  62. // Note that localhost can specify a port.
  63. $localhost_check = explode(':', $hostname);
  64. if (substr_count($hostname, '.') == 0 && $localhost_check[0] != 'localhost') {
  65. $error_list[] = $this->t('At least one dot (.) is required, except when using <em>localhost</em>.');
  66. }
  67. // Check for one colon only.
  68. if (substr_count($hostname, ':') > 1) {
  69. $error_list[] = $this->t('Only one colon (:) is allowed.');
  70. }
  71. // If a colon, make sure it is only followed by numbers.
  72. elseif (substr_count($hostname, ':') == 1) {
  73. $parts = explode(':', $hostname);
  74. $port = (int) $parts[1];
  75. if (strcmp($port, $parts[1])) {
  76. $error_list[] = $this->t('The port protocol must be an integer.');
  77. }
  78. }
  79. // The domain cannot begin or end with a period.
  80. if (substr($hostname, 0, 1) == '.') {
  81. $error_list[] = $this->t('The domain must not begin with a dot (.)');
  82. }
  83. // The domain cannot begin or end with a period.
  84. if (substr($hostname, -1) == '.') {
  85. $error_list[] = $this->t('The domain must not end with a dot (.)');
  86. }
  87. // Check for valid characters, unless using non-ASCII domains.
  88. $config = $this->configFactory->get('domain.settings');
  89. $non_ascii = $config->get('allow_non_ascii');
  90. if (!$non_ascii) {
  91. $pattern = '/^[a-z0-9\.\-:]*$/i';
  92. if (!preg_match($pattern, $hostname)) {
  93. $error_list[] = $this->t('Only alphanumeric characters, dashes, and a colon are allowed.');
  94. }
  95. }
  96. // Check for lower case.
  97. if ($hostname != mb_strtolower($hostname)) {
  98. $error_list[] = $this->t('Only lower-case characters are allowed.');
  99. }
  100. // Check for 'www' prefix if redirection / handling is
  101. // enabled under global domain settings.
  102. // Note that www prefix handling must be set explicitly in the UI.
  103. // See http://drupal.org/node/1529316 and http://drupal.org/node/1783042
  104. if ($config->get('www_prefix') && (substr($hostname, 0, strpos($hostname, '.')) == 'www')) {
  105. $error_list[] = $this->t('WWW prefix handling: Domains must be registered without the www. prefix.');
  106. }
  107. // Allow modules to alter this behavior.
  108. $this->moduleHandler->alter('domain_validate', $error_list, $hostname);
  109. return $error_list;
  110. }
  111. /**
  112. * {@inheritdoc}
  113. */
  114. public function checkResponse(DomainInterface $domain) {
  115. $url = $domain->getPath() . drupal_get_path('module', 'domain') . '/tests/200.png';
  116. try {
  117. // GuzzleHttp no longer allows for bogus URL calls.
  118. $request = $this->httpClient->get($url);
  119. }
  120. // We cannot know which Guzzle Exception class will be returned; be generic.
  121. catch (RequestException $e) {
  122. watchdog_exception('domain', $e);
  123. // File a general server failure.
  124. $domain->setResponse(500);
  125. return;
  126. }
  127. // Expected result (i.e. no exception thrown.)
  128. $domain->setResponse($request->getStatusCode());
  129. return $domain->getResponse();
  130. }
  131. /**
  132. * {@inheritdoc}
  133. */
  134. public function getRequiredFields() {
  135. return ['hostname', 'name', 'scheme', 'status', 'weight'];
  136. }
  137. }