PageRenderTime 61ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/ railoapacheportable/php/PEAR/Net/SMTP.php

http://railoapacheportable.googlecode.com/
PHP | 1133 lines | 488 code | 119 blank | 526 comment | 101 complexity | a85bf9942adc0331dfa6f82e051e7ae4 MD5 | raw file
Possible License(s): BSD-2-Clause, Apache-2.0, EPL-1.0, LGPL-3.0, GPL-3.0, AGPL-1.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, GPL-2.0, LGPL-2.1, BSD-3-Clause, AGPL-3.0, CC-BY-SA-3.0
  1. <?php
  2. /* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Chuck Hagenbuch <chuck@horde.org> |
  17. // | Jon Parise <jon@php.net> |
  18. // | Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar> |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: SMTP.php 284052 2009-07-14 05:34:26Z jon $
  22. require_once 'PEAR.php';
  23. require_once 'Net/Socket.php';
  24. /**
  25. * Provides an implementation of the SMTP protocol using PEAR's
  26. * Net_Socket:: class.
  27. *
  28. * @package Net_SMTP
  29. * @author Chuck Hagenbuch <chuck@horde.org>
  30. * @author Jon Parise <jon@php.net>
  31. * @author Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>
  32. *
  33. * @example basic.php A basic implementation of the Net_SMTP package.
  34. */
  35. class Net_SMTP
  36. {
  37. /**
  38. * The server to connect to.
  39. * @var string
  40. * @access public
  41. */
  42. var $host = 'localhost';
  43. /**
  44. * The port to connect to.
  45. * @var int
  46. * @access public
  47. */
  48. var $port = 25;
  49. /**
  50. * The value to give when sending EHLO or HELO.
  51. * @var string
  52. * @access public
  53. */
  54. var $localhost = 'localhost';
  55. /**
  56. * List of supported authentication methods, in preferential order.
  57. * @var array
  58. * @access public
  59. */
  60. var $auth_methods = array('DIGEST-MD5', 'CRAM-MD5', 'LOGIN', 'PLAIN');
  61. /**
  62. * Use SMTP command pipelining (specified in RFC 2920) if the SMTP
  63. * server supports it.
  64. *
  65. * When pipeling is enabled, rcptTo(), mailFrom(), sendFrom(),
  66. * somlFrom() and samlFrom() do not wait for a response from the
  67. * SMTP server but return immediately.
  68. *
  69. * @var bool
  70. * @access public
  71. */
  72. var $pipelining = false;
  73. /**
  74. * Number of pipelined commands.
  75. * @var int
  76. * @access private
  77. */
  78. var $_pipelined_commands = 0;
  79. /**
  80. * Should debugging output be enabled?
  81. * @var boolean
  82. * @access private
  83. */
  84. var $_debug = false;
  85. /**
  86. * Debug output handler.
  87. * @var callback
  88. * @access private
  89. */
  90. var $_debug_handler = null;
  91. /**
  92. * The socket resource being used to connect to the SMTP server.
  93. * @var resource
  94. * @access private
  95. */
  96. var $_socket = null;
  97. /**
  98. * The most recent server response code.
  99. * @var int
  100. * @access private
  101. */
  102. var $_code = -1;
  103. /**
  104. * The most recent server response arguments.
  105. * @var array
  106. * @access private
  107. */
  108. var $_arguments = array();
  109. /**
  110. * Stores the SMTP server's greeting string.
  111. * @var string
  112. * @access private
  113. */
  114. var $_greeting = null;
  115. /**
  116. * Stores detected features of the SMTP server.
  117. * @var array
  118. * @access private
  119. */
  120. var $_esmtp = array();
  121. /**
  122. * Instantiates a new Net_SMTP object, overriding any defaults
  123. * with parameters that are passed in.
  124. *
  125. * If you have SSL support in PHP, you can connect to a server
  126. * over SSL using an 'ssl://' prefix:
  127. *
  128. * // 465 is a common smtps port.
  129. * $smtp = new Net_SMTP('ssl://mail.host.com', 465);
  130. * $smtp->connect();
  131. *
  132. * @param string $host The server to connect to.
  133. * @param integer $port The port to connect to.
  134. * @param string $localhost The value to give when sending EHLO or HELO.
  135. * @param boolean $pipeling Use SMTP command pipelining
  136. *
  137. * @access public
  138. * @since 1.0
  139. */
  140. function Net_SMTP($host = null, $port = null, $localhost = null, $pipelining = false)
  141. {
  142. if (isset($host)) {
  143. $this->host = $host;
  144. }
  145. if (isset($port)) {
  146. $this->port = $port;
  147. }
  148. if (isset($localhost)) {
  149. $this->localhost = $localhost;
  150. }
  151. $this->pipelining = $pipelining;
  152. $this->_socket = new Net_Socket();
  153. /* Include the Auth_SASL package. If the package is not
  154. * available, we disable the authentication methods that
  155. * depend upon it. */
  156. if ((@include_once 'Auth/SASL.php') === false) {
  157. $pos = array_search('DIGEST-MD5', $this->auth_methods);
  158. unset($this->auth_methods[$pos]);
  159. $pos = array_search('CRAM-MD5', $this->auth_methods);
  160. unset($this->auth_methods[$pos]);
  161. }
  162. }
  163. /**
  164. * Set the value of the debugging flag.
  165. *
  166. * @param boolean $debug New value for the debugging flag.
  167. *
  168. * @access public
  169. * @since 1.1.0
  170. */
  171. function setDebug($debug, $handler = null)
  172. {
  173. $this->_debug = $debug;
  174. $this->_debug_handler = $handler;
  175. }
  176. /**
  177. * Write the given debug text to the current debug output handler.
  178. *
  179. * @param string $message Debug mesage text.
  180. *
  181. * @access private
  182. * @since 1.3.3
  183. */
  184. function _debug($message)
  185. {
  186. if ($this->_debug) {
  187. if ($this->_debug_handler) {
  188. call_user_func_array($this->_debug_handler,
  189. array(&$this, $message));
  190. } else {
  191. echo "DEBUG: $message\n";
  192. }
  193. }
  194. }
  195. /**
  196. * Send the given string of data to the server.
  197. *
  198. * @param string $data The string of data to send.
  199. *
  200. * @return mixed True on success or a PEAR_Error object on failure.
  201. *
  202. * @access private
  203. * @since 1.1.0
  204. */
  205. function _send($data)
  206. {
  207. $this->_debug("Send: $data");
  208. if (PEAR::isError($error = $this->_socket->write($data))) {
  209. return PEAR::raiseError('Failed to write to socket: ' .
  210. $error->getMessage());
  211. }
  212. return true;
  213. }
  214. /**
  215. * Send a command to the server with an optional string of
  216. * arguments. A carriage return / linefeed (CRLF) sequence will
  217. * be appended to each command string before it is sent to the
  218. * SMTP server - an error will be thrown if the command string
  219. * already contains any newline characters. Use _send() for
  220. * commands that must contain newlines.
  221. *
  222. * @param string $command The SMTP command to send to the server.
  223. * @param string $args A string of optional arguments to append
  224. * to the command.
  225. *
  226. * @return mixed The result of the _send() call.
  227. *
  228. * @access private
  229. * @since 1.1.0
  230. */
  231. function _put($command, $args = '')
  232. {
  233. if (!empty($args)) {
  234. $command .= ' ' . $args;
  235. }
  236. if (strcspn($command, "\r\n") !== strlen($command)) {
  237. return PEAR::raiseError('Commands cannot contain newlines');
  238. }
  239. return $this->_send($command . "\r\n");
  240. }
  241. /**
  242. * Read a reply from the SMTP server. The reply consists of a response
  243. * code and a response message.
  244. *
  245. * @param mixed $valid The set of valid response codes. These
  246. * may be specified as an array of integer
  247. * values or as a single integer value.
  248. * @param bool $later Do not parse the response now, but wait
  249. * until the last command in the pipelined
  250. * command group
  251. *
  252. * @return mixed True if the server returned a valid response code or
  253. * a PEAR_Error object is an error condition is reached.
  254. *
  255. * @access private
  256. * @since 1.1.0
  257. *
  258. * @see getResponse
  259. */
  260. function _parseResponse($valid, $later = false)
  261. {
  262. $this->_code = -1;
  263. $this->_arguments = array();
  264. if ($later) {
  265. $this->_pipelined_commands++;
  266. return true;
  267. }
  268. for ($i = 0; $i <= $this->_pipelined_commands; $i++) {
  269. while ($line = $this->_socket->readLine()) {
  270. $this->_debug("Recv: $line");
  271. /* If we receive an empty line, the connection has been closed. */
  272. if (empty($line)) {
  273. $this->disconnect();
  274. return PEAR::raiseError('Connection was unexpectedly closed');
  275. }
  276. /* Read the code and store the rest in the arguments array. */
  277. $code = substr($line, 0, 3);
  278. $this->_arguments[] = trim(substr($line, 4));
  279. /* Check the syntax of the response code. */
  280. if (is_numeric($code)) {
  281. $this->_code = (int)$code;
  282. } else {
  283. $this->_code = -1;
  284. break;
  285. }
  286. /* If this is not a multiline response, we're done. */
  287. if (substr($line, 3, 1) != '-') {
  288. break;
  289. }
  290. }
  291. }
  292. $this->_pipelined_commands = 0;
  293. /* Compare the server's response code with the valid code/codes. */
  294. if (is_int($valid) && ($this->_code === $valid)) {
  295. return true;
  296. } elseif (is_array($valid) && in_array($this->_code, $valid, true)) {
  297. return true;
  298. }
  299. return PEAR::raiseError('Invalid response code received from server',
  300. $this->_code);
  301. }
  302. /**
  303. * Return a 2-tuple containing the last response from the SMTP server.
  304. *
  305. * @return array A two-element array: the first element contains the
  306. * response code as an integer and the second element
  307. * contains the response's arguments as a string.
  308. *
  309. * @access public
  310. * @since 1.1.0
  311. */
  312. function getResponse()
  313. {
  314. return array($this->_code, join("\n", $this->_arguments));
  315. }
  316. /**
  317. * Return the SMTP server's greeting string.
  318. *
  319. * @return string A string containing the greeting string, or null if a
  320. * greeting has not been received.
  321. *
  322. * @access public
  323. * @since 1.3.3
  324. */
  325. function getGreeting()
  326. {
  327. return $this->_greeting;
  328. }
  329. /**
  330. * Attempt to connect to the SMTP server.
  331. *
  332. * @param int $timeout The timeout value (in seconds) for the
  333. * socket connection.
  334. * @param bool $persistent Should a persistent socket connection
  335. * be used?
  336. *
  337. * @return mixed Returns a PEAR_Error with an error message on any
  338. * kind of failure, or true on success.
  339. * @access public
  340. * @since 1.0
  341. */
  342. function connect($timeout = null, $persistent = false)
  343. {
  344. $this->_greeting = null;
  345. $result = $this->_socket->connect($this->host, $this->port,
  346. $persistent, $timeout);
  347. if (PEAR::isError($result)) {
  348. return PEAR::raiseError('Failed to connect socket: ' .
  349. $result->getMessage());
  350. }
  351. if (PEAR::isError($error = $this->_parseResponse(220))) {
  352. return $error;
  353. }
  354. /* Extract and store a copy of the server's greeting string. */
  355. list(, $this->_greeting) = $this->getResponse();
  356. if (PEAR::isError($error = $this->_negotiate())) {
  357. return $error;
  358. }
  359. return true;
  360. }
  361. /**
  362. * Attempt to disconnect from the SMTP server.
  363. *
  364. * @return mixed Returns a PEAR_Error with an error message on any
  365. * kind of failure, or true on success.
  366. * @access public
  367. * @since 1.0
  368. */
  369. function disconnect()
  370. {
  371. if (PEAR::isError($error = $this->_put('QUIT'))) {
  372. return $error;
  373. }
  374. if (PEAR::isError($error = $this->_parseResponse(221))) {
  375. return $error;
  376. }
  377. if (PEAR::isError($error = $this->_socket->disconnect())) {
  378. return PEAR::raiseError('Failed to disconnect socket: ' .
  379. $error->getMessage());
  380. }
  381. return true;
  382. }
  383. /**
  384. * Attempt to send the EHLO command and obtain a list of ESMTP
  385. * extensions available, and failing that just send HELO.
  386. *
  387. * @return mixed Returns a PEAR_Error with an error message on any
  388. * kind of failure, or true on success.
  389. *
  390. * @access private
  391. * @since 1.1.0
  392. */
  393. function _negotiate()
  394. {
  395. if (PEAR::isError($error = $this->_put('EHLO', $this->localhost))) {
  396. return $error;
  397. }
  398. if (PEAR::isError($this->_parseResponse(250))) {
  399. /* If we receive a 503 response, we're already authenticated. */
  400. if ($this->_code === 503) {
  401. return true;
  402. }
  403. /* If the EHLO failed, try the simpler HELO command. */
  404. if (PEAR::isError($error = $this->_put('HELO', $this->localhost))) {
  405. return $error;
  406. }
  407. if (PEAR::isError($this->_parseResponse(250))) {
  408. return PEAR::raiseError('HELO was not accepted: ', $this->_code);
  409. }
  410. return true;
  411. }
  412. foreach ($this->_arguments as $argument) {
  413. $verb = strtok($argument, ' ');
  414. $arguments = substr($argument, strlen($verb) + 1,
  415. strlen($argument) - strlen($verb) - 1);
  416. $this->_esmtp[$verb] = $arguments;
  417. }
  418. if (!isset($this->_esmtp['PIPELINING'])) {
  419. $this->pipelining = false;
  420. }
  421. return true;
  422. }
  423. /**
  424. * Returns the name of the best authentication method that the server
  425. * has advertised.
  426. *
  427. * @return mixed Returns a string containing the name of the best
  428. * supported authentication method or a PEAR_Error object
  429. * if a failure condition is encountered.
  430. * @access private
  431. * @since 1.1.0
  432. */
  433. function _getBestAuthMethod()
  434. {
  435. $available_methods = explode(' ', $this->_esmtp['AUTH']);
  436. foreach ($this->auth_methods as $method) {
  437. if (in_array($method, $available_methods)) {
  438. return $method;
  439. }
  440. }
  441. return PEAR::raiseError('No supported authentication methods');
  442. }
  443. /**
  444. * Attempt to do SMTP authentication.
  445. *
  446. * @param string The userid to authenticate as.
  447. * @param string The password to authenticate with.
  448. * @param string The requested authentication method. If none is
  449. * specified, the best supported method will be used.
  450. *
  451. * @return mixed Returns a PEAR_Error with an error message on any
  452. * kind of failure, or true on success.
  453. * @access public
  454. * @since 1.0
  455. */
  456. function auth($uid, $pwd , $method = '')
  457. {
  458. /* We can only attempt a TLS connection if we're running PHP 5.1.0 or
  459. * later, have access to the OpenSSL extension, are connected to an
  460. * SMTP server which supports the STARTTLS extension, and aren't
  461. * already connected over a secure (SSL) socket connection. */
  462. $tls = version_compare(PHP_VERSION, '5.1.0', '>=') && extension_loaded('openssl') &&
  463. isset($this->_esmtp['STARTTLS']) && strncasecmp($this->host, 'ssl://', 6) != 0;
  464. if ($tls) {
  465. if (PEAR::isError($result = $this->_put('STARTTLS'))) {
  466. return $result;
  467. }
  468. if (PEAR::isError($result = $this->_parseResponse(220))) {
  469. return $result;
  470. }
  471. if (PEAR::isError($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT))) {
  472. return $result;
  473. } elseif ($result !== true) {
  474. return PEAR::raiseError('STARTTLS failed');
  475. }
  476. /* Send EHLO again to recieve the AUTH string from the
  477. * SMTP server. */
  478. $this->_negotiate();
  479. }
  480. if (empty($this->_esmtp['AUTH'])) {
  481. return PEAR::raiseError('SMTP server does not support authentication');
  482. }
  483. /* If no method has been specified, get the name of the best
  484. * supported method advertised by the SMTP server. */
  485. if (empty($method)) {
  486. if (PEAR::isError($method = $this->_getBestAuthMethod())) {
  487. /* Return the PEAR_Error object from _getBestAuthMethod(). */
  488. return $method;
  489. }
  490. } else {
  491. $method = strtoupper($method);
  492. if (!in_array($method, $this->auth_methods)) {
  493. return PEAR::raiseError("$method is not a supported authentication method");
  494. }
  495. }
  496. switch ($method) {
  497. case 'DIGEST-MD5':
  498. $result = $this->_authDigest_MD5($uid, $pwd);
  499. break;
  500. case 'CRAM-MD5':
  501. $result = $this->_authCRAM_MD5($uid, $pwd);
  502. break;
  503. case 'LOGIN':
  504. $result = $this->_authLogin($uid, $pwd);
  505. break;
  506. case 'PLAIN':
  507. $result = $this->_authPlain($uid, $pwd);
  508. break;
  509. default:
  510. $result = PEAR::raiseError("$method is not a supported authentication method");
  511. break;
  512. }
  513. /* If an error was encountered, return the PEAR_Error object. */
  514. if (PEAR::isError($result)) {
  515. return $result;
  516. }
  517. return true;
  518. }
  519. /**
  520. * Authenticates the user using the DIGEST-MD5 method.
  521. *
  522. * @param string The userid to authenticate as.
  523. * @param string The password to authenticate with.
  524. *
  525. * @return mixed Returns a PEAR_Error with an error message on any
  526. * kind of failure, or true on success.
  527. * @access private
  528. * @since 1.1.0
  529. */
  530. function _authDigest_MD5($uid, $pwd)
  531. {
  532. if (PEAR::isError($error = $this->_put('AUTH', 'DIGEST-MD5'))) {
  533. return $error;
  534. }
  535. /* 334: Continue authentication request */
  536. if (PEAR::isError($error = $this->_parseResponse(334))) {
  537. /* 503: Error: already authenticated */
  538. if ($this->_code === 503) {
  539. return true;
  540. }
  541. return $error;
  542. }
  543. $challenge = base64_decode($this->_arguments[0]);
  544. $digest = &Auth_SASL::factory('digestmd5');
  545. $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge,
  546. $this->host, "smtp"));
  547. if (PEAR::isError($error = $this->_put($auth_str))) {
  548. return $error;
  549. }
  550. /* 334: Continue authentication request */
  551. if (PEAR::isError($error = $this->_parseResponse(334))) {
  552. return $error;
  553. }
  554. /* We don't use the protocol's third step because SMTP doesn't
  555. * allow subsequent authentication, so we just silently ignore
  556. * it. */
  557. if (PEAR::isError($error = $this->_put(''))) {
  558. return $error;
  559. }
  560. /* 235: Authentication successful */
  561. if (PEAR::isError($error = $this->_parseResponse(235))) {
  562. return $error;
  563. }
  564. }
  565. /**
  566. * Authenticates the user using the CRAM-MD5 method.
  567. *
  568. * @param string The userid to authenticate as.
  569. * @param string The password to authenticate with.
  570. *
  571. * @return mixed Returns a PEAR_Error with an error message on any
  572. * kind of failure, or true on success.
  573. * @access private
  574. * @since 1.1.0
  575. */
  576. function _authCRAM_MD5($uid, $pwd)
  577. {
  578. if (PEAR::isError($error = $this->_put('AUTH', 'CRAM-MD5'))) {
  579. return $error;
  580. }
  581. /* 334: Continue authentication request */
  582. if (PEAR::isError($error = $this->_parseResponse(334))) {
  583. /* 503: Error: already authenticated */
  584. if ($this->_code === 503) {
  585. return true;
  586. }
  587. return $error;
  588. }
  589. $challenge = base64_decode($this->_arguments[0]);
  590. $cram = &Auth_SASL::factory('crammd5');
  591. $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge));
  592. if (PEAR::isError($error = $this->_put($auth_str))) {
  593. return $error;
  594. }
  595. /* 235: Authentication successful */
  596. if (PEAR::isError($error = $this->_parseResponse(235))) {
  597. return $error;
  598. }
  599. }
  600. /**
  601. * Authenticates the user using the LOGIN method.
  602. *
  603. * @param string The userid to authenticate as.
  604. * @param string The password to authenticate with.
  605. *
  606. * @return mixed Returns a PEAR_Error with an error message on any
  607. * kind of failure, or true on success.
  608. * @access private
  609. * @since 1.1.0
  610. */
  611. function _authLogin($uid, $pwd)
  612. {
  613. if (PEAR::isError($error = $this->_put('AUTH', 'LOGIN'))) {
  614. return $error;
  615. }
  616. /* 334: Continue authentication request */
  617. if (PEAR::isError($error = $this->_parseResponse(334))) {
  618. /* 503: Error: already authenticated */
  619. if ($this->_code === 503) {
  620. return true;
  621. }
  622. return $error;
  623. }
  624. if (PEAR::isError($error = $this->_put(base64_encode($uid)))) {
  625. return $error;
  626. }
  627. /* 334: Continue authentication request */
  628. if (PEAR::isError($error = $this->_parseResponse(334))) {
  629. return $error;
  630. }
  631. if (PEAR::isError($error = $this->_put(base64_encode($pwd)))) {
  632. return $error;
  633. }
  634. /* 235: Authentication successful */
  635. if (PEAR::isError($error = $this->_parseResponse(235))) {
  636. return $error;
  637. }
  638. return true;
  639. }
  640. /**
  641. * Authenticates the user using the PLAIN method.
  642. *
  643. * @param string The userid to authenticate as.
  644. * @param string The password to authenticate with.
  645. *
  646. * @return mixed Returns a PEAR_Error with an error message on any
  647. * kind of failure, or true on success.
  648. * @access private
  649. * @since 1.1.0
  650. */
  651. function _authPlain($uid, $pwd)
  652. {
  653. if (PEAR::isError($error = $this->_put('AUTH', 'PLAIN'))) {
  654. return $error;
  655. }
  656. /* 334: Continue authentication request */
  657. if (PEAR::isError($error = $this->_parseResponse(334))) {
  658. /* 503: Error: already authenticated */
  659. if ($this->_code === 503) {
  660. return true;
  661. }
  662. return $error;
  663. }
  664. $auth_str = base64_encode(chr(0) . $uid . chr(0) . $pwd);
  665. if (PEAR::isError($error = $this->_put($auth_str))) {
  666. return $error;
  667. }
  668. /* 235: Authentication successful */
  669. if (PEAR::isError($error = $this->_parseResponse(235))) {
  670. return $error;
  671. }
  672. return true;
  673. }
  674. /**
  675. * Send the HELO command.
  676. *
  677. * @param string The domain name to say we are.
  678. *
  679. * @return mixed Returns a PEAR_Error with an error message on any
  680. * kind of failure, or true on success.
  681. * @access public
  682. * @since 1.0
  683. */
  684. function helo($domain)
  685. {
  686. if (PEAR::isError($error = $this->_put('HELO', $domain))) {
  687. return $error;
  688. }
  689. if (PEAR::isError($error = $this->_parseResponse(250))) {
  690. return $error;
  691. }
  692. return true;
  693. }
  694. /**
  695. * Return the list of SMTP service extensions advertised by the server.
  696. *
  697. * @return array The list of SMTP service extensions.
  698. * @access public
  699. * @since 1.3
  700. */
  701. function getServiceExtensions()
  702. {
  703. return $this->_esmtp;
  704. }
  705. /**
  706. * Send the MAIL FROM: command.
  707. *
  708. * @param string $sender The sender (reverse path) to set.
  709. * @param string $params String containing additional MAIL parameters,
  710. * such as the NOTIFY flags defined by RFC 1891
  711. * or the VERP protocol.
  712. *
  713. * If $params is an array, only the 'verp' option
  714. * is supported. If 'verp' is true, the XVERP
  715. * parameter is appended to the MAIL command. If
  716. * the 'verp' value is a string, the full
  717. * XVERP=value parameter is appended.
  718. *
  719. * @return mixed Returns a PEAR_Error with an error message on any
  720. * kind of failure, or true on success.
  721. * @access public
  722. * @since 1.0
  723. */
  724. function mailFrom($sender, $params = null)
  725. {
  726. $args = "FROM:<$sender>";
  727. /* Support the deprecated array form of $params. */
  728. if (is_array($params) && isset($params['verp'])) {
  729. /* XVERP */
  730. if ($params['verp'] === true) {
  731. $args .= ' XVERP';
  732. /* XVERP=something */
  733. } elseif (trim($params['verp'])) {
  734. $args .= ' XVERP=' . $params['verp'];
  735. }
  736. } elseif (is_string($params)) {
  737. $args .= ' ' . $params;
  738. }
  739. if (PEAR::isError($error = $this->_put('MAIL', $args))) {
  740. return $error;
  741. }
  742. if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  743. return $error;
  744. }
  745. return true;
  746. }
  747. /**
  748. * Send the RCPT TO: command.
  749. *
  750. * @param string $recipient The recipient (forward path) to add.
  751. * @param string $params String containing additional RCPT parameters,
  752. * such as the NOTIFY flags defined by RFC 1891.
  753. *
  754. * @return mixed Returns a PEAR_Error with an error message on any
  755. * kind of failure, or true on success.
  756. *
  757. * @access public
  758. * @since 1.0
  759. */
  760. function rcptTo($recipient, $params = null)
  761. {
  762. $args = "TO:<$recipient>";
  763. if (is_string($params)) {
  764. $args .= ' ' . $params;
  765. }
  766. if (PEAR::isError($error = $this->_put('RCPT', $args))) {
  767. return $error;
  768. }
  769. if (PEAR::isError($error = $this->_parseResponse(array(250, 251), $this->pipelining))) {
  770. return $error;
  771. }
  772. return true;
  773. }
  774. /**
  775. * Quote the data so that it meets SMTP standards.
  776. *
  777. * This is provided as a separate public function to facilitate
  778. * easier overloading for the cases where it is desirable to
  779. * customize the quoting behavior.
  780. *
  781. * @param string $data The message text to quote. The string must be passed
  782. * by reference, and the text will be modified in place.
  783. *
  784. * @access public
  785. * @since 1.2
  786. */
  787. function quotedata(&$data)
  788. {
  789. /* Change Unix (\n) and Mac (\r) linefeeds into
  790. * Internet-standard CRLF (\r\n) linefeeds. */
  791. $data = preg_replace(array('/(?<!\r)\n/','/\r(?!\n)/'), "\r\n", $data);
  792. /* Because a single leading period (.) signifies an end to the
  793. * data, legitimate leading periods need to be "doubled"
  794. * (e.g. '..'). */
  795. $data = str_replace("\n.", "\n..", $data);
  796. }
  797. /**
  798. * Send the DATA command.
  799. *
  800. * @param string $data The message body to send.
  801. *
  802. * @return mixed Returns a PEAR_Error with an error message on any
  803. * kind of failure, or true on success.
  804. * @access public
  805. * @since 1.0
  806. */
  807. function data($data)
  808. {
  809. /* RFC 1870, section 3, subsection 3 states "a value of zero
  810. * indicates that no fixed maximum message size is in force".
  811. * Furthermore, it says that if "the parameter is omitted no
  812. * information is conveyed about the server's fixed maximum
  813. * message size". */
  814. if (isset($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0)) {
  815. if (strlen($data) >= $this->_esmtp['SIZE']) {
  816. $this->disconnect();
  817. return PEAR::raiseError('Message size excedes the server limit');
  818. }
  819. }
  820. /* Quote the data based on the SMTP standards. */
  821. $this->quotedata($data);
  822. if (PEAR::isError($error = $this->_put('DATA'))) {
  823. return $error;
  824. }
  825. if (PEAR::isError($error = $this->_parseResponse(354))) {
  826. return $error;
  827. }
  828. if (PEAR::isError($result = $this->_send($data . "\r\n.\r\n"))) {
  829. return $result;
  830. }
  831. if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  832. return $error;
  833. }
  834. return true;
  835. }
  836. /**
  837. * Send the SEND FROM: command.
  838. *
  839. * @param string The reverse path to send.
  840. *
  841. * @return mixed Returns a PEAR_Error with an error message on any
  842. * kind of failure, or true on success.
  843. * @access public
  844. * @since 1.2.6
  845. */
  846. function sendFrom($path)
  847. {
  848. if (PEAR::isError($error = $this->_put('SEND', "FROM:<$path>"))) {
  849. return $error;
  850. }
  851. if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  852. return $error;
  853. }
  854. return true;
  855. }
  856. /**
  857. * Backwards-compatibility wrapper for sendFrom().
  858. *
  859. * @param string The reverse path to send.
  860. *
  861. * @return mixed Returns a PEAR_Error with an error message on any
  862. * kind of failure, or true on success.
  863. *
  864. * @access public
  865. * @since 1.0
  866. * @deprecated 1.2.6
  867. */
  868. function send_from($path)
  869. {
  870. return sendFrom($path);
  871. }
  872. /**
  873. * Send the SOML FROM: command.
  874. *
  875. * @param string The reverse path to send.
  876. *
  877. * @return mixed Returns a PEAR_Error with an error message on any
  878. * kind of failure, or true on success.
  879. * @access public
  880. * @since 1.2.6
  881. */
  882. function somlFrom($path)
  883. {
  884. if (PEAR::isError($error = $this->_put('SOML', "FROM:<$path>"))) {
  885. return $error;
  886. }
  887. if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  888. return $error;
  889. }
  890. return true;
  891. }
  892. /**
  893. * Backwards-compatibility wrapper for somlFrom().
  894. *
  895. * @param string The reverse path to send.
  896. *
  897. * @return mixed Returns a PEAR_Error with an error message on any
  898. * kind of failure, or true on success.
  899. *
  900. * @access public
  901. * @since 1.0
  902. * @deprecated 1.2.6
  903. */
  904. function soml_from($path)
  905. {
  906. return somlFrom($path);
  907. }
  908. /**
  909. * Send the SAML FROM: command.
  910. *
  911. * @param string The reverse path to send.
  912. *
  913. * @return mixed Returns a PEAR_Error with an error message on any
  914. * kind of failure, or true on success.
  915. * @access public
  916. * @since 1.2.6
  917. */
  918. function samlFrom($path)
  919. {
  920. if (PEAR::isError($error = $this->_put('SAML', "FROM:<$path>"))) {
  921. return $error;
  922. }
  923. if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  924. return $error;
  925. }
  926. return true;
  927. }
  928. /**
  929. * Backwards-compatibility wrapper for samlFrom().
  930. *
  931. * @param string The reverse path to send.
  932. *
  933. * @return mixed Returns a PEAR_Error with an error message on any
  934. * kind of failure, or true on success.
  935. *
  936. * @access public
  937. * @since 1.0
  938. * @deprecated 1.2.6
  939. */
  940. function saml_from($path)
  941. {
  942. return samlFrom($path);
  943. }
  944. /**
  945. * Send the RSET command.
  946. *
  947. * @return mixed Returns a PEAR_Error with an error message on any
  948. * kind of failure, or true on success.
  949. * @access public
  950. * @since 1.0
  951. */
  952. function rset()
  953. {
  954. if (PEAR::isError($error = $this->_put('RSET'))) {
  955. return $error;
  956. }
  957. if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  958. return $error;
  959. }
  960. return true;
  961. }
  962. /**
  963. * Send the VRFY command.
  964. *
  965. * @param string The string to verify
  966. *
  967. * @return mixed Returns a PEAR_Error with an error message on any
  968. * kind of failure, or true on success.
  969. * @access public
  970. * @since 1.0
  971. */
  972. function vrfy($string)
  973. {
  974. /* Note: 251 is also a valid response code */
  975. if (PEAR::isError($error = $this->_put('VRFY', $string))) {
  976. return $error;
  977. }
  978. if (PEAR::isError($error = $this->_parseResponse(array(250, 252)))) {
  979. return $error;
  980. }
  981. return true;
  982. }
  983. /**
  984. * Send the NOOP command.
  985. *
  986. * @return mixed Returns a PEAR_Error with an error message on any
  987. * kind of failure, or true on success.
  988. * @access public
  989. * @since 1.0
  990. */
  991. function noop()
  992. {
  993. if (PEAR::isError($error = $this->_put('NOOP'))) {
  994. return $error;
  995. }
  996. if (PEAR::isError($error = $this->_parseResponse(250))) {
  997. return $error;
  998. }
  999. return true;
  1000. }
  1001. /**
  1002. * Backwards-compatibility method. identifySender()'s functionality is
  1003. * now handled internally.
  1004. *
  1005. * @return boolean This method always return true.
  1006. *
  1007. * @access public
  1008. * @since 1.0
  1009. */
  1010. function identifySender()
  1011. {
  1012. return true;
  1013. }
  1014. }