SMTP.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913
  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. require_once 'PEAR.php';
  21. require_once 'Net/Socket.php';
  22. /**
  23. * Provides an implementation of the SMTP protocol using PEAR's
  24. * Net_Socket:: class.
  25. *
  26. * @package Net_SMTP
  27. * @author Chuck Hagenbuch <chuck@horde.org>
  28. * @author Jon Parise <jon@php.net>
  29. * @author Damian Alejandro Fernandez Sosa <damlists@cnba.uba.ar>
  30. */
  31. class Net_SMTP
  32. {
  33. /**
  34. * The server to connect to.
  35. * @var string
  36. * @access public
  37. */
  38. var $host = 'localhost';
  39. /**
  40. * The port to connect to.
  41. * @var int
  42. * @access public
  43. */
  44. var $port = 25;
  45. /**
  46. * The value to give when sending EHLO or HELO.
  47. * @var string
  48. * @access public
  49. */
  50. var $localhost = 'localhost';
  51. /**
  52. * List of supported authentication methods, in preferential order.
  53. * @var array
  54. * @access public
  55. */
  56. var $auth_methods = array('DIGEST-MD5', 'CRAM-MD5', 'LOGIN', 'PLAIN');
  57. /**
  58. * Should debugging output be enabled?
  59. * @var boolean
  60. * @access private
  61. */
  62. var $_debug = false;
  63. /**
  64. * The socket resource being used to connect to the SMTP server.
  65. * @var resource
  66. * @access private
  67. */
  68. var $_socket = null;
  69. /**
  70. * The most recent server response code.
  71. * @var int
  72. * @access private
  73. */
  74. var $_code = -1;
  75. /**
  76. * The most recent server response arguments.
  77. * @var array
  78. * @access private
  79. */
  80. var $_arguments = array();
  81. /**
  82. * Stores detected features of the SMTP server.
  83. * @var array
  84. * @access private
  85. */
  86. var $_esmtp = array();
  87. /**
  88. * Instantiates a new Net_SMTP object, overriding any defaults
  89. * with parameters that are passed in.
  90. *
  91. * @param string The server to connect to.
  92. * @param int The port to connect to.
  93. * @param string The value to give when sending EHLO or HELO.
  94. *
  95. * @access public
  96. * @since 1.0
  97. */
  98. function Net_SMTP($host = null, $port = null, $localhost = null)
  99. {
  100. if (isset($host)) $this->host = $host;
  101. if (isset($port)) $this->port = $port;
  102. if (isset($localhost)) $this->localhost = $localhost;
  103. $this->_socket = new Net_Socket();
  104. /*
  105. * Include the Auth_SASL package. If the package is not available,
  106. * we disable the authentication methods that depend upon it.
  107. */
  108. if ((@include_once 'Auth/SASL.php') === false) {
  109. $pos = array_search('DIGEST-MD5', $this->auth_methods);
  110. unset($this->auth_methods[$pos]);
  111. $pos = array_search('CRAM-MD5', $this->auth_methods);
  112. unset($this->auth_methods[$pos]);
  113. }
  114. }
  115. /**
  116. * Set the value of the debugging flag.
  117. *
  118. * @param boolean $debug New value for the debugging flag.
  119. *
  120. * @access public
  121. * @since 1.1.0
  122. */
  123. function setDebug($debug)
  124. {
  125. $this->_debug = $debug;
  126. }
  127. /**
  128. * Send the given string of data to the server.
  129. *
  130. * @param string $data The string of data to send.
  131. *
  132. * @return mixed True on success or a PEAR_Error object on failure.
  133. *
  134. * @access private
  135. * @since 1.1.0
  136. */
  137. function _send($data)
  138. {
  139. if ($this->_debug) {
  140. echo "DEBUG: Send: $data\n";
  141. }
  142. if (PEAR::isError($error = $this->_socket->write($data))) {
  143. return new PEAR_Error('Failed to write to socket: ' .
  144. $error->getMessage());
  145. }
  146. return true;
  147. }
  148. /**
  149. * Send a command to the server with an optional string of arguments.
  150. * A carriage return / linefeed (CRLF) sequence will be appended to each
  151. * command string before it is sent to the SMTP server.
  152. *
  153. * @param string $command The SMTP command to send to the server.
  154. * @param string $args A string of optional arguments to append
  155. * to the command.
  156. *
  157. * @return mixed The result of the _send() call.
  158. *
  159. * @access private
  160. * @since 1.1.0
  161. */
  162. function _put($command, $args = '')
  163. {
  164. if (!empty($args)) {
  165. return $this->_send($command . ' ' . $args . "\r\n");
  166. }
  167. return $this->_send($command . "\r\n");
  168. }
  169. /**
  170. * Read a reply from the SMTP server. The reply consists of a response
  171. * code and a response message.
  172. *
  173. * @param mixed $valid The set of valid response codes. These
  174. * may be specified as an array of integer
  175. * values or as a single integer value.
  176. *
  177. * @return mixed True if the server returned a valid response code or
  178. * a PEAR_Error object is an error condition is reached.
  179. *
  180. * @access private
  181. * @since 1.1.0
  182. *
  183. * @see getResponse
  184. */
  185. function _parseResponse($valid)
  186. {
  187. $this->_code = -1;
  188. $this->_arguments = array();
  189. while ($line = $this->_socket->readLine()) {
  190. if ($this->_debug) {
  191. echo "DEBUG: Recv: $line\n";
  192. }
  193. /* If we receive an empty line, the connection has been closed. */
  194. if (empty($line)) {
  195. $this->disconnect();
  196. return new PEAR_Error("Connection was unexpectedly closed");
  197. }
  198. /* Read the code and store the rest in the arguments array. */
  199. $code = substr($line, 0, 3);
  200. $this->_arguments[] = trim(substr($line, 4));
  201. /* Check the syntax of the response code. */
  202. if (is_numeric($code)) {
  203. $this->_code = (int)$code;
  204. } else {
  205. $this->_code = -1;
  206. break;
  207. }
  208. /* If this is not a multiline response, we're done. */
  209. if (substr($line, 3, 1) != '-') {
  210. break;
  211. }
  212. }
  213. /* Compare the server's response code with the valid code. */
  214. if (is_int($valid) && ($this->_code === $valid)) {
  215. return true;
  216. }
  217. /* If we were given an array of valid response codes, check each one. */
  218. if (is_array($valid)) {
  219. foreach ($valid as $valid_code) {
  220. if ($this->_code === $valid_code) {
  221. return true;
  222. }
  223. }
  224. }
  225. return new PEAR_Error("Invalid response code received from server");
  226. }
  227. /**
  228. * Return a 2-tuple containing the last response from the SMTP server.
  229. *
  230. * @return array A two-element array: the first element contains the
  231. * response code as an integer and the second element
  232. * contains the response's arguments as a string.
  233. *
  234. * @access public
  235. * @since 1.1.0
  236. */
  237. function getResponse()
  238. {
  239. return array($this->_code, join("\n", $this->_arguments));
  240. }
  241. /**
  242. * Attempt to connect to the SMTP server.
  243. *
  244. * @param int $timeout The timeout value (in seconds) for the
  245. * socket connection.
  246. *
  247. * @return mixed Returns a PEAR_Error with an error message on any
  248. * kind of failure, or true on success.
  249. * @access public
  250. * @since 1.0
  251. */
  252. function connect($timeout = null)
  253. {
  254. $result = $this->_socket->connect($this->host, $this->port,
  255. false, $timeout);
  256. if (PEAR::isError($result)) {
  257. return new PEAR_Error('Failed to connect socket: ' .
  258. $result->getMessage());
  259. }
  260. if (PEAR::isError($error = $this->_parseResponse(220))) {
  261. return $error;
  262. }
  263. if (PEAR::isError($error = $this->_negotiate())) {
  264. return $error;
  265. }
  266. return true;
  267. }
  268. /**
  269. * Attempt to disconnect from the SMTP server.
  270. *
  271. * @return mixed Returns a PEAR_Error with an error message on any
  272. * kind of failure, or true on success.
  273. * @access public
  274. * @since 1.0
  275. */
  276. function disconnect()
  277. {
  278. if (PEAR::isError($error = $this->_put('QUIT'))) {
  279. return $error;
  280. }
  281. if (PEAR::isError($error = $this->_parseResponse(221))) {
  282. return $error;
  283. }
  284. if (PEAR::isError($error = $this->_socket->disconnect())) {
  285. return new PEAR_Error('Failed to disconnect socket: ' .
  286. $error->getMessage());
  287. }
  288. return true;
  289. }
  290. /**
  291. * Attempt to send the EHLO command and obtain a list of ESMTP
  292. * extensions available, and failing that just send HELO.
  293. *
  294. * @return mixed Returns a PEAR_Error with an error message on any
  295. * kind of failure, or true on success.
  296. *
  297. * @access private
  298. * @since 1.1.0
  299. */
  300. function _negotiate()
  301. {
  302. if (PEAR::isError($error = $this->_put('EHLO', $this->localhost))) {
  303. return $error;
  304. }
  305. if (PEAR::isError($this->_parseResponse(250))) {
  306. /* If we receive a 503 response, we're already authenticated. */
  307. if ($this->_code === 503) {
  308. return true;
  309. }
  310. /* If the EHLO failed, try the simpler HELO command. */
  311. if (PEAR::isError($error = $this->_put('HELO', $this->localhost))) {
  312. return $error;
  313. }
  314. if (PEAR::isError($this->_parseResponse(250))) {
  315. return new PEAR_Error('HELO was not accepted: ', $this->_code);
  316. }
  317. return true;
  318. }
  319. foreach ($this->_arguments as $argument) {
  320. $verb = strtok($argument, ' ');
  321. $arguments = substr($argument, strlen($verb) + 1,
  322. strlen($argument) - strlen($verb) - 1);
  323. $this->_esmtp[$verb] = $arguments;
  324. }
  325. return true;
  326. }
  327. /**
  328. * Returns the name of the best authentication method that the server
  329. * has advertised.
  330. *
  331. * @return mixed Returns a string containing the name of the best
  332. * supported authentication method or a PEAR_Error object
  333. * if a failure condition is encountered.
  334. * @access private
  335. * @since 1.1.0
  336. */
  337. function _getBestAuthMethod()
  338. {
  339. $available_methods = explode(' ', $this->_esmtp['AUTH']);
  340. foreach ($this->auth_methods as $method) {
  341. if (in_array($method, $available_methods)) {
  342. return $method;
  343. }
  344. }
  345. return new PEAR_Error('No supported authentication methods');
  346. }
  347. /**
  348. * Attempt to do SMTP authentication.
  349. *
  350. * @param string The userid to authenticate as.
  351. * @param string The password to authenticate with.
  352. * @param string The requested authentication method. If none is
  353. * specified, the best supported method will be used.
  354. *
  355. * @return mixed Returns a PEAR_Error with an error message on any
  356. * kind of failure, or true on success.
  357. * @access public
  358. * @since 1.0
  359. */
  360. function auth($uid, $pwd , $method = '')
  361. {
  362. if (empty($this->_esmtp['AUTH'])) {
  363. return new PEAR_Error('SMTP server does no support authentication');
  364. }
  365. /*
  366. * If no method has been specified, get the name of the best supported
  367. * method advertised by the SMTP server.
  368. */
  369. if (empty($method)) {
  370. if (PEAR::isError($method = $this->_getBestAuthMethod())) {
  371. /* Return the PEAR_Error object from _getBestAuthMethod(). */
  372. return $method;
  373. }
  374. } else {
  375. $method = strtoupper($method);
  376. if (!in_array($method, $this->auth_methods)) {
  377. return new PEAR_Error("$method is not a supported authentication method");
  378. }
  379. }
  380. switch ($method) {
  381. case 'DIGEST-MD5':
  382. $result = $this->_authDigest_MD5($uid, $pwd);
  383. break;
  384. case 'CRAM-MD5':
  385. $result = $this->_authCRAM_MD5($uid, $pwd);
  386. break;
  387. case 'LOGIN':
  388. $result = $this->_authLogin($uid, $pwd);
  389. break;
  390. case 'PLAIN':
  391. $result = $this->_authPlain($uid, $pwd);
  392. break;
  393. default:
  394. $result = new PEAR_Error("$method is not a supported authentication method");
  395. break;
  396. }
  397. /* If an error was encountered, return the PEAR_Error object. */
  398. if (PEAR::isError($result)) {
  399. return $result;
  400. }
  401. /* RFC-2554 requires us to re-negotiate ESMTP after an AUTH. */
  402. if (PEAR::isError($error = $this->_negotiate())) {
  403. return $error;
  404. }
  405. return true;
  406. }
  407. /* Authenticates the user using the DIGEST-MD5 method.
  408. *
  409. * @param string The userid to authenticate as.
  410. * @param string The password to authenticate with.
  411. *
  412. * @return mixed Returns a PEAR_Error with an error message on any
  413. * kind of failure, or true on success.
  414. * @access private
  415. * @since 1.1.0
  416. */
  417. function _authDigest_MD5($uid, $pwd)
  418. {
  419. if (PEAR::isError($error = $this->_put('AUTH', 'DIGEST-MD5'))) {
  420. return $error;
  421. }
  422. /* 334: Continue authentication request */
  423. if (PEAR::isError($error = $this->_parseResponse(334))) {
  424. /* 503: Error: already authenticated */
  425. if ($this->_code === 503) {
  426. return true;
  427. }
  428. return $error;
  429. }
  430. $challenge = base64_decode($this->_arguments[0]);
  431. $digest = &Auth_SASL::factory('digestmd5');
  432. $auth_str = base64_encode($digest->getResponse($uid, $pwd, $challenge,
  433. $this->host, "smtp"));
  434. if (PEAR::isError($error = $this->_put($auth_str))) {
  435. return $error;
  436. }
  437. /* 334: Continue authentication request */
  438. if (PEAR::isError($error = $this->_parseResponse(334))) {
  439. return $error;
  440. }
  441. /*
  442. * We don't use the protocol's third step because SMTP doesn't allow
  443. * subsequent authentication, so we just silently ignore it.
  444. */
  445. if (PEAR::isError($error = $this->_put(' '))) {
  446. return $error;
  447. }
  448. /* 235: Authentication successful */
  449. if (PEAR::isError($error = $this->_parseResponse(235))) {
  450. return $error;
  451. }
  452. }
  453. /* Authenticates the user using the CRAM-MD5 method.
  454. *
  455. * @param string The userid to authenticate as.
  456. * @param string The password to authenticate with.
  457. *
  458. * @return mixed Returns a PEAR_Error with an error message on any
  459. * kind of failure, or true on success.
  460. * @access private
  461. * @since 1.1.0
  462. */
  463. function _authCRAM_MD5($uid, $pwd)
  464. {
  465. if (PEAR::isError($error = $this->_put('AUTH', 'CRAM-MD5'))) {
  466. return $error;
  467. }
  468. /* 334: Continue authentication request */
  469. if (PEAR::isError($error = $this->_parseResponse(334))) {
  470. /* 503: Error: already authenticated */
  471. if ($this->_code === 503) {
  472. return true;
  473. }
  474. return $error;
  475. }
  476. $challenge = base64_decode($this->_arguments[0]);
  477. $cram = &Auth_SASL::factory('crammd5');
  478. $auth_str = base64_encode($cram->getResponse($uid, $pwd, $challenge));
  479. if (PEAR::isError($error = $this->_put($auth_str))) {
  480. return $error;
  481. }
  482. /* 235: Authentication successful */
  483. if (PEAR::isError($error = $this->_parseResponse(235))) {
  484. return $error;
  485. }
  486. }
  487. /**
  488. * Authenticates the user using the LOGIN method.
  489. *
  490. * @param string The userid to authenticate as.
  491. * @param string The password to authenticate with.
  492. *
  493. * @return mixed Returns a PEAR_Error with an error message on any
  494. * kind of failure, or true on success.
  495. * @access private
  496. * @since 1.1.0
  497. */
  498. function _authLogin($uid, $pwd)
  499. {
  500. if (PEAR::isError($error = $this->_put('AUTH', 'LOGIN'))) {
  501. return $error;
  502. }
  503. /* 334: Continue authentication request */
  504. if (PEAR::isError($error = $this->_parseResponse(334))) {
  505. /* 503: Error: already authenticated */
  506. if ($this->_code === 503) {
  507. return true;
  508. }
  509. return $error;
  510. }
  511. if (PEAR::isError($error = $this->_put(base64_encode($uid)))) {
  512. return $error;
  513. }
  514. /* 334: Continue authentication request */
  515. if (PEAR::isError($error = $this->_parseResponse(334))) {
  516. return $error;
  517. }
  518. if (PEAR::isError($error = $this->_put(base64_encode($pwd)))) {
  519. return $error;
  520. }
  521. /* 235: Authentication successful */
  522. if (PEAR::isError($error = $this->_parseResponse(235))) {
  523. return $error;
  524. }
  525. return true;
  526. }
  527. /**
  528. * Authenticates the user using the PLAIN method.
  529. *
  530. * @param string The userid to authenticate as.
  531. * @param string The password to authenticate with.
  532. *
  533. * @return mixed Returns a PEAR_Error with an error message on any
  534. * kind of failure, or true on success.
  535. * @access private
  536. * @since 1.1.0
  537. */
  538. function _authPlain($uid, $pwd)
  539. {
  540. if (PEAR::isError($error = $this->_put('AUTH', 'PLAIN'))) {
  541. return $error;
  542. }
  543. /* 334: Continue authentication request */
  544. if (PEAR::isError($error = $this->_parseResponse(334))) {
  545. /* 503: Error: already authenticated */
  546. if ($this->_code === 503) {
  547. return true;
  548. }
  549. return $error;
  550. }
  551. $auth_str = base64_encode(chr(0) . $uid . chr(0) . $pwd);
  552. if (PEAR::isError($error = $this->_put($auth_str))) {
  553. return $error;
  554. }
  555. /* 235: Authentication successful */
  556. if (PEAR::isError($error = $this->_parseResponse(235))) {
  557. return $error;
  558. }
  559. return true;
  560. }
  561. /**
  562. * Send the HELO command.
  563. *
  564. * @param string The domain name to say we are.
  565. *
  566. * @return mixed Returns a PEAR_Error with an error message on any
  567. * kind of failure, or true on success.
  568. * @access public
  569. * @since 1.0
  570. */
  571. function helo($domain)
  572. {
  573. if (PEAR::isError($error = $this->_put('HELO', $domain))) {
  574. return $error;
  575. }
  576. if (PEAR::isError($error = $this->_parseResponse(250))) {
  577. return $error;
  578. }
  579. return true;
  580. }
  581. /**
  582. * Send the MAIL FROM: command.
  583. *
  584. * @param string The sender (reverse path) to set.
  585. *
  586. * @return mixed Returns a PEAR_Error with an error message on any
  587. * kind of failure, or true on success.
  588. * @access public
  589. * @since 1.0
  590. */
  591. function mailFrom($sender)
  592. {
  593. if (PEAR::isError($error = $this->_put('MAIL', "FROM:<$sender>"))) {
  594. return $error;
  595. }
  596. if (PEAR::isError($error = $this->_parseResponse(250))) {
  597. return $error;
  598. }
  599. return true;
  600. }
  601. /**
  602. * Send the RCPT TO: command.
  603. *
  604. * @param string The recipient (forward path) to add.
  605. *
  606. * @return mixed Returns a PEAR_Error with an error message on any
  607. * kind of failure, or true on success.
  608. * @access public
  609. * @since 1.0
  610. */
  611. function rcptTo($recipient)
  612. {
  613. if (PEAR::isError($error = $this->_put('RCPT', "TO:<$recipient>"))) {
  614. return $error;
  615. }
  616. if (PEAR::isError($error = $this->_parseResponse(array(250, 251)))) {
  617. return $error;
  618. }
  619. return true;
  620. }
  621. /**
  622. * Quote the data so that it meets SMTP standards.
  623. *
  624. * This is provided as a separate public function to facilitate easier
  625. * overloading for the cases where it is desirable to customize the
  626. * quoting behavior.
  627. *
  628. * @param string The message text to quote. The string must be passed
  629. * by reference, and the text will be modified in place.
  630. *
  631. * @access public
  632. * @since 1.2
  633. */
  634. function quotedata(&$data)
  635. {
  636. /*
  637. * Change Unix (\n) and Mac (\r) linefeeds into Internet-standard CRLF
  638. * (\r\n) linefeeds.
  639. */
  640. $data = preg_replace("/([^\r]{1})\n/", "\\1\r\n", $data);
  641. $data = preg_replace("/\n\n/", "\n\r\n", $data);
  642. /*
  643. * Because a single leading period (.) signifies an end to the data,
  644. * legitimate leading periods need to be "doubled" (e.g. '..').
  645. */
  646. $data = preg_replace("/\n\./", "\n..", $data);
  647. }
  648. /**
  649. * Send the DATA command.
  650. *
  651. * @param string The message body to send.
  652. *
  653. * @return mixed Returns a PEAR_Error with an error message on any
  654. * kind of failure, or true on success.
  655. * @access public
  656. * @since 1.0
  657. */
  658. function data($data)
  659. {
  660. /*
  661. * RFC 1870, section 3, subsection 3 states "a value of zero indicates
  662. * that no fixed maximum message size is in force". Furthermore, it
  663. * says that if "the parameter is omitted no information is conveyed
  664. * about the server's fixed maximum message size".
  665. */
  666. if (isset($this->_esmtp['SIZE']) && ($this->_esmtp['SIZE'] > 0)) {
  667. if (strlen($data) >= $this->_esmtp['SIZE']) {
  668. $this->disconnect();
  669. return new PEAR_Error('Message size excedes the server limit');
  670. }
  671. }
  672. /* Quote the data based on the SMTP standards. */
  673. $this->quotedata($data);
  674. if (PEAR::isError($error = $this->_put('DATA'))) {
  675. return $error;
  676. }
  677. if (PEAR::isError($error = $this->_parseResponse(354))) {
  678. return $error;
  679. }
  680. if (PEAR::isError($this->_send($data . "\r\n.\r\n"))) {
  681. return new PEAR_Error('write to socket failed');
  682. }
  683. if (PEAR::isError($error = $this->_parseResponse(250))) {
  684. return $error;
  685. }
  686. return true;
  687. }
  688. /**
  689. * Send the SEND FROM: command.
  690. *
  691. * @param string The reverse path to send.
  692. *
  693. * @return mixed Returns a PEAR_Error with an error message on any
  694. * kind of failure, or true on success.
  695. * @access public
  696. * @since 1.0
  697. */
  698. function send_from($path)
  699. {
  700. if (PEAR::isError($error = $this->_put('SEND', "FROM:<$path>"))) {
  701. return $error;
  702. }
  703. if (PEAR::isError($error = $this->_parseResponse(250))) {
  704. return $error;
  705. }
  706. return true;
  707. }
  708. /**
  709. * Send the SOML FROM: command.
  710. *
  711. * @param string The reverse path to send.
  712. *
  713. * @return mixed Returns a PEAR_Error with an error message on any
  714. * kind of failure, or true on success.
  715. * @access public
  716. * @since 1.0
  717. */
  718. function soml_from($path)
  719. {
  720. if (PEAR::isError($error = $this->_put('SOML', "FROM:<$path>"))) {
  721. return $error;
  722. }
  723. if (PEAR::isError($error = $this->_parseResponse(250))) {
  724. return $error;
  725. }
  726. return true;
  727. }
  728. /**
  729. * Send the SAML FROM: command.
  730. *
  731. * @param string The reverse path to send.
  732. *
  733. * @return mixed Returns a PEAR_Error with an error message on any
  734. * kind of failure, or true on success.
  735. * @access public
  736. * @since 1.0
  737. */
  738. function saml_from($path)
  739. {
  740. if (PEAR::isError($error = $this->_put('SAML', "FROM:<$path>"))) {
  741. return $error;
  742. }
  743. if (PEAR::isError($error = $this->_parseResponse(250))) {
  744. return $error;
  745. }
  746. return true;
  747. }
  748. /**
  749. * Send the RSET command.
  750. *
  751. * @return mixed Returns a PEAR_Error with an error message on any
  752. * kind of failure, or true on success.
  753. * @access public
  754. * @since 1.0
  755. */
  756. function rset()
  757. {
  758. if (PEAR::isError($error = $this->_put('RSET'))) {
  759. return $error;
  760. }
  761. if (PEAR::isError($error = $this->_parseResponse(250))) {
  762. return $error;
  763. }
  764. return true;
  765. }
  766. /**
  767. * Send the VRFY command.
  768. *
  769. * @param string The string to verify
  770. *
  771. * @return mixed Returns a PEAR_Error with an error message on any
  772. * kind of failure, or true on success.
  773. * @access public
  774. * @since 1.0
  775. */
  776. function vrfy($string)
  777. {
  778. /* Note: 251 is also a valid response code */
  779. if (PEAR::isError($error = $this->_put('VRFY', $string))) {
  780. return $error;
  781. }
  782. if (PEAR::isError($error = $this->_parseResponse(250))) {
  783. return $error;
  784. }
  785. return true;
  786. }
  787. /**
  788. * Send the NOOP command.
  789. *
  790. * @return mixed Returns a PEAR_Error with an error message on any
  791. * kind of failure, or true on success.
  792. * @access public
  793. * @since 1.0
  794. */
  795. function noop()
  796. {
  797. if (PEAR::isError($error = $this->_put('NOOP'))) {
  798. return $error;
  799. }
  800. if (PEAR::isError($error = $this->_parseResponse(250))) {
  801. return $error;
  802. }
  803. return true;
  804. }
  805. /**
  806. * Backwards-compatibility method. identifySender()'s functionality is
  807. * now handled internally.
  808. *
  809. * @return boolean This method always return true.
  810. *
  811. * @access public
  812. * @since 1.0
  813. */
  814. function identifySender()
  815. {
  816. return true;
  817. }
  818. }
  819. ?>