aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDavid Walter Seikel2013-03-15 01:06:16 +1000
committerDavid Walter Seikel2013-03-15 01:06:16 +1000
commit4515cca57d6a5248114b03954fe7984ba477fcce (patch)
tree2aedb44fd103effbb6c7d6870b6c2cf4b9c19c1b
parentAdd the SQL update scripts. (diff)
downloadopensim-SC_OLD-4515cca57d6a5248114b03954fe7984ba477fcce.zip
opensim-SC_OLD-4515cca57d6a5248114b03954fe7984ba477fcce.tar.gz
opensim-SC_OLD-4515cca57d6a5248114b03954fe7984ba477fcce.tar.bz2
opensim-SC_OLD-4515cca57d6a5248114b03954fe7984ba477fcce.tar.xz
Add grid server web scripts.
-rw-r--r--config/os_flotsam_config.php42
-rw-r--r--config/os_modules_mysql.php6
-rw-r--r--web/offline.php51
-rw-r--r--web/offline_mysql.php112
-rw-r--r--web/parser.php317
-rw-r--r--web/phpxmlrpclib/compat/array_key_exists.php55
-rw-r--r--web/phpxmlrpclib/compat/is_a.php47
-rw-r--r--web/phpxmlrpclib/compat/is_callable.php53
-rw-r--r--web/phpxmlrpclib/compat/is_scalar.php38
-rw-r--r--web/phpxmlrpclib/compat/var_export.php105
-rw-r--r--web/phpxmlrpclib/compat/version_compare.php179
-rw-r--r--web/phpxmlrpclib/xmlrpc.inc3718
-rw-r--r--web/phpxmlrpclib/xmlrpc_wrappers.inc944
-rw-r--r--web/phpxmlrpclib/xmlrpcs.inc1198
-rw-r--r--web/profile.php691
-rw-r--r--web/query.php593
-rw-r--r--web/register.php61
-rw-r--r--web/xmlrpc.php1755
18 files changed, 9965 insertions, 0 deletions
diff --git a/config/os_flotsam_config.php b/config/os_flotsam_config.php
new file mode 100644
index 0000000..c00fd8d
--- /dev/null
+++ b/config/os_flotsam_config.php
@@ -0,0 +1,42 @@
1<?php
2 // be sure to set "error_reporting = E_ALL & ~E_NOTICE & ~E_DEPRECATED" in your php.ini
3
4 // Set to 0 for no debugging, 1 for essential debugging, 2 for more information.
5 $debugXMLRPC = 0;
6 $debugXMLRPCFile = "/var/log/opensim_beta/xmlrpc.log";
7
8 //////////////////////////////
9 //// Group security
10 /////////////////////
11
12 // A xmlrpc client must have this key to commit changes to the Groups DB
13 // Leave blank to allow all clients to make changes.
14 $groupWriteKey = '';
15 $groupReadKey = '';
16
17 // Enabling this, will require that the service attempt to verify the agent
18 // is authentic by contacting the User Service specified in the request
19 // to authenticate the AgentID and SessionID provided.
20 $groupRequireAgentAuthForWrite = FALSE;
21
22 // This enforces the role Permissions bitmask.
23 $groupEnforceGroupPerms = FALSE;
24
25 // Specify the following to hard-code / lockdown the User Service used to authenticate
26 // user sessions. Example: http://osgrid.org:8002
27 // Note: This causes the User Service specified with requests to be ignored, and
28 // prevents the service from being used cross-grid or by hypergridded users.
29 $overrideAgentUserService = '';
30
31
32 // This setting configures the behavior of the "Members are Visible" checkbox
33 // provided for on the Role configuration panel -- and determines who is allowed
34 // to get a list of members for a role when that checkbox is *NOT* checked.
35
36 $membersVisibleTo = 'Group'; // Anyone in the group can see members
37 // $membersVisibleTo = 'Owners'; // Only members of the owners role can see members
38 // $membersVisibleTo = 'All'; // Anyone can see members
39
40 // Enable UTF-8 characters in group descriptions, ranks or notices, to prevent them from getting garbled:
41 $xmlrpc_internalencoding = 'UTF-8'
42?>
diff --git a/config/os_modules_mysql.php b/config/os_modules_mysql.php
new file mode 100644
index 0000000..f425149
--- /dev/null
+++ b/config/os_modules_mysql.php
@@ -0,0 +1,6 @@
1<?php
2$DB_HOST = "localhost";
3$DB_USER = "DATABASE_USER";
4$DB_PASSWORD = "DATABASE_PASSWORD";
5$DB_NAME = "DATABASE_NAME";
6?>
diff --git a/web/offline.php b/web/offline.php
new file mode 100644
index 0000000..f4b26c6
--- /dev/null
+++ b/web/offline.php
@@ -0,0 +1,51 @@
1<?php
2include("../config/os_modules_mysql.php");
3
4define("C_DB_TYPE","mysql");
5define("C_DB_HOST",$DB_HOST);
6define("C_DB_NAME",$DB_NAME);
7define("C_DB_USER",$DB_USER);
8define("C_DB_PASS",$DB_PASSWORD);
9define("C_OFFLINE_IM_TBL", "Offline_IM");
10include("offline_mysql.php");
11$DbLink = new DB;
12$method = $_SERVER["PATH_INFO"];
13if ($method == "/SaveMessage/")
14{
15 $msg = $HTTP_RAW_POST_DATA;
16 $start = strpos($msg, "?>");
17 if ($start != -1)
18 {
19 $start+=2;
20 $msg = substr($msg, $start);
21 $parts = split("[<>]", $msg);
22 $to_agent = $parts[12];
23 $DbLink->query("insert into ".C_OFFLINE_IM_TBL." (uuid, message) values ('" .
24 mysql_escape_string($to_agent) . "', '" .
25 mysql_escape_string($msg) . "')");
26 echo "<?xml version=\"1.0\" encoding=\"utf-8\"?><boolean>true</boolean>";
27 }
28 else
29 {
30 echo "<?xml version=\"1.0\" encoding=\"utf-8\"?><boolean>false</boolean>";
31 }
32 exit;
33}
34if ($method == "/RetrieveMessages/")
35{
36 $parms = $HTTP_RAW_POST_DATA;
37 $parts = split("[<>]", $parms);
38 $agent_id = $parts[6];
39 $DbLink->query("select message from ".C_OFFLINE_IM_TBL." where uuid='" .
40 mysql_escape_string($agent_id) . "'");
41 echo "<?xml version=\"1.0\" encoding=\"utf-8\"?><ArrayOfGridInstantMessage xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">";
42 while(list($message) = $DbLink->next_record())
43 {
44 echo $message;
45 }
46 echo "</ArrayOfGridInstantMessage>";
47 $DbLink->query("delete from ".C_OFFLINE_IM_TBL." where uuid='" .
48 mysql_escape_string($agent_id) . "'");
49 exit;
50}
51?>
diff --git a/web/offline_mysql.php b/web/offline_mysql.php
new file mode 100644
index 0000000..fe0eb3e
--- /dev/null
+++ b/web/offline_mysql.php
@@ -0,0 +1,112 @@
1<?php
2/*
3 * Copyright (c) 2007, 2008 Contributors, http://opensimulator.org/
4 * See CONTRIBUTORS for a full list of copyright holders.
5 *
6 * See LICENSE for the full licensing terms of this file.
7 *
8*/
9
10// This looks like its lifted from http://www.weberdev.com/get_example-4372.html
11// I'd contact the original developer for licensing info, but his website is broken.
12
13class DB
14{
15 var $Host = C_DB_HOST; // Hostname of our MySQL server
16 var $Database = C_DB_NAME; // Logical database name on that server
17 var $User = C_DB_USER; // Database user
18 var $Password = C_DB_PASS; // Database user's password
19 var $Link_ID = 0; // Result of mysql_connect()
20 var $Query_ID = 0; // Result of most recent mysql_query()
21 var $Record = array(); // Current mysql_fetch_array()-result
22 var $Row; // Current row number
23 var $Errno = 0; // Error state of query
24 var $Error = "";
25
26 function halt($msg)
27 {
28 echo("</TD></TR></TABLE><B>Database error:</B> $msg<BR>\n");
29 echo("<B>MySQL error</B>: $this->Errno ($this->Error)<BR>\n");
30 die("Session halted.");
31 }
32
33 function connect()
34 {
35 if($this->Link_ID == 0)
36 {
37 $this->Link_ID = mysql_connect($this->Host, $this->User, $this->Password);
38 if (!$this->Link_ID)
39 {
40 $this->halt("Link_ID == false, connect failed");
41 }
42 $SelectResult = mysql_select_db($this->Database, $this->Link_ID);
43 if(!$SelectResult)
44 {
45 $this->Errno = mysql_errno($this->Link_ID);
46 $this->Error = mysql_error($this->Link_ID);
47 $this->halt("cannot select database <I>".$this->Database."</I>");
48 }
49 }
50 }
51
52 function escape($String)
53 {
54 return mysql_escape_string($String);
55 }
56
57 function query($Query_String)
58 {
59 $this->connect();
60 $this->Query_ID = mysql_query($Query_String,$this->Link_ID);
61 $this->Row = 0;
62 $this->Errno = mysql_errno();
63 $this->Error = mysql_error();
64 if (!$this->Query_ID)
65 {
66 $this->halt("Invalid SQL: ".$Query_String);
67 }
68 return $this->Query_ID;
69 }
70
71 function next_record()
72 {
73 $this->Record = @mysql_fetch_array($this->Query_ID);
74 $this->Row += 1;
75 $this->Errno = mysql_errno();
76 $this->Error = mysql_error();
77 $stat = is_array($this->Record);
78 if (!$stat)
79 {
80 @mysql_free_result($this->Query_ID);
81 $this->Query_ID = 0;
82 }
83 return $this->Record;
84 }
85
86 function num_rows()
87 {
88 return mysql_num_rows($this->Query_ID);
89 }
90
91 function affected_rows()
92 {
93 return mysql_affected_rows($this->Link_ID);
94 }
95
96 function optimize($tbl_name)
97 {
98 $this->connect();
99 $this->Query_ID = @mysql_query("OPTIMIZE TABLE $tbl_name",$this->Link_ID);
100 }
101
102 function clean_results()
103 {
104 if($this->Query_ID != 0) mysql_freeresult($this->Query_ID);
105 }
106
107 function close()
108 {
109 if($this->Link_ID != 0) mysql_close($this->Link_ID);
110 }
111}
112?>
diff --git a/web/parser.php b/web/parser.php
new file mode 100644
index 0000000..4783148
--- /dev/null
+++ b/web/parser.php
@@ -0,0 +1,317 @@
1<?php
2include("../config/os_modules_mysql.php");
3
4
5//Supress all Warnings/Errors
6//error_reporting(0);
7
8$now = time();
9
10//
11// Search DB
12//
13mysql_connect ($DB_HOST, $DB_USER, $DB_PASSWORD);
14mysql_select_db ($DB_NAME);
15
16function GetURL($host, $port, $url)
17{
18 $url = "http://$host:$port/$url";
19
20 $ch = curl_init();
21 curl_setopt($ch, CURLOPT_URL, $url);
22 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
23 curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
24 curl_setopt($ch, CURLOPT_TIMEOUT, 30);
25
26 $data = curl_exec($ch);
27 if (curl_errno($ch) == 0)
28 {
29 curl_close($ch);
30 return $data;
31 }
32
33 curl_close($ch);
34 return "";
35}
36
37function CheckHost($hostname, $port)
38{
39 global $now;
40
41 $xml = GetURL($hostname, $port, "?method=collector");
42 if ($xml == "") //No data was retrieved? (CURL may have timed out)
43 $failcounter = "failcounter + 1";
44 else
45 $failcounter = "0";
46
47 //Update nextcheck to be 10 minutes from now. The current OS instance
48 //won't be checked again until at least this much time has gone by.
49 $next = $now + 600;
50
51 mysql_query("UPDATE hostsregister SET nextcheck = $next," .
52 " checked = 1, failcounter = " . $failcounter .
53 " WHERE host = '" . mysql_real_escape_string($hostname) . "'" .
54 " AND port = '" . mysql_real_escape_string($port) . "'");
55
56 if ($xml != "")
57 parse($hostname, $port, $xml);
58}
59
60function parse($hostname, $port, $xml)
61{
62 global $now;
63
64 ///////////////////////////////////////////////////////////////////////
65 //
66 // Search engine sim scanner
67 //
68
69 //
70 // Load XML doc from URL
71 //
72 $objDOM = new DOMDocument();
73 $objDOM->resolveExternals = false;
74
75 //Don't try and parse if XML is invalid or we got an HTML 404 error.
76 if ($objDOM->loadXML($xml) == False)
77 return;
78
79 //
80 // Get the region data to update
81 //
82 $regiondata = $objDOM->getElementsByTagName("regiondata");
83
84 //If returned length is 0, collector method may have returned an error
85 if ($regiondata->length == 0)
86 return;
87
88 $regiondata = $regiondata->item(0);
89
90 //
91 // Update nextcheck so this host entry won't be checked again until after
92 // the DataSnapshot module has generated a new set of data to be parsed.
93 //
94 $expire = $regiondata->getElementsByTagName("expire")->item(0)->nodeValue;
95 $next = $now + $expire;
96
97 $updater = mysql_query("UPDATE hostsregister SET nextcheck = $next " .
98 "WHERE host = '" . mysql_real_escape_string($hostname) . "' AND " .
99 "port = '" . mysql_real_escape_string($port) . "'");
100
101 //
102 // Get the region data to be saved in the database
103 //
104 $regionlist = $regiondata->getElementsByTagName("region");
105
106 foreach ($regionlist as $region)
107 {
108 $regioncategory = $region->getAttributeNode("category")->nodeValue;
109
110 //
111 // Start reading the Region info
112 //
113 $info = $region->getElementsByTagName("info")->item(0);
114
115 $regionuuid = $info->getElementsByTagName("uuid")->item(0)->nodeValue;
116
117 $regionname = $info->getElementsByTagName("name")->item(0)->nodeValue;
118
119 $regionhandle = $info->getElementsByTagName("handle")->item(0)->nodeValue;
120
121 $url = $info->getElementsByTagName("url")->item(0)->nodeValue;
122
123 //
124 // First, check if we already have a region that is the same
125 //
126 $check = mysql_query("SELECT * FROM osregions WHERE regionuuid = '" .
127 mysql_real_escape_string($regionuuid) . "'");
128
129 if (mysql_num_rows($check) > 0)
130 {
131 mysql_query("DELETE FROM osregions WHERE regionuuid = '" .
132 mysql_real_escape_string($regionuuid) . "'");
133 mysql_query("DELETE FROM parcels WHERE regionuuid = '" .
134 mysql_real_escape_string($regionuuid) . "'");
135 mysql_query("DELETE FROM allparcels WHERE regionUUID = '" .
136 mysql_real_escape_string($regionuuid) . "'");
137 mysql_query("DELETE FROM parcelsales WHERE regionUUID = '" .
138 mysql_real_escape_string($regionuuid) . "'");
139 mysql_query("DELETE FROM objects WHERE regionuuid = '" .
140 mysql_real_escape_string($regionuuid) . "'");
141 }
142
143 $data = $region->getElementsByTagName("data")->item(0);
144 $estate = $data->getElementsByTagName("estate")->item(0);
145
146 $username = $estate->getElementsByTagName("name")->item(0)->nodeValue;
147 $useruuid = $estate->getElementsByTagName("uuid")->item(0)->nodeValue;
148
149 $estateid = $estate->getElementsByTagName("id")->item(0)->nodeValue;
150
151 //
152 // Second, add the new info to the database
153 //
154 $sql = "INSERT INTO osregions VALUES('" .
155 mysql_real_escape_string($regionname) . "','" .
156 mysql_real_escape_string($regionuuid) . "','" .
157 mysql_real_escape_string($regionhandle) . "','" .
158 mysql_real_escape_string($url) . "','" .
159 mysql_real_escape_string($username) ."','" .
160 mysql_real_escape_string($useruuid) ."')";
161
162 mysql_query($sql);
163
164 //
165 // Start reading the parcel info
166 //
167 $parcel = $data->getElementsByTagName("parcel");
168
169 foreach ($parcel as $value)
170 {
171 $parcelname = $value->getElementsByTagName("name")->item(0)->nodeValue;
172
173 $parceluuid = $value->getElementsByTagName("uuid")->item(0)->nodeValue;
174
175 $infouuid = $value->getElementsByTagName("infouuid")->item(0)->nodeValue;
176
177 $parcellanding = $value->getElementsByTagName("location")->item(0)->nodeValue;
178
179 $parceldescription = $value->getElementsByTagName("description")->item(0)->nodeValue;
180
181 $parcelarea = $value->getElementsByTagName("area")->item(0)->nodeValue;
182
183 $parcelcategory = $value->getAttributeNode("category")->nodeValue;
184
185 $parcelsaleprice = $value->getAttributeNode("salesprice")->nodeValue;
186
187 $dwell = $value->getElementsByTagName("dwell")->item(0)->nodeValue;
188
189 $owner = $value->getElementsByTagName("owner")->item(0);
190
191 $owneruuid = $owner->getElementsByTagName("uuid")->item(0)->nodeValue;
192
193 // Adding support for groups
194
195 $group = $value->getElementsByTagName("group")->item(0);
196
197 if ($group != "")
198 {
199 $groupuuid = $group->getElementsByTagName("groupuuid")->item(0)->nodeValue;
200 }
201 else
202 {
203 $groupuuid = "00000000-0000-0000-0000-000000000000";
204 }
205
206 //
207 // Check bits on Public, Build, Script
208 //
209 $parcelforsale = $value->getAttributeNode("forsale")->nodeValue;
210 $parceldirectory = $value->getAttributeNode("showinsearch")->nodeValue;
211 $parcelbuild = $value->getAttributeNode("build")->nodeValue;
212 $parcelscript = $value->getAttributeNode("scripts")->nodeValue;
213 $parcelpublic = $value->getAttributeNode("public")->nodeValue;
214
215 //
216 // Save
217 //
218 $sql = "INSERT INTO allparcels VALUES('" .
219 mysql_real_escape_string($regionuuid) . "','" .
220 mysql_real_escape_string($parcelname) . "','" .
221 mysql_real_escape_string($owneruuid) . "','" .
222 mysql_real_escape_string($groupuuid) . "','" .
223 mysql_real_escape_string($parcellanding) . "','" .
224 mysql_real_escape_string($parceluuid) . "','" .
225 mysql_real_escape_string($infouuid) . "','" .
226 mysql_real_escape_string($parcelarea) . "' )";
227
228 mysql_query($sql);
229
230 if ($parceldirectory == "true")
231 {
232 $sql = "INSERT INTO parcels VALUES('" .
233 mysql_real_escape_string($regionuuid) . "','" .
234 mysql_real_escape_string($parcelname) . "','" .
235 mysql_real_escape_string($parceluuid) . "','" .
236 mysql_real_escape_string($parcellanding) . "','" .
237 mysql_real_escape_string($parceldescription) . "','" .
238 mysql_real_escape_string($parcelcategory) . "','" .
239 mysql_real_escape_string($parcelbuild) . "','" .
240 mysql_real_escape_string($parcelscript) . "','" .
241 mysql_real_escape_string($parcelpublic) . "','".
242 mysql_real_escape_string($dwell) . "','" .
243 mysql_real_escape_string($infouuid) . "','" .
244 mysql_real_escape_string($regioncategory) . "')";
245
246 mysql_query($sql);
247 }
248
249 if ($parcelforsale == "true")
250 {
251 $sql = "INSERT INTO parcelsales VALUES('" .
252 mysql_real_escape_string($regionuuid) . "','" .
253 mysql_real_escape_string($parcelname) . "','" .
254 mysql_real_escape_string($parceluuid) . "','" .
255 mysql_real_escape_string($parcelarea) . "','" .
256 mysql_real_escape_string($parcelsaleprice) . "','" .
257 mysql_real_escape_string($parcellanding) . "','" .
258 mysql_real_escape_string($infouuid) . "', '" .
259 mysql_real_escape_string($dwell) . "', '" .
260 mysql_real_escape_string($estateid) . "', '" .
261 mysql_real_escape_string($regioncategory) . "')";
262
263 mysql_query($sql);
264 }
265 }
266
267 //
268 // Handle objects
269 //
270 $objects = $data->getElementsByTagName("object");
271
272 foreach ($objects as $value)
273 {
274 $uuid = $value->getElementsByTagName("uuid")->item(0)->nodeValue;
275
276 $regionuuid = $value->getElementsByTagName("regionuuid")->item(0)->nodeValue;
277
278 $parceluuid = $value->getElementsByTagName("parceluuid")->item(0)->nodeValue;
279
280 $location = $value->getElementsByTagName("location")->item(0)->nodeValue;
281
282 $title = $value->getElementsByTagName("title")->item(0)->nodeValue;
283
284 $description = $value->getElementsByTagName("description")->item(0)->nodeValue;
285
286 $flags = $value->getElementsByTagName("flags")->item(0)->nodeValue;
287
288 mysql_query("INSERT INTO objects VALUES('" .
289 mysql_real_escape_string($uuid) . "','" .
290 mysql_real_escape_string($parceluuid) . "','" .
291 mysql_real_escape_string($location) . "','" .
292 mysql_real_escape_string($title) . "','" .
293 mysql_real_escape_string($description) . "','" .
294 mysql_real_escape_string($regionuuid) . "')");
295 }
296 }
297}
298
299$sql = "SELECT host, port FROM hostsregister " .
300 "WHERE nextcheck < $now AND checked = 0 LIMIT 0,10";
301
302$jobsearch = mysql_query($sql);
303
304//
305// If the sql query returns no rows, all entries in the hostsregister
306// table have been checked. Reset the checked flag and re-run the
307// query to select the next set of hosts to be checked.
308//
309if (mysql_num_rows($jobsearch) == 0)
310{
311 mysql_query("UPDATE hostsregister SET checked = 0");
312 $jobsearch = mysql_query($sql);
313}
314
315while ($jobs = mysql_fetch_row($jobsearch))
316 CheckHost($jobs[0], $jobs[1]);
317?>
diff --git a/web/phpxmlrpclib/compat/array_key_exists.php b/web/phpxmlrpclib/compat/array_key_exists.php
new file mode 100644
index 0000000..4a93ce4
--- /dev/null
+++ b/web/phpxmlrpclib/compat/array_key_exists.php
@@ -0,0 +1,55 @@
1<?php
2// +----------------------------------------------------------------------+
3// | PHP Version 4 |
4// +----------------------------------------------------------------------+
5// | Copyright (c) 1997-2004 The PHP Group |
6// +----------------------------------------------------------------------+
7// | This source file is subject to version 3.0 of the PHP license, |
8// | that is bundled with this package in the file LICENSE, and is |
9// | available at through the world-wide-web at |
10// | http://www.php.net/license/3_0.txt. |
11// | If you did not receive a copy of the PHP license and are unable to |
12// | obtain it through the world-wide-web, please send a note to |
13// | license@php.net so we can mail you a copy immediately. |
14// +----------------------------------------------------------------------+
15// | Authors: Aidan Lister <aidan@php.net> |
16// +----------------------------------------------------------------------+
17//
18// $Id: array_key_exists.php 2 2009-03-16 20:22:51Z ggiunta $
19
20
21/**
22 * Replace array_key_exists()
23 *
24 * @category PHP
25 * @package PHP_Compat
26 * @link http://php.net/function.array_key_exists
27 * @author Aidan Lister <aidan@php.net>
28 * @version $Revision: 1.1 $
29 * @since PHP 4.1.0
30 * @require PHP 4.0.0 (user_error)
31 */
32if (!function_exists('array_key_exists')) {
33 function array_key_exists($key, $search)
34 {
35 if (!is_scalar($key)) {
36 user_error('array_key_exists() The first argument should be either a string or an integer',
37 E_USER_WARNING);
38 return false;
39 }
40
41 if (is_object($search)) {
42 $search = get_object_vars($search);
43 }
44
45 if (!is_array($search)) {
46 user_error('array_key_exists() The second argument should be either an array or an object',
47 E_USER_WARNING);
48 return false;
49 }
50
51 return in_array($key, array_keys($search));
52 }
53}
54
55?> \ No newline at end of file
diff --git a/web/phpxmlrpclib/compat/is_a.php b/web/phpxmlrpclib/compat/is_a.php
new file mode 100644
index 0000000..2c5c129
--- /dev/null
+++ b/web/phpxmlrpclib/compat/is_a.php
@@ -0,0 +1,47 @@
1<?php
2// +----------------------------------------------------------------------+
3// | PHP Version 4 |
4// +----------------------------------------------------------------------+
5// | Copyright (c) 1997-2004 The PHP Group |
6// +----------------------------------------------------------------------+
7// | This source file is subject to version 3.0 of the PHP license, |
8// | that is bundled with this package in the file LICENSE, and is |
9// | available at through the world-wide-web at |
10// | http://www.php.net/license/3_0.txt. |
11// | If you did not receive a copy of the PHP license and are unable to |
12// | obtain it through the world-wide-web, please send a note to |
13// | license@php.net so we can mail you a copy immediately. |
14// +----------------------------------------------------------------------+
15// | Authors: Aidan Lister <aidan@php.net> |
16// +----------------------------------------------------------------------+
17//
18// $Id: is_a.php 2 2009-03-16 20:22:51Z ggiunta $
19
20
21/**
22 * Replace function is_a()
23 *
24 * @category PHP
25 * @package PHP_Compat
26 * @link http://php.net/function.is_a
27 * @author Aidan Lister <aidan@php.net>
28 * @version $Revision: 1.2 $
29 * @since PHP 4.2.0
30 * @require PHP 4.0.0 (user_error) (is_subclass_of)
31 */
32if (!function_exists('is_a')) {
33 function is_a($object, $class)
34 {
35 if (!is_object($object)) {
36 return false;
37 }
38
39 if (get_class($object) == strtolower($class)) {
40 return true;
41 } else {
42 return is_subclass_of($object, $class);
43 }
44 }
45}
46
47?> \ No newline at end of file
diff --git a/web/phpxmlrpclib/compat/is_callable.php b/web/phpxmlrpclib/compat/is_callable.php
new file mode 100644
index 0000000..419697a
--- /dev/null
+++ b/web/phpxmlrpclib/compat/is_callable.php
@@ -0,0 +1,53 @@
1<?php
2/**
3 * Replace function is_callable()
4 *
5 * @category PHP
6 * @package PHP_Compat
7 * @link http://php.net/function.is_callable
8 * @author Gaetano Giunta <giunta.gaetano@sea-aeroportimilano.it>
9 * @version $Id: is_callable.php 2 2009-03-16 20:22:51Z ggiunta $
10 * @since PHP 4.0.6
11 * @require PHP 4.0.0 (true, false, etc...)
12 * @todo add the 3rd parameter syntax...
13 */
14if (!function_exists('is_callable')) {
15 function is_callable($var, $syntax_only=false)
16 {
17 if ($syntax_only)
18 {
19 /* from The Manual:
20 * If the syntax_only argument is TRUE the function only verifies
21 * that var might be a function or method. It will only reject simple
22 * variables that are not strings, or an array that does not have a
23 * valid structure to be used as a callback. The valid ones are
24 * supposed to have only 2 entries, the first of which is an object
25 * or a string, and the second a string
26 */
27 return (is_string($var) || (is_array($var) && count($var) == 2 && is_string(end($var)) && (is_string(reset($var)) || is_object(reset($var)))));
28 }
29 else
30 {
31 if (is_string($var))
32 {
33 return function_exists($var);
34 }
35 else if (is_array($var) && count($var) == 2 && is_string($method = end($var)))
36 {
37 $obj = reset($var);
38 if (is_string($obj))
39 {
40 $methods = get_class_methods($obj);
41 return (bool)(is_array($methods) && in_array(strtolower($method), $methods));
42 }
43 else if (is_object($obj))
44 {
45 return method_exists($obj, $method);
46 }
47 }
48 return false;
49 }
50 }
51}
52
53?> \ No newline at end of file
diff --git a/web/phpxmlrpclib/compat/is_scalar.php b/web/phpxmlrpclib/compat/is_scalar.php
new file mode 100644
index 0000000..dd87730
--- /dev/null
+++ b/web/phpxmlrpclib/compat/is_scalar.php
@@ -0,0 +1,38 @@
1<?php
2// +----------------------------------------------------------------------+
3// | PHP Version 4 |
4// +----------------------------------------------------------------------+
5// | Copyright (c) 1997-2004 The PHP Group |
6// +----------------------------------------------------------------------+
7// | This source file is subject to version 3.0 of the PHP license, |
8// | that is bundled with this package in the file LICENSE, and is |
9// | available at through the world-wide-web at |
10// | http://www.php.net/license/3_0.txt. |
11// | If you did not receive a copy of the PHP license and are unable to |
12// | obtain it through the world-wide-web, please send a note to |
13// | license@php.net so we can mail you a copy immediately. |
14// +----------------------------------------------------------------------+
15//
16// $Id: is_scalar.php 2 2009-03-16 20:22:51Z ggiunta $
17
18
19/**
20 * Replace is_scalar()
21 *
22 * @category PHP
23 * @package PHP_Compat
24 * @link http://php.net/function.is_scalar
25 * @author Gaetano Giunta
26 * @version $Revision: 1.2 $
27 * @since PHP 4.0.5
28 * @require PHP 4 (is_bool)
29 */
30if (!function_exists('is_scalar')) {
31 function is_scalar($val)
32 {
33 // Check input
34 return (is_bool($val) || is_int($val) || is_float($val) || is_string($val));
35 }
36}
37
38?> \ No newline at end of file
diff --git a/web/phpxmlrpclib/compat/var_export.php b/web/phpxmlrpclib/compat/var_export.php
new file mode 100644
index 0000000..7273a1e
--- /dev/null
+++ b/web/phpxmlrpclib/compat/var_export.php
@@ -0,0 +1,105 @@
1<?php
2// +----------------------------------------------------------------------+
3// | PHP Version 4 |
4// +----------------------------------------------------------------------+
5// | Copyright (c) 1997-2004 The PHP Group |
6// +----------------------------------------------------------------------+
7// | This source file is subject to version 3.0 of the PHP license, |
8// | that is bundled with this package in the file LICENSE, and is |
9// | available at through the world-wide-web at |
10// | http://www.php.net/license/3_0.txt. |
11// | If you did not receive a copy of the PHP license and are unable to |
12// | obtain it through the world-wide-web, please send a note to |
13// | license@php.net so we can mail you a copy immediately. |
14// +----------------------------------------------------------------------+
15// | Authors: Aidan Lister <aidan@php.net> |
16// +----------------------------------------------------------------------+
17//
18// $Id: var_export.php 2 2009-03-16 20:22:51Z ggiunta $
19
20
21/**
22 * Replace var_export()
23 *
24 * @category PHP
25 * @package PHP_Compat
26 * @link http://php.net/function.var_export
27 * @author Aidan Lister <aidan@php.net>
28 * @version $Revision: 1.2 $
29 * @since PHP 4.2.0
30 * @require PHP 4.0.0 (user_error)
31 */
32if (!function_exists('var_export')) {
33 function var_export($array, $return = false, $lvl=0)
34 {
35 // Common output variables
36 $indent = ' ';
37 $doublearrow = ' => ';
38 $lineend = ",\n";
39 $stringdelim = '\'';
40
41 // Check the export isn't a simple string / int
42 if (is_string($array)) {
43 $out = $stringdelim . str_replace('\'', '\\\'', str_replace('\\', '\\\\', $array)) . $stringdelim;
44 } elseif (is_int($array) || is_float($array)) {
45 $out = (string)$array;
46 } elseif (is_bool($array)) {
47 $out = $array ? 'true' : 'false';
48 } elseif (is_null($array)) {
49 $out = 'NULL';
50 } elseif (is_resource($array)) {
51 $out = 'resource';
52 } else {
53 // Begin the array export
54 // Start the string
55 $out = "array (\n";
56
57 // Loop through each value in array
58 foreach ($array as $key => $value) {
59 // If the key is a string, delimit it
60 if (is_string($key)) {
61 $key = str_replace('\'', '\\\'', str_replace('\\', '\\\\', $key));
62 $key = $stringdelim . $key . $stringdelim;
63 }
64
65 $val = var_export($value, true, $lvl+1);
66 // Delimit value
67 /*if (is_array($value)) {
68 // We have an array, so do some recursion
69 // Do some basic recursion while increasing the indent
70 $recur_array = explode($newline, var_export($value, true));
71 $temp_array = array();
72 foreach ($recur_array as $recur_line) {
73 $temp_array[] = $indent . $recur_line;
74 }
75 $recur_array = implode($newline, $temp_array);
76 $value = $newline . $recur_array;
77 } elseif (is_null($value)) {
78 $value = 'NULL';
79 } else {
80 $value = str_replace($find, $replace, $value);
81 $value = $stringdelim . $value . $stringdelim;
82 }*/
83
84 // Piece together the line
85 for ($i = 0; $i < $lvl; $i++)
86 $out .= $indent;
87 $out .= $key . $doublearrow . $val . $lineend;
88 }
89
90 // End our string
91 for ($i = 0; $i < $lvl; $i++)
92 $out .= $indent;
93 $out .= ")";
94 }
95
96 // Decide method of output
97 if ($return === true) {
98 return $out;
99 } else {
100 echo $out;
101 return;
102 }
103 }
104}
105?> \ No newline at end of file
diff --git a/web/phpxmlrpclib/compat/version_compare.php b/web/phpxmlrpclib/compat/version_compare.php
new file mode 100644
index 0000000..a1211ea
--- /dev/null
+++ b/web/phpxmlrpclib/compat/version_compare.php
@@ -0,0 +1,179 @@
1<?php
2// +----------------------------------------------------------------------+
3// | PHP Version 4 |
4// +----------------------------------------------------------------------+
5// | Copyright (c) 1997-2004 The PHP Group |
6// +----------------------------------------------------------------------+
7// | This source file is subject to version 3.0 of the PHP license, |
8// | that is bundled with this package in the file LICENSE, and is |
9// | available at through the world-wide-web at |
10// | http://www.php.net/license/3_0.txt. |
11// | If you did not receive a copy of the PHP license and are unable to |
12// | obtain it through the world-wide-web, please send a note to |
13// | license@php.net so we can mail you a copy immediately. |
14// +----------------------------------------------------------------------+
15// | Authors: Philippe Jausions <Philippe.Jausions@11abacus.com> |
16// | Aidan Lister <aidan@php.net> |
17// +----------------------------------------------------------------------+
18//
19// $Id: version_compare.php 2 2009-03-16 20:22:51Z ggiunta $
20
21
22/**
23 * Replace version_compare()
24 *
25 * @category PHP
26 * @package PHP_Compat
27 * @link http://php.net/function.version_compare
28 * @author Philippe Jausions <Philippe.Jausions@11abacus.com>
29 * @author Aidan Lister <aidan@php.net>
30 * @version $Revision: 1.1 $
31 * @since PHP 4.1.0
32 * @require PHP 4.0.0 (user_error)
33 */
34if (!function_exists('version_compare')) {
35 function version_compare($version1, $version2, $operator = '<')
36 {
37 // Check input
38 if (!is_scalar($version1)) {
39 user_error('version_compare() expects parameter 1 to be string, ' .
40 gettype($version1) . ' given', E_USER_WARNING);
41 return;
42 }
43
44 if (!is_scalar($version2)) {
45 user_error('version_compare() expects parameter 2 to be string, ' .
46 gettype($version2) . ' given', E_USER_WARNING);
47 return;
48 }
49
50 if (!is_scalar($operator)) {
51 user_error('version_compare() expects parameter 3 to be string, ' .
52 gettype($operator) . ' given', E_USER_WARNING);
53 return;
54 }
55
56 // Standardise versions
57 $v1 = explode('.',
58 str_replace('..', '.',
59 preg_replace('/([^0-9\.]+)/', '.$1.',
60 str_replace(array('-', '_', '+'), '.',
61 trim($version1)))));
62
63 $v2 = explode('.',
64 str_replace('..', '.',
65 preg_replace('/([^0-9\.]+)/', '.$1.',
66 str_replace(array('-', '_', '+'), '.',
67 trim($version2)))));
68
69 // Replace empty entries at the start of the array
70 while (empty($v1[0]) && array_shift($v1)) {}
71 while (empty($v2[0]) && array_shift($v2)) {}
72
73 // Release state order
74 // '#' stands for any number
75 $versions = array(
76 'dev' => 0,
77 'alpha' => 1,
78 'a' => 1,
79 'beta' => 2,
80 'b' => 2,
81 'RC' => 3,
82 '#' => 4,
83 'p' => 5,
84 'pl' => 5);
85
86 // Loop through each segment in the version string
87 $compare = 0;
88 for ($i = 0, $x = min(count($v1), count($v2)); $i < $x; $i++) {
89 if ($v1[$i] == $v2[$i]) {
90 continue;
91 }
92 $i1 = $v1[$i];
93 $i2 = $v2[$i];
94 if (is_numeric($i1) && is_numeric($i2)) {
95 $compare = ($i1 < $i2) ? -1 : 1;
96 break;
97 }
98 // We use the position of '#' in the versions list
99 // for numbers... (so take care of # in original string)
100 if ($i1 == '#') {
101 $i1 = '';
102 } elseif (is_numeric($i1)) {
103 $i1 = '#';
104 }
105 if ($i2 == '#') {
106 $i2 = '';
107 } elseif (is_numeric($i2)) {
108 $i2 = '#';
109 }
110 if (isset($versions[$i1]) && isset($versions[$i2])) {
111 $compare = ($versions[$i1] < $versions[$i2]) ? -1 : 1;
112 } elseif (isset($versions[$i1])) {
113 $compare = 1;
114 } elseif (isset($versions[$i2])) {
115 $compare = -1;
116 } else {
117 $compare = 0;
118 }
119
120 break;
121 }
122
123 // If previous loop didn't find anything, compare the "extra" segments
124 if ($compare == 0) {
125 if (count($v2) > count($v1)) {
126 if (isset($versions[$v2[$i]])) {
127 $compare = ($versions[$v2[$i]] < 4) ? 1 : -1;
128 } else {
129 $compare = -1;
130 }
131 } elseif (count($v2) < count($v1)) {
132 if (isset($versions[$v1[$i]])) {
133 $compare = ($versions[$v1[$i]] < 4) ? -1 : 1;
134 } else {
135 $compare = 1;
136 }
137 }
138 }
139
140 // Compare the versions
141 if (func_num_args() > 2) {
142 switch ($operator) {
143 case '>':
144 case 'gt':
145 return (bool) ($compare > 0);
146 break;
147 case '>=':
148 case 'ge':
149 return (bool) ($compare >= 0);
150 break;
151 case '<=':
152 case 'le':
153 return (bool) ($compare <= 0);
154 break;
155 case '==':
156 case '=':
157 case 'eq':
158 return (bool) ($compare == 0);
159 break;
160 case '<>':
161 case '!=':
162 case 'ne':
163 return (bool) ($compare != 0);
164 break;
165 case '':
166 case '<':
167 case 'lt':
168 return (bool) ($compare < 0);
169 break;
170 default:
171 return;
172 }
173 }
174
175 return $compare;
176 }
177}
178
179?> \ No newline at end of file
diff --git a/web/phpxmlrpclib/xmlrpc.inc b/web/phpxmlrpclib/xmlrpc.inc
new file mode 100644
index 0000000..a050d35
--- /dev/null
+++ b/web/phpxmlrpclib/xmlrpc.inc
@@ -0,0 +1,3718 @@
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 // Try to be backward compat with php < 4.2 (are we not being nice ?)
49 $phpversion = phpversion();
50 if($phpversion[0] == '4' && $phpversion[2] < 2)
51 {
52 // give an opportunity to user to specify where to include other files from
53 if(!defined('PHP_XMLRPC_COMPAT_DIR'))
54 {
55 define('PHP_XMLRPC_COMPAT_DIR',dirname(__FILE__).'/compat/');
56 }
57 if($phpversion[2] == '0')
58 {
59 if($phpversion[4] < 6)
60 {
61 include(PHP_XMLRPC_COMPAT_DIR.'is_callable.php');
62 }
63 include(PHP_XMLRPC_COMPAT_DIR.'is_scalar.php');
64 include(PHP_XMLRPC_COMPAT_DIR.'array_key_exists.php');
65 include(PHP_XMLRPC_COMPAT_DIR.'version_compare.php');
66 }
67 include(PHP_XMLRPC_COMPAT_DIR.'var_export.php');
68 include(PHP_XMLRPC_COMPAT_DIR.'is_a.php');
69 }
70
71 // G. Giunta 2005/01/29: declare global these variables,
72 // so that xmlrpc.inc will work even if included from within a function
73 // Milosch: 2005/08/07 - explicitly request these via $GLOBALS where used.
74 $GLOBALS['xmlrpcI4']='i4';
75 $GLOBALS['xmlrpcInt']='int';
76 $GLOBALS['xmlrpcBoolean']='boolean';
77 $GLOBALS['xmlrpcDouble']='double';
78 $GLOBALS['xmlrpcString']='string';
79 $GLOBALS['xmlrpcDateTime']='dateTime.iso8601';
80 $GLOBALS['xmlrpcBase64']='base64';
81 $GLOBALS['xmlrpcArray']='array';
82 $GLOBALS['xmlrpcStruct']='struct';
83 $GLOBALS['xmlrpcValue']='undefined';
84
85 $GLOBALS['xmlrpcTypes']=array(
86 $GLOBALS['xmlrpcI4'] => 1,
87 $GLOBALS['xmlrpcInt'] => 1,
88 $GLOBALS['xmlrpcBoolean'] => 1,
89 $GLOBALS['xmlrpcString'] => 1,
90 $GLOBALS['xmlrpcDouble'] => 1,
91 $GLOBALS['xmlrpcDateTime'] => 1,
92 $GLOBALS['xmlrpcBase64'] => 1,
93 $GLOBALS['xmlrpcArray'] => 2,
94 $GLOBALS['xmlrpcStruct'] => 3
95 );
96
97 $GLOBALS['xmlrpc_valid_parents'] = array(
98 'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
99 'BOOLEAN' => array('VALUE'),
100 'I4' => array('VALUE'),
101 'INT' => array('VALUE'),
102 'STRING' => array('VALUE'),
103 'DOUBLE' => array('VALUE'),
104 'DATETIME.ISO8601' => array('VALUE'),
105 'BASE64' => array('VALUE'),
106 'MEMBER' => array('STRUCT'),
107 'NAME' => array('MEMBER'),
108 'DATA' => array('ARRAY'),
109 'ARRAY' => array('VALUE'),
110 'STRUCT' => array('VALUE'),
111 'PARAM' => array('PARAMS'),
112 'METHODNAME' => array('METHODCALL'),
113 'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
114 'FAULT' => array('METHODRESPONSE'),
115 'NIL' => array('VALUE') // only used when extension activated
116 );
117
118 // define extra types for supporting NULL (useful for json or <NIL/>)
119 $GLOBALS['xmlrpcNull']='null';
120 $GLOBALS['xmlrpcTypes']['null']=1;
121
122 // Not in use anymore since 2.0. Shall we remove it?
123 /// @deprecated
124 $GLOBALS['xmlEntities']=array(
125 'amp' => '&',
126 'quot' => '"',
127 'lt' => '<',
128 'gt' => '>',
129 'apos' => "'"
130 );
131
132 // tables used for transcoding different charsets into us-ascii xml
133
134 $GLOBALS['xml_iso88591_Entities']=array();
135 $GLOBALS['xml_iso88591_Entities']['in'] = array();
136 $GLOBALS['xml_iso88591_Entities']['out'] = array();
137 for ($i = 0; $i < 32; $i++)
138 {
139 $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
140 $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
141 }
142 for ($i = 160; $i < 256; $i++)
143 {
144 $GLOBALS['xml_iso88591_Entities']['in'][] = chr($i);
145 $GLOBALS['xml_iso88591_Entities']['out'][] = '&#'.$i.';';
146 }
147
148 /// @todo add to iso table the characters from cp_1252 range, i.e. 128 to 159?
149 /// These will NOT be present in true ISO-8859-1, but will save the unwary
150 /// windows user from sending junk (though no luck when reciving them...)
151 /*
152 $GLOBALS['xml_cp1252_Entities']=array();
153 for ($i = 128; $i < 160; $i++)
154 {
155 $GLOBALS['xml_cp1252_Entities']['in'][] = chr($i);
156 }
157 $GLOBALS['xml_cp1252_Entities']['out'] = array(
158 '&#x20AC;', '?', '&#x201A;', '&#x0192;',
159 '&#x201E;', '&#x2026;', '&#x2020;', '&#x2021;',
160 '&#x02C6;', '&#x2030;', '&#x0160;', '&#x2039;',
161 '&#x0152;', '?', '&#x017D;', '?',
162 '?', '&#x2018;', '&#x2019;', '&#x201C;',
163 '&#x201D;', '&#x2022;', '&#x2013;', '&#x2014;',
164 '&#x02DC;', '&#x2122;', '&#x0161;', '&#x203A;',
165 '&#x0153;', '?', '&#x017E;', '&#x0178;'
166 );
167 */
168
169 $GLOBALS['xmlrpcerr'] = array(
170 'unknown_method'=>1,
171 'invalid_return'=>2,
172 'incorrect_params'=>3,
173 'introspect_unknown'=>4,
174 'http_error'=>5,
175 'no_data'=>6,
176 'no_ssl'=>7,
177 'curl_fail'=>8,
178 'invalid_request'=>15,
179 'no_curl'=>16,
180 'server_error'=>17,
181 'multicall_error'=>18,
182 'multicall_notstruct'=>9,
183 'multicall_nomethod'=>10,
184 'multicall_notstring'=>11,
185 'multicall_recursion'=>12,
186 'multicall_noparams'=>13,
187 'multicall_notarray'=>14,
188
189 'cannot_decompress'=>103,
190 'decompress_fail'=>104,
191 'dechunk_fail'=>105,
192 'server_cannot_decompress'=>106,
193 'server_decompress_fail'=>107
194 );
195
196 $GLOBALS['xmlrpcstr'] = array(
197 'unknown_method'=>'Unknown method',
198 'invalid_return'=>'Invalid return payload: enable debugging to examine incoming payload',
199 'incorrect_params'=>'Incorrect parameters passed to method',
200 'introspect_unknown'=>"Can't introspect: method unknown",
201 'http_error'=>"Didn't receive 200 OK from remote server.",
202 'no_data'=>'No data received from server.',
203 'no_ssl'=>'No SSL support compiled in.',
204 'curl_fail'=>'CURL error',
205 'invalid_request'=>'Invalid request payload',
206 'no_curl'=>'No CURL support compiled in.',
207 'server_error'=>'Internal server error',
208 'multicall_error'=>'Received from server invalid multicall response',
209 'multicall_notstruct'=>'system.multicall expected struct',
210 'multicall_nomethod'=>'missing methodName',
211 'multicall_notstring'=>'methodName is not a string',
212 'multicall_recursion'=>'recursive system.multicall forbidden',
213 'multicall_noparams'=>'missing params',
214 'multicall_notarray'=>'params is not an array',
215
216 'cannot_decompress'=>'Received from server compressed HTTP and cannot decompress',
217 'decompress_fail'=>'Received from server invalid compressed HTTP',
218 'dechunk_fail'=>'Received from server invalid chunked HTTP',
219 'server_cannot_decompress'=>'Received from client compressed HTTP request and cannot decompress',
220 'server_decompress_fail'=>'Received from client invalid compressed HTTP request'
221 );
222
223 // The charset encoding used by the server for received messages and
224 // by the client for received responses when received charset cannot be determined
225 // or is not supported
226 $GLOBALS['xmlrpc_defencoding']='UTF-8';
227
228 // The encoding used internally by PHP.
229 // String values received as xml will be converted to this, and php strings will be converted to xml
230 // as if having been coded with this
231 $GLOBALS['xmlrpc_internalencoding']='ISO-8859-1';
232
233 $GLOBALS['xmlrpcName']='XML-RPC for PHP';
234 $GLOBALS['xmlrpcVersion']='2.2.2';
235
236 // let user errors start at 800
237 $GLOBALS['xmlrpcerruser']=800;
238 // let XML parse errors start at 100
239 $GLOBALS['xmlrpcerrxml']=100;
240
241 // formulate backslashes for escaping regexp
242 // Not in use anymore since 2.0. Shall we remove it?
243 /// @deprecated
244 $GLOBALS['xmlrpc_backslash']=chr(92).chr(92);
245
246 // set to TRUE to enable correct decoding of <NIL/> values
247 $GLOBALS['xmlrpc_null_extension']=false;
248
249 // used to store state during parsing
250 // quick explanation of components:
251 // ac - used to accumulate values
252 // isf - used to indicate a parsing fault (2) or xmlrpcresp fault (1)
253 // isf_reason - used for storing xmlrpcresp fault string
254 // lv - used to indicate "looking for a value": implements
255 // the logic to allow values with no types to be strings
256 // params - used to store parameters in method calls
257 // method - used to store method name
258 // stack - array with genealogy of xml elements names:
259 // used to validate nesting of xmlrpc elements
260 $GLOBALS['_xh']=null;
261
262 /**
263 * Convert a string to the correct XML representation in a target charset
264 * To help correct communication of non-ascii chars inside strings, regardless
265 * of the charset used when sending requests, parsing them, sending responses
266 * and parsing responses, an option is to convert all non-ascii chars present in the message
267 * into their equivalent 'charset entity'. Charset entities enumerated this way
268 * are independent of the charset encoding used to transmit them, and all XML
269 * parsers are bound to understand them.
270 * Note that in the std case we are not sending a charset encoding mime type
271 * along with http headers, so we are bound by RFC 3023 to emit strict us-ascii.
272 *
273 * @todo do a bit of basic benchmarking (strtr vs. str_replace)
274 * @todo make usage of iconv() or recode_string() or mb_string() where available
275 */
276 function xmlrpc_encode_entitites($data, $src_encoding='', $dest_encoding='')
277 {
278 if ($src_encoding == '')
279 {
280 // lame, but we know no better...
281 $src_encoding = $GLOBALS['xmlrpc_internalencoding'];
282 }
283
284 switch(strtoupper($src_encoding.'_'.$dest_encoding))
285 {
286 case 'ISO-8859-1_':
287 case 'ISO-8859-1_US-ASCII':
288 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
289 $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
290 break;
291 case 'ISO-8859-1_UTF-8':
292 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
293 $escaped_data = utf8_encode($escaped_data);
294 break;
295 case 'ISO-8859-1_ISO-8859-1':
296 case 'US-ASCII_US-ASCII':
297 case 'US-ASCII_UTF-8':
298 case 'US-ASCII_':
299 case 'US-ASCII_ISO-8859-1':
300 case 'UTF-8_UTF-8':
301 //case 'CP1252_CP1252':
302 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
303 break;
304 case 'UTF-8_':
305 case 'UTF-8_US-ASCII':
306 case 'UTF-8_ISO-8859-1':
307 // NB: this will choke on invalid UTF-8, going most likely beyond EOF
308 $escaped_data = '';
309 // be kind to users creating string xmlrpcvals out of different php types
310 $data = (string) $data;
311 $ns = strlen ($data);
312 for ($nn = 0; $nn < $ns; $nn++)
313 {
314 $ch = $data[$nn];
315 $ii = ord($ch);
316 //1 7 0bbbbbbb (127)
317 if ($ii < 128)
318 {
319 /// @todo shall we replace this with a (supposedly) faster str_replace?
320 switch($ii){
321 case 34:
322 $escaped_data .= '&quot;';
323 break;
324 case 38:
325 $escaped_data .= '&amp;';
326 break;
327 case 39:
328 $escaped_data .= '&apos;';
329 break;
330 case 60:
331 $escaped_data .= '&lt;';
332 break;
333 case 62:
334 $escaped_data .= '&gt;';
335 break;
336 default:
337 $escaped_data .= $ch;
338 } // switch
339 }
340 //2 11 110bbbbb 10bbbbbb (2047)
341 else if ($ii>>5 == 6)
342 {
343 $b1 = ($ii & 31);
344 $ii = ord($data[$nn+1]);
345 $b2 = ($ii & 63);
346 $ii = ($b1 * 64) + $b2;
347 $ent = sprintf ('&#%d;', $ii);
348 $escaped_data .= $ent;
349 $nn += 1;
350 }
351 //3 16 1110bbbb 10bbbbbb 10bbbbbb
352 else if ($ii>>4 == 14)
353 {
354 $b1 = ($ii & 15);
355 $ii = ord($data[$nn+1]);
356 $b2 = ($ii & 63);
357 $ii = ord($data[$nn+2]);
358 $b3 = ($ii & 63);
359 $ii = ((($b1 * 64) + $b2) * 64) + $b3;
360 $ent = sprintf ('&#%d;', $ii);
361 $escaped_data .= $ent;
362 $nn += 2;
363 }
364 //4 21 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
365 else if ($ii>>3 == 30)
366 {
367 $b1 = ($ii & 7);
368 $ii = ord($data[$nn+1]);
369 $b2 = ($ii & 63);
370 $ii = ord($data[$nn+2]);
371 $b3 = ($ii & 63);
372 $ii = ord($data[$nn+3]);
373 $b4 = ($ii & 63);
374 $ii = ((((($b1 * 64) + $b2) * 64) + $b3) * 64) + $b4;
375 $ent = sprintf ('&#%d;', $ii);
376 $escaped_data .= $ent;
377 $nn += 3;
378 }
379 }
380 break;
381/*
382 case 'CP1252_':
383 case 'CP1252_US-ASCII':
384 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
385 $escaped_data = str_replace($GLOBALS['xml_iso88591_Entities']['in'], $GLOBALS['xml_iso88591_Entities']['out'], $escaped_data);
386 $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
387 break;
388 case 'CP1252_UTF-8':
389 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
390 /// @todo we could use real UTF8 chars here instead of xml entities... (note that utf_8 encode all allone will NOT convert them)
391 $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
392 $escaped_data = utf8_encode($escaped_data);
393 break;
394 case 'CP1252_ISO-8859-1':
395 $escaped_data = str_replace(array('&', '"', "'", '<', '>'), array('&amp;', '&quot;', '&apos;', '&lt;', '&gt;'), $data);
396 // 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...
397 $escaped_data = str_replace($GLOBALS['xml_cp1252_Entities']['in'], $GLOBALS['xml_cp1252_Entities']['out'], $escaped_data);
398 break;
399*/
400 default:
401 $escaped_data = '';
402 error_log("Converting from $src_encoding to $dest_encoding: not supported...");
403 }
404 return $escaped_data;
405 }
406
407 /// xml parser handler function for opening element tags
408 function xmlrpc_se($parser, $name, $attrs, $accept_single_vals=false)
409 {
410 // if invalid xmlrpc already detected, skip all processing
411 if ($GLOBALS['_xh']['isf'] < 2)
412 {
413 // check for correct element nesting
414 // top level element can only be of 2 types
415 /// @todo optimization creep: save this check into a bool variable, instead of using count() every time:
416 /// there is only a single top level element in xml anyway
417 if (count($GLOBALS['_xh']['stack']) == 0)
418 {
419 if ($name != 'METHODRESPONSE' && $name != 'METHODCALL' && (
420 $name != 'VALUE' && !$accept_single_vals))
421 {
422 $GLOBALS['_xh']['isf'] = 2;
423 $GLOBALS['_xh']['isf_reason'] = 'missing top level xmlrpc element';
424 return;
425 }
426 else
427 {
428 $GLOBALS['_xh']['rt'] = strtolower($name);
429 }
430 }
431 else
432 {
433 // not top level element: see if parent is OK
434 $parent = end($GLOBALS['_xh']['stack']);
435 if (!array_key_exists($name, $GLOBALS['xmlrpc_valid_parents']) || !in_array($parent, $GLOBALS['xmlrpc_valid_parents'][$name]))
436 {
437 $GLOBALS['_xh']['isf'] = 2;
438 $GLOBALS['_xh']['isf_reason'] = "xmlrpc element $name cannot be child of $parent";
439 return;
440 }
441 }
442
443 switch($name)
444 {
445 // optimize for speed switch cases: most common cases first
446 case 'VALUE':
447 /// @todo we could check for 2 VALUE elements inside a MEMBER or PARAM element
448 $GLOBALS['_xh']['vt']='value'; // indicator: no value found yet
449 $GLOBALS['_xh']['ac']='';
450 $GLOBALS['_xh']['lv']=1;
451 $GLOBALS['_xh']['php_class']=null;
452 break;
453 case 'I4':
454 case 'INT':
455 case 'STRING':
456 case 'BOOLEAN':
457 case 'DOUBLE':
458 case 'DATETIME.ISO8601':
459 case 'BASE64':
460 if ($GLOBALS['_xh']['vt']!='value')
461 {
462 //two data elements inside a value: an error occurred!
463 $GLOBALS['_xh']['isf'] = 2;
464 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
465 return;
466 }
467 $GLOBALS['_xh']['ac']=''; // reset the accumulator
468 break;
469 case 'STRUCT':
470 case 'ARRAY':
471 if ($GLOBALS['_xh']['vt']!='value')
472 {
473 //two data elements inside a value: an error occurred!
474 $GLOBALS['_xh']['isf'] = 2;
475 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
476 return;
477 }
478 // create an empty array to hold child values, and push it onto appropriate stack
479 $cur_val = array();
480 $cur_val['values'] = array();
481 $cur_val['type'] = $name;
482 // check for out-of-band information to rebuild php objs
483 // and in case it is found, save it
484 if (@isset($attrs['PHP_CLASS']))
485 {
486 $cur_val['php_class'] = $attrs['PHP_CLASS'];
487 }
488 $GLOBALS['_xh']['valuestack'][] = $cur_val;
489 $GLOBALS['_xh']['vt']='data'; // be prepared for a data element next
490 break;
491 case 'DATA':
492 if ($GLOBALS['_xh']['vt']!='data')
493 {
494 //two data elements inside a value: an error occurred!
495 $GLOBALS['_xh']['isf'] = 2;
496 $GLOBALS['_xh']['isf_reason'] = "found two data elements inside an array element";
497 return;
498 }
499 case 'METHODCALL':
500 case 'METHODRESPONSE':
501 case 'PARAMS':
502 // valid elements that add little to processing
503 break;
504 case 'METHODNAME':
505 case 'NAME':
506 /// @todo we could check for 2 NAME elements inside a MEMBER element
507 $GLOBALS['_xh']['ac']='';
508 break;
509 case 'FAULT':
510 $GLOBALS['_xh']['isf']=1;
511 break;
512 case 'MEMBER':
513 $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
514 //$GLOBALS['_xh']['ac']='';
515 // Drop trough intentionally
516 case 'PARAM':
517 // clear value type, so we can check later if no value has been passed for this param/member
518 $GLOBALS['_xh']['vt']=null;
519 break;
520 case 'NIL':
521 if ($GLOBALS['xmlrpc_null_extension'])
522 {
523 if ($GLOBALS['_xh']['vt']!='value')
524 {
525 //two data elements inside a value: an error occurred!
526 $GLOBALS['_xh']['isf'] = 2;
527 $GLOBALS['_xh']['isf_reason'] = "$name element following a {$GLOBALS['_xh']['vt']} element inside a single value";
528 return;
529 }
530 $GLOBALS['_xh']['ac']=''; // reset the accumulator
531 break;
532 }
533 // we do not support the <NIL/> extension, so
534 // drop through intentionally
535 default:
536 /// INVALID ELEMENT: RAISE ISF so that it is later recognized!!!
537 $GLOBALS['_xh']['isf'] = 2;
538 $GLOBALS['_xh']['isf_reason'] = "found not-xmlrpc xml element $name";
539 break;
540 }
541
542 // Save current element name to stack, to validate nesting
543 $GLOBALS['_xh']['stack'][] = $name;
544
545 /// @todo optimization creep: move this inside the big switch() above
546 if($name!='VALUE')
547 {
548 $GLOBALS['_xh']['lv']=0;
549 }
550 }
551 }
552
553 /// Used in decoding xml chunks that might represent single xmlrpc values
554 function xmlrpc_se_any($parser, $name, $attrs)
555 {
556 xmlrpc_se($parser, $name, $attrs, true);
557 }
558
559 /// xml parser handler function for close element tags
560 function xmlrpc_ee($parser, $name, $rebuild_xmlrpcvals = true)
561 {
562 if ($GLOBALS['_xh']['isf'] < 2)
563 {
564 // push this element name from stack
565 // NB: if XML validates, correct opening/closing is guaranteed and
566 // we do not have to check for $name == $curr_elem.
567 // we also checked for proper nesting at start of elements...
568 $curr_elem = array_pop($GLOBALS['_xh']['stack']);
569
570 switch($name)
571 {
572 case 'VALUE':
573 // This if() detects if no scalar was inside <VALUE></VALUE>
574 if ($GLOBALS['_xh']['vt']=='value')
575 {
576 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
577 $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcString'];
578 }
579
580 if ($rebuild_xmlrpcvals)
581 {
582 // build the xmlrpc val out of the data received, and substitute it
583 $temp = new xmlrpcval($GLOBALS['_xh']['value'], $GLOBALS['_xh']['vt']);
584 // in case we got info about underlying php class, save it
585 // in the object we're rebuilding
586 if (isset($GLOBALS['_xh']['php_class']))
587 $temp->_php_class = $GLOBALS['_xh']['php_class'];
588 // check if we are inside an array or struct:
589 // if value just built is inside an array, let's move it into array on the stack
590 $vscount = count($GLOBALS['_xh']['valuestack']);
591 if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
592 {
593 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $temp;
594 }
595 else
596 {
597 $GLOBALS['_xh']['value'] = $temp;
598 }
599 }
600 else
601 {
602 /// @todo this needs to treat correctly php-serialized objects,
603 /// since std deserializing is done by php_xmlrpc_decode,
604 /// which we will not be calling...
605 if (isset($GLOBALS['_xh']['php_class']))
606 {
607 }
608
609 // check if we are inside an array or struct:
610 // if value just built is inside an array, let's move it into array on the stack
611 $vscount = count($GLOBALS['_xh']['valuestack']);
612 if ($vscount && $GLOBALS['_xh']['valuestack'][$vscount-1]['type']=='ARRAY')
613 {
614 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][] = $GLOBALS['_xh']['value'];
615 }
616 }
617 break;
618 case 'BOOLEAN':
619 case 'I4':
620 case 'INT':
621 case 'STRING':
622 case 'DOUBLE':
623 case 'DATETIME.ISO8601':
624 case 'BASE64':
625 $GLOBALS['_xh']['vt']=strtolower($name);
626 /// @todo: optimization creep - remove the if/elseif cycle below
627 /// since the case() in which we are already did that
628 if ($name=='STRING')
629 {
630 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
631 }
632 elseif ($name=='DATETIME.ISO8601')
633 {
634 if (!preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $GLOBALS['_xh']['ac']))
635 {
636 error_log('XML-RPC: invalid value received in DATETIME: '.$GLOBALS['_xh']['ac']);
637 }
638 $GLOBALS['_xh']['vt']=$GLOBALS['xmlrpcDateTime'];
639 $GLOBALS['_xh']['value']=$GLOBALS['_xh']['ac'];
640 }
641 elseif ($name=='BASE64')
642 {
643 /// @todo check for failure of base64 decoding / catch warnings
644 $GLOBALS['_xh']['value']=base64_decode($GLOBALS['_xh']['ac']);
645 }
646 elseif ($name=='BOOLEAN')
647 {
648 // special case here: we translate boolean 1 or 0 into PHP
649 // constants true or false.
650 // Strings 'true' and 'false' are accepted, even though the
651 // spec never mentions them (see eg. Blogger api docs)
652 // NB: this simple checks helps a lot sanitizing input, ie no
653 // security problems around here
654 if ($GLOBALS['_xh']['ac']=='1' || strcasecmp($GLOBALS['_xh']['ac'], 'true') == 0)
655 {
656 $GLOBALS['_xh']['value']=true;
657 }
658 else
659 {
660 // log if receiveing something strange, even though we set the value to false anyway
661 if ($GLOBALS['_xh']['ac']!='0' && strcasecmp($GLOBALS['_xh']['ac'], 'false') != 0)
662 error_log('XML-RPC: invalid value received in BOOLEAN: '.$GLOBALS['_xh']['ac']);
663 $GLOBALS['_xh']['value']=false;
664 }
665 }
666 elseif ($name=='DOUBLE')
667 {
668 // we have a DOUBLE
669 // we must check that only 0123456789-.<space> are characters here
670 // NOTE: regexp could be much stricter than this...
671 if (!preg_match('/^[+-eE0123456789 \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 DOUBLE: '.$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']=(double)$GLOBALS['_xh']['ac'];
681 }
682 }
683 else
684 {
685 // we have an I4/INT
686 // we must check that only 0123456789-<space> are characters here
687 if (!preg_match('/^[+-]?[0123456789 \t]+$/', $GLOBALS['_xh']['ac']))
688 {
689 /// @todo find a better way of throwing an error than this!
690 error_log('XML-RPC: non numeric value received in INT: '.$GLOBALS['_xh']['ac']);
691 $GLOBALS['_xh']['value']='ERROR_NON_NUMERIC_FOUND';
692 }
693 else
694 {
695 // it's ok, add it on
696 $GLOBALS['_xh']['value']=(int)$GLOBALS['_xh']['ac'];
697 }
698 }
699 //$GLOBALS['_xh']['ac']=''; // is this necessary?
700 $GLOBALS['_xh']['lv']=3; // indicate we've found a value
701 break;
702 case 'NAME':
703 $GLOBALS['_xh']['valuestack'][count($GLOBALS['_xh']['valuestack'])-1]['name'] = $GLOBALS['_xh']['ac'];
704 break;
705 case 'MEMBER':
706 //$GLOBALS['_xh']['ac']=''; // is this necessary?
707 // add to array in the stack the last element built,
708 // unless no VALUE was found
709 if ($GLOBALS['_xh']['vt'])
710 {
711 $vscount = count($GLOBALS['_xh']['valuestack']);
712 $GLOBALS['_xh']['valuestack'][$vscount-1]['values'][$GLOBALS['_xh']['valuestack'][$vscount-1]['name']] = $GLOBALS['_xh']['value'];
713 } else
714 error_log('XML-RPC: missing VALUE inside STRUCT in received xml');
715 break;
716 case 'DATA':
717 //$GLOBALS['_xh']['ac']=''; // is this necessary?
718 $GLOBALS['_xh']['vt']=null; // reset this to check for 2 data elements in a row - even if they're empty
719 break;
720 case 'STRUCT':
721 case 'ARRAY':
722 // fetch out of stack array of values, and promote it to current value
723 $curr_val = array_pop($GLOBALS['_xh']['valuestack']);
724 $GLOBALS['_xh']['value'] = $curr_val['values'];
725 $GLOBALS['_xh']['vt']=strtolower($name);
726 if (isset($curr_val['php_class']))
727 {
728 $GLOBALS['_xh']['php_class'] = $curr_val['php_class'];
729 }
730 break;
731 case 'PARAM':
732 // add to array of params the current value,
733 // unless no VALUE was found
734 if ($GLOBALS['_xh']['vt'])
735 {
736 $GLOBALS['_xh']['params'][]=$GLOBALS['_xh']['value'];
737 $GLOBALS['_xh']['pt'][]=$GLOBALS['_xh']['vt'];
738 }
739 else
740 error_log('XML-RPC: missing VALUE inside PARAM in received xml');
741 break;
742 case 'METHODNAME':
743 $GLOBALS['_xh']['method']=preg_replace('/^[\n\r\t ]+/', '', $GLOBALS['_xh']['ac']);
744 break;
745 case 'NIL':
746 if ($GLOBALS['xmlrpc_null_extension'])
747 {
748 $GLOBALS['_xh']['vt']='null';
749 $GLOBALS['_xh']['value']=null;
750 $GLOBALS['_xh']['lv']=3;
751 break;
752 }
753 // drop through intentionally if nil extension not enabled
754 case 'PARAMS':
755 case 'FAULT':
756 case 'METHODCALL':
757 case 'METHORESPONSE':
758 break;
759 default:
760 // End of INVALID ELEMENT!
761 // shall we add an assert here for unreachable code???
762 break;
763 }
764 }
765 }
766
767 /// Used in decoding xmlrpc requests/responses without rebuilding xmlrpc values
768 function xmlrpc_ee_fast($parser, $name)
769 {
770 xmlrpc_ee($parser, $name, false);
771 }
772
773 /// xml parser handler function for character data
774 function xmlrpc_cd($parser, $data)
775 {
776 // skip processing if xml fault already detected
777 if ($GLOBALS['_xh']['isf'] < 2)
778 {
779 // "lookforvalue==3" means that we've found an entire value
780 // and should discard any further character data
781 if($GLOBALS['_xh']['lv']!=3)
782 {
783 // G. Giunta 2006-08-23: useless change of 'lv' from 1 to 2
784 //if($GLOBALS['_xh']['lv']==1)
785 //{
786 // if we've found text and we're just in a <value> then
787 // say we've found a value
788 //$GLOBALS['_xh']['lv']=2;
789 //}
790 // we always initialize the accumulator before starting parsing, anyway...
791 //if(!@isset($GLOBALS['_xh']['ac']))
792 //{
793 // $GLOBALS['_xh']['ac'] = '';
794 //}
795 $GLOBALS['_xh']['ac'].=$data;
796 }
797 }
798 }
799
800 /// xml parser handler function for 'other stuff', ie. not char data or
801 /// element start/end tag. In fact it only gets called on unknown entities...
802 function xmlrpc_dh($parser, $data)
803 {
804 // skip processing if xml fault already detected
805 if ($GLOBALS['_xh']['isf'] < 2)
806 {
807 if(substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';')
808 {
809 // G. Giunta 2006-08-25: useless change of 'lv' from 1 to 2
810 //if($GLOBALS['_xh']['lv']==1)
811 //{
812 // $GLOBALS['_xh']['lv']=2;
813 //}
814 $GLOBALS['_xh']['ac'].=$data;
815 }
816 }
817 return true;
818 }
819
820 class xmlrpc_client
821 {
822 var $path;
823 var $server;
824 var $port=0;
825 var $method='http';
826 var $errno;
827 var $errstr;
828 var $debug=0;
829 var $username='';
830 var $password='';
831 var $authtype=1;
832 var $cert='';
833 var $certpass='';
834 var $cacert='';
835 var $cacertdir='';
836 var $key='';
837 var $keypass='';
838 var $verifypeer=true;
839 var $verifyhost=1;
840 var $no_multicall=false;
841 var $proxy='';
842 var $proxyport=0;
843 var $proxy_user='';
844 var $proxy_pass='';
845 var $proxy_authtype=1;
846 var $cookies=array();
847 /**
848 * List of http compression methods accepted by the client for responses.
849 * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
850 *
851 * NNB: you can set it to any non-empty array for HTTP11 and HTTPS, since
852 * in those cases it will be up to CURL to decide the compression methods
853 * it supports. You might check for the presence of 'zlib' in the output of
854 * curl_version() to determine wheter compression is supported or not
855 */
856 var $accepted_compression = array();
857 /**
858 * Name of compression scheme to be used for sending requests.
859 * Either null, gzip or deflate
860 */
861 var $request_compression = '';
862 /**
863 * CURL handle: used for keep-alive connections (PHP 4.3.8 up, see:
864 * http://curl.haxx.se/docs/faq.html#7.3)
865 */
866 var $xmlrpc_curl_handle = null;
867 /// Wheter to use persistent connections for http 1.1 and https
868 var $keepalive = false;
869 /// Charset encodings that can be decoded without problems by the client
870 var $accepted_charset_encodings = array();
871 /// Charset encoding to be used in serializing request. NULL = use ASCII
872 var $request_charset_encoding = '';
873 /**
874 * Decides the content of xmlrpcresp objects returned by calls to send()
875 * valid strings are 'xmlrpcvals', 'phpvals' or 'xml'
876 */
877 var $return_type = 'xmlrpcvals';
878
879 /**
880 * @param string $path either the complete server URL or the PATH part of the xmlrc server URL, e.g. /xmlrpc/server.php
881 * @param string $server the server name / ip address
882 * @param integer $port the port the server is listening on, defaults to 80 or 443 depending on protocol used
883 * @param string $method the http protocol variant: defaults to 'http', 'https' and 'http11' can be used if CURL is installed
884 */
885 function xmlrpc_client($path, $server='', $port='', $method='')
886 {
887 // allow user to specify all params in $path
888 if($server == '' and $port == '' and $method == '')
889 {
890 $parts = parse_url($path);
891 $server = $parts['host'];
892 $path = isset($parts['path']) ? $parts['path'] : '';
893 if(isset($parts['query']))
894 {
895 $path .= '?'.$parts['query'];
896 }
897 if(isset($parts['fragment']))
898 {
899 $path .= '#'.$parts['fragment'];
900 }
901 if(isset($parts['port']))
902 {
903 $port = $parts['port'];
904 }
905 if(isset($parts['scheme']))
906 {
907 $method = $parts['scheme'];
908 }
909 if(isset($parts['user']))
910 {
911 $this->username = $parts['user'];
912 }
913 if(isset($parts['pass']))
914 {
915 $this->password = $parts['pass'];
916 }
917 }
918 if($path == '' || $path[0] != '/')
919 {
920 $this->path='/'.$path;
921 }
922 else
923 {
924 $this->path=$path;
925 }
926 $this->server=$server;
927 if($port != '')
928 {
929 $this->port=$port;
930 }
931 if($method != '')
932 {
933 $this->method=$method;
934 }
935
936 // if ZLIB is enabled, let the client by default accept compressed responses
937 if(function_exists('gzinflate') || (
938 function_exists('curl_init') && (($info = curl_version()) &&
939 ((is_string($info) && strpos($info, 'zlib') !== null) || isset($info['libz_version'])))
940 ))
941 {
942 $this->accepted_compression = array('gzip', 'deflate');
943 }
944
945 // keepalives: enabled by default ONLY for PHP >= 4.3.8
946 // (see http://curl.haxx.se/docs/faq.html#7.3)
947 if(version_compare(phpversion(), '4.3.8') >= 0)
948 {
949 $this->keepalive = true;
950 }
951
952 // by default the xml parser can support these 3 charset encodings
953 $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
954 }
955
956 /**
957 * Enables/disables the echoing to screen of the xmlrpc responses received
958 * @param integer $debug values 0, 1 and 2 are supported (2 = echo sent msg too, before received response)
959 * @access public
960 */
961 function setDebug($in)
962 {
963 $this->debug=$in;
964 }
965
966 /**
967 * Add some http BASIC AUTH credentials, used by the client to authenticate
968 * @param string $u username
969 * @param string $p password
970 * @param integer $t auth type. See curl_setopt man page for supported auth types. Defaults to CURLAUTH_BASIC (basic auth)
971 * @access public
972 */
973 function setCredentials($u, $p, $t=1)
974 {
975 $this->username=$u;
976 $this->password=$p;
977 $this->authtype=$t;
978 }
979
980 /**
981 * Add a client-side https certificate
982 * @param string $cert
983 * @param string $certpass
984 * @access public
985 */
986 function setCertificate($cert, $certpass)
987 {
988 $this->cert = $cert;
989 $this->certpass = $certpass;
990 }
991
992 /**
993 * Add a CA certificate to verify server with (see man page about
994 * CURLOPT_CAINFO for more details
995 * @param string $cacert certificate file name (or dir holding certificates)
996 * @param bool $is_dir set to true to indicate cacert is a dir. defaults to false
997 * @access public
998 */
999 function setCaCertificate($cacert, $is_dir=false)
1000 {
1001 if ($is_dir)
1002 {
1003 $this->cacertdir = $cacert;
1004 }
1005 else
1006 {
1007 $this->cacert = $cacert;
1008 }
1009 }
1010
1011 /**
1012 * Set attributes for SSL communication: private SSL key
1013 * NB: does not work in older php/curl installs
1014 * Thanks to Daniel Convissor
1015 * @param string $key The name of a file containing a private SSL key
1016 * @param string $keypass The secret password needed to use the private SSL key
1017 * @access public
1018 */
1019 function setKey($key, $keypass)
1020 {
1021 $this->key = $key;
1022 $this->keypass = $keypass;
1023 }
1024
1025 /**
1026 * Set attributes for SSL communication: verify server certificate
1027 * @param bool $i enable/disable verification of peer certificate
1028 * @access public
1029 */
1030 function setSSLVerifyPeer($i)
1031 {
1032 $this->verifypeer = $i;
1033 }
1034
1035 /**
1036 * Set attributes for SSL communication: verify match of server cert w. hostname
1037 * @param int $i
1038 * @access public
1039 */
1040 function setSSLVerifyHost($i)
1041 {
1042 $this->verifyhost = $i;
1043 }
1044
1045 /**
1046 * Set proxy info
1047 * @param string $proxyhost
1048 * @param string $proxyport Defaults to 8080 for HTTP and 443 for HTTPS
1049 * @param string $proxyusername Leave blank if proxy has public access
1050 * @param string $proxypassword Leave blank if proxy has public access
1051 * @param int $proxyauthtype set to constant CURLAUTH_NTLM to use NTLM auth with proxy
1052 * @access public
1053 */
1054 function setProxy($proxyhost, $proxyport, $proxyusername = '', $proxypassword = '', $proxyauthtype = 1)
1055 {
1056 $this->proxy = $proxyhost;
1057 $this->proxyport = $proxyport;
1058 $this->proxy_user = $proxyusername;
1059 $this->proxy_pass = $proxypassword;
1060 $this->proxy_authtype = $proxyauthtype;
1061 }
1062
1063 /**
1064 * Enables/disables reception of compressed xmlrpc responses.
1065 * Note that enabling reception of compressed responses merely adds some standard
1066 * http headers to xmlrpc requests. It is up to the xmlrpc server to return
1067 * compressed responses when receiving such requests.
1068 * @param string $compmethod either 'gzip', 'deflate', 'any' or ''
1069 * @access public
1070 */
1071 function setAcceptedCompression($compmethod)
1072 {
1073 if ($compmethod == 'any')
1074 $this->accepted_compression = array('gzip', 'deflate');
1075 else
1076 $this->accepted_compression = array($compmethod);
1077 }
1078
1079 /**
1080 * Enables/disables http compression of xmlrpc request.
1081 * Take care when sending compressed requests: servers might not support them
1082 * (and automatic fallback to uncompressed requests is not yet implemented)
1083 * @param string $compmethod either 'gzip', 'deflate' or ''
1084 * @access public
1085 */
1086 function setRequestCompression($compmethod)
1087 {
1088 $this->request_compression = $compmethod;
1089 }
1090
1091 /**
1092 * Adds a cookie to list of cookies that will be sent to server.
1093 * NB: setting any param but name and value will turn the cookie into a 'version 1' cookie:
1094 * do not do it unless you know what you are doing
1095 * @param string $name
1096 * @param string $value
1097 * @param string $path
1098 * @param string $domain
1099 * @param int $port
1100 * @access public
1101 *
1102 * @todo check correctness of urlencoding cookie value (copied from php way of doing it...)
1103 */
1104 function setCookie($name, $value='', $path='', $domain='', $port=null)
1105 {
1106 $this->cookies[$name]['value'] = urlencode($value);
1107 if ($path || $domain || $port)
1108 {
1109 $this->cookies[$name]['path'] = $path;
1110 $this->cookies[$name]['domain'] = $domain;
1111 $this->cookies[$name]['port'] = $port;
1112 $this->cookies[$name]['version'] = 1;
1113 }
1114 else
1115 {
1116 $this->cookies[$name]['version'] = 0;
1117 }
1118 }
1119
1120 /**
1121 * Send an xmlrpc request
1122 * @param mixed $msg The message object, or an array of messages for using multicall, or the complete xml representation of a request
1123 * @param integer $timeout Connection timeout, in seconds, If unspecified, a platform specific timeout will apply
1124 * @param string $method if left unspecified, the http protocol chosen during creation of the object will be used
1125 * @return xmlrpcresp
1126 * @access public
1127 */
1128 function& send($msg, $timeout=0, $method='')
1129 {
1130 // if user deos not specify http protocol, use native method of this client
1131 // (i.e. method set during call to constructor)
1132 if($method == '')
1133 {
1134 $method = $this->method;
1135 }
1136
1137 if(is_array($msg))
1138 {
1139 // $msg is an array of xmlrpcmsg's
1140 $r = $this->multicall($msg, $timeout, $method);
1141 return $r;
1142 }
1143 elseif(is_string($msg))
1144 {
1145 $n = new xmlrpcmsg('');
1146 $n->payload = $msg;
1147 $msg = $n;
1148 }
1149
1150 // where msg is an xmlrpcmsg
1151 $msg->debug=$this->debug;
1152
1153 if($method == 'https')
1154 {
1155 $r =& $this->sendPayloadHTTPS(
1156 $msg,
1157 $this->server,
1158 $this->port,
1159 $timeout,
1160 $this->username,
1161 $this->password,
1162 $this->authtype,
1163 $this->cert,
1164 $this->certpass,
1165 $this->cacert,
1166 $this->cacertdir,
1167 $this->proxy,
1168 $this->proxyport,
1169 $this->proxy_user,
1170 $this->proxy_pass,
1171 $this->proxy_authtype,
1172 $this->keepalive,
1173 $this->key,
1174 $this->keypass
1175 );
1176 }
1177 elseif($method == 'http11')
1178 {
1179 $r =& $this->sendPayloadCURL(
1180 $msg,
1181 $this->server,
1182 $this->port,
1183 $timeout,
1184 $this->username,
1185 $this->password,
1186 $this->authtype,
1187 null,
1188 null,
1189 null,
1190 null,
1191 $this->proxy,
1192 $this->proxyport,
1193 $this->proxy_user,
1194 $this->proxy_pass,
1195 $this->proxy_authtype,
1196 'http',
1197 $this->keepalive
1198 );
1199 }
1200 else
1201 {
1202 $r =& $this->sendPayloadHTTP10(
1203 $msg,
1204 $this->server,
1205 $this->port,
1206 $timeout,
1207 $this->username,
1208 $this->password,
1209 $this->authtype,
1210 $this->proxy,
1211 $this->proxyport,
1212 $this->proxy_user,
1213 $this->proxy_pass,
1214 $this->proxy_authtype
1215 );
1216 }
1217
1218 return $r;
1219 }
1220
1221 /**
1222 * @access private
1223 */
1224 function &sendPayloadHTTP10($msg, $server, $port, $timeout=0,
1225 $username='', $password='', $authtype=1, $proxyhost='',
1226 $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1)
1227 {
1228 if($port==0)
1229 {
1230 $port=80;
1231 }
1232
1233 // Only create the payload if it was not created previously
1234 if(empty($msg->payload))
1235 {
1236 $msg->createPayload($this->request_charset_encoding);
1237 }
1238
1239 $payload = $msg->payload;
1240 // Deflate request body and set appropriate request headers
1241 if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1242 {
1243 if($this->request_compression == 'gzip')
1244 {
1245 $a = @gzencode($payload);
1246 if($a)
1247 {
1248 $payload = $a;
1249 $encoding_hdr = "Content-Encoding: gzip\r\n";
1250 }
1251 }
1252 else
1253 {
1254 $a = @gzcompress($payload);
1255 if($a)
1256 {
1257 $payload = $a;
1258 $encoding_hdr = "Content-Encoding: deflate\r\n";
1259 }
1260 }
1261 }
1262 else
1263 {
1264 $encoding_hdr = '';
1265 }
1266
1267 // thanks to Grant Rauscher <grant7@firstworld.net> for this
1268 $credentials='';
1269 if($username!='')
1270 {
1271 $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n";
1272 if ($authtype != 1)
1273 {
1274 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported with HTTP 1.0');
1275 }
1276 }
1277
1278 $accepted_encoding = '';
1279 if(is_array($this->accepted_compression) && count($this->accepted_compression))
1280 {
1281 $accepted_encoding = 'Accept-Encoding: ' . implode(', ', $this->accepted_compression) . "\r\n";
1282 }
1283
1284 $proxy_credentials = '';
1285 if($proxyhost)
1286 {
1287 if($proxyport == 0)
1288 {
1289 $proxyport = 8080;
1290 }
1291 $connectserver = $proxyhost;
1292 $connectport = $proxyport;
1293 $uri = 'http://'.$server.':'.$port.$this->path;
1294 if($proxyusername != '')
1295 {
1296 if ($proxyauthtype != 1)
1297 {
1298 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported with HTTP 1.0');
1299 }
1300 $proxy_credentials = 'Proxy-Authorization: Basic ' . base64_encode($proxyusername.':'.$proxypassword) . "\r\n";
1301 }
1302 }
1303 else
1304 {
1305 $connectserver = $server;
1306 $connectport = $port;
1307 $uri = $this->path;
1308 }
1309
1310 // Cookie generation, as per rfc2965 (version 1 cookies) or
1311 // netscape's rules (version 0 cookies)
1312 $cookieheader='';
1313 if (count($this->cookies))
1314 {
1315 $version = '';
1316 foreach ($this->cookies as $name => $cookie)
1317 {
1318 if ($cookie['version'])
1319 {
1320 $version = ' $Version="' . $cookie['version'] . '";';
1321 $cookieheader .= ' ' . $name . '="' . $cookie['value'] . '";';
1322 if ($cookie['path'])
1323 $cookieheader .= ' $Path="' . $cookie['path'] . '";';
1324 if ($cookie['domain'])
1325 $cookieheader .= ' $Domain="' . $cookie['domain'] . '";';
1326 if ($cookie['port'])
1327 $cookieheader .= ' $Port="' . $cookie['port'] . '";';
1328 }
1329 else
1330 {
1331 $cookieheader .= ' ' . $name . '=' . $cookie['value'] . ";";
1332 }
1333 }
1334 $cookieheader = 'Cookie:' . $version . substr($cookieheader, 0, -1) . "\r\n";
1335 }
1336
1337 $op= 'POST ' . $uri. " HTTP/1.0\r\n" .
1338 'User-Agent: ' . $GLOBALS['xmlrpcName'] . ' ' . $GLOBALS['xmlrpcVersion'] . "\r\n" .
1339 'Host: '. $server . ':' . $port . "\r\n" .
1340 $credentials .
1341 $proxy_credentials .
1342 $accepted_encoding .
1343 $encoding_hdr .
1344 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings) . "\r\n" .
1345 $cookieheader .
1346 'Content-Type: ' . $msg->content_type . "\r\nContent-Length: " .
1347 strlen($payload) . "\r\n\r\n" .
1348 $payload;
1349
1350 if($this->debug > 1)
1351 {
1352 print "<PRE>\n---SENDING---\n" . htmlentities($op) . "\n---END---\n</PRE>";
1353 // let the client see this now in case http times out...
1354 flush();
1355 }
1356
1357 if($timeout>0)
1358 {
1359 $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr, $timeout);
1360 }
1361 else
1362 {
1363 $fp=@fsockopen($connectserver, $connectport, $this->errno, $this->errstr);
1364 }
1365 if($fp)
1366 {
1367 if($timeout>0 && function_exists('stream_set_timeout'))
1368 {
1369 stream_set_timeout($fp, $timeout);
1370 }
1371 }
1372 else
1373 {
1374 $this->errstr='Connect error: '.$this->errstr;
1375 $r= new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr . ' (' . $this->errno . ')');
1376 return $r;
1377 }
1378
1379 if(!fputs($fp, $op, strlen($op)))
1380 {
1381 fclose($fp);
1382 $this->errstr='Write error';
1383 $r= new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $this->errstr);
1384 return $r;
1385 }
1386 else
1387 {
1388 // reset errno and errstr on succesful socket connection
1389 $this->errstr = '';
1390 }
1391 // G. Giunta 2005/10/24: close socket before parsing.
1392 // should yeld slightly better execution times, and make easier recursive calls (e.g. to follow http redirects)
1393 $ipd='';
1394 do
1395 {
1396 // shall we check for $data === FALSE?
1397 // as per the manual, it signals an error
1398 $ipd.=fread($fp, 32768);
1399 } while(!feof($fp));
1400 fclose($fp);
1401 $r =& $msg->parseResponse($ipd, false, $this->return_type);
1402 return $r;
1403
1404 }
1405
1406 /**
1407 * @access private
1408 */
1409 function &sendPayloadHTTPS($msg, $server, $port, $timeout=0, $username='',
1410 $password='', $authtype=1, $cert='',$certpass='', $cacert='', $cacertdir='',
1411 $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1,
1412 $keepalive=false, $key='', $keypass='')
1413 {
1414 $r =& $this->sendPayloadCURL($msg, $server, $port, $timeout, $username,
1415 $password, $authtype, $cert, $certpass, $cacert, $cacertdir, $proxyhost, $proxyport,
1416 $proxyusername, $proxypassword, $proxyauthtype, 'https', $keepalive, $key, $keypass);
1417 return $r;
1418 }
1419
1420 /**
1421 * Contributed by Justin Miller <justin@voxel.net>
1422 * Requires curl to be built into PHP
1423 * NB: CURL versions before 7.11.10 cannot use proxy to talk to https servers!
1424 * @access private
1425 */
1426 function &sendPayloadCURL($msg, $server, $port, $timeout=0, $username='',
1427 $password='', $authtype=1, $cert='', $certpass='', $cacert='', $cacertdir='',
1428 $proxyhost='', $proxyport=0, $proxyusername='', $proxypassword='', $proxyauthtype=1, $method='https',
1429 $keepalive=false, $key='', $keypass='')
1430 {
1431 if(!function_exists('curl_init'))
1432 {
1433 $this->errstr='CURL unavailable on this install';
1434 $r= new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_curl'], $GLOBALS['xmlrpcstr']['no_curl']);
1435 return $r;
1436 }
1437 if($method == 'https')
1438 {
1439 if(($info = curl_version()) &&
1440 ((is_string($info) && strpos($info, 'OpenSSL') === null) || (is_array($info) && !isset($info['ssl_version']))))
1441 {
1442 $this->errstr='SSL unavailable on this install';
1443 $r= new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_ssl'], $GLOBALS['xmlrpcstr']['no_ssl']);
1444 return $r;
1445 }
1446 }
1447
1448 if($port == 0)
1449 {
1450 if($method == 'http')
1451 {
1452 $port = 80;
1453 }
1454 else
1455 {
1456 $port = 443;
1457 }
1458 }
1459
1460 // Only create the payload if it was not created previously
1461 if(empty($msg->payload))
1462 {
1463 $msg->createPayload($this->request_charset_encoding);
1464 }
1465
1466 // Deflate request body and set appropriate request headers
1467 $payload = $msg->payload;
1468 if(function_exists('gzdeflate') && ($this->request_compression == 'gzip' || $this->request_compression == 'deflate'))
1469 {
1470 if($this->request_compression == 'gzip')
1471 {
1472 $a = @gzencode($payload);
1473 if($a)
1474 {
1475 $payload = $a;
1476 $encoding_hdr = 'Content-Encoding: gzip';
1477 }
1478 }
1479 else
1480 {
1481 $a = @gzcompress($payload);
1482 if($a)
1483 {
1484 $payload = $a;
1485 $encoding_hdr = 'Content-Encoding: deflate';
1486 }
1487 }
1488 }
1489 else
1490 {
1491 $encoding_hdr = '';
1492 }
1493
1494 if($this->debug > 1)
1495 {
1496 print "<PRE>\n---SENDING---\n" . htmlentities($payload) . "\n---END---\n</PRE>";
1497 // let the client see this now in case http times out...
1498 flush();
1499 }
1500
1501 if(!$keepalive || !$this->xmlrpc_curl_handle)
1502 {
1503 $curl = curl_init($method . '://' . $server . ':' . $port . $this->path);
1504 if($keepalive)
1505 {
1506 $this->xmlrpc_curl_handle = $curl;
1507 }
1508 }
1509 else
1510 {
1511 $curl = $this->xmlrpc_curl_handle;
1512 }
1513
1514 // results into variable
1515 curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
1516
1517 if($this->debug)
1518 {
1519 curl_setopt($curl, CURLOPT_VERBOSE, 1);
1520 }
1521 curl_setopt($curl, CURLOPT_USERAGENT, $GLOBALS['xmlrpcName'].' '.$GLOBALS['xmlrpcVersion']);
1522 // required for XMLRPC: post the data
1523 curl_setopt($curl, CURLOPT_POST, 1);
1524 // the data
1525 curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
1526
1527 // return the header too
1528 curl_setopt($curl, CURLOPT_HEADER, 1);
1529
1530 // will only work with PHP >= 5.0
1531 // NB: if we set an empty string, CURL will add http header indicating
1532 // ALL methods it is supporting. This is possibly a better option than
1533 // letting the user tell what curl can / cannot do...
1534 if(is_array($this->accepted_compression) && count($this->accepted_compression))
1535 {
1536 //curl_setopt($curl, CURLOPT_ENCODING, implode(',', $this->accepted_compression));
1537 // empty string means 'any supported by CURL' (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1538 if (count($this->accepted_compression) == 1)
1539 {
1540 curl_setopt($curl, CURLOPT_ENCODING, $this->accepted_compression[0]);
1541 }
1542 else
1543 curl_setopt($curl, CURLOPT_ENCODING, '');
1544 }
1545 // extra headers
1546 $headers = array('Content-Type: ' . $msg->content_type , 'Accept-Charset: ' . implode(',', $this->accepted_charset_encodings));
1547 // if no keepalive is wanted, let the server know it in advance
1548 if(!$keepalive)
1549 {
1550 $headers[] = 'Connection: close';
1551 }
1552 // request compression header
1553 if($encoding_hdr)
1554 {
1555 $headers[] = $encoding_hdr;
1556 }
1557
1558 curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
1559 // timeout is borked
1560 if($timeout)
1561 {
1562 curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1);
1563 }
1564
1565 if($username && $password)
1566 {
1567 curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password);
1568 if (defined('CURLOPT_HTTPAUTH'))
1569 {
1570 curl_setopt($curl, CURLOPT_HTTPAUTH, $authtype);
1571 }
1572 else if ($authtype != 1)
1573 {
1574 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth is supported by the current PHP/curl install');
1575 }
1576 }
1577
1578 if($method == 'https')
1579 {
1580 // set cert file
1581 if($cert)
1582 {
1583 curl_setopt($curl, CURLOPT_SSLCERT, $cert);
1584 }
1585 // set cert password
1586 if($certpass)
1587 {
1588 curl_setopt($curl, CURLOPT_SSLCERTPASSWD, $certpass);
1589 }
1590 // whether to verify remote host's cert
1591 curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer);
1592 // set ca certificates file/dir
1593 if($cacert)
1594 {
1595 curl_setopt($curl, CURLOPT_CAINFO, $cacert);
1596 }
1597 if($cacertdir)
1598 {
1599 curl_setopt($curl, CURLOPT_CAPATH, $cacertdir);
1600 }
1601 // set key file (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1602 if($key)
1603 {
1604 curl_setopt($curl, CURLOPT_SSLKEY, $key);
1605 }
1606 // set key password (shall we catch errors in case CURLOPT_SSLKEY undefined ?)
1607 if($keypass)
1608 {
1609 curl_setopt($curl, CURLOPT_SSLKEYPASSWD, $keypass);
1610 }
1611 // 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
1612 curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost);
1613 }
1614
1615 // proxy info
1616 if($proxyhost)
1617 {
1618 if($proxyport == 0)
1619 {
1620 $proxyport = 8080; // NB: even for HTTPS, local connection is on port 8080
1621 }
1622 curl_setopt($curl, CURLOPT_PROXY, $proxyhost.':'.$proxyport);
1623 //curl_setopt($curl, CURLOPT_PROXYPORT,$proxyport);
1624 if($proxyusername)
1625 {
1626 curl_setopt($curl, CURLOPT_PROXYUSERPWD, $proxyusername.':'.$proxypassword);
1627 if (defined('CURLOPT_PROXYAUTH'))
1628 {
1629 curl_setopt($curl, CURLOPT_PROXYAUTH, $proxyauthtype);
1630 }
1631 else if ($proxyauthtype != 1)
1632 {
1633 error_log('XML-RPC: xmlrpc_client::send: warning. Only Basic auth to proxy is supported by the current PHP/curl install');
1634 }
1635 }
1636 }
1637
1638 // NB: should we build cookie http headers by hand rather than let CURL do it?
1639 // the following code does not honour 'expires', 'path' and 'domain' cookie attributes
1640 // set to client obj the the user...
1641 if (count($this->cookies))
1642 {
1643 $cookieheader = '';
1644 foreach ($this->cookies as $name => $cookie)
1645 {
1646 $cookieheader .= $name . '=' . $cookie['value'] . '; ';
1647 }
1648 curl_setopt($curl, CURLOPT_COOKIE, substr($cookieheader, 0, -2));
1649 }
1650
1651 $result = curl_exec($curl);
1652
1653 if ($this->debug > 1)
1654 {
1655 print "<PRE>\n---CURL INFO---\n";
1656 foreach(curl_getinfo($curl) as $name => $val)
1657 print $name . ': ' . htmlentities($val). "\n";
1658 print "---END---\n</PRE>";
1659 }
1660
1661 if(!$result) /// @todo we should use a better check here - what if we get back '' or '0'?
1662 {
1663 $this->errstr='no response';
1664 $resp= new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['curl_fail'], $GLOBALS['xmlrpcstr']['curl_fail']. ': '. curl_error($curl));
1665 curl_close($curl);
1666 if($keepalive)
1667 {
1668 $this->xmlrpc_curl_handle = null;
1669 }
1670 }
1671 else
1672 {
1673 if(!$keepalive)
1674 {
1675 curl_close($curl);
1676 }
1677 $resp =& $msg->parseResponse($result, true, $this->return_type);
1678 }
1679 return $resp;
1680 }
1681
1682 /**
1683 * Send an array of request messages and return an array of responses.
1684 * Unless $this->no_multicall has been set to true, it will try first
1685 * to use one single xmlrpc call to server method system.multicall, and
1686 * revert to sending many successive calls in case of failure.
1687 * This failure is also stored in $this->no_multicall for subsequent calls.
1688 * Unfortunately, there is no server error code universally used to denote
1689 * the fact that multicall is unsupported, so there is no way to reliably
1690 * distinguish between that and a temporary failure.
1691 * If you are sure that server supports multicall and do not want to
1692 * fallback to using many single calls, set the fourth parameter to FALSE.
1693 *
1694 * NB: trying to shoehorn extra functionality into existing syntax has resulted
1695 * in pretty much convoluted code...
1696 *
1697 * @param array $msgs an array of xmlrpcmsg objects
1698 * @param integer $timeout connection timeout (in seconds)
1699 * @param string $method the http protocol variant to be used
1700 * @param boolean fallback When true, upon receiveing an error during multicall, multiple single calls will be attempted
1701 * @return array
1702 * @access public
1703 */
1704 function multicall($msgs, $timeout=0, $method='', $fallback=true)
1705 {
1706 if ($method == '')
1707 {
1708 $method = $this->method;
1709 }
1710 if(!$this->no_multicall)
1711 {
1712 $results = $this->_try_multicall($msgs, $timeout, $method);
1713 if(is_array($results))
1714 {
1715 // System.multicall succeeded
1716 return $results;
1717 }
1718 else
1719 {
1720 // either system.multicall is unsupported by server,
1721 // or call failed for some other reason.
1722 if ($fallback)
1723 {
1724 // Don't try it next time...
1725 $this->no_multicall = true;
1726 }
1727 else
1728 {
1729 if (is_a($results, 'xmlrpcresp'))
1730 {
1731 $result = $results;
1732 }
1733 else
1734 {
1735 $result = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['multicall_error'], $GLOBALS['xmlrpcstr']['multicall_error']);
1736 }
1737 }
1738 }
1739 }
1740 else
1741 {
1742 // override fallback, in case careless user tries to do two
1743 // opposite things at the same time
1744 $fallback = true;
1745 }
1746
1747 $results = array();
1748 if ($fallback)
1749 {
1750 // system.multicall is (probably) unsupported by server:
1751 // emulate multicall via multiple requests
1752 foreach($msgs as $msg)
1753 {
1754 $results[] =& $this->send($msg, $timeout, $method);
1755 }
1756 }
1757 else
1758 {
1759 // user does NOT want to fallback on many single calls:
1760 // since we should always return an array of responses,
1761 // return an array with the same error repeated n times
1762 foreach($msgs as $msg)
1763 {
1764 $results[] = $result;
1765 }
1766 }
1767 return $results;
1768 }
1769
1770 /**
1771 * Attempt to boxcar $msgs via system.multicall.
1772 * Returns either an array of xmlrpcreponses, an xmlrpc error response
1773 * or false (when received response does not respect valid multicall syntax)
1774 * @access private
1775 */
1776 function _try_multicall($msgs, $timeout, $method)
1777 {
1778 // Construct multicall message
1779 $calls = array();
1780 foreach($msgs as $msg)
1781 {
1782 $call['methodName'] = new xmlrpcval($msg->method(),'string');
1783 $numParams = $msg->getNumParams();
1784 $params = array();
1785 for($i = 0; $i < $numParams; $i++)
1786 {
1787 $params[$i] = $msg->getParam($i);
1788 }
1789 $call['params'] = new xmlrpcval($params, 'array');
1790 $calls[] = new xmlrpcval($call, 'struct');
1791 }
1792 $multicall = new xmlrpcmsg('system.multicall');
1793 $multicall->addParam(new xmlrpcval($calls, 'array'));
1794
1795 // Attempt RPC call
1796 $result =& $this->send($multicall, $timeout, $method);
1797
1798 if($result->faultCode() != 0)
1799 {
1800 // call to system.multicall failed
1801 return $result;
1802 }
1803
1804 // Unpack responses.
1805 $rets = $result->value();
1806
1807 if ($this->return_type == 'xml')
1808 {
1809 return $rets;
1810 }
1811 else if ($this->return_type == 'phpvals')
1812 {
1813 ///@todo test this code branch...
1814 $rets = $result->value();
1815 if(!is_array($rets))
1816 {
1817 return false; // bad return type from system.multicall
1818 }
1819 $numRets = count($rets);
1820 if($numRets != count($msgs))
1821 {
1822 return false; // wrong number of return values.
1823 }
1824
1825 $response = array();
1826 for($i = 0; $i < $numRets; $i++)
1827 {
1828 $val = $rets[$i];
1829 if (!is_array($val)) {
1830 return false;
1831 }
1832 switch(count($val))
1833 {
1834 case 1:
1835 if(!isset($val[0]))
1836 {
1837 return false; // Bad value
1838 }
1839 // Normal return value
1840 $response[$i] = new xmlrpcresp($val[0], 0, '', 'phpvals');
1841 break;
1842 case 2:
1843 /// @todo remove usage of @: it is apparently quite slow
1844 $code = @$val['faultCode'];
1845 if(!is_int($code))
1846 {
1847 return false;
1848 }
1849 $str = @$val['faultString'];
1850 if(!is_string($str))
1851 {
1852 return false;
1853 }
1854 $response[$i] = new xmlrpcresp(0, $code, $str);
1855 break;
1856 default:
1857 return false;
1858 }
1859 }
1860 return $response;
1861 }
1862 else // return type == 'xmlrpcvals'
1863 {
1864 $rets = $result->value();
1865 if($rets->kindOf() != 'array')
1866 {
1867 return false; // bad return type from system.multicall
1868 }
1869 $numRets = $rets->arraysize();
1870 if($numRets != count($msgs))
1871 {
1872 return false; // wrong number of return values.
1873 }
1874
1875 $response = array();
1876 for($i = 0; $i < $numRets; $i++)
1877 {
1878 $val = $rets->arraymem($i);
1879 switch($val->kindOf())
1880 {
1881 case 'array':
1882 if($val->arraysize() != 1)
1883 {
1884 return false; // Bad value
1885 }
1886 // Normal return value
1887 $response[$i] = new xmlrpcresp($val->arraymem(0));
1888 break;
1889 case 'struct':
1890 $code = $val->structmem('faultCode');
1891 if($code->kindOf() != 'scalar' || $code->scalartyp() != 'int')
1892 {
1893 return false;
1894 }
1895 $str = $val->structmem('faultString');
1896 if($str->kindOf() != 'scalar' || $str->scalartyp() != 'string')
1897 {
1898 return false;
1899 }
1900 $response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval());
1901 break;
1902 default:
1903 return false;
1904 }
1905 }
1906 return $response;
1907 }
1908 }
1909 } // end class xmlrpc_client
1910
1911 class xmlrpcresp
1912 {
1913 var $val = 0;
1914 var $valtyp;
1915 var $errno = 0;
1916 var $errstr = '';
1917 var $payload;
1918 var $hdrs = array();
1919 var $_cookies = array();
1920 var $content_type = 'text/xml';
1921 var $raw_data = '';
1922
1923 /**
1924 * @param mixed $val either an xmlrpcval obj, a php value or the xml serialization of an xmlrpcval (a string)
1925 * @param integer $fcode set it to anything but 0 to create an error response
1926 * @param string $fstr the error string, in case of an error response
1927 * @param string $valtyp either 'xmlrpcvals', 'phpvals' or 'xml'
1928 *
1929 * @todo add check that $val / $fcode / $fstr is of correct type???
1930 * NB: as of now we do not do it, since it might be either an xmlrpcval or a plain
1931 * php val, or a complete xml chunk, depending on usage of xmlrpc_client::send() inside which creator is called...
1932 */
1933 function xmlrpcresp($val, $fcode = 0, $fstr = '', $valtyp='')
1934 {
1935 if($fcode != 0)
1936 {
1937 // error response
1938 $this->errno = $fcode;
1939 $this->errstr = $fstr;
1940 //$this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later.
1941 }
1942 else
1943 {
1944 // successful response
1945 $this->val = $val;
1946 if ($valtyp == '')
1947 {
1948 // user did not declare type of response value: try to guess it
1949 if (is_object($this->val) && is_a($this->val, 'xmlrpcval'))
1950 {
1951 $this->valtyp = 'xmlrpcvals';
1952 }
1953 else if (is_string($this->val))
1954 {
1955 $this->valtyp = 'xml';
1956
1957 }
1958 else
1959 {
1960 $this->valtyp = 'phpvals';
1961 }
1962 }
1963 else
1964 {
1965 // user declares type of resp value: believe him
1966 $this->valtyp = $valtyp;
1967 }
1968 }
1969 }
1970
1971 /**
1972 * Returns the error code of the response.
1973 * @return integer the error code of this response (0 for not-error responses)
1974 * @access public
1975 */
1976 function faultCode()
1977 {
1978 return $this->errno;
1979 }
1980
1981 /**
1982 * Returns the error code of the response.
1983 * @return string the error string of this response ('' for not-error responses)
1984 * @access public
1985 */
1986 function faultString()
1987 {
1988 return $this->errstr;
1989 }
1990
1991 /**
1992 * Returns the value received by the server.
1993 * @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
1994 * @access public
1995 */
1996 function value()
1997 {
1998 return $this->val;
1999 }
2000
2001 /**
2002 * Returns an array with the cookies received from the server.
2003 * Array has the form: $cookiename => array ('value' => $val, $attr1 => $val1, $attr2 = $val2, ...)
2004 * with attributes being e.g. 'expires', 'path', domain'.
2005 * NB: cookies sent as 'expired' by the server (i.e. with an expiry date in the past)
2006 * are still present in the array. It is up to the user-defined code to decide
2007 * how to use the received cookies, and wheter they have to be sent back with the next
2008 * request to the server (using xmlrpc_client::setCookie) or not
2009 * @return array array of cookies received from the server
2010 * @access public
2011 */
2012 function cookies()
2013 {
2014 return $this->_cookies;
2015 }
2016
2017 /**
2018 * Returns xml representation of the response. XML prologue not included
2019 * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
2020 * @return string the xml representation of the response
2021 * @access public
2022 */
2023 function serialize($charset_encoding='')
2024 {
2025 if ($charset_encoding != '')
2026 $this->content_type = 'text/xml; charset=' . $charset_encoding;
2027 else
2028 $this->content_type = 'text/xml';
2029 $result = "<methodResponse>\n";
2030 if($this->errno)
2031 {
2032 // G. Giunta 2005/2/13: let non-ASCII response messages be tolerated by clients
2033 // by xml-encoding non ascii chars
2034 $result .= "<fault>\n" .
2035"<value>\n<struct><member><name>faultCode</name>\n<value><int>" . $this->errno .
2036"</int></value>\n</member>\n<member>\n<name>faultString</name>\n<value><string>" .
2037xmlrpc_encode_entitites($this->errstr, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "</string></value>\n</member>\n" .
2038"</struct>\n</value>\n</fault>";
2039 }
2040 else
2041 {
2042 if(!is_object($this->val) || !is_a($this->val, 'xmlrpcval'))
2043 {
2044 if (is_string($this->val) && $this->valtyp == 'xml')
2045 {
2046 $result .= "<params>\n<param>\n" .
2047 $this->val .
2048 "</param>\n</params>";
2049 }
2050 else
2051 {
2052 /// @todo try to build something serializable?
2053 die('cannot serialize xmlrpcresp objects whose content is native php values');
2054 }
2055 }
2056 else
2057 {
2058 $result .= "<params>\n<param>\n" .
2059 $this->val->serialize($charset_encoding) .
2060 "</param>\n</params>";
2061 }
2062 }
2063 $result .= "\n</methodResponse>";
2064 $this->payload = $result;
2065 return $result;
2066 }
2067 }
2068
2069 class xmlrpcmsg
2070 {
2071 var $payload;
2072 var $methodname;
2073 var $params=array();
2074 var $debug=0;
2075 var $content_type = 'text/xml';
2076
2077 /**
2078 * @param string $meth the name of the method to invoke
2079 * @param array $pars array of parameters to be paased to the method (xmlrpcval objects)
2080 */
2081 function xmlrpcmsg($meth, $pars=0)
2082 {
2083 $this->methodname=$meth;
2084 if(is_array($pars) && count($pars)>0)
2085 {
2086 for($i=0; $i<count($pars); $i++)
2087 {
2088 $this->addParam($pars[$i]);
2089 }
2090 }
2091 }
2092
2093 /**
2094 * @access private
2095 */
2096 function xml_header($charset_encoding='')
2097 {
2098 if ($charset_encoding != '')
2099 {
2100 return "<?xml version=\"1.0\" encoding=\"$charset_encoding\" ?" . ">\n<methodCall>\n";
2101 }
2102 else
2103 {
2104 return "<?xml version=\"1.0\"?" . ">\n<methodCall>\n";
2105 }
2106 }
2107
2108 /**
2109 * @access private
2110 */
2111 function xml_footer()
2112 {
2113 return '</methodCall>';
2114 }
2115
2116 /**
2117 * @access private
2118 */
2119 function kindOf()
2120 {
2121 return 'msg';
2122 }
2123
2124 /**
2125 * @access private
2126 */
2127 function createPayload($charset_encoding='')
2128 {
2129 if ($charset_encoding != '')
2130 $this->content_type = 'text/xml; charset=' . $charset_encoding;
2131 else
2132 $this->content_type = 'text/xml';
2133 $this->payload=$this->xml_header($charset_encoding);
2134 $this->payload.='<methodName>' . $this->methodname . "</methodName>\n";
2135 $this->payload.="<params>\n";
2136 for($i=0; $i<count($this->params); $i++)
2137 {
2138 $p=$this->params[$i];
2139 $this->payload.="<param>\n" . $p->serialize($charset_encoding) .
2140 "</param>\n";
2141 }
2142 $this->payload.="</params>\n";
2143 $this->payload.=$this->xml_footer();
2144 }
2145
2146 /**
2147 * Gets/sets the xmlrpc method to be invoked
2148 * @param string $meth the method to be set (leave empty not to set it)
2149 * @return string the method that will be invoked
2150 * @access public
2151 */
2152 function method($meth='')
2153 {
2154 if($meth!='')
2155 {
2156 $this->methodname=$meth;
2157 }
2158 return $this->methodname;
2159 }
2160
2161 /**
2162 * Returns xml representation of the message. XML prologue included
2163 * @return string the xml representation of the message, xml prologue included
2164 * @access public
2165 */
2166 function serialize($charset_encoding='')
2167 {
2168 $this->createPayload($charset_encoding);
2169 return $this->payload;
2170 }
2171
2172 /**
2173 * Add a parameter to the list of parameters to be used upon method invocation
2174 * @param xmlrpcval $par
2175 * @return boolean false on failure
2176 * @access public
2177 */
2178 function addParam($par)
2179 {
2180 // add check: do not add to self params which are not xmlrpcvals
2181 if(is_object($par) && is_a($par, 'xmlrpcval'))
2182 {
2183 $this->params[]=$par;
2184 return true;
2185 }
2186 else
2187 {
2188 return false;
2189 }
2190 }
2191
2192 /**
2193 * Returns the nth parameter in the message. The index zero-based.
2194 * @param integer $i the index of the parameter to fetch (zero based)
2195 * @return xmlrpcval the i-th parameter
2196 * @access public
2197 */
2198 function getParam($i) { return $this->params[$i]; }
2199
2200 /**
2201 * Returns the number of parameters in the messge.
2202 * @return integer the number of parameters currently set
2203 * @access public
2204 */
2205 function getNumParams() { return count($this->params); }
2206
2207 /**
2208 * Given an open file handle, read all data available and parse it as axmlrpc response.
2209 * NB: the file handle is not closed by this function.
2210 * NNB: might have trouble in rare cases to work on network streams, as we
2211 * check for a read of 0 bytes instead of feof($fp).
2212 * But since checking for feof(null) returns false, we would risk an
2213 * infinite loop in that case, because we cannot trust the caller
2214 * to give us a valid pointer to an open file...
2215 * @access public
2216 * @return xmlrpcresp
2217 * @todo add 2nd & 3rd param to be passed to ParseResponse() ???
2218 */
2219 function &parseResponseFile($fp)
2220 {
2221 $ipd='';
2222 while($data=fread($fp, 32768))
2223 {
2224 $ipd.=$data;
2225 }
2226 //fclose($fp);
2227 $r =& $this->parseResponse($ipd);
2228 return $r;
2229 }
2230
2231 /**
2232 * Parses HTTP headers and separates them from data.
2233 * @access private
2234 */
2235 function &parseResponseHeaders(&$data, $headers_processed=false)
2236 {
2237 // Support "web-proxy-tunelling" connections for https through proxies
2238 if(preg_match('/^HTTP\/1\.[0-1] 200 Connection established/', $data))
2239 {
2240 // Look for CR/LF or simple LF as line separator,
2241 // (even though it is not valid http)
2242 $pos = strpos($data,"\r\n\r\n");
2243 if($pos || is_int($pos))
2244 {
2245 $bd = $pos+4;
2246 }
2247 else
2248 {
2249 $pos = strpos($data,"\n\n");
2250 if($pos || is_int($pos))
2251 {
2252 $bd = $pos+2;
2253 }
2254 else
2255 {
2256 // No separation between response headers and body: fault?
2257 $bd = 0;
2258 }
2259 }
2260 if ($bd)
2261 {
2262 // this filters out all http headers from proxy.
2263 // maybe we could take them into account, too?
2264 $data = substr($data, $bd);
2265 }
2266 else
2267 {
2268 error_log('XML-RPC: xmlrpcmsg::parseResponse: HTTPS via proxy error, tunnel connection possibly failed');
2269 $r= new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (HTTPS via proxy error, tunnel connection possibly failed)');
2270 return $r;
2271 }
2272 }
2273
2274 // Strip HTTP 1.1 100 Continue header if present
2275 while(preg_match('/^HTTP\/1\.1 1[0-9]{2} /', $data))
2276 {
2277 $pos = strpos($data, 'HTTP', 12);
2278 // server sent a Continue header without any (valid) content following...
2279 // give the client a chance to know it
2280 if(!$pos && !is_int($pos)) // works fine in php 3, 4 and 5
2281 {
2282 break;
2283 }
2284 $data = substr($data, $pos);
2285 }
2286 if(!preg_match('/^HTTP\/[0-9.]+ 200 /', $data))
2287 {
2288 $errstr= substr($data, 0, strpos($data, "\n")-1);
2289 error_log('XML-RPC: xmlrpcmsg::parseResponse: HTTP error, got response: ' .$errstr);
2290 $r= new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['http_error'], $GLOBALS['xmlrpcstr']['http_error']. ' (' . $errstr . ')');
2291 return $r;
2292 }
2293
2294 $GLOBALS['_xh']['headers'] = array();
2295 $GLOBALS['_xh']['cookies'] = array();
2296
2297 // be tolerant to usage of \n instead of \r\n to separate headers and data
2298 // (even though it is not valid http)
2299 $pos = strpos($data,"\r\n\r\n");
2300 if($pos || is_int($pos))
2301 {
2302 $bd = $pos+4;
2303 }
2304 else
2305 {
2306 $pos = strpos($data,"\n\n");
2307 if($pos || is_int($pos))
2308 {
2309 $bd = $pos+2;
2310 }
2311 else
2312 {
2313 // No separation between response headers and body: fault?
2314 // we could take some action here instead of going on...
2315 $bd = 0;
2316 }
2317 }
2318 // be tolerant to line endings, and extra empty lines
2319 $ar = split("\r?\n", trim(substr($data, 0, $pos)));
2320 while(list(,$line) = @each($ar))
2321 {
2322 // take care of multi-line headers and cookies
2323 $arr = explode(':',$line,2);
2324 if(count($arr) > 1)
2325 {
2326 $header_name = strtolower(trim($arr[0]));
2327 /// @todo some other headers (the ones that allow a CSV list of values)
2328 /// do allow many values to be passed using multiple header lines.
2329 /// We should add content to $GLOBALS['_xh']['headers'][$header_name]
2330 /// instead of replacing it for those...
2331 if ($header_name == 'set-cookie' || $header_name == 'set-cookie2')
2332 {
2333 if ($header_name == 'set-cookie2')
2334 {
2335 // version 2 cookies:
2336 // there could be many cookies on one line, comma separated
2337 $cookies = explode(',', $arr[1]);
2338 }
2339 else
2340 {
2341 $cookies = array($arr[1]);
2342 }
2343 foreach ($cookies as $cookie)
2344 {
2345 // glue together all received cookies, using a comma to separate them
2346 // (same as php does with getallheaders())
2347 if (isset($GLOBALS['_xh']['headers'][$header_name]))
2348 $GLOBALS['_xh']['headers'][$header_name] .= ', ' . trim($cookie);
2349 else
2350 $GLOBALS['_xh']['headers'][$header_name] = trim($cookie);
2351 // parse cookie attributes, in case user wants to correctly honour them
2352 // feature creep: only allow rfc-compliant cookie attributes?
2353 // @todo support for server sending multiple time cookie with same name, but using different PATHs
2354 $cookie = explode(';', $cookie);
2355 foreach ($cookie as $pos => $val)
2356 {
2357 $val = explode('=', $val, 2);
2358 $tag = trim($val[0]);
2359 $val = trim(@$val[1]);
2360 /// @todo with version 1 cookies, we should strip leading and trailing " chars
2361 if ($pos == 0)
2362 {
2363 $cookiename = $tag;
2364 $GLOBALS['_xh']['cookies'][$tag] = array();
2365 $GLOBALS['_xh']['cookies'][$cookiename]['value'] = urldecode($val);
2366 }
2367 else
2368 {
2369 if ($tag != 'value')
2370 {
2371 $GLOBALS['_xh']['cookies'][$cookiename][$tag] = $val;
2372 }
2373 }
2374 }
2375 }
2376 }
2377 else
2378 {
2379 $GLOBALS['_xh']['headers'][$header_name] = trim($arr[1]);
2380 }
2381 }
2382 elseif(isset($header_name))
2383 {
2384 /// @todo version1 cookies might span multiple lines, thus breaking the parsing above
2385 $GLOBALS['_xh']['headers'][$header_name] .= ' ' . trim($line);
2386 }
2387 }
2388
2389 $data = substr($data, $bd);
2390
2391 if($this->debug && count($GLOBALS['_xh']['headers']))
2392 {
2393 print '<PRE>';
2394 foreach($GLOBALS['_xh']['headers'] as $header => $value)
2395 {
2396 print htmlentities("HEADER: $header: $value\n");
2397 }
2398 foreach($GLOBALS['_xh']['cookies'] as $header => $value)
2399 {
2400 print htmlentities("COOKIE: $header={$value['value']}\n");
2401 }
2402 print "</PRE>\n";
2403 }
2404
2405 // if CURL was used for the call, http headers have been processed,
2406 // and dechunking + reinflating have been carried out
2407 if(!$headers_processed)
2408 {
2409 // Decode chunked encoding sent by http 1.1 servers
2410 if(isset($GLOBALS['_xh']['headers']['transfer-encoding']) && $GLOBALS['_xh']['headers']['transfer-encoding'] == 'chunked')
2411 {
2412 if(!$data = decode_chunked($data))
2413 {
2414 error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to rebuild the chunked data received from server');
2415 $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['dechunk_fail'], $GLOBALS['xmlrpcstr']['dechunk_fail']);
2416 return $r;
2417 }
2418 }
2419
2420 // Decode gzip-compressed stuff
2421 // code shamelessly inspired from nusoap library by Dietrich Ayala
2422 if(isset($GLOBALS['_xh']['headers']['content-encoding']))
2423 {
2424 $GLOBALS['_xh']['headers']['content-encoding'] = str_replace('x-', '', $GLOBALS['_xh']['headers']['content-encoding']);
2425 if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' || $GLOBALS['_xh']['headers']['content-encoding'] == 'gzip')
2426 {
2427 // if decoding works, use it. else assume data wasn't gzencoded
2428 if(function_exists('gzinflate'))
2429 {
2430 if($GLOBALS['_xh']['headers']['content-encoding'] == 'deflate' && $degzdata = @gzuncompress($data))
2431 {
2432 $data = $degzdata;
2433 if($this->debug)
2434 print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2435 }
2436 elseif($GLOBALS['_xh']['headers']['content-encoding'] == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
2437 {
2438 $data = $degzdata;
2439 if($this->debug)
2440 print "<PRE>---INFLATED RESPONSE---[".strlen($data)." chars]---\n" . htmlentities($data) . "\n---END---</PRE>";
2441 }
2442 else
2443 {
2444 error_log('XML-RPC: xmlrpcmsg::parseResponse: errors occurred when trying to decode the deflated data received from server');
2445 $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['decompress_fail'], $GLOBALS['xmlrpcstr']['decompress_fail']);
2446 return $r;
2447 }
2448 }
2449 else
2450 {
2451 error_log('XML-RPC: xmlrpcmsg::parseResponse: the server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
2452 $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['cannot_decompress'], $GLOBALS['xmlrpcstr']['cannot_decompress']);
2453 return $r;
2454 }
2455 }
2456 }
2457 } // end of 'if needed, de-chunk, re-inflate response'
2458
2459 // real stupid hack to avoid PHP 4 complaining about returning NULL by ref
2460 $r = null;
2461 $r =& $r;
2462 return $r;
2463 }
2464
2465 /**
2466 * Parse the xmlrpc response contained in the string $data and return an xmlrpcresp object.
2467 * @param string $data the xmlrpc response, eventually including http headers
2468 * @param bool $headers_processed when true prevents parsing HTTP headers for interpretation of content-encoding and consequent decoding
2469 * @param string $return_type decides return type, i.e. content of response->value(). Either 'xmlrpcvals', 'xml' or 'phpvals'
2470 * @return xmlrpcresp
2471 * @access public
2472 */
2473 function &parseResponse($data='', $headers_processed=false, $return_type='xmlrpcvals')
2474 {
2475 if($this->debug)
2476 {
2477 //by maHo, replaced htmlspecialchars with htmlentities
2478 print "<PRE>---GOT---\n" . htmlentities($data) . "\n---END---\n</PRE>";
2479 }
2480
2481 if($data == '')
2482 {
2483 error_log('XML-RPC: xmlrpcmsg::parseResponse: no response received from server.');
2484 $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['no_data'], $GLOBALS['xmlrpcstr']['no_data']);
2485 return $r;
2486 }
2487
2488 $GLOBALS['_xh']=array();
2489
2490 $raw_data = $data;
2491 // parse the HTTP headers of the response, if present, and separate them from data
2492 if(substr($data, 0, 4) == 'HTTP')
2493 {
2494 $r =& $this->parseResponseHeaders($data, $headers_processed);
2495 if ($r)
2496 {
2497 // failed processing of HTTP response headers
2498 // save into response obj the full payload received, for debugging
2499 $r->raw_data = $data;
2500 return $r;
2501 }
2502 }
2503 else
2504 {
2505 $GLOBALS['_xh']['headers'] = array();
2506 $GLOBALS['_xh']['cookies'] = array();
2507 }
2508
2509 if($this->debug)
2510 {
2511 $start = strpos($data, '<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2512 if ($start)
2513 {
2514 $start += strlen('<!-- SERVER DEBUG INFO (BASE64 ENCODED):');
2515 $end = strpos($data, '-->', $start);
2516 $comments = substr($data, $start, $end-$start);
2517 print "<PRE>---SERVER DEBUG INFO (DECODED) ---\n\t".htmlentities(str_replace("\n", "\n\t", base64_decode($comments)))."\n---END---\n</PRE>";
2518 }
2519 }
2520
2521 // be tolerant of extra whitespace in response body
2522 $data = trim($data);
2523
2524 /// @todo return an error msg if $data=='' ?
2525
2526 // be tolerant of junk after methodResponse (e.g. javascript ads automatically inserted by free hosts)
2527 // idea from Luca Mariano <luca.mariano@email.it> originally in PEARified version of the lib
2528 $bd = false;
2529 // Poor man's version of strrpos for php 4...
2530 $pos = strpos($data, '</methodResponse>');
2531 while($pos || is_int($pos))
2532 {
2533 $bd = $pos+17;
2534 $pos = strpos($data, '</methodResponse>', $bd);
2535 }
2536 if($bd)
2537 {
2538 $data = substr($data, 0, $bd);
2539 }
2540
2541 // if user wants back raw xml, give it to him
2542 if ($return_type == 'xml')
2543 {
2544 $r = new xmlrpcresp($data, 0, '', 'xml');
2545 $r->hdrs = $GLOBALS['_xh']['headers'];
2546 $r->_cookies = $GLOBALS['_xh']['cookies'];
2547 $r->raw_data = $raw_data;
2548 return $r;
2549 }
2550
2551 // try to 'guestimate' the character encoding of the received response
2552 $resp_encoding = guess_encoding(@$GLOBALS['_xh']['headers']['content-type'], $data);
2553
2554 $GLOBALS['_xh']['ac']='';
2555 //$GLOBALS['_xh']['qt']=''; //unused...
2556 $GLOBALS['_xh']['stack'] = array();
2557 $GLOBALS['_xh']['valuestack'] = array();
2558 $GLOBALS['_xh']['isf']=0; // 0 = OK, 1 for xmlrpc fault responses, 2 = invalid xmlrpc
2559 $GLOBALS['_xh']['isf_reason']='';
2560 $GLOBALS['_xh']['rt']=''; // 'methodcall or 'methodresponse'
2561
2562 // if response charset encoding is not known / supported, try to use
2563 // the default encoding and parse the xml anyway, but log a warning...
2564 if (!in_array($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2565 // the following code might be better for mb_string enabled installs, but
2566 // makes the lib about 200% slower...
2567 //if (!is_valid_charset($resp_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2568 {
2569 error_log('XML-RPC: xmlrpcmsg::parseResponse: invalid charset encoding of received response: '.$resp_encoding);
2570 $resp_encoding = $GLOBALS['xmlrpc_defencoding'];
2571 }
2572 $parser = xml_parser_create($resp_encoding);
2573 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
2574 // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
2575 // the xml parser to give us back data in the expected charset.
2576 // What if internal encoding is not in one of the 3 allowed?
2577 // we use the broadest one, ie. utf8
2578 // This allows to send data which is native in various charset,
2579 // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
2580 if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
2581 {
2582 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
2583 }
2584 else
2585 {
2586 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
2587 }
2588
2589 if ($return_type == 'phpvals')
2590 {
2591 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
2592 }
2593 else
2594 {
2595 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
2596 }
2597
2598 xml_set_character_data_handler($parser, 'xmlrpc_cd');
2599 xml_set_default_handler($parser, 'xmlrpc_dh');
2600
2601 // first error check: xml not well formed
2602 if(!xml_parse($parser, $data, count($data)))
2603 {
2604 // thanks to Peter Kocks <peter.kocks@baygate.com>
2605 if((xml_get_current_line_number($parser)) == 1)
2606 {
2607 $errstr = 'XML error at line 1, check URL';
2608 }
2609 else
2610 {
2611 $errstr = sprintf('XML error: %s at line %d, column %d',
2612 xml_error_string(xml_get_error_code($parser)),
2613 xml_get_current_line_number($parser), xml_get_current_column_number($parser));
2614 }
2615 error_log($errstr);
2616 $r= new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'], $GLOBALS['xmlrpcstr']['invalid_return'].' ('.$errstr.')');
2617 xml_parser_free($parser);
2618 if($this->debug)
2619 {
2620 print $errstr;
2621 }
2622 $r->hdrs = $GLOBALS['_xh']['headers'];
2623 $r->_cookies = $GLOBALS['_xh']['cookies'];
2624 $r->raw_data = $raw_data;
2625 return $r;
2626 }
2627 xml_parser_free($parser);
2628 // second error check: xml well formed but not xml-rpc compliant
2629 if ($GLOBALS['_xh']['isf'] > 1)
2630 {
2631 if ($this->debug)
2632 {
2633 /// @todo echo something for user?
2634 }
2635
2636 $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2637 $GLOBALS['xmlrpcstr']['invalid_return'] . ' ' . $GLOBALS['_xh']['isf_reason']);
2638 }
2639 // third error check: parsing of the response has somehow gone boink.
2640 // NB: shall we omit this check, since we trust the parsing code?
2641 elseif ($return_type == 'xmlrpcvals' && !is_object($GLOBALS['_xh']['value']))
2642 {
2643 // something odd has happened
2644 // and it's time to generate a client side error
2645 // indicating something odd went on
2646 $r= new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['invalid_return'],
2647 $GLOBALS['xmlrpcstr']['invalid_return']);
2648 }
2649 else
2650 {
2651 if ($this->debug)
2652 {
2653 print "<PRE>---PARSED---\n";
2654 // somehow htmlentities chokes on var_export, and some full html string...
2655 //print htmlentitites(var_export($GLOBALS['_xh']['value'], true));
2656 print htmlspecialchars(var_export($GLOBALS['_xh']['value'], true));
2657 print "\n---END---</PRE>";
2658 }
2659
2660 // note that using =& will raise an error if $GLOBALS['_xh']['st'] does not generate an object.
2661 $v =& $GLOBALS['_xh']['value'];
2662
2663 if($GLOBALS['_xh']['isf'])
2664 {
2665 /// @todo we should test here if server sent an int and a string,
2666 /// and/or coerce them into such...
2667 if ($return_type == 'xmlrpcvals')
2668 {
2669 $errno_v = $v->structmem('faultCode');
2670 $errstr_v = $v->structmem('faultString');
2671 $errno = $errno_v->scalarval();
2672 $errstr = $errstr_v->scalarval();
2673 }
2674 else
2675 {
2676 $errno = $v['faultCode'];
2677 $errstr = $v['faultString'];
2678 }
2679
2680 if($errno == 0)
2681 {
2682 // FAULT returned, errno needs to reflect that
2683 $errno = -1;
2684 }
2685
2686 $r = new xmlrpcresp(0, $errno, $errstr);
2687 }
2688 else
2689 {
2690 $r= new xmlrpcresp($v, 0, '', $return_type);
2691 }
2692 }
2693
2694 $r->hdrs = $GLOBALS['_xh']['headers'];
2695 $r->_cookies = $GLOBALS['_xh']['cookies'];
2696 $r->raw_data = $raw_data;
2697 return $r;
2698 }
2699 }
2700
2701 class xmlrpcval
2702 {
2703 var $me=array();
2704 var $mytype=0;
2705 var $_php_class=null;
2706
2707 /**
2708 * @param mixed $val
2709 * @param string $type any valid xmlrpc type name (lowercase). If null, 'string' is assumed
2710 */
2711 function xmlrpcval($val=-1, $type='')
2712 {
2713 /// @todo: optimization creep - do not call addXX, do it all inline.
2714 /// downside: booleans will not be coerced anymore
2715 if($val!==-1 || $type!='')
2716 {
2717 // optimization creep: inlined all work done by constructor
2718 switch($type)
2719 {
2720 case '':
2721 $this->mytype=1;
2722 $this->me['string']=$val;
2723 break;
2724 case 'i4':
2725 case 'int':
2726 case 'double':
2727 case 'string':
2728 case 'boolean':
2729 case 'dateTime.iso8601':
2730 case 'base64':
2731 case 'null':
2732 $this->mytype=1;
2733 $this->me[$type]=$val;
2734 break;
2735 case 'array':
2736 $this->mytype=2;
2737 $this->me['array']=$val;
2738 break;
2739 case 'struct':
2740 $this->mytype=3;
2741 $this->me['struct']=$val;
2742 break;
2743 default:
2744 error_log("XML-RPC: xmlrpcval::xmlrpcval: not a known type ($type)");
2745 }
2746 /*if($type=='')
2747 {
2748 $type='string';
2749 }
2750 if($GLOBALS['xmlrpcTypes'][$type]==1)
2751 {
2752 $this->addScalar($val,$type);
2753 }
2754 elseif($GLOBALS['xmlrpcTypes'][$type]==2)
2755 {
2756 $this->addArray($val);
2757 }
2758 elseif($GLOBALS['xmlrpcTypes'][$type]==3)
2759 {
2760 $this->addStruct($val);
2761 }*/
2762 }
2763 }
2764
2765 /**
2766 * Add a single php value to an (unitialized) xmlrpcval
2767 * @param mixed $val
2768 * @param string $type
2769 * @return int 1 or 0 on failure
2770 */
2771 function addScalar($val, $type='string')
2772 {
2773 $typeof=@$GLOBALS['xmlrpcTypes'][$type];
2774 if($typeof!=1)
2775 {
2776 error_log("XML-RPC: xmlrpcval::addScalar: not a scalar type ($type)");
2777 return 0;
2778 }
2779
2780 // coerce booleans into correct values
2781 // NB: we should iether do it for datetimes, integers and doubles, too,
2782 // or just plain remove this check, implemnted on booleans only...
2783 if($type==$GLOBALS['xmlrpcBoolean'])
2784 {
2785 if(strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false')))
2786 {
2787 $val=true;
2788 }
2789 else
2790 {
2791 $val=false;
2792 }
2793 }
2794
2795 switch($this->mytype)
2796 {
2797 case 1:
2798 error_log('XML-RPC: xmlrpcval::addScalar: scalar xmlrpcval can have only one value');
2799 return 0;
2800 case 3:
2801 error_log('XML-RPC: xmlrpcval::addScalar: cannot add anonymous scalar to struct xmlrpcval');
2802 return 0;
2803 case 2:
2804 // we're adding a scalar value to an array here
2805 //$ar=$this->me['array'];
2806 //$ar[]= new xmlrpcval($val, $type);
2807 //$this->me['array']=$ar;
2808 // Faster (?) avoid all the costly array-copy-by-val done here...
2809 $this->me['array'][]= new xmlrpcval($val, $type);
2810 return 1;
2811 default:
2812 // a scalar, so set the value and remember we're scalar
2813 $this->me[$type]=$val;
2814 $this->mytype=$typeof;
2815 return 1;
2816 }
2817 }
2818
2819 /**
2820 * Add an array of xmlrpcval objects to an xmlrpcval
2821 * @param array $vals
2822 * @return int 1 or 0 on failure
2823 * @access public
2824 *
2825 * @todo add some checking for $vals to be an array of xmlrpcvals?
2826 */
2827 function addArray($vals)
2828 {
2829 if($this->mytype==0)
2830 {
2831 $this->mytype=$GLOBALS['xmlrpcTypes']['array'];
2832 $this->me['array']=$vals;
2833 return 1;
2834 }
2835 elseif($this->mytype==2)
2836 {
2837 // we're adding to an array here
2838 $this->me['array'] = array_merge($this->me['array'], $vals);
2839 return 1;
2840 }
2841 else
2842 {
2843 error_log('XML-RPC: xmlrpcval::addArray: already initialized as a [' . $this->kindOf() . ']');
2844 return 0;
2845 }
2846 }
2847
2848 /**
2849 * Add an array of named xmlrpcval objects to an xmlrpcval
2850 * @param array $vals
2851 * @return int 1 or 0 on failure
2852 * @access public
2853 *
2854 * @todo add some checking for $vals to be an array?
2855 */
2856 function addStruct($vals)
2857 {
2858 if($this->mytype==0)
2859 {
2860 $this->mytype=$GLOBALS['xmlrpcTypes']['struct'];
2861 $this->me['struct']=$vals;
2862 return 1;
2863 }
2864 elseif($this->mytype==3)
2865 {
2866 // we're adding to a struct here
2867 $this->me['struct'] = array_merge($this->me['struct'], $vals);
2868 return 1;
2869 }
2870 else
2871 {
2872 error_log('XML-RPC: xmlrpcval::addStruct: already initialized as a [' . $this->kindOf() . ']');
2873 return 0;
2874 }
2875 }
2876
2877 // poor man's version of print_r ???
2878 // DEPRECATED!
2879 function dump($ar)
2880 {
2881 foreach($ar as $key => $val)
2882 {
2883 echo "$key => $val<br />";
2884 if($key == 'array')
2885 {
2886 while(list($key2, $val2) = each($val))
2887 {
2888 echo "-- $key2 => $val2<br />";
2889 }
2890 }
2891 }
2892 }
2893
2894 /**
2895 * Returns a string containing "struct", "array" or "scalar" describing the base type of the value
2896 * @return string
2897 * @access public
2898 */
2899 function kindOf()
2900 {
2901 switch($this->mytype)
2902 {
2903 case 3:
2904 return 'struct';
2905 break;
2906 case 2:
2907 return 'array';
2908 break;
2909 case 1:
2910 return 'scalar';
2911 break;
2912 default:
2913 return 'undef';
2914 }
2915 }
2916
2917 /**
2918 * @access private
2919 */
2920 function serializedata($typ, $val, $charset_encoding='')
2921 {
2922 $rs='';
2923 switch(@$GLOBALS['xmlrpcTypes'][$typ])
2924 {
2925 case 1:
2926 switch($typ)
2927 {
2928 case $GLOBALS['xmlrpcBase64']:
2929 $rs.="<${typ}>" . base64_encode($val) . "</${typ}>";
2930 break;
2931 case $GLOBALS['xmlrpcBoolean']:
2932 $rs.="<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
2933 break;
2934 case $GLOBALS['xmlrpcString']:
2935 // G. Giunta 2005/2/13: do NOT use htmlentities, since
2936 // it will produce named html entities, which are invalid xml
2937 $rs.="<${typ}>" . xmlrpc_encode_entitites($val, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding). "</${typ}>";
2938 break;
2939 case $GLOBALS['xmlrpcInt']:
2940 case $GLOBALS['xmlrpcI4']:
2941 $rs.="<${typ}>".(int)$val."</${typ}>";
2942 break;
2943 case $GLOBALS['xmlrpcDouble']:
2944 // avoid using standard conversion of float to string because it is locale-dependent,
2945 // and also because the xmlrpc spec forbids exponential notation
2946 // sprintf('%F') would be most likely ok but it is only available since PHP 4.3.10 and PHP 5.0.3.
2947 // The code below tries its best at keeping max precision while avoiding exp notation,
2948 // but there is of course no limit in the number of decimal places to be used...
2949 $rs.="<${typ}>".preg_replace('/\\.?0+$/','',number_format((double)$val, 128, '.', ''))."</${typ}>";
2950 break;
2951 case $GLOBALS['xmlrpcNull']:
2952 $rs.="<nil/>";
2953 break;
2954 default:
2955 // no standard type value should arrive here, but provide a possibility
2956 // for xmlrpcvals of unknown type...
2957 $rs.="<${typ}>${val}</${typ}>";
2958 }
2959 break;
2960 case 3:
2961 // struct
2962 if ($this->_php_class)
2963 {
2964 $rs.='<struct php_class="' . $this->_php_class . "\">\n";
2965 }
2966 else
2967 {
2968 $rs.="<struct>\n";
2969 }
2970 foreach($val as $key2 => $val2)
2971 {
2972 $rs.='<member><name>'.xmlrpc_encode_entitites($key2, $GLOBALS['xmlrpc_internalencoding'], $charset_encoding)."</name>\n";
2973 //$rs.=$this->serializeval($val2);
2974 $rs.=$val2->serialize($charset_encoding);
2975 $rs.="</member>\n";
2976 }
2977 $rs.='</struct>';
2978 break;
2979 case 2:
2980 // array
2981 $rs.="<array>\n<data>\n";
2982 for($i=0; $i<count($val); $i++)
2983 {
2984 //$rs.=$this->serializeval($val[$i]);
2985 $rs.=$val[$i]->serialize($charset_encoding);
2986 }
2987 $rs.="</data>\n</array>";
2988 break;
2989 default:
2990 break;
2991 }
2992 return $rs;
2993 }
2994
2995 /**
2996 * Returns xml representation of the value. XML prologue not included
2997 * @param string $charset_encoding the charset to be used for serialization. if null, US-ASCII is assumed
2998 * @return string
2999 * @access public
3000 */
3001 function serialize($charset_encoding='')
3002 {
3003 // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3004 //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3005 //{
3006 reset($this->me);
3007 list($typ, $val) = each($this->me);
3008 return '<value>' . $this->serializedata($typ, $val, $charset_encoding) . "</value>\n";
3009 //}
3010 }
3011
3012 // DEPRECATED
3013 function serializeval($o)
3014 {
3015 // add check? slower, but helps to avoid recursion in serializing broken xmlrpcvals...
3016 //if (is_object($o) && (get_class($o) == 'xmlrpcval' || is_subclass_of($o, 'xmlrpcval')))
3017 //{
3018 $ar=$o->me;
3019 reset($ar);
3020 list($typ, $val) = each($ar);
3021 return '<value>' . $this->serializedata($typ, $val) . "</value>\n";
3022 //}
3023 }
3024
3025 /**
3026 * Checks wheter a struct member with a given name is present.
3027 * Works only on xmlrpcvals of type struct.
3028 * @param string $m the name of the struct member to be looked up
3029 * @return boolean
3030 * @access public
3031 */
3032 function structmemexists($m)
3033 {
3034 return array_key_exists($m, $this->me['struct']);
3035 }
3036
3037 /**
3038 * Returns the value of a given struct member (an xmlrpcval object in itself).
3039 * Will raise a php warning if struct member of given name does not exist
3040 * @param string $m the name of the struct member to be looked up
3041 * @return xmlrpcval
3042 * @access public
3043 */
3044 function structmem($m)
3045 {
3046 return $this->me['struct'][$m];
3047 }
3048
3049 /**
3050 * Reset internal pointer for xmlrpcvals of type struct.
3051 * @access public
3052 */
3053 function structreset()
3054 {
3055 reset($this->me['struct']);
3056 }
3057
3058 /**
3059 * Return next member element for xmlrpcvals of type struct.
3060 * @return xmlrpcval
3061 * @access public
3062 */
3063 function structeach()
3064 {
3065 return each($this->me['struct']);
3066 }
3067
3068 // DEPRECATED! this code looks like it is very fragile and has not been fixed
3069 // for a long long time. Shall we remove it for 2.0?
3070 function getval()
3071 {
3072 // UNSTABLE
3073 reset($this->me);
3074 list($a,$b)=each($this->me);
3075 // contributed by I Sofer, 2001-03-24
3076 // add support for nested arrays to scalarval
3077 // i've created a new method here, so as to
3078 // preserve back compatibility
3079
3080 if(is_array($b))
3081 {
3082 @reset($b);
3083 while(list($id,$cont) = @each($b))
3084 {
3085 $b[$id] = $cont->scalarval();
3086 }
3087 }
3088
3089 // add support for structures directly encoding php objects
3090 if(is_object($b))
3091 {
3092 $t = get_object_vars($b);
3093 @reset($t);
3094 while(list($id,$cont) = @each($t))
3095 {
3096 $t[$id] = $cont->scalarval();
3097 }
3098 @reset($t);
3099 while(list($id,$cont) = @each($t))
3100 {
3101 @$b->$id = $cont;
3102 }
3103 }
3104 // end contrib
3105 return $b;
3106 }
3107
3108 /**
3109 * Returns the value of a scalar xmlrpcval
3110 * @return mixed
3111 * @access public
3112 */
3113 function scalarval()
3114 {
3115 reset($this->me);
3116 list(,$b)=each($this->me);
3117 return $b;
3118 }
3119
3120 /**
3121 * Returns the type of the xmlrpcval.
3122 * For integers, 'int' is always returned in place of 'i4'
3123 * @return string
3124 * @access public
3125 */
3126 function scalartyp()
3127 {
3128 reset($this->me);
3129 list($a,)=each($this->me);
3130 if($a==$GLOBALS['xmlrpcI4'])
3131 {
3132 $a=$GLOBALS['xmlrpcInt'];
3133 }
3134 return $a;
3135 }
3136
3137 /**
3138 * Returns the m-th member of an xmlrpcval of struct type
3139 * @param integer $m the index of the value to be retrieved (zero based)
3140 * @return xmlrpcval
3141 * @access public
3142 */
3143 function arraymem($m)
3144 {
3145 return $this->me['array'][$m];
3146 }
3147
3148 /**
3149 * Returns the number of members in an xmlrpcval of array type
3150 * @return integer
3151 * @access public
3152 */
3153 function arraysize()
3154 {
3155 return count($this->me['array']);
3156 }
3157
3158 /**
3159 * Returns the number of members in an xmlrpcval of struct type
3160 * @return integer
3161 * @access public
3162 */
3163 function structsize()
3164 {
3165 return count($this->me['struct']);
3166 }
3167 }
3168
3169
3170 // date helpers
3171
3172 /**
3173 * Given a timestamp, return the corresponding ISO8601 encoded string.
3174 *
3175 * Really, timezones ought to be supported
3176 * but the XML-RPC spec says:
3177 *
3178 * "Don't assume a timezone. It should be specified by the server in its
3179 * documentation what assumptions it makes about timezones."
3180 *
3181 * These routines always assume localtime unless
3182 * $utc is set to 1, in which case UTC is assumed
3183 * and an adjustment for locale is made when encoding
3184 *
3185 * @param int $timet (timestamp)
3186 * @param int $utc (0 or 1)
3187 * @return string
3188 */
3189 function iso8601_encode($timet, $utc=0)
3190 {
3191 if(!$utc)
3192 {
3193 $t=strftime("%Y%m%dT%H:%M:%S", $timet);
3194 }
3195 else
3196 {
3197 if(function_exists('gmstrftime'))
3198 {
3199 // gmstrftime doesn't exist in some versions
3200 // of PHP
3201 $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet);
3202 }
3203 else
3204 {
3205 $t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z'));
3206 }
3207 }
3208 return $t;
3209 }
3210
3211 /**
3212 * Given an ISO8601 date string, return a timet in the localtime, or UTC
3213 * @param string $idate
3214 * @param int $utc either 0 or 1
3215 * @return int (datetime)
3216 */
3217 function iso8601_decode($idate, $utc=0)
3218 {
3219 $t=0;
3220 if(preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})/', $idate, $regs))
3221 {
3222 if($utc)
3223 {
3224 $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3225 }
3226 else
3227 {
3228 $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
3229 }
3230 }
3231 return $t;
3232 }
3233
3234 /**
3235 * Takes an xmlrpc value in PHP xmlrpcval object format and translates it into native PHP types.
3236 *
3237 * Works with xmlrpc message objects as input, too.
3238 *
3239 * Given proper options parameter, can rebuild generic php object instances
3240 * (provided those have been encoded to xmlrpc format using a corresponding
3241 * option in php_xmlrpc_encode())
3242 * PLEASE NOTE that rebuilding php objects involves calling their constructor function.
3243 * This means that the remote communication end can decide which php code will
3244 * get executed on your server, leaving the door possibly open to 'php-injection'
3245 * style of attacks (provided you have some classes defined on your server that
3246 * might wreak havoc if instances are built outside an appropriate context).
3247 * Make sure you trust the remote server/client before eanbling this!
3248 *
3249 * @author Dan Libby (dan@libby.com)
3250 *
3251 * @param xmlrpcval $xmlrpc_val
3252 * @param array $options if 'decode_php_objs' is set in the options array, xmlrpc structs can be decoded into php objects
3253 * @return mixed
3254 */
3255 function php_xmlrpc_decode($xmlrpc_val, $options=array())
3256 {
3257 switch($xmlrpc_val->kindOf())
3258 {
3259 case 'scalar':
3260 if (in_array('extension_api', $options))
3261 {
3262 reset($xmlrpc_val->me);
3263 list($typ,$val) = each($xmlrpc_val->me);
3264 switch ($typ)
3265 {
3266 case 'dateTime.iso8601':
3267 $xmlrpc_val->scalar = $val;
3268 $xmlrpc_val->xmlrpc_type = 'datetime';
3269 $xmlrpc_val->timestamp = iso8601_decode($val);
3270 return $xmlrpc_val;
3271 case 'base64':
3272 $xmlrpc_val->scalar = $val;
3273 $xmlrpc_val->type = $typ;
3274 return $xmlrpc_val;
3275 default:
3276 return $xmlrpc_val->scalarval();
3277 }
3278 }
3279 return $xmlrpc_val->scalarval();
3280 case 'array':
3281 $size = $xmlrpc_val->arraysize();
3282 $arr = array();
3283 for($i = 0; $i < $size; $i++)
3284 {
3285 $arr[] = php_xmlrpc_decode($xmlrpc_val->arraymem($i), $options);
3286 }
3287 return $arr;
3288 case 'struct':
3289 $xmlrpc_val->structreset();
3290 // If user said so, try to rebuild php objects for specific struct vals.
3291 /// @todo should we raise a warning for class not found?
3292 // shall we check for proper subclass of xmlrpcval instead of
3293 // presence of _php_class to detect what we can do?
3294 if (in_array('decode_php_objs', $options) && $xmlrpc_val->_php_class != ''
3295 && class_exists($xmlrpc_val->_php_class))
3296 {
3297 $obj = @new $xmlrpc_val->_php_class;
3298 while(list($key,$value)=$xmlrpc_val->structeach())
3299 {
3300 $obj->$key = php_xmlrpc_decode($value, $options);
3301 }
3302 return $obj;
3303 }
3304 else
3305 {
3306 $arr = array();
3307 while(list($key,$value)=$xmlrpc_val->structeach())
3308 {
3309 $arr[$key] = php_xmlrpc_decode($value, $options);
3310 }
3311 return $arr;
3312 }
3313 case 'msg':
3314 $paramcount = $xmlrpc_val->getNumParams();
3315 $arr = array();
3316 for($i = 0; $i < $paramcount; $i++)
3317 {
3318 $arr[] = php_xmlrpc_decode($xmlrpc_val->getParam($i));
3319 }
3320 return $arr;
3321 }
3322 }
3323
3324 // This constant left here only for historical reasons...
3325 // it was used to decide if we have to define xmlrpc_encode on our own, but
3326 // we do not do it anymore
3327 if(function_exists('xmlrpc_decode'))
3328 {
3329 define('XMLRPC_EPI_ENABLED','1');
3330 }
3331 else
3332 {
3333 define('XMLRPC_EPI_ENABLED','0');
3334 }
3335
3336 /**
3337 * Takes native php types and encodes them into xmlrpc PHP object format.
3338 * It will not re-encode xmlrpcval objects.
3339 *
3340 * Feature creep -- could support more types via optional type argument
3341 * (string => datetime support has been added, ??? => base64 not yet)
3342 *
3343 * If given a proper options parameter, php object instances will be encoded
3344 * into 'special' xmlrpc values, that can later be decoded into php objects
3345 * by calling php_xmlrpc_decode() with a corresponding option
3346 *
3347 * @author Dan Libby (dan@libby.com)
3348 *
3349 * @param mixed $php_val the value to be converted into an xmlrpcval object
3350 * @param array $options can include 'encode_php_objs', 'auto_dates', 'null_extension' or 'extension_api'
3351 * @return xmlrpcval
3352 */
3353 function &php_xmlrpc_encode($php_val, $options=array())
3354 {
3355 $type = gettype($php_val);
3356 switch($type)
3357 {
3358 case 'string':
3359 if (in_array('auto_dates', $options) && preg_match('/^[0-9]{8}T[0-9]{2}:[0-9]{2}:[0-9]{2}$/', $php_val))
3360 $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDateTime']);
3361 else
3362 $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcString']);
3363 break;
3364 case 'integer':
3365 $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcInt']);
3366 break;
3367 case 'double':
3368 $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcDouble']);
3369 break;
3370 // <G_Giunta_2001-02-29>
3371 // Add support for encoding/decoding of booleans, since they are supported in PHP
3372 case 'boolean':
3373 $xmlrpc_val = new xmlrpcval($php_val, $GLOBALS['xmlrpcBoolean']);
3374 break;
3375 // </G_Giunta_2001-02-29>
3376 case 'array':
3377 // PHP arrays can be encoded to either xmlrpc structs or arrays,
3378 // depending on wheter they are hashes or plain 0..n integer indexed
3379 // A shorter one-liner would be
3380 // $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
3381 // but execution time skyrockets!
3382 $j = 0;
3383 $arr = array();
3384 $ko = false;
3385 foreach($php_val as $key => $val)
3386 {
3387 $arr[$key] =& php_xmlrpc_encode($val, $options);
3388 if(!$ko && $key !== $j)
3389 {
3390 $ko = true;
3391 }
3392 $j++;
3393 }
3394 if($ko)
3395 {
3396 $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3397 }
3398 else
3399 {
3400 $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcArray']);
3401 }
3402 break;
3403 case 'object':
3404 if(is_a($php_val, 'xmlrpcval'))
3405 {
3406 $xmlrpc_val = $php_val;
3407 }
3408 else
3409 {
3410 $arr = array();
3411 while(list($k,$v) = each($php_val))
3412 {
3413 $arr[$k] = php_xmlrpc_encode($v, $options);
3414 }
3415 $xmlrpc_val = new xmlrpcval($arr, $GLOBALS['xmlrpcStruct']);
3416 if (in_array('encode_php_objs', $options))
3417 {
3418 // let's save original class name into xmlrpcval:
3419 // might be useful later on...
3420 $xmlrpc_val->_php_class = get_class($php_val);
3421 }
3422 }
3423 break;
3424 case 'NULL':
3425 if (in_array('extension_api', $options))
3426 {
3427 $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcString']);
3428 }
3429 if (in_array('null_extension', $options))
3430 {
3431 $xmlrpc_val = new xmlrpcval('', $GLOBALS['xmlrpcNull']);
3432 }
3433 else
3434 {
3435 $xmlrpc_val = new xmlrpcval();
3436 }
3437 break;
3438 case 'resource':
3439 if (in_array('extension_api', $options))
3440 {
3441 $xmlrpc_val = new xmlrpcval((int)$php_val, $GLOBALS['xmlrpcInt']);
3442 }
3443 else
3444 {
3445 $xmlrpc_val = new xmlrpcval();
3446 }
3447 // catch "user function", "unknown type"
3448 default:
3449 // giancarlo pinerolo <ping@alt.it>
3450 // it has to return
3451 // an empty object in case, not a boolean.
3452 $xmlrpc_val = new xmlrpcval();
3453 break;
3454 }
3455 return $xmlrpc_val;
3456 }
3457
3458 /**
3459 * Convert the xml representation of a method response, method request or single
3460 * xmlrpc value into the appropriate object (a.k.a. deserialize)
3461 * @param string $xml_val
3462 * @param array $options
3463 * @return mixed false on error, or an instance of either xmlrpcval, xmlrpcmsg or xmlrpcresp
3464 */
3465 function php_xmlrpc_decode_xml($xml_val, $options=array())
3466 {
3467 $GLOBALS['_xh'] = array();
3468 $GLOBALS['_xh']['ac'] = '';
3469 $GLOBALS['_xh']['stack'] = array();
3470 $GLOBALS['_xh']['valuestack'] = array();
3471 $GLOBALS['_xh']['params'] = array();
3472 $GLOBALS['_xh']['pt'] = array();
3473 $GLOBALS['_xh']['isf'] = 0;
3474 $GLOBALS['_xh']['isf_reason'] = '';
3475 $GLOBALS['_xh']['method'] = false;
3476 $GLOBALS['_xh']['rt'] = '';
3477 /// @todo 'guestimate' encoding
3478 $parser = xml_parser_create();
3479 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
3480 // What if internal encoding is not in one of the 3 allowed?
3481 // we use the broadest one, ie. utf8!
3482 if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
3483 {
3484 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
3485 }
3486 else
3487 {
3488 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
3489 }
3490 xml_set_element_handler($parser, 'xmlrpc_se_any', 'xmlrpc_ee');
3491 xml_set_character_data_handler($parser, 'xmlrpc_cd');
3492 xml_set_default_handler($parser, 'xmlrpc_dh');
3493 if(!xml_parse($parser, $xml_val, 1))
3494 {
3495 $errstr = sprintf('XML error: %s at line %d, column %d',
3496 xml_error_string(xml_get_error_code($parser)),
3497 xml_get_current_line_number($parser), xml_get_current_column_number($parser));
3498 error_log($errstr);
3499 xml_parser_free($parser);
3500 return false;
3501 }
3502 xml_parser_free($parser);
3503 if ($GLOBALS['_xh']['isf'] > 1) // test that $GLOBALS['_xh']['value'] is an obj, too???
3504 {
3505 error_log($GLOBALS['_xh']['isf_reason']);
3506 return false;
3507 }
3508 switch ($GLOBALS['_xh']['rt'])
3509 {
3510 case 'methodresponse':
3511 $v =& $GLOBALS['_xh']['value'];
3512 if ($GLOBALS['_xh']['isf'] == 1)
3513 {
3514 $vc = $v->structmem('faultCode');
3515 $vs = $v->structmem('faultString');
3516 $r = new xmlrpcresp(0, $vc->scalarval(), $vs->scalarval());
3517 }
3518 else
3519 {
3520 $r = new xmlrpcresp($v);
3521 }
3522 return $r;
3523 case 'methodcall':
3524 $m = new xmlrpcmsg($GLOBALS['_xh']['method']);
3525 for($i=0; $i < count($GLOBALS['_xh']['params']); $i++)
3526 {
3527 $m->addParam($GLOBALS['_xh']['params'][$i]);
3528 }
3529 return $m;
3530 case 'value':
3531 return $GLOBALS['_xh']['value'];
3532 default:
3533 return false;
3534 }
3535 }
3536
3537 /**
3538 * decode a string that is encoded w/ "chunked" transfer encoding
3539 * as defined in rfc2068 par. 19.4.6
3540 * code shamelessly stolen from nusoap library by Dietrich Ayala
3541 *
3542 * @param string $buffer the string to be decoded
3543 * @return string
3544 */
3545 function decode_chunked($buffer)
3546 {
3547 // length := 0
3548 $length = 0;
3549 $new = '';
3550
3551 // read chunk-size, chunk-extension (if any) and crlf
3552 // get the position of the linebreak
3553 $chunkend = strpos($buffer,"\r\n") + 2;
3554 $temp = substr($buffer,0,$chunkend);
3555 $chunk_size = hexdec( trim($temp) );
3556 $chunkstart = $chunkend;
3557 while($chunk_size > 0)
3558 {
3559 $chunkend = strpos($buffer, "\r\n", $chunkstart + $chunk_size);
3560
3561 // just in case we got a broken connection
3562 if($chunkend == false)
3563 {
3564 $chunk = substr($buffer,$chunkstart);
3565 // append chunk-data to entity-body
3566 $new .= $chunk;
3567 $length += strlen($chunk);
3568 break;
3569 }
3570
3571 // read chunk-data and crlf
3572 $chunk = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3573 // append chunk-data to entity-body
3574 $new .= $chunk;
3575 // length := length + chunk-size
3576 $length += strlen($chunk);
3577 // read chunk-size and crlf
3578 $chunkstart = $chunkend + 2;
3579
3580 $chunkend = strpos($buffer,"\r\n",$chunkstart)+2;
3581 if($chunkend == false)
3582 {
3583 break; //just in case we got a broken connection
3584 }
3585 $temp = substr($buffer,$chunkstart,$chunkend-$chunkstart);
3586 $chunk_size = hexdec( trim($temp) );
3587 $chunkstart = $chunkend;
3588 }
3589 return $new;
3590 }
3591
3592 /**
3593 * xml charset encoding guessing helper function.
3594 * Tries to determine the charset encoding of an XML chunk received over HTTP.
3595 * NB: according to the spec (RFC 3023), if text/xml content-type is received over HTTP without a content-type,
3596 * we SHOULD assume it is strictly US-ASCII. But we try to be more tolerant of unconforming (legacy?) clients/servers,
3597 * which will be most probably using UTF-8 anyway...
3598 *
3599 * @param string $httpheaders the http Content-type header
3600 * @param string $xmlchunk xml content buffer
3601 * @param string $encoding_prefs comma separated list of character encodings to be used as default (when mb extension is enabled)
3602 *
3603 * @todo explore usage of mb_http_input(): does it detect http headers + post data? if so, use it instead of hand-detection!!!
3604 */
3605 function guess_encoding($httpheader='', $xmlchunk='', $encoding_prefs=null)
3606 {
3607 // discussion: see http://www.yale.edu/pclt/encoding/
3608 // 1 - test if encoding is specified in HTTP HEADERS
3609
3610 //Details:
3611 // LWS: (\13\10)?( |\t)+
3612 // token: (any char but excluded stuff)+
3613 // quoted string: " (any char but double quotes and cointrol chars)* "
3614 // header: Content-type = ...; charset=value(; ...)*
3615 // where value is of type token, no LWS allowed between 'charset' and value
3616 // Note: we do not check for invalid chars in VALUE:
3617 // this had better be done using pure ereg as below
3618 // Note 2: we might be removing whitespace/tabs that ought to be left in if
3619 // the received charset is a quoted string. But nobody uses such charset names...
3620
3621 /// @todo this test will pass if ANY header has charset specification, not only Content-Type. Fix it?
3622 $matches = array();
3623 if(preg_match('/;\s*charset\s*=([^;]+)/i', $httpheader, $matches))
3624 {
3625 return strtoupper(trim($matches[1], " \t\""));
3626 }
3627
3628 // 2 - scan the first bytes of the data for a UTF-16 (or other) BOM pattern
3629 // (source: http://www.w3.org/TR/2000/REC-xml-20001006)
3630 // NOTE: actually, according to the spec, even if we find the BOM and determine
3631 // an encoding, we should check if there is an encoding specified
3632 // in the xml declaration, and verify if they match.
3633 /// @todo implement check as described above?
3634 /// @todo implement check for first bytes of string even without a BOM? (It sure looks harder than for cases WITH a BOM)
3635 if(preg_match('/^(\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\x00\x00\xFF\xFE|\xFE\xFF\x00\x00)/', $xmlchunk))
3636 {
3637 return 'UCS-4';
3638 }
3639 elseif(preg_match('/^(\xFE\xFF|\xFF\xFE)/', $xmlchunk))
3640 {
3641 return 'UTF-16';
3642 }
3643 elseif(preg_match('/^(\xEF\xBB\xBF)/', $xmlchunk))
3644 {
3645 return 'UTF-8';
3646 }
3647
3648 // 3 - test if encoding is specified in the xml declaration
3649 // Details:
3650 // SPACE: (#x20 | #x9 | #xD | #xA)+ === [ \x9\xD\xA]+
3651 // EQ: SPACE?=SPACE? === [ \x9\xD\xA]*=[ \x9\xD\xA]*
3652 if (preg_match('/^<\?xml\s+version\s*=\s*'. "((?:\"[a-zA-Z0-9_.:-]+\")|(?:'[a-zA-Z0-9_.:-]+'))".
3653 '\s+encoding\s*=\s*' . "((?:\"[A-Za-z][A-Za-z0-9._-]*\")|(?:'[A-Za-z][A-Za-z0-9._-]*'))/",
3654 $xmlchunk, $matches))
3655 {
3656 return strtoupper(substr($matches[2], 1, -1));
3657 }
3658
3659 // 4 - if mbstring is available, let it do the guesswork
3660 // NB: we favour finding an encoding that is compatible with what we can process
3661 if(extension_loaded('mbstring'))
3662 {
3663 if($encoding_prefs)
3664 {
3665 $enc = mb_detect_encoding($xmlchunk, $encoding_prefs);
3666 }
3667 else
3668 {
3669 $enc = mb_detect_encoding($xmlchunk);
3670 }
3671 // NB: mb_detect likes to call it ascii, xml parser likes to call it US_ASCII...
3672 // IANA also likes better US-ASCII, so go with it
3673 if($enc == 'ASCII')
3674 {
3675 $enc = 'US-'.$enc;
3676 }
3677 return $enc;
3678 }
3679 else
3680 {
3681 // no encoding specified: as per HTTP1.1 assume it is iso-8859-1?
3682 // Both RFC 2616 (HTTP 1.1) and 1945 (HTTP 1.0) clearly state that for text/xxx content types
3683 // this should be the standard. And we should be getting text/xml as request and response.
3684 // BUT we have to be backward compatible with the lib, which always used UTF-8 as default...
3685 return $GLOBALS['xmlrpc_defencoding'];
3686 }
3687 }
3688
3689 /**
3690 * Checks if a given charset encoding is present in a list of encodings or
3691 * if it is a valid subset of any encoding in the list
3692 * @param string $encoding charset to be tested
3693 * @param mixed $validlist comma separated list of valid charsets (or array of charsets)
3694 */
3695 function is_valid_charset($encoding, $validlist)
3696 {
3697 $charset_supersets = array(
3698 'US-ASCII' => array ('ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
3699 'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8',
3700 'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-11', 'ISO-8859-12',
3701 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'UTF-8',
3702 'EUC-JP', 'EUC-', 'EUC-KR', 'EUC-CN')
3703 );
3704 if (is_string($validlist))
3705 $validlist = explode(',', $validlist);
3706 if (@in_array(strtoupper($encoding), $validlist))
3707 return true;
3708 else
3709 {
3710 if (array_key_exists($encoding, $charset_supersets))
3711 foreach ($validlist as $allowed)
3712 if (in_array($allowed, $charset_supersets[$encoding]))
3713 return true;
3714 return false;
3715 }
3716 }
3717
3718?> \ No newline at end of file
diff --git a/web/phpxmlrpclib/xmlrpc_wrappers.inc b/web/phpxmlrpclib/xmlrpc_wrappers.inc
new file mode 100644
index 0000000..cb0c6e8
--- /dev/null
+++ b/web/phpxmlrpclib/xmlrpc_wrappers.inc
@@ -0,0 +1,944 @@
1<?php
2/**
3 * PHP-XMLRPC "wrapper" functions
4 * Generate stubs to transparently access xmlrpc methods as php functions and viceversa
5 *
6 * @version $Id: xmlrpc_wrappers.inc,v 1.13 2008/09/20 01:23:47 ggiunta Exp $
7 * @author Gaetano Giunta
8 * @copyright (C) 2006-2008 G. Giunta
9 * @license code licensed under the BSD License: http://phpxmlrpc.sourceforge.net/license.txt
10 *
11 * @todo separate introspection from code generation for func-2-method wrapping
12 * @todo use some better templating system for code generation?
13 * @todo implement method wrapping with preservation of php objs in calls
14 * @todo when wrapping methods without obj rebuilding, use return_type = 'phpvals' (faster)
15 * @todo implement self-parsing of php code for PHP <= 4
16 */
17
18 // requires: xmlrpc.inc
19
20 /**
21 * Given a string defining a php type or phpxmlrpc type (loosely defined: strings
22 * accepted come from javadoc blocks), return corresponding phpxmlrpc type.
23 * NB: for php 'resource' types returns empty string, since resources cannot be serialized;
24 * for php class names returns 'struct', since php objects can be serialized as xmlrpc structs
25 * @param string $phptype
26 * @return string
27 */
28 function php_2_xmlrpc_type($phptype)
29 {
30 switch(strtolower($phptype))
31 {
32 case 'string':
33 return $GLOBALS['xmlrpcString'];
34 case 'integer':
35 case $GLOBALS['xmlrpcInt']: // 'int'
36 case $GLOBALS['xmlrpcI4']:
37 return $GLOBALS['xmlrpcInt'];
38 case 'double':
39 return $GLOBALS['xmlrpcDouble'];
40 case 'boolean':
41 return $GLOBALS['xmlrpcBoolean'];
42 case 'array':
43 return $GLOBALS['xmlrpcArray'];
44 case 'object':
45 return $GLOBALS['xmlrpcStruct'];
46 case $GLOBALS['xmlrpcBase64']:
47 case $GLOBALS['xmlrpcStruct']:
48 return strtolower($phptype);
49 case 'resource':
50 return '';
51 default:
52 if(class_exists($phptype))
53 {
54 return $GLOBALS['xmlrpcStruct'];
55 }
56 else
57 {
58 // unknown: might be any 'extended' xmlrpc type
59 return $GLOBALS['xmlrpcValue'];
60 }
61 }
62 }
63
64 /**
65 * Given a string defining a phpxmlrpc type return corresponding php type.
66 * @param string $xmlrpctype
67 * @return string
68 */
69 function xmlrpc_2_php_type($xmlrpctype)
70 {
71 switch(strtolower($xmlrpctype))
72 {
73 case 'base64':
74 case 'datetime.iso8601':
75 case 'string':
76 return $GLOBALS['xmlrpcString'];
77 case 'int':
78 case 'i4':
79 return 'integer';
80 case 'struct':
81 case 'array':
82 return 'array';
83 case 'double':
84 return 'float';
85 case 'undefined':
86 return 'mixed';
87 case 'boolean':
88 case 'null':
89 default:
90 // unknown: might be any xmlrpc type
91 return strtolower($xmlrpctype);
92 }
93 }
94
95 /**
96 * Given a user-defined PHP function, create a PHP 'wrapper' function that can
97 * be exposed as xmlrpc method from an xmlrpc_server object and called from remote
98 * clients (as well as its corresponding signature info).
99 *
100 * Since php is a typeless language, to infer types of input and output parameters,
101 * it relies on parsing the javadoc-style comment block associated with the given
102 * function. Usage of xmlrpc native types (such as datetime.dateTime.iso8601 and base64)
103 * in the @param tag is also allowed, if you need the php function to receive/send
104 * data in that particular format (note that base64 encoding/decoding is transparently
105 * carried out by the lib, while datetime vals are passed around as strings)
106 *
107 * Known limitations:
108 * - requires PHP 5.0.3 +
109 * - only works for user-defined functions, not for PHP internal functions
110 * (reflection does not support retrieving number/type of params for those)
111 * - functions returning php objects will generate special xmlrpc responses:
112 * when the xmlrpc decoding of those responses is carried out by this same lib, using
113 * the appropriate param in php_xmlrpc_decode, the php objects will be rebuilt.
114 * In short: php objects can be serialized, too (except for their resource members),
115 * using this function.
116 * Other libs might choke on the very same xml that will be generated in this case
117 * (i.e. it has a nonstandard attribute on struct element tags)
118 * - usage of javadoc @param tags using param names in a different order from the
119 * function prototype is not considered valid (to be fixed?)
120 *
121 * Note that since rel. 2.0RC3 the preferred method to have the server call 'standard'
122 * php functions (ie. functions not expecting a single xmlrpcmsg obj as parameter)
123 * is by making use of the functions_parameters_type class member.
124 *
125 * @param string $funcname the name of the PHP user function to be exposed as xmlrpc method; array($obj, 'methodname') and array('class', 'methodname') are ok too
126 * @param string $newfuncname (optional) name for function to be created
127 * @param array $extra_options (optional) array of options for conversion. valid values include:
128 * bool return_source when true, php code w. function definition will be returned, not evaluated
129 * bool encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
130 * bool decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
131 * bool suppress_warnings remove from produced xml any runtime warnings due to the php function being invoked
132 * @return false on error, or an array containing the name of the new php function,
133 * its signature and docs, to be used in the server dispatch map
134 *
135 * @todo decide how to deal with params passed by ref: bomb out or allow?
136 * @todo finish using javadoc info to build method sig if all params are named but out of order
137 * @todo add a check for params of 'resource' type
138 * @todo add some trigger_errors / error_log when returning false?
139 * @todo what to do when the PHP function returns NULL? we are currently returning an empty string value...
140 * @todo add an option to suppress php warnings in invocation of user function, similar to server debug level 3?
141 * @todo if $newfuncname is empty, we could use create_user_func instead of eval, as it is possibly faster
142 * @todo add a verbatim_object_copy parameter to allow avoiding the same obj instance?
143 */
144 function wrap_php_function($funcname, $newfuncname='', $extra_options=array())
145 {
146 $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
147 $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
148 $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
149 $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
150 $catch_warnings = isset($extra_options['suppress_warnings']) && $extra_options['suppress_warnings'] ? '@' : '';
151
152 if(version_compare(phpversion(), '5.0.3') == -1)
153 {
154 // up to php 5.0.3 some useful reflection methods were missing
155 error_log('XML-RPC: cannot not wrap php functions unless running php version bigger than 5.0.3');
156 return false;
157 }
158
159 $exists = false;
160 if(is_array($funcname))
161 {
162 if(count($funcname) < 2 || (!is_string($funcname[0]) && !is_object($funcname[0])))
163 {
164 error_log('XML-RPC: syntax for function to be wrapped is wrong');
165 return false;
166 }
167 if(is_string($funcname[0]))
168 {
169 $plainfuncname = implode('::', $funcname);
170 }
171 elseif(is_object($funcname[0]))
172 {
173 $plainfuncname = get_class($funcname[0]) . '->' . $funcname[1];
174 }
175 $exists = method_exists($funcname[0], $funcname[1]);
176 }
177 else
178 {
179 $plainfuncname = $funcname;
180 $exists = function_exists($funcname);
181 }
182
183 if(!$exists)
184 {
185 error_log('XML-RPC: function to be wrapped is not defined: '.$plainfuncname);
186 return false;
187 }
188 else
189 {
190 // determine name of new php function
191 if($newfuncname == '')
192 {
193 if(is_array($funcname))
194 {
195 if(is_string($funcname[0]))
196 $xmlrpcfuncname = "{$prefix}_".implode('_', $funcname);
197 else
198 $xmlrpcfuncname = "{$prefix}_".get_class($funcname[0]) . '_' . $funcname[1];
199 }
200 else
201 {
202 $xmlrpcfuncname = "{$prefix}_$funcname";
203 }
204 }
205 else
206 {
207 $xmlrpcfuncname = $newfuncname;
208 }
209 while($buildit && function_exists($xmlrpcfuncname))
210 {
211 $xmlrpcfuncname .= 'x';
212 }
213
214 // start to introspect PHP code
215 if(is_array($funcname))
216 {
217 $func =& new ReflectionMethod($funcname[0], $funcname[1]);
218 if($func->isPrivate())
219 {
220 error_log('XML-RPC: method to be wrapped is private: '.$plainfuncname);
221 return false;
222 }
223 if($func->isProtected())
224 {
225 error_log('XML-RPC: method to be wrapped is protected: '.$plainfuncname);
226 return false;
227 }
228 if($func->isConstructor())
229 {
230 error_log('XML-RPC: method to be wrapped is the constructor: '.$plainfuncname);
231 return false;
232 }
233 if($func->isDestructor())
234 {
235 error_log('XML-RPC: method to be wrapped is the destructor: '.$plainfuncname);
236 return false;
237 }
238 if($func->isAbstract())
239 {
240 error_log('XML-RPC: method to be wrapped is abstract: '.$plainfuncname);
241 return false;
242 }
243 /// @todo add more checks for static vs. nonstatic?
244 }
245 else
246 {
247 $func =& new ReflectionFunction($funcname);
248 }
249 if($func->isInternal())
250 {
251 // Note: from PHP 5.1.0 onward, we will possibly be able to use invokeargs
252 // instead of getparameters to fully reflect internal php functions ?
253 error_log('XML-RPC: function to be wrapped is internal: '.$plainfuncname);
254 return false;
255 }
256
257 // retrieve parameter names, types and description from javadoc comments
258
259 // function description
260 $desc = '';
261 // type of return val: by default 'any'
262 $returns = $GLOBALS['xmlrpcValue'];
263 // desc of return val
264 $returnsDocs = '';
265 // type + name of function parameters
266 $paramDocs = array();
267
268 $docs = $func->getDocComment();
269 if($docs != '')
270 {
271 $docs = explode("\n", $docs);
272 $i = 0;
273 foreach($docs as $doc)
274 {
275 $doc = trim($doc, " \r\t/*");
276 if(strlen($doc) && strpos($doc, '@') !== 0 && !$i)
277 {
278 if($desc)
279 {
280 $desc .= "\n";
281 }
282 $desc .= $doc;
283 }
284 elseif(strpos($doc, '@param') === 0)
285 {
286 // syntax: @param type [$name] desc
287 if(preg_match('/@param\s+(\S+)(\s+\$\S+)?\s+(.+)/', $doc, $matches))
288 {
289 if(strpos($matches[1], '|'))
290 {
291 //$paramDocs[$i]['type'] = explode('|', $matches[1]);
292 $paramDocs[$i]['type'] = 'mixed';
293 }
294 else
295 {
296 $paramDocs[$i]['type'] = $matches[1];
297 }
298 $paramDocs[$i]['name'] = trim($matches[2]);
299 $paramDocs[$i]['doc'] = $matches[3];
300 }
301 $i++;
302 }
303 elseif(strpos($doc, '@return') === 0)
304 {
305 // syntax: @return type desc
306 //$returns = preg_split('/\s+/', $doc);
307 if(preg_match('/@return\s+(\S+)\s+(.+)/', $doc, $matches))
308 {
309 $returns = php_2_xmlrpc_type($matches[1]);
310 if(isset($matches[2]))
311 {
312 $returnsDocs = $matches[2];
313 }
314 }
315 }
316 }
317 }
318
319 // execute introspection of actual function prototype
320 $params = array();
321 $i = 0;
322 foreach($func->getParameters() as $paramobj)
323 {
324 $params[$i] = array();
325 $params[$i]['name'] = '$'.$paramobj->getName();
326 $params[$i]['isoptional'] = $paramobj->isOptional();
327 $i++;
328 }
329
330
331 // start building of PHP code to be eval'd
332 $innercode = '';
333 $i = 0;
334 $parsvariations = array();
335 $pars = array();
336 $pnum = count($params);
337 foreach($params as $param)
338 {
339 if (isset($paramDocs[$i]['name']) && $paramDocs[$i]['name'] && strtolower($paramDocs[$i]['name']) != strtolower($param['name']))
340 {
341 // param name from phpdoc info does not match param definition!
342 $paramDocs[$i]['type'] = 'mixed';
343 }
344
345 if($param['isoptional'])
346 {
347 // this particular parameter is optional. save as valid previous list of parameters
348 $innercode .= "if (\$paramcount > $i) {\n";
349 $parsvariations[] = $pars;
350 }
351 $innercode .= "\$p$i = \$msg->getParam($i);\n";
352 if ($decode_php_objects)
353 {
354 $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i, array('decode_php_objs'));\n";
355 }
356 else
357 {
358 $innercode .= "if (\$p{$i}->kindOf() == 'scalar') \$p$i = \$p{$i}->scalarval(); else \$p$i = php_{$prefix}_decode(\$p$i);\n";
359 }
360
361 $pars[] = "\$p$i";
362 $i++;
363 if($param['isoptional'])
364 {
365 $innercode .= "}\n";
366 }
367 if($i == $pnum)
368 {
369 // last allowed parameters combination
370 $parsvariations[] = $pars;
371 }
372 }
373
374 $sigs = array();
375 $psigs = array();
376 if(count($parsvariations) == 0)
377 {
378 // only known good synopsis = no parameters
379 $parsvariations[] = array();
380 $minpars = 0;
381 }
382 else
383 {
384 $minpars = count($parsvariations[0]);
385 }
386
387 if($minpars)
388 {
389 // add to code the check for min params number
390 // NB: this check needs to be done BEFORE decoding param values
391 $innercode = "\$paramcount = \$msg->getNumParams();\n" .
392 "if (\$paramcount < $minpars) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}');\n" . $innercode;
393 }
394 else
395 {
396 $innercode = "\$paramcount = \$msg->getNumParams();\n" . $innercode;
397 }
398
399 $innercode .= "\$np = false;\n";
400 // since there are no closures in php, if we are given an object instance,
401 // we store a pointer to it in a global var...
402 if ( is_array($funcname) && is_object($funcname[0]) )
403 {
404 $GLOBALS['xmlrpcWPFObjHolder'][$xmlrpcfuncname] =& $funcname[0];
405 $innercode .= "\$obj =& \$GLOBALS['xmlrpcWPFObjHolder']['$xmlrpcfuncname'];\n";
406 $realfuncname = '$obj->'.$funcname[1];
407 }
408 else
409 {
410 $realfuncname = $plainfuncname;
411 }
412 foreach($parsvariations as $pars)
413 {
414 $innercode .= "if (\$paramcount == " . count($pars) . ") \$retval = {$catch_warnings}$realfuncname(" . implode(',', $pars) . "); else\n";
415 // build a 'generic' signature (only use an appropriate return type)
416 $sig = array($returns);
417 $psig = array($returnsDocs);
418 for($i=0; $i < count($pars); $i++)
419 {
420 if (isset($paramDocs[$i]['type']))
421 {
422 $sig[] = php_2_xmlrpc_type($paramDocs[$i]['type']);
423 }
424 else
425 {
426 $sig[] = $GLOBALS['xmlrpcValue'];
427 }
428 $psig[] = isset($paramDocs[$i]['doc']) ? $paramDocs[$i]['doc'] : '';
429 }
430 $sigs[] = $sig;
431 $psigs[] = $psig;
432 }
433 $innercode .= "\$np = true;\n";
434 $innercode .= "if (\$np) return new {$prefix}resp(0, {$GLOBALS['xmlrpcerr']['incorrect_params']}, '{$GLOBALS['xmlrpcstr']['incorrect_params']}'); else {\n";
435 //$innercode .= "if (\$_xmlrpcs_error_occurred) return new xmlrpcresp(0, $GLOBALS['xmlrpcerr']user, \$_xmlrpcs_error_occurred); else\n";
436 $innercode .= "if (is_a(\$retval, '{$prefix}resp')) return \$retval; else\n";
437 if($returns == $GLOBALS['xmlrpcDateTime'] || $returns == $GLOBALS['xmlrpcBase64'])
438 {
439 $innercode .= "return new {$prefix}resp(new {$prefix}val(\$retval, '$returns'));";
440 }
441 else
442 {
443 if ($encode_php_objects)
444 $innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval, array('encode_php_objs')));\n";
445 else
446 $innercode .= "return new {$prefix}resp(php_{$prefix}_encode(\$retval));\n";
447 }
448 // shall we exclude functions returning by ref?
449 // if($func->returnsReference())
450 // return false;
451 $code = "function $xmlrpcfuncname(\$msg) {\n" . $innercode . "}\n}";
452 //print_r($code);
453 if ($buildit)
454 {
455 $allOK = 0;
456 eval($code.'$allOK=1;');
457 // alternative
458 //$xmlrpcfuncname = create_function('$m', $innercode);
459
460 if(!$allOK)
461 {
462 error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap php function '.$plainfuncname);
463 return false;
464 }
465 }
466
467 /// @todo examine if $paramDocs matches $parsvariations and build array for
468 /// usage as method signature, plus put together a nice string for docs
469
470 $ret = array('function' => $xmlrpcfuncname, 'signature' => $sigs, 'docstring' => $desc, 'signature_docs' => $psigs, 'source' => $code);
471 return $ret;
472 }
473 }
474
475 /**
476 * Given a user-defined PHP class or php object, map its methods onto a list of
477 * PHP 'wrapper' functions that can be exposed as xmlrpc methods from an xmlrpc_server
478 * object and called from remote clients (as well as their corresponding signature info).
479 *
480 * @param mixed $classname the name of the class whose methods are to be exposed as xmlrpc methods, or an object instance of that class
481 * @param array $extra_options see the docs for wrap_php_method for more options
482 * string method_type 'static', 'nonstatic', 'all' and 'auto' (default); the latter will switch between static and non-static depending on wheter $classname is a class name or object instance
483 * @return array or false on failure
484 *
485 * @todo get_class_methods will return both static and non-static methods.
486 * we have to differentiate the action, depending on wheter we recived a class name or object
487 */
488 function wrap_php_class($classname, $extra_options=array())
489 {
490 $methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
491 $methodtype = isset($extra_options['method_type']) ? $extra_options['method_type'] : 'auto';
492
493 if(version_compare(phpversion(), '5.0.3') == -1)
494 {
495 // up to php 5.0.3 some useful reflection methods were missing
496 error_log('XML-RPC: cannot not wrap php functions unless running php version bigger than 5.0.3');
497 return false;
498 }
499
500 $result = array();
501 $mlist = get_class_methods($classname);
502 foreach($mlist as $mname)
503 {
504 if ($methodfilter == '' || preg_match($methodfilter, $mname))
505 {
506 // echo $mlist."\n";
507 $func =& new ReflectionMethod($classname, $mname);
508 if(!$func->isPrivate() && !$func->isProtected() && !$func->isConstructor() && !$func->isDestructor() && !$func->isAbstract())
509 {
510 if(($func->isStatic && ($methodtype == 'all' || $methodtype == 'static' || ($methodtype == 'auto' && is_string($classname)))) ||
511 (!$func->isStatic && ($methodtype == 'all' || $methodtype == 'nonstatic' || ($methodtype == 'auto' && is_object($classname)))))
512 {
513 $methodwrap = wrap_php_function(array($classname, $mname), '', $extra_options);
514 if ( $methodwrap )
515 {
516 $result[$methodwrap['function']] = $methodwrap['function'];
517 }
518 }
519 }
520 }
521 }
522 return $result;
523 }
524
525 /**
526 * Given an xmlrpc client and a method name, register a php wrapper function
527 * that will call it and return results using native php types for both
528 * params and results. The generated php function will return an xmlrpcresp
529 * oject for failed xmlrpc calls
530 *
531 * Known limitations:
532 * - server must support system.methodsignature for the wanted xmlrpc method
533 * - for methods that expose many signatures, only one can be picked (we
534 * could in priciple check if signatures differ only by number of params
535 * and not by type, but it would be more complication than we can spare time)
536 * - nested xmlrpc params: the caller of the generated php function has to
537 * encode on its own the params passed to the php function if these are structs
538 * or arrays whose (sub)members include values of type datetime or base64
539 *
540 * Notes: the connection properties of the given client will be copied
541 * and reused for the connection used during the call to the generated
542 * php function.
543 * Calling the generated php function 'might' be slow: a new xmlrpc client
544 * is created on every invocation and an xmlrpc-connection opened+closed.
545 * An extra 'debug' param is appended to param list of xmlrpc method, useful
546 * for debugging purposes.
547 *
548 * @param xmlrpc_client $client an xmlrpc client set up correctly to communicate with target server
549 * @param string $methodname the xmlrpc method to be mapped to a php function
550 * @param array $extra_options array of options that specify conversion details. valid ptions include
551 * integer signum the index of the method signature to use in mapping (if method exposes many sigs)
552 * integer timeout timeout (in secs) to be used when executing function/calling remote method
553 * string protocol 'http' (default), 'http11' or 'https'
554 * string new_function_name the name of php function to create. If unsepcified, lib will pick an appropriate name
555 * string return_source if true return php code w. function definition instead fo function name
556 * bool encode_php_objs let php objects be sent to server using the 'improved' xmlrpc notation, so server can deserialize them as php objects
557 * bool decode_php_objs --- WARNING !!! possible security hazard. only use it with trusted servers ---
558 * mixed return_on_fault a php value to be returned when the xmlrpc call fails/returns a fault response (by default the xmlrpcresp object is returned in this case). If a string is used, '%faultCode%' and '%faultString%' tokens will be substituted with actual error values
559 * bool debug set it to 1 or 2 to see debug results of querying server for method synopsis
560 * @return string the name of the generated php function (or false) - OR AN ARRAY...
561 */
562 function wrap_xmlrpc_method($client, $methodname, $extra_options=0, $timeout=0, $protocol='', $newfuncname='')
563 {
564 // mind numbing: let caller use sane calling convention (as per javadoc, 3 params),
565 // OR the 2.0 calling convention (no options) - we really love backward compat, don't we?
566 if (!is_array($extra_options))
567 {
568 $signum = $extra_options;
569 $extra_options = array();
570 }
571 else
572 {
573 $signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
574 $timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
575 $protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
576 $newfuncname = isset($extra_options['new_function_name']) ? $extra_options['new_function_name'] : '';
577 }
578 //$encode_php_objects = in_array('encode_php_objects', $extra_options);
579 //$verbatim_client_copy = in_array('simple_client_copy', $extra_options) ? 1 :
580 // in_array('build_class_code', $extra_options) ? 2 : 0;
581
582 $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
583 $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
584 $simple_client_copy = isset($extra_options['simple_client_copy']) ? (int)($extra_options['simple_client_copy']) : 0;
585 $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
586 $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
587 if (isset($extra_options['return_on_fault']))
588 {
589 $decode_fault = true;
590 $fault_response = $extra_options['return_on_fault'];
591 }
592 else
593 {
594 $decode_fault = false;
595 $fault_response = '';
596 }
597 $debug = isset($extra_options['debug']) ? ($extra_options['debug']) : 0;
598
599 $msgclass = $prefix.'msg';
600 $valclass = $prefix.'val';
601 $decodefunc = 'php_'.$prefix.'_decode';
602
603 $msg =& new $msgclass('system.methodSignature');
604 $msg->addparam(new $valclass($methodname));
605 $client->setDebug($debug);
606 $response =& $client->send($msg, $timeout, $protocol);
607 if($response->faultCode())
608 {
609 error_log('XML-RPC: could not retrieve method signature from remote server for method '.$methodname);
610 return false;
611 }
612 else
613 {
614 $msig = $response->value();
615 if ($client->return_type != 'phpvals')
616 {
617 $msig = $decodefunc($msig);
618 }
619 if(!is_array($msig) || count($msig) <= $signum)
620 {
621 error_log('XML-RPC: could not retrieve method signature nr.'.$signum.' from remote server for method '.$methodname);
622 return false;
623 }
624 else
625 {
626 // pick a suitable name for the new function, avoiding collisions
627 if($newfuncname != '')
628 {
629 $xmlrpcfuncname = $newfuncname;
630 }
631 else
632 {
633 // take care to insure that methodname is translated to valid
634 // php function name
635 $xmlrpcfuncname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
636 array('_', ''), $methodname);
637 }
638 while($buildit && function_exists($xmlrpcfuncname))
639 {
640 $xmlrpcfuncname .= 'x';
641 }
642
643 $msig = $msig[$signum];
644 $mdesc = '';
645 // if in 'offline' mode, get method description too.
646 // in online mode, favour speed of operation
647 if(!$buildit)
648 {
649 $msg =& new $msgclass('system.methodHelp');
650 $msg->addparam(new $valclass($methodname));
651 $response =& $client->send($msg, $timeout, $protocol);
652 if (!$response->faultCode())
653 {
654 $mdesc = $response->value();
655 if ($client->return_type != 'phpvals')
656 {
657 $mdesc = $mdesc->scalarval();
658 }
659 }
660 }
661
662 $results = build_remote_method_wrapper_code($client, $methodname,
663 $xmlrpcfuncname, $msig, $mdesc, $timeout, $protocol, $simple_client_copy,
664 $prefix, $decode_php_objects, $encode_php_objects, $decode_fault,
665 $fault_response);
666
667 //print_r($code);
668 if ($buildit)
669 {
670 $allOK = 0;
671 eval($results['source'].'$allOK=1;');
672 // alternative
673 //$xmlrpcfuncname = create_function('$m', $innercode);
674 if($allOK)
675 {
676 return $xmlrpcfuncname;
677 }
678 else
679 {
680 error_log('XML-RPC: could not create function '.$xmlrpcfuncname.' to wrap remote method '.$methodname);
681 return false;
682 }
683 }
684 else
685 {
686 $results['function'] = $xmlrpcfuncname;
687 return $results;
688 }
689 }
690 }
691 }
692
693 /**
694 * Similar to wrap_xmlrpc_method, but will generate a php class that wraps
695 * all xmlrpc methods exposed by the remote server as own methods.
696 * For more details see wrap_xmlrpc_method.
697 * @param xmlrpc_client $client the client obj all set to query the desired server
698 * @param array $extra_options list of options for wrapped code
699 * @return mixed false on error, the name of the created class if all ok or an array with code, class name and comments (if the appropriatevoption is set in extra_options)
700 */
701 function wrap_xmlrpc_server($client, $extra_options=array())
702 {
703 $methodfilter = isset($extra_options['method_filter']) ? $extra_options['method_filter'] : '';
704 //$signum = isset($extra_options['signum']) ? (int)$extra_options['signum'] : 0;
705 $timeout = isset($extra_options['timeout']) ? (int)$extra_options['timeout'] : 0;
706 $protocol = isset($extra_options['protocol']) ? $extra_options['protocol'] : '';
707 $newclassname = isset($extra_options['new_class_name']) ? $extra_options['new_class_name'] : '';
708 $encode_php_objects = isset($extra_options['encode_php_objs']) ? (bool)$extra_options['encode_php_objs'] : false;
709 $decode_php_objects = isset($extra_options['decode_php_objs']) ? (bool)$extra_options['decode_php_objs'] : false;
710 $verbatim_client_copy = isset($extra_options['simple_client_copy']) ? !($extra_options['simple_client_copy']) : true;
711 $buildit = isset($extra_options['return_source']) ? !($extra_options['return_source']) : true;
712 $prefix = isset($extra_options['prefix']) ? $extra_options['prefix'] : 'xmlrpc';
713
714 $msgclass = $prefix.'msg';
715 //$valclass = $prefix.'val';
716 $decodefunc = 'php_'.$prefix.'_decode';
717
718 $msg =& new $msgclass('system.listMethods');
719 $response =& $client->send($msg, $timeout, $protocol);
720 if($response->faultCode())
721 {
722 error_log('XML-RPC: could not retrieve method list from remote server');
723 return false;
724 }
725 else
726 {
727 $mlist = $response->value();
728 if ($client->return_type != 'phpvals')
729 {
730 $mlist = $decodefunc($mlist);
731 }
732 if(!is_array($mlist) || !count($mlist))
733 {
734 error_log('XML-RPC: could not retrieve meaningful method list from remote server');
735 return false;
736 }
737 else
738 {
739 // pick a suitable name for the new function, avoiding collisions
740 if($newclassname != '')
741 {
742 $xmlrpcclassname = $newclassname;
743 }
744 else
745 {
746 $xmlrpcclassname = $prefix.'_'.preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
747 array('_', ''), $client->server).'_client';
748 }
749 while($buildit && class_exists($xmlrpcclassname))
750 {
751 $xmlrpcclassname .= 'x';
752 }
753
754 /// @todo add function setdebug() to new class, to enable/disable debugging
755 $source = "class $xmlrpcclassname\n{\nvar \$client;\n\n";
756 $source .= "function $xmlrpcclassname()\n{\n";
757 $source .= build_client_wrapper_code($client, $verbatim_client_copy, $prefix);
758 $source .= "\$this->client =& \$client;\n}\n\n";
759 $opts = array('simple_client_copy' => 2, 'return_source' => true,
760 'timeout' => $timeout, 'protocol' => $protocol,
761 'encode_php_objs' => $encode_php_objects, 'prefix' => $prefix,
762 'decode_php_objs' => $decode_php_objects
763 );
764 /// @todo build javadoc for class definition, too
765 foreach($mlist as $mname)
766 {
767 if ($methodfilter == '' || preg_match($methodfilter, $mname))
768 {
769 $opts['new_function_name'] = preg_replace(array('/\./', '/[^a-zA-Z0-9_\x7f-\xff]/'),
770 array('_', ''), $mname);
771 $methodwrap = wrap_xmlrpc_method($client, $mname, $opts);
772 if ($methodwrap)
773 {
774 if (!$buildit)
775 {
776 $source .= $methodwrap['docstring'];
777 }
778 $source .= $methodwrap['source']."\n";
779 }
780 else
781 {
782 error_log('XML-RPC: will not create class method to wrap remote method '.$mname);
783 }
784 }
785 }
786 $source .= "}\n";
787 if ($buildit)
788 {
789 $allOK = 0;
790 eval($source.'$allOK=1;');
791 // alternative
792 //$xmlrpcfuncname = create_function('$m', $innercode);
793 if($allOK)
794 {
795 return $xmlrpcclassname;
796 }
797 else
798 {
799 error_log('XML-RPC: could not create class '.$xmlrpcclassname.' to wrap remote server '.$client->server);
800 return false;
801 }
802 }
803 else
804 {
805 return array('class' => $xmlrpcclassname, 'code' => $source, 'docstring' => '');
806 }
807 }
808 }
809 }
810
811 /**
812 * Given the necessary info, build php code that creates a new function to
813 * invoke a remote xmlrpc method.
814 * Take care that no full checking of input parameters is done to ensure that
815 * valid php code is emitted.
816 * Note: real spaghetti code follows...
817 * @access private
818 */
819 function build_remote_method_wrapper_code($client, $methodname, $xmlrpcfuncname,
820 $msig, $mdesc='', $timeout=0, $protocol='', $client_copy_mode=0, $prefix='xmlrpc',
821 $decode_php_objects=false, $encode_php_objects=false, $decode_fault=false,
822 $fault_response='')
823 {
824 $code = "function $xmlrpcfuncname (";
825 if ($client_copy_mode < 2)
826 {
827 // client copy mode 0 or 1 == partial / full client copy in emitted code
828 $innercode = build_client_wrapper_code($client, $client_copy_mode, $prefix);
829 $innercode .= "\$client->setDebug(\$debug);\n";
830 $this_ = '';
831 }
832 else
833 {
834 // client copy mode 2 == no client copy in emitted code
835 $innercode = '';
836 $this_ = 'this->';
837 }
838 $innercode .= "\$msg =& new {$prefix}msg('$methodname');\n";
839
840 if ($mdesc != '')
841 {
842 // take care that PHP comment is not terminated unwillingly by method description
843 $mdesc = "/**\n* ".str_replace('*/', '* /', $mdesc)."\n";
844 }
845 else
846 {
847 $mdesc = "/**\nFunction $xmlrpcfuncname\n";
848 }
849
850 // param parsing
851 $plist = array();
852 $pcount = count($msig);
853 for($i = 1; $i < $pcount; $i++)
854 {
855 $plist[] = "\$p$i";
856 $ptype = $msig[$i];
857 if($ptype == 'i4' || $ptype == 'int' || $ptype == 'boolean' || $ptype == 'double' ||
858 $ptype == 'string' || $ptype == 'dateTime.iso8601' || $ptype == 'base64' || $ptype == 'null')
859 {
860 // only build directly xmlrpcvals when type is known and scalar
861 $innercode .= "\$p$i =& new {$prefix}val(\$p$i, '$ptype');\n";
862 }
863 else
864 {
865 if ($encode_php_objects)
866 {
867 $innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i, array('encode_php_objs'));\n";
868 }
869 else
870 {
871 $innercode .= "\$p$i =& php_{$prefix}_encode(\$p$i);\n";
872 }
873 }
874 $innercode .= "\$msg->addparam(\$p$i);\n";
875 $mdesc .= '* @param '.xmlrpc_2_php_type($ptype)." \$p$i\n";
876 }
877 if ($client_copy_mode < 2)
878 {
879 $plist[] = '$debug=0';
880 $mdesc .= "* @param int \$debug when 1 (or 2) will enable debugging of the underlying {$prefix} call (defaults to 0)\n";
881 }
882 $plist = implode(', ', $plist);
883 $mdesc .= '* @return '.xmlrpc_2_php_type($msig[0])." (or an {$prefix}resp obj instance if call fails)\n*/\n";
884
885 $innercode .= "\$res =& \${$this_}client->send(\$msg, $timeout, '$protocol');\n";
886 if ($decode_fault)
887 {
888 if (is_string($fault_response) && ((strpos($fault_response, '%faultCode%') !== false) || (strpos($fault_response, '%faultString%') !== false)))
889 {
890 $respcode = "str_replace(array('%faultCode%', '%faultString%'), array(\$res->faultCode(), \$res->faultString()), '".str_replace("'", "''", $fault_response)."')";
891 }
892 else
893 {
894 $respcode = var_export($fault_response, true);
895 }
896 }
897 else
898 {
899 $respcode = '$res';
900 }
901 if ($decode_php_objects)
902 {
903 $innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value(), array('decode_php_objs'));";
904 }
905 else
906 {
907 $innercode .= "if (\$res->faultcode()) return $respcode; else return php_{$prefix}_decode(\$res->value());";
908 }
909
910 $code = $code . $plist. ") {\n" . $innercode . "\n}\n";
911
912 return array('source' => $code, 'docstring' => $mdesc);
913 }
914
915 /**
916 * Given necessary info, generate php code that will rebuild a client object
917 * Take care that no full checking of input parameters is done to ensure that
918 * valid php code is emitted.
919 * @access private
920 */
921 function build_client_wrapper_code($client, $verbatim_client_copy, $prefix='xmlrpc')
922 {
923 $code = "\$client =& new {$prefix}_client('".str_replace("'", "\'", $client->path).
924 "', '" . str_replace("'", "\'", $client->server) . "', $client->port);\n";
925
926 // copy all client fields to the client that will be generated runtime
927 // (this provides for future expansion or subclassing of client obj)
928 if ($verbatim_client_copy)
929 {
930 foreach($client as $fld => $val)
931 {
932 if($fld != 'debug' && $fld != 'return_type')
933 {
934 $val = var_export($val, true);
935 $code .= "\$client->$fld = $val;\n";
936 }
937 }
938 }
939 // only make sure that client always returns the correct data type
940 $code .= "\$client->return_type = '{$prefix}vals';\n";
941 //$code .= "\$client->setDebug(\$debug);\n";
942 return $code;
943 }
944?> \ No newline at end of file
diff --git a/web/phpxmlrpclib/xmlrpcs.inc b/web/phpxmlrpclib/xmlrpcs.inc
new file mode 100644
index 0000000..7b47ca0
--- /dev/null
+++ b/web/phpxmlrpclib/xmlrpcs.inc
@@ -0,0 +1,1198 @@
1<?php
2// by Edd Dumbill (C) 1999-2002
3// <edd@usefulinc.com>
4// $Id: xmlrpcs.inc,v 1.71 2008/10/29 23:41:28 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 // XML RPC Server class
39 // requires: xmlrpc.inc
40
41 $GLOBALS['xmlrpcs_capabilities'] = array(
42 // xmlrpc spec: always supported
43 'xmlrpc' => new xmlrpcval(array(
44 'specUrl' => new xmlrpcval('http://www.xmlrpc.com/spec', 'string'),
45 'specVersion' => new xmlrpcval(1, 'int')
46 ), 'struct'),
47 // if we support system.xxx functions, we always support multicall, too...
48 // Note that, as of 2006/09/17, the following URL does not respond anymore
49 'system.multicall' => new xmlrpcval(array(
50 'specUrl' => new xmlrpcval('http://www.xmlrpc.com/discuss/msgReader$1208', 'string'),
51 'specVersion' => new xmlrpcval(1, 'int')
52 ), 'struct'),
53 // introspection: version 2! we support 'mixed', too
54 'introspection' => new xmlrpcval(array(
55 'specUrl' => new xmlrpcval('http://phpxmlrpc.sourceforge.net/doc-2/ch10.html', 'string'),
56 'specVersion' => new xmlrpcval(2, 'int')
57 ), 'struct')
58 );
59
60 /* Functions that implement system.XXX methods of xmlrpc servers */
61 $_xmlrpcs_getCapabilities_sig=array(array($GLOBALS['xmlrpcStruct']));
62 $_xmlrpcs_getCapabilities_doc='This method lists all the capabilites that the XML-RPC server has: the (more or less standard) extensions to the xmlrpc spec that it adheres to';
63 $_xmlrpcs_getCapabilities_sdoc=array(array('list of capabilities, described as structs with a version number and url for the spec'));
64 function _xmlrpcs_getCapabilities($server, $m=null)
65 {
66 $outAr = $GLOBALS['xmlrpcs_capabilities'];
67 // NIL extension
68 if ($GLOBALS['xmlrpc_null_extension']) {
69 $outAr['nil'] = new xmlrpcval(array(
70 'specUrl' => new xmlrpcval('http://www.ontosys.com/xml-rpc/extensions.php', 'string'),
71 'specVersion' => new xmlrpcval(1, 'int')
72 ), 'struct');
73 }
74 return new xmlrpcresp(new xmlrpcval($outAr, 'struct'));
75 }
76
77 // listMethods: signature was either a string, or nothing.
78 // The useless string variant has been removed
79 $_xmlrpcs_listMethods_sig=array(array($GLOBALS['xmlrpcArray']));
80 $_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch';
81 $_xmlrpcs_listMethods_sdoc=array(array('list of method names'));
82 function _xmlrpcs_listMethods($server, $m=null) // if called in plain php values mode, second param is missing
83 {
84
85 $outAr=array();
86 foreach($server->dmap as $key => $val)
87 {
88 $outAr[]= new xmlrpcval($key, 'string');
89 }
90 if($server->allow_system_funcs)
91 {
92 foreach($GLOBALS['_xmlrpcs_dmap'] as $key => $val)
93 {
94 $outAr[]= new xmlrpcval($key, 'string');
95 }
96 }
97 return new xmlrpcresp(new xmlrpcval($outAr, 'array'));
98 }
99
100 $_xmlrpcs_methodSignature_sig=array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcString']));
101 $_xmlrpcs_methodSignature_doc='Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)';
102 $_xmlrpcs_methodSignature_sdoc=array(array('list of known signatures, each sig being an array of xmlrpc type names', 'name of method to be described'));
103 function _xmlrpcs_methodSignature($server, $m)
104 {
105 // let accept as parameter both an xmlrpcval or string
106 if (is_object($m))
107 {
108 $methName=$m->getParam(0);
109 $methName=$methName->scalarval();
110 }
111 else
112 {
113 $methName=$m;
114 }
115 if(strpos($methName, "system.") === 0)
116 {
117 $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
118 }
119 else
120 {
121 $dmap=$server->dmap; $sysCall=0;
122 }
123 if(isset($dmap[$methName]))
124 {
125 if(isset($dmap[$methName]['signature']))
126 {
127 $sigs=array();
128 foreach($dmap[$methName]['signature'] as $inSig)
129 {
130 $cursig=array();
131 foreach($inSig as $sig)
132 {
133 $cursig[]= new xmlrpcval($sig, 'string');
134 }
135 $sigs[]= new xmlrpcval($cursig, 'array');
136 }
137 $r= new xmlrpcresp(new xmlrpcval($sigs, 'array'));
138 }
139 else
140 {
141 // NB: according to the official docs, we should be returning a
142 // "none-array" here, which means not-an-array
143 $r= new xmlrpcresp(new xmlrpcval('undef', 'string'));
144 }
145 }
146 else
147 {
148 $r= new xmlrpcresp(0,$GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
149 }
150 return $r;
151 }
152
153 $_xmlrpcs_methodHelp_sig=array(array($GLOBALS['xmlrpcString'], $GLOBALS['xmlrpcString']));
154 $_xmlrpcs_methodHelp_doc='Returns help text if defined for the method passed, otherwise returns an empty string';
155 $_xmlrpcs_methodHelp_sdoc=array(array('method description', 'name of the method to be described'));
156 function _xmlrpcs_methodHelp($server, $m)
157 {
158 // let accept as parameter both an xmlrpcval or string
159 if (is_object($m))
160 {
161 $methName=$m->getParam(0);
162 $methName=$methName->scalarval();
163 }
164 else
165 {
166 $methName=$m;
167 }
168 if(strpos($methName, "system.") === 0)
169 {
170 $dmap=$GLOBALS['_xmlrpcs_dmap']; $sysCall=1;
171 }
172 else
173 {
174 $dmap=$server->dmap; $sysCall=0;
175 }
176 if(isset($dmap[$methName]))
177 {
178 if(isset($dmap[$methName]['docstring']))
179 {
180 $r= new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string');
181 }
182 else
183 {
184 $r= new xmlrpcresp(new xmlrpcval('', 'string'));
185 }
186 }
187 else
188 {
189 $r=new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['introspect_unknown'], $GLOBALS['xmlrpcstr']['introspect_unknown']);
190 }
191 return $r;
192 }
193
194 $_xmlrpcs_multicall_sig = array(array($GLOBALS['xmlrpcArray'], $GLOBALS['xmlrpcArray']));
195 $_xmlrpcs_multicall_doc = 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details';
196 $_xmlrpcs_multicall_sdoc = array(array('list of response structs, where each struct has the usual members', 'list of calls, with each call being represented as a struct, with members "methodname" and "params"'));
197 function _xmlrpcs_multicall_error($err)
198 {
199 if(is_string($err))
200 {
201 $str = $GLOBALS['xmlrpcstr']["multicall_${err}"];
202 $code = $GLOBALS['xmlrpcerr']["multicall_${err}"];
203 }
204 else
205 {
206 $code = $err->faultCode();
207 $str = $err->faultString();
208 }
209 $struct = array();
210 $struct['faultCode'] = new xmlrpcval($code, 'int');
211 $struct['faultString'] = new xmlrpcval($str, 'string');
212 return new xmlrpcval($struct, 'struct');
213 }
214
215 function _xmlrpcs_multicall_do_call($server, $call)
216 {
217 if($call->kindOf() != 'struct')
218 {
219 return _xmlrpcs_multicall_error('notstruct');
220 }
221 $methName = @$call->structmem('methodName');
222 if(!$methName)
223 {
224 return _xmlrpcs_multicall_error('nomethod');
225 }
226 if($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string')
227 {
228 return _xmlrpcs_multicall_error('notstring');
229 }
230 if($methName->scalarval() == 'system.multicall')
231 {
232 return _xmlrpcs_multicall_error('recursion');
233 }
234
235 $params = @$call->structmem('params');
236 if(!$params)
237 {
238 return _xmlrpcs_multicall_error('noparams');
239 }
240 if($params->kindOf() != 'array')
241 {
242 return _xmlrpcs_multicall_error('notarray');
243 }
244 $numParams = $params->arraysize();
245
246 $msg = new xmlrpcmsg($methName->scalarval());
247 for($i = 0; $i < $numParams; $i++)
248 {
249 if(!$msg->addParam($params->arraymem($i)))
250 {
251 $i++;
252 return _xmlrpcs_multicall_error(new xmlrpcresp(0,
253 $GLOBALS['xmlrpcerr']['incorrect_params'],
254 $GLOBALS['xmlrpcstr']['incorrect_params'] . ": probable xml error in param " . $i));
255 }
256 }
257
258 $result = $server->execute($msg);
259
260 if($result->faultCode() != 0)
261 {
262 return _xmlrpcs_multicall_error($result); // Method returned fault.
263 }
264
265 return new xmlrpcval(array($result->value()), 'array');
266 }
267
268 function _xmlrpcs_multicall_do_call_phpvals($server, $call)
269 {
270 if(!is_array($call))
271 {
272 return _xmlrpcs_multicall_error('notstruct');
273 }
274 if(!array_key_exists('methodName', $call))
275 {
276 return _xmlrpcs_multicall_error('nomethod');
277 }
278 if (!is_string($call['methodName']))
279 {
280 return _xmlrpcs_multicall_error('notstring');
281 }
282 if($call['methodName'] == 'system.multicall')
283 {
284 return _xmlrpcs_multicall_error('recursion');
285 }
286 if(!array_key_exists('params', $call))
287 {
288 return _xmlrpcs_multicall_error('noparams');
289 }
290 if(!is_array($call['params']))
291 {
292 return _xmlrpcs_multicall_error('notarray');
293 }
294
295 // this is a real dirty and simplistic hack, since we might have received a
296 // base64 or datetime values, but they will be listed as strings here...
297 $numParams = count($call['params']);
298 $pt = array();
299 foreach($call['params'] as $val)
300 $pt[] = php_2_xmlrpc_type(gettype($val));
301
302 $result = $server->execute($call['methodName'], $call['params'], $pt);
303
304 if($result->faultCode() != 0)
305 {
306 return _xmlrpcs_multicall_error($result); // Method returned fault.
307 }
308
309 return new xmlrpcval(array($result->value()), 'array');
310 }
311
312 function _xmlrpcs_multicall($server, $m)
313 {
314 $result = array();
315 // let accept a plain list of php parameters, beside a single xmlrpc msg object
316 if (is_object($m))
317 {
318 $calls = $m->getParam(0);
319 $numCalls = $calls->arraysize();
320 for($i = 0; $i < $numCalls; $i++)
321 {
322 $call = $calls->arraymem($i);
323 $result[$i] = _xmlrpcs_multicall_do_call($server, $call);
324 }
325 }
326 else
327 {
328 $numCalls=count($m);
329 for($i = 0; $i < $numCalls; $i++)
330 {
331 $result[$i] = _xmlrpcs_multicall_do_call_phpvals($server, $m[$i]);
332 }
333 }
334
335 return new xmlrpcresp(new xmlrpcval($result, 'array'));
336 }
337
338 $GLOBALS['_xmlrpcs_dmap']=array(
339 'system.listMethods' => array(
340 'function' => '_xmlrpcs_listMethods',
341 'signature' => $_xmlrpcs_listMethods_sig,
342 'docstring' => $_xmlrpcs_listMethods_doc,
343 'signature_docs' => $_xmlrpcs_listMethods_sdoc),
344 'system.methodHelp' => array(
345 'function' => '_xmlrpcs_methodHelp',
346 'signature' => $_xmlrpcs_methodHelp_sig,
347 'docstring' => $_xmlrpcs_methodHelp_doc,
348 'signature_docs' => $_xmlrpcs_methodHelp_sdoc),
349 'system.methodSignature' => array(
350 'function' => '_xmlrpcs_methodSignature',
351 'signature' => $_xmlrpcs_methodSignature_sig,
352 'docstring' => $_xmlrpcs_methodSignature_doc,
353 'signature_docs' => $_xmlrpcs_methodSignature_sdoc),
354 'system.multicall' => array(
355 'function' => '_xmlrpcs_multicall',
356 'signature' => $_xmlrpcs_multicall_sig,
357 'docstring' => $_xmlrpcs_multicall_doc,
358 'signature_docs' => $_xmlrpcs_multicall_sdoc),
359 'system.getCapabilities' => array(
360 'function' => '_xmlrpcs_getCapabilities',
361 'signature' => $_xmlrpcs_getCapabilities_sig,
362 'docstring' => $_xmlrpcs_getCapabilities_doc,
363 'signature_docs' => $_xmlrpcs_getCapabilities_sdoc)
364 );
365
366 $GLOBALS['_xmlrpcs_occurred_errors'] = '';
367 $GLOBALS['_xmlrpcs_prev_ehandler'] = '';
368 /**
369 * Error handler used to track errors that occur during server-side execution of PHP code.
370 * This allows to report back to the client whether an internal error has occurred or not
371 * using an xmlrpc response object, instead of letting the client deal with the html junk
372 * that a PHP execution error on the server generally entails.
373 *
374 * NB: in fact a user defined error handler can only handle WARNING, NOTICE and USER_* errors.
375 *
376 */
377 function _xmlrpcs_errorHandler($errcode, $errstring, $filename=null, $lineno=null, $context=null)
378 {
379 // obey the @ protocol
380 if (error_reporting() == 0)
381 return;
382
383 //if($errcode != E_NOTICE && $errcode != E_WARNING && $errcode != E_USER_NOTICE && $errcode != E_USER_WARNING)
384 if($errcode != 2048) // do not use E_STRICT by name, since on PHP 4 it will not be defined
385 {
386 $GLOBALS['_xmlrpcs_occurred_errors'] = $GLOBALS['_xmlrpcs_occurred_errors'] . $errstring . "\n";
387 }
388 // Try to avoid as much as possible disruption to the previous error handling
389 // mechanism in place
390 if($GLOBALS['_xmlrpcs_prev_ehandler'] == '')
391 {
392 // The previous error handler was the default: all we should do is log error
393 // to the default error log (if level high enough)
394 if(ini_get('log_errors') && (intval(ini_get('error_reporting')) & $errcode))
395 {
396 error_log($errstring);
397 }
398 }
399 else
400 {
401 // Pass control on to previous error handler, trying to avoid loops...
402 if($GLOBALS['_xmlrpcs_prev_ehandler'] != '_xmlrpcs_errorHandler')
403 {
404 // NB: this code will NOT work on php < 4.0.2: only 2 params were used for error handlers
405 if(is_array($GLOBALS['_xmlrpcs_prev_ehandler']))
406 {
407 // the following works both with static class methods and plain object methods as error handler
408 call_user_func_array($GLOBALS['_xmlrpcs_prev_ehandler'], array($errcode, $errstring, $filename, $lineno, $context));
409 }
410 else
411 {
412 $GLOBALS['_xmlrpcs_prev_ehandler']($errcode, $errstring, $filename, $lineno, $context);
413 }
414 }
415 }
416 }
417
418 $GLOBALS['_xmlrpc_debuginfo']='';
419
420 /**
421 * Add a string to the debug info that can be later seralized by the server
422 * as part of the response message.
423 * Note that for best compatbility, the debug string should be encoded using
424 * the $GLOBALS['xmlrpc_internalencoding'] character set.
425 * @param string $m
426 * @access public
427 */
428 function xmlrpc_debugmsg($m)
429 {
430 $GLOBALS['_xmlrpc_debuginfo'] .= $m . "\n";
431 }
432
433 class xmlrpc_server
434 {
435 /// array defining php functions exposed as xmlrpc methods by this server
436 var $dmap=array();
437 /**
438 * Defines how functions in dmap will be invokde: either using an xmlrpc msg object
439 * or plain php values.
440 * valid strings are 'xmlrpcvals', 'phpvals' or 'epivals'
441 */
442 var $functions_parameters_type='xmlrpcvals';
443 /// controls wether the server is going to echo debugging messages back to the client as comments in response body. valid values: 0,1,2,3
444 var $debug = 1;
445 /**
446 * When set to true, it will enable HTTP compression of the response, in case
447 * the client has declared its support for compression in the request.
448 */
449 var $compress_response = false;
450 /**
451 * List of http compression methods accepted by the server for requests.
452 * NB: PHP supports deflate, gzip compressions out of the box if compiled w. zlib
453 */
454 var $accepted_compression = array();
455 /// shall we serve calls to system.* methods?
456 var $allow_system_funcs = true;
457 /// list of charset encodings natively accepted for requests
458 var $accepted_charset_encodings = array();
459 /**
460 * charset encoding to be used for response.
461 * NB: if we can, we will convert the generated response from internal_encoding to the intended one.
462 * can be: a supported xml encoding (only UTF-8 and ISO-8859-1 at present, unless mbstring is enabled),
463 * null (leave unspecified in response, convert output stream to US_ASCII),
464 * 'default' (use xmlrpc library default as specified in xmlrpc.inc, convert output stream if needed),
465 * or 'auto' (use client-specified charset encoding or same as request if request headers do not specify it (unless request is US-ASCII: then use library default anyway).
466 * NB: pretty dangerous if you accept every charset and do not have mbstring enabled)
467 */
468 var $response_charset_encoding = '';
469 /// storage for internal debug info
470 var $debug_info = '';
471 /// extra data passed at runtime to method handling functions. Used only by EPI layer
472 var $user_data = null;
473
474 /**
475 * @param array $dispmap the dispatch map withd efinition of exposed services
476 * @param boolean $servicenow set to false to prevent the server from runnung upon construction
477 */
478 function xmlrpc_server($dispMap=null, $serviceNow=true)
479 {
480 // if ZLIB is enabled, let the server by default accept compressed requests,
481 // and compress responses sent to clients that support them
482 if(function_exists('gzinflate'))
483 {
484 $this->accepted_compression = array('gzip', 'deflate');
485 $this->compress_response = true;
486 }
487
488 // by default the xml parser can support these 3 charset encodings
489 $this->accepted_charset_encodings = array('UTF-8', 'ISO-8859-1', 'US-ASCII');
490
491 // dispMap is a dispatch array of methods
492 // mapped to function names and signatures
493 // if a method
494 // doesn't appear in the map then an unknown
495 // method error is generated
496 /* milosch - changed to make passing dispMap optional.
497 * instead, you can use the class add_to_map() function
498 * to add functions manually (borrowed from SOAPX4)
499 */
500 if($dispMap)
501 {
502 $this->dmap = $dispMap;
503 if($serviceNow)
504 {
505 $this->service();
506 }
507 }
508 }
509
510 /**
511 * Set debug level of server.
512 * @param integer $in debug lvl: determines info added to xmlrpc responses (as xml comments)
513 * 0 = no debug info,
514 * 1 = msgs set from user with debugmsg(),
515 * 2 = add complete xmlrpc request (headers and body),
516 * 3 = add also all processing warnings happened during method processing
517 * (NB: this involves setting a custom error handler, and might interfere
518 * with the standard processing of the php function exposed as method. In
519 * particular, triggering an USER_ERROR level error will not halt script
520 * execution anymore, but just end up logged in the xmlrpc response)
521 * Note that info added at elevel 2 and 3 will be base64 encoded
522 * @access public
523 */
524 function setDebug($in)
525 {
526 $this->debug=$in;
527 }
528
529 /**
530 * Return a string with the serialized representation of all debug info
531 * @param string $charset_encoding the target charset encoding for the serialization
532 * @return string an XML comment (or two)
533 */
534 function serializeDebug($charset_encoding='')
535 {
536 // Tough encoding problem: which internal charset should we assume for debug info?
537 // It might contain a copy of raw data received from client, ie with unknown encoding,
538 // intermixed with php generated data and user generated data...
539 // so we split it: system debug is base 64 encoded,
540 // user debug info should be encoded by the end user using the INTERNAL_ENCODING
541 $out = '';
542 if ($this->debug_info != '')
543 {
544 $out .= "<!-- SERVER DEBUG INFO (BASE64 ENCODED):\n".base64_encode($this->debug_info)."\n-->\n";
545 }
546 if($GLOBALS['_xmlrpc_debuginfo']!='')
547 {
548
549 $out .= "<!-- DEBUG INFO:\n" . xmlrpc_encode_entitites(str_replace('--', '_-', $GLOBALS['_xmlrpc_debuginfo']), $GLOBALS['xmlrpc_internalencoding'], $charset_encoding) . "\n-->\n";
550 // NB: a better solution MIGHT be to use CDATA, but we need to insert it
551 // into return payload AFTER the beginning tag
552 //$out .= "<![CDATA[ DEBUG INFO:\n\n" . str_replace(']]>', ']_]_>', $GLOBALS['_xmlrpc_debuginfo']) . "\n]]>\n";
553 }
554 return $out;
555 }
556
557 /**
558 * Execute the xmlrpc request, printing the response
559 * @param string $data the request body. If null, the http POST request will be examined
560 * @return xmlrpcresp the response object (usually not used by caller...)
561 * @access public
562 */
563 function service($data=null, $return_payload=false)
564 {
565 if ($data === null)
566 {
567 // workaround for a known bug in php ver. 5.2.2 that broke $HTTP_RAW_POST_DATA
568 $ver = phpversion();
569 if ($ver[0] >= 5)
570 {
571 $data = file_get_contents('php://input');
572 }
573 else
574 {
575 $data = isset($GLOBALS['HTTP_RAW_POST_DATA']) ? $GLOBALS['HTTP_RAW_POST_DATA'] : '';
576 }
577 }
578 $raw_data = $data;
579
580 // reset internal debug info
581 $this->debug_info = '';
582
583 // Echo back what we received, before parsing it
584 if($this->debug > 1)
585 {
586 $this->debugmsg("+++GOT+++\n" . $data . "\n+++END+++");
587 }
588
589 $r = $this->parseRequestHeaders($data, $req_charset, $resp_charset, $resp_encoding);
590 if (!$r)
591 {
592 $r=$this->parseRequest($data, $req_charset);
593 }
594
595 // save full body of request into response, for more debugging usages
596 $r->raw_data = $raw_data;
597
598 if($this->debug > 2 && $GLOBALS['_xmlrpcs_occurred_errors'])
599 {
600 $this->debugmsg("+++PROCESSING ERRORS AND WARNINGS+++\n" .
601 $GLOBALS['_xmlrpcs_occurred_errors'] . "+++END+++");
602 }
603
604 $payload=$this->xml_header($resp_charset);
605 if($this->debug > 0)
606 {
607 $payload = $payload . $this->serializeDebug($resp_charset);
608 }
609
610 // G. Giunta 2006-01-27: do not create response serialization if it has
611 // already happened. Helps building json magic
612 if (empty($r->payload))
613 {
614 $r->serialize($resp_charset);
615 }
616 $payload = $payload . $r->payload;
617
618 if ($return_payload)
619 {
620 return $payload;
621 }
622
623 // if we get a warning/error that has output some text before here, then we cannot
624 // add a new header. We cannot say we are sending xml, either...
625 if(!headers_sent())
626 {
627 header('Content-Type: '.$r->content_type);
628 // we do not know if client actually told us an accepted charset, but if he did
629 // we have to tell him what we did
630 header("Vary: Accept-Charset");
631
632 // http compression of output: only
633 // if we can do it, and we want to do it, and client asked us to,
634 // and php ini settings do not force it already
635 $php_no_self_compress = !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler');
636 if($this->compress_response && function_exists('gzencode') && $resp_encoding != ''
637 && $php_no_self_compress)
638 {
639 if(strpos($resp_encoding, 'gzip') !== false)
640 {
641 $payload = gzencode($payload);
642 header("Content-Encoding: gzip");
643 header("Vary: Accept-Encoding");
644 }
645 elseif (strpos($resp_encoding, 'deflate') !== false)
646 {
647 $payload = gzcompress($payload);
648 header("Content-Encoding: deflate");
649 header("Vary: Accept-Encoding");
650 }
651 }
652
653 // do not ouput content-length header if php is compressing output for us:
654 // it will mess up measurements
655 if($php_no_self_compress)
656 {
657 header('Content-Length: ' . (int)strlen($payload));
658 }
659 }
660 else
661 {
662 error_log('XML-RPC: xmlrpc_server::service: http headers already sent before response is fully generated. Check for php warning or error messages');
663 }
664
665 print $payload;
666
667 // return request, in case subclasses want it
668 return $r;
669 }
670
671 /**
672 * Add a method to the dispatch map
673 * @param string $methodname the name with which the method will be made available
674 * @param string $function the php function that will get invoked
675 * @param array $sig the array of valid method signatures
676 * @param string $doc method documentation
677 * @param array $sigdoc the array of valid method signatures docs (one string per param, one for return type)
678 * @access public
679 */
680 function add_to_map($methodname,$function,$sig=null,$doc=false,$sigdoc=false)
681 {
682 $this->dmap[$methodname] = array(
683 'function' => $function,
684 'docstring' => $doc
685 );
686 if ($sig)
687 {
688 $this->dmap[$methodname]['signature'] = $sig;
689 }
690 if ($sigdoc)
691 {
692 $this->dmap[$methodname]['signature_docs'] = $sigdoc;
693 }
694 }
695
696 /**
697 * Verify type and number of parameters received against a list of known signatures
698 * @param array $in array of either xmlrpcval objects or xmlrpc type definitions
699 * @param array $sig array of known signatures to match against
700 * @access private
701 */
702 function verifySignature($in, $sig)
703 {
704 // check each possible signature in turn
705 if (is_object($in))
706 {
707 $numParams = $in->getNumParams();
708 }
709 else
710 {
711 $numParams = count($in);
712 }
713 foreach($sig as $cursig)
714 {
715 if(count($cursig)==$numParams+1)
716 {
717 $itsOK=1;
718 for($n=0; $n<$numParams; $n++)
719 {
720 if (is_object($in))
721 {
722 $p=$in->getParam($n);
723 if($p->kindOf() == 'scalar')
724 {
725 $pt=$p->scalartyp();
726 }
727 else
728 {
729 $pt=$p->kindOf();
730 }
731 }
732 else
733 {
734 $pt= $in[$n] == 'i4' ? 'int' : $in[$n]; // dispatch maps never use i4...
735 }
736
737 // param index is $n+1, as first member of sig is return type
738 if($pt != $cursig[$n+1] && $cursig[$n+1] != $GLOBALS['xmlrpcValue'])
739 {
740 $itsOK=0;
741 $pno=$n+1;
742 $wanted=$cursig[$n+1];
743 $got=$pt;
744 break;
745 }
746 }
747 if($itsOK)
748 {
749 return array(1,'');
750 }
751 }
752 }
753 if(isset($wanted))
754 {
755 return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
756 }
757 else
758 {
759 return array(0, "No method signature matches number of parameters");
760 }
761 }
762
763 /**
764 * Parse http headers received along with xmlrpc request. If needed, inflate request
765 * @return null on success or an xmlrpcresp
766 * @access private
767 */
768 function parseRequestHeaders(&$data, &$req_encoding, &$resp_encoding, &$resp_compression)
769 {
770 // Play nice to PHP 4.0.x: superglobals were not yet invented...
771 if(!isset($_SERVER))
772 {
773 $_SERVER = $GLOBALS['HTTP_SERVER_VARS'];
774 }
775
776 if($this->debug > 1)
777 {
778 if(function_exists('getallheaders'))
779 {
780 $this->debugmsg(''); // empty line
781 foreach(getallheaders() as $name => $val)
782 {
783 $this->debugmsg("HEADER: $name: $val");
784 }
785 }
786
787 }
788
789 if(isset($_SERVER['HTTP_CONTENT_ENCODING']))
790 {
791 $content_encoding = str_replace('x-', '', $_SERVER['HTTP_CONTENT_ENCODING']);
792 }
793 else
794 {
795 $content_encoding = '';
796 }
797
798 // check if request body has been compressed and decompress it
799 if($content_encoding != '' && strlen($data))
800 {
801 if($content_encoding == 'deflate' || $content_encoding == 'gzip')
802 {
803 // if decoding works, use it. else assume data wasn't gzencoded
804 if(function_exists('gzinflate') && in_array($content_encoding, $this->accepted_compression))
805 {
806 if($content_encoding == 'deflate' && $degzdata = @gzuncompress($data))
807 {
808 $data = $degzdata;
809 if($this->debug > 1)
810 {
811 $this->debugmsg("\n+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
812 }
813 }
814 elseif($content_encoding == 'gzip' && $degzdata = @gzinflate(substr($data, 10)))
815 {
816 $data = $degzdata;
817 if($this->debug > 1)
818 $this->debugmsg("+++INFLATED REQUEST+++[".strlen($data)." chars]+++\n" . $data . "\n+++END+++");
819 }
820 else
821 {
822 $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_decompress_fail'], $GLOBALS['xmlrpcstr']['server_decompress_fail']);
823 return $r;
824 }
825 }
826 else
827 {
828 //error_log('The server sent deflated data. Your php install must have the Zlib extension compiled in to support this.');
829 $r = new xmlrpcresp(0, $GLOBALS['xmlrpcerr']['server_cannot_decompress'], $GLOBALS['xmlrpcstr']['server_cannot_decompress']);
830 return $r;
831 }
832 }
833 }
834
835 // check if client specified accepted charsets, and if we know how to fulfill
836 // the request
837 if ($this->response_charset_encoding == 'auto')
838 {
839 $resp_encoding = '';
840 if (isset($_SERVER['HTTP_ACCEPT_CHARSET']))
841 {
842 // here we should check if we can match the client-requested encoding
843 // with the encodings we know we can generate.
844 /// @todo we should parse q=0.x preferences instead of getting first charset specified...
845 $client_accepted_charsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET']));
846 // Give preference to internal encoding
847 $known_charsets = array($GLOBALS['xmlrpc_internalencoding'], 'UTF-8', 'ISO-8859-1', 'US-ASCII');
848 foreach ($known_charsets as $charset)
849 {
850 foreach ($client_accepted_charsets as $accepted)
851 if (strpos($accepted, $charset) === 0)
852 {
853 $resp_encoding = $charset;
854 break;
855 }
856 if ($resp_encoding)
857 break;
858 }
859 }
860 }
861 else
862 {
863 $resp_encoding = $this->response_charset_encoding;
864 }
865
866 if (isset($_SERVER['HTTP_ACCEPT_ENCODING']))
867 {
868 $resp_compression = $_SERVER['HTTP_ACCEPT_ENCODING'];
869 }
870 else
871 {
872 $resp_compression = '';
873 }
874
875 // 'guestimate' request encoding
876 /// @todo check if mbstring is enabled and automagic input conversion is on: it might mingle with this check???
877 $req_encoding = guess_encoding(isset($_SERVER['CONTENT_TYPE']) ? $_SERVER['CONTENT_TYPE'] : '',
878 $data);
879
880 return null;
881 }
882
883 /**
884 * Parse an xml chunk containing an xmlrpc request and execute the corresponding
885 * php function registered with the server
886 * @param string $data the xml request
887 * @param string $req_encoding (optional) the charset encoding of the xml request
888 * @return xmlrpcresp
889 * @access private
890 */
891 function parseRequest($data, $req_encoding='')
892 {
893 // 2005/05/07 commented and moved into caller function code
894 //if($data=='')
895 //{
896 // $data=$GLOBALS['HTTP_RAW_POST_DATA'];
897 //}
898
899 // G. Giunta 2005/02/13: we do NOT expect to receive html entities
900 // so we do not try to convert them into xml character entities
901 //$data = xmlrpc_html_entity_xlate($data);
902
903 $GLOBALS['_xh']=array();
904 $GLOBALS['_xh']['ac']='';
905 $GLOBALS['_xh']['stack']=array();
906 $GLOBALS['_xh']['valuestack'] = array();
907 $GLOBALS['_xh']['params']=array();
908 $GLOBALS['_xh']['pt']=array();
909 $GLOBALS['_xh']['isf']=0;
910 $GLOBALS['_xh']['isf_reason']='';
911 $GLOBALS['_xh']['method']=false; // so we can check later if we got a methodname or not
912 $GLOBALS['_xh']['rt']='';
913
914 // decompose incoming XML into request structure
915 if ($req_encoding != '')
916 {
917 if (!in_array($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
918 // the following code might be better for mb_string enabled installs, but
919 // makes the lib about 200% slower...
920 //if (!is_valid_charset($req_encoding, array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
921 {
922 error_log('XML-RPC: xmlrpc_server::parseRequest: invalid charset encoding of received request: '.$req_encoding);
923 $req_encoding = $GLOBALS['xmlrpc_defencoding'];
924 }
925 /// @BUG this will fail on PHP 5 if charset is not specified in the xml prologue,
926 // the encoding is not UTF8 and there are non-ascii chars in the text...
927 /// @todo use an ampty string for php 5 ???
928 $parser = xml_parser_create($req_encoding);
929 }
930 else
931 {
932 $parser = xml_parser_create();
933 }
934
935 xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true);
936 // G. Giunta 2005/02/13: PHP internally uses ISO-8859-1, so we have to tell
937 // the xml parser to give us back data in the expected charset
938 // What if internal encoding is not in one of the 3 allowed?
939 // we use the broadest one, ie. utf8
940 // This allows to send data which is native in various charset,
941 // by extending xmlrpc_encode_entitites() and setting xmlrpc_internalencoding
942 if (!in_array($GLOBALS['xmlrpc_internalencoding'], array('UTF-8', 'ISO-8859-1', 'US-ASCII')))
943 {
944 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, 'UTF-8');
945 }
946 else
947 {
948 xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $GLOBALS['xmlrpc_internalencoding']);
949 }
950
951 if ($this->functions_parameters_type != 'xmlrpcvals')
952 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee_fast');
953 else
954 xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee');
955 xml_set_character_data_handler($parser, 'xmlrpc_cd');
956 xml_set_default_handler($parser, 'xmlrpc_dh');
957 if(!xml_parse($parser, $data, 1))
958 {
959 // return XML error as a faultCode
960 $r= new xmlrpcresp(0,
961 $GLOBALS['xmlrpcerrxml']+xml_get_error_code($parser),
962 sprintf('XML error: %s at line %d, column %d',
963 xml_error_string(xml_get_error_code($parser)),
964 xml_get_current_line_number($parser), xml_get_current_column_number($parser)));
965 xml_parser_free($parser);
966 }
967 elseif ($GLOBALS['_xh']['isf'])
968 {
969 xml_parser_free($parser);
970 $r= new xmlrpcresp(0,
971 $GLOBALS['xmlrpcerr']['invalid_request'],
972 $GLOBALS['xmlrpcstr']['invalid_request'] . ' ' . $GLOBALS['_xh']['isf_reason']);
973 }
974 else
975 {
976 xml_parser_free($parser);
977 if ($this->functions_parameters_type != 'xmlrpcvals')
978 {
979 if($this->debug > 1)
980 {
981 $this->debugmsg("\n+++PARSED+++\n".var_export($GLOBALS['_xh']['params'], true)."\n+++END+++");
982 }
983 $r = $this->execute($GLOBALS['_xh']['method'], $GLOBALS['_xh']['params'], $GLOBALS['_xh']['pt']);
984 }
985 else
986 {
987 // build an xmlrpcmsg object with data parsed from xml
988 $m= new xmlrpcmsg($GLOBALS['_xh']['method']);
989 // now add parameters in
990 for($i=0; $i<count($GLOBALS['_xh']['params']); $i++)
991 {
992 $m->addParam($GLOBALS['_xh']['params'][$i]);
993 }
994
995 if($this->debug > 1)
996 {
997 $this->debugmsg("\n+++PARSED+++\n".var_export($m, true)."\n+++END+++");
998 }
999 $r = $this->execute($m);
1000 }
1001 }
1002 return $r;
1003 }
1004
1005 /**
1006 * Execute a method invoked by the client, checking parameters used
1007 * @param mixed $m either an xmlrpcmsg obj or a method name
1008 * @param array $params array with method parameters as php types (if m is method name only)
1009 * @param array $paramtypes array with xmlrpc types of method parameters (if m is method name only)
1010 * @return xmlrpcresp
1011 * @access private
1012 */
1013 function execute($m, $params=null, $paramtypes=null)
1014 {
1015 if (is_object($m))
1016 {
1017 $methName = $m->method();
1018 }
1019 else
1020 {
1021 $methName = $m;
1022 }
1023 $sysCall = $this->allow_system_funcs && (strpos($methName, "system.") === 0);
1024 $dmap = $sysCall ? $GLOBALS['_xmlrpcs_dmap'] : $this->dmap;
1025
1026 if(!isset($dmap[$methName]['function']))
1027 {
1028 // No such method
1029 return new xmlrpcresp(0,
1030 $GLOBALS['xmlrpcerr']['unknown_method'],
1031 $GLOBALS['xmlrpcstr']['unknown_method']);
1032 }
1033
1034 // Check signature
1035 if(isset($dmap[$methName]['signature']))
1036 {
1037 $sig = $dmap[$methName]['signature'];
1038 if (is_object($m))
1039 {
1040 list($ok, $errstr) = $this->verifySignature($m, $sig);
1041 }
1042 else
1043 {
1044 list($ok, $errstr) = $this->verifySignature($paramtypes, $sig);
1045 }
1046 if(!$ok)
1047 {
1048 // Didn't match.
1049 return new xmlrpcresp(
1050 0,
1051 $GLOBALS['xmlrpcerr']['incorrect_params'],
1052 $GLOBALS['xmlrpcstr']['incorrect_params'] . ": ${errstr}"
1053 );
1054 }
1055 }
1056
1057 $func = $dmap[$methName]['function'];
1058 // let the 'class::function' syntax be accepted in dispatch maps
1059 if(is_string($func) && strpos($func, '::'))
1060 {
1061 $func = explode('::', $func);
1062 }
1063 // verify that function to be invoked is in fact callable
1064 if(!is_callable($func))
1065 {
1066 error_log("XML-RPC: xmlrpc_server::execute: function $func registered as method handler is not callable");
1067 return new xmlrpcresp(
1068 0,
1069 $GLOBALS['xmlrpcerr']['server_error'],
1070 $GLOBALS['xmlrpcstr']['server_error'] . ": no function matches method"
1071 );
1072 }
1073
1074 // If debug level is 3, we should catch all errors generated during
1075 // processing of user function, and log them as part of response
1076 if($this->debug > 2)
1077 {
1078 $GLOBALS['_xmlrpcs_prev_ehandler'] = set_error_handler('_xmlrpcs_errorHandler');
1079 }
1080 if (is_object($m))
1081 {
1082 if($sysCall)
1083 {
1084 $r = call_user_func($func, $this, $m);
1085 }
1086 else
1087 {
1088 $r = call_user_func($func, $m);
1089 }
1090 if (!is_a($r, 'xmlrpcresp'))
1091 {
1092 error_log("XML-RPC: xmlrpc_server::execute: function $func registered as method handler does not return an xmlrpcresp object");
1093 if (is_a($r, 'xmlrpcval'))
1094 {
1095 $r = new xmlrpcresp($r);
1096 }
1097 else
1098 {
1099 $r = new xmlrpcresp(
1100 0,
1101 $GLOBALS['xmlrpcerr']['server_error'],
1102 $GLOBALS['xmlrpcstr']['server_error'] . ": function does not return xmlrpcresp object"
1103 );
1104 }
1105 }
1106 }
1107 else
1108 {
1109 // call a 'plain php' function
1110 if($sysCall)
1111 {
1112 array_unshift($params, $this);
1113 $r = call_user_func_array($func, $params);
1114 }
1115 else
1116 {
1117 // 3rd API convention for method-handling functions: EPI-style
1118 if ($this->functions_parameters_type == 'epivals')
1119 {
1120 $r = call_user_func_array($func, array($methName, $params, $this->user_data));
1121 // mimic EPI behaviour: if we get an array that looks like an error, make it
1122 // an eror response
1123 if (is_array($r) && array_key_exists('faultCode', $r) && array_key_exists('faultString', $r))
1124 {
1125 $r = new xmlrpcresp(0, (integer)$r['faultCode'], (string)$r['faultString']);
1126 }
1127 else
1128 {
1129 // functions using EPI api should NOT return resp objects,
1130 // so make sure we encode the return type correctly
1131 $r = new xmlrpcresp(php_xmlrpc_encode($r, array('extension_api')));
1132 }
1133 }
1134 else
1135 {
1136 $r = call_user_func_array($func, $params);
1137 }
1138 }
1139 // the return type can be either an xmlrpcresp object or a plain php value...
1140 if (!is_a($r, 'xmlrpcresp'))
1141 {
1142 // what should we assume here about automatic encoding of datetimes
1143 // and php classes instances???
1144 $r = new xmlrpcresp(php_xmlrpc_encode($r, array('auto_dates')));
1145 }
1146 }
1147 if($this->debug > 2)
1148 {
1149 // note: restore the error handler we found before calling the
1150 // user func, even if it has been changed inside the func itself
1151 if($GLOBALS['_xmlrpcs_prev_ehandler'])
1152 {
1153 set_error_handler($GLOBALS['_xmlrpcs_prev_ehandler']);
1154 }
1155 else
1156 {
1157 restore_error_handler();
1158 }
1159 }
1160 return $r;
1161 }
1162
1163 /**
1164 * add a string to the 'internal debug message' (separate from 'user debug message')
1165 * @param string $strings
1166 * @access private
1167 */
1168 function debugmsg($string)
1169 {
1170 $this->debug_info .= $string."\n";
1171 }
1172
1173 /**
1174 * @access private
1175 */
1176 function xml_header($charset_encoding='')
1177 {
1178 if ($charset_encoding != '')
1179 {
1180 return "<?xml version=\"1.0\" encoding=\"$charset_encoding\"?" . ">\n";
1181 }
1182 else
1183 {
1184 return "<?xml version=\"1.0\"?" . ">\n";
1185 }
1186 }
1187
1188 /**
1189 * A debugging routine: just echoes back the input packet as a string value
1190 * DEPRECATED!
1191 */
1192 function echoInput()
1193 {
1194 $r= new xmlrpcresp(new xmlrpcval( "'Aha said I: '" . $GLOBALS['HTTP_RAW_POST_DATA'], 'string'));
1195 print $r->serialize();
1196 }
1197 }
1198?> \ No newline at end of file
diff --git a/web/profile.php b/web/profile.php
new file mode 100644
index 0000000..d4c1ca4
--- /dev/null
+++ b/web/profile.php
@@ -0,0 +1,691 @@
1<?php
2
3include("../config/os_modules_mysql.php");
4
5//
6// Search DB
7//
8mysql_connect ($DB_HOST, $DB_USER, $DB_PASSWORD);
9mysql_select_db ($DB_NAME);
10
11#
12# Copyright (c)Melanie Thielker (http://opensimulator.org/)
13#
14
15###################### No user serviceable parts below #####################
16
17$zeroUUID = "00000000-0000-0000-0000-000000000000";
18
19#
20# The XMLRPC server object
21#
22
23$xmlrpc_server = xmlrpc_server_create();
24
25#
26# Classifieds
27#
28
29# Avatar Classifieds Request
30
31xmlrpc_server_register_method($xmlrpc_server, "avatarclassifiedsrequest",
32 "avatarclassifiedsrequest");
33
34function avatarclassifiedsrequest($method_name, $params, $app_data)
35{
36 $req = $params[0];
37
38 $uuid = $req['uuid'];
39
40
41 $result = mysql_query("SELECT * FROM classifieds WHERE ".
42 "creatoruuid = '". mysql_real_escape_string($uuid) ."'");
43
44 $data = array();
45
46 while (($row = mysql_fetch_assoc($result)))
47 {
48 $data[] = array(
49 "classifiedid" => $row["classifieduuid"],
50 "name" => $row["name"]);
51 }
52
53 $response_xml = xmlrpc_encode(array(
54 'success' => True,
55 'data' => $data
56 ));
57
58 print $response_xml;
59}
60
61# Classifieds Update
62
63xmlrpc_server_register_method($xmlrpc_server, "classified_update",
64 "classified_update");
65
66function classified_update($method_name, $params, $app_data)
67{
68 global $zeroUUID;
69
70 $req = $params[0];
71
72 $classifieduuid = $req['classifiedUUID'];
73 $creator = $req['creatorUUID'];
74 $category = $req['category'];
75 $name = $req['name'];
76 $description = $req['description'];
77 $parceluuid = $req['parcelUUID'];
78 $parentestate = $req['parentestate'];
79 $snapshotuuid = $req['snapshotUUID'];
80 $simname = $req['sim_name'];
81 $parcelname = $req['parcelname'];
82 $globalpos = $req['globalpos'];
83 $classifiedflag = $req['classifiedFlags'];
84 $priceforlist = $req['classifiedPrice'];
85
86 // Check if we already have this one in the database
87 $check = mysql_query("SELECT COUNT(*) FROM classifieds WHERE ".
88 "classifieduuid = '". mysql_real_escape_string($classifieduuid) ."'");
89
90 while ($row = mysql_fetch_row($check))
91 {
92 $ready = $row[0];
93 }
94
95 // Doing some late checking
96 // Should be done by the module but let's see what happens when
97 // I do it here
98
99 if ($parcelname == "")
100 $parcelname = "Unknown";
101
102 if ($parceluuid == "")
103 $parceluuid = $zeroUUID;
104
105 if ($description == "")
106 $description = "No Description";
107
108 //If PG, Mature, and Adult flags are all 0 assume PG and set bit 2.
109 //This works around what might be a viewer bug regarding the flags.
110 //The ossearch query.php file expects bit 2 set for any PG listing.
111 if (($classifiedflag & 76) == 0)
112 $classifiedflag |= 4;
113
114 if ($ready == 0)
115 {
116 //Renew Weekly flag is 32 (1 << 5)
117 if (($classifiedflag & 32) == 0)
118 {
119 $creationdate = time();
120 $expirationdate = time() + (7 * 24 * 60 * 60);
121 }
122 else
123 {
124 $creationdate = time();
125 $expirationdate = time() + (52 * 7 * 24 * 60 * 60);
126 }
127
128 $sql = "INSERT INTO classifieds VALUES ".
129 "('". mysql_real_escape_string($classifieduuid) ."',".
130 "'". mysql_real_escape_string($creator) ."',".
131 "". mysql_real_escape_string($creationdate) .",".
132 "". mysql_real_escape_string($expirationdate) .",".
133 "'". mysql_real_escape_string($category) ."',".
134 "'". mysql_real_escape_string($name) ."',".
135 "'". mysql_real_escape_string($description) ."',".
136 "'". mysql_real_escape_string($parceluuid) ."',".
137 "". mysql_real_escape_string($parentestate) .",".
138 "'". mysql_real_escape_string($snapshotuuid) ."',".
139 "'". mysql_real_escape_string($simname) ."',".
140 "'". mysql_real_escape_string($globalpos) ."',".
141 "'". $parcelname ."',".
142 "". mysql_real_escape_string($classifiedflag) .",".
143 "". mysql_real_escape_string($priceforlist) .")";
144 }
145 else
146 {
147 $expirationdate = $creationdate + (52 * 7 * 24 * 60 * 60);
148
149 $sql = "UPDATE classifieds SET ".
150 "`creatoruuid`='". mysql_real_escape_string($creator)."',".
151 "`expirationdate`=". mysql_real_escape_string($expirationdate).",".
152 "`category`='". mysql_real_escape_string($category)."',".
153 "`name`='". mysql_real_escape_string($name)."',".
154 "`description`='". mysql_real_escape_string($description)."',".
155 "`parceluuid`='". mysql_real_escape_string($parceluuid)."',".
156 "`parentestate`=". mysql_real_escape_string($parentestate).",".
157 "`snapshotuuid`='". mysql_real_escape_string($snapshotuuid)."',".
158 "`simname`='". mysql_real_escape_string($simname)."',".
159 "`posglobal`='". mysql_real_escape_string($globalpos)."',".
160 "`parcelname`='". $parcelname."',".
161 "`classifiedflags`=". mysql_real_escape_string($classifiedflag).",".
162 "`priceforlisting`=". mysql_real_escape_string($priceforlist).
163 " WHERE ".
164 "`classifieduuid`='". mysql_real_escape_string($classifieduuid)."'";
165 }
166
167 // Create a new record for this classified
168 $result = mysql_query($sql);
169
170 $response_xml = xmlrpc_encode(array(
171 'success' => $result,
172 'errorMessage' => mysql_error()
173 ));
174
175 print $response_xml;
176}
177
178# Classifieds Delete
179
180xmlrpc_server_register_method($xmlrpc_server, "classified_delete",
181 "classified_delete");
182
183function classified_delete($method_name, $params, $app_data)
184{
185 $req = $params[0];
186
187 $classifieduuid = $req['classifiedID'];
188
189 $result = mysql_query("DELETE FROM classifieds WHERE ".
190 "classifieduuid = '".mysql_real_escape_string($classifieduuid) ."'");
191
192 $response_xml = xmlrpc_encode(array(
193 'success' => True,
194 'data' => $data
195 ));
196
197 print $response_xml;
198}
199
200#
201# Picks
202#
203
204# Avatar Picks Request
205
206xmlrpc_server_register_method($xmlrpc_server, "avatarpicksrequest",
207 "avatarpicksrequest");
208
209function avatarpicksrequest($method_name, $params, $app_data)
210{
211 $req = $params[0];
212
213 $uuid = $req['uuid'];
214
215 $data = array();
216
217 $result = mysql_query("SELECT `pickuuid`,`name` FROM userpicks WHERE ".
218 "creatoruuid = '". mysql_real_escape_string($uuid) ."'");
219
220 while (($row = mysql_fetch_assoc($result)))
221 {
222 $data[] = array(
223 "pickid" => $row["pickuuid"],
224 "name" => $row["name"]);
225 }
226
227 $response_xml = xmlrpc_encode(array(
228 'success' => True,
229 'data' => $data
230 ));
231
232 print $response_xml;
233}
234
235# Request Picks for User
236
237xmlrpc_server_register_method($xmlrpc_server, "pickinforequest",
238 "pickinforequest");
239
240function pickinforequest($method_name, $params, $app_data)
241{
242 $req = $params[0];
243
244 $uuid = $req['avatar_id'];
245 $pick = $req['pick_id'];
246
247 $data = array();
248
249 $result = mysql_query("SELECT * FROM userpicks WHERE ".
250 "creatoruuid = '". mysql_real_escape_string($uuid) ."' AND ".
251 "pickuuid = '". mysql_real_escape_string($pick) ."'");
252
253 $row = mysql_fetch_assoc($result);
254 if ($row != False)
255 {
256 if ($row["description"] == null || $row["description"] == "")
257 $row["description"] = "No description given";
258
259 $data[] = array(
260 "pickuuid" => $row["pickuuid"],
261 "creatoruuid" => $row["creatoruuid"],
262 "toppick" => $row["toppick"],
263 "parceluuid" => $row["parceluuid"],
264 "name" => $row["name"],
265 "description" => $row["description"],
266 "snapshotuuid" => $row["snapshotuuid"],
267 "user" => $row["user"],
268 "originalname" => $row["originalname"],
269 "simname" => $row["simname"],
270 "posglobal" => $row["posglobal"],
271 "sortorder"=> $row["sortorder"],
272 "enabled" => $row["enabled"]);
273 }
274
275 $response_xml = xmlrpc_encode(array(
276 'success' => True,
277 'data' => $data
278 ));
279
280 print $response_xml;
281}
282
283# Picks Update
284
285xmlrpc_server_register_method($xmlrpc_server, "picks_update",
286 "picks_update");
287
288function picks_update($method_name, $params, $app_data)
289{
290 global $zeroUUID;
291
292 $req = $params[0];
293
294 $pickuuid = $req['pick_id'];
295 $creator = $req['creator_id'];
296 $toppick = $req['top_pick'];
297 $name = $req['name'];
298 $description = $req['desc'];
299 $parceluuid = $req['parcel_uuid'];
300 $snapshotuuid = $req['snapshot_id'];
301 $user = $req['user'];
302 $simname = $req['sim_name'];
303 $posglobal = $req['pos_global'];
304 $sortorder = $req['sort_order'];
305 $enabled = $req['enabled'];
306
307 if ($parceluuid == "")
308 $parceluuid = $zeroUUID;
309
310 if ($description == "")
311 $description = "No Description";
312
313 // Check if we already have this one in the database
314 $check = mysql_query("SELECT COUNT(*) FROM userpicks WHERE ".
315 "pickuuid = '". mysql_real_escape_string($pickuuid) ."'");
316
317 $row = mysql_fetch_row($check);
318
319 if ($row[0] == 0)
320 {
321 if ($user == null || $user == "")
322 $user = "Unknown";
323
324 //The original parcel name is the same as the name of the
325 //profile pick when a new profile pick is being created.
326 $original = $name;
327
328 $query = "INSERT INTO userpicks VALUES ".
329 "('". mysql_real_escape_string($pickuuid) ."',".
330 "'". mysql_real_escape_string($creator) ."',".
331 "'". mysql_real_escape_string($toppick) ."',".
332 "'". mysql_real_escape_string($parceluuid) ."',".
333 "'". mysql_real_escape_string($name) ."',".
334 "'". mysql_real_escape_string($description) ."',".
335 "'". mysql_real_escape_string($snapshotuuid) ."',".
336 "'". mysql_real_escape_string($user) ."',".
337 "'". mysql_real_escape_string($original) ."',".
338 "'". mysql_real_escape_string($simname) ."',".
339 "'". mysql_real_escape_string($posglobal) ."',".
340 "'". mysql_real_escape_string($sortorder) ."',".
341 "'". mysql_real_escape_string($enabled) ."')";
342 }
343 else
344 {
345 $query = "UPDATE userpicks SET " .
346 "parceluuid = '". mysql_real_escape_string($parceluuid) . "', " .
347 "name = '". mysql_real_escape_string($name) . "', " .
348 "description = '". mysql_real_escape_string($description) . "', " .
349 "snapshotuuid = '". mysql_real_escape_string($snapshotuuid) . "' WHERE ".
350 "pickuuid = '". mysql_real_escape_string($pickuuid) ."'";
351 }
352
353 $result = mysql_query($query);
354 if ($result != False)
355 $result = True;
356
357 $response_xml = xmlrpc_encode(array(
358 'success' => $result,
359 'errorMessage' => mysql_error()
360 ));
361
362 print $response_xml;
363}
364
365# Picks Delete
366
367xmlrpc_server_register_method($xmlrpc_server, "picks_delete",
368 "picks_delete");
369
370function picks_delete($method_name, $params, $app_data)
371{
372 $req = $params[0];
373
374 $pickuuid = $req['pick_id'];
375
376 $result = mysql_query("DELETE FROM userpicks WHERE ".
377 "pickuuid = '".mysql_real_escape_string($pickuuid) ."'");
378
379 if ($result != False)
380 $result = True;
381
382 $response_xml = xmlrpc_encode(array(
383 'success' => $result,
384 'errorMessage' => mysql_error()
385 ));
386
387 print $response_xml;
388}
389
390#
391# Notes
392#
393
394# Avatar Notes Request
395
396
397xmlrpc_server_register_method($xmlrpc_server, "avatarnotesrequest",
398 "avatarnotesrequest");
399
400function avatarnotesrequest($method_name, $params, $app_data)
401{
402 $req = $params[0];
403
404 $uuid = $req['avatar_id'];
405 $targetuuid = $req['uuid'];
406
407 $result = mysql_query("SELECT notes FROM usernotes WHERE ".
408 "useruuid = '". mysql_real_escape_string($uuid) ."' AND ".
409 "targetuuid = '". mysql_real_escape_string($targetuuid) ."'");
410
411 $row = mysql_fetch_row($result);
412 if ($row == False)
413 $notes = "";
414 else
415 $notes = $row[0];
416
417 $data[] = array(
418 "targetid" => $targetuuid,
419 "notes" => $notes);
420
421 $response_xml = xmlrpc_encode(array(
422 'success' => True,
423 'data' => $data
424 ));
425
426 print $response_xml;
427}
428
429# Avatar Notes Update
430
431xmlrpc_server_register_method($xmlrpc_server, "avatar_notes_update",
432 "avatar_notes_update");
433
434function avatar_notes_update($method_name, $params, $app_data)
435{
436 $req = $params[0];
437
438 $uuid = $req['avatar_id'];
439 $targetuuid = $req['target_id'];
440 $notes = $req['notes'];
441
442 // Check if we already have this one in the database
443
444 $check = mysql_query("SELECT COUNT(*) FROM usernotes WHERE ".
445 "useruuid = '". mysql_real_escape_string($uuid) ."' AND ".
446 "targetuuid = '". mysql_real_escape_string($targetuuid) ."'");
447
448 $row = mysql_fetch_row($check);
449
450 if ($row[0] == 0)
451 {
452 // Create a new record for this avatar note
453 $result = mysql_query("INSERT INTO usernotes VALUES ".
454 "('". mysql_real_escape_string($uuid) ."',".
455 "'". mysql_real_escape_string($targetuuid) ."',".
456 "'". mysql_real_escape_string($notes) ."')");
457 }
458 else if ($notes == "")
459 {
460 // Delete the record for this avatar note
461 $result = mysql_query("DELETE FROM usernotes WHERE ".
462 "useruuid = '". mysql_real_escape_string($uuid) ."' AND ".
463 "targetuuid = '". mysql_real_escape_string($targetuuid) ."'");
464 }
465 else
466 {
467 // Update the existing record
468 $result = mysql_query("UPDATE usernotes SET ".
469 "notes = '". mysql_real_escape_string($notes) ."' WHERE ".
470 "useruuid = '". mysql_real_escape_string($uuid) ."' AND ".
471 "targetuuid = '". mysql_real_escape_string($targetuuid) ."'");
472 }
473
474 $response_xml = xmlrpc_encode(array(
475 'success' => True
476 ));
477
478 print $response_xml;
479}
480
481# Profile bits
482
483xmlrpc_server_register_method($xmlrpc_server, "avatar_properties_request",
484 "avatar_properties_request");
485
486function avatar_properties_request($method_name, $params, $app_data)
487{
488 global $zeroUUID;
489
490 $req = $params[0];
491
492 $uuid = $req['avatar_id'];
493
494 $result = mysql_query("SELECT * FROM userprofile WHERE ".
495 "useruuid = '". mysql_real_escape_string($uuid) ."'");
496 $row = mysql_fetch_assoc($result);
497
498 if ($row != False)
499 {
500 $data[] = array(
501 "ProfileUrl" => $row["profileURL"],
502 "Image" => $row["profileImage"],
503 "AboutText" => $row["profileAboutText"],
504 "FirstLifeImage" => $row["profileFirstImage"],
505 "FirstLifeAboutText" => $row["profileFirstText"],
506 "Partner" => $row["profilePartner"],
507
508 //Return interest data along with avatar properties
509 "wantmask" => $row["profileWantToMask"],
510 "wanttext" => $row["profileWantToText"],
511 "skillsmask" => $row["profileSkillsMask"],
512 "skillstext" => $row["profileSkillsText"],
513 "languages" => $row["profileLanguages"]);
514 }
515 else
516 {
517 //Insert empty record for avatar.
518 //FIXME: Should this only be done when asking for ones own profile?
519 $sql = "INSERT INTO userprofile VALUES ( ".
520 "'". mysql_real_escape_string($uuid) ."', ".
521 "'$zeroUUID', 0, 0, '', 0, '', 0, '', '', ".
522 "'$zeroUUID', '', '$zeroUUID', '')";
523 $result = mysql_query($sql);
524
525 $data[] = array(
526 "ProfileUrl" => "",
527 "Image" => $zeroUUID,
528 "AboutText" => "",
529 "FirstLifeImage" => $zeroUUID,
530 "FirstLifeAboutText" => "",
531 "Partner" => $zeroUUID,
532
533 "wantmask" => 0,
534 "wanttext" => "",
535 "skillsmask" => 0,
536 "skillstext" => "",
537 "languages" => "");
538 }
539
540 $response_xml = xmlrpc_encode(array(
541 'success' => True,
542 'data' => $data
543 ));
544
545 print $response_xml;
546}
547
548xmlrpc_server_register_method($xmlrpc_server, "avatar_properties_update",
549 "avatar_properties_update");
550
551function avatar_properties_update($method_name, $params, $app_data)
552{
553 $req = $params[0];
554
555 $uuid = $req['avatar_id'];
556 $profileURL = $req['ProfileUrl'];
557 $image = $req['Image'];
558 $abouttext = $req['AboutText'];
559 $firstlifeimage = $req['FirstLifeImage'];
560 $firstlifetext = $req['FirstLifeAboutText'];
561
562 $result=mysql_query("UPDATE userprofile SET ".
563 "profileURL='". mysql_real_escape_string($profileURL) ."', ".
564 "profileImage='". mysql_real_escape_string($image) ."', ".
565 "profileAboutText='". mysql_real_escape_string($abouttext) ."', ".
566 "profileFirstImage='". mysql_real_escape_string($firstlifeimage) ."', ".
567 "profileFirstText='". mysql_real_escape_string($firstlifetext) ."' ".
568 "WHERE useruuid='". mysql_real_escape_string($uuid) ."'"
569 );
570
571 $response_xml = xmlrpc_encode(array(
572 'success' => $result,
573 'errorMessage' => mysql_error()
574 ));
575
576 print $response_xml;
577}
578
579
580// Profile Interests
581
582xmlrpc_server_register_method($xmlrpc_server, "avatar_interests_update",
583 "avatar_interests_update");
584
585function avatar_interests_update($method_name, $params, $app_data)
586{
587 $req = $params[0];
588
589 $uuid = $req['avatar_id'];
590 $wanttext = $req['wanttext'];
591 $wantmask = $req['wantmask'];
592 $skillstext = $req['skillstext'];
593 $skillsmask = $req['skillsmask'];
594 $languages = $req['languages'];
595
596 $result = mysql_query("UPDATE userprofile SET ".
597 "profileWantToMask = ". mysql_real_escape_string($wantmask) .",".
598 "profileWantToText = '". mysql_real_escape_string($wanttext) ."',".
599 "profileSkillsMask = ". mysql_real_escape_string($skillsmask) .",".
600 "profileSkillsText = '". mysql_real_escape_string($skillstext) ."',".
601 "profileLanguages = '". mysql_real_escape_string($languages) ."' ".
602 "WHERE useruuid = '". mysql_real_escape_string($uuid) ."'"
603 );
604
605 $response_xml = xmlrpc_encode(array(
606 'success' => True
607 ));
608
609 print $response_xml;
610}
611
612// User Preferences
613
614xmlrpc_server_register_method($xmlrpc_server, "user_preferences_request",
615 "user_preferences_request");
616
617function user_preferences_request($method_name, $params, $app_data)
618{
619 $req = $params[0];
620
621 $uuid = $req['avatar_id'];
622
623 $result = mysql_query("SELECT imviaemail,visible,email FROM usersettings WHERE ".
624 "useruuid = '". mysql_real_escape_string($uuid) ."'");
625
626 $row = mysql_fetch_assoc($result);
627
628 if ($row != False)
629 {
630 $data[] = array(
631 "imviaemail" => $row["imviaemail"],
632 "visible" => $row["visible"],
633 "email" => $row["email"]);
634 }
635 else
636 {
637 //Insert empty record for avatar.
638 //NOTE: The 'false' values here are enums defined in database
639 $sql = "INSERT INTO usersettings VALUES ".
640 "('". mysql_real_escape_string($uuid) ."', ".
641 "'false', 'false', '')";
642 $result = mysql_query($sql);
643
644 $data[] = array(
645 "imviaemail" => False,
646 "visible" => False,
647 "email" => "");
648 }
649
650 $response_xml = xmlrpc_encode(array(
651 'success' => True,
652 'data' => $data
653 ));
654
655 print $response_xml;
656}
657
658xmlrpc_server_register_method($xmlrpc_server, "user_preferences_update",
659 "user_preferences_update");
660
661function user_preferences_update($method_name, $params, $app_data)
662{
663
664 $req = $params[0];
665
666 $uuid = $req['avatar_id'];
667 $wantim = $req['imViaEmail'];
668 $directory = $req['visible'];
669
670 $result = mysql_query("UPDATE usersettings SET ".
671 "imviaemail = '".mysql_real_escape_string($wantim) ."', ".
672 "visible = '".mysql_real_escape_string($directory) ."' WHERE ".
673 "useruuid = '". mysql_real_escape_string($uuid) ."'");
674
675 $response_xml = xmlrpc_encode(array(
676 'success' => True,
677 'data' => $data
678 ));
679
680 print $response_xml;
681}
682
683#
684# Process the request
685#
686
687$request_xml = file_get_contents("php://input");
688
689xmlrpc_server_call_method($xmlrpc_server, $request_xml, '');
690xmlrpc_server_destroy($xmlrpc_server);
691?>
diff --git a/web/query.php b/web/query.php
new file mode 100644
index 0000000..13b701b
--- /dev/null
+++ b/web/query.php
@@ -0,0 +1,593 @@
1<?php
2//The description of the flags used in this file are being based on the
3//DirFindFlags enum which is defined in OpenMetaverse/DirectoryManager.cs
4//of the libopenmetaverse library.
5
6include("../config/os_modules_mysql.php");
7
8$now = time();
9
10//
11// Search DB
12//
13mysql_connect ($DB_HOST, $DB_USER, $DB_PASSWORD);
14mysql_select_db ($DB_NAME);
15
16#
17# Copyright (c)Melanie Thielker (http://opensimulator.org/)
18#
19
20###################### No user serviceable parts below #####################
21
22//Join a series of terms together with optional parentheses around the result.
23//This function is used in place of the simpler join to handle the cases where
24//one or more of the supplied terms are an empty string. The parentheses can
25//be added when mixing AND and OR clauses in a SQL query.
26function join_terms($glue, $terms, $add_paren)
27{
28 if (count($terms) > 1)
29 {
30 $type = join($glue, $terms);
31 if ($add_paren == True)
32 $type = "(" . $type . ")";
33 }
34 else
35 {
36 if (count($terms) == 1)
37 $type = $terms[0];
38 else
39 $type = "";
40 }
41
42 return $type;
43}
44
45
46function process_region_type_flags($flags)
47{
48 $terms = array();
49
50 if ($flags & 16777216) //IncludePG (1 << 24)
51 $terms[] = "mature = 'PG'";
52 if ($flags & 33554432) //IncludeMature (1 << 25)
53 $terms[] = "mature = 'Mature'";
54 if ($flags & 67108864) //IncludeAdult (1 << 26)
55 $terms[] = "mature = 'Adult'";
56
57 return join_terms(" OR ", $terms, True);
58}
59
60
61#
62# The XMLRPC server object
63#
64
65$xmlrpc_server = xmlrpc_server_create();
66
67#
68# Places Query
69#
70
71xmlrpc_server_register_method($xmlrpc_server, "dir_places_query",
72 "dir_places_query");
73
74function dir_places_query($method_name, $params, $app_data)
75{
76 $req = $params[0];
77
78 $flags = $req['flags'];
79 $text = $req['text'];
80 $category = $req['category'];
81 $query_start = $req['query_start'];
82
83 $pieces = split(" ", $text);
84 $text = join("%", $pieces);
85
86 if ($text == "%%%")
87 {
88 $response_xml = xmlrpc_encode(array(
89 'success' => False,
90 'errorMessage' => "Invalid search terms"
91 ));
92
93 print $response_xml;
94
95 return;
96 }
97
98 $terms = array();
99
100 $type = process_region_type_flags($flags);
101 if ($type != "")
102 $type = " AND " . $type;
103
104 if ($flags & 1024)
105 $order = "dwell DESC,";
106
107 if ($category > 0)
108 $category = "searchcategory = '".mysql_real_escape_string($category)."' AND ";
109 else
110 $category = "";
111
112 $text = mysql_real_escape_string($text);
113 $result = mysql_query("SELECT * FROM parcels WHERE $category " .
114 "(parcelname LIKE '%$text%'" .
115 " OR description LIKE '%$text%')" .
116 $type . " ORDER BY $order parcelname" .
117 " LIMIT ".(0+$query_start).",101");
118
119 $data = array();
120 while (($row = mysql_fetch_assoc($result)))
121 {
122 $data[] = array(
123 "parcel_id" => $row["infouuid"],
124 "name" => $row["parcelname"],
125 "for_sale" => "False",
126 "auction" => "False",
127 "dwell" => $row["dwell"]);
128 }
129 $response_xml = xmlrpc_encode(array(
130 'success' => True,
131 'errorMessage' => "",
132 'data' => $data
133 ));
134
135 print $response_xml;
136}
137
138#
139# Popular Places Query
140#
141
142xmlrpc_server_register_method($xmlrpc_server, "dir_popular_query",
143 "dir_popular_query");
144
145function dir_popular_query($method_name, $params, $app_data)
146{
147 $req = $params[0];
148
149 $text = $req['text'];
150 $flags = $req['flags'];
151 $query_start = $req['query_start'];
152
153 $terms = array();
154
155 if ($flags & 0x1000) //PicturesOnly (1 << 12)
156 $terms[] = "has_picture = 1";
157
158 if ($flags & 0x0800) //PgSimsOnly (1 << 11)
159 $terms[] = "mature = 0";
160
161 if ($text != "")
162 {
163 $text = mysql_real_escape_string($text);
164 $terms[] = "(name LIKE '%$text%')";
165 }
166
167 if (count($terms) > 0)
168 $where = " WHERE " . join_terms(" AND ", $terms, False);
169 else
170 $where = "";
171
172 $result = mysql_query("SELECT * FROM popularplaces" . $where .
173 " LIMIT " . mysql_real_escape_string($query_start) . ",101");
174
175 $data = array();
176 while (($row = mysql_fetch_assoc($result)))
177 {
178 $data[] = array(
179 "parcel_id" => $row["infoUUID"],
180 "name" => $row["name"],
181 "dwell" => $row["dwell"]);
182 }
183
184 $response_xml = xmlrpc_encode(array(
185 'success' => True,
186 'errorMessage' => "",
187 'data' => $data));
188
189 print $response_xml;
190}
191
192#
193# Land Query
194#
195
196xmlrpc_server_register_method($xmlrpc_server, "dir_land_query",
197 "dir_land_query");
198
199function dir_land_query($method_name, $params, $app_data)
200{
201 $req = $params[0];
202
203 $flags = $req['flags'];
204 $type = $req['type'];
205 $price = $req['price'];
206 $area = $req['area'];
207 $query_start = $req['query_start'];
208
209 $terms = array();
210
211 if ($type != 4294967295) //Include all types of land?
212 {
213 //Do this check first so we can bail out quickly on Auction search
214 if (($type & 26) == 2) // Auction (from SearchTypeFlags enum)
215 {
216 $response_xml = xmlrpc_encode(array(
217 'success' => False,
218 'errorMessage' => "No auctions listed"));
219
220 print $response_xml;
221
222 return;
223 }
224
225 if (($type & 24) == 8) //Mainland (24=0x18 [bits 3 & 4])
226 $terms[] = "parentestate = 1";
227 if (($type & 24) == 16) //Estate (24=0x18 [bits 3 & 4])
228 $terms[] = "parentestate <> 1";
229 }
230
231 $s = process_region_type_flags($flags);
232 if ($s != "")
233 $terms[] = $s;
234
235 if ($flags & 0x100000) //LimitByPrice (1 << 20)
236 $terms[] = "saleprice <= '" . mysql_real_escape_string($price) . "'";
237 if ($flags & 0x200000) //LimitByArea (1 << 21)
238 $terms[] = "area >= '" . mysql_real_escape_string($area) . "'";
239
240 //The PerMeterSort flag is always passed from a map item query.
241 //It doesn't hurt to have this as the default search order.
242 $order = "lsq"; //PerMeterSort (1 << 17)
243
244 if ($flags & 0x80000) //NameSort (1 << 19)
245 $order = "parcelname";
246 if ($flags & 0x10000) //PriceSort (1 << 16)
247 $order = "saleprice";
248 if ($flags & 0x40000) //AreaSort (1 << 18)
249 $order = "area";
250 if (!($flags & 0x8000)) //SortAsc (1 << 15)
251 $order .= " DESC";
252
253 if (count($terms) > 0)
254 $where = " WHERE " . join_terms(" AND ", $terms, False);
255 else
256 $where = "";
257
258 $sql = "SELECT *, saleprice/area AS lsq FROM parcelsales" . $where .
259 " ORDER BY " . $order . " LIMIT " .
260 mysql_real_escape_string($query_start) . ",101";
261
262 $result = mysql_query($sql);
263
264 $data = array();
265 while (($row = mysql_fetch_assoc($result)))
266 {
267 $data[] = array(
268 "parcel_id" => $row["infoUUID"],
269 "name" => $row["parcelname"],
270 "auction" => "false",
271 "for_sale" => "true",
272 "sale_price" => $row["saleprice"],
273 "landing_point" => $row["landingpoint"],
274 "region_UUID" => $row["regionUUID"],
275 "area" => $row["area"]);
276 }
277
278 $response_xml = xmlrpc_encode(array(
279 'success' => True,
280 'errorMessage' => "",
281 'data' => $data));
282
283 print $response_xml;
284}
285
286#
287# Events Query
288#
289
290xmlrpc_server_register_method($xmlrpc_server, "dir_events_query",
291 "dir_events_query");
292
293function dir_events_query($method_name, $params, $app_data)
294{
295 $req = $params[0];
296
297 $text = $req['text'];
298 $flags = $req['flags'];
299 $query_start = $req['query_start'];
300
301 if ($text == "%%%")
302 {
303 $response_xml = xmlrpc_encode(array(
304 'success' => False,
305 'errorMessage' => "Invalid search terms"
306 ));
307
308 print $response_xml;
309
310 return;
311 }
312
313 $pieces = explode("|", $text);
314
315 $day = $pieces[0];
316 $category = $pieces[1];
317 if (count($pieces) < 3)
318 $search_text = "";
319 else
320 $search_text = $pieces[2];
321
322 //Get todays date/time and adjust it to UTC
323 $now = time() - date_offset_get(new DateTime);
324
325 $terms = array();
326
327 if ($day == "u")
328 $terms[] = "dateUTC > ".$now;
329 else
330 {
331 //Is $day a number of days before or after current date?
332 if ($day != 0)
333 $now += $day * 86400;
334 $now -= ($now % 86400);
335 $then = $now + 86400;
336 $terms[] = "(dateUTC > ".$now." AND dateUTC <= ".$then.")";
337 }
338
339 if ($category != 0)
340 $terms[] = "category = ".$category."";
341
342 $type = array();
343 if ($flags & 16777216) //IncludePG (1 << 24)
344 $type[] = "eventflags = 0";
345 if ($flags & 33554432) //IncludeMature (1 << 25)
346 $type[] = "eventflags = 1";
347 if ($flags & 67108864) //IncludeAdult (1 << 26)
348 $type[] = "eventflags = 2";
349
350 //Was there at least one PG, Mature, or Adult flag?
351 if (count($type) > 0)
352 $terms[] = join_terms(" OR ", $type, True);
353
354 if ($search_text != "")
355 {
356 $search_text = mysql_real_escape_string($search_text);
357 $terms[] = "(name LIKE '%$search_text%' OR " .
358 "description LIKE '%$search_text%')";
359 }
360
361 if (count($terms) > 0)
362 $where = " WHERE " . join_terms(" AND ", $terms, False);
363 else
364 $where = "";
365
366 $sql = "SELECT * FROM events". $where.
367 " LIMIT " . mysql_real_escape_string($query_start) . ",101";
368
369 $result = mysql_query($sql);
370
371 $data = array();
372
373 while (($row = mysql_fetch_assoc($result)))
374 {
375 $date = strftime("%m/%d %I:%M %p",$row["dateUTC"]);
376
377 $data[] = array(
378 "owner_id" => $row["owneruuid"],
379 "name" => $row["name"],
380 "event_id" => $row["eventid"],
381 "date" => $date,
382 "unix_time" => $row["dateUTC"],
383 "event_flags" => $row["eventflags"],
384 "landing_point" => $row["globalPos"],
385 "region_UUID" => $row["simname"]);
386 }
387
388 $response_xml = xmlrpc_encode(array(
389 'success' => True,
390 'errorMessage' => "",
391 'data' => $data));
392
393 print $response_xml;
394}
395
396#
397# Classifieds Query
398#
399
400xmlrpc_server_register_method($xmlrpc_server, "dir_classified_query",
401 "dir_classified_query");
402
403function dir_classified_query ($method_name, $params, $app_data)
404{
405 $req = $params[0];
406
407 $text = $req['text'];
408 $flags = $req['flags'];
409 $category = $req['category'];
410 $query_start = $req['query_start'];
411
412 if ($text == "%%%")
413 {
414 $response_xml = xmlrpc_encode(array(
415 'success' => False,
416 'errorMessage' => "Invalid search terms"
417 ));
418
419 print $response_xml;
420
421 return;
422 }
423
424 $terms = array();
425
426 //Renew Weekly flag is bit 5 (32) in $flags.
427 $f = array();
428 if ($flags & 4) //PG (1 << 2)
429 $f[] = "classifiedflags & 4 = 4";
430 if ($flags & 8) //Mature (1 << 3)
431 $f[] = "classifiedflags & 8 = 8";
432 if ($flags & 64) //Adult (1 << 6)
433 $f[] = "classifiedflags & 64 = 64";
434
435 //Was there at least one PG, Mature, or Adult flag?
436 if (count($f) > 0)
437 $terms[] = join_terms(" OR ", $f, True);
438
439 //Only restrict results based on category if it is not 0 (Any Category)
440 if ($category != 0)
441 $terms[] = "category = " . $category;
442
443 if ($text != "")
444 $terms[] = "(name LIKE '%$text%'" .
445 " OR description LIKE '%$text%')";
446
447 //Was there at least condition for the search?
448 if (count($terms) > 0)
449 $where = " WHERE " . join_terms(" AND ", $terms, False);
450 else
451 $where = "";
452
453 $sql = "SELECT * FROM classifieds" . $where .
454 " ORDER BY priceforlisting DESC" .
455 " LIMIT " . mysql_real_escape_string($query_start) . ",101";
456
457 $result = mysql_query($sql);
458
459 $data = array();
460 while (($row = mysql_fetch_assoc($result)))
461 {
462 $data[] = array(
463 "classifiedid" => $row["classifieduuid"],
464 "name" => $row["name"],
465 "classifiedflags" => $row["classifiedflags"],
466 "creation_date" => $row["creationdate"],
467 "expiration_date" => $row["expirationdate"],
468 "priceforlisting" => $row["priceforlisting"]);
469 }
470
471 $response_xml = xmlrpc_encode(array(
472 'success' => True,
473 'errorMessage' => "",
474 'data' => $data));
475
476 print $response_xml;
477}
478
479#
480# Events Info Query
481#
482
483xmlrpc_server_register_method($xmlrpc_server, "event_info_query",
484 "event_info_query");
485
486function event_info_query($method_name, $params, $app_data)
487{
488 $req = $params[0];
489
490 $eventID = $req['eventID'];
491
492 $sql = "SELECT * FROM events WHERE eventID = " .
493 mysql_real_escape_string($eventID);
494
495 $result = mysql_query($sql);
496
497 $data = array();
498 while (($row = mysql_fetch_assoc($result)))
499 {
500 $date = strftime("%G-%m-%d %H:%M:%S",$row["dateUTC"]);
501
502 $category = "*Unspecified*";
503 if ($row['category'] == 18) $category = "Discussion";
504 if ($row['category'] == 19) $category = "Sports";
505 if ($row['category'] == 20) $category = "Live Music";
506 if ($row['category'] == 22) $category = "Commercial";
507 if ($row['category'] == 23) $category = "Nightlife/Entertainment";
508 if ($row['category'] == 24) $category = "Games/Contests";
509 if ($row['category'] == 25) $category = "Pageants";
510 if ($row['category'] == 26) $category = "Education";
511 if ($row['category'] == 27) $category = "Arts and Culture";
512 if ($row['category'] == 28) $category = "Charity/Support Groups";
513 if ($row['category'] == 29) $category = "Miscellaneous";
514
515 $data[] = array(
516 "event_id" => $row["eventid"],
517 "creator" => $row["creatoruuid"],
518 "name" => $row["name"],
519 "category" => $category,
520 "description" => $row["description"],
521 "date" => $date,
522 "dateUTC" => $row["dateUTC"],
523 "duration" => $row["duration"],
524 "covercharge" => $row["covercharge"],
525 "coveramount" => $row["coveramount"],
526 "simname" => $row["simname"],
527 "globalposition" => $row["globalPos"],
528 "eventflags" => $row["eventflags"]);
529 }
530
531 $response_xml = xmlrpc_encode(array(
532 'success' => True,
533 'errorMessage' => "",
534 'data' => $data));
535
536 print $response_xml;
537}
538
539#
540# Classifieds Info Query
541#
542
543xmlrpc_server_register_method($xmlrpc_server, "classifieds_info_query",
544 "classifieds_info_query");
545
546function classifieds_info_query($method_name, $params, $app_data)
547{
548 $req = $params[0];
549
550 $classifiedID = $req['classifiedID'];
551
552 $sql = "SELECT * FROM classifieds WHERE classifieduuid = '" .
553 mysql_real_escape_string($classifiedID). "'";
554
555 $result = mysql_query($sql);
556
557 $data = array();
558 while (($row = mysql_fetch_assoc($result)))
559 {
560 $data[] = array(
561 "classifieduuid" => $row["classifieduuid"],
562 "creatoruuid" => $row["creatoruuid"],
563 "creationdate" => $row["creationdate"],
564 "expirationdate" => $row["expirationdate"],
565 "category" => $row["category"],
566 "name" => $row["name"],
567 "description" => $row["description"],
568 "parceluuid" => $row["parceluuid"],
569 "parentestate" => $row["parentestate"],
570 "snapshotuuid" => $row["snapshotuuid"],
571 "simname" => $row["simname"],
572 "posglobal" => $row["posglobal"],
573 "parcelname" => $row["parcelname"],
574 "classifiedflags" => $row["classifiedflags"],
575 "priceforlisting" => $row["priceforlisting"]);
576 }
577
578 $response_xml = xmlrpc_encode(array(
579 'success' => True,
580 'errorMessage' => "",
581 'data' => $data));
582
583 print $response_xml;
584}
585
586#
587# Process the request
588#
589
590$request_xml = file_get_contents("php://input");
591xmlrpc_server_call_method($xmlrpc_server, $request_xml, '');
592xmlrpc_server_destroy($xmlrpc_server);
593?>
diff --git a/web/register.php b/web/register.php
new file mode 100644
index 0000000..70f42a6
--- /dev/null
+++ b/web/register.php
@@ -0,0 +1,61 @@
1<?php
2//////////////////////////////////////////////////////////////////////////////
3// register.php //
4// (C) 2008, Fly-man- //
5// This file contains the registration of a simulator to the database //
6// and checks if the simulator is new in the database or a reconnected one //
7// //
8// If the simulator is old, check if the nextcheck date > registration //
9// When the date is older, make a request to the Parser to grab new data //
10//////////////////////////////////////////////////////////////////////////////
11
12include("../config/os_modules_mysql.php");
13//establish connection to master db server
14mysql_connect ($DB_HOST, $DB_USER, $DB_PASSWORD);
15mysql_select_db ($DB_NAME);
16
17$hostname = $_GET['host'];
18$port = $_GET['port'];
19$service = $_GET['service'];
20
21if ($hostname != "" && $port != "" && $service == "online")
22{
23 // Check if there is already a database row for this host
24 $checkhost = mysql_query("SELECT register FROM hostsregister WHERE " .
25 "host = '" . mysql_real_escape_string($hostname) . "' AND " .
26 "port = '" . mysql_real_escape_string($port) . "'");
27
28 // Get the request time as a timestamp for later
29 $timestamp = $_SERVER['REQUEST_TIME'];
30
31 // if greater than 1, check the nextcheck date
32 if (mysql_num_rows($checkhost) > 0)
33 {
34 $update = "UPDATE hostsregister SET " .
35 "register = '" . mysql_real_escape_string($timestamp) . "', " .
36 "nextcheck = '0', checked = '0', " .
37 "failcounter = '0' " .
38 "WHERE host = '" . mysql_real_escape_string($hostname) . "' AND " .
39 "port = '" . mysql_real_escape_string($port) . "'";
40
41 $runupdate = mysql_query($update);
42 }
43 else
44 {
45 $register = "INSERT INTO hostsregister VALUES ".
46 "('" . mysql_real_escape_string($hostname) . "', " .
47 "'" . mysql_real_escape_string($port) . "', " .
48 "'" . mysql_real_escape_string($timestamp) . "', 0, 0, 0)";
49
50 $runupdate = mysql_query($register);
51 }
52}
53elseif ($hostname != "" && $port != "" && $service = "offline")
54{
55 $delete = "DELETE FROM hostsregister " .
56 "WHERE host = '" . mysql_real_escape_string($hostname) . "' AND " .
57 "port = '" . mysql_real_escape_string($port) . "'";
58
59 $rundelete = mysql_query($delete);
60}
61?>
diff --git a/web/xmlrpc.php b/web/xmlrpc.php
new file mode 100644
index 0000000..fc0c575
--- /dev/null
+++ b/web/xmlrpc.php
@@ -0,0 +1,1755 @@
1<?php
2// ini_set("display_errors",0);
3 /*
4 Actual failures that result in mysql or php errors should be returned as:
5
6 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
7
8 Methods that run without errors, but do not have the intended result should return as:
9
10 return array('succeed' => 'false', 'message' => 'No Groups Found', 'params' => var_export($params, TRUE));
11
12 or if applicable:
13
14 return array('succeed' => 'false', 'message' => 'What went wrong', 'params' => var_export($params, TRUE), 'sql' => $sql);
15 */
16
17 include("phpxmlrpclib/xmlrpc.inc");
18 include("phpxmlrpclib/xmlrpcs.inc");
19
20 include("../config/os_flotsam_config.php");
21 include("../config/os_modules_mysql.php");
22
23
24 $groupPowers = array(
25 'None' => '0',
26 /// <summary>Can send invitations to groups default role</summary>
27 'Invite' => '2',
28 /// <summary>Can eject members from group</summary>
29 'Eject' => '4',
30 /// <summary>Can toggle 'Open Enrollment' and change 'Signup fee'</summary>
31 'ChangeOptions' => '8',
32 /// <summary>Can create new roles</summary>
33 'CreateRole' => '16',
34 /// <summary>Can delete existing roles</summary>
35 'DeleteRole' => '32',
36 /// <summary>Can change Role names, titles and descriptions</summary>
37 'RoleProperties' => '64',
38 /// <summary>Can assign other members to assigners role</summary>
39 'AssignMemberLimited' => '128',
40 /// <summary>Can assign other members to any role</summary>
41 'AssignMember' => '256',
42 /// <summary>Can remove members from roles</summary>
43 'RemoveMember' => '512',
44 /// <summary>Can assign and remove abilities in roles</summary>
45 'ChangeActions' => '1024',
46 /// <summary>Can change group Charter, Insignia, 'Publish on the web' and which
47 /// members are publicly visible in group member listings</summary>
48 'ChangeIdentity' => '2048',
49 /// <summary>Can buy land or deed land to group</summary>
50 'LandDeed' => '4096',
51 /// <summary>Can abandon group owned land to Governor Linden on mainland, or Estate owner for
52 /// private estates</summary>
53 'LandRelease' => '8192',
54 /// <summary>Can set land for-sale information on group owned parcels</summary>
55 'LandSetSale' => '16384',
56 /// <summary>Can subdivide and join parcels</summary>
57 'LandDivideJoin' => '32768',
58 /// <summary>Can join group chat sessions</summary>
59 'JoinChat' => '65536',
60 /// <summary>Can toggle "Show in Find Places" and set search category</summary>
61 'FindPlaces' => '131072',
62 /// <summary>Can change parcel name, description, and 'Publish on web' settings</summary>
63 'LandChangeIdentity' => '262144',
64 /// <summary>Can set the landing point and teleport routing on group land</summary>
65 'SetLandingPoint' => '524288',
66 /// <summary>Can change music and media settings</summary>
67 'ChangeMedia' => '1048576',
68 /// <summary>Can toggle 'Edit Terrain' option in Land settings</summary>
69 'LandEdit' => '2097152',
70 /// <summary>Can toggle various About Land > Options settings</summary>
71 'LandOptions' => '4194304',
72 /// <summary>Can always terraform land, even if parcel settings have it turned off</summary>
73 'AllowEditLand' => '8388608',
74 /// <summary>Can always fly while over group owned land</summary>
75 'AllowFly' => '16777216',
76 /// <summary>Can always rez objects on group owned land</summary>
77 'AllowRez' => '33554432',
78 /// <summary>Can always create landmarks for group owned parcels</summary>
79 'AllowLandmark' => '67108864',
80 /// <summary>Can use voice chat in Group Chat sessions</summary>
81 'AllowVoiceChat' => '134217728',
82 /// <summary>Can set home location on any group owned parcel</summary>
83 'AllowSetHome' => '268435456',
84 /// <summary>Can modify public access settings for group owned parcels</summary>
85 'LandManageAllowed' => '536870912',
86 /// <summary>Can manager parcel ban lists on group owned land</summary>
87 'LandManageBanned' => '1073741824',
88 /// <summary>Can manage pass list sales information</summary>
89 'LandManagePasses' => '2147483648',
90 /// <summary>Can eject and freeze other avatars on group owned land</summary>
91 'LandEjectAndFreeze' => '4294967296',
92 /// <summary>Can return objects set to group</summary>
93 'ReturnGroupSet' => '8589934592',
94 /// <summary>Can return non-group owned/set objects</summary>
95 'ReturnNonGroup' => '17179869184',
96 /// <summary>Can landscape using Linden plants</summary>
97 'LandGardening' => '34359738368',
98 /// <summary>Can deed objects to group</summary>
99 'DeedObject' => '68719476736',
100 /// <summary>Can moderate group chat sessions</summary>
101 'ModerateChat' => '137438953472',
102 /// <summary>Can move group owned objects</summary>
103 'ObjectManipulate' => '274877906944',
104 /// <summary>Can set group owned objects for-sale</summary>
105 'ObjectSetForSale' => '549755813888',
106 /// <summary>Pay group liabilities and receive group dividends</summary>
107 'Accountable' => '1099511627776',
108 /// <summary>Can send group notices</summary>
109 'SendNotices' => '4398046511104',
110 /// <summary>Can receive group notices</summary>
111 'ReceiveNotices' => '8796093022208',
112 /// <summary>Can create group proposals</summary>
113 'StartProposal' => '17592186044416',
114 /// <summary>Can vote on group proposals</summary>
115 'VoteOnProposal' => '35184372088832',
116 /// <summary>Can return group owned objects</summary>
117 'ReturnGroupOwned' => '281474976710656',
118 /// <summary>Members are visible to non-owners</summary>
119 'RoleMembersVisible' => '140737488355328'
120 );
121
122 $uuidZero = "00000000-0000-0000-0000-000000000000";
123
124 $groupDBCon = mysql_connect($DB_HOST,$DB_USER,$DB_PASSWORD);
125 if (!$groupDBCon)
126 {
127 die('Could not connect: ' . mysql_error());
128 }
129 mysql_select_db($DB_NAME, $groupDBCon);
130
131 // This is filled in by secure()
132 $requestingAgent = $uuidZero;
133
134 function test()
135 {
136 return array('name' => 'Joe','age' => 27);
137 }
138
139 // Use a common signature for all the group functions -> struct foo($struct)
140 $common_sig = array(array($xmlrpcStruct, $xmlrpcStruct));
141
142 function createGroup($params)
143 {
144 if( is_array($error = secureRequest($params, TRUE)) )
145 {
146 return $error;
147 }
148
149 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
150
151 $groupID = $params["GroupID"];
152 $name = $params["Name"];
153 $charter = $params["Charter"];
154 $insigniaID = $params["InsigniaID"];
155 $founderID = $params["FounderID"];
156 $membershipFee = $params["MembershipFee"];
157 $openEnrollment = $params["OpenEnrollment"];
158 $showInList = $params["ShowInList"];
159 $allowPublish = $params["AllowPublish"];
160 $maturePublish = $params["MaturePublish"];
161 $ownerRoleID = $params["OwnerRoleID"];
162 $everyonePowers = $params["EveryonePowers"];
163 $ownersPowers = $params["OwnersPowers"];
164
165 $escapedParams = array_map("mysql_real_escape_string", $params);
166 $escapedGroupID = $escapedParams["GroupID"];
167 $escapedName = $escapedParams["Name"];
168 $escapedCharter = $escapedParams["Charter"];
169 $escapedInsigniaID = $escapedParams["InsigniaID"];
170 $escapedFounderID = $escapedParams["FounderID"];
171 $escapedMembershipFee = $escapedParams["MembershipFee"];
172 $escapedOpenEnrollment = $escapedParams["OpenEnrollment"];
173 $escapedShowInList = $escapedParams["ShowInList"];
174 $escapedAllowPublish = $escapedParams["AllowPublish"];
175 $escapedMaturePublish = $escapedParams["MaturePublish"];
176 $escapedOwnerRoleID = $escapedParams["OwnerRoleID"];
177
178 // Create group
179 $sql = "INSERT INTO osgroup
180 (GroupID, Name, Charter, InsigniaID, FounderID, MembershipFee, OpenEnrollment, ShowInList, AllowPublish, MaturePublish, OwnerRoleID)
181 VALUES
182 ('$escapedGroupID', '$escapedName', '$escapedCharter', '$escapedInsigniaID', '$escapedFounderID', $escapedMembershipFee, $escapedOpenEnrollment, $escapedShowInList, $escapedAllowPublish, $escapedMaturePublish, '$escapedOwnerRoleID')";
183
184 if (!mysql_query($sql, $groupDBCon))
185 {
186 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
187 }
188
189 // Create Everyone Role
190 // NOTE: FIXME: This is a temp fix until the libomv enum for group powers is fixed in OpenSim
191
192 $result = _addRoleToGroup(array('GroupID' => $groupID, 'RoleID' => $uuidZero, 'Name' => 'Everyone', 'Description' => 'Everyone in the group is in the everyone role.', 'Title' => "Member of $name", 'Powers' => $everyonePowers));
193 if( isset($result['error']) )
194 {
195 return $result;
196 }
197
198 // Create Owner Role
199 $result = _addRoleToGroup(array('GroupID' => $groupID, 'RoleID' => $ownerRoleID, 'Name' => 'Owners', 'Description' => "Owners of $name", 'Title' => "Owner of $name", 'Powers' => $ownersPowers));
200 if( isset($result['error']) )
201 {
202 return $result;
203 }
204
205 // Add founder to group, will automatically place them in the Everyone Role, also places them in specified Owner Role
206 $result = _addAgentToGroup(array('AgentID' => $founderID, 'GroupID' => $groupID, 'RoleID' => $ownerRoleID));
207 if( isset($result['error']) )
208 {
209 return $result;
210 }
211
212 // Select the owner's role for the founder
213 $result = _setAgentGroupSelectedRole(array('AgentID' => $founderID, 'RoleID' => $ownerRoleID, 'GroupID' => $groupID));
214 if( isset($result['error']) )
215 {
216 return $result;
217 }
218
219 // Set the new group as the founder's active group
220 $result = _setAgentActiveGroup(array('AgentID' => $founderID, 'GroupID' => $groupID));
221 if( isset($result['error']) )
222 {
223 return $result;
224 }
225
226 return getGroup(array("GroupID"=>$groupID));
227 }
228
229 // Private method, does not include security, to only be called from places that have already verified security
230 function _addRoleToGroup($params)
231 {
232 $everyonePowers = 8796495740928; // This should now be fixed, when libomv was updated...
233
234 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
235 $groupID = mysql_real_escape_string( $params['GroupID'] );
236 $roleID = mysql_real_escape_string( $params['RoleID'] );
237 $name = mysql_real_escape_string( $params['Name'] );
238 $desc = mysql_real_escape_string( $params['Description'] );
239 $title = mysql_real_escape_string( $params['Title'] );
240 $powers = mysql_real_escape_string( $params['Powers'] );
241
242 if( !isset($powers) || ($powers == 0) || ($powers == '') )
243 {
244 $powers = $everyonePowers;
245 }
246
247 $sql = " INSERT INTO osrole (GroupID, RoleID, Name, Description, Title, Powers) VALUES "
248 ." ('$groupID', '$roleID', '$name', '$desc', '$title', $powers)";
249
250 if (!mysql_query($sql, $groupDBCon))
251 {
252 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error()
253 , 'method' => 'addRoleToGroup'
254 , 'params' => var_export($params, TRUE));
255 }
256
257 return array("success" => "true");
258 }
259
260 function addRoleToGroup($params)
261 {
262 if( is_array($error = secureRequest($params, TRUE)) )
263 {
264 return $error;
265 }
266
267 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
268 $groupID = $params['GroupID'];
269
270 // Verify the requesting agent has permission
271 if( is_array($error = checkGroupPermission($groupID, $groupPowers['CreateRole'])) )
272 {
273 return $error;
274 }
275
276 return _addRoleToGroup($params);
277 }
278
279 function updateGroupRole($params)
280 {
281 if( is_array($error = secureRequest($params, TRUE)) )
282 {
283 return $error;
284 }
285
286 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
287 $groupID = mysql_real_escape_string( $params['GroupID'] );
288 $roleID = mysql_real_escape_string( $params['RoleID'] );
289 $name = mysql_real_escape_string( $params['Name'] );
290 $desc = mysql_real_escape_string( $params['Description'] );
291 $title = mysql_real_escape_string( $params['Title'] );
292 $powers = mysql_real_escape_string( $params['Powers'] );
293
294 // Verify the requesting agent has permission
295 if( is_array($error = checkGroupPermission($groupID, $groupPowers['RoleProperties'])) )
296 {
297 return $error;
298 }
299
300 $sql = " UPDATE osrole SET RoleID = '$roleID' ";
301 if( isset($params['Name']) )
302 {
303 $sql .= ", Name = '$name'";
304 }
305 if( isset($params['Description']) )
306 {
307 $sql .= ", Description = '$desc'";
308 }
309 if( isset($params['Title']) )
310 {
311 $sql .= ", Title = '$title'";
312 }
313 if( isset($params['Powers']) )
314 {
315 $sql .= ", Powers = $powers";
316 }
317
318 $sql .= " WHERE GroupID = '$groupID' AND RoleID = '$roleID'";
319
320 if (!mysql_query($sql, $groupDBCon))
321 {
322 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
323 }
324
325 return array("success" => "true");
326 }
327
328 function removeRoleFromGroup($params)
329 {
330 if( is_array($error = secureRequest($params, TRUE)) )
331 {
332 return $error;
333 }
334
335 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
336 $groupID = mysql_real_escape_string( $params['GroupID'] );
337 $roleID = mysql_real_escape_string( $params['RoleID'] );
338
339 if( is_array($error = checkGroupPermission($groupID, $groupPowers['RoleProperties'])) )
340 {
341 return $error;
342 }
343
344 /// 1. Remove all members from Role
345 /// 2. Set selected Role to uuidZero for anyone that had the role selected
346 /// 3. Delete roll
347
348 $sql = "DELETE FROM osgrouprolemembership WHERE GroupID = '$groupID' AND RoleID = '$roleID'";
349 if (!mysql_query($sql, $groupDBCon))
350 {
351 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
352 }
353
354 $sql = "UPDATE osgroupmembership SET SelectedRoleID = '$uuidZero' WHERE GroupID = '$groupID' AND SelectedRoleID = '$roleID'";
355 if (!mysql_query($sql, $groupDBCon))
356 {
357 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
358 }
359
360 $sql = "DELETE FROM osrole WHERE GroupID = '$groupID' AND RoleID = '$roleID'";
361 if (!mysql_query($sql, $groupDBCon))
362 {
363 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
364 }
365
366 return array("success" => "true");
367 }
368
369 function getGroup($params)
370 {
371 if( is_array($error = secureRequest($params, FALSE)) )
372 {
373 return $error;
374 }
375
376 return _getGroup($params);
377 }
378
379 function _getGroup($params)
380 {
381 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
382 $sql = " SELECT osgroup.GroupID, osgroup.Name, Charter, InsigniaID, FounderID, MembershipFee, OpenEnrollment, ShowInList, AllowPublish, MaturePublish, OwnerRoleID"
383 ." , count(osrole.RoleID) as GroupRolesCount, count(osgroupmembership.AgentID) as GroupMembershipCount "
384 ." FROM osgroup "
385 ." LEFT JOIN osrole ON (osgroup.GroupID = osrole.GroupID)"
386 ." LEFT JOIN osgroupmembership ON (osgroup.GroupID = osgroupmembership.GroupID)"
387 ." WHERE ";
388
389 if( isset($params['GroupID']) )
390 {
391 $sql .= "osgroup.GroupID = '" . mysql_real_escape_string($params['GroupID']). "'";
392 }
393 else if( isset($params['Name']) )
394 {
395 $sql .= "osgroup.Name = '" . mysql_real_escape_string($params['Name']) . "'";
396 }
397 else
398 {
399 return array("error" => "Must specify GroupID or Name");
400 }
401
402 $sql .= " GROUP BY osgroup.GroupID, osgroup.name, charter, insigniaID, founderID, membershipFee, openEnrollment, showInList, allowPublish, maturePublish, ownerRoleID";
403
404 $result = mysql_query($sql, $groupDBCon);
405
406 if (!$result)
407 {
408 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
409 }
410
411 if (mysql_num_rows($result) == 0)
412 {
413 return array('succeed' => 'false', 'error' => 'Group Not Found', 'params' => var_export($params, TRUE), 'sql' => $sql);
414 }
415
416 return mysql_fetch_assoc($result);
417 }
418
419 function updateGroup($params)
420 {
421 if( is_array($error = secureRequest($params, TRUE)) )
422 {
423 return $error;
424 }
425
426 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
427 $groupID = mysql_real_escape_string( $params["GroupID"] );
428 $charter = mysql_real_escape_string( $params["Charter"] );
429 $insigniaID = mysql_real_escape_string( $params["InsigniaID"] );
430 $membershipFee = mysql_real_escape_string( $params["MembershipFee"] );
431 $openEnrollment = mysql_real_escape_string( $params["OpenEnrollment"] );
432 $showInList = mysql_real_escape_string( $params["ShowInList"] );
433 $allowPublish = mysql_real_escape_string( $params["AllowPublish"] );
434 $maturePublish = mysql_real_escape_string( $params["MaturePublish"] );
435
436 if( is_array($error = checkGroupPermission($groupID, $groupPowers['ChangeOptions'])) )
437 {
438 return $error;
439 }
440
441 // Create group
442 $sql = "UPDATE osgroup
443 SET
444 Charter = '$charter'
445 , InsigniaID = '$insigniaID'
446 , MembershipFee = $membershipFee
447 , OpenEnrollment= $openEnrollment
448 , ShowInList = $showInList
449 , AllowPublish = $allowPublish
450 , MaturePublish = $maturePublish
451 WHERE
452 GroupID = '$groupID'";
453
454 if (!mysql_query($sql, $groupDBCon))
455 {
456 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
457 }
458
459 return array('success' => 'true');
460 }
461
462 function findGroups($params)
463 {
464 if( is_array($error = secureRequest($params, FALSE)) )
465 {
466 return $error;
467 }
468
469 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
470 $search = mysql_real_escape_string( $params['Search'] );
471
472 $sql = " SELECT osgroup.GroupID, osgroup.Name, count(osgroupmembership.AgentID) as Members "
473 ." FROM osgroup LEFT JOIN osgroupmembership ON (osgroup.GroupID = osgroupmembership.GroupID) "
474 ." WHERE "
475 ." ( MATCH (osgroup.name) AGAINST ('$search' IN BOOLEAN MODE)"
476 ." OR osgroup.name LIKE '%$search%'"
477 ." OR osgroup.name REGEXP '$search'"
478 ." ) AND ShowInList = 1"
479 ." GROUP BY osgroup.GroupID, osgroup.Name";
480
481 $result = mysql_query($sql, $groupDBCon);
482
483 if (!$result)
484 {
485 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
486 }
487
488 if( mysql_num_rows($result) == 0 )
489 {
490 return array('succeed' => 'false', 'error' => 'No groups found.', 'params' => var_export($params, TRUE), 'sql' => $sql);
491 }
492
493 $results = array();
494
495 while ($row = mysql_fetch_assoc($result))
496 {
497 $groupID = $row['GroupID'];
498 $results[$groupID] = $row;
499 }
500
501 return array('results' => $results, 'success' => TRUE);
502 }
503
504 function _setAgentActiveGroup($params)
505 {
506 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
507 $agentID = mysql_real_escape_string( $params['AgentID'] );
508 $groupID = mysql_real_escape_string( $params['GroupID'] );
509
510 $sql = " UPDATE osagent "
511 ." SET ActiveGroupID = '$groupID'"
512 ." WHERE AgentID = '$agentID'";
513
514 if (!mysql_query($sql, $groupDBCon))
515 {
516 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
517 }
518
519 if( mysql_affected_rows() == 0 )
520 {
521 $sql = " INSERT INTO osagent (ActiveGroupID, AgentID) VALUES "
522 ." ('$groupID', '$agentID')";
523
524 if (!mysql_query($sql, $groupDBCon))
525 {
526 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
527 }
528 }
529
530 return array("success" => "true");
531 }
532
533 function setAgentActiveGroup($params)
534 {
535 if( is_array($error = secureRequest($params, TRUE)) )
536 {
537 return $error;
538 }
539
540 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
541 $agentID = $params['AgentID'];
542 $groupID = $params['GroupID'];
543
544 if( isset($requestingAgent) && ($requestingAgent != $uuidZero) && ($requestingAgent != $agentID) )
545 {
546 return array('error' => "Agent can only change their own Selected Group Role", 'params' => var_export($params, TRUE));
547 }
548
549 return _setAgentActiveGroup($params);
550 }
551
552 function addAgentToGroup($params)
553 {
554 if( is_array($error = secureRequest($params, TRUE)) )
555 {
556 return $error;
557 }
558
559 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
560 $groupID = $params["GroupID"];
561 $agentID = $params["AgentID"];
562
563 if( is_array($error = checkGroupPermission($groupID, $groupPowers['AssignMember'])) )
564 {
565 // If they don't have direct permission, check to see if the group is marked for open enrollment
566 $groupInfo = _getGroup( array ('GroupID' => $groupID) );
567
568 if( isset($groupInfo['error']))
569 {
570 return $groupInfo;
571 }
572
573 if($groupInfo['OpenEnrollment'] != 1)
574 {
575 $escapedAgentID = mysql_real_escape_string($agentID);
576 $escapedGroupID = mysql_real_escape_string($groupID);
577
578 // Group is not open enrollment, check if the specified agentid has an invite
579 $sql = " SELECT GroupID, RoleID, AgentID FROM osgroupinvite"
580 ." WHERE osgroupinvite.AgentID = '$escapedAgentID' AND osgroupinvite.GroupID = '$escapedGroupID'";
581
582 $results = mysql_query($sql, $groupDBCon);
583 if (!$results)
584 {
585 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
586 }
587
588 if( mysql_num_rows($results) == 1 )
589 {
590 // if there is an invite, make sure we're adding the user to the role specified in the invite
591 $inviteInfo = mysql_fetch_assoc($results);
592 $params['RoleID'] = $inviteInfo['RoleID'];
593 }
594 else
595 {
596 // Not openenrollment, not invited, return permission denied error
597 return $error;
598 }
599 }
600 }
601
602 return _addAgentToGroup($params);
603 }
604
605 // Private method, does not include security, to only be called from places that have already verified security
606 function _addAgentToGroup($params)
607 {
608 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
609 $agentID = $params["AgentID"];
610 $groupID = $params["GroupID"];
611
612 $roleID = $uuidZero;
613 if( isset($params["RoleID"]) )
614 {
615 $roleID = $params["RoleID"];
616 }
617
618 $escapedAgentID = mysql_real_escape_string($agentID);
619 $escapedGroupID = mysql_real_escape_string($groupID);
620 $escapedRoleID = mysql_real_escape_string($roleID);
621
622 // Check if agent already a member
623 $sql = " SELECT count(AgentID) as isMember FROM osgroupmembership WHERE AgentID = '$escapedAgentID' AND GroupID = '$escapedGroupID'";
624 $result = mysql_query($sql, $groupDBCon);
625 if (!$result)
626 {
627 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
628 }
629
630 // If not a member, add membership, select role (defaults to uuidZero, or everyone role)
631 if( mysql_result($result, 0) == 0 )
632 {
633 $sql = " INSERT INTO osgroupmembership (GroupID, AgentID, Contribution, ListInProfile, AcceptNotices, SelectedRoleID) VALUES "
634 ."('$escapedGroupID','$escapedAgentID', 0, 1, 1,'$escapedRoleID')";
635
636 if (!mysql_query($sql, $groupDBCon))
637 {
638 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
639 }
640 }
641
642 // Make sure they're in the Everyone role
643 $result = _addAgentToGroupRole(array("GroupID" => $groupID, "RoleID" => $uuidZero, "AgentID" => $agentID));
644 if( isset($result['error']) )
645 {
646 return $result;
647 }
648
649 // Make sure they're in specified role, if they were invited
650 if( $roleID != $uuidZero )
651 {
652 $result = _addAgentToGroupRole(array("GroupID" => $groupID, "RoleID" => $roleID, "AgentID" => $agentID));
653 if( isset($result['error']) )
654 {
655 return $result;
656 }
657 }
658
659 //Set the role they were invited to as their selected role
660 _setAgentGroupSelectedRole(array('AgentID' => $agentID, 'RoleID' => $roleID, 'GroupID' => $groupID));
661
662 // Set the group as their active group.
663 // _setAgentActiveGroup(array("GroupID" => $groupID, "AgentID" => $agentID));
664
665 return array("success" => "true");
666 }
667
668 function removeAgentFromGroup($params)
669 {
670 if( is_array($error = secureRequest($params, TRUE)) )
671 {
672 return $error;
673 }
674
675 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
676 $agentID = $params["AgentID"];
677 $groupID = $params["GroupID"];
678
679 // An agent is always allowed to remove themselves from a group -- so only check if the requesting agent is different then the agent being removed.
680 if( $agentID != $requestingAgent )
681 {
682 if( is_array($error = checkGroupPermission($groupID, $groupPowers['RemoveMember'])) )
683 {
684 return $error;
685 }
686 }
687
688 $escapedAgentID = mysql_real_escape_string($agentID);
689 $escapedGroupID = mysql_real_escape_string($groupID);
690
691 // 1. If group is agent's active group, change active group to uuidZero
692 // 2. Remove Agent from group (osgroupmembership)
693 // 3. Remove Agent from all of the groups roles (osgrouprolemembership)
694
695 $sql = " UPDATE osagent "
696 ." SET ActiveGroupID = '$uuidZero'"
697 ." WHERE AgentID = '$escapedAgentID' AND ActiveGroupID = '$escapedGroupID'";
698
699 if (!mysql_query($sql, $groupDBCon))
700 {
701 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
702 }
703
704 $sql = " DELETE FROM osgroupmembership "
705 ." WHERE AgentID = '$agentID' AND GroupID = '$groupID'";
706 if (!mysql_query($sql, $groupDBCon))
707 {
708 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
709 }
710
711 $sql = " DELETE FROM osgrouprolemembership "
712 ." WHERE AgentID = '$escapedAgentID' AND GroupID = '$escapedGroupID'";
713 if (!mysql_query($sql, $groupDBCon))
714 {
715 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
716 }
717
718 return array("success" => "true");
719 }
720
721 function _addAgentToGroupRole($params)
722 {
723 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
724 $agentID = mysql_real_escape_string($params["AgentID"]);
725 $groupID = mysql_real_escape_string($params["GroupID"]);
726 $roleID = mysql_real_escape_string($params["RoleID"]);
727
728 // Check if agent already a member
729 $sql = " SELECT count(AgentID) as isMember FROM osgrouprolemembership WHERE AgentID = '$agentID' AND RoleID = '$roleID' AND GroupID = '$groupID'";
730 $result = mysql_query($sql, $groupDBCon);
731 if (!$result)
732 {
733 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
734 }
735
736 if( mysql_result($result, 0) == 0 )
737 {
738 $sql = " INSERT INTO osgrouprolemembership (GroupID, RoleID, AgentID) VALUES "
739 ."('$groupID', '$roleID', '$agentID')";
740
741 if (!mysql_query($sql, $groupDBCon))
742 {
743 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
744 }
745 }
746
747 return array("success" => "true");
748 }
749
750 function addAgentToGroupRole($params)
751 {
752 if( is_array($error = secureRequest($params, TRUE)) )
753 {
754 return $error;
755 }
756
757 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
758 $agentID = $params["AgentID"];
759 $groupID = $params["GroupID"];
760 $roleID = $params["RoleID"];
761
762 $escapedAgentID = mysql_real_escape_string($agentID);
763 $escapedGroupID = mysql_real_escape_string($groupID);
764 $escapedRoleID = mysql_real_escape_string($roleID);
765
766 // Check if being assigned to Owners role, assignments to an owners role can only be requested by owners.
767 $sql = " SELECT OwnerRoleID, osgrouprolemembership.AgentID "
768 ." FROM osgroup LEFT JOIN osgrouprolemembership ON (osgroup.GroupID = osgrouprolemembership.GroupID AND osgroup.OwnerRoleID = osgrouprolemembership.RoleID) "
769 ." WHERE osgrouprolemembership.AgentID = '" . mysql_real_escape_string($requestingAgent) . "' AND osgroup.GroupID = '$escapedGroupID'";
770
771 $results = mysql_query($sql, $groupDBCon);
772 if (!$results)
773 {
774 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
775 }
776
777 if( mysql_num_rows($results) == 0 )
778 {
779 return array('error' => "Group ($groupID) not found or Agent ($agentID) is not in the owner's role", 'params' => var_export($params, TRUE));
780 }
781
782 $ownerRoleInfo = mysql_fetch_assoc($results);
783 if( ($ownerRoleInfo['OwnerRoleID'] == $roleID) && ($ownerRoleInfo['AgentID'] != $requestingAgent) )
784 {
785 return array('error' => "Requesting agent $requestingAgent is not a member of the Owners Role and cannot add members to the owners role.", 'params' => var_export($params, TRUE));
786 }
787
788 if( is_array($error = checkGroupPermission($groupID, $groupPowers['AssignMember'])) )
789 {
790 return $error;
791 }
792
793 return _addAgentToGroupRole($params);
794 }
795
796 function removeAgentFromGroupRole($params)
797 {
798 if( is_array($error = secureRequest($params, TRUE)) )
799 {
800 return $error;
801 }
802
803 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
804 $agentID = mysql_real_escape_string($params["AgentID"]);
805 $groupID = mysql_real_escape_string($params["GroupID"]);
806 $roleID = mysql_real_escape_string($params["RoleID"]);
807
808 if( is_array($error = checkGroupPermission($groupID, $groupPowers['AssignMember'])) )
809 {
810 return $error;
811 }
812
813 // If agent has this role selected, change their selection to everyone (uuidZero) role
814 $sql = " UPDATE osgroupmembership SET SelectedRoleID = '$uuidZero' WHERE AgentID = '$agentID' AND GroupID = '$groupID' AND SelectedRoleID = '$roleID'";
815 $result = mysql_query($sql, $groupDBCon);
816 if (!$result)
817 {
818 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
819 }
820
821 $sql = " DELETE FROM osgrouprolemembership WHERE AgentID = '$agentID' AND GroupID = '$groupID' AND RoleID = '$roleID'";
822
823 if (!mysql_query($sql, $groupDBCon))
824 {
825 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
826 }
827
828 return array("success" => "true");
829 }
830
831 function _setAgentGroupSelectedRole($params)
832 {
833 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
834 $agentID = mysql_real_escape_string($params["AgentID"]);
835 $groupID = mysql_real_escape_string($params["GroupID"]);
836 $roleID = mysql_real_escape_string($params["RoleID"]);
837
838 $sql = " UPDATE osgroupmembership SET SelectedRoleID = '$roleID' WHERE AgentID = '$agentID' AND GroupID = '$groupID'";
839 $result = mysql_query($sql, $groupDBCon);
840 if (!$result)
841 {
842 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
843 }
844
845 return array('success' => 'true');
846 }
847
848 function setAgentGroupSelectedRole($params)
849 {
850 if( is_array($error = secureRequest($params, TRUE)) )
851 {
852 return $error;
853 }
854
855 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
856 $agentID = $params["AgentID"];
857 $groupID = $params["GroupID"];
858 $roleID = $params["RoleID"];
859
860 if( isset($requestingAgent) && ($requestingAgent != $uuidZero) && ($requestingAgent != $agentID) )
861 {
862 return array('error' => "Agent can only change their own Selected Group Role", 'params' => var_export($params, TRUE));
863 }
864
865 return _setAgentGroupSelectedRole($params);
866 }
867
868 function getAgentGroupMembership($params)
869 {
870 if( is_array($error = secureRequest($params, FALSE)) )
871 {
872 return $error;
873 }
874
875 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
876 $groupID = mysql_real_escape_string($params['GroupID']);
877 $agentID = mysql_real_escape_string($params['AgentID']);
878
879 $sql = " SELECT osgroup.GroupID, osgroup.Name as GroupName, osgroup.Charter, osgroup.InsigniaID, osgroup.FounderID, osgroup.MembershipFee, osgroup.OpenEnrollment, osgroup.ShowInList, osgroup.AllowPublish, osgroup.MaturePublish"
880 ." , osgroupmembership.Contribution, osgroupmembership.ListInProfile, osgroupmembership.AcceptNotices"
881 ." , osgroupmembership.SelectedRoleID, osrole.Title"
882 ." , osagent.ActiveGroupID "
883 ." FROM osgroup JOIN osgroupmembership ON (osgroup.GroupID = osgroupmembership.GroupID)"
884 ." JOIN osrole ON (osgroupmembership.SelectedRoleID = osrole.RoleID AND osgroupmembership.GroupID = osrole.GroupID)"
885 ." JOIN osagent ON (osagent.AgentID = osgroupmembership.AgentID)"
886 ." WHERE osgroup.GroupID = '$groupID' AND osgroupmembership.AgentID = '$agentID'";
887
888 $groupmembershipResult = mysql_query($sql, $groupDBCon);
889 if (!$groupmembershipResult)
890 {
891 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
892 }
893
894 if( mysql_num_rows($groupmembershipResult) == 0 )
895 {
896 return array('succeed' => 'false', 'error' => 'None Found', 'params' => var_export($params, TRUE), 'sql' => $sql);
897 }
898
899 $groupMembershipInfo = mysql_fetch_assoc($groupmembershipResult);
900
901 $sql = " SELECT BIT_OR(osrole.Powers) AS GroupPowers"
902 ." FROM osgrouprolemembership JOIN osrole ON (osgrouprolemembership.GroupID = osrole.GroupID AND osgrouprolemembership.RoleID = osrole.RoleID)"
903 ." WHERE osgrouprolemembership.GroupID = '$groupID' AND osgrouprolemembership.AgentID = '$agentID'";
904 $groupPowersResult = mysql_query($sql, $groupDBCon);
905 if (!$groupPowersResult)
906 {
907 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
908 }
909 $groupPowersInfo = mysql_fetch_assoc($groupPowersResult);
910
911 return array_merge($groupMembershipInfo, $groupPowersInfo);
912 }
913
914 function getAgentGroupMemberships($params)
915 {
916 if( is_array($error = secureRequest($params, FALSE)) )
917 {
918 return $error;
919 }
920
921 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
922 $agentID = mysql_real_escape_string($params['AgentID']);
923
924 $sql = " SELECT osgroup.GroupID, osgroup.Name as GroupName, osgroup.Charter, osgroup.InsigniaID, osgroup.FounderID, osgroup.MembershipFee, osgroup.OpenEnrollment, osgroup.ShowInList, osgroup.AllowPublish, osgroup.MaturePublish"
925 ." , osgroupmembership.Contribution, osgroupmembership.ListInProfile, osgroupmembership.AcceptNotices"
926 ." , osgroupmembership.SelectedRoleID, osrole.Title"
927 ." , IFNULL(osagent.ActiveGroupID, '$uuidZero') AS ActiveGroupID"
928 ." FROM osgroup JOIN osgroupmembership ON (osgroup.GroupID = osgroupmembership.GroupID)"
929 ." JOIN osrole ON (osgroupmembership.SelectedRoleID = osrole.RoleID AND osgroupmembership.GroupID = osrole.GroupID)"
930 ." LEFT JOIN osagent ON (osagent.AgentID = osgroupmembership.AgentID)"
931 ." WHERE osgroupmembership.AgentID = '$agentID'";
932
933 $groupmembershipResults = mysql_query($sql, $groupDBCon);
934 if (!$groupmembershipResults)
935 {
936 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
937 }
938
939 if( mysql_num_rows($groupmembershipResults) == 0 )
940 {
941 return array('succeed' => 'false', 'error' => 'No Memberships', 'params' => var_export($params, TRUE), 'sql' => $sql);
942 }
943
944 $groupResults = array();
945 while($groupMembershipInfo = mysql_fetch_assoc($groupmembershipResults))
946 {
947 $groupID = $groupMembershipInfo['GroupID'];
948 $sql = " SELECT BIT_OR(osrole.Powers) AS GroupPowers"
949 ." FROM osgrouprolemembership JOIN osrole ON (osgrouprolemembership.GroupID = osrole.GroupID AND osgrouprolemembership.RoleID = osrole.RoleID)"
950 ." WHERE osgrouprolemembership.GroupID = '$groupID' AND osgrouprolemembership.AgentID = '$agentID'";
951 $groupPowersResult = mysql_query($sql, $groupDBCon);
952 if (!$groupPowersResult)
953 {
954 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
955 }
956 $groupPowersInfo = mysql_fetch_assoc($groupPowersResult);
957 $groupResults[$groupID] = array_merge($groupMembershipInfo, $groupPowersInfo);
958 }
959
960 return $groupResults;
961 }
962
963 // Parameters should not already be mysql_real_escape_string() escaped
964 function canAgentViewRoleMembers( $agentID, $groupID, $roleID )
965 {
966 global $membersVisibleTo, $groupDBCon;
967
968 if( $membersVisibleTo == 'All' )
969 return true;
970
971 $agentID = mysql_real_escape_string($agentID);
972 $groupID = mysql_real_escape_string($groupID);
973 $roleID = mysql_real_escape_string($roleID);
974
975 $sql = " SELECT CASE WHEN min(OwnerRoleMembership.AgentID) IS NOT NULL THEN 1 ELSE 0 END AS IsOwner ";
976 $sql .= " FROM osgroup JOIN osgroupmembership ON (osgroup.GroupID = osgroupmembership.GroupID AND osgroupmembership.AgentID = '$agentID')";
977 $sql .= " LEFT JOIN osgrouprolemembership AS OwnerRoleMembership ON (OwnerRoleMembership.GroupID = osgroup.GroupID ";
978 $sql .= " AND OwnerRoleMembership.RoleID = osgroup.OwnerRoleID ";
979 $sql .= " AND OwnerRoleMembership.AgentID = '$agentID')";
980 $sql .= " WHERE osgroup.GroupID = '$groupID' GROUP BY osgroup.GroupID";
981
982 $viewMemberResults = mysql_query($sql, $groupDBCon);
983 if (!$viewMemberResults)
984 {
985 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error());
986 }
987
988 if (mysql_num_rows($viewMemberResults) == 0)
989 {
990 return false;
991 }
992
993 $viewMemberInfo = mysql_fetch_assoc($viewMemberResults);
994
995 switch( $membersVisibleTo )
996 {
997 case 'Group':
998 // if we get to here, there is at least one row, so they are a member of the group
999 return true;
1000 case 'Owners':
1001 default:
1002 return $viewMemberInfo['IsOwner'];
1003 }
1004 }
1005
1006 function getGroupMembers($params)
1007 {
1008 if( is_array($error = secureRequest($params, FALSE)) )
1009 {
1010 return $error;
1011 }
1012
1013 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
1014 $groupID = $params['GroupID'];
1015 $escapedGroupID = mysql_real_escape_string($groupID);
1016
1017 $sql = " SELECT osgroupmembership.AgentID"
1018 ." , osgroupmembership.Contribution, osgroupmembership.ListInProfile, osgroupmembership.AcceptNotices"
1019 ." , osgroupmembership.SelectedRoleID, osrole.Title"
1020 ." , CASE WHEN OwnerRoleMembership.AgentID IS NOT NULL THEN 1 ELSE 0 END AS IsOwner"
1021 ." FROM osgroup JOIN osgroupmembership ON (osgroup.GroupID = osgroupmembership.GroupID)"
1022 ." JOIN osrole ON (osgroupmembership.SelectedRoleID = osrole.RoleID AND osgroupmembership.GroupID = osrole.GroupID)"
1023 ." JOIN osrole AS OwnerRole ON (osgroup.OwnerRoleID = OwnerRole.RoleID AND osgroup.GroupID = OwnerRole.GroupID)"
1024 ." LEFT JOIN osgrouprolemembership AS OwnerRoleMembership ON (osgroup.OwnerRoleID = OwnerRoleMembership.RoleID
1025 AND (osgroup.GroupID = OwnerRoleMembership.GroupID)
1026 AND (osgroupmembership.AgentID = OwnerRoleMembership.AgentID))"
1027 ." WHERE osgroup.GroupID = '$escapedGroupID'";
1028
1029 $groupmemberResults = mysql_query($sql, $groupDBCon);
1030 if (!$groupmemberResults)
1031 {
1032 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1033 }
1034
1035 if (mysql_num_rows($groupmemberResults) == 0)
1036 {
1037 return array('succeed' => 'false', 'error' => 'No Group Members found', 'params' => var_export($params, TRUE), 'sql' => $sql);
1038 }
1039
1040 $roleMembersVisibleBit = $groupPowers['RoleMembersVisible'];
1041 $canViewAllGroupRoleMembers = canAgentViewRoleMembers($requestingAgent, $groupID, '');
1042
1043 $memberResults = array();
1044 while ($memberInfo = mysql_fetch_assoc($groupmemberResults))
1045 {
1046 $agentID = $memberInfo['AgentID'];
1047 $sql = " SELECT BIT_OR(osrole.Powers) AS AgentPowers, ( BIT_OR(osrole.Powers) & $roleMembersVisibleBit) as MemberVisible"
1048 ." FROM osgrouprolemembership JOIN osrole ON (osgrouprolemembership.GroupID = osrole.GroupID AND osgrouprolemembership.RoleID = osrole.RoleID)"
1049 ." WHERE osgrouprolemembership.GroupID = '$escapedGroupID' AND osgrouprolemembership.AgentID = '$agentID'";
1050 $memberPowersResult = mysql_query($sql, $groupDBCon);
1051 if (!$memberPowersResult)
1052 {
1053 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1054 }
1055
1056 $memberPowersCount = mysql_num_rows($memberPowersResult);
1057 error_log("Found $memberPowersCount rows for agent $agentID for requesting agent $requestingAgent");
1058
1059 if ($memberPowersCount == 0)
1060 {
1061 if ($canViewAllGroupRoleMembers || $agentID == $requestingAgent)
1062 {
1063 $memberResults[$agentID] = array_merge($memberInfo, array('AgentPowers' => 0));
1064 }
1065 else
1066 {
1067 // if can't view all group role members and there is no Member Visible bit, then don't return this member's info
1068 unset($memberResults[$agentID]);
1069 }
1070 }
1071 else
1072 {
1073 $memberPowersInfo = mysql_fetch_assoc($memberPowersResult);
1074 if ($memberPowersInfo['MemberVisible'] || $canViewAllGroupRoleMembers || $agentID == $requestingAgent)
1075 {
1076 $memberResults[$agentID] = array_merge($memberInfo, $memberPowersInfo);
1077 }
1078 else
1079 {
1080 // if can't view all group role members and there is no Member Visible bit, then don't return this member's info
1081 unset($memberResults[$agentID]);
1082 }
1083 }
1084 }
1085
1086 error_log("Returning " . count($memberResults) . " visible members for group $groupID for agent $agentID");
1087
1088 if (count($memberResults) == 0)
1089 {
1090 return array('succeed' => 'false', 'error' => 'No Visible Group Members found', 'params' => var_export($params, TRUE), 'sql' => $sql);
1091 }
1092
1093 return $memberResults;
1094 }
1095
1096 function getAgentActiveMembership($params)
1097 {
1098 if( is_array($error = secureRequest($params, FALSE)) )
1099 {
1100 return $error;
1101 }
1102
1103 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
1104 $agentID = mysql_real_escape_string($params['AgentID']);
1105
1106 $sql = " SELECT osgroup.GroupID, osgroup.Name as GroupName, osgroup.Charter, osgroup.InsigniaID, osgroup.FounderID, osgroup.MembershipFee, osgroup.OpenEnrollment, osgroup.ShowInList, osgroup.AllowPublish, osgroup.MaturePublish"
1107 ." , osgroupmembership.Contribution, osgroupmembership.ListInProfile, osgroupmembership.AcceptNotices"
1108 ." , osgroupmembership.SelectedRoleID, osrole.Title"
1109 ." , osagent.ActiveGroupID "
1110 ." FROM osagent JOIN osgroup ON (osgroup.GroupID = osagent.ActiveGroupID)"
1111 ." JOIN osgroupmembership ON (osgroup.GroupID = osgroupmembership.GroupID AND osagent.AgentID = osgroupmembership.AgentID)"
1112 ." JOIN osrole ON (osgroupmembership.SelectedRoleID = osrole.RoleID AND osgroupmembership.GroupID = osrole.GroupID)"
1113 ." WHERE osagent.AgentID = '$agentID'";
1114
1115 $groupmembershipResult = mysql_query($sql, $groupDBCon);
1116 if (!$groupmembershipResult)
1117 {
1118 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1119 }
1120 if (mysql_num_rows($groupmembershipResult) == 0)
1121 {
1122 return array('succeed' => 'false', 'error' => 'No Active Group Specified', 'params' => var_export($params, TRUE), 'sql' => $sql);
1123 }
1124 $groupMembershipInfo = mysql_fetch_assoc($groupmembershipResult);
1125
1126 $groupID = $groupMembershipInfo['GroupID'];
1127 $sql = " SELECT BIT_OR(osrole.Powers) AS GroupPowers"
1128 ." FROM osgrouprolemembership JOIN osrole ON (osgrouprolemembership.GroupID = osrole.GroupID AND osgrouprolemembership.RoleID = osrole.RoleID)"
1129 ." WHERE osgrouprolemembership.GroupID = '$groupID' AND osgrouprolemembership.AgentID = '$agentID'";
1130 $groupPowersResult = mysql_query($sql, $groupDBCon);
1131 if (!$groupPowersResult)
1132 {
1133 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1134 }
1135 $groupPowersInfo = mysql_fetch_assoc($groupPowersResult);
1136
1137 return array_merge($groupMembershipInfo, $groupPowersInfo);
1138 }
1139
1140 function getAgentRoles($params)
1141 {
1142 if( is_array($error = secureRequest($params, FALSE)) )
1143 {
1144 return $error;
1145 }
1146
1147 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
1148 $agentID = mysql_real_escape_string($params['AgentID']);
1149
1150 $sql = " SELECT "
1151 ." osrole.RoleID, osrole.GroupID, osrole.Title, osrole.Name, osrole.Description, osrole.Powers"
1152 ." , CASE WHEN osgroupmembership.SelectedRoleID = osrole.RoleID THEN 1 ELSE 0 END AS Selected"
1153 ." FROM osgroupmembership JOIN osgrouprolemembership ON (osgroupmembership.GroupID = osgrouprolemembership.GroupID AND osgroupmembership.AgentID = osgrouprolemembership.AgentID)"
1154 ." JOIN osrole ON ( osgrouprolemembership.RoleID = osrole.RoleID AND osgrouprolemembership.GroupID = osrole.GroupID)"
1155 ." LEFT JOIN osagent ON (osagent.AgentID = osgroupmembership.AgentID)"
1156 ." WHERE osgroupmembership.AgentID = '$agentID'";
1157
1158 if( isset($params['GroupID']) )
1159 {
1160 $groupID = $params['GroupID'];
1161 $sql .= " AND osgroupmembership.GroupID = '$groupID'";
1162 }
1163
1164 $roleResults = mysql_query($sql, $groupDBCon);
1165 if (!$roleResults)
1166 {
1167 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1168 }
1169
1170 if( mysql_num_rows($roleResults) == 0 )
1171 {
1172 return array('succeed' => 'false', 'error' => 'None found', 'params' => var_export($params, TRUE), 'sql' => $sql);
1173 }
1174
1175 $roles = array();
1176 while($role = mysql_fetch_assoc($roleResults))
1177 {
1178 $ID = $role['GroupID'].$role['RoleID'];
1179 $roles[$ID] = $role;
1180 }
1181
1182 return $roles;
1183 }
1184
1185 function getGroupRoles($params)
1186 {
1187 if( is_array($error = secureRequest($params, FALSE)) )
1188 {
1189 return $error;
1190 }
1191
1192 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
1193 $groupID = mysql_real_escape_string($params['GroupID']);
1194
1195 $sql = " SELECT "
1196 ." osrole.RoleID, osrole.Name, osrole.Title, osrole.Description, osrole.Powers, count(osgrouprolemembership.AgentID) as Members"
1197 ." FROM osrole LEFT JOIN osgrouprolemembership ON (osrole.GroupID = osgrouprolemembership.GroupID AND osrole.RoleID = osgrouprolemembership.RoleID)"
1198 ." WHERE osrole.GroupID = '$groupID'"
1199 ." GROUP BY osrole.RoleID, osrole.Name, osrole.Title, osrole.Description, osrole.Powers";
1200
1201 $roleResults = mysql_query($sql, $groupDBCon);
1202 if (!$roleResults)
1203 {
1204 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1205 }
1206
1207 if( mysql_num_rows($roleResults) == 0 )
1208 {
1209 return array('succeed' => 'false', 'error' => 'No roles found for group', 'params' => var_export($params, TRUE), 'sql' => $sql);
1210 }
1211
1212 $roles = array();
1213 while($role = mysql_fetch_assoc($roleResults))
1214 {
1215 $RoleID = $role['RoleID'];
1216 $roles[$RoleID] = $role;
1217 }
1218
1219 return $roles;
1220 }
1221
1222 function getGroupRoleMembers($params)
1223 {
1224 if( is_array($error = secureRequest($params, FALSE)) )
1225 {
1226 return $error;
1227 }
1228
1229 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
1230 $groupID = $params['GroupID'];
1231
1232 $roleMembersVisibleBit = $groupPowers['RoleMembersVisible'];
1233 $canViewAllGroupRoleMembers = canAgentViewRoleMembers($requestingAgent, $groupID, '');
1234
1235 $escapedGroupID = mysql_real_escape_string($groupID);
1236
1237 $sql = " SELECT "
1238 ." osrole.RoleID, osgrouprolemembership.AgentID"
1239 ." , (osrole.Powers & $roleMembersVisibleBit) as MemberVisible"
1240 ." FROM osrole JOIN osgrouprolemembership ON (osrole.GroupID = osgrouprolemembership.GroupID AND osrole.RoleID = osgrouprolemembership.RoleID)"
1241 ." WHERE osrole.GroupID = '$escapedGroupID'";
1242
1243 $memberResults = mysql_query($sql, $groupDBCon);
1244 if (!$memberResults)
1245 {
1246 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1247 }
1248
1249 if( mysql_num_rows($memberResults) == 0 )
1250 {
1251 return array('succeed' => 'false', 'error' => 'No role memberships found for group', 'params' => var_export($params, TRUE), 'sql' => $sql);
1252 }
1253
1254 $members = array();
1255 while($member = mysql_fetch_assoc($memberResults))
1256 {
1257 if( $canViewAllGroupRoleMembers || $member['MemberVisible'] || ($member['AgentID'] == $requestingAgent) )
1258 {
1259 $Key = $member['AgentID'] . $member['RoleID'];
1260 $members[$Key ] = $member;
1261 }
1262 }
1263
1264 if( count($members) == 0 )
1265 {
1266 return array('succeed' => 'false', 'error' => 'No role memberships visible for group', 'params' => var_export($params, TRUE), 'sql' => $sql);
1267 }
1268
1269 return $members;
1270 }
1271
1272 function setAgentGroupInfo($params)
1273 {
1274 if( is_array($error = secureRequest($params, TRUE)) )
1275 {
1276 return $error;
1277 }
1278
1279 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
1280
1281 if (isset($params['AgentID'])) {
1282 $agentID = mysql_real_escape_string($params['AgentID']);
1283 } else {
1284 $agentID = "";
1285 }
1286 if (isset($params['GroupID'])) {
1287 $groupID = mysql_real_escape_string($params['GroupID']);
1288 } else {
1289 $groupID = "";
1290 }
1291 if (isset($params['SelectedRoleID'])) {
1292 $roleID = mysql_real_escape_string($params['SelectedRoleID']);
1293 } else {
1294 $roleID = "";
1295 }
1296 if (isset($params['AcceptNotices'])) {
1297 $acceptNotices = mysql_real_escape_string($params['AcceptNotices']);
1298 } else {
1299 $acceptNotices = 1;
1300 }
1301 if (isset($params['ListInProfile'])) {
1302 $listInProfile = mysql_real_escape_string($params['ListInProfile']);
1303 } else {
1304 $listInProfile = 0;
1305 }
1306
1307 if( isset($requestingAgent) && ($requestingAgent != $uuidZero) && ($requestingAgent != $agentID) )
1308 {
1309 return array('error' => "Agent can only change their own group info", 'params' => var_export($params, TRUE));
1310 }
1311
1312 $sql = " UPDATE "
1313 ." osgroupmembership"
1314 ." SET "
1315 ." AgentID = '$agentID'";
1316
1317 if( isset($params['SelectedRoleID']) )
1318 {
1319 $sql .=" , SelectedRoleID = '$roleID'";
1320 }
1321 if( isset($params['AcceptNotices']) )
1322 {
1323 $sql .=" , AcceptNotices = $acceptNotices";
1324 }
1325 if( isset($params['ListInProfile']) )
1326 {
1327 $sql .=" , ListInProfile = $listInProfile";
1328 }
1329
1330 $sql .=" WHERE osgroupmembership.GroupID = '$groupID' AND osgroupmembership.AgentID = '$agentID'";
1331
1332 $memberResults = mysql_query($sql, $groupDBCon);
1333 if (!$memberResults)
1334 {
1335 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1336 }
1337
1338 return array('success'=> 'true');
1339 }
1340
1341 function getGroupNotices($params)
1342 {
1343 if( is_array($error = secureRequest($params, FALSE)) )
1344 {
1345 return $error;
1346 }
1347
1348 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
1349 $groupID = mysql_real_escape_string($params['GroupID']);
1350
1351 $sql = " SELECT "
1352 ." GroupID, NoticeID, Timestamp, FromName, Subject, Message, BinaryBucket"
1353 ." FROM osgroupnotice"
1354 ." WHERE osgroupnotice.GroupID = '$groupID'";
1355
1356 $results = mysql_query($sql, $groupDBCon);
1357 if (!$results)
1358 {
1359 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1360 }
1361
1362 if( mysql_num_rows($results) == 0 )
1363 {
1364 return array('succeed' => 'false', 'error' => 'No Notices', 'params' => var_export($params, TRUE), 'sql' => $sql);
1365 }
1366
1367 $notices = array();
1368 while($notice = mysql_fetch_assoc($results))
1369 {
1370 $NoticeID = $notice['NoticeID'];
1371 $notices[$NoticeID] = $notice;
1372 }
1373
1374 return $notices;
1375 }
1376
1377 function getGroupNotice($params)
1378 {
1379 if( is_array($error = secureRequest($params, FALSE)) )
1380 {
1381 return $error;
1382 }
1383
1384 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
1385 $noticeID = mysql_real_escape_string($params['NoticeID']);
1386
1387 $sql = " SELECT "
1388 ." GroupID, NoticeID, Timestamp, FromName, Subject, Message, BinaryBucket"
1389 ." FROM osgroupnotice"
1390 ." WHERE osgroupnotice.NoticeID = '$noticeID'";
1391
1392 $results = mysql_query($sql, $groupDBCon);
1393 if (!$results)
1394 {
1395 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1396 }
1397
1398 if( mysql_num_rows($results) == 0 )
1399 {
1400 return array('succeed' => 'false', 'error' => 'Group Notice Not Found', 'params' => var_export($params, TRUE), 'sql' => $sql);
1401 }
1402
1403 return mysql_fetch_assoc($results);
1404 }
1405
1406 function addGroupNotice($params)
1407 {
1408 if( is_array($error = secureRequest($params, TRUE)) )
1409 {
1410 return $error;
1411 }
1412
1413 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
1414 $groupID = mysql_real_escape_string($params['GroupID']);
1415 $noticeID = mysql_real_escape_string($params['NoticeID']);
1416 $fromName = mysql_real_escape_string($params['FromName']);
1417 $subject = mysql_real_escape_string($params['Subject']);
1418 $binaryBucket = mysql_real_escape_string($params['BinaryBucket']);
1419 $message = mysql_real_escape_string($params['Message']);
1420 $timeStamp = mysql_real_escape_string($params['TimeStamp']);
1421
1422 if( is_array($error = checkGroupPermission($groupID, $groupPowers['SendNotices'])) )
1423 {
1424 return $error;
1425 }
1426
1427 $sql = " INSERT INTO osgroupnotice"
1428 ." (GroupID, NoticeID, Timestamp, FromName, Subject, Message, BinaryBucket)"
1429 ." VALUES "
1430 ." ('$groupID', '$noticeID', $timeStamp, '$fromName', '$subject', '$message', '$binaryBucket')";
1431
1432 $results = mysql_query($sql, $groupDBCon);
1433 if (!$results)
1434 {
1435 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1436 }
1437
1438 return array('success' => 'true');
1439 }
1440
1441 function addAgentToGroupInvite($params)
1442 {
1443 if( is_array($error = secureRequest($params, TRUE)) )
1444 {
1445 return $error;
1446 }
1447
1448 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
1449
1450 if( is_array($error = checkGroupPermission($params['GroupID'], $groupPowers['Invite'])) )
1451 {
1452 return $error;
1453 }
1454
1455 $inviteID = mysql_real_escape_string($params['InviteID']);
1456 $groupID = mysql_real_escape_string($params['GroupID']);
1457 $roleID = mysql_real_escape_string($params['RoleID']);
1458 $agentID = mysql_real_escape_string($params['AgentID']);
1459
1460 // Remove any existing invites for this agent to this group
1461 $sql = " DELETE FROM osgroupinvite"
1462 ." WHERE osgroupinvite.AgentID = '$agentID' AND osgroupinvite.GroupID = '$groupID'";
1463
1464 $results = mysql_query($sql, $groupDBCon);
1465 if (!$results)
1466 {
1467 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1468 }
1469
1470 // Add new invite for this agent to this group for the specifide role
1471 $sql = " INSERT INTO osgroupinvite"
1472 ." (InviteID, GroupID, RoleID, AgentID) VALUES ('$inviteID', '$groupID', '$roleID', '$agentID')";
1473
1474 $results = mysql_query($sql, $groupDBCon);
1475 if (!$results)
1476 {
1477 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1478 }
1479
1480 return array('success' => 'true');
1481 }
1482
1483 function getAgentToGroupInvite($params)
1484 {
1485 if( is_array($error = secureRequest($params, FALSE)) )
1486 {
1487 return $error;
1488 }
1489
1490 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
1491 $inviteID = mysql_real_escape_string($params['InviteID']);
1492
1493 $sql = " SELECT GroupID, RoleID, AgentID FROM osgroupinvite"
1494 ." WHERE osgroupinvite.InviteID = '$inviteID'";
1495
1496 $results = mysql_query($sql, $groupDBCon);
1497 if (!$results)
1498 {
1499 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1500 }
1501
1502 if( mysql_num_rows($results) == 1 )
1503 {
1504 $inviteInfo = mysql_fetch_assoc($results);
1505 $groupID = $inviteInfo['GroupID'];
1506 $roleID = $inviteInfo['RoleID'];
1507 $agentID = $inviteInfo['AgentID'];
1508
1509 return array('success' => 'true', 'GroupID'=>$groupID, 'RoleID'=>$roleID, 'AgentID'=>$agentID);
1510 }
1511 else
1512 {
1513 return array('succeed' => 'false', 'error' => 'Invitation not found', 'params' => var_export($params, TRUE), 'sql' => $sql);
1514 }
1515 }
1516
1517 function removeAgentToGroupInvite($params)
1518 {
1519 if( is_array($error = secureRequest($params, TRUE)) )
1520 {
1521 return $error;
1522 }
1523
1524 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon;
1525 $inviteID = mysql_real_escape_string($params['InviteID']);
1526
1527 $sql = " DELETE FROM osgroupinvite"
1528 ." WHERE osgroupinvite.InviteID = '$inviteID'";
1529
1530 $results = mysql_query($sql, $groupDBCon);
1531 if (!$results)
1532 {
1533 return array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error(), 'params' => var_export($params, TRUE));
1534 }
1535
1536 return array('success' => 'true');
1537 }
1538
1539 function secureRequest($params, $write = FALSE)
1540 {
1541 global $groupWriteKey, $groupReadKey, $verifiedReadKey, $verifiedWriteKey, $groupRequireAgentAuthForWrite, $requestingAgent;
1542 global $overrideAgentUserService;
1543
1544 // Cache this for access by other security functions
1545 $requestingAgent = $params['RequestingAgentID'];
1546
1547 if( isset($groupReadKey) && ($groupReadKey != '') && (!isset($verifiedReadKey) || ($verifiedReadKey !== TRUE)) )
1548 {
1549 if( !isset($params['ReadKey']) || ($params['ReadKey'] != $groupReadKey ) )
1550 {
1551 return array('error' => "Invalid (or No) Read Key Specified", 'params' => var_export($params, TRUE));
1552 }
1553 else
1554 {
1555 $verifiedReadKey = TRUE;
1556 }
1557 }
1558
1559 if( ($write == TRUE) && isset($groupWriteKey) && ($groupWriteKey != '') && (!isset($verifiedWriteKey) || ($verifiedWriteKey !== TRUE)) )
1560 {
1561 if( !isset($params['WriteKey']) || ($params['WriteKey'] != $groupWriteKey ) )
1562 {
1563 return array('error' => "Invalid (or No) Write Key Specified", 'params' => var_export($params, TRUE));
1564 }
1565 else
1566 {
1567 $verifiedWriteKey = TRUE;
1568 }
1569 }
1570
1571 if( ($write == TRUE) && isset($groupRequireAgentAuthForWrite) && ($groupRequireAgentAuthForWrite == TRUE) )
1572 {
1573 // Note: my brain can't do boolean logic this morning, so just putting this here instead of integrating with line above.
1574 // If the write key has already been verified for this request, don't check it again. This comes into play with methods that call other methods, such as CreateGroup() which calls Addrole()
1575 if( isset($verifiedWriteKey) && ($verifiedWriteKey !== TRUE))
1576 {
1577 return TRUE;
1578 }
1579
1580 if( !isset($params['RequestingAgentID'])
1581 || !isset($params['RequestingAgentUserService'])
1582 || !isset($params['RequestingSessionID']) )
1583 {
1584 return array('error' => "Requesting AgentID and SessionID must be specified", 'params' => var_export($params, TRUE));
1585 }
1586
1587 // NOTE: an AgentID and SessionID of $uuidZero will likely be a region making a request, that is not tied to a specific agent making the request.
1588
1589 $UserService = $params['RequestingAgentUserService'];
1590 if( isset($overrideAgentUserService) && ($overrideAgentUserService != "") )
1591 {
1592 $UserService = $overrideAgentUserService;
1593 }
1594
1595 $client = new xmlrpc_client($UserService);
1596 $client->return_type = 'phpvals';
1597
1598 $verifyParams = new xmlrpcval(array('avatar_uuid' => new xmlrpcval($params['RequestingAgentID'], 'string')
1599 ,'session_id' => new xmlrpcval($params['RequestingSessionID'], 'string'))
1600 , 'struct');
1601
1602 $message = new xmlrpcmsg("check_auth_session", array($verifyParams));
1603 $resp = $client->send($message, 5);
1604 if ($resp->faultCode())
1605 {
1606 return array('error' => "Error validating AgentID and SessionID"
1607 , 'xmlrpcerror'=> $resp->faultString()
1608 , 'params' => var_export($params, TRUE));
1609 }
1610
1611 $verifyReturn = $resp->value();
1612
1613 if( !isset($verifyReturn['auth_session']) || ($verifyReturn['auth_session'] != 'TRUE') )
1614 {
1615 return array('error' => "UserService.check_auth_session() did not return TRUE"
1616 , 'userservice' => var_export($verifyReturn, TRUE)
1617 , 'params' => var_export($params, TRUE));
1618
1619 }
1620 }
1621
1622 return TRUE;
1623 }
1624
1625 function checkGroupPermission($GroupID, $Permission)
1626 {
1627 global $groupEnforceGroupPerms, $requestingAgent, $uuidZero, $groupDBCon, $groupPowers;
1628
1629 if( !isset($Permission) || ($Permission == 0) )
1630 {
1631 return array('error' => 'No Permission value specified for checkGroupPermission'
1632 , 'Permission' => $Permission);
1633 }
1634
1635 // If it isn't set to true, then always return true, otherwise verify they have perms
1636 if( !isset($groupEnforceGroupPerms) || ($groupEnforceGroupPerms != TRUE) )
1637 {
1638 return true;
1639 }
1640
1641 if( !isset($requestingAgent) || ($requestingAgent == $uuidZero) )
1642 {
1643 return array('error' => 'Requesting agent was either not specified or not validated.'
1644 , 'requestingAgent' => $requestingAgent);
1645 }
1646
1647 $params = array('AgentID' => $requestingAgent, 'GroupID' => $GroupID);
1648 $reqAgentMembership = getAgentGroupMembership($params);
1649
1650 if( isset($reqAgentMembership['error'] ) )
1651 {
1652 return array('error' => 'Could not get agent membership for group'
1653 , 'params' => var_export($params, TRUE)
1654 , 'nestederror' => $reqAgentMembership['error']);
1655 }
1656
1657 // Worlds ugliest bitwise operation, EVER
1658 $PermMask = $reqAgentMembership['GroupPowers'];
1659 $PermValue = $Permission;
1660
1661 global $groupDBCon;
1662 $sql = " SELECT $PermMask & $PermValue AS Allowed";
1663 $results = mysql_query($sql, $groupDBCon);
1664 if (!$results)
1665 {
1666 echo print_r( array('error' => "Could not successfully run query ($sql) from DB: " . mysql_error()));
1667 }
1668 $PermMasked = mysql_result($results, 0);
1669
1670 if( $PermMasked != $Permission )
1671 {
1672 $permNames = array_flip($groupPowers);
1673
1674 return array('error' => 'Agent does not have group power to ' . $Permission .'('.$permNames[$Permission].')'
1675 , 'PermMasked' => $PermMasked
1676 , 'params' => var_export($params, TRUE)
1677 , 'permBitMaskSql' => $sql
1678 , 'Permission' => $Permission);
1679 }
1680
1681 /*
1682 return array('error' => 'Reached end'
1683 , 'reqAgentMembership' => var_export($reqAgentMembership, TRUE)
1684 , 'GroupID' => $GroupID
1685 , 'Permission' => $Permission
1686 , 'PermMasked' => $PermMasked
1687 );
1688 */
1689 return TRUE;
1690 }
1691
1692
1693 $s = new xmlrpc_server(array(
1694 "test" => array("function" => "test")
1695 , "groups.createGroup" => array("function" => "createGroup", "signature" => $common_sig)
1696 , "groups.updateGroup" => array("function" => "updateGroup", "signature" => $common_sig)
1697 , "groups.getGroup" => array("function" => "getGroup", "signature" => $common_sig)
1698 , "groups.findGroups" => array("function" => "findGroups", "signature" => $common_sig)
1699
1700 , "groups.getGroupRoles" => array("function" => "getGroupRoles", "signature" => $common_sig)
1701 , "groups.addRoleToGroup" => array("function" => "addRoleToGroup", "signature" => $common_sig)
1702 , "groups.removeRoleFromGroup" => array("function" => "removeRoleFromGroup", "signature" => $common_sig)
1703 , "groups.updateGroupRole" => array("function" => "updateGroupRole", "signature" => $common_sig)
1704 , "groups.getGroupRoleMembers" => array("function" => "getGroupRoleMembers", "signature" => $common_sig)
1705
1706 , "groups.setAgentGroupSelectedRole" => array("function" => "setAgentGroupSelectedRole", "signature" => $common_sig)
1707 , "groups.addAgentToGroupRole" => array("function" => "addAgentToGroupRole", "signature" => $common_sig)
1708 , "groups.removeAgentFromGroupRole" => array("function" => "removeAgentFromGroupRole", "signature" => $common_sig)
1709
1710 , "groups.getGroupMembers" => array("function" => "getGroupMembers", "signature" => $common_sig)
1711 , "groups.addAgentToGroup" => array("function" => "addAgentToGroup", "signature" => $common_sig)
1712 , "groups.removeAgentFromGroup" => array("function" => "removeAgentFromGroup", "signature" => $common_sig)
1713 , "groups.setAgentGroupInfo" => array("function" => "setAgentGroupInfo", "signature" => $common_sig)
1714
1715 , "groups.addAgentToGroupInvite" => array("function" => "addAgentToGroupInvite", "signature" => $common_sig)
1716 , "groups.getAgentToGroupInvite" => array("function" => "getAgentToGroupInvite", "signature" => $common_sig)
1717 , "groups.removeAgentToGroupInvite" => array("function" => "removeAgentToGroupInvite", "signature" => $common_sig)
1718
1719 , "groups.setAgentActiveGroup" => array("function" => "setAgentActiveGroup", "signature" => $common_sig)
1720 , "groups.getAgentGroupMembership" => array("function" => "getAgentGroupMembership", "signature" => $common_sig)
1721 , "groups.getAgentGroupMemberships" => array("function" => "getAgentGroupMemberships", "signature" => $common_sig)
1722 , "groups.getAgentActiveMembership" => array("function" => "getAgentActiveMembership", "signature" => $common_sig)
1723 , "groups.getAgentRoles" => array("function" => "getAgentRoles", "signature" => $common_sig)
1724
1725 , "groups.getGroupNotices" => array("function" => "getGroupNotices", "signature" => $common_sig)
1726 , "groups.getGroupNotice" => array("function" => "getGroupNotice", "signature" => $common_sig)
1727 , "groups.addGroupNotice" => array("function" => "addGroupNotice", "signature" => $common_sig)
1728
1729
1730
1731
1732 ), false);
1733
1734 $s->functions_parameters_type = 'phpvals';
1735 if (isset($debugXMLRPC) && $debugXMLRPC > 0 && isset($debugXMLRPCFile) && $debugXMLRPCFile != "")
1736 {
1737 $s->setDebug($debugXMLRPC);
1738 }
1739 $s->service();
1740
1741 if (isset($debugXMLRPC) && $debugXMLRPC > 0 && isset($debugXMLRPCFile) && $debugXMLRPCFile != "")
1742 {
1743 $f = fopen($debugXMLRPCFile,"a");
1744 fwrite($f,"\n----- " . date("Y-m-d H:i:s") . " -----\n");
1745 $debugInfo = $s->serializeDebug();
1746 $debugInfo = split("\n",$debugInfo);
1747 unset($debugInfo[0]);
1748 unset($debugInfo[count($debugInfo) -1]);
1749 $debugInfo = join("\n",$debugInfo);
1750 fwrite($f,base64_decode($debugInfo));
1751 fclose($f);
1752 }
1753
1754 mysql_close($groupDBCon);
1755?>