1 <?php 2 /** 3 * Copyright 2010-2017 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-2017 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-2016 Horde LLC 46 * @copyright 2010 Gerd Schaufelberger 47 * @deprecated Use Horde_Mail_Transport_Hordesmtp instead 48 * @license http://www.horde.org/licenses/bsd New BSD License 49 * @package Mail 50 */ 51 class Horde_Mail_Transport_Smtpmx extends Horde_Mail_Transport 52 { 53 /** 54 * SMTP connection object. 55 * 56 * @var Net_SMTP 57 */ 58 protected $_smtp = null; 59 60 /** 61 * Net_DNS2_Resolver object. 62 * 63 * @var Net_DNS2_Resolver 64 */ 65 protected $_resolver; 66 67 /** 68 * Internal error codes. 69 * Translate internal error identifier to human readable messages. 70 * 71 * @var array 72 */ 73 protected $_errorCode = array( 74 'not_connected' => array( 75 'code' => 1, 76 'msg' => 'Could not connect to any mail server ({HOST}) at port {PORT} to send mail to {RCPT}.' 77 ), 78 'failed_vrfy_rcpt' => array( 79 'code' => 2, 80 'msg' => 'Recipient "{RCPT}" could not be veryfied.' 81 ), 82 'failed_set_from' => array( 83 'code' => 3, 84 'msg' => 'Failed to set sender: {FROM}.' 85 ), 86 'failed_set_rcpt' => array( 87 'code' => 4, 88 'msg' => 'Failed to set recipient: {RCPT}.' 89 ), 90 'failed_send_data' => array( 91 'code' => 5, 92 'msg' => 'Failed to send mail to: {RCPT}.' 93 ), 94 'no_from' => array( 95 'code' => 5, 96 'msg' => 'No from address has be provided.' 97 ), 98 'send_data' => array( 99 'code' => 7, 100 'msg' => 'Failed to create Net_SMTP object.' 101 ), 102 'no_mx' => array( 103 'code' => 8, 104 'msg' => 'No MX-record for {RCPT} found.' 105 ), 106 'no_resolver' => array( 107 'code' => 9, 108 'msg' => 'Could not start resolver! Install PEAR:Net_DNS2 or switch off "netdns"' 109 ), 110 'failed_rset' => array( 111 'code' => 10, 112 'msg' => 'RSET command failed, SMTP-connection corrupt.' 113 ) 114 ); 115 116 /** 117 * @param array $params Additional options: 118 * - debug: (boolean) Activate SMTP debug mode? 119 * DEFAULT: false 120 * - mailname: (string) The name of the local mail system (a valid 121 * hostname which matches the reverse lookup) 122 * DEFAULT: Auto-determined 123 * - netdns: (boolean) Use PEAR:Net_DNS2 (true) or the PHP builtin 124 * getmxrr(). 125 * DEFAULT: true 126 * - port: (integer) Port. 127 * DEFAULT: Auto-determined 128 * - test: (boolean) Activate test mode? 129 * DEFAULT: false 130 * - timeout: (integer) The SMTP connection timeout (in seconds). 131 * DEFAULT: 10 132 * - verp: (boolean) Whether to use VERP. 133 * If not a boolean, the string value will be used as the VERP 134 * separators. 135 * DEFAULT: false 136 * - vrfy: (boolean) Whether to use VRFY. 137 * DEFAULT: false 138 */ 139 public function __construct(array $params = array()) 140 { 141 /* Try to find a valid mailname. */ 142 if (!isset($params['mailname']) && function_exists('posix_uname')) { 143 $uname = posix_uname(); 144 $params['mailname'] = $uname['nodename']; 145 } 146 147 if (!isset($params['port'])) { 148 $params['port'] = getservbyname('smtp', 'tcp'); 149 } 150 151 $this->_params = array_merge(array( 152 'debug' => false, 153 'mailname' => 'localhost', 154 'netdns' => true, 155 'port' => 25, 156 'test' => false, 157 'timeout' => 10, 158 'verp' => false, 159 'vrfy' => false 160 ), $params); 161 162 /* SMTP requires CRLF line endings. */ 163 $this->sep = "\r\n"; 164 } 165 166 /** 167 * Destructor implementation to ensure that we disconnect from any 168 * potentially-alive persistent SMTP connections. 169 */ 170 public function __destruct() 171 { 172 if (is_object($this->_smtp)) { 173 $this->_smtp->disconnect(); 174 $this->_smtp = null; 175 } 176 } 177 178 /** 179 */ 180 public function send($recipients, array $headers, $body) 181 { 182 $headers = $this->_sanitizeHeaders($headers); 183 184 // Prepare headers 185 list($from, $textHeaders) = $this->prepareHeaders($headers); 186 187 try { 188 $from = $this->_getFrom($from, $headers); 189 } catch (Horde_Mail_Exception $e) { 190 $this->_error('no_from'); 191 } 192 193 // Prepare recipients 194 foreach ($this->parseRecipients($recipients) as $rcpt) { 195 list(,$host) = explode('@', $rcpt); 196 197 $mx = $this->_getMx($host); 198 if (!$mx) { 199 $this->_error('no_mx', array('rcpt' => $rcpt)); 200 } 201 202 $connected = false; 203 foreach (array_keys($mx) as $mserver) { 204 $this->_smtp = new Net_SMTP($mserver, $this->_params['port'], $this->_params['mailname']); 205 206 // configure the SMTP connection. 207 if ($this->_params['debug']) { 208 $this->_smtp->setDebug(true); 209 } 210 211 // attempt to connect to the configured SMTP server. 212 $res = $this->_smtp->connect($this->_params['timeout']); 213 if ($res instanceof PEAR_Error) { 214 $this->_smtp = null; 215 continue; 216 } 217 218 // connection established 219 if ($res) { 220 $connected = true; 221 break; 222 } 223 } 224 225 if (!$connected) { 226 $this->_error('not_connected', array( 227 'host' => implode(', ', array_keys($mx)), 228 'port' => $this->_params['port'], 229 'rcpt' => $rcpt 230 )); 231 } 232 233 // Verify recipient 234 if ($this->_params['vrfy']) { 235 $res = $this->_smtp->vrfy($rcpt); 236 if ($res instanceof PEAR_Error) { 237 $this->_error('failed_vrfy_rcpt', array('rcpt' => $rcpt)); 238 } 239 } 240 241 // mail from: 242 $args['verp'] = $this->_params['verp']; 243 $res = $this->_smtp->mailFrom($from, $args); 244 if ($res instanceof PEAR_Error) { 245 $this->_error('failed_set_from', array('from' => $from)); 246 } 247 248 // rcpt to: 249 $res = $this->_smtp->rcptTo($rcpt); 250 if ($res instanceof PEAR_Error) { 251 $this->_error('failed_set_rcpt', array('rcpt' => $rcpt)); 252 } 253 254 // Don't send anything in test mode 255 if ($this->_params['test']) { 256 $res = $this->_smtp->rset(); 257 if ($res instanceof PEAR_Error) { 258 $this->_error('failed_rset'); 259 } 260 261 $this->_smtp->disconnect(); 262 $this->_smtp = null; 263 return; 264 } 265 266 // Send data. Net_SMTP does necessary EOL conversions. 267 $res = $this->_smtp->data($body, $textHeaders); 268 if ($res instanceof PEAR_Error) { 269 $this->_error('failed_send_data', array('rcpt' => $rcpt)); 270 } 271 272 $this->_smtp->disconnect(); 273 $this->_smtp = null; 274 } 275 } 276 277 /** 278 * Recieve MX records for a host. 279 * 280 * @param string $host Mail host. 281 * 282 * @return mixed Sorted MX list or false on error. 283 */ 284 protected function _getMx($host) 285 { 286 $mx = array(); 287 288 if ($this->params['netdns']) { 289 $this->_loadNetDns(); 290 291 try { 292 $response = $this->_resolver->query($host, 'MX'); 293 if (!$response) { 294 return false; 295 } 296 } catch (Exception $e) { 297 throw new Horde_Mail_Exception($e); 298 } 299 300 foreach ($response->answer as $rr) { 301 if ($rr->type == 'MX') { 302 $mx[$rr->exchange] = $rr->preference; 303 } 304 } 305 } else { 306 $mxHost = $mxWeight = array(); 307 308 if (!getmxrr($host, $mxHost, $mxWeight)) { 309 return false; 310 } 311 312 for ($i = 0; $i < count($mxHost); ++$i) { 313 $mx[$mxHost[$i]] = $mxWeight[$i]; 314 } 315 } 316 317 asort($mx); 318 319 return $mx; 320 } 321 322 /** 323 * Initialize Net_DNS2_Resolver. 324 */ 325 protected function _loadNetDns() 326 { 327 if (!$this->_resolver) { 328 if (!class_exists('Net_DNS2_Resolver')) { 329 $this->_error('no_resolver'); 330 } 331 $this->_resolver = new Net_DNS2_Resolver(); 332 } 333 } 334 335 /** 336 * Format error message. 337 * 338 * @param string $id Maps error ids to codes and message. 339 * @param array $info Optional information in associative array. 340 * 341 * @throws Horde_Mail_Exception 342 */ 343 protected function _error($id, $info = array()) 344 { 345 $msg = $this->_errorCode[$id]['msg']; 346 347 // include info to messages 348 if (!empty($info)) { 349 $replace = $search = array(); 350 351 foreach ($info as $key => $value) { 352 $search[] = '{' . Horde_String::upper($key) . '}'; 353 $replace[] = $value; 354 } 355 356 $msg = str_replace($search, $replace, $msg); 357 } 358 359 throw new Horde_Mail_Exception($msg, $this->_errorCode[$id]['code']); 360 } 361 362 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body