Spam protection, AntiSpam, FireWall by CleanTalk - Version 2.4.12

Version Description

2013-08-12 = * Removed RPC::XML library from plugin. * Switched plugin to HTTP+JSON connection with servers. * Fixed bug with comments antispam tests with non UTF8 codepage.

Download this release

Release Info

Developer shagimuratov
Plugin Icon 128x128 Spam protection, AntiSpam, FireWall by CleanTalk
Version 2.4.12
Comparing to
See all releases

Code changes from version 2.4.11 to 2.4.12

Files changed (6) hide show
  1. JSON.php +806 -0
  2. cleantalk.class.php +868 -830
  3. cleantalk.php +4 -4
  4. cleantalk.xmlrpc.php +0 -3777
  5. readme.txt +16 -3
  6. screenshot-4.png +0 -0
JSON.php ADDED
@@ -0,0 +1,806 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
3
+
4
+ /**
5
+ * Converts to and from JSON format.
6
+ *
7
+ * JSON (JavaScript Object Notation) is a lightweight data-interchange
8
+ * format. It is easy for humans to read and write. It is easy for machines
9
+ * to parse and generate. It is based on a subset of the JavaScript
10
+ * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
11
+ * This feature can also be found in Python. JSON is a text format that is
12
+ * completely language independent but uses conventions that are familiar
13
+ * to programmers of the C-family of languages, including C, C++, C#, Java,
14
+ * JavaScript, Perl, TCL, and many others. These properties make JSON an
15
+ * ideal data-interchange language.
16
+ *
17
+ * This package provides a simple encoder and decoder for JSON notation. It
18
+ * is intended for use with client-side Javascript applications that make
19
+ * use of HTTPRequest to perform server communication functions - data can
20
+ * be encoded into JSON notation for use in a client-side javascript, or
21
+ * decoded from incoming Javascript requests. JSON format is native to
22
+ * Javascript, and can be directly eval()'ed with no further parsing
23
+ * overhead
24
+ *
25
+ * All strings should be in ASCII or UTF-8 format!
26
+ *
27
+ * LICENSE: Redistribution and use in source and binary forms, with or
28
+ * without modification, are permitted provided that the following
29
+ * conditions are met: Redistributions of source code must retain the
30
+ * above copyright notice, this list of conditions and the following
31
+ * disclaimer. Redistributions in binary form must reproduce the above
32
+ * copyright notice, this list of conditions and the following disclaimer
33
+ * in the documentation and/or other materials provided with the
34
+ * distribution.
35
+ *
36
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
37
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
38
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
39
+ * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
40
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
41
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
42
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
44
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
45
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
46
+ * DAMAGE.
47
+ *
48
+ * @category
49
+ * @package Services_JSON
50
+ * @author Michal Migurski <mike-json@teczno.com>
51
+ * @author Matt Knapp <mdknapp[at]gmail[dot]com>
52
+ * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
53
+ * @copyright 2005 Michal Migurski
54
+ * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $
55
+ * @license http://www.opensource.org/licenses/bsd-license.php
56
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
57
+ */
58
+
59
+ /**
60
+ * Marker constant for Services_JSON::decode(), used to flag stack state
61
+ */
62
+ define('SERVICES_JSON_SLICE', 1);
63
+
64
+ /**
65
+ * Marker constant for Services_JSON::decode(), used to flag stack state
66
+ */
67
+ define('SERVICES_JSON_IN_STR', 2);
68
+
69
+ /**
70
+ * Marker constant for Services_JSON::decode(), used to flag stack state
71
+ */
72
+ define('SERVICES_JSON_IN_ARR', 3);
73
+
74
+ /**
75
+ * Marker constant for Services_JSON::decode(), used to flag stack state
76
+ */
77
+ define('SERVICES_JSON_IN_OBJ', 4);
78
+
79
+ /**
80
+ * Marker constant for Services_JSON::decode(), used to flag stack state
81
+ */
82
+ define('SERVICES_JSON_IN_CMT', 5);
83
+
84
+ /**
85
+ * Behavior switch for Services_JSON::decode()
86
+ */
87
+ define('SERVICES_JSON_LOOSE_TYPE', 16);
88
+
89
+ /**
90
+ * Behavior switch for Services_JSON::decode()
91
+ */
92
+ define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
93
+
94
+ /**
95
+ * Converts to and from JSON format.
96
+ *
97
+ * Brief example of use:
98
+ *
99
+ * <code>
100
+ * // create a new instance of Services_JSON
101
+ * $json = new Services_JSON();
102
+ *
103
+ * // convert a complexe value to JSON notation, and send it to the browser
104
+ * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
105
+ * $output = $json->encode($value);
106
+ *
107
+ * print($output);
108
+ * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
109
+ *
110
+ * // accept incoming POST data, assumed to be in JSON notation
111
+ * $input = file_get_contents('php://input', 1000000);
112
+ * $value = $json->decode($input);
113
+ * </code>
114
+ */
115
+ class Services_JSON
116
+ {
117
+ /**
118
+ * constructs a new JSON instance
119
+ *
120
+ * @param int $use object behavior flags; combine with boolean-OR
121
+ *
122
+ * possible values:
123
+ * - SERVICES_JSON_LOOSE_TYPE: loose typing.
124
+ * "{...}" syntax creates associative arrays
125
+ * instead of objects in decode().
126
+ * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
127
+ * Values which can't be encoded (e.g. resources)
128
+ * appear as NULL instead of throwing errors.
129
+ * By default, a deeply-nested resource will
130
+ * bubble up with an error, so all return values
131
+ * from encode() should be checked with isError()
132
+ */
133
+ function Services_JSON($use = 0)
134
+ {
135
+ $this->use = $use;
136
+ }
137
+
138
+ /**
139
+ * convert a string from one UTF-16 char to one UTF-8 char
140
+ *
141
+ * Normally should be handled by mb_convert_encoding, but
142
+ * provides a slower PHP-only method for installations
143
+ * that lack the multibye string extension.
144
+ *
145
+ * @param string $utf16 UTF-16 character
146
+ * @return string UTF-8 character
147
+ * @access private
148
+ */
149
+ function utf162utf8($utf16)
150
+ {
151
+ // oh please oh please oh please oh please oh please
152
+ if(function_exists('mb_convert_encoding')) {
153
+ return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
154
+ }
155
+
156
+ $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
157
+
158
+ switch(true) {
159
+ case ((0x7F & $bytes) == $bytes):
160
+ // this case should never be reached, because we are in ASCII range
161
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
162
+ return chr(0x7F & $bytes);
163
+
164
+ case (0x07FF & $bytes) == $bytes:
165
+ // return a 2-byte UTF-8 character
166
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
167
+ return chr(0xC0 | (($bytes >> 6) & 0x1F))
168
+ . chr(0x80 | ($bytes & 0x3F));
169
+
170
+ case (0xFFFF & $bytes) == $bytes:
171
+ // return a 3-byte UTF-8 character
172
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
173
+ return chr(0xE0 | (($bytes >> 12) & 0x0F))
174
+ . chr(0x80 | (($bytes >> 6) & 0x3F))
175
+ . chr(0x80 | ($bytes & 0x3F));
176
+ }
177
+
178
+ // ignoring UTF-32 for now, sorry
179
+ return '';
180
+ }
181
+
182
+ /**
183
+ * convert a string from one UTF-8 char to one UTF-16 char
184
+ *
185
+ * Normally should be handled by mb_convert_encoding, but
186
+ * provides a slower PHP-only method for installations
187
+ * that lack the multibye string extension.
188
+ *
189
+ * @param string $utf8 UTF-8 character
190
+ * @return string UTF-16 character
191
+ * @access private
192
+ */
193
+ function utf82utf16($utf8)
194
+ {
195
+ // oh please oh please oh please oh please oh please
196
+ if(function_exists('mb_convert_encoding')) {
197
+ return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
198
+ }
199
+
200
+ switch(strlen($utf8)) {
201
+ case 1:
202
+ // this case should never be reached, because we are in ASCII range
203
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
204
+ return $utf8;
205
+
206
+ case 2:
207
+ // return a UTF-16 character from a 2-byte UTF-8 char
208
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
209
+ return chr(0x07 & (ord($utf8{0}) >> 2))
210
+ . chr((0xC0 & (ord($utf8{0}) << 6))
211
+ | (0x3F & ord($utf8{1})));
212
+
213
+ case 3:
214
+ // return a UTF-16 character from a 3-byte UTF-8 char
215
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
216
+ return chr((0xF0 & (ord($utf8{0}) << 4))
217
+ | (0x0F & (ord($utf8{1}) >> 2)))
218
+ . chr((0xC0 & (ord($utf8{1}) << 6))
219
+ | (0x7F & ord($utf8{2})));
220
+ }
221
+
222
+ // ignoring UTF-32 for now, sorry
223
+ return '';
224
+ }
225
+
226
+ /**
227
+ * encodes an arbitrary variable into JSON format
228
+ *
229
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
230
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
231
+ * if var is a strng, note that encode() always expects it
232
+ * to be in ASCII or UTF-8 format!
233
+ *
234
+ * @return mixed JSON string representation of input var or an error if a problem occurs
235
+ * @access public
236
+ */
237
+ function encode($var)
238
+ {
239
+ switch (gettype($var)) {
240
+ case 'boolean':
241
+ return $var ? 'true' : 'false';
242
+
243
+ case 'NULL':
244
+ return 'null';
245
+
246
+ case 'integer':
247
+ return (int) $var;
248
+
249
+ case 'double':
250
+ case 'float':
251
+ return (float) $var;
252
+
253
+ case 'string':
254
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
255
+ $ascii = '';
256
+ $strlen_var = strlen($var);
257
+
258
+ /*
259
+ * Iterate over every character in the string,
260
+ * escaping with a slash or encoding to UTF-8 where necessary
261
+ */
262
+ for ($c = 0; $c < $strlen_var; ++$c) {
263
+
264
+ $ord_var_c = ord($var{$c});
265
+
266
+ switch (true) {
267
+ case $ord_var_c == 0x08:
268
+ $ascii .= '\b';
269
+ break;
270
+ case $ord_var_c == 0x09:
271
+ $ascii .= '\t';
272
+ break;
273
+ case $ord_var_c == 0x0A:
274
+ $ascii .= '\n';
275
+ break;
276
+ case $ord_var_c == 0x0C:
277
+ $ascii .= '\f';
278
+ break;
279
+ case $ord_var_c == 0x0D:
280
+ $ascii .= '\r';
281
+ break;
282
+
283
+ case $ord_var_c == 0x22:
284
+ case $ord_var_c == 0x2F:
285
+ case $ord_var_c == 0x5C:
286
+ // double quote, slash, slosh
287
+ $ascii .= '\\'.$var{$c};
288
+ break;
289
+
290
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
291
+ // characters U-00000000 - U-0000007F (same as ASCII)
292
+ $ascii .= $var{$c};
293
+ break;
294
+
295
+ case (($ord_var_c & 0xE0) == 0xC0):
296
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
297
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
298
+ $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
299
+ $c += 1;
300
+ $utf16 = $this->utf82utf16($char);
301
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
302
+ break;
303
+
304
+ case (($ord_var_c & 0xF0) == 0xE0):
305
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
306
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
307
+ $char = pack('C*', $ord_var_c,
308
+ ord($var{$c + 1}),
309
+ ord($var{$c + 2}));
310
+ $c += 2;
311
+ $utf16 = $this->utf82utf16($char);
312
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
313
+ break;
314
+
315
+ case (($ord_var_c & 0xF8) == 0xF0):
316
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
317
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
318
+ $char = pack('C*', $ord_var_c,
319
+ ord($var{$c + 1}),
320
+ ord($var{$c + 2}),
321
+ ord($var{$c + 3}));
322
+ $c += 3;
323
+ $utf16 = $this->utf82utf16($char);
324
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
325
+ break;
326
+
327
+ case (($ord_var_c & 0xFC) == 0xF8):
328
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
329
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
330
+ $char = pack('C*', $ord_var_c,
331
+ ord($var{$c + 1}),
332
+ ord($var{$c + 2}),
333
+ ord($var{$c + 3}),
334
+ ord($var{$c + 4}));
335
+ $c += 4;
336
+ $utf16 = $this->utf82utf16($char);
337
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
338
+ break;
339
+
340
+ case (($ord_var_c & 0xFE) == 0xFC):
341
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
342
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
343
+ $char = pack('C*', $ord_var_c,
344
+ ord($var{$c + 1}),
345
+ ord($var{$c + 2}),
346
+ ord($var{$c + 3}),
347
+ ord($var{$c + 4}),
348
+ ord($var{$c + 5}));
349
+ $c += 5;
350
+ $utf16 = $this->utf82utf16($char);
351
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
352
+ break;
353
+ }
354
+ }
355
+
356
+ return '"'.$ascii.'"';
357
+
358
+ case 'array':
359
+ /*
360
+ * As per JSON spec if any array key is not an integer
361
+ * we must treat the the whole array as an object. We
362
+ * also try to catch a sparsely populated associative
363
+ * array with numeric keys here because some JS engines
364
+ * will create an array with empty indexes up to
365
+ * max_index which can cause memory issues and because
366
+ * the keys, which may be relevant, will be remapped
367
+ * otherwise.
368
+ *
369
+ * As per the ECMA and JSON specification an object may
370
+ * have any string as a property. Unfortunately due to
371
+ * a hole in the ECMA specification if the key is a
372
+ * ECMA reserved word or starts with a digit the
373
+ * parameter is only accessible using ECMAScript's
374
+ * bracket notation.
375
+ */
376
+
377
+ // treat as a JSON object
378
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
379
+ $properties = array_map(array($this, 'name_value'),
380
+ array_keys($var),
381
+ array_values($var));
382
+
383
+ foreach($properties as $property) {
384
+ if(Services_JSON::isError($property)) {
385
+ return $property;
386
+ }
387
+ }
388
+
389
+ return '{' . join(',', $properties) . '}';
390
+ }
391
+
392
+ // treat it like a regular array
393
+ $elements = array_map(array($this, 'encode'), $var);
394
+
395
+ foreach($elements as $element) {
396
+ if(Services_JSON::isError($element)) {
397
+ return $element;
398
+ }
399
+ }
400
+
401
+ return '[' . join(',', $elements) . ']';
402
+
403
+ case 'object':
404
+ $vars = get_object_vars($var);
405
+
406
+ $properties = array_map(array($this, 'name_value'),
407
+ array_keys($vars),
408
+ array_values($vars));
409
+
410
+ foreach($properties as $property) {
411
+ if(Services_JSON::isError($property)) {
412
+ return $property;
413
+ }
414
+ }
415
+
416
+ return '{' . join(',', $properties) . '}';
417
+
418
+ default:
419
+ return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
420
+ ? 'null'
421
+ : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
422
+ }
423
+ }
424
+
425
+ /**
426
+ * array-walking function for use in generating JSON-formatted name-value pairs
427
+ *
428
+ * @param string $name name of key to use
429
+ * @param mixed $value reference to an array element to be encoded
430
+ *
431
+ * @return string JSON-formatted name-value pair, like '"name":value'
432
+ * @access private
433
+ */
434
+ function name_value($name, $value)
435
+ {
436
+ $encoded_value = $this->encode($value);
437
+
438
+ if(Services_JSON::isError($encoded_value)) {
439
+ return $encoded_value;
440
+ }
441
+
442
+ return $this->encode(strval($name)) . ':' . $encoded_value;
443
+ }
444
+
445
+ /**
446
+ * reduce a string by removing leading and trailing comments and whitespace
447
+ *
448
+ * @param $str string string value to strip of comments and whitespace
449
+ *
450
+ * @return string string value stripped of comments and whitespace
451
+ * @access private
452
+ */
453
+ function reduce_string($str)
454
+ {
455
+ $str = preg_replace(array(
456
+
457
+ // eliminate single line comments in '// ...' form
458
+ '#^\s*//(.+)$#m',
459
+
460
+ // eliminate multi-line comments in '/* ... */' form, at start of string
461
+ '#^\s*/\*(.+)\*/#Us',
462
+
463
+ // eliminate multi-line comments in '/* ... */' form, at end of string
464
+ '#/\*(.+)\*/\s*$#Us'
465
+
466
+ ), '', $str);
467
+
468
+ // eliminate extraneous space
469
+ return trim($str);
470
+ }
471
+
472
+ /**
473
+ * decodes a JSON string into appropriate variable
474
+ *
475
+ * @param string $str JSON-formatted string
476
+ *
477
+ * @return mixed number, boolean, string, array, or object
478
+ * corresponding to given JSON input string.
479
+ * See argument 1 to Services_JSON() above for object-output behavior.
480
+ * Note that decode() always returns strings
481
+ * in ASCII or UTF-8 format!
482
+ * @access public
483
+ */
484
+ function decode($str)
485
+ {
486
+ $str = $this->reduce_string($str);
487
+
488
+ switch (strtolower($str)) {
489
+ case 'true':
490
+ return true;
491
+
492
+ case 'false':
493
+ return false;
494
+
495
+ case 'null':
496
+ return null;
497
+
498
+ default:
499
+ $m = array();
500
+
501
+ if (is_numeric($str)) {
502
+ // Lookie-loo, it's a number
503
+
504
+ // This would work on its own, but I'm trying to be
505
+ // good about returning integers where appropriate:
506
+ // return (float)$str;
507
+
508
+ // Return float or int, as appropriate
509
+ return ((float)$str == (integer)$str)
510
+ ? (integer)$str
511
+ : (float)$str;
512
+
513
+ } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
514
+ // STRINGS RETURNED IN UTF-8 FORMAT
515
+ $delim = substr($str, 0, 1);
516
+ $chrs = substr($str, 1, -1);
517
+ $utf8 = '';
518
+ $strlen_chrs = strlen($chrs);
519
+
520
+ for ($c = 0; $c < $strlen_chrs; ++$c) {
521
+
522
+ $substr_chrs_c_2 = substr($chrs, $c, 2);
523
+ $ord_chrs_c = ord($chrs{$c});
524
+
525
+ switch (true) {
526
+ case $substr_chrs_c_2 == '\b':
527
+ $utf8 .= chr(0x08);
528
+ ++$c;
529
+ break;
530
+ case $substr_chrs_c_2 == '\t':
531
+ $utf8 .= chr(0x09);
532
+ ++$c;
533
+ break;
534
+ case $substr_chrs_c_2 == '\n':
535
+ $utf8 .= chr(0x0A);
536
+ ++$c;
537
+ break;
538
+ case $substr_chrs_c_2 == '\f':
539
+ $utf8 .= chr(0x0C);
540
+ ++$c;
541
+ break;
542
+ case $substr_chrs_c_2 == '\r':
543
+ $utf8 .= chr(0x0D);
544
+ ++$c;
545
+ break;
546
+
547
+ case $substr_chrs_c_2 == '\\"':
548
+ case $substr_chrs_c_2 == '\\\'':
549
+ case $substr_chrs_c_2 == '\\\\':
550
+ case $substr_chrs_c_2 == '\\/':
551
+ if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
552
+ ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
553
+ $utf8 .= $chrs{++$c};
554
+ }
555
+ break;
556
+
557
+ case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
558
+ // single, escaped unicode character
559
+ $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
560
+ . chr(hexdec(substr($chrs, ($c + 4), 2)));
561
+ $utf8 .= $this->utf162utf8($utf16);
562
+ $c += 5;
563
+ break;
564
+
565
+ case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
566
+ $utf8 .= $chrs{$c};
567
+ break;
568
+
569
+ case ($ord_chrs_c & 0xE0) == 0xC0:
570
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
571
+ //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
572
+ $utf8 .= substr($chrs, $c, 2);
573
+ ++$c;
574
+ break;
575
+
576
+ case ($ord_chrs_c & 0xF0) == 0xE0:
577
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
578
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
579
+ $utf8 .= substr($chrs, $c, 3);
580
+ $c += 2;
581
+ break;
582
+
583
+ case ($ord_chrs_c & 0xF8) == 0xF0:
584
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
585
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
586
+ $utf8 .= substr($chrs, $c, 4);
587
+ $c += 3;
588
+ break;
589
+
590
+ case ($ord_chrs_c & 0xFC) == 0xF8:
591
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
592
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
593
+ $utf8 .= substr($chrs, $c, 5);
594
+ $c += 4;
595
+ break;
596
+
597
+ case ($ord_chrs_c & 0xFE) == 0xFC:
598
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
599
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
600
+ $utf8 .= substr($chrs, $c, 6);
601
+ $c += 5;
602
+ break;
603
+
604
+ }
605
+
606
+ }
607
+
608
+ return $utf8;
609
+
610
+ } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
611
+ // array, or object notation
612
+
613
+ if ($str{0} == '[') {
614
+ $stk = array(SERVICES_JSON_IN_ARR);
615
+ $arr = array();
616
+ } else {
617
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
618
+ $stk = array(SERVICES_JSON_IN_OBJ);
619
+ $obj = array();
620
+ } else {
621
+ $stk = array(SERVICES_JSON_IN_OBJ);
622
+ $obj = new stdClass();
623
+ }
624
+ }
625
+
626
+ array_push($stk, array('what' => SERVICES_JSON_SLICE,
627
+ 'where' => 0,
628
+ 'delim' => false));
629
+
630
+ $chrs = substr($str, 1, -1);
631
+ $chrs = $this->reduce_string($chrs);
632
+
633
+ if ($chrs == '') {
634
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
635
+ return $arr;
636
+
637
+ } else {
638
+ return $obj;
639
+
640
+ }
641
+ }
642
+
643
+ //print("\nparsing {$chrs}\n");
644
+
645
+ $strlen_chrs = strlen($chrs);
646
+
647
+ for ($c = 0; $c <= $strlen_chrs; ++$c) {
648
+
649
+ $top = end($stk);
650
+ $substr_chrs_c_2 = substr($chrs, $c, 2);
651
+
652
+ if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
653
+ // found a comma that is not inside a string, array, etc.,
654
+ // OR we've reached the end of the character list
655
+ $slice = substr($chrs, $top['where'], ($c - $top['where']));
656
+ array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
657
+ //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
658
+
659
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
660
+ // we are in an array, so just push an element onto the stack
661
+ array_push($arr, $this->decode($slice));
662
+
663
+ } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
664
+ // we are in an object, so figure
665
+ // out the property name and set an
666
+ // element in an associative array,
667
+ // for now
668
+ $parts = array();
669
+
670
+ if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
671
+ // "name":value pair
672
+ $key = $this->decode($parts[1]);
673
+ $val = $this->decode($parts[2]);
674
+
675
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
676
+ $obj[$key] = $val;
677
+ } else {
678
+ $obj->$key = $val;
679
+ }
680
+ } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
681
+ // name:value pair, where name is unquoted
682
+ $key = $parts[1];
683
+ $val = $this->decode($parts[2]);
684
+
685
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
686
+ $obj[$key] = $val;
687
+ } else {
688
+ $obj->$key = $val;
689
+ }
690
+ }
691
+
692
+ }
693
+
694
+ } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
695
+ // found a quote, and we are not inside a string
696
+ array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
697
+ //print("Found start of string at {$c}\n");
698
+
699
+ } elseif (($chrs{$c} == $top['delim']) &&
700
+ ($top['what'] == SERVICES_JSON_IN_STR) &&
701
+ ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
702
+ // found a quote, we're in a string, and it's not escaped
703
+ // we know that it's not escaped becase there is _not_ an
704
+ // odd number of backslashes at the end of the string so far
705
+ array_pop($stk);
706
+ //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
707
+
708
+ } elseif (($chrs{$c} == '[') &&
709
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
710
+ // found a left-bracket, and we are in an array, object, or slice
711
+ array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
712
+ //print("Found start of array at {$c}\n");
713
+
714
+ } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
715
+ // found a right-bracket, and we're in an array
716
+ array_pop($stk);
717
+ //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
718
+
719
+ } elseif (($chrs{$c} == '{') &&
720
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
721
+ // found a left-brace, and we are in an array, object, or slice
722
+ array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
723
+ //print("Found start of object at {$c}\n");
724
+
725
+ } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
726
+ // found a right-brace, and we're in an object
727
+ array_pop($stk);
728
+ //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
729
+
730
+ } elseif (($substr_chrs_c_2 == '/*') &&
731
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
732
+ // found a comment start, and we are in an array, object, or slice
733
+ array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
734
+ $c++;
735
+ //print("Found start of comment at {$c}\n");
736
+
737
+ } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
738
+ // found a comment end, and we're in one now
739
+ array_pop($stk);
740
+ $c++;
741
+
742
+ for ($i = $top['where']; $i <= $c; ++$i)
743
+ $chrs = substr_replace($chrs, ' ', $i, 1);
744
+
745
+ //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
746
+
747
+ }
748
+
749
+ }
750
+
751
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
752
+ return $arr;
753
+
754
+ } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
755
+ return $obj;
756
+
757
+ }
758
+
759
+ }
760
+ }
761
+ }
762
+
763
+ /**
764
+ * @todo Ultimately, this should just call PEAR::isError()
765
+ */
766
+ function isError($data, $code = null)
767
+ {
768
+ if (class_exists('pear')) {
769
+ return PEAR::isError($data, $code);
770
+ } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
771
+ is_subclass_of($data, 'services_json_error'))) {
772
+ return true;
773
+ }
774
+
775
+ return false;
776
+ }
777
+ }
778
+
779
+ if (class_exists('PEAR_Error')) {
780
+
781
+ class Services_JSON_Error extends PEAR_Error
782
+ {
783
+ function Services_JSON_Error($message = 'unknown error', $code = null,
784
+ $mode = null, $options = null, $userinfo = null)
785
+ {
786
+ parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
787
+ }
788
+ }
789
+
790
+ } else {
791
+
792
+ /**
793
+ * @todo Ultimately, this class shall be descended from PEAR_Error
794
+ */
795
+ class Services_JSON_Error
796
+ {
797
+ function Services_JSON_Error($message = 'unknown error', $code = null,
798
+ $mode = null, $options = null, $userinfo = null)
799
+ {
800
+
801
+ }
802
+ }
803
+
804
+ }
805
+
806
+ ?>
cleantalk.class.php CHANGED
@@ -1,830 +1,868 @@
1
- <?php
2
- /**
3
- * Cleantalk base class
4
- *
5
- * @version 0.20.3
6
- * @package Cleantalk
7
- * @subpackage Base
8
- * @author Сleantalk team (welcome@cleantalk.ru)
9
- * @copyright (C) 2013 СleanTalk team (http://cleantalk.org)
10
- * @license GNU/GPL: http://www.gnu.org/copyleft/gpl.html
11
- * @see http://cleantalk.ru/wiki/doku.php/api
12
- *
13
- */
14
-
15
- /**
16
- * @ignore
17
- */
18
- require_once(dirname(__FILE__) . '/cleantalk.xmlrpc.php');
19
-
20
- /**
21
- * Response class
22
- */
23
- class CleantalkResponse {
24
-
25
- /**
26
- * Is stop words
27
- * @var int
28
- */
29
- public $stop_words = null;
30
-
31
- /**
32
- * Cleantalk comment
33
- * @var string
34
- */
35
- public $comment = null;
36
-
37
- /**
38
- * Is blacklisted
39
- * @var int
40
- */
41
- public $blacklisted = null;
42
-
43
- /**
44
- * Is allow, 1|0
45
- * @var int
46
- */
47
- public $allow = null;
48
-
49
- /**
50
- * Request ID
51
- * @var int
52
- */
53
- public $id = null;
54
-
55
- /**
56
- * Request errno
57
- * @var int
58
- */
59
- public $errno = null;
60
-
61
- /**
62
- * Error string
63
- * @var string
64
- */
65
- public $errstr = null;
66
-
67
- /**
68
- * Is fast submit, 1|0
69
- * @var string
70
- */
71
- public $fast_submit = null;
72
-
73
- /**
74
- * Is spam comment
75
- * @var string
76
- */
77
- public $spam = null;
78
-
79
- /**
80
- * Is JS
81
- * @var type
82
- */
83
- public $js_disabled = null;
84
-
85
- /**
86
- * Sms check
87
- * @var type
88
- */
89
- public $sms_allow = null;
90
-
91
- /**
92
- * Sms code result
93
- * @var type
94
- */
95
- public $sms = null;
96
-
97
- /**
98
- * Sms error code
99
- * @var type
100
- */
101
- public $sms_error_code = null;
102
-
103
- /**
104
- * Sms error code
105
- * @var type
106
- */
107
- public $sms_error_text = null;
108
-
109
- /**
110
- * Stop queue message, 1|0
111
- * @var int
112
- */
113
- public $stop_queue = null;
114
-
115
- /**
116
- * Account shuld by deactivated after registration, 1|0
117
- * @var int
118
- */
119
- public $inactive = null;
120
-
121
- /**
122
- * Create server response
123
- *
124
- * @param type $response
125
- * @param xmlrpcresp $obj
126
- */
127
- function __construct($response = null, xmlrpcresp $obj = null) {
128
- if ($response && is_array($response) && count($response) > 0) {
129
- foreach ($response as $param => $value) {
130
- $this->{$param} = $value;
131
- }
132
- } else {
133
- $this->errno = $obj->errno;
134
- $this->errstr = $obj->errstr;
135
-
136
- $this->errstr = preg_replace("/.+(\*\*\*.+\*\*\*).+/", "$1", $this->errstr);
137
-
138
- // Разбираем xmlrpcresp ответ с клинтолка
139
- if ($obj->val !== 0) {
140
- $this->stop_words = isset($obj->val['stop_words']) ? $obj->val['stop_words'] : null;
141
- $this->comment = $obj->val['comment'];
142
- $this->blacklisted = (isset($obj->val['blacklisted'])) ? $obj->val['blacklisted'] : null;
143
- $this->allow = (isset($obj->val['allow'])) ? $obj->val['allow'] : null;
144
- $this->id = (isset($obj->val['id'])) ? $obj->val['id'] : null;
145
- $this->fast_submit = (isset($obj->val['fast_submit'])) ? $obj->val['fast_submit'] : 0;
146
- $this->spam = (isset($obj->val['spam'])) ? $obj->val['spam'] : 0;
147
- $this->js_disabled = (isset($obj->val['js_disabled'])) ? $obj->val['js_disabled'] : 0;
148
- $this->sms_allow = (isset($obj->val['sms_allow'])) ? $obj->val['sms_allow'] : null;
149
- $this->sms = (isset($obj->val['sms'])) ? $obj->val['sms'] : null;
150
- $this->sms_error_code = (isset($obj->val['sms_error_code'])) ? $obj->val['sms_error_code'] : null;
151
- $this->sms_error_text = (isset($obj->val['sms_error_text'])) ? $obj->val['sms_error_text'] : null;
152
- $this->stop_queue = (isset($obj->val['stop_queue'])) ? $obj->val['stop_queue'] : 0;
153
- $this->inactive = (isset($obj->val['inactive'])) ? $obj->val['inactive'] : 0;
154
- } else {
155
- $this->comment = $this->errstr . '. Automoderator cleantalk.org';
156
- }
157
- }
158
- }
159
-
160
- }
161
-
162
- /**
163
- * Request class
164
- */
165
- class CleantalkRequest {
166
-
167
- const VERSION = '0.7';
168
-
169
- /**
170
- * User message
171
- * @var string
172
- */
173
- public $message = null;
174
-
175
- /**
176
- * Post example with last comments
177
- * @var string
178
- */
179
- public $example = null;
180
-
181
- /**
182
- * Auth key
183
- * @var string
184
- */
185
- public $auth_key = null;
186
-
187
- /**
188
- * Engine
189
- * @var string
190
- */
191
- public $agent = null;
192
-
193
- /**
194
- * Is check for stoplist,
195
- * valid are 0|1
196
- * @var int
197
- */
198
- public $stoplist_check = null;
199
-
200
- /**
201
- * Language server response,
202
- * valid are 'en' or 'ru'
203
- * @var string
204
- */
205
- public $response_lang = null;
206
-
207
- /**
208
- * User IP
209
- * @var strings
210
- */
211
- public $sender_ip = null;
212
-
213
- /**
214
- * User email
215
- * @var strings
216
- */
217
- public $sender_email = null;
218
-
219
- /**
220
- * User nickname
221
- * @var string
222
- */
223
- public $sender_nickname = null;
224
-
225
- /**
226
- * Sender info JSON string
227
- * @var string
228
- */
229
- public $sender_info = null;
230
-
231
- /**
232
- * Post info JSON string
233
- * @var string
234
- */
235
- public $post_info = null;
236
-
237
- /**
238
- * Is allow links, email and icq,
239
- * valid are 1|0
240
- * @var int
241
- */
242
- public $allow_links = null;
243
-
244
- /**
245
- * Time form filling
246
- * @var int
247
- */
248
- public $submit_time = null;
249
-
250
- /**
251
- * Is enable Java Script,
252
- * valid are 0|1|2
253
- * Status:
254
- * null - JS html code not inserted into phpBB templates
255
- * 0 - JS disabled at the client browser
256
- * 1 - JS enabled at the client broswer
257
- * @var int
258
- */
259
- public $js_on = null;
260
-
261
- /**
262
- * user time zone
263
- * @var string
264
- */
265
- public $tz = null;
266
-
267
- /**
268
- * Feedback string,
269
- * valid are 'requset_id:(1|0)'
270
- * @var string
271
- */
272
- public $feedback = null;
273
-
274
- /**
275
- * Phone number
276
- * @var type
277
- */
278
- public $phone = null;
279
-
280
- /**
281
- * Fill params with constructor
282
- * @param type $params
283
- */
284
- public function __construct($params = null) {
285
- if (is_array($params) && count($params) > 0) {
286
- foreach ($params as $param => $value) {
287
- $this->{$param} = $value;
288
- }
289
- }
290
- }
291
-
292
- }
293
-
294
- /**
295
- * Cleantalk class create request
296
- */
297
- class Cleantalk {
298
-
299
- /**
300
- * Debug level
301
- * @var int
302
- */
303
- public $debug = 0;
304
-
305
- /**
306
- * Maximum data size in bytes
307
- * @var int
308
- */
309
- private $dataMaxSise = 32768;
310
-
311
- /**
312
- * Data compression rate
313
- * @var int
314
- */
315
- private $compressRate = 6;
316
-
317
- /**
318
- * Server connection timeout in seconds
319
- * @var int
320
- */
321
- private $server_timeout = 2;
322
-
323
- /**
324
- * Cleantalk server url
325
- * @var string
326
- */
327
- public $server_url = null;
328
-
329
- /**
330
- * Last work url
331
- * @var string
332
- */
333
- public $work_url = null;
334
-
335
- /**
336
- * WOrk url ttl
337
- * @var int
338
- */
339
- public $server_ttl = null;
340
-
341
- /**
342
- * Time wotk_url changer
343
- * @var int
344
- */
345
- public $server_changed = null;
346
-
347
- /**
348
- * Flag is change server url
349
- * @var bool
350
- */
351
- public $server_change = false;
352
-
353
- /**
354
- * Use TRUE when need stay on server. Example: send feedback
355
- * @var bool
356
- */
357
- public $stay_on_server = false;
358
-
359
- /**
360
- * Function checks whether it is possible to publish the message
361
- * @param CleantalkRequest $request
362
- * @return type
363
- */
364
- public function isAllowMessage(CleantalkRequest $request) {
365
- $error_params = $this->filterRequest('check_message', $request);
366
-
367
- if (!empty($error_params)) {
368
- $response = new CleantalkResponse(
369
- array(
370
- 'allow' => 0,
371
- 'comment' => 'CleanTalk. Request params error: ' . implode(', ', $error_params)
372
- ), null);
373
-
374
- return $response;
375
- }
376
-
377
- $msg = $this->createMsg('check_message', $request);
378
- return $this->xmlRequest($msg);
379
- }
380
-
381
- /**
382
- * Function checks whether it is possible to publish the message
383
- * @param CleantalkRequest $request
384
- * @return type
385
- */
386
- public function isAllowUser(CleantalkRequest $request) {
387
- $error_params = $this->filterRequest('check_newuser', $request);
388
-
389
- if (!empty($error_params)) {
390
- $response = new CleantalkResponse(
391
- array(
392
- 'allow' => 0,
393
- 'comment' => 'CleanTalk. Request params error: ' . implode(', ', $error_params)
394
- ), null);
395
-
396
- return $response;
397
- }
398
-
399
- $msg = $this->createMsg('check_newuser', $request);
400
- return $this->xmlRequest($msg);
401
- }
402
-
403
- /**
404
- * Function sends the results of manual moderation
405
- *
406
- * @param CleantalkRequest $request
407
- * @return type
408
- */
409
- public function sendFeedback(CleantalkRequest $request) {
410
- $error_params = $this->filterRequest('send_feedback', $request);
411
-
412
- if (!empty($error_params)) {
413
- $response = new CleantalkResponse(
414
- array(
415
- 'allow' => 0,
416
- 'comment' => 'Cleantalk. Spam protect. Request params error: ' . implode(', ', $error_params)
417
- ), null);
418
-
419
- return $response;
420
- }
421
-
422
- $msg = $this->createMsg('send_feedback', $request);
423
- return $this->xmlRequest($msg);
424
- }
425
-
426
- /**
427
- * Filter request params
428
- * @param CleantalkRequest $request
429
- * @return type
430
- */
431
- private function filterRequest($method, CleantalkRequest $request) {
432
- $error_params = array();
433
-
434
- // general and optional
435
- foreach ($request as $param => $value) {
436
- if (in_array($param, array('message', 'example', 'agent',
437
- 'sender_info', 'sender_nickname', 'post_info', 'phone')) && !empty($value)) {
438
- if (!is_string($value) && !is_integer($value)) {
439
- $error_params[] = $param;
440
- }
441
- }
442
-
443
- if (in_array($param, array('stoplist_check', 'allow_links')) && !empty($value)) {
444
- if (!in_array($value, array(1, 2))) {
445
- $error_params[] = $param;
446
- }
447
- }
448
-
449
- if (in_array($param, array('js_on')) && !empty($value)) {
450
- if (!is_integer($value)) {
451
- $error_params[] = $param;
452
- }
453
- }
454
-
455
- if ($param == 'sender_ip' && !empty($value)) {
456
- if (!is_string($value)) {
457
- $error_params[] = $param;
458
- }
459
- }
460
-
461
- if ($param == 'sender_email' && !empty($value)) {
462
- if (!is_string($value)) {
463
- $error_params[] = $param;
464
- }
465
- }
466
-
467
- if ($param == 'submit_time' && !empty($value)) {
468
- if (!is_int($value)) {
469
- $error_params[] = $param;
470
- }
471
- }
472
- }
473
-
474
- // special and must be
475
- switch ($method) {
476
- case 'check_message':
477
-
478
- // Convert strings to UTF8
479
- $this->message = $this->stringToUTF8($request->message);
480
- $this->example = $this->stringToUTF8($request->example);
481
-
482
- $request->message = $this->compressData($request->message);
483
- $request->example = $this->compressData($request->example);
484
- break;
485
-
486
- case 'check_newuser':
487
- if (empty($request->sender_nickname)) {
488
- $error_params[] = 'sender_nickname';
489
- }
490
- if (empty($request->sender_email)) {
491
- $error_params[] = 'sender_email';
492
- }
493
- break;
494
-
495
- case 'send_feedback':
496
- if (empty($request->feedback)) {
497
- $error_params[] = 'feedback';
498
- }
499
- break;
500
- }
501
-
502
- if (isset($request->sender_info)) {
503
- if (function_exists('json_decode')){
504
- $sender_info = json_decode($request->sender_info, true);
505
-
506
- // Save request's creation timestamps
507
- if (function_exists('microtime'))
508
- $sender_info['request_submit_time'] = (float) (time() + (float) microtime());
509
-
510
- if (function_exists('json_encode')){
511
- $request->sender_info = json_encode($sender_info);
512
-
513
- }
514
- }
515
- }
516
-
517
- return $error_params;
518
- }
519
-
520
- /**
521
- * Compress data and encode to base64
522
- * @param type string
523
- * @return string
524
- */
525
- private function compressData($data = null){
526
-
527
- if (strlen($data) > $this->dataMaxSise && function_exists('gzencode') && function_exists('base64_encode')){
528
-
529
- $localData = gzencode($data, $this->compressRate, FORCE_GZIP);
530
-
531
- if ($localData === false)
532
- return $data;
533
-
534
- $localData = base64_encode($localData);
535
-
536
- if ($localData === false)
537
- return $data;
538
-
539
- return $localData;
540
- }
541
-
542
- return $data;
543
- }
544
-
545
- /**
546
- * Create msg for cleantalk server
547
- * @param type $method
548
- * @param CleantalkRequest $request
549
- * @return \xmlrpcmsg
550
- */
551
- private function createMsg($method, CleantalkRequest $request) {
552
- switch ($method) {
553
- case 'check_message':
554
- $params = array(
555
- 'message' => $request->message,
556
- 'base_text' => $request->example,
557
- 'auth_key' => $request->auth_key,
558
- 'agent' => $request->agent,
559
- 'sender_info' => $request->sender_info,
560
- 'ct_stop_words' => $request->stoplist_check,
561
- 'response_lang' => $request->response_lang,
562
- 'session_ip' => $request->sender_ip,
563
- 'user_email' => $request->sender_email,
564
- 'user_name' => $request->sender_nickname,
565
- 'post_info' => $request->post_info,
566
- 'ct_links' => $request->allow_links,
567
- 'submit_time' => $request->submit_time,
568
- 'js_on' => $request->js_on);
569
- break;
570
-
571
- case 'check_newuser':
572
- $params = array(
573
- 'auth_key' => $request->auth_key,
574
- 'agent' => $request->agent,
575
- 'response_lang' => $request->response_lang,
576
- 'session_ip' => $request->sender_ip,
577
- 'user_email' => $request->sender_email,
578
- 'user_name' => $request->sender_nickname,
579
- 'tz' => $request->tz,
580
- 'submit_time' => $request->submit_time,
581
- 'js_on' => $request->js_on,
582
- 'phone' => $request->phone,
583
- 'sender_info' => $request->sender_info);
584
- break;
585
-
586
- case 'send_feedback':
587
- if (is_array($request->feedback)) {
588
- $feedback = implode(';', $request->feedback);
589
- } else {
590
- $feedback = $request->feedback;
591
- }
592
-
593
- $params = array(
594
- 'auth_key' => $request->auth_key, // !
595
- 'feedback' => $feedback);
596
- break;
597
- }
598
-
599
- $xmlvars = array();
600
- foreach ($params as $param) {
601
- $xmlvars[] = new xmlrpcval($param);
602
- }
603
-
604
- $ct_params = new xmlrpcmsg(
605
- $method,
606
- array(new xmlrpcval($xmlvars, "array"))
607
- );
608
-
609
- return $ct_params;
610
- }
611
-
612
- /**
613
- * XM-Request
614
- * @param xmlrpcmsg $msg
615
- * @return boolean|\CleantalkResponse
616
- */
617
- private function xmlRequest(xmlrpcmsg $msg) {
618
- if (((isset($this->work_url) && $this->work_url !== '') && ($this->server_changed + $this->server_ttl > time()))
619
- || $this->stay_on_server == true) {
620
-
621
- $url = (!empty($this->work_url)) ? $this->work_url : $this->server_url;
622
- $ct_request = new xmlrpc_client($url);
623
- $ct_request->request_charset_encoding = 'utf-8';
624
- $ct_request->return_type = 'phpvals';
625
- $ct_request->setDebug($this->debug);
626
-
627
- $result = $ct_request->send($msg, $this->server_timeout);
628
- }
629
-
630
- if ((!isset($result) || $result->errno != 0) && $this->stay_on_server == false) {
631
- $matches = array();
632
- preg_match("#^(http://|https://)([a-z\.\-0-9]+):?(\d*)$#i", $this->server_url, $matches);
633
- $url_prefix = $matches[1];
634
- $pool = $matches[2];
635
- $port = $matches[3];
636
- if (empty($url_prefix))
637
- $url_prefix = 'http://';
638
- if (empty($pool)) {
639
- return false;
640
- } else {
641
- foreach ($this->get_servers_ip($pool) as $server) {
642
- if ($server['host'] === 'localhost' || $server['ip'] === null) {
643
- $work_url = $url_prefix . $server['host'];
644
- } else {
645
- $server_host = gethostbyaddr($server['ip']);
646
- $work_url = $url_prefix . $server_host;
647
- }
648
-
649
- $work_url = ($port !== '') ? $work_url . ':' . $port : $work_url;
650
-
651
- $this->work_url = $work_url;
652
- $this->server_ttl = $server['ttl'];
653
- $ct_request = new xmlrpc_client($this->work_url);
654
- $ct_request->request_charset_encoding = 'utf-8';
655
- $ct_request->return_type = 'phpvals';
656
- $ct_request->setDebug($this->debug);
657
- $result = $ct_request->send($msg, $this->server_timeout);
658
-
659
- if (!$result->faultCode()) {
660
- $this->server_change = true;
661
- break;
662
- }
663
- }
664
- }
665
- }
666
-
667
- $response = new CleantalkResponse(null, $result);
668
-
669
- return $response;
670
- }
671
-
672
- /**
673
- * Function DNS request
674
- * @param $host
675
- * @return array
676
- */
677
- public function get_servers_ip($host) {
678
- $response = null;
679
- if (!isset($host))
680
- return $response;
681
-
682
- if (function_exists('dns_get_record')) {
683
- $records = dns_get_record($host, DNS_A);
684
-
685
- if ($records !== FALSE) {
686
- foreach ($records as $server) {
687
- $response[] = $server;
688
- }
689
- }
690
- }
691
-
692
- if (count($response) == 0 && function_exists('gethostbynamel')) {
693
- $records = gethostbynamel($host);
694
-
695
- if ($records !== FALSE) {
696
- foreach ($records as $server) {
697
- $response[] = array("ip" => $server,
698
- "host" => $host,
699
- "ttl" => $this->server_ttl
700
- );
701
- }
702
- }
703
- }
704
-
705
- if (count($response) == 0) {
706
- $response[] = array("ip" => null,
707
- "host" => $host,
708
- "ttl" => $this->server_ttl
709
- );
710
- } else {
711
-
712
- // $i - to resolve collisions with localhost and
713
- $i = 0;
714
- $r_temp = null;
715
- foreach ($response as $server) {
716
- $ping = $this->httpPing($server['ip']);
717
-
718
- // -1 server is down, skips not reachable server
719
- if ($ping != -1)
720
- $r_temp[$ping * 10000 + $i] = $server;
721
-
722
- $i++;
723
- }
724
- if (count($r_temp)){
725
- ksort($r_temp);
726
- $response = $r_temp;
727
- }
728
- }
729
-
730
- return $response;
731
- }
732
-
733
- /**
734
- * Function to get the message hash from Cleantalk.ru comment
735
- * @param $message
736
- * @return null
737
- */
738
- public function getCleantalkCommentHash($message) {
739
- $matches = array();
740
- if (preg_match('/\n\n\*\*\*.+([a-z0-9]{32}).+\*\*\*$/', $message, $matches))
741
- return $matches[1];
742
- else if (preg_match('/\<br.*\>[\n]{0,1}\<br.*\>[\n]{0,1}\*\*\*.+([a-z0-9]{32}).+\*\*\*$/', $message, $matches))
743
- return $matches[1];
744
-
745
- return NULL;
746
- }
747
-
748
- /**
749
- * Function adds to the post comment Cleantalk.ru
750
- * @param $message
751
- * @param $comment
752
- * @return string
753
- */
754
- public function addCleantalkComment($message, $comment) {
755
- $comment = preg_match('/\*\*\*(.+)\*\*\*/', $comment, $matches) ? $comment : '*** ' . $comment . ' ***';
756
- return $message . "\n\n" . $comment;
757
- }
758
-
759
- /**
760
- * Function deletes the comment Cleantalk.ru
761
- * @param $message
762
- * @return mixed
763
- */
764
- public function delCleantalkComment($message) {
765
- $message = preg_replace('/\n\n\*\*\*.+\*\*\*$/', '', $message);
766
- $message = preg_replace('/\<br.*\>[\n]{0,1}\<br.*\>[\n]{0,1}\*\*\*.+\*\*\*$/', '', $message);
767
- return $message;
768
- }
769
-
770
- /*
771
- Get user IP
772
- */
773
- public function ct_session_ip( $data_ip )
774
- {
775
- if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
776
- {
777
- $forwarded_for = (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) ? htmlentities($_SERVER['HTTP_X_FORWARDED_FOR']) : '';
778
- }
779
-
780
- // 127.0.0.1 usually used at reverse proxy
781
- $session_ip = ($data_ip == '127.0.0.1' && !empty($forwarded_for)) ? $forwarded_for : $data_ip;
782
-
783
- return $session_ip;
784
- }
785
-
786
- /**
787
- * Function to check response time
788
- * param string
789
- * @return int
790
- */
791
- function httpPing($host){
792
-
793
- // Skip localhost ping cause it raise error at fsockopen.
794
- // And return minimun value
795
- if ($host == 'localhost')
796
- return 0.001;
797
-
798
- $starttime = microtime(true);
799
- $file = @fsockopen ($host, 80, $errno, $errstr, $this->server_timeout);
800
- $stoptime = microtime(true);
801
- $status = 0;
802
-
803
- if (!$file) {
804
- $status = -1; // Site is down
805
- } else {
806
- fclose($file);
807
- $status = ($stoptime - $starttime);
808
- $status = round($status, 4);
809
- }
810
-
811
- return $status;
812
- }
813
-
814
- /**
815
- * Function convert string to UTF8 and removes non UTF8 characters
816
- * param string
817
- * @return string
818
- */
819
- function stringToUTF8($str){
820
-
821
- if (!preg_match('//u', $str) && function_exists('mb_detect_encoding') && function_exists('mb_convert_encoding')) {
822
- $encoding = mb_detect_encoding($str);
823
- $srt = mb_convert_encoding($str, 'UTF-8', $encoding);
824
- }
825
-
826
- return $str;
827
- }
828
- }
829
-
830
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Cleantalk base class
4
+ *
5
+ * @version 1.21.5
6
+ * @package Cleantalk
7
+ * @subpackage Base
8
+ * @author Сleantalk team (welcome@cleantalk.ru)
9
+ * @copyright (C) 2013 СleanTalk team (http://cleantalk.org)
10
+ * @license GNU/GPL: http://www.gnu.org/copyleft/gpl.html
11
+ * @see http://cleantalk.ru/wiki/doku.php/api
12
+ *
13
+ */
14
+
15
+ /**
16
+ * Load JSON functions if they are not exists
17
+ */
18
+ if(!function_exists('json_encode')) {
19
+ require_once 'JSON.php';
20
+
21
+ function json_encode($data) {
22
+ $json = new Services_JSON();
23
+ return( $json->encode($data) );
24
+ }
25
+
26
+ }
27
+ if(!function_exists('json_decode')) {
28
+ require_once 'JSON.php';
29
+
30
+ function json_decode($data) {
31
+ $json = new Services_JSON();
32
+ return( $json->decode($data) );
33
+ }
34
+ }
35
+
36
+ /**
37
+ * Response class
38
+ */
39
+ class CleantalkResponse {
40
+
41
+ /**
42
+ * Is stop words
43
+ * @var int
44
+ */
45
+ public $stop_words = null;
46
+
47
+ /**
48
+ * Cleantalk comment
49
+ * @var string
50
+ */
51
+ public $comment = null;
52
+
53
+ /**
54
+ * Is blacklisted
55
+ * @var int
56
+ */
57
+ public $blacklisted = null;
58
+
59
+ /**
60
+ * Is allow, 1|0
61
+ * @var int
62
+ */
63
+ public $allow = null;
64
+
65
+ /**
66
+ * Request ID
67
+ * @var int
68
+ */
69
+ public $id = null;
70
+
71
+ /**
72
+ * Request errno
73
+ * @var int
74
+ */
75
+ public $errno = null;
76
+
77
+ /**
78
+ * Error string
79
+ * @var string
80
+ */
81
+ public $errstr = null;
82
+
83
+ /**
84
+ * Is fast submit, 1|0
85
+ * @var string
86
+ */
87
+ public $fast_submit = null;
88
+
89
+ /**
90
+ * Is spam comment
91
+ * @var string
92
+ */
93
+ public $spam = null;
94
+
95
+ /**
96
+ * Is JS
97
+ * @var type
98
+ */
99
+ public $js_disabled = null;
100
+
101
+ /**
102
+ * Sms check
103
+ * @var type
104
+ */
105
+ public $sms_allow = null;
106
+
107
+ /**
108
+ * Sms code result
109
+ * @var type
110
+ */
111
+ public $sms = null;
112
+
113
+ /**
114
+ * Sms error code
115
+ * @var type
116
+ */
117
+ public $sms_error_code = null;
118
+
119
+ /**
120
+ * Sms error code
121
+ * @var type
122
+ */
123
+ public $sms_error_text = null;
124
+
125
+ /**
126
+ * Stop queue message, 1|0
127
+ * @var int
128
+ */
129
+ public $stop_queue = null;
130
+
131
+ /**
132
+ * Account shuld by deactivated after registration, 1|0
133
+ * @var int
134
+ */
135
+ public $inactive = null;
136
+
137
+ /**
138
+ * Create server response
139
+ *
140
+ * @param type $response
141
+ * @param type $obj
142
+ */
143
+ function __construct($response = null, $obj = null) {
144
+ if ($response && is_array($response) && count($response) > 0) {
145
+ foreach ($response as $param => $value) {
146
+ $this->{$param} = $value;
147
+ }
148
+ } else {
149
+ $this->errno = $obj->errno;
150
+ $this->errstr = $obj->errstr;
151
+
152
+ $this->errstr = preg_replace("/.+(\*\*\*.+\*\*\*).+/", "$1", $this->errstr);
153
+ // Разбираем ответ с клинтолка
154
+ $this->stop_words = isset($obj->stop_words) ? $obj->stop_words : null;
155
+ $this->comment = isset($obj->comment) ? utf8_decode($obj->comment) : null;
156
+ $this->blacklisted = (isset($obj->blacklisted)) ? $obj->blacklisted : null;
157
+ $this->allow = (isset($obj->allow)) ? $obj->allow : null;
158
+ $this->id = (isset($obj->id)) ? $obj->id : null;
159
+ $this->fast_submit = (isset($obj->fast_submit)) ? $obj->fast_submit : 0;
160
+ $this->spam = (isset($obj->spam)) ? $obj->spam : 0;
161
+ $this->js_disabled = (isset($obj->js_disabled)) ? $obj->js_disabled : 0;
162
+ $this->sms_allow = (isset($obj->sms_allow)) ? $obj->sms_allow : null;
163
+ $this->sms = (isset($obj->sms)) ? $obj->sms : null;
164
+ $this->sms_error_code = (isset($obj->sms_error_code)) ? $obj->sms_error_code : null;
165
+ $this->sms_error_text = (isset($obj->sms_error_text)) ? $obj->sms_error_text : null;
166
+ $this->stop_queue = (isset($obj->stop_queue)) ? $obj->stop_queue : 0;
167
+ $this->inactive = (isset($obj->inactive)) ? $obj->inactive : 0;
168
+
169
+ if ($this->errno !== 0 && $this->errstr !== null && $this->comment === null)
170
+ $this->comment = '*** ' . $this->errstr . ' Automoderator cleantalk.org ***';
171
+ }
172
+ }
173
+
174
+ }
175
+
176
+ /**
177
+ * Request class
178
+ */
179
+ class CleantalkRequest {
180
+
181
+ /**
182
+ * User message
183
+ * @var string
184
+ */
185
+ public $message = null;
186
+
187
+ /**
188
+ * Post example with last comments
189
+ * @var string
190
+ */
191
+ public $example = null;
192
+
193
+ /**
194
+ * Auth key
195
+ * @var string
196
+ */
197
+ public $auth_key = null;
198
+
199
+ /**
200
+ * Engine
201
+ * @var string
202
+ */
203
+ public $agent = null;
204
+
205
+ /**
206
+ * Is check for stoplist,
207
+ * valid are 0|1
208
+ * @var int
209
+ */
210
+ public $stoplist_check = null;
211
+
212
+ /**
213
+ * Language server response,
214
+ * valid are 'en' or 'ru'
215
+ * @var string
216
+ */
217
+ public $response_lang = null;
218
+
219
+ /**
220
+ * User IP
221
+ * @var strings
222
+ */
223
+ public $sender_ip = null;
224
+
225
+ /**
226
+ * User email
227
+ * @var strings
228
+ */
229
+ public $sender_email = null;
230
+
231
+ /**
232
+ * User nickname
233
+ * @var string
234
+ */
235
+ public $sender_nickname = null;
236
+
237
+ /**
238
+ * Sender info JSON string
239
+ * @var string
240
+ */
241
+ public $sender_info = null;
242
+
243
+ /**
244
+ * Post info JSON string
245
+ * @var string
246
+ */
247
+ public $post_info = null;
248
+
249
+ /**
250
+ * Is allow links, email and icq,
251
+ * valid are 1|0
252
+ * @var int
253
+ */
254
+ public $allow_links = null;
255
+
256
+ /**
257
+ * Time form filling
258
+ * @var int
259
+ */
260
+ public $submit_time = null;
261
+
262
+ /**
263
+ * Is enable Java Script,
264
+ * valid are 0|1|2
265
+ * Status:
266
+ * null - JS html code not inserted into phpBB templates
267
+ * 0 - JS disabled at the client browser
268
+ * 1 - JS enabled at the client broswer
269
+ * @var int
270
+ */
271
+ public $js_on = null;
272
+
273
+ /**
274
+ * user time zone
275
+ * @var string
276
+ */
277
+ public $tz = null;
278
+
279
+ /**
280
+ * Feedback string,
281
+ * valid are 'requset_id:(1|0)'
282
+ * @var string
283
+ */
284
+ public $feedback = null;
285
+
286
+ /**
287
+ * Phone number
288
+ * @var type
289
+ */
290
+ public $phone = null;
291
+
292
+ /**
293
+ * Method name
294
+ * @var string
295
+ */
296
+ public $method_name = 'check_message';
297
+
298
+ /**
299
+ * Fill params with constructor
300
+ * @param type $params
301
+ */
302
+ public function __construct($params = null) {
303
+ if (is_array($params) && count($params) > 0) {
304
+ foreach ($params as $param => $value) {
305
+ $this->{$param} = $value;
306
+ }
307
+ }
308
+ }
309
+
310
+ }
311
+
312
+ /**
313
+ * Cleantalk class create request
314
+ */
315
+ class Cleantalk {
316
+
317
+ /**
318
+ * Debug level
319
+ * @var int
320
+ */
321
+ public $debug = 0;
322
+
323
+ /**
324
+ * Maximum data size in bytes
325
+ * @var int
326
+ */
327
+ private $dataMaxSise = 32768;
328
+
329
+ /**
330
+ * Data compression rate
331
+ * @var int
332
+ */
333
+ private $compressRate = 6;
334
+
335
+ /**
336
+ * Server connection timeout in seconds
337
+ * @var int
338
+ */
339
+ private $server_timeout = 2;
340
+
341
+ /**
342
+ * Cleantalk server url
343
+ * @var string
344
+ */
345
+ public $server_url = null;
346
+
347
+ /**
348
+ * Last work url
349
+ * @var string
350
+ */
351
+ public $work_url = null;
352
+
353
+ /**
354
+ * WOrk url ttl
355
+ * @var int
356
+ */
357
+ public $server_ttl = null;
358
+
359
+ /**
360
+ * Time wotk_url changer
361
+ * @var int
362
+ */
363
+ public $server_changed = null;
364
+
365
+ /**
366
+ * Flag is change server url
367
+ * @var bool
368
+ */
369
+ public $server_change = false;
370
+
371
+ /**
372
+ * Use TRUE when need stay on server. Example: send feedback
373
+ * @var bool
374
+ */
375
+ public $stay_on_server = false;
376
+
377
+ /**
378
+ * Codepage of the data
379
+ * @var bool
380
+ */
381
+ public $data_codepage = null;
382
+
383
+ /**
384
+ * API version to use
385
+ * @var string
386
+ */
387
+ public $api_version = '/api2.0';
388
+
389
+ /**
390
+ * Function checks whether it is possible to publish the message
391
+ * @param CleantalkRequest $request
392
+ * @return type
393
+ */
394
+ public function isAllowMessage(CleantalkRequest $request) {
395
+ $error_params = $this->filterRequest('check_message', $request);
396
+
397
+ if (!empty($error_params)) {
398
+ $response = new CleantalkResponse(
399
+ array(
400
+ 'allow' => 0,
401
+ 'comment' => 'CleanTalk. Request params error: ' . implode(', ', $error_params)
402
+ ), null);
403
+
404
+ return $response;
405
+ }
406
+
407
+ $msg = $this->createMsg('check_message', $request);
408
+ return $this->httpRequest($msg);
409
+ }
410
+
411
+ /**
412
+ * Function checks whether it is possible to publish the message
413
+ * @param CleantalkRequest $request
414
+ * @return type
415
+ */
416
+ public function isAllowUser(CleantalkRequest $request) {
417
+ $error_params = $this->filterRequest('check_newuser', $request);
418
+
419
+ if (!empty($error_params)) {
420
+ $response = new CleantalkResponse(
421
+ array(
422
+ 'allow' => 0,
423
+ 'comment' => 'CleanTalk. Request params error: ' . implode(', ', $error_params)
424
+ ), null);
425
+
426
+ return $response;
427
+ }
428
+
429
+ $msg = $this->createMsg('check_newuser', $request);
430
+ return $this->httpRequest($msg);
431
+ }
432
+
433
+ /**
434
+ * Function sends the results of manual moderation
435
+ *
436
+ * @param CleantalkRequest $request
437
+ * @return type
438
+ */
439
+ public function sendFeedback(CleantalkRequest $request) {
440
+ $error_params = $this->filterRequest('send_feedback', $request);
441
+
442
+ if (!empty($error_params)) {
443
+ $response = new CleantalkResponse(
444
+ array(
445
+ 'allow' => 0,
446
+ 'comment' => 'Cleantalk. Spam protect. Request params error: ' . implode(', ', $error_params)
447
+ ), null);
448
+
449
+ return $response;
450
+ }
451
+
452
+ $msg = $this->createMsg('send_feedback', $request);
453
+
454
+ return $this->httpRequest($msg);
455
+ }
456
+
457
+ /**
458
+ * Filter request params
459
+ * @param CleantalkRequest $request
460
+ * @return type
461
+ */
462
+ private function filterRequest($method, CleantalkRequest $request) {
463
+ $error_params = array();
464
+
465
+ // general and optional
466
+ foreach ($request as $param => $value) {
467
+ if (in_array($param, array('message', 'example', 'agent',
468
+ 'sender_info', 'sender_nickname', 'post_info', 'phone')) && !empty($value)) {
469
+ if (!is_string($value) && !is_integer($value)) {
470
+ $error_params[] = $param;
471
+ }
472
+ }
473
+
474
+ if (in_array($param, array('stoplist_check', 'allow_links')) && !empty($value)) {
475
+ if (!in_array($value, array(1, 2))) {
476
+ $error_params[] = $param;
477
+ }
478
+ }
479
+
480
+ if (in_array($param, array('js_on')) && !empty($value)) {
481
+ if (!is_integer($value)) {
482
+ $error_params[] = $param;
483
+ }
484
+ }
485
+
486
+ if ($param == 'sender_ip' && !empty($value)) {
487
+ if (!is_string($value)) {
488
+ $error_params[] = $param;
489
+ }
490
+ }
491
+
492
+ if ($param == 'sender_email' && !empty($value)) {
493
+ if (!is_string($value)) {
494
+ $error_params[] = $param;
495
+ }
496
+ }
497
+
498
+ if ($param == 'submit_time' && !empty($value)) {
499
+ if (!is_int($value)) {
500
+ $error_params[] = $param;
501
+ }
502
+ }
503
+ }
504
+
505
+ // special and must be
506
+ switch ($method) {
507
+ case 'check_message':
508
+ break;
509
+
510
+ case 'check_newuser':
511
+ if (empty($request->sender_nickname)) {
512
+ $error_params[] = 'sender_nickname';
513
+ }
514
+ if (empty($request->sender_email)) {
515
+ $error_params[] = 'sender_email';
516
+ }
517
+ break;
518
+
519
+ case 'send_feedback':
520
+ if (empty($request->feedback)) {
521
+ $error_params[] = 'feedback';
522
+ }
523
+ break;
524
+ }
525
+
526
+ return $error_params;
527
+ }
528
+
529
+ /**
530
+ * Compress data and encode to base64
531
+ * @param type string
532
+ * @return string
533
+ */
534
+ private function compressData($data = null){
535
+
536
+ if (strlen($data) > $this->dataMaxSise && function_exists('gzencode') && function_exists('base64_encode')){
537
+
538
+ $localData = gzencode($data, $this->compressRate, FORCE_GZIP);
539
+
540
+ if ($localData === false)
541
+ return $data;
542
+
543
+ $localData = base64_encode($localData);
544
+
545
+ if ($localData === false)
546
+ return $data;
547
+
548
+ return $localData;
549
+ }
550
+
551
+ return $data;
552
+ }
553
+
554
+ /**
555
+ * Create msg for cleantalk server
556
+ * @param type $method
557
+ * @param CleantalkRequest $request
558
+ * @return \xmlrpcmsg
559
+ */
560
+ private function createMsg($method, CleantalkRequest $request) {
561
+ switch ($method) {
562
+ case 'check_message':
563
+ // Convert strings to UTF8
564
+ $request->message = $this->stringToUTF8($request->message, $this->data_codepage);
565
+ $request->example = $this->stringToUTF8($request->example, $this->data_codepage);
566
+ $request->sender_email = $this->stringToUTF8($request->sender_email, $this->data_codepage);
567
+ $request->sender_nickname = $this->stringToUTF8($request->sender_nickname, $this->data_codepage);
568
+
569
+ $request->message = $this->compressData($request->message);
570
+ $request->example = $this->compressData($request->example);
571
+ break;
572
+
573
+ case 'check_newuser':
574
+ // Convert strings to UTF8
575
+ $request->sender_email = $this->stringToUTF8($request->sender_email, $this->data_codepage);
576
+ $request->sender_nickname = $this->stringToUTF8($request->sender_nickname, $this->data_codepage);
577
+ break;
578
+
579
+ case 'send_feedback':
580
+ if (is_array($request->feedback)) {
581
+ $request->feedback = implode(';', $request->feedback);
582
+ }
583
+ break;
584
+ }
585
+
586
+ $request->method_name = $method;
587
+
588
+ return $request;
589
+ }
590
+
591
+ /**
592
+ * Send JSON request to servers
593
+ * @param $msg
594
+ * @return boolean|\CleantalkResponse
595
+ */
596
+ private function sendRequest($data = null, $url, $server_timeout = 3) {
597
+ // Convert to array
598
+ $data = json_decode(json_encode($data), true);
599
+
600
+ // Convert to JSON
601
+ $data = json_encode($data);
602
+
603
+ $opts = array('http' =>
604
+ array(
605
+ 'method' => 'POST',
606
+ 'header' => "Content-Type: text/html\r\n",
607
+ 'content' => $data,
608
+ 'timeout' => $server_timeout
609
+ )
610
+ );
611
+
612
+ if (isset($this->api_version))
613
+ $url = $url . $this->api_version;
614
+
615
+ $context = stream_context_create($opts);
616
+ $result = @file_get_contents($url, false, $context);
617
+
618
+ $errstr = null;
619
+ $response = json_decode($result);
620
+ if ($result !== false && is_object($response)) {
621
+ $response->errno = 0;
622
+ $response->errstr = $errstr;
623
+ } else {
624
+ if ($result === false)
625
+ $errstr = 'Failed connect to ' . $url;
626
+ else
627
+ $errstr = $result;
628
+
629
+ $response = null;
630
+ $response['errno'] = 1;
631
+ $response['errstr'] = $errstr;
632
+ $response = json_decode(json_encode($response));
633
+ }
634
+
635
+ return $response;
636
+ }
637
+
638
+ /**
639
+ * httpRequest
640
+ * @param $msg
641
+ * @return boolean|\CleantalkResponse
642
+ */
643
+ private function httpRequest($msg) {
644
+ $result = false;
645
+ if (((isset($this->work_url) && $this->work_url !== '') && ($this->server_changed + $this->server_ttl > time()))
646
+ || $this->stay_on_server == true) {
647
+
648
+ $url = (!empty($this->work_url)) ? $this->work_url : $this->server_url;
649
+
650
+ $result = $this->sendRequest($msg, $url, $this->server_timeout);
651
+ }
652
+
653
+ if (($result === false || $result->errno != 0) && $this->stay_on_server == false) {
654
+
655
+ // Split server url to parts
656
+ preg_match("@^(https?://)([^/:]+)(.*)@i", $this->server_url, $matches);
657
+ $url_prefix = '';
658
+ if (isset($matches[1]))
659
+ $url_prefix = $matches[1];
660
+
661
+ $pool = null;
662
+ if (isset($matches[2]))
663
+ $pool = $matches[2];
664
+
665
+ $url_suffix = '';
666
+ if (isset($matches[3]))
667
+ $url_suffix = $matches[3];
668
+
669
+ if ($url_prefix === '')
670
+ $url_prefix = 'http://';
671
+
672
+ if (empty($pool)) {
673
+ return false;
674
+ } else {
675
+
676
+ // Loop until find work server
677
+ foreach ($this->get_servers_ip($pool) as $server) {
678
+ if ($server['host'] === 'localhost' || $server['ip'] === null) {
679
+ $work_url = $server['host'];
680
+ } else {
681
+ $server_host = gethostbyaddr($server['ip']);
682
+ $work_url = $server_host;
683
+ }
684
+ $work_url = $url_prefix . $work_url;
685
+ if (isset($url_suffix))
686
+ $work_url = $work_url . $url_suffix;
687
+
688
+ $this->work_url = $work_url;
689
+ $this->server_ttl = $server['ttl'];
690
+
691
+ $result = $this->sendRequest($msg, $this->work_url, $this->server_timeout);
692
+
693
+ if ($result !== false && $result->errno === 0) {
694
+ $this->server_change = true;
695
+ break;
696
+ }
697
+ }
698
+ }
699
+ }
700
+
701
+ $response = new CleantalkResponse(null, $result);
702
+
703
+ return $response;
704
+ }
705
+
706
+ /**
707
+ * Function DNS request
708
+ * @param $host
709
+ * @return array
710
+ */
711
+ public function get_servers_ip($host) {
712
+ $response = null;
713
+ if (!isset($host))
714
+ return $response;
715
+
716
+ if (function_exists('dns_get_record')) {
717
+ $records = dns_get_record($host, DNS_A);
718
+
719
+ if ($records !== FALSE) {
720
+ foreach ($records as $server) {
721
+ $response[] = $server;
722
+ }
723
+ }
724
+ }
725
+
726
+ if (count($response) == 0 && function_exists('gethostbynamel')) {
727
+ $records = gethostbynamel($host);
728
+
729
+ if ($records !== FALSE) {
730
+ foreach ($records as $server) {
731
+ $response[] = array("ip" => $server,
732
+ "host" => $host,
733
+ "ttl" => $this->server_ttl
734
+ );
735
+ }
736
+ }
737
+ }
738
+
739
+ if (count($response) == 0) {
740
+ $response[] = array("ip" => null,
741
+ "host" => $host,
742
+ "ttl" => $this->server_ttl
743
+ );
744
+ } else {
745
+
746
+ // $i - to resolve collisions with localhost and
747
+ $i = 0;
748
+ $r_temp = null;
749
+ foreach ($response as $server) {
750
+ $ping = $this->httpPing($server['ip']);
751
+
752
+ // -1 server is down, skips not reachable server
753
+ if ($ping != -1)
754
+ $r_temp[$ping * 10000 + $i] = $server;
755
+
756
+ $i++;
757
+ }
758
+ if (count($r_temp)){
759
+ ksort($r_temp);
760
+ $response = $r_temp;
761
+ }
762
+ }
763
+
764
+ return $response;
765
+ }
766
+
767
+ /**
768
+ * Function to get the message hash from Cleantalk.ru comment
769
+ * @param $message
770
+ * @return null
771
+ */
772
+ public function getCleantalkCommentHash($message) {
773
+ $matches = array();
774
+ if (preg_match('/\n\n\*\*\*.+([a-z0-9]{32}).+\*\*\*$/', $message, $matches))
775
+ return $matches[1];
776
+ else if (preg_match('/\<br.*\>[\n]{0,1}\<br.*\>[\n]{0,1}\*\*\*.+([a-z0-9]{32}).+\*\*\*$/', $message, $matches))
777
+ return $matches[1];
778
+
779
+ return NULL;
780
+ }
781
+
782
+ /**
783
+ * Function adds to the post comment Cleantalk.ru
784
+ * @param $message
785
+ * @param $comment
786
+ * @return string
787
+ */
788
+ public function addCleantalkComment($message, $comment) {
789
+ $comment = preg_match('/\*\*\*(.+)\*\*\*/', $comment, $matches) ? $comment : '*** ' . $comment . ' ***';
790
+ return $message . "\n\n" . $comment;
791
+ }
792
+
793
+ /**
794
+ * Function deletes the comment Cleantalk.ru
795
+ * @param $message
796
+ * @return mixed
797
+ */
798
+ public function delCleantalkComment($message) {
799
+ $message = preg_replace('/\n\n\*\*\*.+\*\*\*$/', '', $message);
800
+ $message = preg_replace('/\<br.*\>[\n]{0,1}\<br.*\>[\n]{0,1}\*\*\*.+\*\*\*$/', '', $message);
801
+ return $message;
802
+ }
803
+
804
+ /*
805
+ Get user IP
806
+ */
807
+ public function ct_session_ip( $data_ip )
808
+ {
809
+ if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
810
+ {
811
+ $forwarded_for = (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) ? htmlentities($_SERVER['HTTP_X_FORWARDED_FOR']) : '';
812
+ }
813
+
814
+ // 127.0.0.1 usually used at reverse proxy
815
+ $session_ip = ($data_ip == '127.0.0.1' && !empty($forwarded_for)) ? $forwarded_for : $data_ip;
816
+
817
+ return $session_ip;
818
+ }
819
+
820
+ /**
821
+ * Function to check response time
822
+ * param string
823
+ * @return int
824
+ */
825
+ function httpPing($host){
826
+
827
+ // Skip localhost ping cause it raise error at fsockopen.
828
+ // And return minimun value
829
+ if ($host == 'localhost')
830
+ return 0.001;
831
+
832
+ $starttime = microtime(true);
833
+ $file = @fsockopen ($host, 80, $errno, $errstr, $this->server_timeout);
834
+ $stoptime = microtime(true);
835
+ $status = 0;
836
+
837
+ if (!$file) {
838
+ $status = -1; // Site is down
839
+ } else {
840
+ fclose($file);
841
+ $status = ($stoptime - $starttime);
842
+ $status = round($status, 4);
843
+ }
844
+
845
+ return $status;
846
+ }
847
+
848
+ /**
849
+ * Function convert string to UTF8 and removes non UTF8 characters
850
+ * param string
851
+ * param string
852
+ * @return string
853
+ */
854
+ function stringToUTF8($str, $data_codepage = null){
855
+ if (!preg_match('//u', $str) && function_exists('mb_detect_encoding') && function_exists('mb_convert_encoding')) {
856
+
857
+ if ($data_codepage !== null)
858
+ return mb_convert_encoding($str, 'UTF-8', $data_codepage);
859
+
860
+ $encoding = mb_detect_encoding($str);
861
+ return mb_convert_encoding($str, 'UTF-8', $encoding);
862
+ }
863
+
864
+ return $str;
865
+ }
866
+ }
867
+
868
+ ?>
cleantalk.php CHANGED
@@ -2,13 +2,13 @@
2
  /*
3
  Plugin Name: CleanTalk. Anti-spam app
4
  Plugin URI: http://cleantalk.org/wordpress
5
- Description: Plug-in filters spam bots in the comments of a blog without move to trash. Spam protection is invisible for visitors of a blog. The plug-in not uses CAPTCHA or Q&A to stop spam-bots. It's simple, smart antispam for your blog.
6
- Version: 2.4.11
7
  Author: СleanTalk team <welcome@cleantalk.ru>
8
  Author URI: http://cleantalk.org
9
  */
10
 
11
- $ct_agent_version = 'wordpress-2411';
12
 
13
  add_action('init', 'ct_init_locale');
14
  add_action('delete_comment', 'ct_delete_comment_meta'); // param - comment ID
@@ -257,7 +257,7 @@ function ct_check($comment) {
257
  $comment_post_id = $comment['comment_post_ID'];
258
 
259
  $post = get_post($comment_post_id);
260
-
261
  $checkjs = null;
262
  if (!isset($_POST['ct_checkjs'])) {
263
  $checkjs = null;
2
  /*
3
  Plugin Name: CleanTalk. Anti-spam app
4
  Plugin URI: http://cleantalk.org/wordpress
5
+ Description: Plug-in filters spam bots in the comments of a blog without move to trash. Spam protection is invisible for visitors of a blog. The plug-in doesn't use CAPTCHA or Q&A to stop spam bots. It's simple, smart antispam for your blog.
6
+ Version: 2.4.12
7
  Author: СleanTalk team <welcome@cleantalk.ru>
8
  Author URI: http://cleantalk.org
9
  */
10
 
11
+ $ct_agent_version = 'wordpress-2412';
12
 
13
  add_action('init', 'ct_init_locale');
14
  add_action('delete_comment', 'ct_delete_comment_meta'); // param - comment ID
257
  $comment_post_id = $comment['comment_post_ID'];
258
 
259
  $post = get_post($comment_post_id);
260
+
261
  $checkjs = null;
262
  if (!isset($_POST['ct_checkjs'])) {
263
  $checkjs = null;
cleantalk.xmlrpc.php DELETED
@@ -1,3777 +0,0 @@
1
- <?php
2
- // by Edd Dumbill (C) 1999-2002
3
- // <edd@usefulinc.com>
4
- // $Id: xmlrpc.inc,v 1.174 2009/03/16 19:36:38 ggiunta Exp $
5
-
6
- // Copyright (c) 1999,2000,2002 Edd Dumbill.
7
- // All rights reserved.
8
- //
9
- // Redistribution and use in source and binary forms, with or without
10
- // modification, are permitted provided that the following conditions
11
- // are met:
12
- //
13
- // * Redistributions of source code must retain the above copyright
14
- // notice, this list of conditions and the following disclaimer.
15
- //
16
- // * Redistributions in binary form must reproduce the above
17
- // copyright notice, this list of conditions and the following
18
- // disclaimer in the documentation and/or other materials provided
19
- // with the distribution.
20
- //
21
- // * Neither the name of the "XML-RPC for PHP" nor the names of its
22
- // contributors may be used to endorse or promote products derived
23
- // from this software without specific prior written permission.
24
- //
25
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28
- // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29
- // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30
- // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31
- // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32
- // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
- // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34
- // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35
- // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36
- // OF THE POSSIBILITY OF SUCH DAMAGE.
37
-
38
- if(!function_exists('xml_parser_create'))
39
- {
40
- // For PHP 4 onward, XML functionality is always compiled-in on windows:
41
- // no more need to dl-open it. It might have been compiled out on *nix...
42
- if(strtoupper(substr(PHP_OS, 0, 3) != 'WIN'))
43
- {
44
- dl('xml.so');
45
- }
46
- }
47
-
48
- // G. Giunta 2005/01/29: declare global these variables,
49
- // so that xmlrpc.inc will work even if included from within a function
50
- // Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
51
- $GLOBALS['xmlrpcI4']='i4';
52
- $GLOBALS['xmlrpcInt']='int';
53
- $GLOBALS['xmlrpcBoolean']='boolean';
54
- $GLOBALS['xmlrpcDouble']='double';
55
- $GLOBALS['xmlrpcString']='string';
56
- $GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
57
- $GLOBALS['xmlrpcBase64']='base64';
58
- $GLOBALS['xmlrpcArray']='array';
59
- $GLOBALS['xmlrpcStruct']='struct';
60
- $GLOBALS['xmlrpcValue']='undefined';
61
-
62
- $GLOBALS['xmlrpcTypes']=array(
63
- $GLOBALS['xmlrpcI4'] => 1,
64
- $GLOBALS['xmlrpcInt'] => 1,
65
- $GLOBALS['xmlrpcBoolean'] => 1,
66
- $GLOBALS['xmlrpcString'] => 1,
67
- $GLOBALS['xmlrpcDouble'] => 1,
68
- $GLOBALS['xmlrpcDateTime'] => 1,
69
- $GLOBALS['xmlrpcBase64'] => 1,
70
- $GLOBALS['xmlrpcArray'] => 2,
71
- $GLOBALS['xmlrpcStruct'] => 3
72
- );
73
-
74
- $GLOBALS['xmlrpc_valid_parents'] = array(
75
- 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
76
- 'BOOLEAN' => array('VALUE'),
77
- 'I4' => array('VALUE'),
78
- 'INT' => array('VALUE'),
79
- 'STRING' => array('VALUE'),
80
- 'DOUBLE' => array('VALUE'),
81
- 'DATETIME.ISO8601' => array('VALUE'),
82
- 'BASE64' => array('VALUE'),
83
- 'MEMBER' => array('STRUCT'),
84
- 'NAME' => array('MEMBER'),
85
- 'DATA' => array('ARRAY'),
86
- 'ARRAY' => array('VALUE'),
87
- 'STRUCT' => array('VALUE'),
88
- 'PARAM' => array('PARAMS'),
89
- 'METHODNAME' => array('METHODCALL'),
90
- 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
91
- 'FAULT' => array('METHODRESPONSE'),
92
- 'NIL' => array('VALUE'), // only used when extension activated
93
- 'EX:NIL' => array('VALUE') // only used when extension activated
94
- );
95
-
96
- // define extra types for supporting NULL (useful for json or <NIL/>)
97
- $GLOBALS['xmlrpcNull']='null';
98
- $GLOBALS['xmlrpcTypes']['null']=1;
99
-
100
- // Not in use anymore since 2.0. Shall we remove it?
101
- /// @deprecated
102
- $GLOBALS['xmlEntities']=array(
103
- 'amp' => '&',
104
- 'quot' => '"',
105
- 'lt' => '<',
106
- 'gt' => '>',
107
- 'apos' => "'"
108
- );
109
-
110
- // tables used for transcoding different charsets into us-ascii xml
111
-
112
- $GLOBALS['xml_iso88591_Entities']=array();
113
- $GLOBALS['xml_iso88591_Entities']['in'] = array();
114
- $GLOBALS['xml_iso88591_Entities']['out'] = array();
115
- for ($i = 0; $i < 32; $i++)
116
- {
117
- $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
118
- $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
119
- }
120
- for ($i = 160; $i < 256; $i++)
121
- {
122
- $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
123
- $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
124
- }
125
-
126
- /// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159?
127
- /// These will NOT be present in true ISO-8859-1, but will save the unwary
128
- /// windows user from sending junk (though no luck when reciving them...)
129
- /*
130
- $GLOBALS['xml_cp1252_Entities']=array();
131
- for ($i = 128; $i < 160; $i++)
132
- {
133
- $GLOBALS['xml_cp1252_Entities']['in'][] = chr($i);
134
- }
135
- $GLOBALS['xml_cp1252_Entities']['out'] = array(
136
- '&#x20AC;', '?', '&#x201A;', '&#x0192;',
137
- '&#x201E;', '&#x2026;', '&#x2020;', '&#x2021;',
138
- '&#x02C6;', '&#x2030;', '&#x0160;', '&#x2039;',
139
- '&#x0152;', '?', '&#x017D;', '?',
140
- '?', '&#x2018;', '&#x2019;', '&#x201C;',
141
- '&#x201D;', '&#x2022;', '&#x2013;', '&#x2014;',
142
- '&#x02DC;', '&#x2122;', '&#x0161;', '&#x203A;',
143
- '&#x0153;', '?', '&#x017E;', '&#x0178;'
144
- );
145
- */
146
-
147
- $GLOBALS['xmlrpcerr'] = array(
148
- 'unknown_method'=>1,
149
- 'invalid_return'=>2,
150
- 'incorrect_params'=>3,
151
- 'introspect_unknown'=>4,
152
- 'http_error'=>5,
153
- 'no_data'=>6,
154
- 'no_ssl'=>7,
155
- 'curl_fail'=>8,
156
- 'invalid_request'=>15,
157
- 'no_curl'=>16,
158
- 'server_error'=>17,
159
- 'multicall_error'=>18,
160
- 'multicall_notstruct'=>9,
161
- 'multicall_nomethod'=>10,
162
- 'multicall_notstring'=>11,
163
- 'multicall_recursion'=>12,
164
- 'multicall_noparams'=>13,
165
- 'multicall_notarray'=>14,
166
-
167
- 'cannot_decompress'=>103,
168
- 'decompress_fail'=>104,
169
- 'dechunk_fail'=>105,
170
- 'server_cannot_decompress'=>106,
171
- 'server_decompress_fail'=>107
172
- );
173
-
174
- $GLOBALS['xmlrpcstr'] = array(
175
- 'unknown_method'=>'Unknown method',
176
- 'invalid_return'=>'Invalid return payload: enable debugging to examine incoming payload',
177
- 'incorrect_params'=>'Incorrect parameters passed to method',
178
- 'introspect_unknown'=>"Can't introspect: method unknown",
179
- 'http_error'=>"Didn't receive 200 OK from remote server.",
180
- 'no_data'=>'No data received from server.',
181
- 'no_ssl'=>'No SSL support compiled in.',
182
- 'curl_fail'=>'CURL error',
183
- 'invalid_request'=>'Invalid request payload',
184
- 'no_curl'=>'No CURL support compiled in.',
185
- 'server_error'=>'Internal server error',
186
- 'multicall_error'=>'Received from server invalid multicall response',
187
- 'multicall_notstruct'=>'system.multicall expected struct',
188
- 'multicall_nomethod'=>'missing methodName',
189
- 'multicall_notstring'=>'methodName is not a string',
190
- 'multicall_recursion'=>'recursive system.multicall forbidden',
191
- 'multicall_noparams'=>'missing params',
192
- 'multicall_notarray'=>'params is not an array',
193
-
194
- 'cannot_decompress'=>'Received from server compressed HTTP and cannot decompress',
195
- 'decompress_fail'=>'Received from server invalid compressed HTTP',
196
- 'dechunk_fail'=>'Received from server invalid chunked HTTP',
197
- 'server_cannot_decompress'=>'Received from client compressed HTTP request and cannot decompress',
198
- 'server_decompress_fail'=>'Received from client invalid compressed HTTP request'
199
- );
200
-
201
- // The charset encoding used by the server for received messages and
202
- // by the client for received responses when received charset cannot be determined
203
- // or is not supported
204
- $GLOBALS['xmlrpc_defencoding']='UTF-8';
205
-
206
- // The encoding used internally by PHP.
207
- // String values received as xml will be converted to this, and php strings will be converted to xml
208
- // as if having been coded with this
209
- // $GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
210
- $GLOBALS['xmlrpc_internalencoding']='UTF-8'; // by Denis Shagimuratov
211
-
212
- $GLOBALS['xmlrpcName']='XML-RPC for PHP';
213
- $GLOBALS['xmlrpcVersion']='3.0.0.beta';
214
-
215
- // let user errors start at 800
216
- $GLOBALS['xmlrpcerruser']=800;
217
- // let XML parse errors start at 100
218
- $GLOBALS['xmlrpcerrxml']=100;
219
-
220
- // formulate backslashes for escaping regexp
221
- // Not in use anymore since 2.0. Shall we remove it?
222
- /// @deprecated
223
- $GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
224
-
225
- // set to TRUE to enable correct decoding of <NIL/> and <EX:NIL/> values
226
- $GLOBALS['xmlrpc_null_extension']=false;
227
-
228
- // set to TRUE to enable encoding of php NULL values to <EX:NIL/> instead of <NIL/>
229
- $GLOBALS['xmlrpc_null_apache_encoding']=false;
230
-
231
- // used to store state during parsing
232
- // quick explanation of components:
233
- // ac - used to accumulate values
234
- // isf - used to indicate a parsing fault (2) or xmlrpcresp fault (1)
235
- // isf_reason - used for storing xmlrpcresp fault string
236
- // lv - used to indicate "looking for a value": implements
237
- // the logic to allow values with no types to be strings
238
- // params - used to store parameters in method calls
239
- // method - used to store method name
240
- // stack - array with genealogy of xml elements names:
241
- // used to validate nesting of xmlrpc elements
242
- $GLOBALS['_xh']=null;
243
-
244
- /**
245
- * Convert a string to the correct XML representation in a target charset
246
- * To help correct communication of non-ascii chars inside strings, regardless
247
- * of the charset used when sending requests, parsing them, sending responses
248
- * and parsing responses, an option is to convert all non-ascii chars present in the message
249
- * into their equivalent 'charset entity'. Charset entities enumerated this way
250
- * are independent of the charset encoding used to transmit them, and all XML
251
- * parsers are bound to understand them.
252
- * Note that in the std case we are not sending a charset encoding mime type
253
- * along with http headers, so we are bound by RFC 3023 to emit strict us-ascii.
254
- *
255
- * @todo do a bit of basic benchmarking (strtr vs. str_replace)
256
- * @todo make usage of iconv() or recode_string() or mb_string() where available
257
- */
258
- function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='')
259
- {
260
- if ($src_encoding == '')
261
- {
262
- // lame, but we know no better...
263
- $src_encoding = $GLOBALS['xmlrpc_internalencoding'];
264
- }
265
-
266
- switch(strtoupper($src_encoding.'_'.$dest_encoding))
267
- {
268
- case 'ISO-8859-1_':
269
- case 'ISO-8859-1_US-ASCII':
270
- $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
271
- $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
272
- break;
273
- case 'ISO-8859-1_UTF-8':
274
- $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
275
- $escaped_data = utf8_encode($escaped_data);
276
- break;
277
- case 'ISO-8859-1_ISO-8859-1':
278
- case 'US-ASCII_US-ASCII':
279
- case 'US-ASCII_UTF-8':
280
- case 'US-ASCII_':
281
- case 'US-ASCII_ISO-8859-1':
282
- case 'UTF-8_UTF-8':
283
- //case 'CP1252_CP1252':
284
- $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
285
- break;
286
- case 'UTF-8_':
287
- case 'UTF-8_US-ASCII':
288
- case 'UTF-8_ISO-8859-1':
289
- // NB: this will choke on invalid UTF-8, going most likely beyond EOF
290
- $escaped_data = '';
291
- // be kind to users creating string xmlrpcvals out of different php types
292
- $data = (string) $data;
293
- $ns = strlen ($data);
294
- for ($nn = 0; $nn < $ns; $nn++)
295
- {
296
- $ch = $data[$nn];
297
- $ii = ord($ch);
298
- //1 7 0bbbbbbb (127)
299
- if ($ii < 128)
300
- {
301
- /// @todo shall we replace this with a (supposedly) faster str_replace?
302
- switch($ii){
303
- case 34:
304
- $escaped_data .= '&quot;';
305
- break;
306
- case 38:
307
- $escaped_data .= '&amp;';
308
- break;
309
- case 39:
310
- $escaped_data .= '&apos;';
311
- break;
312
- case 60:
313
- $escaped_data .= '&lt;';
314
- break;
315
- case 62:
316
- $escaped_data .= '&gt;';
317
- break;
318
- default:
319
- $escaped_data .= $ch;
320
- } // switch
321
- }
322
- //2 11 110bbbbb 10bbbbbb (2047)
323
- else if ($ii>>5 == 6)
324
- {
325
- $b1 = ($ii & 31);
326
- $ii = ord($data[$nn+1]);
327
- $b2 = ($ii & 63);
328
- $ii = ($b1 * 64) + $b2;
329
- $ent = sprintf ('&#%d;', $ii);
330
- $escaped_data .= $ent;
331
- $nn += 1;
332
- }
333
- //3 16 1110bbbb 10bbbbbb 10bbbbbb
334
- else if ($ii>>4 == 14)
335
- {
336
- $b1 = ($ii & 15);
337
- $ii = ord($data[$nn+1]);
338
- $b2 = ($ii & 63);
339
- $ii = ord($data[$nn+2]);
340
- $b3 = ($ii & 63);
341
- $ii = ((($b1 * 64) + $b2) * 64) + $b3;
342
- $ent = sprintf ('&#%d;', $ii);
343
- $escaped_data .= $ent;
344
- $nn += 2;
345
- }
346
- //4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
347
- else if ($ii>>3 == 30)
348
- {
349
- $b1 = ($ii & 7);
350
- $ii = ord($data[$nn+1]);
351
- $b2 = ($ii & 63);
352
- $ii = ord($data[$nn+2]);
353
- $b3 = ($ii & 63);
354
- $ii = ord($data[$nn+3]);
355
- $b4 = ($ii & 63);
356
- $ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4;
357
- $ent = sprintf ('&#%d;', $ii);
358
- $escaped_data .= $ent;
359
- $nn += 3;
360
- }
361
- }
362
- break;
363
- /*
364
- case 'CP1252_':
365
- case 'CP1252_US-ASCII':
366
- $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
367
- $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
368
- $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
369
- break;
370
- case 'CP1252_UTF-8':
371
- $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
372
- /// @todo we could use real UTF8 chars here instead of xml entities... (note that utf_8 encode all allone will NOT convert them)
373
- $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
374
- $escaped_data = utf8_encode($escaped_data);
375
- break;
376
- case 'CP1252_ISO-8859-1':
377
- $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
378
- // we might as well replave all funky chars with a '?' here, but we are kind and leave it to the receiving application layer to decide what to do with these weird entities...
379
- $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
380
- break;
381
- */
382
- default:
383
- $escaped_data = '';
384
- error_log("Converting from $src_encoding to $dest_encoding: not supported...");
385
- }
386
- return $escaped_data;
387
- }
388
-
389
- /// xml parser handler function for opening element tags
390
- function xmlrpc_se($parser, $name, $attrs, $accept_single_vals=false)
391
- {
392
- // if invalid xmlrpc already detected, skip all processing
393
- if ($GLOBALS['_xh']['isf'] < 2)
394
- {
395
- // check for correct element nesting
396
- // top level element can only be of 2 types
397
- /// @todo optimization creep: save this check into a bool variable, instead of using count() every time:
398
- /// there is only a single top level element in xml anyway
399
- if (count($GLOBALS['_xh']['stack']) == 0)
400
- {
401
- if ($name != 'METHODRESPONSE' && $name != 'METHODCALL' && (
402
- $name != 'VALUE' && !$accept_single_vals))
403
- {
404
- $GLOBALS['_xh']['isf'] = 2;
405
- $GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element';
406
- return;
407
- }
408
- else
409
- {
410
- $GLOBALS['_xh']['rt'] = strtolower($name);
411
- $GLOBALS['_xh']['rt'] = strtolower($name);
412
- }
413
- }
414
- else
415
- {
416
- // not top level element: see if parent is OK
417
- $parent = end($GLOBALS['_xh']['stack']);
418
- if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name]))
419
- {
420
- $GLOBALS['_xh']['isf'] = 2;
421
- $GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent";
422
- return;
423
- }
424
- }
425
-
426
- switch($name)
427
- {
428
- // optimize for speed switch cases: most common cases first
429
- case 'VALUE':
430
- /// @todo we could check for 2 VALUE elements inside a MEMBER or PARAM element
431
- $GLOBALS['_xh']['vt']='value'; // indicator: no value found yet
432
- $GLOBALS['_xh']['ac']='';
433
- $GLOBALS['_xh']['lv']=1;
434
- $GLOBALS['_xh']['php_class']=null;
435
- break;
436
- case 'I4':
437
- case 'INT':
438
- case 'STRING':
439
- case 'BOOLEAN':
440
- case 'DOUBLE':
441
- case 'DATETIME.ISO8601':
442
- case 'BASE64':
443
- if ($GLOBALS['_xh']['vt']!='value')
444
- {
445
- //two data elements inside a value: an error occurred!
446
- $GLOBALS['_xh']['isf'] = 2;
447
- $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
448
- return;
449
- }
450
- $GLOBALS['_xh']['ac']=''; // reset the accumulator
451
- break;
452
- case 'STRUCT':
453
- case 'ARRAY':
454
- if ($GLOBALS['_xh']['vt']!='value')
455
- {
456
- //two data elements inside a value: an error occurred!
457
- $GLOBALS['_xh']['isf'] = 2;
458
- $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
459
- return;
460
- }
461
- // create an empty array to hold child values, and push it onto appropriate stack
462
- $cur_val = array();
463
- $cur_val['values'] = array();
464
- $cur_val['type'] = $name;
465
- // check for out-of-band information to rebuild php objs
466
- // and in case it is found, save it
467
- if (@isset($attrs['PHP_CLASS']))
468
- {
469
- $cur_val['php_class'] = $attrs['PHP_CLASS'];
470
- }
471
- $GLOBALS['_xh']['valuestack'][] = $cur_val;
472
- $GLOBALS['_xh']['vt']='data'; // be prepared for a data element next
473
- break;
474
- case 'DATA':
475
- if ($GLOBALS['_xh']['vt']!='data')
476
- {
477
- //two data elements inside a value: an error occurred!
478
- $GLOBALS['_xh']['isf'] = 2;
479
- $GLOBALS['_xh']['isf_reason'] = "found two data elements inside an array element";
480
- return;
481
- }
482
- case 'METHODCALL':
483
- case 'METHODRESPONSE':
484
- case 'PARAMS':
485
- // valid elements that add little to processing
486
- break;
487
- case 'METHODNAME':
488
- case 'NAME':
489
- /// @todo we could check for 2 NAME elements inside a MEMBER element
490
- $GLOBALS['_xh']['ac']='';
491
- break;
492
- case 'FAULT':
493
- $GLOBALS['_xh']['isf']=1;
494
- break;
495
- case 'MEMBER':
496
- $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name']=''; // set member name to null, in case we do not find in the xml later on
497
- //$GLOBALS['_xh']['ac']='';
498
- // Drop trough intentionally
499
- case 'PARAM':
500
- // clear value type, so we can check later if no value has been passed for this param/member
501
- $GLOBALS['_xh']['vt']=null;
502
- break;
503
- case 'NIL':
504
- case 'EX:NIL':
505
- if ($GLOBALS['xmlrpc_null_extension'])
506
- {
507
- if ($GLOBALS['_xh']['vt']!='value')
508
- {
509
- //two data elements inside a value: an error occurred!
510
- $GLOBALS['_xh']['isf'] = 2;
511
- $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
512
- return;
513
- }
514
- $GLOBALS['_xh']['ac']=''; // reset the accumulator
515
- break;
516
- }
517
- // we do not support the <NIL/> extension, so
518
- // drop through intentionally
519
- default:
520
- /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
521
- $GLOBALS['_xh']['isf'] = 2;
522
- $GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name";
523
- break;
524
- }
525
-
526
- // Save current element name to stack, to validate nesting
527
- $GLOBALS['_xh']['stack'][] = $name;
528
-
529
- /// @todo optimization creep: move this inside the big switch() above
530
- if($name!='VALUE')
531
- {
532
- $GLOBALS['_xh']['lv']=0;
533
- }
534
- }
535
- }
536
-
537
- /// Used in decoding xml chunks that might represent single xmlrpc values
538
- function xmlrpc_se_any($parser, $name, $attrs)
539
- {
540
- xmlrpc_se($parser, $name, $attrs, true);
541
- }
542
-
543
- /// xml parser handler function for close element tags
544
- function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true)
545
- {
546
- if ($GLOBALS['_xh']['isf'] < 2)
547
- {
548
- // push this element name from stack
549
- // NB: if XML validates, correct opening/closing is guaranteed and
550
- // we do not have to check for $name == $curr_elem.
551
- // we also checked for proper nesting at start of elements...
552
- $curr_elem = array_pop($GLOBALS['_xh']['stack']);
553
-
554
- switch($name)
555
- {
556
- case 'VALUE':
557
- // This if() detects if no scalar was inside <VALUE></VALUE>
558
- if ($GLOBALS['_xh']['vt']=='value')
559
- {
560
- $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
561
- $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString'];
562
- }
563
-
564
- if ($rebuild_xmlrpcvals)
565
- {
566
- // build the xmlrpc val out of the data received, and substitute it
567
- $temp = new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']);
568
- // in case we got info about underlying php class, save it
569
- // in the object we're rebuilding
570
- if (isset($GLOBALS['_xh']['php_class']))
571
- $temp->_php_class = $GLOBALS['_xh']['php_class'];
572
- // check if we are inside an array or struct:
573
- // if value just built is inside an array, let's move it into array on the stack
574
- $vscount = count($GLOBALS['_xh']['valuestack']);
575
- if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
576
- {
577
- $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp;
578
- }
579
- else
580
- {
581
- $GLOBALS['_xh']['value'] = $temp;
582
- }
583
- }
584
- else
585
- {
586
- /// @todo this needs to treat correctly php-serialized objects,
587
- /// since std deserializing is done by php_xmlrpc_decode,
588
- /// which we will not be calling...
589
- if (isset($GLOBALS['_xh']['php_class']))
590
- {
591
- }
592
-
593
- // check if we are inside an array or struct:
594
- // if value just built is inside an array, let's move it into array on the stack
595
- $vscount = count($GLOBALS['_xh']['valuestack']);
596
- if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
597
- {
598
- $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value'];
599
- }
600
- }
601
- break;
602
- case 'BOOLEAN':
603
- case 'I4':
604
- case 'INT':
605
- case 'STRING':
606
- case 'DOUBLE':
607
- case 'DATETIME.ISO8601':
608
- case 'BASE64':
609
- $GLOBALS['_xh']['vt']=strtolower($name);
610
- /// @todo: optimization creep - remove the if/elseif cycle below
611
- /// since the case() in which we are already did that
612
- if ($name=='STRING')
613
- {
614
- $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
615
- }
616
- elseif ($name=='DATETIME.ISO8601')
617
- {
618
- if (!preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $GLOBALS['_xh']['ac']))
619
- {
620
- error_log('XML-RPC: invalid value received in DATETIME: '.$GLOBALS['_xh']['ac']);
621
- }
622
- $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime'];
623
- $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
624
- }
625
- elseif ($name=='BASE64')
626
- {
627
- /// @todo check for failure of base64 decoding / catch warnings
628
- $GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']);
629
- }
630
- elseif ($name=='BOOLEAN')
631
- {
632
- // special case here: we translate boolean 1 or 0 into PHP
633
- // constants true or false.
634
- // Strings 'true' and 'false' are accepted, even though the
635
- // spec never mentions them (see eg. Blogger api docs)
636
- // NB: this simple checks helps a lot sanitizing input, ie no
637
- // security problems around here
638
- if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac'], 'true') == 0)
639
- {
640
- $GLOBALS['_xh']['value']=true;
641
- }
642
- else
643
- {
644
- // log if receiveing something strange, even though we set the value to false anyway
645
- if ($GLOBALS['_xh']['ac']!='0' && strcasecmp($GLOBALS['_xh']['ac'], 'false') != 0)
646
- error_log('XML-RPC: invalid value received in BOOLEAN: '.$GLOBALS['_xh']['ac']);
647
- $GLOBALS['_xh']['value']=false;
648
- }
649
- }
650
- elseif ($name=='DOUBLE')
651
- {
652
- // we have a DOUBLE
653
- // we must check that only 0123456789-.<space> are characters here
654
- // NOTE: regexp could be much stricter than this...
655
- if (!preg_match('/^[+-eE0123456789 \t.]+$/', $GLOBALS['_xh']['ac']))
656
- {
657
- /// @todo: find a better way of throwing an error than this!
658
- error_log('XML-RPC: non numeric value received in DOUBLE: '.$GLOBALS['_xh']['ac']);
659
- $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
660
- }
661
- else
662
- {
663
- // it's ok, add it on
664
- $GLOBALS['_xh']['value']=(double)$GLOBALS['_xh']['ac'];
665
- }
666
- }
667
- else
668
- {
669
- // we have an I4/INT
670
- // we must check that only 0123456789-<space> are characters here
671
- if (!preg_match('/^[+-]?[0123456789 \t]+$/', $GLOBALS['_xh']['ac']))
672
- {
673
- /// @todo find a better way of throwing an error than this!
674
- error_log('XML-RPC: non numeric value received in INT: '.$GLOBALS['_xh']['ac']);
675
- $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
676
- }
677
- else
678
- {
679
- // it's ok, add it on
680
- $GLOBALS['_xh']['value']=(int)$GLOBALS['_xh']['ac'];
681
- }
682
- }
683
- //$GLOBALS['_xh']['ac']=''; // is this necessary?
684
- $GLOBALS['_xh']['lv']=3; // indicate we've found a value
685
- break;
686
- case 'NAME':
687
- $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name'] = $GLOBALS['_xh']['ac'];
688
- break;
689
- case 'MEMBER':
690
- //$GLOBALS['_xh']['ac']=''; // is this necessary?
691
- // add to array in the stack the last element built,
692
- // unless no VALUE was found
693
- if ($GLOBALS['_xh']['vt'])
694
- {
695
- $vscount = count($GLOBALS['_xh']['valuestack']);
696
- $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['valuestack'][$vscount-1]['name']] = $GLOBALS['_xh']['value'];
697
- } else
698
- error_log('XML-RPC: missing VALUE inside STRUCT in received xml');
699
- break;
700
- case 'DATA':
701
- //$GLOBALS['_xh']['ac']=''; // is this necessary?
702
- $GLOBALS['_xh']['vt']=null; // reset this to check for 2 data elements in a row - even if they're empty
703
- break;
704
- case 'STRUCT':
705
- case 'ARRAY':
706
- // fetch out of stack array of values, and promote it to current value
707
- $curr_val = array_pop($GLOBALS['_xh']['valuestack']);
708
- $GLOBALS['_xh']['value'] = $curr_val['values'];
709
- $GLOBALS['_xh']['vt']=strtolower($name);
710
- if (isset($curr_val['php_class']))
711
- {
712
- $GLOBALS['_xh']['php_class'] = $curr_val['php_class'];
713
- }
714
- break;
715
- case 'PARAM':
716
- // add to array of params the current value,
717
- // unless no VALUE was found
718
- if ($GLOBALS['_xh']['vt'])
719
- {
720
- $GLOBALS['_xh']['params'][]=$GLOBALS['_xh']['value'];
721
- $GLOBALS['_xh']['pt'][]=$GLOBALS['_xh']['vt'];
722
- }
723
- else
724
- error_log('XML-RPC: missing VALUE inside PARAM in received xml');
725
- break;
726
- case 'METHODNAME':
727
- $GLOBALS['_xh']['method']=preg_replace('/^[\n\r\t ]+/', '', $GLOBALS['_xh']['ac']);
728
- break;
729
- case 'NIL':
730
- case 'EX:NIL':
731
- if ($GLOBALS['xmlrpc_null_extension'])
732
- {
733
- $GLOBALS['_xh']['vt']='null';
734
- $GLOBALS['_xh']['value']=null;
735
- $GLOBALS['_xh']['lv']=3;
736
- break;
737
- }
738
- // drop through intentionally if nil extension not enabled
739
- case 'PARAMS':
740
- case 'FAULT':
741
- case 'METHODCALL':
742
- case 'METHORESPONSE':
743
- break;
744
- default:
745
- // End of INVALID ELEMENT!
746
- // shall we add an assert here for unreachable code???
747
- break;
748
- }
749
- }
750
- }
751
-
752
- /// Used in decoding xmlrpc requests/responses without rebuilding xmlrpc values
753
- function xmlrpc_ee_fast($parser, $name)
754
- {
755
- xmlrpc_ee($parser, $name, false);
756
- }
757
-
758
- /// xml parser handler function for character data
759
- function xmlrpc_cd($parser, $data)
760
- {
761
- // skip processing if xml fault already detected
762
- if ($GLOBALS['_xh']['isf'] < 2)
763
- {
764
- // "lookforvalue==3" means that we've found an entire value
765
- // and should discard any further character data
766
- if($GLOBALS['_xh']['lv']!=3)
767
- {
768
- // G. Giunta 2006-08-23: useless change of 'lv' from 1 to 2
769
- //if($GLOBALS['_xh']['lv']==1)
770
- //{
771
- // if we've found text and we're just in a <value> then
772
- // say we've found a value
773
- //$GLOBALS['_xh']['lv']=2;
774
- //}
775
- // we always initialize the accumulator before starting parsing, anyway...
776
- //if(!@isset($GLOBALS['_xh']['ac']))
777
- //{
778
- // $GLOBALS['_xh']['ac'] = '';
779
- //}
780
- $GLOBALS['_xh']['ac'].=$data;
781
- }
782
- }
783
- }
784
-
785
- /// xml parser handler function for 'other stuff', ie. not char data or
786
- /// element start/end tag. In fact it only gets called on unknown entities...
787
- function xmlrpc_dh($parser, $data)
788
- {
789
- // skip processing if xml fault already detected
790
- if ($GLOBALS['_xh']['isf'] < 2)
791
- {
792
- if(substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
793
- {
794
- // G. Giunta 2006-08-25: useless change of 'lv' from 1 to 2
795
- //if($GLOBALS['_xh']['lv']==1)
796
- //{
797
- // $GLOBALS['_xh']['lv']=2;
798
- //}
799
- $GLOBALS['_xh']['ac'].=$data;
800
- }
801
- }
802
- return true;
803
- }
804
-
805
- class xmlrpc_client
806
- {
807
- var $path;
808
- var $server;
809
- var $port=0;
810
- var $method='http';
811
- var $errno;
812
- var $errstr;
813
- var $debug=0;
814
- var $username='';
815
- var $password='';
816
- var $authtype=1;
817
- var $cert='';
818
- var $certpass='';
819
- var $cacert='';
820
- var $cacertdir='';
821
- var $key='';
822
- var $keypass='';
823
- var $verifypeer=true;
824
- var $verifyhost=1;
825
- var $no_multicall=false;
826
- var $proxy='';
827
- var $proxyport=0;
828
- var $proxy_user='';
829
- var $proxy_pass='';
830
- var $proxy_authtype=1;
831
- var $cookies=array();
832
- var $extracurlopts=array();
833
-
834
- /**
835
- * List of http compression methods accepted by the client for responses.
836
- * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
837
- *
838
- * NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
839
- * in those cases it will be up to CURL to decide the compression methods
840
- * it supports. You might check for the presence of 'zlib' in the output of
841
- * curl_version() to determine wheter compression is supported or not
842
- */
843
- var $accepted_compression = array();
844
- /**
845
- * Name of compression scheme to be used for sending requests.
846
- * Either null, gzip or deflate
847
- */
848
- var $request_compression = '';
849
- /**
850
- * CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
851
- * http://curl.haxx.se/docs/faq.html#7.3)
852
- */
853
- var $xmlrpc_curl_handle = null;
854
- /// Wheter to use persistent connections for http 1.1 and https
855
- var $keepalive = false;
856
- /// Charset encodings that can be decoded without problems by the client
857
- var $accepted_charset_encodings = array();
858
- /// Charset encoding to be used in serializing request. NULL = use ASCII
859
- var $request_charset_encoding = '';
860
- /**
861
- * Decides the content of xmlrpcresp objects returned by calls to send()
862
- * valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
863
- */
864
- var $return_type = 'xmlrpcvals';
865
- /**
866
- * Sent to servers in http headers
867
- */
868
- var $user_agent;
869
-
870
- /**
871
- * @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
872
- * @param string $server the server name / ip address
873
- * @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
874
- * @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
875
- */
876
- function xmlrpc_client($path, $server='', $port='', $method='')
877
- {
878
- // allow user to specify all params in $path
879
- if($server == '' and $port == '' and $method == '')
880
- {
881
- $parts = parse_url($path);
882
- $server = $parts['host'];
883
- $path = isset($parts['path']) ? $parts['path'] : '';
884
- if(isset($parts['query']))
885
- {
886
- $path .= '?'.$parts['query'];
887
- }
888
- if(isset($parts['fragment']))
889
- {
890
- $path .= '#'.$parts['fragment'];
891
- }
892
- if(isset($parts['port']))
893
- {
894
- $port = $parts['port'];
895
- }
896
- if(isset($parts['scheme']))
897
- {
898
- $method = $parts['scheme'];
899
- }
900
- if(isset($parts['user']))
901
- {
902
- $this->username = $parts['user'];
903
- }
904
- if(isset($parts['pass']))
905
- {
906
- $this->password = $parts['pass'];
907
- }
908
- }
909
- if($path == '' || $path[0] != '/')
910
- {
911
- $this->path='/'.$path;
912
- }
913
- else
914
- {
915
- $this->path=$path;
916
- }
917
- $this->server=$server;
918
- if($port != '')
919
- {
920
- $this->port=$port;
921
- }
922
- if($method != '')
923
- {
924
- $this->method=$method;
925
- }
926
-
927
- // if ZLIB is enabled, let the client by default accept compressed responses
928
- if(function_exists('gzinflate') || (
929
- function_exists('curl_init') && (($info = curl_version()) &&
930
- ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
931
- ))
932
- {
933
- $this->accepted_compression = array('gzip', 'deflate');
934
- }
935
-
936
- // keepalives: enabled by default
937
- $this->keepalive = true;
938
-
939
- // by default the xml parser can support these 3 charset encodings
940
- $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
941
-
942
- // initialize user_agent string
943
- $this->user_agent = $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'];
944
- }
945
-
946
- /**
947
- * Enables/disables the echoing to screen of the xmlrpc responses received
948
- * @param integer $debug values 0, 1 and 2 are supported (2 = echo sent msg too, before received response)
949
- * @access public
950
- */
951
- function setDebug($in)
952
- {
953
- $this->debug=$in;
954
- }
955
-
956
- /**
957
- * Add some http BASIC AUTH credentials, used by the client to authenticate
958
- * @param string $u username
959
- * @param string $p password
960
- * @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
961
- * @access public
962
- */
963
- function setCredentials($u, $p, $t=1)
964
- {
965
- $this->username=$u;
966
- $this->password=$p;
967
- $this->authtype=$t;
968
- }
969
-
970
- /**
971
- * Add a client-side https certificate
972
- * @param string $cert
973
- * @param string $certpass
974
- * @access public
975
- */
976
- function setCertificate($cert, $certpass)
977
- {
978
- $this->cert = $cert;
979
- $this->certpass = $certpass;
980
- }
981
-
982
- /**
983
- * Add a CA certificate to verify server with (see man page about
984
- * CURLOPT_CAINFO for more details
985
- * @param string $cacert certificate file name (or dir holding certificates)
986
- * @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
987
- * @access public
988
- */
989
- function setCaCertificate($cacert, $is_dir=false)
990
- {
991
- if ($is_dir)
992
- {
993
- $this->cacertdir = $cacert;
994
- }
995
- else
996
- {
997
- $this->cacert = $cacert;
998
- }
999
- }
1000
-
1001
- /**
1002
- * Set attributes for SSL communication: private SSL key
1003
- * NB: does not work in older php/curl installs
1004
- * Thanks to Daniel Convissor
1005
- * @param string $key The name of a file containing a private SSL key
1006
- * @param string $keypass The secret password needed to use the private SSL key
1007
- * @access public
1008
- */
1009
- function setKey($key, $keypass)
1010
- {
1011
- $this->key = $key;
1012
- $this->keypass = $keypass;
1013
- }
1014
-
1015
- /**
1016
- * Set attributes for SSL communication: verify server certificate
1017
- * @param bool $i enable/disable verification of peer certificate
1018
- * @access public
1019
- */
1020
- function setSSLVerifyPeer($i)
1021
- {
1022
- $this->verifypeer = $i;
1023
- }
1024
-
1025
- /**
1026
- * Set attributes for SSL communication: verify match of server cert w. hostname
1027
- * @param int $i
1028
- * @access public
1029
- */
1030
- function setSSLVerifyHost($i)
1031
- {
1032
- $this->verifyhost = $i;
1033
- }
1034
-
1035
- /**
1036
- * Set proxy info
1037
- * @param string $proxyhost
1038
- * @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
1039
- * @param string $proxyusername Leave blank if proxy has public access
1040
- * @param string $proxypassword Leave blank if proxy has public access
1041
- * @param int $proxyauthtype set to constant CURLAUTH_NTLM to use NTLM auth with proxy
1042
- * @access public
1043
- */
1044
- function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1)
1045
- {
1046
- $this->proxy = $proxyhost;
1047
- $this->proxyport = $proxyport;
1048
- $this->proxy_user = $proxyusername;
1049
- $this->proxy_pass = $proxypassword;
1050
- $this->proxy_authtype = $proxyauthtype;
1051
- }
1052
-
1053
- /**
1054
- * Enables/disables reception of compressed xmlrpc responses.
1055
- * Note that enabling reception of compressed responses merely adds some standard
1056
- * http headers to xmlrpc requests. It is up to the xmlrpc server to return
1057
- * compressed responses when receiving such requests.
1058
- * @param string $compmethod either 'gzip', 'deflate', 'any' or ''
1059
- * @access public
1060
- */
1061
- function setAcceptedCompression($compmethod)
1062
- {
1063
- if ($compmethod == 'any')
1064
- $this->accepted_compression = array('gzip', 'deflate');
1065
- else
1066
- $this->accepted_compression = array($compmethod);
1067
- }
1068
-
1069
- /**
1070
- * Enables/disables http compression of xmlrpc request.
1071
- * Take care when sending compressed requests: servers might not support them
1072
- * (and automatic fallback to uncompressed requests is not yet implemented)
1073
- * @param string $compmethod either 'gzip', 'deflate' or ''
1074
- * @access public
1075
- */
1076
- function setRequestCompression($compmethod)
1077
- {
1078
- $this->request_compression = $compmethod;
1079
- }
1080
-
1081
- /**
1082
- * Adds a cookie to list of cookies that will be sent to server.
1083
- * NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
1084
- * do not do it unless you know what you are doing
1085
- * @param string $name
1086
- * @param string $value
1087
- * @param string $path
1088
- * @param string $domain
1089
- * @param int $port
1090
- * @access public
1091
- *
1092
- * @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
1093
- */
1094
- function setCookie($name, $value='', $path='', $domain='', $port=null)
1095
- {
1096
- $this->cookies[$name]['value'] = urlencode($value);
1097
- if ($path || $domain || $port)
1098
- {
1099
- $this->cookies[$name]['path'] = $path;
1100
- $this->cookies[$name]['domain'] = $domain;
1101
- $this->cookies[$name]['port'] = $port;
1102
- $this->cookies[$name]['version'] = 1;
1103
- }
1104
- else
1105
- {
1106
- $this->cookies[$name]['version'] = 0;
1107
- }
1108
- }
1109
-
1110
- /**
1111
- * Directly set cURL options, for extra flexibility
1112
- * It allows eg. to bind client to a specific IP interface / address
1113
- * @param $options array
1114
- */
1115
- function SetCurlOptions( $options )
1116
- {
1117
- $this->extracurlopts = $options;
1118
- }
1119
-
1120
- /**
1121
- * Set user-agent string that will be used by this client instance
1122
- * in http headers sent to the server
1123
- */
1124
- function SetUserAgent( $agentstring )
1125
- {
1126
- $this->user_agent = $agentstring;
1127
- }
1128
-
1129
- /**
1130
- * Send an xmlrpc request
1131
- * @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
1132
- * @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
1133
- * @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
1134
- * @return xmlrpcresp
1135
- * @access public
1136
- */
1137
- function& send($msg, $timeout=0, $method='')
1138
- {
1139
- // if user deos not specify http protocol, use native method of this client
1140
- // (i.e. method set during call to constructor)
1141
- if($method == '')
1142
- {
1143
- $method = $this->method;
1144
- }
1145
-
1146
- if(is_array($msg))
1147
- {
1148
- // $msg is an array of xmlrpcmsg's
1149
- $r = $this->multicall($msg, $timeout, $method);
1150
- return $r;
1151
- }
1152
- elseif(is_string($msg))
1153
- {
1154
- $n = new xmlrpcmsg('');
1155
- $n->payload = $msg;
1156
- $msg = $n;
1157
- }
1158
-
1159
- // where msg is an xmlrpcmsg
1160
- $msg->debug=$this->debug;
1161
-
1162
- if($method == 'https')
1163
- {
1164
- $r =& $this->sendPayloadHTTPS(
1165
- $msg,
1166
- $this->server,
1167
- $this->port,
1168
- $timeout,
1169
- $this->username,
1170
- $this->password,
1171
- $this->authtype,
1172
- $this->cert,
1173
- $this->certpass,
1174
- $this->cacert,
1175
- $this->cacertdir,
1176
- $this->proxy,
1177
- $this->proxyport,
1178
- $this->proxy_user,
1179
- $this->proxy_pass,
1180
- $this->proxy_authtype,
1181
- $this->keepalive,
1182
- $this->key,
1183
- $this->keypass
1184
- );
1185
- }
1186
- elseif($method == 'http11')
1187
- {
1188
- $r =& $this->sendPayloadCURL(
1189
- $msg,
1190
- $this->server,
1191
- $this->port,
1192
- $timeout,
1193
- $this->username,
1194
- $this->password,
1195
- $this->authtype,
1196
- null,
1197
- null,
1198
- null,
1199
- null,
1200
- $this->proxy,
1201
- $this->proxyport,
1202
- $this->proxy_user,
1203
- $this->proxy_pass,
1204
- $this->proxy_authtype,
1205
- 'http',
1206
- $this->keepalive
1207
- );
1208
- }
1209
- else
1210
- {
1211
- $r =& $this->sendPayloadHTTP10(
1212
- $msg,
1213
- $this->server,
1214
- $this->port,
1215
- $timeout,
1216
- $this->username,
1217
- $this->password,
1218
- $this->authtype,
1219
- $this->proxy,
1220
- $this->proxyport,
1221
- $this->proxy_user,
1222
- $this->proxy_pass,
1223
- $this->proxy_authtype
1224
- );
1225
- }
1226
-
1227
- return $r;
1228
- }
1229
-
1230
- /**
1231
- * @access private
1232
- */
1233
- function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,
1234
- $username='', $password='', $authtype=1, $proxyhost='',
1235
- $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1)
1236
- {
1237
- if($port==0)
1238
- {
1239
- $port=80;
1240
- }
1241
-
1242
- // Only create the payload if it was not created previously
1243
- if(empty($msg->payload))
1244
- {
1245
- $msg->createPayload($this->request_charset_encoding);
1246
- }
1247
-
1248
- $payload = $msg->payload;
1249
- // Deflate request body and set appropriate request headers
1250
- if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1251
- {
1252
- if($this->request_compression == 'gzip')
1253
- {
1254
- $a = @gzencode($payload);
1255
- if($a)
1256
- {
1257
- $payload = $a;
1258
- $encoding_hdr = "Content-Encoding: gzip\r\n";
1259
- }
1260
- }
1261
- else
1262
- {
1263
- $a = @gzcompress($payload);
1264
- if($a)
1265
- {
1266
- $payload = $a;
1267
- $encoding_hdr = "Content-Encoding: deflate\r\n";
1268
- }
1269
- }
1270
- }
1271
- else
1272
- {
1273
- $encoding_hdr = '';
1274
- }
1275
-
1276
- // thanks to Grant Rauscher <grant7@firstworld.net> for this
1277
- $credentials='';
1278
- if($username!='')
1279
- {
1280
- $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
1281
- if ($authtype != 1)
1282
- {
1283
- error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported with HTTP 1.0');
1284
- }
1285
- }
1286
-
1287
- $accepted_encoding = '';
1288
- if(is_array($this->accepted_compression) && count($this->accepted_compression))
1289
- {
1290
- $accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
1291
- }
1292
-
1293
- $proxy_credentials = '';
1294
- if($proxyhost)
1295
- {
1296
- if($proxyport == 0)
1297
- {
1298
- $proxyport = 8080;
1299
- }
1300
- $connectserver = $proxyhost;
1301
- $connectport = $proxyport;
1302
- $uri = 'http://'.$server.':'.$port.$this->path;
1303
- if($proxyusername != '')
1304
- {
1305
- if ($proxyauthtype != 1)
1306
- {
1307
- error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported with HTTP 1.0');
1308
- }
1309
- $proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
1310
- }
1311
- }
1312
- else
1313
- {
1314
- $connectserver = $server;
1315
- $connectport = $port;
1316
- $uri = $this->path;
1317
- }
1318
-
1319
- // Cookie generation, as per rfc2965 (version 1 cookies) or
1320
- // netscape's rules (version 0 cookies)
1321
- $cookieheader='';
1322
- if (count($this->cookies))
1323
- {
1324
- $version = '';
1325
- foreach ($this->cookies as $name => $cookie)
1326
- {
1327
- if ($cookie['version'])
1328
- {
1329
- $version = ' $Version="' . $cookie['version'] . '";';
1330
- $cookieheader .= ' ' . $name . '="' . $cookie['value'] . '";';
1331
- if ($cookie['path'])
1332
- $cookieheader .= ' $Path="' . $cookie['path'] . '";';
1333
- if ($cookie['domain'])
1334
- $cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
1335
- if ($cookie['port'])
1336
- $cookieheader .= ' $Port="' . $cookie['port'] . '";';
1337
- }
1338
- else
1339
- {
1340
- $cookieheader .= ' ' . $name . '=' . $cookie['value'] . ";";
1341
- }
1342
- }
1343
- $cookieheader = 'Cookie:' . $version . substr($cookieheader, 0, -1) . "\r\n";
1344
- }
1345
-
1346
- $op= 'POST ' . $uri. " HTTP/1.0\r\n" .
1347
- 'User-Agent: ' . $this->user_agent . "\r\n" .
1348
- 'Host: '. $server . ':' . $port . "\r\n" .
1349
- $credentials .
1350
- $proxy_credentials .
1351
- $accepted_encoding .
1352
- $encoding_hdr .
1353
- 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" .
1354
- $cookieheader .
1355
- 'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " .
1356
- strlen($payload) . "\r\n\r\n" .
1357
- $payload;
1358
-
1359
- if($this->debug > 1)
1360
- {
1361
- print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
1362
- // let the client see this now in case http times out...
1363
- flush();
1364
- }
1365
-
1366
- if($timeout>0)
1367
- {
1368
- $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
1369
- }
1370
- else
1371
- {
1372
- $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
1373
- }
1374
- if($fp)
1375
- {
1376
- if($timeout>0 && function_exists('stream_set_timeout'))
1377
- {
1378
- stream_set_timeout($fp, $timeout);
1379
- }
1380
- }
1381
- else
1382
- {
1383
- $this->errstr='Connect error: '.$this->errstr;
1384
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')');
1385
- return $r;
1386
- }
1387
-
1388
- if(!fputs($fp, $op, strlen($op)))
1389
- {
1390
- fclose($fp);
1391
- $this->errstr='Write error';
1392
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr);
1393
- return $r;
1394
- }
1395
- else
1396
- {
1397
- // reset errno and errstr on succesful socket connection
1398
- $this->errstr = '';
1399
- }
1400
- // G. Giunta 2005/10/24: close socket before parsing.
1401
- // should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
1402
- $ipd='';
1403
- do
1404
- {
1405
- // shall we check for $data === FALSE?
1406
- // as per the manual, it signals an error
1407
- $ipd.=fread($fp, 32768);
1408
- } while(!feof($fp));
1409
- fclose($fp);
1410
- $r =& $msg->parseResponse($ipd, false, $this->return_type);
1411
- return $r;
1412
-
1413
- }
1414
-
1415
- /**
1416
- * @access private
1417
- */
1418
- function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='',
1419
- $password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='',
1420
- $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1,
1421
- $keepalive=false, $key='', $keypass='')
1422
- {
1423
- $r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username,
1424
- $password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport,
1425
- $proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass);
1426
- return $r;
1427
- }
1428
-
1429
- /**
1430
- * Contributed by Justin Miller <justin@voxel.net>
1431
- * Requires curl to be built into PHP
1432
- * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
1433
- * @access private
1434
- */
1435
- function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='',
1436
- $password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='',
1437
- $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https',
1438
- $keepalive=false, $key='', $keypass='')
1439
- {
1440
- if(!function_exists('curl_init'))
1441
- {
1442
- $this->errstr='CURL unavailable on this install';
1443
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']);
1444
- return $r;
1445
- }
1446
- if($method == 'https')
1447
- {
1448
- if(($info = curl_version()) &&
1449
- ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
1450
- {
1451
- $this->errstr='SSL unavailable on this install';
1452
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']);
1453
- return $r;
1454
- }
1455
- }
1456
-
1457
- if($port == 0)
1458
- {
1459
- if($method == 'http')
1460
- {
1461
- $port = 80;
1462
- }
1463
- else
1464
- {
1465
- $port = 443;
1466
- }
1467
- }
1468
-
1469
- // Only create the payload if it was not created previously
1470
- if(empty($msg->payload))
1471
- {
1472
- $msg->createPayload($this->request_charset_encoding);
1473
- }
1474
-
1475
- // Deflate request body and set appropriate request headers
1476
- $payload = $msg->payload;
1477
- if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1478
- {
1479
- if($this->request_compression == 'gzip')
1480
- {
1481
- $a = @gzencode($payload);
1482
- if($a)
1483
- {
1484
- $payload = $a;
1485
- $encoding_hdr = 'Content-Encoding: gzip';
1486
- }
1487
- }
1488
- else
1489
- {
1490
- $a = @gzcompress($payload);
1491
- if($a)
1492
- {
1493
- $payload = $a;
1494
- $encoding_hdr = 'Content-Encoding: deflate';
1495
- }
1496
- }
1497
- }
1498
- else
1499
- {
1500
- $encoding_hdr = '';
1501
- }
1502
-
1503
- if($this->debug > 1)
1504
- {
1505
- print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
1506
- // let the client see this now in case http times out...
1507
- flush();
1508
- }
1509
-
1510
- if(!$keepalive || !$this->xmlrpc_curl_handle)
1511
- {
1512
- $curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
1513
- if($keepalive)
1514
- {
1515
- $this->xmlrpc_curl_handle = $curl;
1516
- }
1517
- }
1518
- else
1519
- {
1520
- $curl = $this->xmlrpc_curl_handle;
1521
- }
1522
-
1523
- // results into variable
1524
- curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1525
-
1526
- if($this->debug)
1527
- {
1528
- curl_setopt($curl, CURLOPT_VERBOSE, 1);
1529
- }
1530
- curl_setopt($curl, CURLOPT_USERAGENT, $this->user_agent);
1531
- // required for XMLRPC: post the data
1532
- curl_setopt($curl, CURLOPT_POST, 1);
1533
- // the data
1534
- curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
1535
-
1536
- // return the header too
1537
- curl_setopt($curl, CURLOPT_HEADER, 1);
1538
-
1539
- // will only work with PHP >= 5.0
1540
- // NB: if we set an empty string, CURL will add http header indicating
1541
- // ALL methods it is supporting. This is possibly a better option than
1542
- // letting the user tell what curl can / cannot do...
1543
- if(is_array($this->accepted_compression) && count($this->accepted_compression))
1544
- {
1545
- //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
1546
- // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1547
- if (count($this->accepted_compression) == 1)
1548
- {
1549
- curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]);
1550
- }
1551
- else
1552
- curl_setopt($curl, CURLOPT_ENCODING, '');
1553
- }
1554
- // extra headers
1555
- $headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
1556
- // if no keepalive is wanted, let the server know it in advance
1557
- if(!$keepalive)
1558
- {
1559
- $headers[] = 'Connection: close';
1560
- }
1561
- // request compression header
1562
- if($encoding_hdr)
1563
- {
1564
- $headers[] = $encoding_hdr;
1565
- }
1566
-
1567
- curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1568
- // timeout is borked
1569
- if($timeout)
1570
- {
1571
- curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
1572
- }
1573
-
1574
- if($username && $password)
1575
- {
1576
- curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password);
1577
- if (defined('CURLOPT_HTTPAUTH'))
1578
- {
1579
- curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype);
1580
- }
1581
- else if ($authtype != 1)
1582
- {
1583
- error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth is supported by the current PHP/curl install');
1584
- }
1585
- }
1586
-
1587
- if($method == 'https')
1588
- {
1589
- // set cert file
1590
- if($cert)
1591
- {
1592
- curl_setopt($curl, CURLOPT_SSLCERT, $cert);
1593
- }
1594
- // set cert password
1595
- if($certpass)
1596
- {
1597
- curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
1598
- }
1599
- // whether to verify remote host's cert
1600
- curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
1601
- // set ca certificates file/dir
1602
- if($cacert)
1603
- {
1604
- curl_setopt($curl, CURLOPT_CAINFO, $cacert);
1605
- }
1606
- if($cacertdir)
1607
- {
1608
- curl_setopt($curl, CURLOPT_CAPATH, $cacertdir);
1609
- }
1610
- // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1611
- if($key)
1612
- {
1613
- curl_setopt($curl, CURLOPT_SSLKEY, $key);
1614
- }
1615
- // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1616
- if($keypass)
1617
- {
1618
- curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass);
1619
- }
1620
- // whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that it matches the hostname used
1621
- curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
1622
- }
1623
-
1624
- // proxy info
1625
- if($proxyhost)
1626
- {
1627
- if($proxyport == 0)
1628
- {
1629
- $proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
1630
- }
1631
- curl_setopt($curl, CURLOPT_PROXY, $proxyhost.':'.$proxyport);
1632
- //curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
1633
- if($proxyusername)
1634
- {
1635
- curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword);
1636
- if (defined('CURLOPT_PROXYAUTH'))
1637
- {
1638
- curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyauthtype);
1639
- }
1640
- else if ($proxyauthtype != 1)
1641
- {
1642
- error_log('XML-RPC: '.__METHOD__.': warning. Only Basic auth to proxy is supported by the current PHP/curl install');
1643
- }
1644
- }
1645
- }
1646
-
1647
- // NB: should we build cookie http headers by hand rather than let CURL do it?
1648
- // the following code does not honour 'expires', 'path' and 'domain' cookie attributes
1649
- // set to client obj the the user...
1650
- if (count($this->cookies))
1651
- {
1652
- $cookieheader = '';
1653
- foreach ($this->cookies as $name => $cookie)
1654
- {
1655
- $cookieheader .= $name . '=' . $cookie['value'] . '; ';
1656
- }
1657
- curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2));
1658
- }
1659
-
1660
- foreach ($this->extracurlopts as $opt => $val)
1661
- {
1662
- curl_setopt($curl, $opt, $val);
1663
- }
1664
-
1665
- $result = curl_exec($curl);
1666
-
1667
- if ($this->debug > 1)
1668
- {
1669
- print "<PRE>\n---CURL INFO---\n";
1670
- foreach(curl_getinfo($curl) as $name => $val)
1671
- print $name . ': ' . htmlentities($val). "\n";
1672
- print "---END---\n</PRE>";
1673
- }
1674
-
1675
- if(!$result) /// @todo we should use a better check here - what if we get back '' or '0'?
1676
- {
1677
- $this->errstr='no response';
1678
- $resp=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl));
1679
- curl_close($curl);
1680
- if($keepalive)
1681
- {
1682
- $this->xmlrpc_curl_handle = null;
1683
- }
1684
- }
1685
- else
1686
- {
1687
- if(!$keepalive)
1688
- {
1689
- curl_close($curl);
1690
- }
1691
- $resp =& $msg->parseResponse($result, true, $this->return_type);
1692
- }
1693
- return $resp;
1694
- }
1695
-
1696
- /**
1697
- * Send an array of request messages and return an array of responses.
1698
- * Unless $this->no_multicall has been set to true, it will try first
1699
- * to use one single xmlrpc call to server method system.multicall, and
1700
- * revert to sending many successive calls in case of failure.
1701
- * This failure is also stored in $this->no_multicall for subsequent calls.
1702
- * Unfortunately, there is no server error code universally used to denote
1703
- * the fact that multicall is unsupported, so there is no way to reliably
1704
- * distinguish between that and a temporary failure.
1705
- * If you are sure that server supports multicall and do not want to
1706
- * fallback to using many single calls, set the fourth parameter to FALSE.
1707
- *
1708
- * NB: trying to shoehorn extra functionality into existing syntax has resulted
1709
- * in pretty much convoluted code...
1710
- *
1711
- * @param array $msgs an array of xmlrpcmsg objects
1712
- * @param integer $timeout connection timeout (in seconds)
1713
- * @param string $method the http protocol variant to be used
1714
- * @param boolean fallback When true, upon receiveing an error during multicall, multiple single calls will be attempted
1715
- * @return array
1716
- * @access public
1717
- */
1718
- function multicall($msgs, $timeout=0, $method='', $fallback=true)
1719
- {
1720
- if ($method == '')
1721
- {
1722
- $method = $this->method;
1723
- }
1724
- if(!$this->no_multicall)
1725
- {
1726
- $results = $this->_try_multicall($msgs, $timeout, $method);
1727
- if(is_array($results))
1728
- {
1729
- // System.multicall succeeded
1730
- return $results;
1731
- }
1732
- else
1733
- {
1734
- // either system.multicall is unsupported by server,
1735
- // or call failed for some other reason.
1736
- if ($fallback)
1737
- {
1738
- // Don't try it next time...
1739
- $this->no_multicall = true;
1740
- }
1741
- else
1742
- {
1743
- if (is_a($results, 'xmlrpcresp'))
1744
- {
1745
- $result = $results;
1746
- }
1747
- else
1748
- {
1749
- $result = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']);
1750
- }
1751
- }
1752
- }
1753
- }
1754
- else
1755
- {
1756
- // override fallback, in case careless user tries to do two
1757
- // opposite things at the same time
1758
- $fallback = true;
1759
- }
1760
-
1761
- $results = array();
1762
- if ($fallback)
1763
- {
1764
- // system.multicall is (probably) unsupported by server:
1765
- // emulate multicall via multiple requests
1766
- foreach($msgs as $msg)
1767
- {
1768
- $results[] =& $this->send($msg, $timeout, $method);
1769
- }
1770
- }
1771
- else
1772
- {
1773
- // user does NOT want to fallback on many single calls:
1774
- // since we should always return an array of responses,
1775
- // return an array with the same error repeated n times
1776
- foreach($msgs as $msg)
1777
- {
1778
- $results[] = $result;
1779
- }
1780
- }
1781
- return $results;
1782
- }
1783
-
1784
- /**
1785
- * Attempt to boxcar $msgs via system.multicall.
1786
- * Returns either an array of xmlrpcreponses, an xmlrpc error response
1787
- * or false (when received response does not respect valid multicall syntax)
1788
- * @access private
1789
- */
1790
- function _try_multicall($msgs, $timeout, $method)
1791
- {
1792
- // Construct multicall message
1793
- $calls = array();
1794
- foreach($msgs as $msg)
1795
- {
1796
- $call['methodName'] = new xmlrpcval($msg->method(),'string');
1797
- $numParams = $msg->getNumParams();
1798
- $params = array();
1799
- for($i = 0; $i < $numParams; $i++)
1800
- {
1801
- $params[$i] = $msg->getParam($i);
1802
- }
1803
- $call['params'] = new xmlrpcval($params, 'array');
1804
- $calls[] = new xmlrpcval($call, 'struct');
1805
- }
1806
- $multicall = new xmlrpcmsg('system.multicall');
1807
- $multicall->addParam(new xmlrpcval($calls, 'array'));
1808
-
1809
- // Attempt RPC call
1810
- $result =& $this->send($multicall, $timeout, $method);
1811
-
1812
- if($result->faultCode() != 0)
1813
- {
1814
- // call to system.multicall failed
1815
- return $result;
1816
- }
1817
-
1818
- // Unpack responses.
1819
- $rets = $result->value();
1820
-
1821
- if ($this->return_type == 'xml')
1822
- {
1823
- return $rets;
1824
- }
1825
- else if ($this->return_type == 'phpvals')
1826
- {
1827
- ///@todo test this code branch...
1828
- $rets = $result->value();
1829
- if(!is_array($rets))
1830
- {
1831
- return false; // bad return type from system.multicall
1832
- }
1833
- $numRets = count($rets);
1834
- if($numRets != count($msgs))
1835
- {
1836
- return false; // wrong number of return values.
1837
- }
1838
-
1839
- $response = array();
1840
- for($i = 0; $i < $numRets; $i++)
1841
- {
1842
- $val = $rets[$i];
1843
- if (!is_array($val)) {
1844
- return false;
1845
- }
1846
- switch(count($val))
1847
- {
1848
- case 1:
1849
- if(!isset($val[0]))
1850
- {
1851
- return false; // Bad value
1852
- }
1853
- // Normal return value
1854
- $response[$i] = new xmlrpcresp($val[0], 0, '', 'phpvals');
1855
- break;
1856
- case 2:
1857
- /// @todo remove usage of @: it is apparently quite slow
1858
- $code = @$val['faultCode'];
1859
- if(!is_int($code))
1860
- {
1861
- return false;
1862
- }
1863
- $str = @$val['faultString'];
1864
- if(!is_string($str))
1865
- {
1866
- return false;
1867
- }
1868
- $response[$i] = new xmlrpcresp(0, $code, $str);
1869
- break;
1870
- default:
1871
- return false;
1872
- }
1873
- }
1874
- return $response;
1875
- }
1876
- else // return type == 'xmlrpcvals'
1877
- {
1878
- $rets = $result->value();
1879
- if($rets->kindOf() != 'array')
1880
- {
1881
- return false; // bad return type from system.multicall
1882
- }
1883
- $numRets = $rets->arraysize();
1884
- if($numRets != count($msgs))
1885
- {
1886
- return false; // wrong number of return values.
1887
- }
1888
-
1889
- $response = array();
1890
- for($i = 0; $i < $numRets; $i++)
1891
- {
1892
- $val = $rets->arraymem($i);
1893
- switch($val->kindOf())
1894
- {
1895
- case 'array':
1896
- if($val->arraysize() != 1)
1897
- {
1898
- return false; // Bad value
1899
- }
1900
- // Normal return value
1901
- $response[$i] = new xmlrpcresp($val->arraymem(0));
1902
- break;
1903
- case 'struct':
1904
- $code = $val->structmem('faultCode');
1905
- if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
1906
- {
1907
- return false;
1908
- }
1909
- $str = $val->structmem('faultString');
1910
- if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
1911
- {
1912
- return false;
1913
- }
1914
- $response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
1915
- break;
1916
- default:
1917
- return false;
1918
- }
1919
- }
1920
- return $response;
1921
- }
1922
- }
1923
- } // end class xmlrpc_client
1924
-
1925
- class xmlrpcresp
1926
- {
1927
- var $val = 0;
1928
- var $valtyp;
1929
- var $errno = 0;
1930
- var $errstr = '';
1931
- var $payload;
1932
- var $hdrs = array();
1933
- var $_cookies = array();
1934
- var $content_type = 'text/xml';
1935
- var $raw_data = '';
1936
-
1937
- /**
1938
- * @param mixed $val either an xmlrpcval obj, a php value or the xml serialization of an xmlrpcval (a string)
1939
- * @param integer $fcode set it to anything but 0 to create an error response
1940
- * @param string $fstr the error string, in case of an error response
1941
- * @param string $valtyp either 'xmlrpcvals', 'phpvals' or 'xml'
1942
- *
1943
- * @todo add check that $val / $fcode / $fstr is of correct type???
1944
- * NB: as of now we do not do it, since it might be either an xmlrpcval or a plain
1945
- * php val, or a complete xml chunk, depending on usage of xmlrpc_client::send() inside which creator is called...
1946
- */
1947
- function xmlrpcresp($val, $fcode = 0, $fstr = '', $valtyp='')
1948
- {
1949
- if($fcode != 0)
1950
- {
1951
- // error response
1952
- $this->errno = $fcode;
1953
- $this->errstr = $fstr;
1954
- //$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
1955
- }
1956
- else
1957
- {
1958
- // successful response
1959
- $this->val = $val;
1960
- if ($valtyp == '')
1961
- {
1962
- // user did not declare type of response value: try to guess it
1963
- if (is_object($this->val) && is_a($this->val, 'xmlrpcval'))
1964
- {
1965
- $this->valtyp = 'xmlrpcvals';
1966
- }
1967
- else if (is_string($this->val))
1968
- {
1969
- $this->valtyp = 'xml';
1970
-
1971
- }
1972
- else
1973
- {
1974
- $this->valtyp = 'phpvals';
1975
- }
1976
- }
1977
- else
1978
- {
1979
- // user declares type of resp value: believe him
1980
- $this->valtyp = $valtyp;
1981
- }
1982
- }
1983
- }
1984
-
1985
- /**
1986
- * Returns the error code of the response.
1987
- * @return integer the error code of this response (0 for not-error responses)
1988
- * @access public
1989
- */
1990
- function faultCode()
1991
- {
1992
- return $this->errno;
1993
- }
1994
-
1995
- /**
1996
- * Returns the error code of the response.
1997
- * @return string the error string of this response ('' for not-error responses)
1998
- * @access public
1999
- */
2000
- function faultString()
2001
- {
2002
- return $this->errstr;
2003
- }
2004
-
2005
- /**
2006
- * Returns the value received by the server.
2007
- * @return mixed the xmlrpcval object returned by the server. Might be an xml string or php value if the response has been created by specially configured xmlrpc_client objects
2008
- * @access public
2009
- */
2010
- function value()
2011
- {
2012
- return $this->val;
2013
- }
2014
-
2015
- /**
2016
- * Returns an array with the cookies received from the server.
2017
- * Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
2018
- * with attributes being e.g. 'expires', 'path', domain'.
2019
- * NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
2020
- * are still present in the array. It is up to the user-defined code to decide
2021
- * how to use the received cookies, and wheter they have to be sent back with the next
2022
- * request to the server (using xmlrpc_client::setCookie) or not
2023
- * @return array array of cookies received from the server
2024
- * @access public
2025
- */
2026
- function cookies()
2027
- {
2028
- return $this->_cookies;
2029
- }
2030
-
2031
- /**
2032
- * Returns xml representation of the response. XML prologue not included
2033
- * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
2034
- * @return string the xml representation of the response
2035
- * @access public
2036
- */
2037
- function serialize($charset_encoding='')
2038
- {
2039
- if ($charset_encoding != '')
2040
- $this->content_type = 'text/xml; charset=' . $charset_encoding;
2041
- else
2042
- $this->content_type = 'text/xml';
2043
- $result = "<methodResponse>\n";
2044
- if($this->errno)
2045
- {
2046
- // G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
2047
- // by xml-encoding non ascii chars
2048
- $result .= "<fault>\n" .
2049
- "<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno .
2050
- "</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
2051
- xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "</string></value>\n</member>\n" .
2052
- "</struct>\n</value>\n</fault>";
2053
- }
2054
- else
2055
- {
2056
- if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval'))
2057
- {
2058
- if (is_string($this->val) && $this->valtyp == 'xml')
2059
- {
2060
- $result .= "<params>\n<param>\n" .
2061
- $this->val .
2062
- "</param>\n</params>";
2063
- }
2064
- else
2065
- {
2066
- /// @todo try to build something serializable?
2067
- die('cannot serialize xmlrpcresp objects whose content is native php values');
2068
- }
2069
- }
2070
- else
2071
- {
2072
- $result .= "<params>\n<param>\n" .
2073
- $this->val->serialize($charset_encoding) .
2074
- "</param>\n</params>";
2075
- }
2076
- }
2077
- $result .= "\n</methodResponse>";
2078
- $this->payload = $result;
2079
- return $result;
2080
- }
2081
- }
2082
-
2083
- class xmlrpcmsg
2084
- {
2085
- var $payload;
2086
- var $methodname;
2087
- var $params=array();
2088
- var $debug=0;
2089
- var $content_type = 'text/xml';
2090
-
2091
- /**
2092
- * @param string $meth the name of the method to invoke
2093
- * @param array $pars array of parameters to be paased to the method (xmlrpcval objects)
2094
- */
2095
- function xmlrpcmsg($meth, $pars=0)
2096
- {
2097
- $this->methodname=$meth;
2098
- if(is_array($pars) && count($pars)>0)
2099
- {
2100
- for($i=0; $i<count($pars); $i++)
2101
- {
2102
- $this->addParam($pars[$i]);
2103
- }
2104
- }
2105
- }
2106
-
2107
- /**
2108
- * @access private
2109
- */
2110
- function xml_header($charset_encoding='')
2111
- {
2112
- if ($charset_encoding != '')
2113
- {
2114
- return "<?xml version=\"1.0\" encoding=\"$charset_encoding\" ?" . ">\n<methodCall>\n";
2115
- }
2116
- else
2117
- {
2118
- return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
2119
- }
2120
- }
2121
-
2122
- /**
2123
- * @access private
2124
- */
2125
- function xml_footer()
2126
- {
2127
- return '</methodCall>';
2128
- }
2129
-
2130
- /**
2131
- * @access private
2132
- */
2133
- function kindOf()
2134
- {
2135
- return 'msg';
2136
- }
2137
-
2138
- /**
2139
- * @access private
2140
- */
2141
- function createPayload($charset_encoding='')
2142
- {
2143
- if ($charset_encoding != '')
2144
- $this->content_type = 'text/xml; charset=' . $charset_encoding;
2145
- else
2146
- $this->content_type = 'text/xml';
2147
- $this->payload=$this->xml_header($charset_encoding);
2148
- $this->payload.='<methodName>' . $this->methodname . "</methodName>\n";
2149
- $this->payload.="<params>\n";
2150
- for($i=0; $i<count($this->params); $i++)
2151
- {
2152
- $p=$this->params[$i];
2153
- $this->payload.="<param>\n" . $p->serialize($charset_encoding) .
2154
- "</param>\n";
2155
- }
2156
- $this->payload.="</params>\n";
2157
- $this->payload.=$this->xml_footer();
2158
- }
2159
-
2160
- /**
2161
- * Gets/sets the xmlrpc method to be invoked
2162
- * @param string $meth the method to be set (leave empty not to set it)
2163
- * @return string the method that will be invoked
2164
- * @access public
2165
- */
2166
- function method($meth='')
2167
- {
2168
- if($meth!='')
2169
- {
2170
- $this->methodname=$meth;
2171
- }
2172
- return $this->methodname;
2173
- }
2174
-
2175
- /**
2176
- * Returns xml representation of the message. XML prologue included
2177
- * @return string the xml representation of the message, xml prologue included
2178
- * @access public
2179
- */
2180
- function serialize($charset_encoding='')
2181
- {
2182
- $this->createPayload($charset_encoding);
2183
- return $this->payload;
2184
- }
2185
-
2186
- /**
2187
- * Add a parameter to the list of parameters to be used upon method invocation
2188
- * @param xmlrpcval $par
2189
- * @return boolean false on failure
2190
- * @access public
2191
- */
2192
- function addParam($par)
2193
- {
2194
- // add check: do not add to self params which are not xmlrpcvals
2195
- if(is_object($par) && is_a($par, 'xmlrpcval'))
2196
- {
2197
- $this->params[]=$par;
2198
- return true;
2199
- }
2200
- else
2201
- {
2202
- return false;
2203
- }
2204
- }
2205
-
2206
- /**
2207
- * Returns the nth parameter in the message. The index zero-based.
2208
- * @param integer $i the index of the parameter to fetch (zero based)
2209
- * @return xmlrpcval the i-th parameter
2210
- * @access public
2211
- */
2212
- function getParam($i) { return $this->params[$i]; }
2213
-
2214
- /**
2215
- * Returns the number of parameters in the messge.
2216
- * @return integer the number of parameters currently set
2217
- * @access public
2218
- */
2219
- function getNumParams() { return count($this->params); }
2220
-
2221
- /**
2222
- * Given an open file handle, read all data available and parse it as axmlrpc response.
2223
- * NB: the file handle is not closed by this function.
2224
- * NNB: might have trouble in rare cases to work on network streams, as we
2225
- * check for a read of 0 bytes instead of feof($fp).
2226
- * But since checking for feof(null) returns false, we would risk an
2227
- * infinite loop in that case, because we cannot trust the caller
2228
- * to give us a valid pointer to an open file...
2229
- * @access public
2230
- * @return xmlrpcresp
2231
- * @todo add 2nd & 3rd param to be passed to ParseResponse() ???
2232
- */
2233
- function &parseResponseFile($fp)
2234
- {
2235
- $ipd='';
2236
- while($data=fread($fp, 32768))
2237
- {
2238
- $ipd.=$data;
2239
- }
2240
- //fclose($fp);
2241
- $r =& $this->parseResponse($ipd);
2242
- return $r;
2243
- }
2244
-
2245
- /**
2246
- * Parses HTTP headers and separates them from data.
2247
- * @access private
2248
- */
2249
- function &parseResponseHeaders(&$data, $headers_processed=false)
2250
- {
2251
- // Support "web-proxy-tunelling" connections for https through proxies
2252
- if(preg_match('/^HTTP\/1\.[0-1] 200 Connection established/', $data))
2253
- {
2254
- // Look for CR/LF or simple LF as line separator,
2255
- // (even though it is not valid http)
2256
- $pos = strpos($data,"\r\n\r\n");
2257
- if($pos || is_int($pos))
2258
- {
2259
- $bd = $pos+4;
2260
- }
2261
- else
2262
- {
2263
- $pos = strpos($data,"\n\n");
2264
- if($pos || is_int($pos))
2265
- {
2266
- $bd = $pos+2;
2267
- }
2268
- else
2269
- {
2270
- // No separation between response headers and body: fault?
2271
- $bd = 0;
2272
- }
2273
- }
2274
- if ($bd)
2275
- {
2276
- // this filters out all http headers from proxy.
2277
- // maybe we could take them into account, too?
2278
- $data = substr($data, $bd);
2279
- }
2280
- else
2281
- {
2282
- error_log('XML-RPC: '.__METHOD__.': HTTPS via proxy error, tunnel connection possibly failed');
2283
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)');
2284
- return $r;
2285
- }
2286
- }
2287
-
2288
- // Strip HTTP 1.1 100 Continue header if present
2289
- while(preg_match('/^HTTP\/1\.1 1[0-9]{2} /', $data))
2290
- {
2291
- $pos = strpos($data, 'HTTP', 12);
2292
- // server sent a Continue header without any (valid) content following...
2293
- // give the client a chance to know it
2294
- if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
2295
- {
2296
- break;
2297
- }
2298
- $data = substr($data, $pos);
2299
- }
2300
- if(!preg_match('/^HTTP\/[0-9.]+ 200 /', $data))
2301
- {
2302
- $errstr= substr($data, 0, strpos($data, "\n")-1);
2303
- error_log('XML-RPC: '.__METHOD__.': HTTP error, got response: ' .$errstr);
2304
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')');
2305
- return $r;
2306
- }
2307
-
2308
- $GLOBALS['_xh']['headers'] = array();
2309
- $GLOBALS['_xh']['cookies'] = array();
2310
-
2311
- // be tolerant to usage of \n instead of \r\n to separate headers and data
2312
- // (even though it is not valid http)
2313
- $pos = strpos($data,"\r\n\r\n");
2314
- if($pos || is_int($pos))
2315
- {
2316
- $bd = $pos+4;
2317
- }
2318
- else
2319
- {
2320
- $pos = strpos($data,"\n\n");
2321
- if($pos || is_int($pos))
2322
- {
2323
- $bd = $pos+2;
2324
- }
2325
- else
2326
- {
2327
- // No separation between response headers and body: fault?
2328
- // we could take some action here instead of going on...
2329
- $bd = 0;
2330
- }
2331
- }
2332
- // be tolerant to line endings, and extra empty lines
2333
- $ar = preg_split("/\r?\n/", trim(substr($data, 0, $pos)));
2334
- while(list(,$line) = @each($ar))
2335
- {
2336
- // take care of multi-line headers and cookies
2337
- $arr = explode(':',$line,2);
2338
- if(count($arr) > 1)
2339
- {
2340
- $header_name = strtolower(trim($arr[0]));
2341
- /// @todo some other headers (the ones that allow a CSV list of values)
2342
- /// do allow many values to be passed using multiple header lines.
2343
- /// We should add content to $GLOBALS['_xh']['headers'][$header_name]
2344
- /// instead of replacing it for those...
2345
- if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
2346
- {
2347
- if ($header_name == 'set-cookie2')
2348
- {
2349
- // version 2 cookies:
2350
- // there could be many cookies on one line, comma separated
2351
- $cookies = explode(',', $arr[1]);
2352
- }
2353
- else
2354
- {
2355
- $cookies = array($arr[1]);
2356
- }
2357
- foreach ($cookies as $cookie)
2358
- {
2359
- // glue together all received cookies, using a comma to separate them
2360
- // (same as php does with getallheaders())
2361
- if (isset($GLOBALS['_xh']['headers'][$header_name]))
2362
- $GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie);
2363
- else
2364
- $GLOBALS['_xh']['headers'][$header_name] = trim($cookie);
2365
- // parse cookie attributes, in case user wants to correctly honour them
2366
- // feature creep: only allow rfc-compliant cookie attributes?
2367
- // @todo support for server sending multiple time cookie with same name, but using different PATHs
2368
- $cookie = explode(';', $cookie);
2369
- foreach ($cookie as $pos => $val)
2370
- {
2371
- $val = explode('=', $val, 2);
2372
- $tag = trim($val[0]);
2373
- $val = trim(@$val[1]);
2374
- /// @todo with version 1 cookies, we should strip leading and trailing " chars
2375
- if ($pos == 0)
2376
- {
2377
- $cookiename = $tag;
2378
- $GLOBALS['_xh']['cookies'][$tag] = array();
2379
- $GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val);
2380
- }
2381
- else
2382
- {
2383
- if ($tag != 'value')
2384
- {
2385
- $GLOBALS['_xh']['cookies'][$cookiename][$tag] = $val;
2386
- }
2387
- }
2388
- }
2389
- }
2390
- }
2391
- else
2392
- {
2393
- $GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]);
2394
- }
2395
- }
2396
- elseif(isset($header_name))
2397
- {
2398
- /// @todo version1 cookies might span multiple lines, thus breaking the parsing above
2399
- $GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line);
2400
- }
2401
- }
2402
-
2403
- $data = substr($data, $bd);
2404
-
2405
- if($this->debug && count($GLOBALS['_xh']['headers']))
2406
- {
2407
- print '<PRE>';
2408
- foreach($GLOBALS['_xh']['headers'] as $header => $value)
2409
- {
2410
- print htmlentities("HEADER: $header: $value\n");
2411
- }
2412
- foreach($GLOBALS['_xh']['cookies'] as $header => $value)
2413
- {
2414
- print htmlentities("COOKIE: $header={$value['value']}\n");
2415
- }
2416
- print "</PRE>\n";
2417
- }
2418
-
2419
- // if CURL was used for the call, http headers have been processed,
2420
- // and dechunking + reinflating have been carried out
2421
- if(!$headers_processed)
2422
- {
2423
- // Decode chunked encoding sent by http 1.1 servers
2424
- if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked')
2425
- {
2426
- if(!$data = decode_chunked($data))
2427
- {
2428
- error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to rebuild the chunked data received from server');
2429
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']);
2430
- return $r;
2431
- }
2432
- }
2433
-
2434
- // Decode gzip-compressed stuff
2435
- // code shamelessly inspired from nusoap library by Dietrich Ayala
2436
- if(isset($GLOBALS['_xh']['headers']['content-encoding']))
2437
- {
2438
- $GLOBALS['_xh']['headers']['content-encoding'] = str_replace('x-', '', $GLOBALS['_xh']['headers']['content-encoding']);
2439
- if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip')
2440
- {
2441
- // if decoding works, use it. else assume data wasn't gzencoded
2442
- if(function_exists('gzinflate'))
2443
- {
2444
- if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data))
2445
- {
2446
- $data = $degzdata;
2447
- if($this->debug)
2448
- print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2449
- }
2450
- elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
2451
- {
2452
- $data = $degzdata;
2453
- if($this->debug)
2454
- print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2455
- }
2456
- else
2457
- {
2458
- error_log('XML-RPC: '.__METHOD__.': errors occurred when trying to decode the deflated data received from server');
2459
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
2460
- return $r;
2461
- }
2462
- }
2463
- else
2464
- {
2465
- error_log('XML-RPC: '.__METHOD__.': the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
2466
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
2467
- return $r;
2468
- }
2469
- }
2470
- }
2471
- } // end of 'if needed, de-chunk, re-inflate response'
2472
-
2473
- // real stupid hack to avoid PHP complaining about returning NULL by ref
2474
- $r = null;
2475
- $r =& $r;
2476
- return $r;
2477
- }
2478
-
2479
- /**
2480
- * Parse the xmlrpc response contained in the string $data and return an xmlrpcresp object.
2481
- * @param string $data the xmlrpc response, eventually including http headers
2482
- * @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and consequent decoding
2483
- * @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
2484
- * @return xmlrpcresp
2485
- * @access public
2486
- */
2487
- function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals')
2488
- {
2489
- if($this->debug)
2490
- {
2491
- //by maHo, replaced htmlspecialchars with htmlentities
2492
- print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
2493
- }
2494
-
2495
- if($data == '')
2496
- {
2497
- error_log('XML-RPC: '.__METHOD__.': no response received from server.');
2498
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
2499
- return $r;
2500
- }
2501
-
2502
- $GLOBALS['_xh']=array();
2503
-
2504
- $raw_data = $data;
2505
- // parse the HTTP headers of the response, if present, and separate them from data
2506
- if(substr($data, 0, 4) == 'HTTP')
2507
- {
2508
- $r =& $this->parseResponseHeaders($data, $headers_processed);
2509
- if ($r)
2510
- {
2511
- // failed processing of HTTP response headers
2512
- // save into response obj the full payload received, for debugging
2513
- $r->raw_data = $data;
2514
- return $r;
2515
- }
2516
- }
2517
- else
2518
- {
2519
- $GLOBALS['_xh']['headers'] = array();
2520
- $GLOBALS['_xh']['cookies'] = array();
2521
- }
2522
-
2523
- if($this->debug)
2524
- {
2525
- $start = strpos($data, '<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2526
- if ($start)
2527
- {
2528
- $start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2529
- $end = strpos($data, '-->', $start);
2530
- $comments = substr($data, $start, $end-$start);
2531
- print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>";
2532
- }
2533
- }
2534
-
2535
- // be tolerant of extra whitespace in response body
2536
- $data = trim($data);
2537
-
2538
- /// @todo return an error msg if $data=='' ?
2539
-
2540
- // be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
2541
- // idea from Luca Mariano <luca.mariano@email.it> originally in PEARified version of the lib
2542
- $pos = strrpos($data, '</methodResponse>');
2543
- if($pos !== false)
2544
- {
2545
- $data = substr($data, 0, $pos+17);
2546
- }
2547
-
2548
- // if user wants back raw xml, give it to him
2549
- if ($return_type == 'xml')
2550
- {
2551
- $r = new xmlrpcresp($data, 0, '', 'xml');
2552
- $r->hdrs = $GLOBALS['_xh']['headers'];
2553
- $r->_cookies = $GLOBALS['_xh']['cookies'];
2554
- $r->raw_data = $raw_data;
2555
- return $r;
2556
- }
2557
-
2558
- // try to 'guestimate' the character encoding of the received response
2559
- $resp_encoding = guess_encoding(@$GLOBALS['_xh']['headers']['content-type'], $data);
2560
-
2561
- $GLOBALS['_xh']['ac']='';
2562
- //$GLOBALS['_xh']['qt']=''; //unused...
2563
- $GLOBALS['_xh']['stack'] = array();
2564
- $GLOBALS['_xh']['valuestack'] = array();
2565
- $GLOBALS['_xh']['isf']=0; // 0 = OK, 1 for xmlrpc fault responses, 2 = invalid xmlrpc
2566
- $GLOBALS['_xh']['isf_reason']='';
2567
- $GLOBALS['_xh']['rt']=''; // 'methodcall or 'methodresponse'
2568
-
2569
- // if response charset encoding is not known / supported, try to use
2570
- // the default encoding and parse the xml anyway, but log a warning...
2571
- if (!in_array($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2572
- // the following code might be better for mb_string enabled installs, but
2573
- // makes the lib about 200% slower...
2574
- //if (!is_valid_charset($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2575
- {
2576
- error_log('XML-RPC: '.__METHOD__.': invalid charset encoding of received response: '.$resp_encoding);
2577
- $resp_encoding = $GLOBALS['xmlrpc_defencoding'];
2578
- }
2579
- $parser = xml_parser_create($resp_encoding);
2580
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
2581
- // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
2582
- // the xml parser to give us back data in the expected charset.
2583
- // What if internal encoding is not in one of the 3 allowed?
2584
- // we use the broadest one, ie. utf8
2585
- // This allows to send data which is native in various charset,
2586
- // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
2587
- if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2588
- {
2589
- xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
2590
- }
2591
- else
2592
- {
2593
- xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
2594
- }
2595
-
2596
- if ($return_type == 'phpvals')
2597
- {
2598
- xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
2599
- }
2600
- else
2601
- {
2602
- xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
2603
- }
2604
-
2605
- xml_set_character_data_handler($parser, 'xmlrpc_cd');
2606
- xml_set_default_handler($parser, 'xmlrpc_dh');
2607
-
2608
- // first error check: xml not well formed
2609
- if(!xml_parse($parser, $data, count($data)))
2610
- {
2611
- // thanks to Peter Kocks <peter.kocks@baygate.com>
2612
- if((xml_get_current_line_number($parser)) == 1)
2613
- {
2614
- $errstr = 'XML error at line 1, check URL';
2615
- }
2616
- else
2617
- {
2618
- $errstr = sprintf('XML error: %s at line %d, column %d',
2619
- xml_error_string(xml_get_error_code($parser)),
2620
- xml_get_current_line_number($parser), xml_get_current_column_number($parser));
2621
- }
2622
- error_log($errstr);
2623
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')');
2624
- xml_parser_free($parser);
2625
- if($this->debug)
2626
- {
2627
- print $errstr;
2628
- }
2629
- $r->hdrs = $GLOBALS['_xh']['headers'];
2630
- $r->_cookies = $GLOBALS['_xh']['cookies'];
2631
- $r->raw_data = $raw_data;
2632
- return $r;
2633
- }
2634
- xml_parser_free($parser);
2635
- // second error check: xml well formed but not xml-rpc compliant
2636
- if ($GLOBALS['_xh']['isf'] > 1)
2637
- {
2638
- if ($this->debug)
2639
- {
2640
- /// @todo echo something for user?
2641
- }
2642
-
2643
- $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2644
- $GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
2645
- }
2646
- // third error check: parsing of the response has somehow gone boink.
2647
- // NB: shall we omit this check, since we trust the parsing code?
2648
- elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value']))
2649
- {
2650
- // something odd has happened
2651
- // and it's time to generate a client side error
2652
- // indicating something odd went on
2653
- $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2654
- $GLOBALS['xmlrpcstr']['invalid_return']);
2655
- }
2656
- else
2657
- {
2658
- if ($this->debug)
2659
- {
2660
- print "<PRE>---PARSED---\n";
2661
- // somehow htmlentities chokes on var_export, and some full html string...
2662
- //print htmlentitites(var_export($GLOBALS['_xh']['value'], true));
2663
- print htmlspecialchars(var_export($GLOBALS['_xh']['value'], true));
2664
- print "\n---END---</PRE>";
2665
- }
2666
-
2667
- // note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object.
2668
- $v =& $GLOBALS['_xh']['value'];
2669
-
2670
- if($GLOBALS['_xh']['isf'])
2671
- {
2672
- /// @todo we should test here if server sent an int and a string,
2673
- /// and/or coerce them into such...
2674
- if ($return_type == 'xmlrpcvals')
2675
- {
2676
- $errno_v = $v->structmem('faultCode');
2677
- $errstr_v = $v->structmem('faultString');
2678
- $errno = $errno_v->scalarval();
2679
- $errstr = $errstr_v->scalarval();
2680
- }
2681
- else
2682
- {
2683
- $errno = $v['faultCode'];
2684
- $errstr = $v['faultString'];
2685
- }
2686
-
2687
- if($errno == 0)
2688
- {
2689
- // FAULT returned, errno needs to reflect that
2690
- $errno = -1;
2691
- }
2692
-
2693
- $r = new xmlrpcresp(0, $errno, $errstr);
2694
- }
2695
- else
2696
- {
2697
- $r=new xmlrpcresp($v, 0, '', $return_type);
2698
- }
2699
- }
2700
-
2701
- $r->hdrs = $GLOBALS['_xh']['headers'];
2702
- $r->_cookies = $GLOBALS['_xh']['cookies'];
2703
- $r->raw_data = $raw_data;
2704
- return $r;
2705
- }
2706
- }
2707
-
2708
- class xmlrpcval
2709
- {
2710
- var $me=array();
2711
- var $mytype=0;
2712
- var $_php_class=null;
2713
-
2714
- /**
2715
- * @param mixed $val
2716
- * @param string $type any valid xmlrpc type name (lowercase). If null, 'string' is assumed
2717
- */
2718
- function xmlrpcval($val=-1, $type='')
2719
- {
2720
- /// @todo: optimization creep - do not call addXX, do it all inline.
2721
- /// downside: booleans will not be coerced anymore
2722
- if($val!==-1 || $type!='')
2723
- {
2724
- // optimization creep: inlined all work done by constructor
2725
- switch($type)
2726
- {
2727
- case '':
2728
- $this->mytype=1;
2729
- $this->me['string']=$val;
2730
- break;
2731
- case 'i4':
2732
- case 'int':
2733
- case 'double':
2734
- case 'string':
2735
- case 'boolean':
2736
- case 'dateTime.iso8601':
2737
- case 'base64':
2738
- case 'null':
2739
- $this->mytype=1;
2740
- $this->me[$type]=$val;
2741
- break;
2742
- case 'array':
2743
- $this->mytype=2;
2744
- $this->me['array']=$val;
2745
- break;
2746
- case 'struct':
2747
- $this->mytype=3;
2748
- $this->me['struct']=$val;
2749
- break;
2750
- default:
2751
- error_log("XML-RPC: ".__METHOD__.": not a known type ($type)");
2752
- }
2753
- /*if($type=='')
2754
- {
2755
- $type='string';
2756
- }
2757
- if($GLOBALS['xmlrpcTypes'][$type]==1)
2758
- {
2759
- $this->addScalar($val,$type);
2760
- }
2761
- elseif($GLOBALS['xmlrpcTypes'][$type]==2)
2762
- {
2763
- $this->addArray($val);
2764
- }
2765
- elseif($GLOBALS['xmlrpcTypes'][$type]==3)
2766
- {
2767
- $this->addStruct($val);
2768
- }*/
2769
- }
2770
- }
2771
-
2772
- /**
2773
- * Add a single php value to an (unitialized) xmlrpcval
2774
- * @param mixed $val
2775
- * @param string $type
2776
- * @return int 1 or 0 on failure
2777
- */
2778
- function addScalar($val, $type='string')
2779
- {
2780
- $typeof=@$GLOBALS['xmlrpcTypes'][$type];
2781
- if($typeof!=1)
2782
- {
2783
- error_log("XML-RPC: ".__METHOD__.": not a scalar type ($type)");
2784
- return 0;
2785
- }
2786
-
2787
- // coerce booleans into correct values
2788
- // NB: we should either do it for datetimes, integers and doubles, too,
2789
- // or just plain remove this check, implemented on booleans only...
2790
- if($type==$GLOBALS['xmlrpcBoolean'])
2791
- {
2792
- if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
2793
- {
2794
- $val=true;
2795
- }
2796
- else
2797
- {
2798
- $val=false;
2799
- }
2800
- }
2801
-
2802
- switch($this->mytype)
2803
- {
2804
- case 1:
2805
- error_log('XML-RPC: '.__METHOD__.': scalar xmlrpcval can have only one value');
2806
- return 0;
2807
- case 3:
2808
- error_log('XML-RPC: '.__METHOD__.': cannot add anonymous scalar to struct xmlrpcval');
2809
- return 0;
2810
- case 2:
2811
- // we're adding a scalar value to an array here
2812
- //$ar=$this->me['array'];
2813
- //$ar[]=new xmlrpcval($val, $type);
2814
- //$this->me['array']=$ar;
2815
- // Faster (?) avoid all the costly array-copy-by-val done here...
2816
- $this->me['array'][]=new xmlrpcval($val, $type);
2817
- return 1;
2818
- default:
2819
- // a scalar, so set the value and remember we're scalar
2820
- $this->me[$type]=$val;
2821
- $this->mytype=$typeof;
2822
- return 1;
2823
- }
2824
- }
2825
-
2826
- /**
2827
- * Add an array of xmlrpcval objects to an xmlrpcval
2828
- * @param array $vals
2829
- * @return int 1 or 0 on failure
2830
- * @access public
2831
- *
2832
- * @todo add some checking for $vals to be an array of xmlrpcvals?
2833
- */
2834
- function addArray($vals)
2835
- {
2836
- if($this->mytype==0)
2837
- {
2838
- $this->mytype=$GLOBALS['xmlrpcTypes']['array'];
2839
- $this->me['array']=$vals;
2840
- return 1;
2841
- }
2842
- elseif($this->mytype==2)
2843
- {
2844
- // we're adding to an array here
2845
- $this->me['array'] = array_merge($this->me['array'], $vals);
2846
- return 1;
2847
- }
2848
- else
2849
- {
2850
- error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2851
- return 0;
2852
- }
2853
- }
2854
-
2855
- /**
2856
- * Add an array of named xmlrpcval objects to an xmlrpcval
2857
- * @param array $vals
2858
- * @return int 1 or 0 on failure
2859
- * @access public
2860
- *
2861
- * @todo add some checking for $vals to be an array?
2862
- */
2863
- function addStruct($vals)
2864
- {
2865
- if($this->mytype==0)
2866
- {
2867
- $this->mytype=$GLOBALS['xmlrpcTypes']['struct'];
2868
- $this->me['struct']=$vals;
2869
- return 1;
2870
- }
2871
- elseif($this->mytype==3)
2872
- {
2873
- // we're adding to a struct here
2874
- $this->me['struct'] = array_merge($this->me['struct'], $vals);
2875
- return 1;
2876
- }
2877
- else
2878
- {
2879
- error_log('XML-RPC: '.__METHOD__.': already initialized as a [' . $this->kindOf() . ']');
2880
- return 0;
2881
- }
2882
- }
2883
-
2884
- // poor man's version of print_r ???
2885
- // DEPRECATED!
2886
- function dump($ar)
2887
- {
2888
- foreach($ar as $key => $val)
2889
- {
2890
- echo "$key => $val<br />";
2891
- if($key == 'array')
2892
- {
2893
- while(list($key2, $val2) = each($val))
2894
- {
2895
- echo "-- $key2 => $val2<br />";
2896
- }
2897
- }
2898
- }
2899
- }
2900
-
2901
- /**
2902
- * Returns a string containing "struct", "array" or "scalar" describing the base type of the value
2903
- * @return string
2904
- * @access public
2905
- */
2906
- function kindOf()
2907
- {
2908
- switch($this->mytype)
2909
- {
2910
- case 3:
2911
- return 'struct';
2912
- break;
2913
- case 2:
2914
- return 'array';
2915
- break;
2916
- case 1:
2917
- return 'scalar';
2918
- break;
2919
- default:
2920
- return 'undef';
2921
- }
2922
- }
2923
-
2924
- /**
2925
- * @access private
2926
- */
2927
- function serializedata($typ, $val, $charset_encoding='')
2928
- {
2929
- $rs='';
2930
- switch(@$GLOBALS['xmlrpcTypes'][$typ])
2931
- {
2932
- case 1:
2933
- switch($typ)
2934
- {
2935
- case $GLOBALS['xmlrpcBase64']:
2936
- $rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
2937
- break;
2938
- case $GLOBALS['xmlrpcBoolean']:
2939
- $rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
2940
- break;
2941
- case $GLOBALS['xmlrpcString']:
2942
- // G. Giunta 2005/2/13: do NOT use htmlentities, since
2943
- // it will produce named html entities, which are invalid xml
2944
- $rs.="<${typ}>" . xmlrpc_encode_entitites($val, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding). "</${typ}>";
2945
- break;
2946
- case $GLOBALS['xmlrpcInt']:
2947
- case $GLOBALS['xmlrpcI4']:
2948
- $rs.="<${typ}>".(int)$val."</${typ}>";
2949
- break;
2950
- case $GLOBALS['xmlrpcDouble']:
2951
- // avoid using standard conversion of float to string because it is locale-dependent,
2952
- // and also because the xmlrpc spec forbids exponential notation.
2953
- // sprintf('%F') could be most likely ok but it fails eg. on 2e-14.
2954
- // The code below tries its best at keeping max precision while avoiding exp notation,
2955
- // but there is of course no limit in the number of decimal places to be used...
2956
- $rs.="<${typ}>".preg_replace('/\\.?0+$/','',number_format((double)$val, 128, '.', ''))."</${typ}>";
2957
- break;
2958
- case $GLOBALS['xmlrpcDateTime']:
2959
- if (is_string($val))
2960
- {
2961
- $rs.="<${typ}>${val}</${typ}>";
2962
- }
2963
- else if(is_a($val, 'DateTime'))
2964
- {
2965
- $rs.="<${typ}>".$val->format('Ymd\TH:i:s')."</${typ}>";
2966
- }
2967
- else if(is_int($val))
2968
- {
2969
- $rs.="<${typ}>".strftime("%Y%m%dT%H:%M:%S", $val)."</${typ}>";
2970
- }
2971
- else
2972
- {
2973
- // not really a good idea here: but what shall we output anyway? left for backward compat...
2974
- $rs.="<${typ}>${val}</${typ}>";
2975
- }
2976
- break;
2977
- case $GLOBALS['xmlrpcNull']:
2978
- if ($GLOBALS['xmlrpc_null_apache_encoding'])
2979
- {
2980
- $rs.="<ex:nil/>";
2981
- }
2982
- else
2983
- {
2984
- $rs.="<nil/>";
2985
- }
2986
- break;
2987
- default:
2988
- // no standard type value should arrive here, but provide a possibility
2989
- // for xmlrpcvals of unknown type...
2990
- $rs.="<${typ}>${val}</${typ}>";
2991
- }
2992
- break;
2993
- case 3:
2994
- // struct
2995
- if ($this->_php_class)
2996
- {
2997
- $rs.='<struct php_class="' . $this->_php_class . "\">\n";
2998
- }
2999
- else
3000
- {
3001
- $rs.="<struct>\n";
3002
- }
3003
- foreach($val as $key2 => $val2)
3004
- {
3005
- $rs.='<member><name>'.xmlrpc_encode_entitites($key2, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding)."</name>\n";
3006
- //$rs.=$this->serializeval($val2);
3007
- $rs.=$val2->serialize($charset_encoding);
3008
- $rs.="</member>\n";
3009
- }
3010
- $rs.='</struct>';
3011
- break;
3012
- case 2:
3013
- // array
3014
- $rs.="<array>\n<data>\n";
3015
- for($i=0; $i<count($val); $i++)
3016
- {
3017
- //$rs.=$this->serializeval($val[$i]);
3018
- $rs.=$val[$i]->serialize($charset_encoding);
3019
- }
3020
- $rs.="</data>\n</array>";
3021
- break;
3022
- default:
3023
- break;
3024
- }
3025
- return $rs;
3026
- }
3027
-
3028
- /**
3029
- * Returns xml representation of the value. XML prologue not included
3030
- * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
3031
- * @return string
3032
- * @access public
3033
- */
3034
- function serialize($charset_encoding='')
3035
- {
3036
- // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3037
- //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3038
- //{
3039
- reset($this->me);
3040
- list($typ, $val) = each($this->me);
3041
- return '<value>' . $this->serializedata($typ, $val, $charset_encoding) . "</value>\n";
3042
- //}
3043
- }
3044
-
3045
- // DEPRECATED
3046
- function serializeval($o)
3047
- {
3048
- // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3049
- //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3050
- //{
3051
- $ar=$o->me;
3052
- reset($ar);
3053
- list($typ, $val) = each($ar);
3054
- return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
3055
- //}
3056
- }
3057
-
3058
- /**
3059
- * Checks wheter a struct member with a given name is present.
3060
- * Works only on xmlrpcvals of type struct.
3061
- * @param string $m the name of the struct member to be looked up
3062
- * @return boolean
3063
- * @access public
3064
- */
3065
- function structmemexists($m)
3066
- {
3067
- return array_key_exists($m, $this->me['struct']);
3068
- }
3069
-
3070
- /**
3071
- * Returns the value of a given struct member (an xmlrpcval object in itself).
3072
- * Will raise a php warning if struct member of given name does not exist
3073
- * @param string $m the name of the struct member to be looked up
3074
- * @return xmlrpcval
3075
- * @access public
3076
- */
3077
- function structmem($m)
3078
- {
3079
- return $this->me['struct'][$m];
3080
- }
3081
-
3082
- /**
3083
- * Reset internal pointer for xmlrpcvals of type struct.
3084
- * @access public
3085
- */
3086
- function structreset()
3087
- {
3088
- reset($this->me['struct']);
3089
- }
3090
-
3091
- /**
3092
- * Return next member element for xmlrpcvals of type struct.
3093
- * @return xmlrpcval
3094
- * @access public
3095
- */
3096
- function structeach()
3097
- {
3098
- return each($this->me['struct']);
3099
- }
3100
-
3101
- // DEPRECATED! this code looks like it is very fragile and has not been fixed
3102
- // for a long long time. Shall we remove it for 2.0?
3103
- function getval()
3104
- {
3105
- // UNSTABLE
3106
- reset($this->me);
3107
- list($a,$b)=each($this->me);
3108
- // contributed by I Sofer, 2001-03-24
3109
- // add support for nested arrays to scalarval
3110
- // i've created a new method here, so as to
3111
- // preserve back compatibility
3112
-
3113
- if(is_array($b))
3114
- {
3115
- @reset($b);
3116
- while(list($id,$cont) = @each($b))
3117
- {
3118
- $b[$id] = $cont->scalarval();
3119
- }
3120
- }
3121
-
3122
- // add support for structures directly encoding php objects
3123
- if(is_object($b))
3124
- {
3125
- $t = get_object_vars($b);
3126
- @reset($t);
3127
- while(list($id,$cont) = @each($t))
3128
- {
3129
- $t[$id] = $cont->scalarval();
3130
- }
3131
- @reset($t);
3132
- while(list($id,$cont) = @each($t))
3133
- {
3134
- @$b->$id = $cont;
3135
- }
3136
- }
3137
- // end contrib
3138
- return $b;
3139
- }
3140
-
3141
- /**
3142
- * Returns the value of a scalar xmlrpcval
3143
- * @return mixed
3144
- * @access public
3145
- */
3146
- function scalarval()
3147
- {
3148
- reset($this->me);
3149
- list(,$b)=each($this->me);
3150
- return $b;
3151
- }
3152
-
3153
- /**
3154
- * Returns the type of the xmlrpcval.
3155
- * For integers, 'int' is always returned in place of 'i4'
3156
- * @return string
3157
- * @access public
3158
- */
3159
- function scalartyp()
3160
- {
3161
- reset($this->me);
3162
- list($a,)=each($this->me);
3163
- if($a==$GLOBALS['xmlrpcI4'])
3164
- {
3165
- $a=$GLOBALS['xmlrpcInt'];
3166
- }
3167
- return $a;
3168
- }
3169
-
3170
- /**
3171
- * Returns the m-th member of an xmlrpcval of struct type
3172
- * @param integer $m the index of the value to be retrieved (zero based)
3173
- * @return xmlrpcval
3174
- * @access public
3175
- */
3176
- function arraymem($m)
3177
- {
3178
- return $this->me['array'][$m];
3179
- }
3180
-
3181
- /**
3182
- * Returns the number of members in an xmlrpcval of array type
3183
- * @return integer
3184
- * @access public
3185
- */
3186
- function arraysize()
3187
- {
3188
- return count($this->me['array']);
3189
- }
3190
-
3191
- /**
3192
- * Returns the number of members in an xmlrpcval of struct type
3193
- * @return integer
3194
- * @access public
3195
- */
3196
- function structsize()
3197
- {
3198
- return count($this->me['struct']);
3199
- }
3200
- }
3201
-
3202
-
3203
- // date helpers
3204
-
3205
- /**
3206
- * Given a timestamp, return the corresponding ISO8601 encoded string.
3207
- *
3208
- * Really, timezones ought to be supported
3209
- * but the XML-RPC spec says:
3210
- *
3211
- * "Don't assume a timezone. It should be specified by the server in its
3212
- * documentation what assumptions it makes about timezones."
3213
- *
3214
- * These routines always assume localtime unless
3215
- * $utc is set to 1, in which case UTC is assumed
3216
- * and an adjustment for locale is made when encoding
3217
- *
3218
- * @param int $timet (timestamp)
3219
- * @param int $utc (0 or 1)
3220
- * @return string
3221
- */
3222
- function iso8601_encode($timet, $utc=0)
3223
- {
3224
- if(!$utc)
3225
- {
3226
- $t=strftime("%Y%m%dT%H:%M:%S", $timet);
3227
- }
3228
- else
3229
- {
3230
- if(function_exists('gmstrftime'))
3231
- {
3232
- // gmstrftime doesn't exist in some versions
3233
- // of PHP
3234
- $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
3235
- }
3236
- else
3237
- {
3238
- $t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
3239
- }
3240
- }
3241
- return $t;
3242
- }
3243
-
3244
- /**
3245
- * Given an ISO8601 date string, return a timet in the localtime, or UTC
3246
- * @param string $idate
3247
- * @param int $utc either 0 or 1
3248
- * @return int (datetime)
3249
- */
3250
- function iso8601_decode($idate, $utc=0)
3251
- {
3252
- $t=0;
3253
- if(preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs))
3254
- {
3255
- if($utc)
3256
- {
3257
- $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3258
- }
3259
- else
3260
- {
3261
- $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3262
- }
3263
- }
3264
- return $t;
3265
- }
3266
-
3267
- /**
3268
- * Takes an xmlrpc value in PHP xmlrpcval object format and translates it into native PHP types.
3269
- *
3270
- * Works with xmlrpc message objects as input, too.
3271
- *
3272
- * Given proper options parameter, can rebuild generic php object instances
3273
- * (provided those have been encoded to xmlrpc format using a corresponding
3274
- * option in php_xmlrpc_encode())
3275
- * PLEASE NOTE that rebuilding php objects involves calling their constructor function.
3276
- * This means that the remote communication end can decide which php code will
3277
- * get executed on your server, leaving the door possibly open to 'php-injection'
3278
- * style of attacks (provided you have some classes defined on your server that
3279
- * might wreak havoc if instances are built outside an appropriate context).
3280
- * Make sure you trust the remote server/client before eanbling this!
3281
- *
3282
- * @author Dan Libby (dan@libby.com)
3283
- *
3284
- * @param xmlrpcval $xmlrpc_val
3285
- * @param array $options if 'decode_php_objs' is set in the options array, xmlrpc structs can be decoded into php objects; if 'dates_as_objects' is set xmlrpc datetimes are decoded as php DateTime objects (standard is
3286
- * @return mixed
3287
- */
3288
- function php_xmlrpc_decode($xmlrpc_val, $options=array())
3289
- {
3290
- switch($xmlrpc_val->kindOf())
3291
- {
3292
- case 'scalar':
3293
- if (in_array('extension_api', $options))
3294
- {
3295
- reset($xmlrpc_val->me);
3296
- list($typ,$val) = each($xmlrpc_val->me);
3297
- switch ($typ)
3298
- {
3299
- case 'dateTime.iso8601':
3300
- $xmlrpc_val->scalar = $val;
3301
- $xmlrpc_val->xmlrpc_type = 'datetime';
3302
- $xmlrpc_val->timestamp = iso8601_decode($val);
3303
- return $xmlrpc_val;
3304
- case 'base64':
3305
- $xmlrpc_val->scalar = $val;
3306
- $xmlrpc_val->type = $typ;
3307
- return $xmlrpc_val;
3308
- default:
3309
- return $xmlrpc_val->scalarval();
3310
- }
3311
- }
3312
- if (in_array('dates_as_objects', $options) && $xmlrpc_val->scalartyp() == 'dateTime.iso8601')
3313
- {
3314
- // we return a Datetime object instead of a string
3315
- // since now the constructor of xmlrpcval accepts safely strings, ints and datetimes,
3316
- // we cater to all 3 cases here
3317
- $out = $xmlrpc_val->scalarval();
3318
- if (is_string($out))
3319
- {
3320
- $out = strtotime($out);
3321
- }
3322
- if (is_int($out))
3323
- {
3324
- $result = new Datetime();
3325
- $result->setTimestamp($out);
3326
- return $result;
3327
- }
3328
- elseif (is_a($out, 'Datetime'))
3329
- {
3330
- return $out;
3331
- }
3332
- }
3333
- return $xmlrpc_val->scalarval();
3334
- case 'array':
3335
- $size = $xmlrpc_val->arraysize();
3336
- $arr = array();
3337
- for($i = 0; $i < $size; $i++)
3338
- {
3339
- $arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i), $options);
3340
- }
3341
- return $arr;
3342
- case 'struct':
3343
- $xmlrpc_val->structreset();
3344
- // If user said so, try to rebuild php objects for specific struct vals.
3345
- /// @todo should we raise a warning for class not found?
3346
- // shall we check for proper subclass of xmlrpcval instead of
3347
- // presence of _php_class to detect what we can do?
3348
- if (in_array('decode_php_objs', $options) && $xmlrpc_val->_php_class != ''
3349
- && class_exists($xmlrpc_val->_php_class))
3350
- {
3351
- $obj = @new $xmlrpc_val->_php_class;
3352
- while(list($key,$value)=$xmlrpc_val->structeach())
3353
- {
3354
- $obj->$key = php_xmlrpc_decode($value, $options);
3355
- }
3356
- return $obj;
3357
- }
3358
- else
3359
- {
3360
- $arr = array();
3361
- while(list($key,$value)=$xmlrpc_val->structeach())
3362
- {
3363
- $arr[$key] = php_xmlrpc_decode($value, $options);
3364
- }
3365
- return $arr;
3366
- }
3367
- case 'msg':
3368
- $paramcount = $xmlrpc_val->getNumParams();
3369
- $arr = array();
3370
- for($i = 0; $i < $paramcount; $i++)
3371
- {
3372
- $arr[] = php_xmlrpc_decode($xmlrpc_val->getParam($i));
3373
- }
3374
- return $arr;
3375
- }
3376
- }
3377
-
3378
- // This constant left here only for historical reasons...
3379
- // it was used to decide if we have to define xmlrpc_encode on our own, but
3380
- // we do not do it anymore
3381
- if(function_exists('xmlrpc_decode'))
3382
- {
3383
- define('XMLRPC_EPI_ENABLED','1');
3384
- }
3385
- else
3386
- {
3387
- define('XMLRPC_EPI_ENABLED','0');
3388
- }
3389
-
3390
- /**
3391
- * Takes native php types and encodes them into xmlrpc PHP object format.
3392
- * It will not re-encode xmlrpcval objects.
3393
- *
3394
- * Feature creep -- could support more types via optional type argument
3395
- * (string => datetime support has been added, ??? => base64 not yet)
3396
- *
3397
- * If given a proper options parameter, php object instances will be encoded
3398
- * into 'special' xmlrpc values, that can later be decoded into php objects
3399
- * by calling php_xmlrpc_decode() with a corresponding option
3400
- *
3401
- * @author Dan Libby (dan@libby.com)
3402
- *
3403
- * @param mixed $php_val the value to be converted into an xmlrpcval object
3404
- * @param array $options can include 'encode_php_objs', 'auto_dates', 'null_extension' or 'extension_api'
3405
- * @return xmlrpcval
3406
- */
3407
- function php_xmlrpc_encode($php_val, $options=array())
3408
- {
3409
- $type = gettype($php_val);
3410
- switch($type)
3411
- {
3412
- case 'string':
3413
- if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $php_val))
3414
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']);
3415
- else
3416
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcString']);
3417
- break;
3418
- case 'integer':
3419
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']);
3420
- break;
3421
- case 'double':
3422
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']);
3423
- break;
3424
- // <G_Giunta_2001-02-29>
3425
- // Add support for encoding/decoding of booleans, since they are supported in PHP
3426
- case 'boolean':
3427
- $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']);
3428
- break;
3429
- // </G_Giunta_2001-02-29>
3430
- case 'array':
3431
- // PHP arrays can be encoded to either xmlrpc structs or arrays,
3432
- // depending on wheter they are hashes or plain 0..n integer indexed
3433
- // A shorter one-liner would be
3434
- // $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
3435
- // but execution time skyrockets!
3436
- $j = 0;
3437
- $arr = array();
3438
- $ko = false;
3439
- foreach($php_val as $key => $val)
3440
- {
3441
- $arr[$key] = php_xmlrpc_encode($val, $options);
3442
- if(!$ko && $key !== $j)
3443
- {
3444
- $ko = true;
3445
- }
3446
- $j++;
3447
- }
3448
- if($ko)
3449
- {
3450
- $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3451
- }
3452
- else
3453
- {
3454
- $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcArray']);
3455
- }
3456
- break;
3457
- case 'object':
3458
- if(is_a($php_val, 'xmlrpcval'))
3459
- {
3460
- $xmlrpc_val = $php_val;
3461
- }
3462
- else if(is_a($php_val, 'DateTime'))
3463
- {
3464
- $xmlrpc_val = new xmlrpcval($php_val->format('Ymd\TH:i:s'), $GLOBALS['xmlrpcStruct']);
3465
- }
3466
- else
3467
- {
3468
- $arr = array();
3469
- reset($php_val);
3470
- while(list($k,$v) = each($php_val))
3471
- {
3472
- $arr[$k] = php_xmlrpc_encode($v, $options);
3473
- }
3474
- $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3475
- if (in_array('encode_php_objs', $options))
3476
- {
3477
- // let's save original class name into xmlrpcval:
3478
- // might be useful later on...
3479
- $xmlrpc_val->_php_class = get_class($php_val);
3480
- }
3481
- }
3482
- break;
3483
- case 'NULL':
3484
- if (in_array('extension_api', $options))
3485
- {
3486
- $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcString']);
3487
- }
3488
- else if (in_array('null_extension', $options))
3489
- {
3490
- $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcNull']);
3491
- }
3492
- else
3493
- {
3494
- $xmlrpc_val = new xmlrpcval();
3495
- }
3496
- break;
3497
- case 'resource':
3498
- if (in_array('extension_api', $options))
3499
- {
3500
- $xmlrpc_val = new xmlrpcval((int)$php_val, $GLOBALS['xmlrpcInt']);
3501
- }
3502
- else
3503
- {
3504
- $xmlrpc_val = new xmlrpcval();
3505
- }
3506
- // catch "user function", "unknown type"
3507
- default:
3508
- // giancarlo pinerolo <ping@alt.it>
3509
- // it has to return
3510
- // an empty object in case, not a boolean.
3511
- $xmlrpc_val = new xmlrpcval();
3512
- break;
3513
- }
3514
- return $xmlrpc_val;
3515
- }
3516
-
3517
- /**
3518
- * Convert the xml representation of a method response, method request or single
3519
- * xmlrpc value into the appropriate object (a.k.a. deserialize)
3520
- * @param string $xml_val
3521
- * @param array $options
3522
- * @return mixed false on error, or an instance of either xmlrpcval, xmlrpcmsg or xmlrpcresp
3523
- */
3524
- function php_xmlrpc_decode_xml($xml_val, $options=array())
3525
- {
3526
- $GLOBALS['_xh'] = array();
3527
- $GLOBALS['_xh']['ac'] = '';
3528
- $GLOBALS['_xh']['stack'] = array();
3529
- $GLOBALS['_xh']['valuestack'] = array();
3530
- $GLOBALS['_xh']['params'] = array();
3531
- $GLOBALS['_xh']['pt'] = array();
3532
- $GLOBALS['_xh']['isf'] = 0;
3533
- $GLOBALS['_xh']['isf_reason'] = '';
3534
- $GLOBALS['_xh']['method'] = false;
3535
- $GLOBALS['_xh']['rt'] = '';
3536
- /// @todo 'guestimate' encoding
3537
- $parser = xml_parser_create();
3538
- xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
3539
- // What if internal encoding is not in one of the 3 allowed?
3540
- // we use the broadest one, ie. utf8!
3541
- if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
3542
- {
3543
- xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
3544
- }
3545
- else
3546
- {
3547
- xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
3548
- }
3549
- xml_set_element_handler($parser, 'xmlrpc_se_any', 'xmlrpc_ee');
3550
- xml_set_character_data_handler($parser, 'xmlrpc_cd');
3551
- xml_set_default_handler($parser, 'xmlrpc_dh');
3552
- if(!xml_parse($parser, $xml_val, 1))
3553
- {
3554
- $errstr = sprintf('XML error: %s at line %d, column %d',
3555
- xml_error_string(xml_get_error_code($parser)),
3556
- xml_get_current_line_number($parser), xml_get_current_column_number($parser));
3557
- error_log($errstr);
3558
- xml_parser_free($parser);
3559
- return false;
3560
- }
3561
- xml_parser_free($parser);
3562
- if ($GLOBALS['_xh']['isf'] > 1) // test that $GLOBALS['_xh']['value'] is an obj, too???
3563
- {
3564
- error_log($GLOBALS['_xh']['isf_reason']);
3565
- return false;
3566
- }
3567
- switch ($GLOBALS['_xh']['rt'])
3568
- {
3569
- case 'methodresponse':
3570
- $v =& $GLOBALS['_xh']['value'];
3571
- if ($GLOBALS['_xh']['isf'] == 1)
3572
- {
3573
- $vc = $v->structmem('faultCode');
3574
- $vs = $v->structmem('faultString');
3575
- $r = new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval());
3576
- }
3577
- else
3578
- {
3579
- $r = new xmlrpcresp($v);
3580
- }
3581
- return $r;
3582
- case 'methodcall':
3583
- $m = new xmlrpcmsg($GLOBALS['_xh']['method']);
3584
- for($i=0; $i < count($GLOBALS['_xh']['params']); $i++)
3585
- {
3586
- $m->addParam($GLOBALS['_xh']['params'][$i]);
3587
- }
3588
- return $m;
3589
- case 'value':
3590
- return $GLOBALS['_xh']['value'];
3591
- default:
3592
- return false;
3593
- }
3594
- }
3595
-
3596
- /**
3597
- * decode a string that is encoded w/ "chunked" transfer encoding
3598
- * as defined in rfc2068 par. 19.4.6
3599
- * code shamelessly stolen from nusoap library by Dietrich Ayala
3600
- *
3601
- * @param string $buffer the string to be decoded
3602
- * @return string
3603
- */
3604
- function decode_chunked($buffer)
3605
- {
3606
- // length := 0
3607
- $length = 0;
3608
- $new = '';
3609
-
3610
- // read chunk-size, chunk-extension (if any) and crlf
3611
- // get the position of the linebreak
3612
- $chunkend = strpos($buffer,"\r\n") + 2;
3613
- $temp = substr($buffer,0,$chunkend);
3614
- $chunk_size = hexdec( trim($temp) );
3615
- $chunkstart = $chunkend;
3616
- while($chunk_size > 0)
3617
- {
3618
- $chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
3619
-
3620
- // just in case we got a broken connection
3621
- if($chunkend == false)
3622
- {
3623
- $chunk = substr($buffer,$chunkstart);
3624
- // append chunk-data to entity-body
3625
- $new .= $chunk;
3626
- $length += strlen($chunk);
3627
- break;
3628
- }
3629
-
3630
- // read chunk-data and crlf
3631
- $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3632
- // append chunk-data to entity-body
3633
- $new .= $chunk;
3634
- // length := length + chunk-size
3635
- $length += strlen($chunk);
3636
- // read chunk-size and crlf
3637
- $chunkstart = $chunkend + 2;
3638
-
3639
- $chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
3640
- if($chunkend == false)
3641
- {
3642
- break; //just in case we got a broken connection
3643
- }
3644
- $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3645
- $chunk_size = hexdec( trim($temp) );
3646
- $chunkstart = $chunkend;
3647
- }
3648
- return $new;
3649
- }
3650
-
3651
- /**
3652
- * xml charset encoding guessing helper function.
3653
- * Tries to determine the charset encoding of an XML chunk received over HTTP.
3654
- * NB: according to the spec (RFC 3023), if text/xml content-type is received over HTTP without a content-type,
3655
- * we SHOULD assume it is strictly US-ASCII. But we try to be more tolerant of unconforming (legacy?) clients/servers,
3656
- * which will be most probably using UTF-8 anyway...
3657
- *
3658
- * @param string $httpheaders the http Content-type header
3659
- * @param string $xmlchunk xml content buffer
3660
- * @param string $encoding_prefs comma separated list of character encodings to be used as default (when mb extension is enabled)
3661
- *
3662
- * @todo explore usage of mb_http_input(): does it detect http headers + post data? if so, use it instead of hand-detection!!!
3663
- */
3664
- function guess_encoding($httpheader='', $xmlchunk='', $encoding_prefs=null)
3665
- {
3666
- // discussion: see http://www.yale.edu/pclt/encoding/
3667
- // 1 - test if encoding is specified in HTTP HEADERS
3668
-
3669
- //Details:
3670
- // LWS: (\13\10)?( |\t)+
3671
- // token: (any char but excluded stuff)+
3672
- // quoted string: " (any char but double quotes and cointrol chars)* "
3673
- // header: Content-type = ...; charset=value(; ...)*
3674
- // where value is of type token, no LWS allowed between 'charset' and value
3675
- // Note: we do not check for invalid chars in VALUE:
3676
- // this had better be done using pure ereg as below
3677
- // Note 2: we might be removing whitespace/tabs that ought to be left in if
3678
- // the received charset is a quoted string. But nobody uses such charset names...
3679
-
3680
- /// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it?
3681
- $matches = array();
3682
- if(preg_match('/;\s*charset\s*=([^;]+)/i', $httpheader, $matches))
3683
- {
3684
- return strtoupper(trim($matches[1], " \t\""));
3685
- }
3686
-
3687
- // 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern
3688
- // (source: http://www.w3.org/TR/2000/REC-xml-20001006)
3689
- // NOTE: actually, according to the spec, even if we find the BOM and determine
3690
- // an encoding, we should check if there is an encoding specified
3691
- // in the xml declaration, and verify if they match.
3692
- /// @todo implement check as described above?
3693
- /// @todo implement check for first bytes of string even without a BOM? (It sure looks harder than for cases WITH a BOM)
3694
- if(preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlchunk))
3695
- {
3696
- return 'UCS-4';
3697
- }
3698
- elseif(preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlchunk))
3699
- {
3700
- return 'UTF-16';
3701
- }
3702
- elseif(preg_match('/^(\xEF\xBB\xBF)/', $xmlchunk))
3703
- {
3704
- return 'UTF-8';
3705
- }
3706
-
3707
- // 3 - test if encoding is specified in the xml declaration
3708
- // Details:
3709
- // SPACE: (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
3710
- // EQ: SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
3711
- if (preg_match('/^<\?xml\s+version\s*=\s*'. "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))".
3712
- '\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/",
3713
- $xmlchunk, $matches))
3714
- {
3715
- return strtoupper(substr($matches[2], 1, -1));
3716
- }
3717
-
3718
- // 4 - if mbstring is available, let it do the guesswork
3719
- // NB: we favour finding an encoding that is compatible with what we can process
3720
- if(extension_loaded('mbstring'))
3721
- {
3722
- if($encoding_prefs)
3723
- {
3724
- $enc = mb_detect_encoding($xmlchunk, $encoding_prefs);
3725
- }
3726
- else
3727
- {
3728
- $enc = mb_detect_encoding($xmlchunk);
3729
- }
3730
- // NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII...
3731
- // IANA also likes better US-ASCII, so go with it
3732
- if($enc == 'ASCII')
3733
- {
3734
- $enc = 'US-'.$enc;
3735
- }
3736
- return $enc;
3737
- }
3738
- else
3739
- {
3740
- // no encoding specified: as per HTTP1.1 assume it is iso-8859-1?
3741
- // Both RFC 2616 (HTTP 1.1) and 1945 (HTTP 1.0) clearly state that for text/xxx content types
3742
- // this should be the standard. And we should be getting text/xml as request and response.
3743
- // BUT we have to be backward compatible with the lib, which always used UTF-8 as default...
3744
- return $GLOBALS['xmlrpc_defencoding'];
3745
- }
3746
- }
3747
-
3748
- /**
3749
- * Checks if a given charset encoding is present in a list of encodings or
3750
- * if it is a valid subset of any encoding in the list
3751
- * @param string $encoding charset to be tested
3752
- * @param mixed $validlist comma separated list of valid charsets (or array of charsets)
3753
- */
3754
- function is_valid_charset($encoding, $validlist)
3755
- {
3756
- $charset_supersets = array(
3757
- 'US-ASCII' => array ('ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
3758
- 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8',
3759
- 'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-12',
3760
- 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'UTF-8',
3761
- 'EUC-JP', 'EUC-', 'EUC-KR', 'EUC-CN')
3762
- );
3763
- if (is_string($validlist))
3764
- $validlist = explode(',', $validlist);
3765
- if (@in_array(strtoupper($encoding), $validlist))
3766
- return true;
3767
- else
3768
- {
3769
- if (array_key_exists($encoding, $charset_supersets))
3770
- foreach ($validlist as $allowed)
3771
- if (in_array($allowed, $charset_supersets[$encoding]))
3772
- return true;
3773
- return false;
3774
- }
3775
- }
3776
-
3777
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
readme.txt CHANGED
@@ -4,7 +4,7 @@ Tags: spam, antispam, anti-spam, spambot, spam-bot, stop spam, spammers, spamfre
4
  Requires at least: 3.1.2
