[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Mail/ -> MailHelper.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2007 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\CMS\Mail;
  11  
  12  use Joomla\CMS\Router\Route;
  13  use Joomla\CMS\String\PunycodeHelper;
  14  use Joomla\CMS\Uri\Uri;
  15  
  16  // phpcs:disable PSR1.Files.SideEffects
  17  \defined('JPATH_PLATFORM') or die;
  18  // phpcs:enable PSR1.Files.SideEffects
  19  
  20  /**
  21   * Email helper class, provides static methods to perform various tasks relevant
  22   * to the Joomla email routines.
  23   *
  24   * @todo: Test these methods as the regex work is first run and not tested thoroughly
  25   *
  26   * @since  1.7.0
  27   */
  28  abstract class MailHelper
  29  {
  30      /**
  31       * Cleans single line inputs.
  32       *
  33       * @param   string  $value  String to be cleaned.
  34       *
  35       * @return  string  Cleaned string.
  36       *
  37       * @since   1.7.0
  38       */
  39      public static function cleanLine($value)
  40      {
  41          $value = PunycodeHelper::emailToPunycode($value);
  42  
  43          return trim(preg_replace('/(%0A|%0D|\n+|\r+)/i', '', $value));
  44      }
  45  
  46      /**
  47       * Cleans multi-line inputs.
  48       *
  49       * @param   string  $value  Multi-line string to be cleaned.
  50       *
  51       * @return  string  Cleaned multi-line string.
  52       *
  53       * @since   1.7.0
  54       */
  55      public static function cleanText($value)
  56      {
  57          return trim(preg_replace('/(%0A|%0D|\n+|\r+)(content-type:|to:|cc:|bcc:)/i', '', $value));
  58      }
  59  
  60      /**
  61       * Cleans any injected headers from the email body.
  62       *
  63       * @param   string  $body  email body string.
  64       *
  65       * @return  string  Cleaned email body string.
  66       *
  67       * @since   1.7.0
  68       */
  69      public static function cleanBody($body)
  70      {
  71          // Strip all email headers from a string
  72          return preg_replace("/((From:|To:|Cc:|Bcc:|Subject:|Content-type:) ([\S]+))/", '', $body);
  73      }
  74  
  75      /**
  76       * Cleans any injected headers from the subject string.
  77       *
  78       * @param   string  $subject  email subject string.
  79       *
  80       * @return  string  Cleaned email subject string.
  81       *
  82       * @since   1.7.0
  83       */
  84      public static function cleanSubject($subject)
  85      {
  86          return preg_replace("/((From:|To:|Cc:|Bcc:|Content-type:) ([\S]+))/", '', $subject);
  87      }
  88  
  89      /**
  90       * Verifies that an email address does not have any extra headers injected into it.
  91       *
  92       * @param   string  $address  email address.
  93       *
  94       * @return  mixed   email address string or boolean false if injected headers are present.
  95       *
  96       * @since   1.7.0
  97       */
  98      public static function cleanAddress($address)
  99      {
 100          if (preg_match("[\s;,]", $address)) {
 101              return false;
 102          }
 103  
 104          return $address;
 105      }
 106  
 107      /**
 108       * Verifies that the string is in a proper email address format.
 109       *
 110       * @param   string  $email  String to be verified.
 111       *
 112       * @return  boolean  True if string has the correct format; false otherwise.
 113       *
 114       * @since   1.7.0
 115       */
 116      public static function isEmailAddress($email)
 117      {
 118          // Split the email into a local and domain
 119          $atIndex = strrpos($email, '@');
 120          $domain = substr($email, $atIndex + 1);
 121          $local = substr($email, 0, $atIndex);
 122  
 123          // Check Length of domain
 124          $domainLen = \strlen($domain);
 125  
 126          if ($domainLen < 1 || $domainLen > 255) {
 127              return false;
 128          }
 129  
 130          /*
 131           * Check the local address
 132           * We're a bit more conservative about what constitutes a "legal" address, that is, a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-
 133           * The first and last character in local cannot be a period ('.')
 134           * Also, period should not appear 2 or more times consecutively
 135           */
 136          $allowed = "a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-";
 137          $regex = "/^[$allowed][\.$allowed]{0,63}$/";
 138  
 139          if (!preg_match($regex, $local) || substr($local, -1) === '.' || $local[0] === '.' || preg_match('/\.\./', $local)) {
 140              return false;
 141          }
 142  
 143          // No problem if the domain looks like an IP address, ish
 144          $regex = '/^[0-9\.]+$/';
 145  
 146          if (preg_match($regex, $domain)) {
 147              return true;
 148          }
 149  
 150          // Check Lengths
 151          $localLen = \strlen($local);
 152  
 153          if ($localLen < 1 || $localLen > 64) {
 154              return false;
 155          }
 156  
 157          // Check the domain
 158          $domain_array = explode('.', $domain);
 159          $regex = '/^[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/';
 160  
 161          foreach ($domain_array as $domain) {
 162              // Convert domain to punycode
 163              $domain = PunycodeHelper::toPunycode($domain);
 164  
 165              // Must be something
 166              if (!$domain) {
 167                  return false;
 168              }
 169  
 170              // Check for invalid characters
 171              if (!preg_match($regex, $domain)) {
 172                  return false;
 173              }
 174  
 175              // Check for a dash at the beginning of the domain
 176              if (strpos($domain, '-') === 0) {
 177                  return false;
 178              }
 179  
 180              // Check for a dash at the end of the domain
 181              $length = \strlen($domain) - 1;
 182  
 183              if (strpos($domain, '-', $length) === $length) {
 184                  return false;
 185              }
 186          }
 187  
 188          return true;
 189      }
 190  
 191      /**
 192       * Convert relative (links, images sources) to absolute urls so that content is accessible in email
 193       *
 194       * @param   string  $content  The content need to convert
 195       *
 196       * @return  string  The converted content which the relative urls are converted to absolute urls
 197       *
 198       * @since  4.1.0
 199       */
 200      public static function convertRelativeToAbsoluteUrls($content)
 201      {
 202          $siteUrl = Uri::root();
 203  
 204          // Replace none SEF URLs by absolute SEF URLs
 205          if (strpos($content, 'href="index.php?') !== false) {
 206              preg_match_all('#href="index.php\?([^"]+)"#m', $content, $matches);
 207  
 208              foreach ($matches[1] as $urlQueryString) {
 209                  $content = str_replace(
 210                      'href="index.php?' . $urlQueryString . '"',
 211                      'href="' . Route::link('site', 'index.php?' . $urlQueryString, Route::TLS_IGNORE, true) . '"',
 212                      $content
 213                  );
 214              }
 215  
 216              self::checkContent($content);
 217          }
 218  
 219          // Replace relative links, image sources with absolute Urls
 220          $protocols  = '[a-zA-Z0-9\-]+:';
 221          $attributes = array('href=', 'src=', 'poster=');
 222  
 223          foreach ($attributes as $attribute) {
 224              if (strpos($content, $attribute) !== false) {
 225                  $regex = '#\s' . $attribute . '"(?!/|' . $protocols . '|\#|\')([^"]*)"#m';
 226  
 227                  $content = preg_replace($regex, ' ' . $attribute . '"' . $siteUrl . '$1"', $content);
 228  
 229                  self::checkContent($content);
 230              }
 231          }
 232  
 233          return $content;
 234      }
 235  
 236      /**
 237       * Check the content after regular expression function call.
 238       *
 239       * @param   string  $content  Content to be checked.
 240       *
 241       * @return  void
 242       *
 243       * @throws  \RuntimeException  If there is an error in previous regular expression function call.
 244       * @since  4.1.0
 245       */
 246      private static function checkContent($content)
 247      {
 248          if ($content !== null) {
 249              return;
 250          }
 251  
 252          switch (preg_last_error()) {
 253              case PREG_BACKTRACK_LIMIT_ERROR:
 254                  $message = 'PHP regular expression limit reached (pcre.backtrack_limit)';
 255                  break;
 256              case PREG_RECURSION_LIMIT_ERROR:
 257                  $message = 'PHP regular expression limit reached (pcre.recursion_limit)';
 258                  break;
 259              case PREG_BAD_UTF8_ERROR:
 260                  $message = 'Bad UTF8 passed to PCRE function';
 261                  break;
 262              default:
 263                  $message = 'Unknown PCRE error calling PCRE function';
 264          }
 265  
 266          throw new \RuntimeException($message);
 267      }
 268  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer