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