PageRenderTime 27ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/Mail/AbstractProtocol.php

http://github.com/zendframework/zf2
PHP | 397 lines | 143 code | 75 blank | 179 comment | 19 complexity | 0e1e06894617366715119c080c232f08 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Mail
  17. * @subpackage Protocol
  18. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. */
  21. /**
  22. * @namespace
  23. */
  24. namespace Zend\Mail;
  25. use Zend\Validator\Hostname as HostnameValidator,
  26. Zend\Validator,
  27. Zend\Mail\Protocol;
  28. /**
  29. * Zend_Mail_Protocol_Abstract
  30. *
  31. * Provides low-level methods for concrete adapters to communicate with a remote mail server and track requests and responses.
  32. *
  33. * @uses \Zend\Mail\Protocol\Exception
  34. * @uses \Zend\Validator\ValidatorChain
  35. * @uses \Zend\Validator\Hostname\Hostname
  36. * @category Zend
  37. * @package Zend_Mail
  38. * @subpackage Protocol
  39. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  40. * @license http://framework.zend.com/license/new-bsd New BSD License
  41. * @todo Implement proxy settings
  42. */
  43. abstract class AbstractProtocol
  44. {
  45. /**
  46. * Mail default EOL string
  47. */
  48. const EOL = "\r\n";
  49. /**
  50. * Default timeout in seconds for initiating session
  51. */
  52. const TIMEOUT_CONNECTION = 30;
  53. /**
  54. * Maximum of the transaction log
  55. * @var integer
  56. */
  57. protected $_maximumLog = 64;
  58. /**
  59. * Hostname or IP address of remote server
  60. * @var string
  61. */
  62. protected $_host;
  63. /**
  64. * Port number of connection
  65. * @var integer
  66. */
  67. protected $_port;
  68. /**
  69. * Instance of Zend\Validator\ValidatorChain to check hostnames
  70. * @var \Zend\Validator\ValidatorChain
  71. */
  72. protected $_validHost;
  73. /**
  74. * Socket connection resource
  75. * @var resource
  76. */
  77. protected $_socket;
  78. /**
  79. * Last request sent to server
  80. * @var string
  81. */
  82. protected $_request;
  83. /**
  84. * Array of server responses to last request
  85. * @var array
  86. */
  87. protected $_response;
  88. /**
  89. * String template for parsing server responses using sscanf (default: 3 digit code and response string)
  90. * @var resource
  91. * @deprecated Since 1.10.3
  92. */
  93. protected $_template = '%d%s';
  94. /**
  95. * Log of mail requests and server responses for a session
  96. * @var array
  97. */
  98. private $_log = array();
  99. /**
  100. * Constructor.
  101. *
  102. * @param string $host OPTIONAL Hostname of remote connection (default: 127.0.0.1)
  103. * @param integer $port OPTIONAL Port number (default: null)
  104. * @throws \Zend\Mail\Protocol\Exception
  105. * @return void
  106. */
  107. public function __construct($host = '127.0.0.1', $port = null)
  108. {
  109. $this->_validHost = new Validator\ValidatorChain();
  110. $this->_validHost->addValidator(new HostnameValidator(HostnameValidator::ALLOW_ALL));
  111. if (!$this->_validHost->isValid($host)) {
  112. throw new Protocol\Exception\RuntimeException(implode(', ', $this->_validHost->getMessages()));
  113. }
  114. $this->_host = $host;
  115. $this->_port = $port;
  116. }
  117. /**
  118. * Class destructor to cleanup open resources
  119. *
  120. * @return void
  121. */
  122. public function __destruct()
  123. {
  124. $this->_disconnect();
  125. }
  126. /**
  127. * Set the maximum log size
  128. *
  129. * @param integer $maximumLog Maximum log size
  130. * @return void
  131. */
  132. public function setMaximumLog($maximumLog)
  133. {
  134. $this->_maximumLog = (int) $maximumLog;
  135. }
  136. /**
  137. * Get the maximum log size
  138. *
  139. * @return int the maximum log size
  140. */
  141. public function getMaximumLog()
  142. {
  143. return $this->_maximumLog;
  144. }
  145. /**
  146. * Create a connection to the remote host
  147. *
  148. * Concrete adapters for this class will implement their own unique connect scripts, using the _connect() method to create the socket resource.
  149. */
  150. abstract public function connect();
  151. /**
  152. * Retrieve the last client request
  153. *
  154. * @return string
  155. */
  156. public function getRequest()
  157. {
  158. return $this->_request;
  159. }
  160. /**
  161. * Retrieve the last server response
  162. *
  163. * @return array
  164. */
  165. public function getResponse()
  166. {
  167. return $this->_response;
  168. }
  169. /**
  170. * Retrieve the transaction log
  171. *
  172. * @return string
  173. */
  174. public function getLog()
  175. {
  176. return implode('', $this->_log);
  177. }
  178. /**
  179. * Reset the transaction log
  180. *
  181. * @return void
  182. */
  183. public function resetLog()
  184. {
  185. $this->_log = array();
  186. }
  187. /**
  188. * Add the transaction log
  189. *
  190. * @param string new transaction
  191. * @return void
  192. */
  193. protected function _addLog($value)
  194. {
  195. if ($this->_maximumLog >= 0 && count($this->_log) >= $this->_maximumLog) {
  196. array_shift($this->_log);
  197. }
  198. $this->_log[] = $value;
  199. }
  200. /**
  201. * Connect to the server using the supplied transport and target
  202. *
  203. * An example $remote string may be 'tcp://mail.example.com:25' or 'ssh://hostname.com:2222'
  204. *
  205. * @param string $remote Remote
  206. * @throws \Zend\Mail\Protocol\Exception
  207. * @return boolean
  208. */
  209. protected function _connect($remote)
  210. {
  211. $errorNum = 0;
  212. $errorStr = '';
  213. // open connection
  214. $this->_socket = @stream_socket_client($remote, $errorNum, $errorStr, self::TIMEOUT_CONNECTION);
  215. if ($this->_socket === false) {
  216. if ($errorNum == 0) {
  217. $errorStr = 'Could not open socket';
  218. }
  219. throw new Protocol\Exception\RuntimeException($errorStr);
  220. }
  221. if (($result = stream_set_timeout($this->_socket, self::TIMEOUT_CONNECTION)) === false) {
  222. throw new Protocol\Exception\RuntimeException('Could not set stream timeout');
  223. }
  224. return $result;
  225. }
  226. /**
  227. * Disconnect from remote host and free resource
  228. *
  229. * @return void
  230. */
  231. protected function _disconnect()
  232. {
  233. if (is_resource($this->_socket)) {
  234. fclose($this->_socket);
  235. }
  236. }
  237. /**
  238. * Send the given request followed by a LINEEND to the server.
  239. *
  240. * @param string $request
  241. * @throws \Zend\Mail\Protocol\Exception
  242. * @return integer|boolean Number of bytes written to remote host
  243. */
  244. protected function _send($request)
  245. {
  246. if (!is_resource($this->_socket)) {
  247. throw new Protocol\Exception\RuntimeException('No connection has been established to ' . $this->_host);
  248. }
  249. $this->_request = $request;
  250. $result = fwrite($this->_socket, $request . self::EOL);
  251. // Save request to internal log
  252. $this->_addLog($request . self::EOL);
  253. if ($result === false) {
  254. throw new Protocol\Exception\RuntimeException('Could not send request to ' . $this->_host);
  255. }
  256. return $result;
  257. }
  258. /**
  259. * Get a line from the stream.
  260. *
  261. * @var integer $timeout Per-request timeout value if applicable
  262. * @throws \Zend\Mail\Protocol\Exception
  263. * @return string
  264. */
  265. protected function _receive($timeout = null)
  266. {
  267. if (!is_resource($this->_socket)) {
  268. throw new Protocol\Exception\RuntimeException('No connection has been established to ' . $this->_host);
  269. }
  270. // Adapters may wish to supply per-commend timeouts according to appropriate RFC
  271. if ($timeout !== null) {
  272. stream_set_timeout($this->_socket, $timeout);
  273. }
  274. // Retrieve response
  275. $reponse = fgets($this->_socket, 1024);
  276. // Save request to internal log
  277. $this->_addLog($reponse);
  278. // Check meta data to ensure connection is still valid
  279. $info = stream_get_meta_data($this->_socket);
  280. if (!empty($info['timed_out'])) {
  281. throw new Protocol\Exception\RuntimeException($this->_host . ' has timed out');
  282. }
  283. if ($reponse === false) {
  284. throw new Protocol\Exception\RuntimeException('Could not read from ' . $this->_host);
  285. }
  286. return $reponse;
  287. }
  288. /**
  289. * Parse server response for successful codes
  290. *
  291. * Read the response from the stream and check for expected return code.
  292. * Throws a Zend_Mail_Protocol_Exception if an unexpected code is returned.
  293. *
  294. * @param string|array $code One or more codes that indicate a successful response
  295. * @throws \Zend\Mail\Protocol\Exception
  296. * @return string Last line of response string
  297. */
  298. protected function _expect($code, $timeout = null)
  299. {
  300. $this->_response = array();
  301. $cmd = '';
  302. $more = '';
  303. $msg = '';
  304. $errMsg = '';
  305. if (!is_array($code)) {
  306. $code = array($code);
  307. }
  308. do {
  309. $this->_response[] = $result = $this->_receive($timeout);
  310. list($cmd, $more, $msg) = preg_split('/([\s-]+)/', $result, 2, PREG_SPLIT_DELIM_CAPTURE);
  311. if ($errMsg !== '') {
  312. $errMsg .= ' ' . $msg;
  313. } elseif ($cmd === null || !in_array($cmd, $code)) {
  314. $errMsg = $msg;
  315. }
  316. } while (strpos($more, '-') === 0); // The '-' message prefix indicates an information string instead of a response string.
  317. if ($errMsg !== '') {
  318. throw new Protocol\Exception\RuntimeException($errMsg);
  319. }
  320. return $msg;
  321. }
  322. }