1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
14:
15: namespace DedicatedApi\Xmlrpc;
16:
17: if (!defined('LF'))
18: {
19: define('LF', "\n");
20: }
21:
22: if (!defined('SIZE_MAX'))
23: {
24: define('SIZE_MAX', 4096*1024);
25: }
26:
27: class Client
28: {
29: public $socket;
30: public $message = false;
31: public $cb_message = array();
32: public $reqhandle;
33: public $protocol = 0;
34:
35: static $received;
36: static $sent;
37:
38: function bigEndianTest()
39: {
40: list($endiantest) = array_values(unpack('L1L', pack('V', 1)));
41: if ($endiantest != 1)
42: {
43: if(!function_exists(__NAMESPACE__.'\\unpack'))
44: {
45: 46: 47: 48: 49:
50: function unpack($format, $data)
51: {
52: $ar = unpack($format, $data);
53: $vals = array_values($ar);
54: $f = explode('/', $format);
55: $i = 0;
56: foreach ($f as $f_k => $f_v)
57: {
58: $repeater = intval(substr($f_v, 1));
59: if ($repeater == 0)
60: {
61: $repeater = 1;
62: }
63: if ($f_v{1} == '*')
64: {
65: $repeater = count($ar) - $i;
66: }
67: if ($f_v{0} != 'd')
68: {
69: $i += $repeater;
70: continue;
71: }
72: $j = $i + $repeater;
73: for ($a = $i; $a < $j; ++$a)
74: {
75: $p = pack('d', $vals[$i]);
76: $p = strrev($p);
77: list($vals[$i]) = array_values(unpack('d1d', $p));
78: ++$i;
79: }
80: }
81: $a = 0;
82: foreach ($ar as $ar_k => $ar_v)
83: {
84: $ar[$ar_k] = $vals[$a];
85: ++$a;
86: }
87: return $ar;
88: }
89: }
90: }
91: }
92:
93: function __construct($hostname = 'localhost', $port = 5000, $timeout)
94: {
95: $this->socket = false;
96: $this->reqhandle = 0x80000000;
97: $this->init($hostname, $port, $timeout);
98: }
99:
100: function __destruct()
101: {
102: $this->terminate();
103: }
104:
105: protected function init($hostname, $port, $timeout)
106: {
107:
108: $this->bigEndianTest();
109:
110:
111: $this->socket = @fsockopen($hostname, $port, $errno, $errstr, $timeout);
112: if (!$this->socket)
113: {
114: throw new Exception("transport error - could not open socket (error: $errno, $errstr)", -32300);
115: }
116:
117: $array_result = unpack('Vsize', fread($this->socket, 4));
118: $size = $array_result['size'];
119: if ($size > 64)
120: {
121: throw new Exception('transport error - wrong lowlevel protocol header', -32300);
122: }
123: $handshake = fread($this->socket, $size);
124: if ($handshake == 'GBXRemote 1')
125: {
126: $this->protocol = 1;
127: }
128: elseif ($handshake == 'GBXRemote 2')
129: {
130: $this->protocol = 2;
131: }
132: else
133: {
134: throw new Exception('transport error - wrong lowlevel protocol version', -32300);
135: }
136: }
137:
138: function terminate()
139: {
140: if ($this->socket)
141: {
142: fclose($this->socket);
143: $this->socket = false;
144: }
145: }
146:
147: protected function sendRequest(Request $request)
148: {
149: $xml = $request->getXml();
150:
151: @stream_set_timeout($this->socket, 5);
152:
153: $this->reqhandle++;
154: if ($this->protocol == 1)
155: {
156: $bytes = pack('Va*', strlen($xml), $xml);
157: }
158: else
159: {
160: $bytes = pack('VVa*', strlen($xml), $this->reqhandle, $xml);
161: }
162:
163: $bytes_to_write = strlen($bytes);
164:
165:
166: self::$sent += $bytes_to_write;
167:
168: while ($bytes_to_write > 0)
169: {
170: $r = fwrite($this->socket, $bytes);
171: if ($r === false || $r == 0)
172: {
173: throw new Exception('Connection interupted');
174: }
175:
176: $bytes_to_write -= $r;
177: if ($bytes_to_write == 0)
178: {
179: break;
180: }
181:
182: $bytes = substr($bytes, $r);
183: }
184: }
185:
186: protected function getResult()
187: {
188: $contents = '';
189: $contents_length = 0;
190: do
191: {
192: $size = 0;
193: $recvhandle = 0;
194: @stream_set_timeout($this->socket, 5);
195:
196: if ($this->protocol == 1)
197: {
198: $contents = fread($this->socket, 4);
199: if (strlen($contents) == 0)
200: {
201: throw new Exception('transport error - connection interrupted!', -32700);
202: }
203: $array_result = unpack('Vsize', $contents);
204: $size = $array_result['size'];
205: $recvhandle = $this->reqhandle;
206: }
207: else
208: {
209: $contents = fread($this->socket, 8);
210: if (strlen($contents) == 0)
211: {
212: throw new Exception('transport error - connection interrupted!', -32700);
213: }
214: $array_result = unpack('Vsize/Vhandle', $contents);
215: $size = $array_result['size'];
216: $recvhandle = $array_result['handle'];
217:
218: $bits = sprintf('%b', $recvhandle);
219: if (strlen($bits) == 64)
220: {
221: $recvhandle = bindec(substr($bits, 32));
222: }
223: }
224:
225: if ($recvhandle == 0 || $size == 0)
226: {
227: throw new Exception('transport error - connection interrupted!', -32700);
228: }
229:
230: if ($size > SIZE_MAX)
231: {
232: throw new Exception("transport error - answer too big ($size)", -32700);
233: }
234:
235: self::$received += $size;
236:
237: $contents = '';
238: $contents_length = 0;
239: @stream_set_timeout($this->socket, 0, 10000);
240: while ($contents_length < $size)
241: {
242: $contents .= fread($this->socket, $size-$contents_length);
243: $contents_length = strlen($contents);
244: }
245:
246: if (($recvhandle & 0x80000000) == 0)
247: {
248:
249:
250: $new_cb_message = new Message($contents);
251: if ($new_cb_message->parse() && $new_cb_message->messageType != 'fault')
252: {
253: array_push($this->cb_message, array($new_cb_message->methodName, $new_cb_message->params));
254: }
255: }
256: }
257: while ((int)$recvhandle != (int)$this->reqhandle);
258:
259: $this->message = new Message($contents);
260: if (!$this->message->parse())
261: {
262:
263: throw new Exception('parse error. not well formed', -32700);
264: }
265:
266: if ($this->message->messageType == 'fault')
267: {
268: throw new Exception($this->message->faultString, $this->message->faultCode);
269: }
270:
271: return $this->message;
272: }
273:
274:
275: function query()
276: {
277: $args = func_get_args();
278: $method = array_shift($args);
279:
280: if (!$this->socket || $this->protocol == 0)
281: {
282: throw new Exception('transport error - Client not initialized', -32300);
283: }
284:
285: $request = new Request($method, $args);
286:
287:
288: if ($request->getLength() > 512*1024-8)
289: {
290: throw new Exception('transport error - request too large!', -32700);
291: }
292:
293: $this->sendRequest($request);
294: return $this->getResult();
295: }
296:
297:
298: function queryIgnoreResult()
299: {
300: $args = func_get_args();
301: $method = array_shift($args);
302:
303: if (!$this->socket || $this->protocol == 0)
304: {
305: throw new Exception('transport error - Client not initialized', -32300);
306: }
307:
308: $request = new Request($method, $args);
309:
310:
311:
312: if ($request->getLength() > 512*1024-8)
313: {
314: if ($method == 'system.multicall' && isset($args[0]))
315: {
316: $count = count($args[0]);
317:
318: if ($count < 2)
319: {
320: throw new Exception('transport error - request too large!', -32700);
321: }
322: $length = floor($count/2);
323:
324: $args1 = array_slice($args[0], 0, $length);
325: $args2 = array_slice($args[0], $length, ($count-$length));
326:
327: $res1 = $this->queryIgnoreResult('system.multicall', $args1);
328: $res2 = $this->queryIgnoreResult('system.multicall', $args2);
329: return ($res1 && $res2);
330: }
331:
332: else
333: {
334: throw new Exception('transport error - request too large!', -32700);
335: }
336: }
337:
338: $this->sendRequest($request);
339: }
340:
341: function getResponse()
342: {
343:
344: return $this->message->params[0];
345: }
346:
347: function readCallbacks($timeout = 2000)
348: {
349: if (!$this->socket || $this->protocol == 0)
350: throw new Exception('transport error - Client not initialized', -32300);
351: if ($this->protocol == 1)
352: return false;
353:
354:
355:
356: $contents = '';
357: $contents_length = 0;
358:
359: @stream_set_timeout($this->socket, 0, 10000);
360:
361: $read = array($this->socket);
362: $write = NULL;
363: $except = NULL;
364: $nb = false;
365:
366: try
367: {
368: $nb = @stream_select($read, $write, $except, 0, $timeout);
369: }
370: catch (\Exception $e)
371: {
372: if (strpos($e->getMessage(), 'Invalid CRT') !== false)
373: {
374: $nb = true;
375: }
376: elseif (strpos($e->getMessage(), 'Interrupted system call') !== false)
377: {
378: return;
379: }
380: else
381: {
382: throw $e;
383: }
384: }
385:
386:
387: if ($nb !== false)
388: {
389: $nb = count($read);
390: }
391:
392: while ($nb !== false && $nb > 0)
393: {
394: $timeout = 0;
395:
396: $size = 0;
397: $recvhandle = 0;
398:
399: $contents = fread($this->socket, 8);
400: if (strlen($contents) == 0)
401: {
402: throw new Exception('transport error - connection interrupted!', -32700);
403: }
404: $array_result = unpack('Vsize/Vhandle', $contents);
405: $size = $array_result['size'];
406: $recvhandle = $array_result['handle'];
407:
408: if ($recvhandle == 0 || $size == 0)
409: {
410: throw new Exception('transport error - connection interrupted!', -32700);
411: }
412: if ($size > SIZE_MAX)
413: {
414: throw new Exception("transport error - answer too big ($size)", -32700);
415: }
416:
417: self::$received += $size;
418:
419: $contents = '';
420: $contents_length = 0;
421: while ($contents_length < $size)
422: {
423: $contents .= fread($this->socket, $size-$contents_length);
424: $contents_length = strlen($contents);
425: }
426:
427: if (($recvhandle & 0x80000000) == 0)
428: {
429:
430:
431: $new_cb_message = new Message($contents);
432: if ($new_cb_message->parse() && $new_cb_message->messageType != 'fault')
433: {
434: array_push($this->cb_message, array($new_cb_message->methodName, $new_cb_message->params));
435: }
436:
437:
438: }
439:
440:
441: $read = array($this->socket);
442: $write = NULL;
443: $except = NULL;
444:
445: try
446: {
447: $nb = @stream_select($read, $write, $except, 0, $timeout);
448: }
449: catch (\Exception $e)
450: {
451: if (strpos($e->getMessage(), 'Invalid CRT') !== false)
452: {
453: $nb = true;
454: }
455: else
456: {
457: throw $e;
458: }
459: }
460:
461:
462: if ($nb !== false)
463: {
464: $nb = count($read);
465: }
466: }
467: return !empty($this->cb_message);
468: }
469:
470: function getCallbackResponses()
471: {
472:
473: $messages = $this->cb_message;
474: $this->cb_message = array();
475: return $messages;
476: }
477: }
478:
479: ?>