PageRenderTime 37ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/www/libs/Zend/Mail/Protocol/Pop3.php

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