PageRenderTime 44ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/PhpOnAzure.Web/data/module/Net/SMTP.php

http://eccubeonwaz.codeplex.com
PHP | 1218 lines | 533 code | 127 blank | 558 comment | 118 complexity | 917deeee49d9cae52ecb5b04ba1f302b MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  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 304535 2010-10-20 06:48:06Z 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. $error = $this->_socket->write($data);
  209. if ($error === false || PEAR::isError($error)) {
  210. $msg = ($error) ? $error->getMessage() : "unknown error";
  211. return PEAR::raiseError("Failed to write to socket: $msg");
  212. }
  213. return true;
  214. }
  215. /**
  216. * Send a command to the server with an optional string of
  217. * arguments. A carriage return / linefeed (CRLF) sequence will
  218. * be appended to each command string before it is sent to the
  219. * SMTP server - an error will be thrown if the command string
  220. * already contains any newline characters. Use _send() for
  221. * commands that must contain newlines.
  222. *
  223. * @param string $command The SMTP command to send to the server.
  224. * @param string $args A string of optional arguments to append
  225. * to the command.
  226. *
  227. * @return mixed The result of the _send() call.
  228. *
  229. * @access private
  230. * @since 1.1.0
  231. */
  232. function _put($command, $args = '')
  233. {
  234. if (!empty($args)) {
  235. $command .= ' ' . $args;
  236. }
  237. if (strcspn($command, "\r\n") !== strlen($command)) {
  238. return PEAR::raiseError('Commands cannot contain newlines');
  239. }
  240. return $this->_send($command . "\r\n");
  241. }
  242. /**
  243. * Read a reply from the SMTP server. The reply consists of a response
  244. * code and a response message.
  245. *
  246. * @param mixed $valid The set of valid response codes. These
  247. * may be specified as an array of integer
  248. * values or as a single integer value.
  249. * @param bool $later Do not parse the response now, but wait
  250. * until the last command in the pipelined
  251. * command group
  252. *
  253. * @return mixed True if the server returned a valid response code or
  254. * a PEAR_Error object is an error condition is reached.
  255. *
  256. * @access private
  257. * @since 1.1.0
  258. *
  259. * @see getResponse
  260. */
  261. function _parseResponse($valid, $later = false)
  262. {
  263. $this->_code = -1;
  264. $this->_arguments = array();
  265. if ($later) {
  266. $this->_pipelined_commands++;
  267. return true;
  268. }
  269. for ($i = 0; $i <= $this->_pipelined_commands; $i++) {
  270. while ($line = $this->_socket->readLine()) {
  271. $this->_debug("Recv: $line");
  272. /* If we receive an empty line, the connection has been closed. */
  273. if (empty($line)) {
  274. $this->disconnect();
  275. return PEAR::raiseError('Connection was unexpectedly closed');
  276. }
  277. /* Read the code and store the rest in the arguments array. */
  278. $code = substr($line, 0, 3);
  279. $this->_arguments[] = trim(substr($line, 4));
  280. /* Check the syntax of the response code. */
  281. if (is_numeric($code)) {
  282. $this->_code = (int)$code;
  283. } else {
  284. $this->_code = -1;
  285. break;
  286. }
  287. /* If this is not a multiline response, we're done. */
  288. if (substr($line, 3, 1) != '-') {
  289. break;
  290. }
  291. }
  292. }
  293. $this->_pipelined_commands = 0;
  294. /* Compare the server's response code with the valid code/codes. */
  295. if (is_int($valid) && ($this->_code === $valid)) {
  296. return true;
  297. } elseif (is_array($valid) && in_array($this->_code, $valid, true)) {
  298. return true;
  299. }
  300. return PEAR::raiseError('Invalid response code received from server',
  301. $this->_code);
  302. }
  303. /**
  304. * Return a 2-tuple containing the last response from the SMTP server.
  305. *
  306. * @return array A two-element array: the first element contains the
  307. * response code as an integer and the second element
  308. * contains the response's arguments as a string.
  309. *
  310. * @access public
  311. * @since 1.1.0
  312. */
  313. function getResponse()
  314. {
  315. return array($this->_code, join("\n", $this->_arguments));
  316. }
  317. /**
  318. * Return the SMTP server's greeting string.
  319. *
  320. * @return string A string containing the greeting string, or null if a
  321. * greeting has not been received.
  322. *
  323. * @access public
  324. * @since 1.3.3
  325. */
  326. function getGreeting()
  327. {
  328. return $this->_greeting;
  329. }
  330. /**
  331. * Attempt to connect to the SMTP server.
  332. *
  333. * @param int $timeout The timeout value (in seconds) for the
  334. * socket connection.
  335. * @param bool $persistent Should a persistent socket connection
  336. * be used?
  337. *
  338. * @return mixed Returns a PEAR_Error with an error message on any
  339. * kind of failure, or true on success.
  340. * @access public
  341. * @since 1.0
  342. */
  343. function connect($timeout = null, $persistent = false)
  344. {
  345. $this->_greeting = null;
  346. $result = $this->_socket->connect($this->host, $this->port,
  347. $persistent, $timeout);
  348. if (PEAR::isError($result)) {
  349. return PEAR::raiseError('Failed to connect socket: ' .
  350. $result->getMessage());
  351. }
  352. if (PEAR::isError($error = $this->_parseResponse(220))) {
  353. return $error;
  354. }
  355. /* Extract and store a copy of the server's greeting string. */
  356. list(, $this->_greeting) = $this->getResponse();
  357. if (PEAR::isError($error = $this->_negotiate())) {
  358. return $error;
  359. }
  360. return true;
  361. }
  362. /**
  363. * Attempt to disconnect from the SMTP server.
  364. *
  365. * @return mixed Returns a PEAR_Error with an error message on any
  366. * kind of failure, or true on success.
  367. * @access public
  368. * @since 1.0
  369. */
  370. function disconnect()
  371. {
  372. if (PEAR::isError($error = $this->_put('QUIT'))) {
  373. return $error;
  374. }
  375. if (PEAR::isError($error = $this->_parseResponse(221))) {
  376. return $error;
  377. }
  378. if (PEAR::isError($error = $this->_socket->disconnect())) {
  379. return PEAR::raiseError('Failed to disconnect socket: ' .
  380. $error->getMessage());
  381. }
  382. return true;
  383. }
  384. /**
  385. * Attempt to send the EHLO command and obtain a list of ESMTP
  386. * extensions available, and failing that just send HELO.
  387. *
  388. * @return mixed Returns a PEAR_Error with an error message on any
  389. * kind of failure, or true on success.
  390. *
  391. * @access private
  392. * @since 1.1.0
  393. */
  394. function _negotiate()
  395. {
  396. if (PEAR::isError($error = $this->_put('EHLO', $this->localhost))) {
  397. return $error;
  398. }
  399. if (PEAR::isError($this->_parseResponse(250))) {
  400. /* If we receive a 503 response, we're already authenticated. */
  401. if ($this->_code === 503) {
  402. return true;
  403. }
  404. /* If the EHLO failed, try the simpler HELO command. */
  405. if (PEAR::isError($error = $this->_put('HELO', $this->localhost))) {
  406. return $error;
  407. }
  408. if (PEAR::isError($this->_parseResponse(250))) {
  409. return PEAR::raiseError('HELO was not accepted: ', $this->_code);
  410. }
  411. return true;
  412. }
  413. foreach ($this->_arguments as $argument) {
  414. $verb = strtok($argument, ' ');
  415. $arguments = substr($argument, strlen($verb) + 1,
  416. strlen($argument) - strlen($verb) - 1);
  417. $this->_esmtp[$verb] = $arguments;
  418. }
  419. if (!isset($this->_esmtp['PIPELINING'])) {
  420. $this->pipelining = false;
  421. }
  422. return true;
  423. }
  424. /**
  425. * Returns the name of the best authentication method that the server
  426. * has advertised.
  427. *
  428. * @return mixed Returns a string containing the name of the best
  429. * supported authentication method or a PEAR_Error object
  430. * if a failure condition is encountered.
  431. * @access private
  432. * @since 1.1.0
  433. */
  434. function _getBestAuthMethod()
  435. {
  436. $available_methods = explode(' ', $this->_esmtp['AUTH']);
  437. foreach ($this->auth_methods as $method) {
  438. if (in_array($method, $available_methods)) {
  439. return $method;
  440. }
  441. }
  442. return PEAR::raiseError('No supported authentication methods');
  443. }
  444. /**
  445. * Attempt to do SMTP authentication.
  446. *
  447. * @param string The userid to authenticate as.
  448. * @param string The password to authenticate with.
  449. * @param string The requested authentication method. If none is
  450. * specified, the best supported method will be used.
  451. * @param bool Flag indicating whether or not TLS should be attempted.
  452. * @param string An optional authorization identifier. If specified, this
  453. * identifier will be used as the authorization proxy.
  454. *
  455. * @return mixed Returns a PEAR_Error with an error message on any
  456. * kind of failure, or true on success.
  457. * @access public
  458. * @since 1.0
  459. */
  460. function auth($uid, $pwd , $method = '', $tls = true, $authz = '')
  461. {
  462. /* We can only attempt a TLS connection if one has been requested,
  463. * we're running PHP 5.1.0 or later, have access to the OpenSSL
  464. * extension, are connected to an SMTP server which supports the
  465. * STARTTLS extension, and aren't already connected over a secure
  466. * (SSL) socket connection. */
  467. if ($tls && version_compare(PHP_VERSION, '5.1.0', '>=') &&
  468. extension_loaded('openssl') && isset($this->_esmtp['STARTTLS']) &&
  469. strncasecmp($this->host, 'ssl://', 6) !== 0) {
  470. /* Start the TLS connection attempt. */
  471. if (PEAR::isError($result = $this->_put('STARTTLS'))) {
  472. return $result;
  473. }
  474. if (PEAR::isError($result = $this->_parseResponse(220))) {
  475. return $result;
  476. }
  477. if (PEAR::isError($result = $this->_socket->enableCrypto(true, STREAM_CRYPTO_METHOD_TLS_CLIENT))) {
  478. return $result;
  479. } elseif ($result !== true) {
  480. return PEAR::raiseError('STARTTLS failed');
  481. }
  482. /* Send EHLO again to recieve the AUTH string from the
  483. * SMTP server. */
  484. $this->_negotiate();
  485. }
  486. if (empty($this->_esmtp['AUTH'])) {
  487. return PEAR::raiseError('SMTP server does not support authentication');
  488. }
  489. /* If no method has been specified, get the name of the best
  490. * supported method advertised by the SMTP server. */
  491. if (empty($method)) {
  492. if (PEAR::isError($method = $this->_getBestAuthMethod())) {
  493. /* Return the PEAR_Error object from _getBestAuthMethod(). */
  494. return $method;
  495. }
  496. } else {
  497. $method = strtoupper($method);
  498. if (!in_array($method, $this->auth_methods)) {
  499. return PEAR::raiseError("$method is not a supported authentication method");
  500. }
  501. }
  502. switch ($method) {
  503. case 'DIGEST-MD5':
  504. $result = $this->_authDigest_MD5($uid, $pwd, $authz);
  505. break;
  506. case 'CRAM-MD5':
  507. $result = $this->_authCRAM_MD5($uid, $pwd);
  508. break;
  509. case 'LOGIN':
  510. $result = $this->_authLogin($uid, $pwd);
  511. break;
  512. case 'PLAIN':
  513. $result = $this->_authPlain($uid, $pwd, $authz);
  514. break;
  515. default:
  516. $result = PEAR::raiseError("$method is not a supported authentication method");
  517. break;
  518. }
  519. /* If an error was encountered, return the PEAR_Error object. */
  520. if (PEAR::isError($result)) {
  521. return $result;
  522. }
  523. return true;
  524. }
  525. /**
  526. * Authenticates the user using the DIGEST-MD5 method.
  527. *
  528. * @param string The userid to authenticate as.
  529. * @param string The password to authenticate with.
  530. * @param string The optional authorization proxy identifier.
  531. *
  532. * @return mixed Returns a PEAR_Error with an error message on any
  533. * kind of failure, or true on success.
  534. * @access private
  535. * @since 1.1.0
  536. */
  537. function _authDigest_MD5($uid, $pwd, $authz = '')
  538. {
  539. if (PEAR::isError($error = $this->_put('AUTH', 'DIGEST-MD5'))) {
  540. return $error;
  541. }
  542. /* 334: Continue authentication request */
  543. if (PEAR::isError($error = $this->_parseResponse(334))) {
  544. /* 503: Error: already authenticated */
  545. if ($this->_code === 503) {
  546. return true;
  547. }
  548. return $error;
  549. }
  550. $challenge = base64_decode($this->_arguments[0]);
  551. $digest = &Auth_SASL::factory('digestmd5');
  552. $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge,
  553. $this->host, "smtp"));
  554. if (PEAR::isError($error = $this->_put($auth_str))) {
  555. return $error;
  556. }
  557. /* 334: Continue authentication request */
  558. if (PEAR::isError($error = $this->_parseResponse(334))) {
  559. return $error;
  560. }
  561. /* We don't use the protocol's third step because SMTP doesn't
  562. * allow subsequent authentication, so we just silently ignore
  563. * it. */
  564. if (PEAR::isError($error = $this->_put(''))) {
  565. return $error;
  566. }
  567. /* 235: Authentication successful */
  568. if (PEAR::isError($error = $this->_parseResponse(235))) {
  569. return $error;
  570. }
  571. }
  572. /**
  573. * Authenticates the user using the CRAM-MD5 method.
  574. *
  575. * @param string The userid to authenticate as.
  576. * @param string The password to authenticate with.
  577. *
  578. * @return mixed Returns a PEAR_Error with an error message on any
  579. * kind of failure, or true on success.
  580. * @access private
  581. * @since 1.1.0
  582. */
  583. function _authCRAM_MD5($uid, $pwd)
  584. {
  585. if (PEAR::isError($error = $this->_put('AUTH', 'CRAM-MD5'))) {
  586. return $error;
  587. }
  588. /* 334: Continue authentication request */
  589. if (PEAR::isError($error = $this->_parseResponse(334))) {
  590. /* 503: Error: already authenticated */
  591. if ($this->_code === 503) {
  592. return true;
  593. }
  594. return $error;
  595. }
  596. $challenge = base64_decode($this->_arguments[0]);
  597. $cram = &Auth_SASL::factory('crammd5');
  598. $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge));
  599. if (PEAR::isError($error = $this->_put($auth_str))) {
  600. return $error;
  601. }
  602. /* 235: Authentication successful */
  603. if (PEAR::isError($error = $this->_parseResponse(235))) {
  604. return $error;
  605. }
  606. }
  607. /**
  608. * Authenticates the user using the LOGIN method.
  609. *
  610. * @param string The userid to authenticate as.
  611. * @param string The password to authenticate with.
  612. *
  613. * @return mixed Returns a PEAR_Error with an error message on any
  614. * kind of failure, or true on success.
  615. * @access private
  616. * @since 1.1.0
  617. */
  618. function _authLogin($uid, $pwd)
  619. {
  620. if (PEAR::isError($error = $this->_put('AUTH', 'LOGIN'))) {
  621. return $error;
  622. }
  623. /* 334: Continue authentication request */
  624. if (PEAR::isError($error = $this->_parseResponse(334))) {
  625. /* 503: Error: already authenticated */
  626. if ($this->_code === 503) {
  627. return true;
  628. }
  629. return $error;
  630. }
  631. if (PEAR::isError($error = $this->_put(base64_encode($uid)))) {
  632. return $error;
  633. }
  634. /* 334: Continue authentication request */
  635. if (PEAR::isError($error = $this->_parseResponse(334))) {
  636. return $error;
  637. }
  638. if (PEAR::isError($error = $this->_put(base64_encode($pwd)))) {
  639. return $error;
  640. }
  641. /* 235: Authentication successful */
  642. if (PEAR::isError($error = $this->_parseResponse(235))) {
  643. return $error;
  644. }
  645. return true;
  646. }
  647. /**
  648. * Authenticates the user using the PLAIN method.
  649. *
  650. * @param string The userid to authenticate as.
  651. * @param string The password to authenticate with.
  652. * @param string The optional authorization proxy identifier.
  653. *
  654. * @return mixed Returns a PEAR_Error with an error message on any
  655. * kind of failure, or true on success.
  656. * @access private
  657. * @since 1.1.0
  658. */
  659. function _authPlain($uid, $pwd, $authz = '')
  660. {
  661. if (PEAR::isError($error = $this->_put('AUTH', 'PLAIN'))) {
  662. return $error;
  663. }
  664. /* 334: Continue authentication request */
  665. if (PEAR::isError($error = $this->_parseResponse(334))) {
  666. /* 503: Error: already authenticated */
  667. if ($this->_code === 503) {
  668. return true;
  669. }
  670. return $error;
  671. }
  672. $auth_str = base64_encode($authz . chr(0) . $uid . chr(0) . $pwd);
  673. if (PEAR::isError($error = $this->_put($auth_str))) {
  674. return $error;
  675. }
  676. /* 235: Authentication successful */
  677. if (PEAR::isError($error = $this->_parseResponse(235))) {
  678. return $error;
  679. }
  680. return true;
  681. }
  682. /**
  683. * Send the HELO command.
  684. *
  685. * @param string The domain name to say we are.
  686. *
  687. * @return mixed Returns a PEAR_Error with an error message on any
  688. * kind of failure, or true on success.
  689. * @access public
  690. * @since 1.0
  691. */
  692. function helo($domain)
  693. {
  694. if (PEAR::isError($error = $this->_put('HELO', $domain))) {
  695. return $error;
  696. }
  697. if (PEAR::isError($error = $this->_parseResponse(250))) {
  698. return $error;
  699. }
  700. return true;
  701. }
  702. /**
  703. * Return the list of SMTP service extensions advertised by the server.
  704. *
  705. * @return array The list of SMTP service extensions.
  706. * @access public
  707. * @since 1.3
  708. */
  709. function getServiceExtensions()
  710. {
  711. return $this->_esmtp;
  712. }
  713. /**
  714. * Send the MAIL FROM: command.
  715. *
  716. * @param string $sender The sender (reverse path) to set.
  717. * @param string $params String containing additional MAIL parameters,
  718. * such as the NOTIFY flags defined by RFC 1891
  719. * or the VERP protocol.
  720. *
  721. * If $params is an array, only the 'verp' option
  722. * is supported. If 'verp' is true, the XVERP
  723. * parameter is appended to the MAIL command. If
  724. * the 'verp' value is a string, the full
  725. * XVERP=value parameter is appended.
  726. *
  727. * @return mixed Returns a PEAR_Error with an error message on any
  728. * kind of failure, or true on success.
  729. * @access public
  730. * @since 1.0
  731. */
  732. function mailFrom($sender, $params = null)
  733. {
  734. $args = "FROM:<$sender>";
  735. /* Support the deprecated array form of $params. */
  736. if (is_array($params) && isset($params['verp'])) {
  737. /* XVERP */
  738. if ($params['verp'] === true) {
  739. $args .= ' XVERP';
  740. /* XVERP=something */
  741. } elseif (trim($params['verp'])) {
  742. $args .= ' XVERP=' . $params['verp'];
  743. }
  744. } elseif (is_string($params)) {
  745. $args .= ' ' . $params;
  746. }
  747. if (PEAR::isError($error = $this->_put('MAIL', $args))) {
  748. return $error;
  749. }
  750. if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  751. return $error;
  752. }
  753. return true;
  754. }
  755. /**
  756. * Send the RCPT TO: command.
  757. *
  758. * @param string $recipient The recipient (forward path) to add.
  759. * @param string $params String containing additional RCPT parameters,
  760. * such as the NOTIFY flags defined by RFC 1891.
  761. *
  762. * @return mixed Returns a PEAR_Error with an error message on any
  763. * kind of failure, or true on success.
  764. *
  765. * @access public
  766. * @since 1.0
  767. */
  768. function rcptTo($recipient, $params = null)
  769. {
  770. $args = "TO:<$recipient>";
  771. if (is_string($params)) {
  772. $args .= ' ' . $params;
  773. }
  774. if (PEAR::isError($error = $this->_put('RCPT', $args))) {
  775. return $error;
  776. }
  777. if (PEAR::isError($error = $this->_parseResponse(array(250, 251), $this->pipelining))) {
  778. return $error;
  779. }
  780. return true;
  781. }
  782. /**
  783. * Quote the data so that it meets SMTP standards.
  784. *
  785. * This is provided as a separate public function to facilitate
  786. * easier overloading for the cases where it is desirable to
  787. * customize the quoting behavior.
  788. *
  789. * @param string $data The message text to quote. The string must be passed
  790. * by reference, and the text will be modified in place.
  791. *
  792. * @access public
  793. * @since 1.2
  794. */
  795. function quotedata(&$data)
  796. {
  797. /* Change Unix (\n) and Mac (\r) linefeeds into
  798. * Internet-standard CRLF (\r\n) linefeeds. */
  799. $data = preg_replace(array('/(?<!\r)\n/','/\r(?!\n)/'), "\r\n", $data);
  800. /* Because a single leading period (.) signifies an end to the
  801. * data, legitimate leading periods need to be "doubled"
  802. * (e.g. '..'). */
  803. $data = str_replace("\n.", "\n..", $data);
  804. }
  805. /**
  806. * Send the DATA command.
  807. *
  808. * @param mixed $data The message data, either as a string or an open
  809. * file resource.
  810. * @param string $headers The message headers. If $headers is provided,
  811. * $data is assumed to contain only body data.
  812. *
  813. * @return mixed Returns a PEAR_Error with an error message on any
  814. * kind of failure, or true on success.
  815. * @access public
  816. * @since 1.0
  817. */
  818. function data($data, $headers = null)
  819. {
  820. /* Verify that $data is a supported type. */
  821. if (!is_string($data) && !is_resource($data)) {
  822. return PEAR::raiseError('Expected a string or file resource');
  823. }
  824. /* Start by considering the size of the optional headers string. We
  825. * also account for the addition 4 character "\r\n\r\n" separator
  826. * sequence. */
  827. $size = (is_null($headers)) ? 0 : strlen($headers) + 4;
  828. if (is_resource($data)) {
  829. $stat = fstat($data);
  830. if ($stat === false) {
  831. return PEAR::raiseError('Failed to get file size');
  832. }
  833. $size += $stat['size'];
  834. } else {
  835. $size += strlen($data);
  836. }
  837. /* RFC 1870, section 3, subsection 3 states "a value of zero indicates
  838. * that no fixed maximum message size is in force". Furthermore, it
  839. * says that if "the parameter is omitted no information is conveyed
  840. * about the server's fixed maximum message size". */
  841. $limit = (isset($this->_esmtp['SIZE'])) ? $this->_esmtp['SIZE'] : 0;
  842. if ($limit > 0 && $size >= $limit) {
  843. $this->disconnect();
  844. return PEAR::raiseError('Message size exceeds server limit');
  845. }
  846. /* Initiate the DATA command. */
  847. if (PEAR::isError($error = $this->_put('DATA'))) {
  848. return $error;
  849. }
  850. if (PEAR::isError($error = $this->_parseResponse(354))) {
  851. return $error;
  852. }
  853. /* If we have a separate headers string, send it first. */
  854. if (!is_null($headers)) {
  855. $this->quotedata($headers);
  856. if (PEAR::isError($result = $this->_send($headers . "\r\n\r\n"))) {
  857. return $result;
  858. }
  859. }
  860. /* Now we can send the message body data. */
  861. if (is_resource($data)) {
  862. /* Stream the contents of the file resource out over our socket
  863. * connection, line by line. Each line must be run through the
  864. * quoting routine. */
  865. while ($line = fgets($data, 1024)) {
  866. $this->quotedata($line);
  867. if (PEAR::isError($result = $this->_send($line))) {
  868. return $result;
  869. }
  870. }
  871. } else {
  872. /*
  873. * Break up the data by sending one chunk (up to 512k) at a time.
  874. * This approach reduces our peak memory usage.
  875. */
  876. for ($offset = 0; $offset < $size;) {
  877. $end = $offset + 512000;
  878. /*
  879. * Ensure we don't read beyond our data size or span multiple
  880. * lines. quotedata() can't properly handle character data
  881. * that's split across two line break boundaries.
  882. */
  883. if ($end >= $size) {
  884. $end = $size;
  885. } else {
  886. for (; $end < $size; $end++) {
  887. if ($data[$end] != "\n") {
  888. break;
  889. }
  890. }
  891. }
  892. /* Extract our chunk and run it through the quoting routine. */
  893. $chunk = substr($data, $offset, $end - $offset);
  894. $this->quotedata($chunk);
  895. /* If we run into a problem along the way, abort. */
  896. if (PEAR::isError($result = $this->_send($chunk))) {
  897. return $result;
  898. }
  899. /* Advance the offset to the end of this chunk. */
  900. $offset = $end;
  901. }
  902. }
  903. /* Finally, send the DATA terminator sequence. */
  904. if (PEAR::isError($result = $this->_send("\r\n.\r\n"))) {
  905. return $result;
  906. }
  907. /* Verify that the data was successfully received by the server. */
  908. if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  909. return $error;
  910. }
  911. return true;
  912. }
  913. /**
  914. * Send the SEND FROM: command.
  915. *
  916. * @param string The reverse path to send.
  917. *
  918. * @return mixed Returns a PEAR_Error with an error message on any
  919. * kind of failure, or true on success.
  920. * @access public
  921. * @since 1.2.6
  922. */
  923. function sendFrom($path)
  924. {
  925. if (PEAR::isError($error = $this->_put('SEND', "FROM:<$path>"))) {
  926. return $error;
  927. }
  928. if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  929. return $error;
  930. }
  931. return true;
  932. }
  933. /**
  934. * Backwards-compatibility wrapper for sendFrom().
  935. *
  936. * @param string The reverse path to send.
  937. *
  938. * @return mixed Returns a PEAR_Error with an error message on any
  939. * kind of failure, or true on success.
  940. *
  941. * @access public
  942. * @since 1.0
  943. * @deprecated 1.2.6
  944. */
  945. function send_from($path)
  946. {
  947. return sendFrom($path);
  948. }
  949. /**
  950. * Send the SOML FROM: command.
  951. *
  952. * @param string The reverse path to send.
  953. *
  954. * @return mixed Returns a PEAR_Error with an error message on any
  955. * kind of failure, or true on success.
  956. * @access public
  957. * @since 1.2.6
  958. */
  959. function somlFrom($path)
  960. {
  961. if (PEAR::isError($error = $this->_put('SOML', "FROM:<$path>"))) {
  962. return $error;
  963. }
  964. if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  965. return $error;
  966. }
  967. return true;
  968. }
  969. /**
  970. * Backwards-compatibility wrapper for somlFrom().
  971. *
  972. * @param string The reverse path to send.
  973. *
  974. * @return mixed Returns a PEAR_Error with an error message on any
  975. * kind of failure, or true on success.
  976. *
  977. * @access public
  978. * @since 1.0
  979. * @deprecated 1.2.6
  980. */
  981. function soml_from($path)
  982. {
  983. return somlFrom($path);
  984. }
  985. /**
  986. * Send the SAML FROM: command.
  987. *
  988. * @param string The reverse path to send.
  989. *
  990. * @return mixed Returns a PEAR_Error with an error message on any
  991. * kind of failure, or true on success.
  992. * @access public
  993. * @since 1.2.6
  994. */
  995. function samlFrom($path)
  996. {
  997. if (PEAR::isError($error = $this->_put('SAML', "FROM:<$path>"))) {
  998. return $error;
  999. }
  1000. if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  1001. return $error;
  1002. }
  1003. return true;
  1004. }
  1005. /**
  1006. * Backwards-compatibility wrapper for samlFrom().
  1007. *
  1008. * @param string The reverse path to send.
  1009. *
  1010. * @return mixed Returns a PEAR_Error with an error message on any
  1011. * kind of failure, or true on success.
  1012. *
  1013. * @access public
  1014. * @since 1.0
  1015. * @deprecated 1.2.6
  1016. */
  1017. function saml_from($path)
  1018. {
  1019. return samlFrom($path);
  1020. }
  1021. /**
  1022. * Send the RSET command.
  1023. *
  1024. * @return mixed Returns a PEAR_Error with an error message on any
  1025. * kind of failure, or true on success.
  1026. * @access public
  1027. * @since 1.0
  1028. */
  1029. function rset()
  1030. {
  1031. if (PEAR::isError($error = $this->_put('RSET'))) {
  1032. return $error;
  1033. }
  1034. if (PEAR::isError($error = $this->_parseResponse(250, $this->pipelining))) {
  1035. return $error;
  1036. }
  1037. return true;
  1038. }
  1039. /**
  1040. * Send the VRFY command.
  1041. *
  1042. * @param string The string to verify
  1043. *
  1044. * @return mixed Returns a PEAR_Error with an error message on any
  1045. * kind of failure, or true on success.
  1046. * @access public
  1047. * @since 1.0
  1048. */
  1049. function vrfy($string)
  1050. {
  1051. /* Note: 251 is also a valid response code */
  1052. if (PEAR::isError($error = $this->_put('VRFY', $string))) {
  1053. return $error;
  1054. }
  1055. if (PEAR::isError($error = $this->_parseResponse(array(250, 252)))) {
  1056. return $error;
  1057. }
  1058. return true;
  1059. }
  1060. /**
  1061. * Send the NOOP command.
  1062. *
  1063. * @return mixed Returns a PEAR_Error with an error message on any
  1064. * kind of failure, or true on success.
  1065. * @access public
  1066. * @since 1.0
  1067. */
  1068. function noop()
  1069. {
  1070. if (PEAR::isError($error = $this->_put('NOOP'))) {
  1071. return $error;
  1072. }
  1073. if (PEAR::isError($error = $this->_parseResponse(250))) {
  1074. return $error;
  1075. }
  1076. return true;
  1077. }
  1078. /**
  1079. * Backwards-compatibility method. identifySender()'s functionality is
  1080. * now handled internally.
  1081. *
  1082. * @return boolean This method always return true.
  1083. *
  1084. * @access public
  1085. * @since 1.0
  1086. */
  1087. function identifySender()
  1088. {
  1089. return true;
  1090. }
  1091. }