smtp.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. <?php
  2. /**
  3. * SMTP implementation of the PEAR Mail interface. Requires the Net_SMTP class.
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * LICENSE:
  8. *
  9. * Copyright (c) 2010, Chuck Hagenbuch
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or without
  13. * modification, are permitted provided that the following conditions
  14. * are met:
  15. *
  16. * o Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. * o Redistributions in binary form must reproduce the above copyright
  19. * notice, this list of conditions and the following disclaimer in the
  20. * documentation and/or other materials provided with the distribution.
  21. * o The names of the authors may not be used to endorse or promote
  22. * products derived from this software without specific prior written
  23. * permission.
  24. *
  25. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  26. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  27. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  28. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  29. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  30. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  31. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  32. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  33. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  34. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  35. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. * @category HTTP
  38. * @package HTTP_Request
  39. * @author Jon Parise <jon@php.net>
  40. * @author Chuck Hagenbuch <chuck@horde.org>
  41. * @copyright 2010 Chuck Hagenbuch
  42. * @license http://opensource.org/licenses/bsd-license.php New BSD License
  43. * @version CVS: $Id: smtp.php 294747 2010-02-08 08:18:33Z clockwerx $
  44. * @link http://pear.php.net/package/Mail/
  45. */
  46. /** Error: Failed to create a Net_SMTP object */
  47. define('PEAR_MAIL_SMTP_ERROR_CREATE', 10000);
  48. /** Error: Failed to connect to SMTP server */
  49. define('PEAR_MAIL_SMTP_ERROR_CONNECT', 10001);
  50. /** Error: SMTP authentication failure */
  51. define('PEAR_MAIL_SMTP_ERROR_AUTH', 10002);
  52. /** Error: No From: address has been provided */
  53. define('PEAR_MAIL_SMTP_ERROR_FROM', 10003);
  54. /** Error: Failed to set sender */
  55. define('PEAR_MAIL_SMTP_ERROR_SENDER', 10004);
  56. /** Error: Failed to add recipient */
  57. define('PEAR_MAIL_SMTP_ERROR_RECIPIENT', 10005);
  58. /** Error: Failed to send data */
  59. define('PEAR_MAIL_SMTP_ERROR_DATA', 10006);
  60. /**
  61. * SMTP implementation of the PEAR Mail interface. Requires the Net_SMTP class.
  62. * @access public
  63. * @package Mail
  64. * @version $Revision: 294747 $
  65. */
  66. class Mail_smtp extends Mail {
  67. /**
  68. * SMTP connection object.
  69. *
  70. * @var object
  71. * @access private
  72. */
  73. var $_smtp = null;
  74. /**
  75. * The list of service extension parameters to pass to the Net_SMTP
  76. * mailFrom() command.
  77. * @var array
  78. */
  79. var $_extparams = array();
  80. /**
  81. * The SMTP host to connect to.
  82. * @var string
  83. */
  84. var $host = 'localhost';
  85. /**
  86. * The port the SMTP server is on.
  87. * @var integer
  88. */
  89. var $port = 25;
  90. /**
  91. * Should SMTP authentication be used?
  92. *
  93. * This value may be set to true, false or the name of a specific
  94. * authentication method.
  95. *
  96. * If the value is set to true, the Net_SMTP package will attempt to use
  97. * the best authentication method advertised by the remote SMTP server.
  98. *
  99. * @var mixed
  100. */
  101. var $auth = false;
  102. /**
  103. * The username to use if the SMTP server requires authentication.
  104. * @var string
  105. */
  106. var $username = '';
  107. /**
  108. * The password to use if the SMTP server requires authentication.
  109. * @var string
  110. */
  111. var $password = '';
  112. /**
  113. * Hostname or domain that will be sent to the remote SMTP server in the
  114. * HELO / EHLO message.
  115. *
  116. * @var string
  117. */
  118. var $localhost = 'localhost';
  119. /**
  120. * SMTP connection timeout value. NULL indicates no timeout.
  121. *
  122. * @var integer
  123. */
  124. var $timeout = null;
  125. /**
  126. * Turn on Net_SMTP debugging?
  127. *
  128. * @var boolean $debug
  129. */
  130. var $debug = false;
  131. /**
  132. * Indicates whether or not the SMTP connection should persist over
  133. * multiple calls to the send() method.
  134. *
  135. * @var boolean
  136. */
  137. var $persist = false;
  138. /**
  139. * Use SMTP command pipelining (specified in RFC 2920) if the SMTP server
  140. * supports it. This speeds up delivery over high-latency connections. By
  141. * default, use the default value supplied by Net_SMTP.
  142. * @var bool
  143. */
  144. var $pipelining;
  145. /**
  146. * Constructor.
  147. *
  148. * Instantiates a new Mail_smtp:: object based on the parameters
  149. * passed in. It looks for the following parameters:
  150. * host The server to connect to. Defaults to localhost.
  151. * port The port to connect to. Defaults to 25.
  152. * auth SMTP authentication. Defaults to none.
  153. * username The username to use for SMTP auth. No default.
  154. * password The password to use for SMTP auth. No default.
  155. * localhost The local hostname / domain. Defaults to localhost.
  156. * timeout The SMTP connection timeout. Defaults to none.
  157. * verp Whether to use VERP or not. Defaults to false.
  158. * DEPRECATED as of 1.2.0 (use setMailParams()).
  159. * debug Activate SMTP debug mode? Defaults to false.
  160. * persist Should the SMTP connection persist?
  161. * pipelining Use SMTP command pipelining
  162. *
  163. * If a parameter is present in the $params array, it replaces the
  164. * default.
  165. *
  166. * @param array Hash containing any parameters different from the
  167. * defaults.
  168. * @access public
  169. */
  170. function Mail_smtp($params)
  171. {
  172. if (isset($params['host'])) $this->host = $params['host'];
  173. if (isset($params['port'])) $this->port = $params['port'];
  174. if (isset($params['auth'])) $this->auth = $params['auth'];
  175. if (isset($params['username'])) $this->username = $params['username'];
  176. if (isset($params['password'])) $this->password = $params['password'];
  177. if (isset($params['localhost'])) $this->localhost = $params['localhost'];
  178. if (isset($params['timeout'])) $this->timeout = $params['timeout'];
  179. if (isset($params['debug'])) $this->debug = (bool)$params['debug'];
  180. if (isset($params['persist'])) $this->persist = (bool)$params['persist'];
  181. if (isset($params['pipelining'])) $this->pipelining = (bool)$params['pipelining'];
  182. // Deprecated options
  183. if (isset($params['verp'])) {
  184. $this->addServiceExtensionParameter('XVERP', is_bool($params['verp']) ? null : $params['verp']);
  185. }
  186. register_shutdown_function(array(&$this, '_Mail_smtp'));
  187. }
  188. /**
  189. * Destructor implementation to ensure that we disconnect from any
  190. * potentially-alive persistent SMTP connections.
  191. */
  192. function _Mail_smtp()
  193. {
  194. $this->disconnect();
  195. }
  196. /**
  197. * Implements Mail::send() function using SMTP.
  198. *
  199. * @param mixed $recipients Either a comma-seperated list of recipients
  200. * (RFC822 compliant), or an array of recipients,
  201. * each RFC822 valid. This may contain recipients not
  202. * specified in the headers, for Bcc:, resending
  203. * messages, etc.
  204. *
  205. * @param array $headers The array of headers to send with the mail, in an
  206. * associative array, where the array key is the
  207. * header name (e.g., 'Subject'), and the array value
  208. * is the header value (e.g., 'test'). The header
  209. * produced from those values would be 'Subject:
  210. * test'.
  211. *
  212. * @param string $body The full text of the message body, including any
  213. * MIME parts, etc.
  214. *
  215. * @return mixed Returns true on success, or a PEAR_Error
  216. * containing a descriptive error message on
  217. * failure.
  218. * @access public
  219. */
  220. function send($recipients, $headers, $body)
  221. {
  222. /* If we don't already have an SMTP object, create one. */
  223. $result = &$this->getSMTPObject();
  224. if (PEAR::isError($result)) {
  225. return $result;
  226. }
  227. if (!is_array($headers)) {
  228. return PEAR::raiseError('$headers must be an array');
  229. }
  230. $this->_sanitizeHeaders($headers);
  231. $headerElements = $this->prepareHeaders($headers);
  232. if (is_a($headerElements, 'PEAR_Error')) {
  233. $this->_smtp->rset();
  234. return $headerElements;
  235. }
  236. list($from, $textHeaders) = $headerElements;
  237. /* Since few MTAs are going to allow this header to be forged
  238. * unless it's in the MAIL FROM: exchange, we'll use
  239. * Return-Path instead of From: if it's set. */
  240. if (!empty($headers['Return-Path'])) {
  241. $from = $headers['Return-Path'];
  242. }
  243. if (!isset($from)) {
  244. $this->_smtp->rset();
  245. return PEAR::raiseError('No From: address has been provided',
  246. PEAR_MAIL_SMTP_ERROR_FROM);
  247. }
  248. $params = null;
  249. if (!empty($this->_extparams)) {
  250. foreach ($this->_extparams as $key => $val) {
  251. $params .= ' ' . $key . (is_null($val) ? '' : '=' . $val);
  252. }
  253. }
  254. if (PEAR::isError($res = $this->_smtp->mailFrom($from, ltrim($params)))) {
  255. $error = $this->_error("Failed to set sender: $from", $res);
  256. $this->_smtp->rset();
  257. return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_SENDER);
  258. }
  259. $recipients = $this->parseRecipients($recipients);
  260. if (is_a($recipients, 'PEAR_Error')) {
  261. $this->_smtp->rset();
  262. return $recipients;
  263. }
  264. foreach ($recipients as $recipient) {
  265. $res = $this->_smtp->rcptTo($recipient);
  266. if (is_a($res, 'PEAR_Error')) {
  267. $error = $this->_error("Failed to add recipient: $recipient", $res);
  268. $this->_smtp->rset();
  269. return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_RECIPIENT);
  270. }
  271. }
  272. /* Send the message's headers and the body as SMTP data. */
  273. $res = $this->_smtp->data($textHeaders . "\r\n\r\n" . $body);
  274. list(,$args) = $this->_smtp->getResponse();
  275. if (preg_match("/Ok: queued as (.*)/", $args, $queued)) {
  276. $this->queued_as = $queued[1];
  277. }
  278. /* we need the greeting; from it we can extract the authorative name of the mail server we've really connected to.
  279. * ideal if we're connecting to a round-robin of relay servers and need to track which exact one took the email */
  280. $this->greeting = $this->_smtp->getGreeting();
  281. if (is_a($res, 'PEAR_Error')) {
  282. $error = $this->_error('Failed to send data', $res);
  283. $this->_smtp->rset();
  284. return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_DATA);
  285. }
  286. /* If persistent connections are disabled, destroy our SMTP object. */
  287. if ($this->persist === false) {
  288. $this->disconnect();
  289. }
  290. return true;
  291. }
  292. /**
  293. * Connect to the SMTP server by instantiating a Net_SMTP object.
  294. *
  295. * @return mixed Returns a reference to the Net_SMTP object on success, or
  296. * a PEAR_Error containing a descriptive error message on
  297. * failure.
  298. *
  299. * @since 1.2.0
  300. * @access public
  301. */
  302. function &getSMTPObject()
  303. {
  304. if (is_object($this->_smtp) !== false) {
  305. return $this->_smtp;
  306. }
  307. include_once 'Net/SMTP.php';
  308. $this->_smtp = &new Net_SMTP($this->host,
  309. $this->port,
  310. $this->localhost);
  311. /* If we still don't have an SMTP object at this point, fail. */
  312. if (is_object($this->_smtp) === false) {
  313. return PEAR::raiseError('Failed to create a Net_SMTP object',
  314. PEAR_MAIL_SMTP_ERROR_CREATE);
  315. }
  316. /* Configure the SMTP connection. */
  317. if ($this->debug) {
  318. $this->_smtp->setDebug(true);
  319. }
  320. /* Attempt to connect to the configured SMTP server. */
  321. if (PEAR::isError($res = $this->_smtp->connect($this->timeout))) {
  322. $error = $this->_error('Failed to connect to ' .
  323. $this->host . ':' . $this->port,
  324. $res);
  325. return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_CONNECT);
  326. }
  327. /* Attempt to authenticate if authentication has been enabled. */
  328. if ($this->auth) {
  329. $method = is_string($this->auth) ? $this->auth : '';
  330. if (PEAR::isError($res = $this->_smtp->auth($this->username,
  331. $this->password,
  332. $method))) {
  333. $error = $this->_error("$method authentication failure",
  334. $res);
  335. $this->_smtp->rset();
  336. return PEAR::raiseError($error, PEAR_MAIL_SMTP_ERROR_AUTH);
  337. }
  338. }
  339. return $this->_smtp;
  340. }
  341. /**
  342. * Add parameter associated with a SMTP service extension.
  343. *
  344. * @param string Extension keyword.
  345. * @param string Any value the keyword needs.
  346. *
  347. * @since 1.2.0
  348. * @access public
  349. */
  350. function addServiceExtensionParameter($keyword, $value = null)
  351. {
  352. $this->_extparams[$keyword] = $value;
  353. }
  354. /**
  355. * Disconnect and destroy the current SMTP connection.
  356. *
  357. * @return boolean True if the SMTP connection no longer exists.
  358. *
  359. * @since 1.1.9
  360. * @access public
  361. */
  362. function disconnect()
  363. {
  364. /* If we have an SMTP object, disconnect and destroy it. */
  365. if (is_object($this->_smtp) && $this->_smtp->disconnect()) {
  366. $this->_smtp = null;
  367. }
  368. /* We are disconnected if we no longer have an SMTP object. */
  369. return ($this->_smtp === null);
  370. }
  371. /**
  372. * Build a standardized string describing the current SMTP error.
  373. *
  374. * @param string $text Custom string describing the error context.
  375. * @param object $error Reference to the current PEAR_Error object.
  376. *
  377. * @return string A string describing the current SMTP error.
  378. *
  379. * @since 1.1.7
  380. * @access private
  381. */
  382. function _error($text, &$error)
  383. {
  384. /* Split the SMTP response into a code and a response string. */
  385. list($code, $response) = $this->_smtp->getResponse();
  386. /* Build our standardized error string. */
  387. return $text
  388. . ' [SMTP: ' . $error->getMessage()
  389. . " (code: $code, response: $response)]";
  390. }
  391. }