1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
18:
19: App::uses('Validation', 'Utility');
20:
21: 22: 23: 24: 25: 26: 27:
28: class CakeSocket {
29:
30: 31: 32: 33: 34:
35: public $description = 'Remote DataSource Network Socket Interface';
36:
37: 38: 39: 40: 41:
42: protected $_baseConfig = array(
43: 'persistent' => false,
44: 'host' => 'localhost',
45: 'protocol' => 'tcp',
46: 'port' => 80,
47: 'timeout' => 30
48: );
49:
50: 51: 52: 53: 54:
55: public $config = array();
56:
57: 58: 59: 60: 61:
62: public $connection = null;
63:
64: 65: 66: 67: 68:
69: public $connected = false;
70:
71: 72: 73: 74: 75:
76: public $lastError = array();
77:
78: 79: 80: 81: 82:
83: public $encrypted = false;
84:
85: 86: 87: 88: 89:
90: protected $_encryptMethods = array(
91:
92: 'sslv2_client' => STREAM_CRYPTO_METHOD_SSLv2_CLIENT,
93: 'sslv3_client' => STREAM_CRYPTO_METHOD_SSLv3_CLIENT,
94: 'sslv23_client' => STREAM_CRYPTO_METHOD_SSLv23_CLIENT,
95: 'tls_client' => STREAM_CRYPTO_METHOD_TLS_CLIENT,
96: 'sslv2_server' => STREAM_CRYPTO_METHOD_SSLv2_SERVER,
97: 'sslv3_server' => STREAM_CRYPTO_METHOD_SSLv3_SERVER,
98: 'sslv23_server' => STREAM_CRYPTO_METHOD_SSLv23_SERVER,
99: 'tls_server' => STREAM_CRYPTO_METHOD_TLS_SERVER
100:
101: );
102:
103: 104: 105: 106: 107: 108:
109: protected $_connectionErrors = array();
110:
111: 112: 113: 114: 115: 116:
117: public function __construct($config = array()) {
118: $this->config = array_merge($this->_baseConfig, $config);
119: if (!is_numeric($this->config['protocol'])) {
120: $this->config['protocol'] = getprotobyname($this->config['protocol']);
121: }
122: }
123:
124: 125: 126: 127: 128: 129:
130: public function connect() {
131: if ($this->connection) {
132: $this->disconnect();
133: }
134:
135: $scheme = null;
136: if (isset($this->config['request']['uri']) && $this->config['request']['uri']['scheme'] === 'https') {
137: $scheme = 'ssl://';
138: }
139:
140: if (!empty($this->config['context'])) {
141: $context = stream_context_create($this->config['context']);
142: } else {
143: $context = stream_context_create();
144: }
145:
146: $connectAs = STREAM_CLIENT_CONNECT;
147: if ($this->config['persistent']) {
148: $connectAs |= STREAM_CLIENT_PERSISTENT;
149: }
150:
151: set_error_handler(array($this, '_connectionErrorHandler'));
152: $this->connection = stream_socket_client(
153: $scheme . $this->config['host'] . ':' . $this->config['port'],
154: $errNum,
155: $errStr,
156: $this->config['timeout'],
157: $connectAs,
158: $context
159: );
160: restore_error_handler();
161:
162: if (!empty($errNum) || !empty($errStr)) {
163: $this->setLastError($errNum, $errStr);
164: throw new SocketException($errStr, $errNum);
165: }
166:
167: if (!$this->connection && $this->_connectionErrors) {
168: $message = implode("\n", $this->_connectionErrors);
169: throw new SocketException($message, E_WARNING);
170: }
171:
172: $this->connected = is_resource($this->connection);
173: if ($this->connected) {
174: stream_set_timeout($this->connection, $this->config['timeout']);
175: }
176: return $this->connected;
177: }
178:
179: 180: 181: 182: 183: 184: 185: 186: 187: 188:
189: protected function _connectionErrorHandler($code, $message) {
190: $this->_connectionErrors[] = $message;
191: }
192:
193: 194: 195: 196: 197:
198: public function context() {
199: if (!$this->connection) {
200: return;
201: }
202: return stream_context_get_options($this->connection);
203: }
204:
205: 206: 207: 208: 209:
210: public function host() {
211: if (Validation::ip($this->config['host'])) {
212: return gethostbyaddr($this->config['host']);
213: }
214: return gethostbyaddr($this->address());
215: }
216:
217: 218: 219: 220: 221:
222: public function address() {
223: if (Validation::ip($this->config['host'])) {
224: return $this->config['host'];
225: }
226: return gethostbyname($this->config['host']);
227: }
228:
229: 230: 231: 232: 233:
234: public function addresses() {
235: if (Validation::ip($this->config['host'])) {
236: return array($this->config['host']);
237: }
238: return gethostbynamel($this->config['host']);
239: }
240:
241: 242: 243: 244: 245:
246: public function lastError() {
247: if (!empty($this->lastError)) {
248: return $this->lastError['num'] . ': ' . $this->lastError['str'];
249: }
250: return null;
251: }
252:
253: 254: 255: 256: 257: 258: 259:
260: public function setLastError($errNum, $errStr) {
261: $this->lastError = array('num' => $errNum, 'str' => $errStr);
262: }
263:
264: 265: 266: 267: 268: 269:
270: public function write($data) {
271: if (!$this->connected) {
272: if (!$this->connect()) {
273: return false;
274: }
275: }
276: $totalBytes = strlen($data);
277: for ($written = 0, $rv = 0; $written < $totalBytes; $written += $rv) {
278: $rv = fwrite($this->connection, substr($data, $written));
279: if ($rv === false || $rv === 0) {
280: return $written;
281: }
282: }
283: return $written;
284: }
285:
286: 287: 288: 289: 290: 291: 292:
293: public function read($length = 1024) {
294: if (!$this->connected) {
295: if (!$this->connect()) {
296: return false;
297: }
298: }
299:
300: if (!feof($this->connection)) {
301: $buffer = fread($this->connection, $length);
302: $info = stream_get_meta_data($this->connection);
303: if ($info['timed_out']) {
304: $this->setLastError(E_WARNING, __d('cake_dev', 'Connection timed out'));
305: return false;
306: }
307: return $buffer;
308: }
309: return false;
310: }
311:
312: 313: 314: 315: 316:
317: public function disconnect() {
318: if (!is_resource($this->connection)) {
319: $this->connected = false;
320: return true;
321: }
322: $this->connected = !fclose($this->connection);
323:
324: if (!$this->connected) {
325: $this->connection = null;
326: }
327: return !$this->connected;
328: }
329:
330: 331: 332:
333: public function __destruct() {
334: $this->disconnect();
335: }
336:
337: 338: 339: 340: 341: 342:
343: public function reset($state = null) {
344: if (empty($state)) {
345: static $initalState = array();
346: if (empty($initalState)) {
347: $initalState = get_class_vars(__CLASS__);
348: }
349: $state = $initalState;
350: }
351:
352: foreach ($state as $property => $value) {
353: $this->{$property} = $value;
354: }
355: return true;
356: }
357:
358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368:
369: public function enableCrypto($type, $clientOrServer = 'client', $enable = true) {
370: if (!array_key_exists($type . '_' . $clientOrServer, $this->_encryptMethods)) {
371: throw new InvalidArgumentException(__d('cake_dev', 'Invalid encryption scheme chosen'));
372: }
373: $enableCryptoResult = false;
374: try {
375: $enableCryptoResult = stream_socket_enable_crypto($this->connection, $enable, $this->_encryptMethods[$type . '_' . $clientOrServer]);
376: } catch (Exception $e) {
377: $this->setLastError(null, $e->getMessage());
378: throw new SocketException($e->getMessage());
379: }
380: if ($enableCryptoResult === true) {
381: $this->encrypted = $enable;
382: return true;
383: }
384: $errorMessage = __d('cake_dev', 'Unable to perform enableCrypto operation on CakeSocket');
385: $this->setLastError(null, $errorMessage);
386: throw new SocketException($errorMessage);
387: }
388:
389: }
390: