Редакция 69 | Содержимое файла | Сравнить с предыдущей | Последнее изменение | Открыть журнал | RSS
Редакция | Автор | № строки | Строка |
---|---|---|---|
69 | alex-w | 1 | <?php |
2 | /** |
||
3 | * PEAR_Remote |
||
4 | * |
||
5 | * PHP versions 4 and 5 |
||
6 | * |
||
7 | * LICENSE: This source file is subject to version 3.0 of the PHP license |
||
8 | * that is available through the world-wide-web at the following URI: |
||
9 | * http://www.php.net/license/3_0.txt. If you did not receive a copy of |
||
10 | * the PHP License and are unable to obtain it through the web, please |
||
11 | * send a note to license@php.net so we can mail you a copy immediately. |
||
12 | * |
||
13 | * @category pear |
||
14 | * @package PEAR |
||
15 | * @author Stig Bakken <ssb@php.net> |
||
16 | * @author Greg Beaver <cellog@php.net> |
||
17 | * @copyright 1997-2008 The PHP Group |
||
18 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
19 | * @version CVS: $Id: Remote.php,v 1.80 2008/01/03 20:26:36 cellog Exp $ |
||
20 | * @link http://pear.php.net/package/PEAR |
||
21 | * @since File available since Release 0.1 |
||
22 | */ |
||
23 | |||
24 | /** |
||
25 | * needed for PEAR_Error |
||
26 | */ |
||
27 | require_once 'PEAR.php'; |
||
28 | require_once 'PEAR/Config.php'; |
||
29 | |||
30 | /** |
||
31 | * This is a class for doing remote operations against the central |
||
32 | * PEAR database. |
||
33 | * |
||
34 | * @nodep XML_RPC_Value |
||
35 | * @nodep XML_RPC_Message |
||
36 | * @nodep XML_RPC_Client |
||
37 | * @category pear |
||
38 | * @package PEAR |
||
39 | * @author Stig Bakken <ssb@php.net> |
||
40 | * @author Greg Beaver <cellog@php.net> |
||
41 | * @copyright 1997-2008 The PHP Group |
||
42 | * @license http://www.php.net/license/3_0.txt PHP License 3.0 |
||
43 | * @version Release: 1.7.2 |
||
44 | * @link http://pear.php.net/package/PEAR |
||
45 | * @since Class available since Release 0.1 |
||
46 | */ |
||
47 | class PEAR_Remote extends PEAR |
||
48 | { |
||
49 | // {{{ properties |
||
50 | |||
51 | var $config = null; |
||
52 | var $cache = null; |
||
53 | /** |
||
54 | * @var PEAR_Registry |
||
55 | * @access private |
||
56 | */ |
||
57 | var $_registry; |
||
58 | |||
59 | // }}} |
||
60 | |||
61 | // {{{ PEAR_Remote(config_object) |
||
62 | |||
63 | function PEAR_Remote(&$config) |
||
64 | { |
||
65 | $this->PEAR(); |
||
66 | $this->config = &$config; |
||
67 | $this->_registry = &$this->config->getRegistry(); |
||
68 | } |
||
69 | |||
70 | // }}} |
||
71 | // {{{ setRegistry() |
||
72 | |||
73 | function setRegistry(&$reg) |
||
74 | { |
||
75 | $this->_registry = &$reg; |
||
76 | } |
||
77 | // }}} |
||
78 | // {{{ getCache() |
||
79 | |||
80 | |||
81 | function getCache($args) |
||
82 | { |
||
83 | $id = md5(serialize($args)); |
||
84 | $cachedir = $this->config->get('cache_dir'); |
||
85 | $filename = $cachedir . DIRECTORY_SEPARATOR . 'xmlrpc_cache_' . $id; |
||
86 | if (!file_exists($filename)) { |
||
87 | return null; |
||
88 | } |
||
89 | |||
90 | $fp = fopen($filename, 'rb'); |
||
91 | if (!$fp) { |
||
92 | return null; |
||
93 | } |
||
94 | fclose($fp); |
||
95 | $content = file_get_contents($filename); |
||
96 | $result = array( |
||
97 | 'age' => time() - filemtime($filename), |
||
98 | 'lastChange' => filemtime($filename), |
||
99 | 'content' => unserialize($content), |
||
100 | ); |
||
101 | return $result; |
||
102 | } |
||
103 | |||
104 | // }}} |
||
105 | |||
106 | // {{{ saveCache() |
||
107 | |||
108 | function saveCache($args, $data) |
||
109 | { |
||
110 | $id = md5(serialize($args)); |
||
111 | $cachedir = $this->config->get('cache_dir'); |
||
112 | if (!file_exists($cachedir)) { |
||
113 | System::mkdir(array('-p', $cachedir)); |
||
114 | } |
||
115 | $filename = $cachedir.'/xmlrpc_cache_'.$id; |
||
116 | |||
117 | $fp = @fopen($filename, "wb"); |
||
118 | if ($fp) { |
||
119 | fwrite($fp, serialize($data)); |
||
120 | fclose($fp); |
||
121 | } |
||
122 | } |
||
123 | |||
124 | // }}} |
||
125 | |||
126 | // {{{ clearCache() |
||
127 | |||
128 | function clearCache($method, $args) |
||
129 | { |
||
130 | array_unshift($args, $method); |
||
131 | array_unshift($args, $this->config->get('default_channel')); // cache by channel |
||
132 | $id = md5(serialize($args)); |
||
133 | $cachedir = $this->config->get('cache_dir'); |
||
134 | $filename = $cachedir.'/xmlrpc_cache_'.$id; |
||
135 | if (file_exists($filename)) { |
||
136 | @unlink($filename); |
||
137 | } |
||
138 | } |
||
139 | |||
140 | // }}} |
||
141 | // {{{ call(method, [args...]) |
||
142 | |||
143 | function call($method) |
||
144 | { |
||
145 | $_args = $args = func_get_args(); |
||
146 | |||
147 | $server_channel = $this->config->get('default_channel'); |
||
148 | $channel = $this->_registry->getChannel($server_channel); |
||
149 | if (!PEAR::isError($channel)) { |
||
150 | $mirror = $this->config->get('preferred_mirror'); |
||
151 | if ($channel->getMirror($mirror)) { |
||
152 | if ($channel->supports('xmlrpc', $method, $mirror)) { |
||
153 | $server_channel = $server_host = $mirror; // use the preferred mirror |
||
154 | $server_port = $channel->getPort($mirror); |
||
155 | } elseif (!$channel->supports('xmlrpc', $method)) { |
||
156 | return $this->raiseError("Channel $server_channel does not " . |
||
157 | "support xml-rpc method $method"); |
||
158 | } |
||
159 | } |
||
160 | if (!isset($server_host)) { |
||
161 | if (!$channel->supports('xmlrpc', $method)) { |
||
162 | return $this->raiseError("Channel $server_channel does not support " . |
||
163 | "xml-rpc method $method"); |
||
164 | } else { |
||
165 | $server_host = $server_channel; |
||
166 | $server_port = $channel->getPort(); |
||
167 | } |
||
168 | } |
||
169 | } else { |
||
170 | return $this->raiseError("Unknown channel '$server_channel'"); |
||
171 | } |
||
172 | |||
173 | array_unshift($_args, $server_channel); // cache by channel |
||
174 | $this->cache = $this->getCache($_args); |
||
175 | $cachettl = $this->config->get('cache_ttl'); |
||
176 | // If cache is newer than $cachettl seconds, we use the cache! |
||
177 | if ($this->cache !== null && $this->cache['age'] < $cachettl) { |
||
178 | return $this->cache['content']; |
||
179 | } |
||
180 | $fp = false; |
||
181 | if (extension_loaded("xmlrpc")) { |
||
182 | $result = call_user_func_array(array(&$this, 'call_epi'), $args); |
||
183 | if (!PEAR::isError($result)) { |
||
184 | $this->saveCache($_args, $result); |
||
185 | } |
||
186 | return $result; |
||
187 | } elseif (!($fp = fopen('XML/RPC.php', 'r', true))) { |
||
188 | return $this->raiseError("For this remote PEAR operation you need to load the xmlrpc extension or install XML_RPC"); |
||
189 | } |
||
190 | include_once 'XML/RPC.php'; |
||
191 | if ($fp) { |
||
192 | fclose($fp); |
||
193 | } |
||
194 | |||
195 | array_shift($args); |
||
196 | $username = $this->config->get('username'); |
||
197 | $password = $this->config->get('password'); |
||
198 | $eargs = array(); |
||
199 | foreach($args as $arg) { |
||
200 | $eargs[] = $this->_encode($arg); |
||
201 | } |
||
202 | $f = new XML_RPC_Message($method, $eargs); |
||
203 | if ($this->cache !== null) { |
||
204 | $maxAge = '?maxAge='.$this->cache['lastChange']; |
||
205 | } else { |
||
206 | $maxAge = ''; |
||
207 | } |
||
208 | $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; |
||
209 | if ($proxy = parse_url($this->config->get('http_proxy'))) { |
||
210 | $proxy_host = isset($proxy['host']) ? $proxy['host'] : null; |
||
211 | if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') { |
||
212 | $proxy_host = 'https://' . $proxy_host; |
||
213 | } |
||
214 | $proxy_port = isset($proxy['port']) ? $proxy['port'] : 8080; |
||
215 | $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; |
||
216 | $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; |
||
217 | } |
||
218 | $shost = $server_host; |
||
219 | if ($channel->getSSL()) { |
||
220 | $shost = "https://$shost"; |
||
221 | } |
||
222 | $c = new XML_RPC_Client('/' . $channel->getPath('xmlrpc') |
||
223 | . $maxAge, $shost, $server_port, $proxy_host, $proxy_port, |
||
224 | $proxy_user, $proxy_pass); |
||
225 | if ($username && $password) { |
||
226 | $c->setCredentials($username, $password); |
||
227 | } |
||
228 | if ($this->config->get('verbose') >= 3) { |
||
229 | $c->setDebug(1); |
||
230 | } |
||
231 | $r = $c->send($f); |
||
232 | if (!$r) { |
||
233 | return $this->raiseError("XML_RPC send failed"); |
||
234 | } |
||
235 | $v = $r->value(); |
||
236 | if ($e = $r->faultCode()) { |
||
237 | if ($e == $GLOBALS['XML_RPC_err']['http_error'] && strstr($r->faultString(), '304 Not Modified') !== false) { |
||
238 | return $this->cache['content']; |
||
239 | } |
||
240 | return $this->raiseError($r->faultString(), $e); |
||
241 | } |
||
242 | |||
243 | $result = XML_RPC_decode($v); |
||
244 | $this->saveCache($_args, $result); |
||
245 | return $result; |
||
246 | } |
||
247 | |||
248 | // }}} |
||
249 | |||
250 | // {{{ call_epi(method, [args...]) |
||
251 | |||
252 | function call_epi($method) |
||
253 | { |
||
254 | if (!extension_loaded("xmlrpc")) { |
||
255 | return $this->raiseError("xmlrpc extension is not loaded"); |
||
256 | } |
||
257 | $server_channel = $this->config->get('default_channel'); |
||
258 | $channel = $this->_registry->getChannel($server_channel); |
||
259 | if (!PEAR::isError($channel)) { |
||
260 | $mirror = $this->config->get('preferred_mirror'); |
||
261 | if ($channel->getMirror($mirror)) { |
||
262 | if ($channel->supports('xmlrpc', $method, $mirror)) { |
||
263 | $server_channel = $server_host = $mirror; // use the preferred mirror |
||
264 | $server_port = $channel->getPort($mirror); |
||
265 | } elseif (!$channel->supports('xmlrpc', $method)) { |
||
266 | return $this->raiseError("Channel $server_channel does not " . |
||
267 | "support xml-rpc method $method"); |
||
268 | } |
||
269 | } |
||
270 | if (!isset($server_host)) { |
||
271 | if (!$channel->supports('xmlrpc', $method)) { |
||
272 | return $this->raiseError("Channel $server_channel does not support " . |
||
273 | "xml-rpc method $method"); |
||
274 | } else { |
||
275 | $server_host = $server_channel; |
||
276 | $server_port = $channel->getPort(); |
||
277 | } |
||
278 | } |
||
279 | } else { |
||
280 | return $this->raiseError("Unknown channel '$server_channel'"); |
||
281 | } |
||
282 | $params = func_get_args(); |
||
283 | array_shift($params); |
||
284 | $method = str_replace("_", ".", $method); |
||
285 | $request = xmlrpc_encode_request($method, $params); |
||
286 | if ($http_proxy = $this->config->get('http_proxy')) { |
||
287 | $proxy = parse_url($http_proxy); |
||
288 | $proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; |
||
289 | $proxy_host = isset($proxy['host']) ? $proxy['host'] : null; |
||
290 | if (isset($proxy['scheme']) && $proxy['scheme'] == 'https') { |
||
291 | $proxy_host = 'https://' . $proxy_host; |
||
292 | } |
||
293 | $proxy_port = isset($proxy['port']) ? $proxy['port'] : null; |
||
294 | $proxy_user = isset($proxy['user']) ? urldecode($proxy['user']) : null; |
||
295 | $proxy_pass = isset($proxy['pass']) ? urldecode($proxy['pass']) : null; |
||
296 | $fp = @fsockopen($proxy_host, $proxy_port); |
||
297 | $use_proxy = true; |
||
298 | if ($channel->getSSL()) { |
||
299 | $server_host = "https://$server_host"; |
||
300 | } |
||
301 | } else { |
||
302 | $use_proxy = false; |
||
303 | $ssl = $channel->getSSL(); |
||
304 | $fp = @fsockopen(($ssl ? 'ssl://' : '') . $server_host, $server_port); |
||
305 | if (!$fp) { |
||
306 | $server_host = "$ssl$server_host"; // for error-reporting |
||
307 | } |
||
308 | } |
||
309 | if (!$fp && $http_proxy) { |
||
310 | return $this->raiseError("PEAR_Remote::call: fsockopen(`$proxy_host', $proxy_port) failed"); |
||
311 | } elseif (!$fp) { |
||
312 | return $this->raiseError("PEAR_Remote::call: fsockopen(`$server_host', $server_port) failed"); |
||
313 | } |
||
314 | $len = strlen($request); |
||
315 | $req_headers = "Host: $server_host:$server_port\r\n" . |
||
316 | "Content-type: text/xml\r\n" . |
||
317 | "Content-length: $len\r\n"; |
||
318 | $username = $this->config->get('username'); |
||
319 | $password = $this->config->get('password'); |
||
320 | if ($username && $password) { |
||
321 | $req_headers .= "Cookie: PEAR_USER=$username; PEAR_PW=$password\r\n"; |
||
322 | $tmp = base64_encode("$username:$password"); |
||
323 | $req_headers .= "Authorization: Basic $tmp\r\n"; |
||
324 | } |
||
325 | if ($this->cache !== null) { |
||
326 | $maxAge = '?maxAge='.$this->cache['lastChange']; |
||
327 | } else { |
||
328 | $maxAge = ''; |
||
329 | } |
||
330 | |||
331 | if ($use_proxy && $proxy_host != '' && $proxy_user != '') { |
||
332 | $req_headers .= 'Proxy-Authorization: Basic ' |
||
333 | .base64_encode($proxy_user.':'.$proxy_pass) |
||
334 | ."\r\n"; |
||
335 | } |
||
336 | |||
337 | if ($this->config->get('verbose') > 3) { |
||
338 | print "XMLRPC REQUEST HEADERS:\n"; |
||
339 | var_dump($req_headers); |
||
340 | print "XMLRPC REQUEST BODY:\n"; |
||
341 | var_dump($request); |
||
342 | } |
||
343 | |||
344 | if ($use_proxy && $proxy_host != '') { |
||
345 | $post_string = "POST http://".$server_host; |
||
346 | if ($proxy_port > '') { |
||
347 | $post_string .= ':'.$server_port; |
||
348 | } |
||
349 | } else { |
||
350 | $post_string = "POST "; |
||
351 | } |
||
352 | |||
353 | $path = '/' . $channel->getPath('xmlrpc'); |
||
354 | fwrite($fp, ($post_string . $path . "$maxAge HTTP/1.0\r\n$req_headers\r\n$request")); |
||
355 | $response = ''; |
||
356 | $line1 = fgets($fp, 2048); |
||
357 | if (!preg_match('!^HTTP/[0-9\.]+ (\d+) (.*)!', $line1, $matches)) { |
||
358 | return $this->raiseError("PEAR_Remote: invalid HTTP response from XML-RPC server"); |
||
359 | } |
||
360 | switch ($matches[1]) { |
||
361 | case "200": // OK |
||
362 | break; |
||
363 | case "304": // Not Modified |
||
364 | return $this->cache['content']; |
||
365 | case "401": // Unauthorized |
||
366 | if ($username && $password) { |
||
367 | return $this->raiseError("PEAR_Remote ($server_host:$server_port) " . |
||
368 | ": authorization failed", 401); |
||
369 | } else { |
||
370 | return $this->raiseError("PEAR_Remote ($server_host:$server_port) " . |
||
371 | ": authorization required, please log in first", 401); |
||
372 | } |
||
373 | default: |
||
374 | return $this->raiseError("PEAR_Remote ($server_host:$server_port) : " . |
||
375 | "unexpected HTTP response", (int)$matches[1], null, null, |
||
376 | "$matches[1] $matches[2]"); |
||
377 | } |
||
378 | while (trim(fgets($fp, 2048)) != ''); // skip rest of headers |
||
379 | while ($chunk = fread($fp, 10240)) { |
||
380 | $response .= $chunk; |
||
381 | } |
||
382 | fclose($fp); |
||
383 | if ($this->config->get('verbose') > 3) { |
||
384 | print "XMLRPC RESPONSE:\n"; |
||
385 | var_dump($response); |
||
386 | } |
||
387 | $ret = xmlrpc_decode($response); |
||
388 | if (is_array($ret) && isset($ret['__PEAR_TYPE__'])) { |
||
389 | if ($ret['__PEAR_TYPE__'] == 'error') { |
||
390 | if (isset($ret['__PEAR_CLASS__'])) { |
||
391 | $class = $ret['__PEAR_CLASS__']; |
||
392 | } else { |
||
393 | $class = "PEAR_Error"; |
||
394 | } |
||
395 | if ($ret['code'] === '') $ret['code'] = null; |
||
396 | if ($ret['message'] === '') $ret['message'] = null; |
||
397 | if ($ret['userinfo'] === '') $ret['userinfo'] = null; |
||
398 | if (strtolower($class) == 'db_error') { |
||
399 | $ret = $this->raiseError(PEAR::errorMessage($ret['code']), |
||
400 | $ret['code'], null, null, |
||
401 | $ret['userinfo']); |
||
402 | } else { |
||
403 | $ret = $this->raiseError($ret['message'], $ret['code'], |
||
404 | null, null, $ret['userinfo']); |
||
405 | } |
||
406 | } |
||
407 | } elseif (is_array($ret) && sizeof($ret) == 1 && isset($ret[0]) |
||
408 | && is_array($ret[0]) && |
||
409 | !empty($ret[0]['faultString']) && |
||
410 | !empty($ret[0]['faultCode'])) { |
||
411 | extract($ret[0]); |
||
412 | $faultString = "XML-RPC Server Fault: " . |
||
413 | str_replace("\n", " ", $faultString); |
||
414 | return $this->raiseError($faultString, $faultCode); |
||
415 | } elseif (is_array($ret) && sizeof($ret) == 2 && !empty($ret['faultString']) && |
||
416 | !empty($ret['faultCode'])) { |
||
417 | extract($ret); |
||
418 | $faultString = "XML-RPC Server Fault: " . |
||
419 | str_replace("\n", " ", $faultString); |
||
420 | return $this->raiseError($faultString, $faultCode); |
||
421 | } |
||
422 | return $ret; |
||
423 | } |
||
424 | |||
425 | // }}} |
||
426 | |||
427 | // {{{ _encode |
||
428 | |||
429 | // a slightly extended version of XML_RPC_encode |
||
430 | function _encode($php_val) |
||
431 | { |
||
432 | global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double; |
||
433 | global $XML_RPC_String, $XML_RPC_Array, $XML_RPC_Struct; |
||
434 | |||
435 | $type = gettype($php_val); |
||
436 | $xmlrpcval = new XML_RPC_Value; |
||
437 | |||
438 | switch($type) { |
||
439 | case "array": |
||
440 | reset($php_val); |
||
441 | $firstkey = key($php_val); |
||
442 | end($php_val); |
||
443 | $lastkey = key($php_val); |
||
444 | reset($php_val); |
||
445 | if ($firstkey === 0 && is_int($lastkey) && |
||
446 | ($lastkey + 1) == count($php_val)) { |
||
447 | $is_continuous = true; |
||
448 | reset($php_val); |
||
449 | $size = count($php_val); |
||
450 | for ($expect = 0; $expect < $size; $expect++, next($php_val)) { |
||
451 | if (key($php_val) !== $expect) { |
||
452 | $is_continuous = false; |
||
453 | break; |
||
454 | } |
||
455 | } |
||
456 | if ($is_continuous) { |
||
457 | reset($php_val); |
||
458 | $arr = array(); |
||
459 | while (list($k, $v) = each($php_val)) { |
||
460 | $arr[$k] = $this->_encode($v); |
||
461 | } |
||
462 | $xmlrpcval->addArray($arr); |
||
463 | break; |
||
464 | } |
||
465 | } |
||
466 | // fall though if not numerical and continuous |
||
467 | case "object": |
||
468 | $arr = array(); |
||
469 | while (list($k, $v) = each($php_val)) { |
||
470 | $arr[$k] = $this->_encode($v); |
||
471 | } |
||
472 | $xmlrpcval->addStruct($arr); |
||
473 | break; |
||
474 | case "integer": |
||
475 | $xmlrpcval->addScalar($php_val, $XML_RPC_Int); |
||
476 | break; |
||
477 | case "double": |
||
478 | $xmlrpcval->addScalar($php_val, $XML_RPC_Double); |
||
479 | break; |
||
480 | case "string": |
||
481 | case "NULL": |
||
482 | $xmlrpcval->addScalar($php_val, $XML_RPC_String); |
||
483 | break; |
||
484 | case "boolean": |
||
485 | $xmlrpcval->addScalar($php_val, $XML_RPC_Boolean); |
||
486 | break; |
||
487 | case "unknown type": |
||
488 | default: |
||
489 | return null; |
||
490 | } |
||
491 | return $xmlrpcval; |
||
492 | } |
||
493 | |||
494 | // }}} |
||
495 | |||
496 | } |
||
497 | |||
498 | ?> |