[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/horde/framework/Horde/Mail/Transport/ -> Smtpmx.php (source)

   1  <?PHP
   2  /**
   3   * Copyright 2010-2014 Horde LLC (http://www.horde.org/)
   4   * Copyright (c) 2010 Gerd Schaufelberger
   5   * All rights reserved.
   6   *
   7   * Redistribution and use in source and binary forms, with or without
   8   * modification, are permitted provided that the following conditions
   9   * are met:
  10   *
  11   * o Redistributions of source code must retain the above copyright
  12   *   notice, this list of conditions and the following disclaimer.
  13   * o Redistributions in binary form must reproduce the above copyright
  14   *   notice, this list of conditions and the following disclaimer in the
  15   *   documentation and/or other materials provided with the distribution.
  16   * o The names of the authors may not be used to endorse or promote
  17   *   products derived from this software without specific prior written
  18   *   permission.
  19   *
  20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31   *
  32   * @category  Horde
  33   * @copyright 2010-2014 Horde LLC
  34   * @copyright 2010 Gerd Schaufelberger
  35   * @license   http://www.horde.org/licenses/bsd New BSD License
  36   * @package   Mail
  37   */
  38  
  39  /**
  40   * SMTP MX implementation.
  41   *
  42   * @author    Gerd Schaufelberger <gerd@php-tools.net>
  43   * @author    Michael Slusarz <slusarz@horde.org>
  44   * @category  Horde
  45   * @copyright 2010-2014 Horde LLC
  46   * @copyright 2010 Gerd Schaufelberger
  47   * @license   http://www.horde.org/licenses/bsd New BSD License
  48   * @package   Mail
  49   */
  50  class Horde_Mail_Transport_Smtpmx extends Horde_Mail_Transport
  51  {
  52      /**
  53       * SMTP connection object.
  54       *
  55       * @var Net_SMTP
  56       */
  57      protected $_smtp = null;
  58  
  59      /**
  60       * Net_DNS2_Resolver object.
  61       *
  62       * @var Net_DNS2_Resolver
  63       */
  64      protected $_resolver;
  65  
  66      /**
  67       * Internal error codes.
  68       * Translate internal error identifier to human readable messages.
  69       *
  70       * @var array
  71       */
  72      protected $_errorCode = array(
  73          'not_connected' => array(
  74              'code' => 1,
  75              'msg' => 'Could not connect to any mail server ({HOST}) at port {PORT} to send mail to {RCPT}.'
  76          ),
  77          'failed_vrfy_rcpt' => array(
  78              'code' => 2,
  79              'msg' => 'Recipient "{RCPT}" could not be veryfied.'
  80          ),
  81          'failed_set_from' => array(
  82              'code' => 3,
  83              'msg' => 'Failed to set sender: {FROM}.'
  84          ),
  85          'failed_set_rcpt' => array(
  86              'code' => 4,
  87              'msg' => 'Failed to set recipient: {RCPT}.'
  88          ),
  89          'failed_send_data' => array(
  90              'code' => 5,
  91              'msg' => 'Failed to send mail to: {RCPT}.'
  92          ),
  93          'no_from' => array(
  94              'code' => 5,
  95              'msg' => 'No from address has be provided.'
  96          ),
  97          'send_data' => array(
  98              'code' => 7,
  99              'msg' => 'Failed to create Net_SMTP object.'
 100          ),
 101          'no_mx' => array(
 102              'code' => 8,
 103              'msg' => 'No MX-record for {RCPT} found.'
 104          ),
 105          'no_resolver' => array(
 106              'code' => 9,
 107              'msg' => 'Could not start resolver! Install PEAR:Net_DNS2 or switch off "netdns"'
 108          ),
 109          'failed_rset' => array(
 110              'code' => 10,
 111              'msg' => 'RSET command failed, SMTP-connection corrupt.'
 112          )
 113      );
 114  
 115      /**
 116       * @param array $params  Additional options:
 117       *   - debug: (boolean) Activate SMTP debug mode?
 118       *            DEFAULT: false
 119       *   - mailname: (string) The name of the local mail system (a valid
 120       *               hostname which matches the reverse lookup)
 121       *               DEFAULT: Auto-determined
 122       *   - netdns: (boolean) Use PEAR:Net_DNS2 (true) or the PHP builtin
 123       *             getmxrr().
 124       *             DEFAULT: true
 125       *   - port: (integer) Port.
 126       *           DEFAULT: Auto-determined
 127       *   - test: (boolean) Activate test mode?
 128       *           DEFAULT: false
 129       *   - timeout: (integer) The SMTP connection timeout (in seconds).
 130       *              DEFAULT: 10
 131       *   - verp: (boolean) Whether to use VERP.
 132       *           If not a boolean, the string value will be used as the VERP
 133       *           separators.
 134       *           DEFAULT: false
 135       *   - vrfy: (boolean) Whether to use VRFY.
 136       *           DEFAULT: false
 137       */
 138      public function __construct(array $params = array())
 139      {
 140          /* Try to find a valid mailname. */
 141          if (!isset($params['mailname']) && function_exists('posix_uname')) {
 142              $uname = posix_uname();
 143              $params['mailname'] = $uname['nodename'];
 144          }
 145  
 146          if (!isset($params['port'])) {
 147              $params['port'] = getservbyname('smtp', 'tcp');
 148          }
 149  
 150          $this->_params = array_merge(array(
 151              'debug' => false,
 152              'mailname' => 'localhost',
 153              'netdns' => true,
 154              'port' => 25,
 155              'test' => false,
 156              'timeout' => 10,
 157              'verp' => false,
 158              'vrfy' => false
 159          ), $params);
 160  
 161          /* SMTP requires CRLF line endings. */
 162          $this->sep = "\r\n";
 163      }
 164  
 165      /**
 166       * Destructor implementation to ensure that we disconnect from any
 167       * potentially-alive persistent SMTP connections.
 168       */
 169      public function __destruct()
 170      {
 171          if (is_object($this->_smtp)) {
 172              $this->_smtp->disconnect();
 173              $this->_smtp = null;
 174          }
 175      }
 176  
 177      /**
 178       */
 179      public function send($recipients, array $headers, $body)
 180      {
 181          $headers = $this->_sanitizeHeaders($headers);
 182  
 183          // Prepare headers
 184          list($from, $textHeaders) = $this->prepareHeaders($headers);
 185  
 186          try {
 187              $from = $this->_getFrom($from, $headers);
 188          } catch (Horde_Mail_Exception $e) {
 189              $this->_error('no_from');
 190          }
 191  
 192          // Prepare recipients
 193          foreach ($this->parseRecipients($recipients) as $rcpt) {
 194              list(,$host) = explode('@', $rcpt);
 195  
 196              $mx = $this->_getMx($host);
 197              if (!$mx) {
 198                  $this->_error('no_mx', array('rcpt' => $rcpt));
 199              }
 200  
 201              $connected = false;
 202              foreach (array_keys($mx) as $mserver) {
 203                  $this->_smtp = new Net_SMTP($mserver, $this->_params['port'], $this->_params['mailname']);
 204  
 205                  // configure the SMTP connection.
 206                  if ($this->_params['debug']) {
 207                      $this->_smtp->setDebug(true);
 208                  }
 209  
 210                  // attempt to connect to the configured SMTP server.
 211                  $res = $this->_smtp->connect($this->_params['timeout']);
 212                  if ($res instanceof PEAR_Error) {
 213                      $this->_smtp = null;
 214                      continue;
 215                  }
 216  
 217                  // connection established
 218                  if ($res) {
 219                      $connected = true;
 220                      break;
 221                  }
 222              }
 223  
 224              if (!$connected) {
 225                  $this->_error('not_connected', array(
 226                      'host' => implode(', ', array_keys($mx)),
 227                      'port' => $this->_params['port'],
 228                      'rcpt' => $rcpt
 229                  ));
 230              }
 231  
 232              // Verify recipient
 233              if ($this->_params['vrfy']) {
 234                  $res = $this->_smtp->vrfy($rcpt);
 235                  if ($res instanceof PEAR_Error) {
 236                      $this->_error('failed_vrfy_rcpt', array('rcpt' => $rcpt));
 237                  }
 238              }
 239  
 240              // mail from:
 241              $args['verp'] = $this->_params['verp'];
 242              $res = $this->_smtp->mailFrom($from, $args);
 243              if ($res instanceof PEAR_Error) {
 244                  $this->_error('failed_set_from', array('from' => $from));
 245              }
 246  
 247              // rcpt to:
 248              $res = $this->_smtp->rcptTo($rcpt);
 249              if ($res instanceof PEAR_Error) {
 250                  $this->_error('failed_set_rcpt', array('rcpt' => $rcpt));
 251              }
 252  
 253              // Don't send anything in test mode
 254              if ($this->_params['test']) {
 255                  $res = $this->_smtp->rset();
 256                  if ($res instanceof PEAR_Error) {
 257                      $this->_error('failed_rset');
 258                  }
 259  
 260                  $this->_smtp->disconnect();
 261                  $this->_smtp = null;
 262                  return;
 263              }
 264  
 265              // Send data. Net_SMTP does necessary EOL conversions.
 266              $res = $this->_smtp->data($body, $textHeaders);
 267              if ($res instanceof PEAR_Error) {
 268                  $this->_error('failed_send_data', array('rcpt' => $rcpt));
 269              }
 270  
 271              $this->_smtp->disconnect();
 272              $this->_smtp = null;
 273          }
 274      }
 275  
 276      /**
 277       * Recieve MX records for a host.
 278       *
 279       * @param string $host  Mail host.
 280       *
 281       * @return mixed  Sorted MX list or false on error.
 282       */
 283      protected function _getMx($host)
 284      {
 285          $mx = array();
 286  
 287          if ($this->params['netdns']) {
 288              $this->_loadNetDns();
 289  
 290              try {
 291                  $response = $this->_resolver->query($host, 'MX');
 292                  if (!$response) {
 293                      return false;
 294                  }
 295              } catch (Exception $e) {
 296                  throw new Horde_Mail_Exception($e);
 297              }
 298  
 299              foreach ($response->answer as $rr) {
 300                  if ($rr->type == 'MX') {
 301                      $mx[$rr->exchange] = $rr->preference;
 302                  }
 303              }
 304          } else {
 305              $mxHost = $mxWeight = array();
 306  
 307              if (!getmxrr($host, $mxHost, $mxWeight)) {
 308                  return false;
 309              }
 310  
 311              for ($i = 0; $i < count($mxHost); ++$i) {
 312                  $mx[$mxHost[$i]] = $mxWeight[$i];
 313              }
 314          }
 315  
 316          asort($mx);
 317  
 318          return $mx;
 319      }
 320  
 321      /**
 322       * Initialize Net_DNS2_Resolver.
 323       */
 324      protected function _loadNetDns()
 325      {
 326          if (!$this->_resolver) {
 327              if (!class_exists('Net_DNS2_Resolver')) {
 328                  $this->_error('no_resolver');
 329              }
 330              $this->_resolver = new Net_DNS2_Resolver();
 331          }
 332      }
 333  
 334      /**
 335       * Format error message.
 336       *
 337       * @param string $id   Maps error ids to codes and message.
 338       * @param array $info  Optional information in associative array.
 339       *
 340       * @throws Horde_Mail_Exception
 341       */
 342      protected function _error($id, $info = array())
 343      {
 344          $msg = $this->_errorCode[$id]['msg'];
 345  
 346          // include info to messages
 347          if (!empty($info)) {
 348              $replace = $search = array();
 349  
 350              foreach ($info as $key => $value) {
 351                  $search[] = '{' . strtoupper($key) . '}';
 352                  $replace[] = $value;
 353              }
 354  
 355              $msg = str_replace($search, $replace, $msg);
 356          }
 357  
 358          throw new Horde_Mail_Exception($msg, $this->_errorCode[$id]['code']);
 359      }
 360  
 361  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1