PageRenderTime 41ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/Cake/Network/CakeSocket.php

https://gitlab.com/fouzia23chowdhury/cakephpCRUD
PHP | 388 lines | 187 code | 38 blank | 163 comment | 31 complexity | 8a593e89f523f5a6926ff7d0ddd55d29 MD5 | raw file
  1. <?php
  2. /**
  3. * CakePHP Socket connection class.
  4. *
  5. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  6. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  7. *
  8. * Licensed under The MIT License
  9. * For full copyright and license information, please see the LICENSE.txt
  10. * Redistributions of files must retain the above copyright notice.
  11. *
  12. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  13. * @link http://cakephp.org CakePHP(tm) Project
  14. * @package Cake.Network
  15. * @since CakePHP(tm) v 1.2.0
  16. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  17. */
  18. App::uses('Validation', 'Utility');
  19. /**
  20. * CakePHP network socket connection class.
  21. *
  22. * Core base class for network communication.
  23. *
  24. * @package Cake.Network
  25. */
  26. class CakeSocket {
  27. /**
  28. * Object description
  29. *
  30. * @var string
  31. */
  32. public $description = 'Remote DataSource Network Socket Interface';
  33. /**
  34. * Base configuration settings for the socket connection
  35. *
  36. * @var array
  37. */
  38. protected $_baseConfig = array(
  39. 'persistent' => false,
  40. 'host' => 'localhost',
  41. 'protocol' => 'tcp',
  42. 'port' => 80,
  43. 'timeout' => 30
  44. );
  45. /**
  46. * Configuration settings for the socket connection
  47. *
  48. * @var array
  49. */
  50. public $config = array();
  51. /**
  52. * Reference to socket connection resource
  53. *
  54. * @var resource
  55. */
  56. public $connection = null;
  57. /**
  58. * This boolean contains the current state of the CakeSocket class
  59. *
  60. * @var bool
  61. */
  62. public $connected = false;
  63. /**
  64. * This variable contains an array with the last error number (num) and string (str)
  65. *
  66. * @var array
  67. */
  68. public $lastError = array();
  69. /**
  70. * True if the socket stream is encrypted after a CakeSocket::enableCrypto() call
  71. *
  72. * @var bool
  73. */
  74. public $encrypted = false;
  75. /**
  76. * Contains all the encryption methods available
  77. *
  78. * @var array
  79. */
  80. protected $_encryptMethods = array(
  81. // @codingStandardsIgnoreStart
  82. 'sslv2_client' => STREAM_CRYPTO_METHOD_SSLv2_CLIENT,
  83. 'sslv3_client' => STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
  84. 'sslv23_client' => STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
  85. 'tls_client' => STREAM_CRYPTO_METHOD_TLS_CLIENT,
  86. 'sslv2_server' => STREAM_CRYPTO_METHOD_SSLv2_SERVER,
  87. 'sslv3_server' => STREAM_CRYPTO_METHOD_SSLv3_SERVER,
  88. 'sslv23_server' => STREAM_CRYPTO_METHOD_SSLv23_SERVER,
  89. 'tls_server' => STREAM_CRYPTO_METHOD_TLS_SERVER
  90. // @codingStandardsIgnoreEnd
  91. );
  92. /**
  93. * Used to capture connection warnings which can happen when there are
  94. * SSL errors for example.
  95. *
  96. * @var array
  97. */
  98. protected $_connectionErrors = array();
  99. /**
  100. * Constructor.
  101. *
  102. * @param array $config Socket configuration, which will be merged with the base configuration
  103. * @see CakeSocket::$_baseConfig
  104. */
  105. public function __construct($config = array()) {
  106. $this->config = array_merge($this->_baseConfig, $config);
  107. }
  108. /**
  109. * Connects the socket to the given host and port.
  110. *
  111. * @return bool Success
  112. * @throws SocketException
  113. */
  114. public function connect() {
  115. if ($this->connection) {
  116. $this->disconnect();
  117. }
  118. $scheme = null;
  119. if (!empty($this->config['protocol']) && strpos($this->config['host'], '://') === false) {
  120. $scheme = $this->config['protocol'] . '://';
  121. }
  122. if (!empty($this->config['context'])) {
  123. $context = stream_context_create($this->config['context']);
  124. } else {
  125. $context = stream_context_create();
  126. }
  127. $connectAs = STREAM_CLIENT_CONNECT;
  128. if ($this->config['persistent']) {
  129. $connectAs |= STREAM_CLIENT_PERSISTENT;
  130. }
  131. set_error_handler(array($this, '_connectionErrorHandler'));
  132. $this->connection = stream_socket_client(
  133. $scheme . $this->config['host'] . ':' . $this->config['port'],
  134. $errNum,
  135. $errStr,
  136. $this->config['timeout'],
  137. $connectAs,
  138. $context
  139. );
  140. restore_error_handler();
  141. if (!empty($errNum) || !empty($errStr)) {
  142. $this->setLastError($errNum, $errStr);
  143. throw new SocketException($errStr, $errNum);
  144. }
  145. if (!$this->connection && $this->_connectionErrors) {
  146. $message = implode("\n", $this->_connectionErrors);
  147. throw new SocketException($message, E_WARNING);
  148. }
  149. $this->connected = is_resource($this->connection);
  150. if ($this->connected) {
  151. stream_set_timeout($this->connection, $this->config['timeout']);
  152. }
  153. return $this->connected;
  154. }
  155. /**
  156. * socket_stream_client() does not populate errNum, or $errStr when there are
  157. * connection errors, as in the case of SSL verification failure.
  158. *
  159. * Instead we need to handle those errors manually.
  160. *
  161. * @param int $code Code.
  162. * @param string $message Message.
  163. * @return void
  164. */
  165. protected function _connectionErrorHandler($code, $message) {
  166. $this->_connectionErrors[] = $message;
  167. }
  168. /**
  169. * Gets the connection context.
  170. *
  171. * @return null|array Null when there is no connection, an array when there is.
  172. */
  173. public function context() {
  174. if (!$this->connection) {
  175. return;
  176. }
  177. return stream_context_get_options($this->connection);
  178. }
  179. /**
  180. * Gets the host name of the current connection.
  181. *
  182. * @return string Host name
  183. */
  184. public function host() {
  185. if (Validation::ip($this->config['host'])) {
  186. return gethostbyaddr($this->config['host']);
  187. }
  188. return gethostbyaddr($this->address());
  189. }
  190. /**
  191. * Gets the IP address of the current connection.
  192. *
  193. * @return string IP address
  194. */
  195. public function address() {
  196. if (Validation::ip($this->config['host'])) {
  197. return $this->config['host'];
  198. }
  199. return gethostbyname($this->config['host']);
  200. }
  201. /**
  202. * Gets all IP addresses associated with the current connection.
  203. *
  204. * @return array IP addresses
  205. */
  206. public function addresses() {
  207. if (Validation::ip($this->config['host'])) {
  208. return array($this->config['host']);
  209. }
  210. return gethostbynamel($this->config['host']);
  211. }
  212. /**
  213. * Gets the last error as a string.
  214. *
  215. * @return string|null Last error
  216. */
  217. public function lastError() {
  218. if (!empty($this->lastError)) {
  219. return $this->lastError['num'] . ': ' . $this->lastError['str'];
  220. }
  221. return null;
  222. }
  223. /**
  224. * Sets the last error.
  225. *
  226. * @param int $errNum Error code
  227. * @param string $errStr Error string
  228. * @return void
  229. */
  230. public function setLastError($errNum, $errStr) {
  231. $this->lastError = array('num' => $errNum, 'str' => $errStr);
  232. }
  233. /**
  234. * Writes data to the socket.
  235. *
  236. * @param string $data The data to write to the socket
  237. * @return bool Success
  238. */
  239. public function write($data) {
  240. if (!$this->connected) {
  241. if (!$this->connect()) {
  242. return false;
  243. }
  244. }
  245. $totalBytes = strlen($data);
  246. for ($written = 0, $rv = 0; $written < $totalBytes; $written += $rv) {
  247. $rv = fwrite($this->connection, substr($data, $written));
  248. if ($rv === false || $rv === 0) {
  249. return $written;
  250. }
  251. }
  252. return $written;
  253. }
  254. /**
  255. * Reads data from the socket. Returns false if no data is available or no connection could be
  256. * established.
  257. *
  258. * @param int $length Optional buffer length to read; defaults to 1024
  259. * @return mixed Socket data
  260. */
  261. public function read($length = 1024) {
  262. if (!$this->connected) {
  263. if (!$this->connect()) {
  264. return false;
  265. }
  266. }
  267. if (!feof($this->connection)) {
  268. $buffer = fread($this->connection, $length);
  269. $info = stream_get_meta_data($this->connection);
  270. if ($info['timed_out']) {
  271. $this->setLastError(E_WARNING, __d('cake_dev', 'Connection timed out'));
  272. return false;
  273. }
  274. return $buffer;
  275. }
  276. return false;
  277. }
  278. /**
  279. * Disconnects the socket from the current connection.
  280. *
  281. * @return bool Success
  282. */
  283. public function disconnect() {
  284. if (!is_resource($this->connection)) {
  285. $this->connected = false;
  286. return true;
  287. }
  288. $this->connected = !fclose($this->connection);
  289. if (!$this->connected) {
  290. $this->connection = null;
  291. }
  292. return !$this->connected;
  293. }
  294. /**
  295. * Destructor, used to disconnect from current connection.
  296. */
  297. public function __destruct() {
  298. $this->disconnect();
  299. }
  300. /**
  301. * Resets the state of this Socket instance to it's initial state (before Object::__construct got executed)
  302. *
  303. * @param array $state Array with key and values to reset
  304. * @return bool True on success
  305. */
  306. public function reset($state = null) {
  307. if (empty($state)) {
  308. static $initalState = array();
  309. if (empty($initalState)) {
  310. $initalState = get_class_vars(__CLASS__);
  311. }
  312. $state = $initalState;
  313. }
  314. foreach ($state as $property => $value) {
  315. $this->{$property} = $value;
  316. }
  317. return true;
  318. }
  319. /**
  320. * Encrypts current stream socket, using one of the defined encryption methods.
  321. *
  322. * @param string $type Type which can be one of 'sslv2', 'sslv3', 'sslv23' or 'tls'.
  323. * @param string $clientOrServer Can be one of 'client', 'server'. Default is 'client'.
  324. * @param bool $enable Enable or disable encryption. Default is true (enable)
  325. * @return bool True on success
  326. * @throws InvalidArgumentException When an invalid encryption scheme is chosen.
  327. * @throws SocketException When attempting to enable SSL/TLS fails.
  328. * @see stream_socket_enable_crypto
  329. */
  330. public function enableCrypto($type, $clientOrServer = 'client', $enable = true) {
  331. if (!array_key_exists($type . '_' . $clientOrServer, $this->_encryptMethods)) {
  332. throw new InvalidArgumentException(__d('cake_dev', 'Invalid encryption scheme chosen'));
  333. }
  334. $enableCryptoResult = false;
  335. try {
  336. $enableCryptoResult = stream_socket_enable_crypto($this->connection, $enable,
  337. $this->_encryptMethods[$type . '_' . $clientOrServer]);
  338. } catch (Exception $e) {
  339. $this->setLastError(null, $e->getMessage());
  340. throw new SocketException($e->getMessage());
  341. }
  342. if ($enableCryptoResult === true) {
  343. $this->encrypted = $enable;
  344. return true;
  345. }
  346. $errorMessage = __d('cake_dev', 'Unable to perform enableCrypto operation on CakeSocket');
  347. $this->setLastError(null, $errorMessage);
  348. throw new SocketException($errorMessage);
  349. }
  350. }