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

/rainloop/v/0.0.0/app/libraries/MailSo/Net/NetClient.php

https://gitlab.com/wuhang2003/rainloop-webmail
PHP | 629 lines | 372 code | 86 blank | 171 comment | 41 complexity | 8d8277204586fca25a7c036aa3ebb7cb MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of MailSo.
  4. *
  5. * (c) 2014 Usenko Timur
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace MailSo\Net;
  11. /**
  12. * @category MailSo
  13. * @package Net
  14. */
  15. abstract class NetClient
  16. {
  17. /**
  18. * @var resource
  19. */
  20. protected $rConnect;
  21. /**
  22. * @var bool
  23. */
  24. protected $bUnreadBuffer;
  25. /**
  26. * @var bool
  27. */
  28. protected $bRunningCallback;
  29. /**
  30. * @var string
  31. */
  32. protected $sResponseBuffer;
  33. /**
  34. * @var int
  35. */
  36. protected $iSecurityType;
  37. /**
  38. * @var string
  39. */
  40. protected $sConnectedHost;
  41. /**
  42. * @var int
  43. */
  44. protected $iConnectedPort;
  45. /**
  46. * @var bool
  47. */
  48. protected $bSecure;
  49. /**
  50. * @var int
  51. */
  52. protected $iConnectTimeOut;
  53. /**
  54. * @var int
  55. */
  56. protected $iSocketTimeOut;
  57. /**
  58. * @var int
  59. */
  60. protected $iStartConnectTime;
  61. /**
  62. * @var \MailSo\Log\Logger
  63. */
  64. protected $oLogger;
  65. /**
  66. * @var bool
  67. */
  68. public $__AUTOLOGOUT__;
  69. /**
  70. * @access protected
  71. */
  72. protected function __construct()
  73. {
  74. $this->rConnect = null;
  75. $this->bUnreadBuffer = false;
  76. $this->bRunningCallback = false;
  77. $this->oLogger = null;
  78. $this->__AUTOLOGOUT__ = true;
  79. $this->sResponseBuffer = '';
  80. $this->iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::NONE;
  81. $this->sConnectedHost = '';
  82. $this->iConnectedPort = 0;
  83. $this->bSecure = false;
  84. $this->iConnectTimeOut = 10;
  85. $this->iSocketTimeOut = 10;
  86. $this->Clear();
  87. }
  88. /**
  89. * @return void
  90. */
  91. public function __destruct()
  92. {
  93. try
  94. {
  95. if ($this->__AUTOLOGOUT__)
  96. {
  97. $this->LogoutAndDisconnect();
  98. }
  99. else
  100. {
  101. $this->Disconnect();
  102. }
  103. }
  104. catch (\Exception $oException) {}
  105. }
  106. /**
  107. * @return void
  108. */
  109. public function Clear()
  110. {
  111. $this->sResponseBuffer = '';
  112. $this->sConnectedHost = '';
  113. $this->iConnectedPort = 0;
  114. $this->iStartConnectTime = 0;
  115. $this->bSecure = false;
  116. }
  117. /**
  118. * @return string
  119. */
  120. public function GetConnectedHost()
  121. {
  122. return $this->sConnectedHost;
  123. }
  124. /**
  125. * @return int
  126. */
  127. public function GetConnectedPort()
  128. {
  129. return $this->iConnectedPort;
  130. }
  131. /**
  132. * @param int $iConnectTimeOut = 10
  133. * @param int $iSocketTimeOut = 10
  134. *
  135. * @return void
  136. */
  137. public function SetTimeOuts($iConnectTimeOut = 10, $iSocketTimeOut = 10)
  138. {
  139. $this->iConnectTimeOut = 5 < $iConnectTimeOut ? $iConnectTimeOut : 5;
  140. $this->iSocketTimeOut = 5 < $iSocketTimeOut ? $iSocketTimeOut : 5;
  141. }
  142. /**
  143. * @return resource|null
  144. */
  145. public function ConnectionResource()
  146. {
  147. return $this->rConnect;
  148. }
  149. /**
  150. * @param int $iErrNo
  151. * @param string $sErrStr
  152. * @param string $sErrFile
  153. * @param int $iErrLine
  154. *
  155. * @return bool
  156. */
  157. public function capturePhpErrorWithException($iErrNo, $sErrStr, $sErrFile, $iErrLine)
  158. {
  159. throw new \MailSo\Base\Exceptions\Exception($sErrStr, $iErrNo);
  160. }
  161. /**
  162. * @param string $sServerName
  163. * @param int $iPort
  164. * @param int $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT
  165. * @param bool $bVerifySsl = false
  166. * @param bool $bAllowSelfSigned = true
  167. *
  168. * @return void
  169. *
  170. * @throws \MailSo\Base\Exceptions\InvalidArgumentException
  171. * @throws \MailSo\Net\Exceptions\SocketAlreadyConnectedException
  172. * @throws \MailSo\Net\Exceptions\SocketCanNotConnectToHostException
  173. */
  174. public function Connect($sServerName, $iPort,
  175. $iSecurityType = \MailSo\Net\Enumerations\ConnectionSecurityType::AUTO_DETECT,
  176. $bVerifySsl = false, $bAllowSelfSigned = true)
  177. {
  178. if (!\MailSo\Base\Validator::NotEmptyString($sServerName, true) || !\MailSo\Base\Validator::PortInt($iPort))
  179. {
  180. $this->writeLogException(
  181. new \MailSo\Base\Exceptions\InvalidArgumentException(),
  182. \MailSo\Log\Enumerations\Type::ERROR, true);
  183. }
  184. if ($this->IsConnected())
  185. {
  186. $this->writeLogException(
  187. new Exceptions\SocketAlreadyConnectedException(),
  188. \MailSo\Log\Enumerations\Type::ERROR, true);
  189. }
  190. $sServerName = \trim($sServerName);
  191. $sErrorStr = '';
  192. $iErrorNo = 0;
  193. $this->sConnectedHost = $sServerName;
  194. $this->iConnectedPort = $iPort;
  195. $this->iSecurityType = $iSecurityType;
  196. $this->bSecure = \MailSo\Net\Enumerations\ConnectionSecurityType::UseSSL(
  197. $this->iConnectedPort, $this->iSecurityType);
  198. if (!\preg_match('/^[a-z0-9._]{2,8}:\/\//i', $this->sConnectedHost))
  199. {
  200. $this->sConnectedHost = ($this->bSecure ? 'ssl://' : 'tcp://').$this->sConnectedHost;
  201. // $this->sConnectedHost = ($this->bSecure ? 'ssl://' : '').$this->sConnectedHost;
  202. }
  203. if (!$this->bSecure && \MailSo\Net\Enumerations\ConnectionSecurityType::SSL === $this->iSecurityType)
  204. {
  205. $this->writeLogException(
  206. new \MailSo\Net\Exceptions\SocketUnsuppoterdSecureConnectionException('SSL isn\'t supported: ('.\implode(', ', \stream_get_transports()).')'),
  207. \MailSo\Log\Enumerations\Type::ERROR, true);
  208. }
  209. $this->iStartConnectTime = \microtime(true);
  210. $this->writeLog('Start connection to "'.$this->sConnectedHost.':'.$this->iConnectedPort.'"',
  211. \MailSo\Log\Enumerations\Type::NOTE);
  212. // $this->rConnect = @\fsockopen($this->sConnectedHost, $this->iConnectedPort,
  213. // $iErrorNo, $sErrorStr, $this->iConnectTimeOut);
  214. $bVerifySsl = !!$bVerifySsl;
  215. $bAllowSelfSigned = $bVerifySsl ? !!$bAllowSelfSigned : true;
  216. $aStreamContextSettings = array(
  217. 'ssl' => array(
  218. 'verify_host' => $bVerifySsl,
  219. 'verify_peer' => $bVerifySsl,
  220. 'verify_peer_name' => $bVerifySsl,
  221. 'allow_self_signed' => $bAllowSelfSigned
  222. )
  223. );
  224. \MailSo\Hooks::Run('Net.NetClient.StreamContextSettings/Filter', array(&$aStreamContextSettings));
  225. $rStreamContext = \stream_context_create($aStreamContextSettings);
  226. \set_error_handler(array(&$this, 'capturePhpErrorWithException'));
  227. try
  228. {
  229. $this->rConnect = \stream_socket_client($this->sConnectedHost.':'.$this->iConnectedPort,
  230. $iErrorNo, $sErrorStr, $this->iConnectTimeOut, STREAM_CLIENT_CONNECT, $rStreamContext);
  231. }
  232. catch (\Exception $oExc)
  233. {
  234. $sErrorStr = $oExc->getMessage();
  235. $iErrorNo = $oExc->getCode();
  236. }
  237. \restore_error_handler();
  238. $this->writeLog('Connected ('.(\is_resource($this->rConnect) ? 'success' : 'unsuccess').')',
  239. \MailSo\Log\Enumerations\Type::NOTE);
  240. if (!\is_resource($this->rConnect))
  241. {
  242. $this->writeLogException(
  243. new Exceptions\SocketCanNotConnectToHostException(
  244. \MailSo\Base\Utils::ConvertSystemString($sErrorStr), (int) $iErrorNo,
  245. 'Can\'t connect to host "'.$this->sConnectedHost.':'.$this->iConnectedPort.'"'
  246. ), \MailSo\Log\Enumerations\Type::NOTICE, true);
  247. }
  248. $this->writeLog((\microtime(true) - $this->iStartConnectTime).' (raw connection)',
  249. \MailSo\Log\Enumerations\Type::TIME);
  250. if ($this->rConnect)
  251. {
  252. if (\MailSo\Base\Utils::FunctionExistsAndEnabled('stream_set_timeout'))
  253. {
  254. @\stream_set_timeout($this->rConnect, $this->iSocketTimeOut);
  255. }
  256. }
  257. }
  258. public function EnableCrypto()
  259. {
  260. $bError = true;
  261. if (\is_resource($this->rConnect) &&
  262. \MailSo\Base\Utils::FunctionExistsAndEnabled('stream_socket_enable_crypto'))
  263. {
  264. switch (true)
  265. {
  266. case defined('STREAM_CRYPTO_METHOD_ANY_CLIENT') &&
  267. @\stream_socket_enable_crypto($this->rConnect, true, STREAM_CRYPTO_METHOD_ANY_CLIENT):
  268. case @\stream_socket_enable_crypto($this->rConnect, true, STREAM_CRYPTO_METHOD_TLS_CLIENT):
  269. case @\stream_socket_enable_crypto($this->rConnect, true, STREAM_CRYPTO_METHOD_SSLv23_CLIENT):
  270. $bError = false;
  271. break;
  272. }
  273. }
  274. if ($bError)
  275. {
  276. $this->writeLogException(
  277. new \MailSo\Net\Exceptions\Exception('Cannot enable STARTTLS.'),
  278. \MailSo\Log\Enumerations\Type::ERROR, true);
  279. }
  280. }
  281. /**
  282. * @return void
  283. */
  284. public function Disconnect()
  285. {
  286. if (\is_resource($this->rConnect))
  287. {
  288. $bResult = \fclose($this->rConnect);
  289. $this->writeLog('Disconnected from "'.$this->sConnectedHost.':'.$this->iConnectedPort.'" ('.
  290. (($bResult) ? 'success' : 'unsuccess').')', \MailSo\Log\Enumerations\Type::NOTE);
  291. if (0 !== $this->iStartConnectTime)
  292. {
  293. $this->writeLog((\microtime(true) - $this->iStartConnectTime).' (net session)',
  294. \MailSo\Log\Enumerations\Type::TIME);
  295. $this->iStartConnectTime = 0;
  296. }
  297. $this->rConnect = null;
  298. }
  299. }
  300. /**
  301. * @retun void
  302. *
  303. * @throws \MailSo\Net\Exceptions\Exception
  304. */
  305. public function LogoutAndDisconnect()
  306. {
  307. if (\method_exists($this, 'Logout') && !$this->bUnreadBuffer && !$this->bRunningCallback)
  308. {
  309. $this->Logout();
  310. }
  311. $this->Disconnect();
  312. }
  313. /**
  314. * @param bool $bThrowExceptionOnFalse = false
  315. *
  316. * @return bool
  317. */
  318. public function IsConnected($bThrowExceptionOnFalse = false)
  319. {
  320. $bResult = \is_resource($this->rConnect);
  321. if (!$bResult && $bThrowExceptionOnFalse)
  322. {
  323. $this->writeLogException(
  324. new Exceptions\SocketConnectionDoesNotAvailableException(),
  325. \MailSo\Log\Enumerations\Type::ERROR, true);
  326. }
  327. return $bResult;
  328. }
  329. /**
  330. * @return void
  331. *
  332. * @throws \MailSo\Net\Exceptions\SocketConnectionDoesNotAvailableException
  333. */
  334. public function IsConnectedWithException()
  335. {
  336. $this->IsConnected(true);
  337. }
  338. /**
  339. * @return array|bool
  340. */
  341. public function StreamContextParams()
  342. {
  343. return \is_resource($this->rConnect) && \MailSo\Base\Utils::FunctionExistsAndEnabled('stream_context_get_options')
  344. ? \stream_context_get_params($this->rConnect) : false;
  345. }
  346. /**
  347. * @param string $sRaw
  348. * @param bool $bWriteToLog = true
  349. * @param string $sFakeRaw = ''
  350. *
  351. * @return void
  352. *
  353. * @throws \MailSo\Net\Exceptions\SocketConnectionDoesNotAvailableException
  354. * @throws \MailSo\Net\Exceptions\SocketWriteException
  355. */
  356. protected function sendRaw($sRaw, $bWriteToLog = true, $sFakeRaw = '')
  357. {
  358. if ($this->bUnreadBuffer)
  359. {
  360. $this->writeLogException(
  361. new Exceptions\SocketUnreadBufferException(),
  362. \MailSo\Log\Enumerations\Type::ERROR, true);
  363. }
  364. $bFake = 0 < \strlen($sFakeRaw);
  365. $sRaw .= "\r\n";
  366. if ($this->oLogger && $this->oLogger->IsShowSecter())
  367. {
  368. $bFake = false;
  369. }
  370. if ($bFake)
  371. {
  372. $sFakeRaw .= "\r\n";
  373. }
  374. $mResult = @\fwrite($this->rConnect, $sRaw);
  375. if (false === $mResult)
  376. {
  377. $this->IsConnected(true);
  378. $this->writeLogException(
  379. new Exceptions\SocketWriteException(),
  380. \MailSo\Log\Enumerations\Type::ERROR, true);
  381. }
  382. else
  383. {
  384. \MailSo\Base\Loader::IncStatistic('NetWrite', $mResult);
  385. if ($bWriteToLog)
  386. {
  387. $this->writeLogWithCrlf('> '.($bFake ? $sFakeRaw : $sRaw), //.' ['.$iWriteSize.']',
  388. $bFake ? \MailSo\Log\Enumerations\Type::SECURE : \MailSo\Log\Enumerations\Type::INFO);
  389. }
  390. }
  391. }
  392. /**
  393. * @param mixed $mReadLen = null
  394. * @param bool $bForceLogin = false
  395. *
  396. * @return void
  397. *
  398. * @throws \MailSo\Net\Exceptions\SocketConnectionDoesNotAvailableException
  399. * @throws \MailSo\Net\Exceptions\SocketReadException
  400. */
  401. protected function getNextBuffer($mReadLen = null, $bForceLogin = false)
  402. {
  403. if (null === $mReadLen)
  404. {
  405. $this->sResponseBuffer = @\fgets($this->rConnect);
  406. }
  407. else
  408. {
  409. $this->sResponseBuffer = '';
  410. $iRead = $mReadLen;
  411. while (0 < $iRead)
  412. {
  413. $sAddRead = @\fread($this->rConnect, $iRead);
  414. if (false === $sAddRead)
  415. {
  416. $this->sResponseBuffer = false;
  417. break;
  418. }
  419. $this->sResponseBuffer .= $sAddRead;
  420. $iRead -= \strlen($sAddRead);
  421. }
  422. }
  423. if (false === $this->sResponseBuffer)
  424. {
  425. $this->IsConnected(true);
  426. $this->bUnreadBuffer = true;
  427. $aSocketStatus = @\stream_get_meta_data($this->rConnect);
  428. if (isset($aSocketStatus['timed_out']) && $aSocketStatus['timed_out'])
  429. {
  430. $this->writeLogException(
  431. new Exceptions\SocketReadTimeoutException(),
  432. \MailSo\Log\Enumerations\Type::ERROR, true);
  433. }
  434. else
  435. {
  436. $this->writeLog('Stream Meta: '.
  437. \print_r($aSocketStatus, true), \MailSo\Log\Enumerations\Type::ERROR);
  438. $this->writeLogException(
  439. new Exceptions\SocketReadException(),
  440. \MailSo\Log\Enumerations\Type::ERROR, true);
  441. }
  442. }
  443. else
  444. {
  445. $iReadedLen = \strlen($this->sResponseBuffer);
  446. if (null === $mReadLen || $bForceLogin)
  447. {
  448. $iLimit = 5000; // 5KB
  449. if ($iLimit < $iReadedLen)
  450. {
  451. $this->writeLogWithCrlf('[cutted:'.$iReadedLen.'] < '.\substr($this->sResponseBuffer, 0, $iLimit).'...',
  452. \MailSo\Log\Enumerations\Type::INFO);
  453. }
  454. else
  455. {
  456. $this->writeLogWithCrlf('< '.$this->sResponseBuffer, //.' ['.$iReadedLen.']',
  457. \MailSo\Log\Enumerations\Type::INFO);
  458. }
  459. }
  460. else
  461. {
  462. $this->writeLog('Received '.$iReadedLen.'/'.$mReadLen.' bytes.',
  463. \MailSo\Log\Enumerations\Type::INFO);
  464. }
  465. \MailSo\Base\Loader::IncStatistic('NetRead', $iReadedLen);
  466. }
  467. }
  468. /**
  469. * @return string
  470. */
  471. protected function getLogName()
  472. {
  473. return 'NET';
  474. }
  475. /**
  476. * @param string $sDesc
  477. * @param int $iDescType = \MailSo\Log\Enumerations\Type::INFO
  478. *
  479. * @return void
  480. */
  481. protected function writeLog($sDesc, $iDescType = \MailSo\Log\Enumerations\Type::INFO, $bDiplayCrLf = false)
  482. {
  483. if ($this->oLogger)
  484. {
  485. $this->oLogger->Write($sDesc, $iDescType, $this->getLogName(), true, $bDiplayCrLf);
  486. }
  487. }
  488. /**
  489. * @param string $sDesc
  490. * @param int $iDescType = \MailSo\Log\Enumerations\Type::INFO
  491. *
  492. * @return void
  493. */
  494. protected function writeLogWithCrlf($sDesc, $iDescType = \MailSo\Log\Enumerations\Type::INFO)
  495. {
  496. $this->writeLog($sDesc, $iDescType, true);
  497. }
  498. /**
  499. * @param \Exception $oException
  500. * @param int $iDescType = \MailSo\Log\Enumerations\Type::NOTICE
  501. * @param bool $bThrowException = false
  502. *
  503. * @return void
  504. */
  505. protected function writeLogException($oException,
  506. $iDescType = \MailSo\Log\Enumerations\Type::NOTICE, $bThrowException = false)
  507. {
  508. if ($this->oLogger)
  509. {
  510. if ($oException instanceof Exceptions\SocketCanNotConnectToHostException)
  511. {
  512. $this->oLogger->Write('Socket: ['.$oException->getSocketCode().'] '.$oException->getSocketMessage(), $iDescType, $this->getLogName());
  513. }
  514. $this->oLogger->WriteException($oException, $iDescType, $this->getLogName());
  515. }
  516. if ($bThrowException)
  517. {
  518. throw $oException;
  519. }
  520. }
  521. /**
  522. * @param \MailSo\Log\Logger $oLogger
  523. *
  524. * @return void
  525. *
  526. * @throws \MailSo\Base\Exceptions\InvalidArgumentException
  527. */
  528. public function SetLogger($oLogger)
  529. {
  530. if (!($oLogger instanceof \MailSo\Log\Logger))
  531. {
  532. throw new \MailSo\Base\Exceptions\InvalidArgumentException();
  533. }
  534. $this->oLogger = $oLogger;
  535. }
  536. /**
  537. * @return \MailSo\Log\Logger|null
  538. */
  539. public function Logger()
  540. {
  541. return $this->oLogger;
  542. }
  543. }