PageRenderTime 50ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/zendframework/zendframework/library/Zend/Mail/Protocol/Pop3.php

https://bitbucket.org/Evgenii/zf2t
PHP | 415 lines | 222 code | 63 blank | 130 comment | 35 complexity | 3f29ba0d099d9aaae5522e16d1b87e91 MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace Zend\Mail\Protocol;
  10. use Zend\Stdlib\ErrorHandler;
  11. class Pop3
  12. {
  13. /**
  14. * Default timeout in seconds for initiating session
  15. */
  16. const TIMEOUT_CONNECTION = 30;
  17. /**
  18. * saves if server supports top
  19. * @var null|bool
  20. */
  21. public $hasTop = null;
  22. /**
  23. * socket to pop3
  24. * @var null|resource
  25. */
  26. protected $socket;
  27. /**
  28. * greeting timestamp for apop
  29. * @var null|string
  30. */
  31. protected $timestamp;
  32. /**
  33. * Public constructor
  34. *
  35. * @param string $host hostname or IP address of POP3 server, if given connect() is called
  36. * @param int|null $port port of POP3 server, null for default (110 or 995 for ssl)
  37. * @param bool|string $ssl use ssl? 'SSL', 'TLS' or false
  38. */
  39. public function __construct($host = '', $port = null, $ssl = false)
  40. {
  41. if ($host) {
  42. $this->connect($host, $port, $ssl);
  43. }
  44. }
  45. /**
  46. * Public destructor
  47. */
  48. public function __destruct()
  49. {
  50. $this->logout();
  51. }
  52. /**
  53. * Open connection to POP3 server
  54. *
  55. * @param string $host hostname or IP address of POP3 server
  56. * @param int|null $port of POP3 server, default is 110 (995 for ssl)
  57. * @param string|bool $ssl use 'SSL', 'TLS' or false
  58. * @throws Exception\RuntimeException
  59. * @return string welcome message
  60. */
  61. public function connect($host, $port = null, $ssl = false)
  62. {
  63. $isTls = false;
  64. if ($ssl) {
  65. $ssl = strtolower($ssl);
  66. }
  67. switch ($ssl) {
  68. case 'ssl':
  69. $host = 'ssl://' . $host;
  70. if (!$port) {
  71. $port = 995;
  72. }
  73. break;
  74. case 'tls':
  75. $isTls = true;
  76. // break intentionally omitted
  77. default:
  78. if (!$port) {
  79. $port = 110;
  80. }
  81. }
  82. ErrorHandler::start();
  83. $this->socket = fsockopen($host, $port, $errno, $errstr, self::TIMEOUT_CONNECTION);
  84. $error = ErrorHandler::stop();
  85. if (!$this->socket) {
  86. throw new Exception\RuntimeException(sprintf(
  87. 'cannot connect to host %s',
  88. ($error ? sprintf('; error = %s (errno = %d )', $error->getMessage(), $error->getCode()) : '')
  89. ), 0, $error);
  90. }
  91. $welcome = $this->readResponse();
  92. strtok($welcome, '<');
  93. $this->timestamp = strtok('>');
  94. if (!strpos($this->timestamp, '@')) {
  95. $this->timestamp = null;
  96. } else {
  97. $this->timestamp = '<' . $this->timestamp . '>';
  98. }
  99. if ($isTls) {
  100. $this->request('STLS');
  101. $result = stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
  102. if (!$result) {
  103. throw new Exception\RuntimeException('cannot enable TLS');
  104. }
  105. }
  106. return $welcome;
  107. }
  108. /**
  109. * Send a request
  110. *
  111. * @param string $request your request without newline
  112. * @throws Exception\RuntimeException
  113. */
  114. public function sendRequest($request)
  115. {
  116. ErrorHandler::start();
  117. $result = fputs($this->socket, $request . "\r\n");
  118. $error = ErrorHandler::stop();
  119. if (!$result) {
  120. throw new Exception\RuntimeException('send failed - connection closed?', 0, $error);
  121. }
  122. }
  123. /**
  124. * read a response
  125. *
  126. * @param bool $multiline response has multiple lines and should be read until "<nl>.<nl>"
  127. * @throws Exception\RuntimeException
  128. * @return string response
  129. */
  130. public function readResponse($multiline = false)
  131. {
  132. ErrorHandler::start();
  133. $result = fgets($this->socket);
  134. $error = ErrorHandler::stop();
  135. if (!is_string($result)) {
  136. throw new Exception\RuntimeException('read failed - connection closed?', 0, $error);
  137. }
  138. $result = trim($result);
  139. if (strpos($result, ' ')) {
  140. list($status, $message) = explode(' ', $result, 2);
  141. } else {
  142. $status = $result;
  143. $message = '';
  144. }
  145. if ($status != '+OK') {
  146. throw new Exception\RuntimeException('last request failed');
  147. }
  148. if ($multiline) {
  149. $message = '';
  150. $line = fgets($this->socket);
  151. while ($line && rtrim($line, "\r\n") != '.') {
  152. if ($line[0] == '.') {
  153. $line = substr($line, 1);
  154. }
  155. $message .= $line;
  156. $line = fgets($this->socket);
  157. };
  158. }
  159. return $message;
  160. }
  161. /**
  162. * Send request and get response
  163. *
  164. * @see sendRequest()
  165. * @see readResponse()
  166. * @param string $request request
  167. * @param bool $multiline multiline response?
  168. * @return string result from readResponse()
  169. */
  170. public function request($request, $multiline = false)
  171. {
  172. $this->sendRequest($request);
  173. return $this->readResponse($multiline);
  174. }
  175. /**
  176. * End communication with POP3 server (also closes socket)
  177. */
  178. public function logout()
  179. {
  180. if ($this->socket) {
  181. try {
  182. $this->request('QUIT');
  183. } catch (Exception\ExceptionInterface $e) {
  184. // ignore error - we're closing the socket anyway
  185. }
  186. fclose($this->socket);
  187. $this->socket = null;
  188. }
  189. }
  190. /**
  191. * Get capabilities from POP3 server
  192. *
  193. * @return array list of capabilities
  194. */
  195. public function capa()
  196. {
  197. $result = $this->request('CAPA', true);
  198. return explode("\n", $result);
  199. }
  200. /**
  201. * Login to POP3 server. Can use APOP
  202. *
  203. * @param string $user username
  204. * @param string $password password
  205. * @param bool $tryApop should APOP be tried?
  206. */
  207. public function login($user, $password, $tryApop = true)
  208. {
  209. if ($tryApop && $this->timestamp) {
  210. try {
  211. $this->request("APOP $user " . md5($this->timestamp . $password));
  212. return;
  213. } catch (Exception\ExceptionInterface $e) {
  214. // ignore
  215. }
  216. }
  217. $result = $this->request("USER $user");
  218. $result = $this->request("PASS $password");
  219. }
  220. /**
  221. * Make STAT call for message count and size sum
  222. *
  223. * @param int $messages out parameter with count of messages
  224. * @param int $octets out parameter with size in octets of messages
  225. */
  226. public function status(&$messages, &$octets)
  227. {
  228. $messages = 0;
  229. $octets = 0;
  230. $result = $this->request('STAT');
  231. list($messages, $octets) = explode(' ', $result);
  232. }
  233. /**
  234. * Make LIST call for size of message(s)
  235. *
  236. * @param int|null $msgno number of message, null for all
  237. * @return int|array size of given message or list with array(num => size)
  238. */
  239. public function getList($msgno = null)
  240. {
  241. if ($msgno !== null) {
  242. $result = $this->request("LIST $msgno");
  243. list(, $result) = explode(' ', $result);
  244. return (int) $result;
  245. }
  246. $result = $this->request('LIST', true);
  247. $messages = array();
  248. $line = strtok($result, "\n");
  249. while ($line) {
  250. list($no, $size) = explode(' ', trim($line));
  251. $messages[(int) $no] = (int) $size;
  252. $line = strtok("\n");
  253. }
  254. return $messages;
  255. }
  256. /**
  257. * Make UIDL call for getting a uniqueid
  258. *
  259. * @param int|null $msgno number of message, null for all
  260. * @return string|array uniqueid of message or list with array(num => uniqueid)
  261. */
  262. public function uniqueid($msgno = null)
  263. {
  264. if ($msgno !== null) {
  265. $result = $this->request("UIDL $msgno");
  266. list(, $result) = explode(' ', $result);
  267. return $result;
  268. }
  269. $result = $this->request('UIDL', true);
  270. $result = explode("\n", $result);
  271. $messages = array();
  272. foreach ($result as $line) {
  273. if (!$line) {
  274. continue;
  275. }
  276. list($no, $id) = explode(' ', trim($line), 2);
  277. $messages[(int) $no] = $id;
  278. }
  279. return $messages;
  280. }
  281. /**
  282. * Make TOP call for getting headers and maybe some body lines
  283. * This method also sets hasTop - before it it's not known if top is supported
  284. *
  285. * The fallback makes normal RETR call, which retrieves the whole message. Additional
  286. * lines are not removed.
  287. *
  288. * @param int $msgno number of message
  289. * @param int $lines number of wanted body lines (empty line is inserted after header lines)
  290. * @param bool $fallback fallback with full retrieve if top is not supported
  291. * @throws Exception\RuntimeException
  292. * @throws Exception\ExceptionInterface
  293. * @return string message headers with wanted body lines
  294. */
  295. public function top($msgno, $lines = 0, $fallback = false)
  296. {
  297. if ($this->hasTop === false) {
  298. if ($fallback) {
  299. return $this->retrieve($msgno);
  300. } else {
  301. throw new Exception\RuntimeException('top not supported and no fallback wanted');
  302. }
  303. }
  304. $this->hasTop = true;
  305. $lines = (!$lines || $lines < 1) ? 0 : (int) $lines;
  306. try {
  307. $result = $this->request("TOP $msgno $lines", true);
  308. } catch (Exception\ExceptionInterface $e) {
  309. $this->hasTop = false;
  310. if ($fallback) {
  311. $result = $this->retrieve($msgno);
  312. } else {
  313. throw $e;
  314. }
  315. }
  316. return $result;
  317. }
  318. /**
  319. * Make a RETR call for retrieving a full message with headers and body
  320. *
  321. * @param int $msgno message number
  322. * @return string message
  323. */
  324. public function retrieve($msgno)
  325. {
  326. $result = $this->request("RETR $msgno", true);
  327. return $result;
  328. }
  329. /**
  330. * Make a NOOP call, maybe needed for keeping the server happy
  331. */
  332. public function noop()
  333. {
  334. $this->request('NOOP');
  335. }
  336. /**
  337. * Make a DELE count to remove a message
  338. *
  339. * @param $msgno
  340. */
  341. public function delete($msgno)
  342. {
  343. $this->request("DELE $msgno");
  344. }
  345. /**
  346. * Make RSET call, which rollbacks delete requests
  347. */
  348. public function undelete()
  349. {
  350. $this->request('RSET');
  351. }
  352. }