5
  License: GPLv2
6
  Tested up to: 3.6
7
- Stable tag: 2.4.11
8
 
9
  No spam in the comments. Smart, simple anti-spam app without CAPTCHA.
10
 
@@ -13,7 +13,7 @@ No spam in the comments. Smart, simple anti-spam app without CAPTCHA.
13
  1. Invisible spam protection for visitors.
14
  1. Anti-spam without CAPTCHA.
15
 
16
- Plugin filters spam bots in the comments of a blog without move to trash or approval in the queue. The plugin is not visible for visitors and administrators of a blog. The plugin not uses CAPTCHA or Q&A to stop spammers. It's simple and clever antispam for your blog.
17
 
18
  Every new comment compares with article and previous comments. If the relevance of the comment is good enough it gets approval at the blog without manual approval.
19
 
@@ -21,7 +21,7 @@ This plugin is a client application for anti-spam service cleantalk.org. It is f
21
 
22
  == Installation ==
23
 
24
- Please use <a href="http://cleantalk.org/install/wordpress" target="_blank">Antispam setup manual</a> at the plugin's site.
25
 
26
  == Frequently Asked Questions ==
27
 
@@ -38,6 +38,14 @@ Plugin uses several simple tests to stop spammers.
38
 
39
  Plugin sends a comment's text and several previous approved comments to the servers. Servers evaluates the relevance of the comment's text on the topic, tests on spam and finaly provides a solution - to publish or put on manual moderation of comments. If a comment is placed on manual moderation, the plugin adds to the text of a comment explaining the reason for the ban server publishing.
