Updating PEAR Mail, Net and Auth
[akelos.git] / vendor / pear / Mail / smtpmx.php
blob9d2dccfb13bc2676843a952ac5cb1788ac778f24
1 <?PHP
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4 /**
5 * SMTP MX
7 * SMTP MX implementation of the PEAR Mail interface. Requires the Net_SMTP class.
9 * PHP versions 4 and 5
11 * LICENSE: This source file is subject to version 3.0 of the PHP license
12 * that is available through the world-wide-web at the following URI:
13 * http://www.php.net/license/3_0.txt. If you did not receive a copy of
14 * the PHP License and are unable to obtain it through the web, please
15 * send a note to license@php.net so we can mail you a copy immediately.
17 * @category Mail
18 * @package Mail_smtpmx
19 * @author gERD Schaufelberger <gerd@php-tools.net>
20 * @copyright 1997-2005 The PHP Group
21 * @license http://www.php.net/license/3_0.txt PHP License 3.0
22 * @version CVS: $Id: smtpmx.php,v 1.2 2007/10/06 17:00:00 chagenbu Exp $
23 * @see Mail
26 require_once 'Net/SMTP.php';
28 /**
29 * SMTP MX implementation of the PEAR Mail interface. Requires the Net_SMTP class.
32 * @access public
33 * @author gERD Schaufelberger <gerd@php-tools.net>
34 * @package Mail
35 * @version $Revision: 1.2 $
37 class Mail_smtpmx extends Mail {
39 /**
40 * SMTP connection object.
42 * @var object
43 * @access private
45 var $_smtp = null;
47 /**
48 * The port the SMTP server is on.
49 * @var integer
50 * @see getservicebyname()
52 var $port = 25;
54 /**
55 * Hostname or domain that will be sent to the remote SMTP server in the
56 * HELO / EHLO message.
58 * @var string
59 * @see posix_uname()
61 var $mailname = 'localhost';
63 /**
64 * SMTP connection timeout value. NULL indicates no timeout.
66 * @var integer
68 var $timeout = 10;
70 /**
71 * use either PEAR:Net_DNS or getmxrr
73 * @var boolean
75 var $withNetDns = true;
77 /**
78 * PEAR:Net_DNS_Resolver
80 * @var object
82 var $resolver;
84 /**
85 * Whether to use VERP or not. If not a boolean, the string value
86 * will be used as the VERP separators.
88 * @var mixed boolean or string
90 var $verp = false;
92 /**
93 * Whether to use VRFY or not.
95 * @var boolean $vrfy
97 var $vrfy = false;
99 /**
100 * Switch to test mode - don't send emails for real
102 * @var boolean $debug
104 var $test = false;
107 * Turn on Net_SMTP debugging?
109 * @var boolean $peardebug
111 var $debug = false;
114 * internal error codes
116 * translate internal error identifier to PEAR-Error codes and human
117 * readable messages.
119 * @var boolean $debug
120 * @todo as I need unique error-codes to identify what exactly went wrond
121 * I did not use intergers as it should be. Instead I added a "namespace"
122 * for each code. This avoids conflicts with error codes from different
123 * classes. How can I use unique error codes and stay conform with PEAR?
125 var $errorCode = array(
126 'not_connected' => array(
127 'code' => 1,
128 'msg' => 'Could not connect to any mail server ({HOST}) at port {PORT} to send mail to {RCPT}.'
130 'failed_vrfy_rcpt' => array(
131 'code' => 2,
132 'msg' => 'Recipient "{RCPT}" could not be veryfied.'
134 'failed_set_from' => array(
135 'code' => 3,
136 'msg' => 'Failed to set sender: {FROM}.'
138 'failed_set_rcpt' => array(
139 'code' => 4,
140 'msg' => 'Failed to set recipient: {RCPT}.'
142 'failed_send_data' => array(
143 'code' => 5,
144 'msg' => 'Failed to send mail to: {RCPT}.'
146 'no_from' => array(
147 'code' => 5,
148 'msg' => 'No from address has be provided.'
150 'send_data' => array(
151 'code' => 7,
152 'msg' => 'Failed to create Net_SMTP object.'
154 'no_mx' => array(
155 'code' => 8,
156 'msg' => 'No MX-record for {RCPT} found.'
158 'no_resolver' => array(
159 'code' => 9,
160 'msg' => 'Could not start resolver! Install PEAR:Net_DNS or switch off "netdns"'
162 'failed_rset' => array(
163 'code' => 10,
164 'msg' => 'RSET command failed, SMTP-connection corrupt.'
169 * Constructor.
171 * Instantiates a new Mail_smtp:: object based on the parameters
172 * passed in. It looks for the following parameters:
173 * mailname The name of the local mail system (a valid hostname which matches the reverse lookup)
174 * port smtp-port - the default comes from getservicebyname() and should work fine
175 * timeout The SMTP connection timeout. Defaults to 30 seconds.
176 * vrfy Whether to use VRFY or not. Defaults to false.
177 * verp Whether to use VERP or not. Defaults to false.
178 * test Activate test mode? Defaults to false.
179 * debug Activate SMTP and Net_DNS debug mode? Defaults to false.
180 * netdns whether to use PEAR:Net_DNS or the PHP build in function getmxrr, default is true
182 * If a parameter is present in the $params array, it replaces the
183 * default.
185 * @access public
186 * @param array Hash containing any parameters different from the
187 * defaults.
188 * @see _Mail_smtpmx()
190 function __construct($params)
192 if (isset($params['mailname'])) {
193 $this->mailname = $params['mailname'];
194 } else {
195 // try to find a valid mailname
196 if (function_exists('posix_uname')) {
197 $uname = posix_uname();
198 $this->mailname = $uname['nodename'];
202 // port number
203 if (isset($params['port'])) {
204 $this->_port = $params['port'];
205 } else {
206 $this->_port = getservbyname('smtp', 'tcp');
209 if (isset($params['timeout'])) $this->timeout = $params['timeout'];
210 if (isset($params['verp'])) $this->verp = $params['verp'];
211 if (isset($params['test'])) $this->test = $params['test'];
212 if (isset($params['peardebug'])) $this->test = $params['peardebug'];
213 if (isset($params['netdns'])) $this->withNetDns = $params['netdns'];
217 * Constructor wrapper for PHP4
219 * @access public
220 * @param array Hash containing any parameters different from the defaults
221 * @see __construct()
223 function Mail_smtpmx($params)
225 $this->__construct($params);
226 register_shutdown_function(array(&$this, '__destruct'));
230 * Destructor implementation to ensure that we disconnect from any
231 * potentially-alive persistent SMTP connections.
233 function __destruct()
235 if (is_object($this->_smtp)) {
236 $this->_smtp->disconnect();
237 $this->_smtp = null;
242 * Implements Mail::send() function using SMTP direct delivery
244 * @access public
245 * @param mixed $recipients in RFC822 style or array
246 * @param array $headers The array of headers to send with the mail.
247 * @param string $body The full text of the message body,
248 * @return mixed Returns true on success, or a PEAR_Error
250 function send($recipients, $headers, $body)
252 if (!is_array($headers)) {
253 return PEAR::raiseError('$headers must be an array');
256 $result = $this->_sanitizeHeaders($headers);
257 if (is_a($result, 'PEAR_Error')) {
258 return $result;
261 // Prepare headers
262 $headerElements = $this->prepareHeaders($headers);
263 if (is_a($headerElements, 'PEAR_Error')) {
264 return $headerElements;
266 list($from, $textHeaders) = $headerElements;
268 // use 'Return-Path' if possible
269 if (!empty($headers['Return-Path'])) {
270 $from = $headers['Return-Path'];
272 if (!isset($from)) {
273 return $this->_raiseError('no_from');
276 // Prepare recipients
277 $recipients = $this->parseRecipients($recipients);
278 if (is_a($recipients, 'PEAR_Error')) {
279 return $recipients;
282 foreach ($recipients as $rcpt) {
283 list($user, $host) = explode('@', $rcpt);
285 $mx = $this->_getMx($host);
286 if (is_a($mx, 'PEAR_Error')) {
287 return $mx;
290 if (empty($mx)) {
291 $info = array('rcpt' => $rcpt);
292 return $this->_raiseError('no_mx', $info);
295 $connected = false;
296 foreach ($mx as $mserver => $mpriority) {
297 $this->_smtp = new Net_SMTP($mserver, $this->port, $this->mailname);
299 // configure the SMTP connection.
300 if ($this->debug) {
301 $this->_smtp->setDebug(true);
304 // attempt to connect to the configured SMTP server.
305 $res = $this->_smtp->connect($this->timeout);
306 if (is_a($res, 'PEAR_Error')) {
307 $this->_smtp = null;
308 continue;
311 // connection established
312 if ($res) {
313 $connected = true;
314 break;
318 if (!$connected) {
319 $info = array(
320 'host' => implode(', ', array_keys($mx)),
321 'port' => $this->port,
322 'rcpt' => $rcpt,
324 return $this->_raiseError('not_connected', $info);
327 // Verify recipient
328 if ($this->vrfy) {
329 $res = $this->_smtp->vrfy($rcpt);
330 if (is_a($res, 'PEAR_Error')) {
331 $info = array('rcpt' => $rcpt);
332 return $this->_raiseError('failed_vrfy_rcpt', $info);
336 // mail from:
337 $args['verp'] = $this->verp;
338 $res = $this->_smtp->mailFrom($from, $args);
339 if (is_a($res, 'PEAR_Error')) {
340 $info = array('from' => $from);
341 return $this->_raiseError('failed_set_from', $info);
344 // rcpt to:
345 $res = $this->_smtp->rcptTo($rcpt);
346 if (is_a($res, 'PEAR_Error')) {
347 $info = array('rcpt' => $rcpt);
348 return $this->_raiseError('failed_set_rcpt', $info);
351 // Don't send anything in test mode
352 if ($this->test) {
353 $result = $this->_smtp->rset();
354 $res = $this->_smtp->rset();
355 if (is_a($res, 'PEAR_Error')) {
356 return $this->_raiseError('failed_rset');
359 $this->_smtp->disconnect();
360 $this->_smtp = null;
361 return true;
364 // Send data
365 $res = $this->_smtp->data("$textHeaders\r\n$body");
366 if (is_a($res, 'PEAR_Error')) {
367 $info = array('rcpt' => $rcpt);
368 return $this->_raiseError('failed_send_data', $info);
371 $this->_smtp->disconnect();
372 $this->_smtp = null;
375 return true;
379 * Recieve mx rexords for a spciefied host
381 * The MX records
383 * @access private
384 * @param string $host mail host
385 * @return mixed sorted
387 function _getMx($host)
389 $mx = array();
391 if ($this->withNetDns) {
392 $res = $this->_loadNetDns();
393 if (is_a($res, 'PEAR_Error')) {
394 return $res;
397 $response = $this->resolver->query($host, 'MX');
398 if (!$response) {
399 return false;
402 foreach ($response->answer as $rr) {
403 if ($rr->type == 'MX') {
404 $mx[$rr->exchange] = $rr->preference;
407 } else {
408 $mxHost = array();
409 $mxWeight = array();
411 if (!getmxrr($host, $mxHost, $mxWeight)) {
412 return false;
414 for ($i = 0; $i < count($mxHost); ++$i) {
415 $mx[$mxHost[$i]] = $mxWeight[$i];
419 asort($mx);
420 return $mx;
424 * initialize PEAR:Net_DNS_Resolver
426 * @access private
427 * @return boolean true on success
429 function _loadNetDns()
431 if (is_object($this->resolver)) {
432 return true;
435 if (!include_once 'Net/DNS.php') {
436 return $this->_raiseError('no_resolver');
439 $this->resolver = new Net_DNS_Resolver();
440 if ($this->debug) {
441 $this->resolver->test = 1;
444 return true;
448 * raise standardized error
450 * include additional information in error message
452 * @access private
453 * @param string $id maps error ids to codes and message
454 * @param array $info optional information in associative array
455 * @see _errorCode
457 function _raiseError($id, $info = array())
459 $code = $this->errorCode[$id]['code'];
460 $msg = $this->errorCode[$id]['msg'];
462 // include info to messages
463 if (!empty($info)) {
464 $search = array();
465 $replace = array();
467 foreach ($info as $key => $value) {
468 array_push($search, '{' . strtoupper($key) . '}');
469 array_push($replace, $value);
472 $msg = str_replace($search, $replace, $msg);
475 return PEAR::raiseError($msg, $code);