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 | 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
- JSON.php +806 -0
- cleantalk.class.php +868 -830
- cleantalk.php +4 -4
- cleantalk.xmlrpc.php +0 -3777
- readme.txt +16 -3
- 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
|
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 |
-
*
|
17 |
-
*/
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
$this->
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
/**
|
182 |
-
*
|
183 |
-
* @var string
|
184 |
-
*/
|
185 |
-
public $
|
186 |
-
|
187 |
-
/**
|
188 |
-
*
|
189 |
-
* @var string
|
190 |
-
*/
|
191 |
-
public $
|
192 |
-
|
193 |
-
/**
|
194 |
-
*
|
195 |
-
*
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
*
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
*
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
*
|
215 |
-
* @var
|
216 |
-
*/
|
217 |
-
public $
|
218 |
-
|
219 |
-
/**
|
220 |
-
* User
|
221 |
-
* @var
|
222 |
-
*/
|
223 |
-
public $
|
224 |
-
|
225 |
-
/**
|
226 |
-
*
|
227 |
-
* @var
|
228 |
-
*/
|
229 |
-
public $
|
230 |
-
|
231 |
-
/**
|
232 |
-
*
|
233 |
-
* @var string
|
234 |
-
*/
|
235 |
-
public $
|
236 |
-
|
237 |
-
/**
|
238 |
-
*
|
239 |
-
*
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
*
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
*
|
252 |
-
*
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
*
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
*
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
*
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
*
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
*
|
282 |
-
* @
|
283 |
-
*/
|
284 |
-
public
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
*
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
/**
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
/**
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
/**
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
/**
|
342 |
-
*
|
343 |
-
* @var
|
344 |
-
*/
|
345 |
-
public $
|
346 |
-
|
347 |
-
/**
|
348 |
-
*
|
349 |
-
* @var
|
350 |
-
*/
|
351 |
-
public $
|
352 |
-
|
353 |
-
/**
|
354 |
-
*
|
355 |
-
* @var
|
356 |
-
*/
|
357 |
-
public $
|
358 |
-
|
359 |
-
/**
|
360 |
-
*
|
361 |
-
* @
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
*
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
if ($param
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
if (
|
488 |
-
$error_params[] =
|
489 |
-
}
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
}
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
'
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
-
|
620 |
-
|
621 |
-
$
|
622 |
-
$
|
623 |
-
|
624 |
-
$
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
$
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
-
|
703 |
-
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
-
|
708 |
-
|
709 |
-
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
$
|
715 |
-
|
716 |
-
|
717 |
-
|
718 |
-
|
719 |
-
|
720 |
-
|
721 |
-
|
722 |
-
|
723 |
-
}
|
724 |
-
|
725 |
-
|
726 |
-
|
727 |
-
|
728 |
-
|
729 |
-
|
730 |
-
|
731 |
-
|
732 |
-
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
-
|
737 |
-
|
738 |
-
|
739 |
-
$
|
740 |
-
|
741 |
-
|
742 |
-
|
743 |
-
|
744 |
-
|
745 |
-
|
746 |
-
|
747 |
-
|
748 |
-
|
749 |
-
|
750 |
-
|
751 |
-
|
752 |
-
|
753 |
-
|
754 |
-
|
755 |
-
|
756 |
-
|
757 |
-
|
758 |
-
|
759 |
-
|
760 |
-
|
761 |
-
|
762 |
-
|
763 |
-
|
764 |
-
|
765 |
-
|
766 |
-
|
767 |
-
|
768 |
-
|
769 |
-
|
770 |
-
|
771 |
-
|
772 |
-
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
{
|
777 |
-
|
778 |
-
|
779 |
-
|
780 |
-
|
781 |
-
|
782 |
-
|
783 |
-
|
784 |
-
|
785 |
-
|
786 |
-
|
787 |
-
|
788 |
-
|
789 |
-
|
790 |
-
|
791 |
-
|
792 |
-
|
793 |
-
|
794 |
-
|
795 |
-
|
796 |
-
|
797 |
-
|
798 |
-
|
799 |
-
$
|
800 |
-
$
|
801 |
-
$
|
802 |
-
|
803 |
-
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
|
820 |
-
|
821 |
-
|
822 |
-
|
823 |
-
|
824 |
-
|
825 |
-
|
826 |
-
|
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
|
6 |
-
Version: 2.4.
|
7 |
Author: СleanTalk team <welcome@cleantalk.ru>
|
8 |
Author URI: http://cleantalk.org
|
9 |
*/
|
10 |
|
11 |
-
$ct_agent_version = 'wordpress-
|
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 |
-
'€', '?', '‚', 'ƒ',
|
137 |
-
'„', '…', '†', '‡',
|
138 |
-
'ˆ', '‰', 'Š', '‹',
|
139 |
-
'Œ', '?', 'Ž', '?',
|
140 |
-
'?', '‘', '’', '“',
|
141 |
-
'”', '•', '–', '—',
|
142 |
-
'˜', '™', 'š', '›',
|
143 |
-
'œ', '?', 'ž', 'Ÿ'
|
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('&', '"', ''', '<', '>'), $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('&', '"', ''', '<', '>'), $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('&', '"', ''', '<', '>'), $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 .= '"';
|
305 |
-
break;
|
306 |
-
case 38:
|
307 |
-
$escaped_data .= '&';
|
308 |
-
break;
|
309 |
-
case 39:
|
310 |
-
$escaped_data .= ''';
|
311 |
-
break;
|
312 |
-
case 60:
|
313 |
-
$escaped_data .= '<';
|
314 |
-
break;
|
315 |
-
case 62:
|
316 |
-
$escaped_data .= '>';
|
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('&', '"', ''', '<', '>'), $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('&', '"', ''', '<', '>'), $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('&', '"', ''', '<', '>'), $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.
|
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
|
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">
|
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
|