40
 
 
 
 
 
 
 
 
 
41
  = Why do I need one more anti-spam plugin? =
42
 
43
  1. The plugin is more effective than CAPTCHA because use several methods to stop spammers.
@@ -52,6 +60,11 @@ Plugin sends a comment's text and several previous approved comments to the serv
52
 
53
  == Changelog ==
54
 
 
 
 
 
 
55
  = 2.4.11 2013-08-02 =
56
  * Removed spam tests for self-made pingbacks
57
  * Tested up to WP 3.6
4
  Requires at least: 3.1.2
5
  License: GPLv2
6
  Tested up to: 3.6
7
+ Stable tag: 2.4.12
8
 
9
  No spam in the comments. Smart, simple anti-spam app without CAPTCHA.
10
 
13
  1. Invisible spam protection for visitors.
14
  1. Anti-spam without CAPTCHA.
15
 
16
+ Plugin filters spam bots in the comments of a blog without move to trash or approval in the queue. The plugin is not visible for visitors and administrators of a blog. The plugin doesn't use CAPTCHA or Q&A to stop spammers. It's simple and clever antispam for your blog.
17
 
18
  Every new comment compares with article and previous comments. If the relevance of the comment is good enough it gets approval at the blog without manual approval.
19
 
21
 
22
  == Installation ==
23
 
24
+ Please use <a href="http://cleantalk.org/install/wordpress" target="_blank">setup manual</a> at the plugin's site.
25
 
26
  == Frequently Asked Questions ==
27
 
38
 
39
  Plugin sends a comment's text and several previous approved comments to the servers. Servers evaluates the relevance of the comment's text on the topic, tests on spam and finaly provides a solution - to publish or put on manual moderation of comments. If a comment is placed on manual moderation, the plugin adds to the text of a comment explaining the reason for the ban server publishing.
40
 
41
+ = Will plugin works with my theme? =
42
+
43
+ Plugin works with all WordPress themes. With some themes may not works JavaScript antispam method, but it's not crucial to protect your blog from spam.
44
+
45
+ = How can I test antispam protection? =
46
+
47
+ Please use test email stop_email@example.com for comments. Also you can see comments proccessed by plugin for last 7 days at <a href="http://cleantalk.org/my/show_requests">Control panel</a>.
48
+
49
  = Why do I need one more anti-spam plugin? =
50
 
51
  1. The plugin is more effective than CAPTCHA because use several methods to stop spammers.
60
 
61
  == Changelog ==
62
 
63
+ = 2.4.12 2013-08-12 =
64
+ * Removed RPC::XML library from plugin.
65
+ * Switched plugin to HTTP+JSON connection with servers.
66
+ * Fixed bug with comments antispam tests with non UTF8 codepage.
67
+
68
  = 2.4.11 2013-08-02 =
69
  * Removed spam tests for self-made pingbacks
70
  * Tested up to WP 3.6
screenshot-4.png ADDED
Binary file