Index: xmlrpc.php =================================================================== RCS file: /cvs/drupal/drupal/xmlrpc.php,v retrieving revision 1.9 diff -u -F^f -r1.9 xmlrpc.php --- xmlrpc.php 21 Aug 2004 06:42:34 -0000 1.9 +++ xmlrpc.php 14 Aug 2005 23:48:56 -0000 @@ -11,8 +11,5 @@ include_once 'includes/xmlrpc.inc'; include_once 'includes/xmlrpcs.inc'; -$functions = module_invoke_all('xmlrpc'); - -$server = new xmlrpc_server($functions); - +xmlrpc_server(module_invoke_all('xmlrpc')); ?> Index: includes/xmlrpc.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/xmlrpc.inc,v retrieving revision 1.12.4.2 diff -u -F^f -r1.12.4.2 xmlrpc.inc --- includes/xmlrpc.inc 29 Jun 2005 19:53:59 -0000 1.12.4.2 +++ includes/xmlrpc.inc 14 Aug 2005 23:48:56 -0000 @@ -1,1069 +1,420 @@ -// $Id: xmlrpc.inc,v 1.12.4.2 2005/06/29 19:53:59 dries Exp $ - - -// Copyright (c) 1999,2000,2001 Edd Dumbill. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// -// * Neither the name of the "XML-RPC for PHP" nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -// REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -// OF THE POSSIBILITY OF SUCH DAMAGE. - -if (!function_exists('xml_parser_create')) { - // Win 32 fix. From: "Leo West" - if($WINDIR) { - dl("php3_xml.dll"); - } else { - dl("xml.so"); - } -} - -$xmlrpcI4="i4"; -$xmlrpcInt="int"; -$xmlrpcBoolean="boolean"; -$xmlrpcDouble="double"; -$xmlrpcString="string"; -$xmlrpcDateTime="dateTime.iso8601"; -$xmlrpcBase64="base64"; -$xmlrpcArray="array"; -$xmlrpcStruct="struct"; - - -$xmlrpcTypes=array($xmlrpcI4 => 1, - $xmlrpcInt => 1, - $xmlrpcBoolean => 1, - $xmlrpcString => 1, - $xmlrpcDouble => 1, - $xmlrpcDateTime => 1, - $xmlrpcBase64 => 1, - $xmlrpcArray => 2, - $xmlrpcStruct => 3); - -$xmlrpcerr["unknown_method"]=1; -$xmlrpcstr["unknown_method"]="Unknown method"; -$xmlrpcerr["invalid_return"]=2; -$xmlrpcstr["invalid_return"]="Invalid return payload: enabling debugging to examine incoming payload"; -$xmlrpcerr["incorrect_params"]=3; -$xmlrpcstr["incorrect_params"]="Incorrect parameters passed to method"; -$xmlrpcerr["introspect_unknown"]=4; -$xmlrpcstr["introspect_unknown"]="Can't introspect: method unknown"; -$xmlrpcerr["http_error"]=5; -$xmlrpcstr["http_error"]="Didn't receive 200 OK from remote server."; -$xmlrpcerr["no_data"]=6; -$xmlrpcstr["no_data"]="No data received from server."; -$xmlrpcerr["no_ssl"]=7; -$xmlrpcstr["no_ssl"]="No SSL support compiled in."; -$xmlrpcerr["curl_fail"]=8; -$xmlrpcstr["curl_fail"]="CURL error"; - -$xmlrpcName="XML-RPC for PHP"; -$xmlrpcVersion="1.02"; - -// let user errors start at 800 -$xmlrpcerruser=800; -// let XML parse errors start at 100 -$xmlrpcerrxml=100; - -// used to store state during parsing -// quick explanation of components: -// st - used to build up a string for evaluation -// ac - used to accumulate values -// qt - used to decide if quotes are needed for evaluation -// cm - used to denote struct or array (comma needed) -// isf - used to indicate a fault -// lv - used to indicate "looking for a value": implements -// the logic to allow values with no types to be strings -// params - used to store parameters in method calls -// method - used to store method name - -$_xh=array(); - -function xmlrpc_se($parser, $name, $attrs) { - global $_xh, $xmlrpcDateTime, $xmlrpcString; - - switch($name) { - case "STRUCT": - case "ARRAY": - $_xh[$parser]['st'].="array("; - $_xh[$parser]['cm']++; - // this last line turns quoting off - // this means if we get an empty array we'll - // simply get a bit of whitespace in the eval - $_xh[$parser]['qt']=0; - break; - case "NAME": - $_xh[$parser]['st'].="'"; $_xh[$parser]['ac']=""; - break; - case "FAULT": - $_xh[$parser]['isf']=1; - break; - case "PARAM": - $_xh[$parser]['st']=""; - break; - case "VALUE": - $_xh[$parser]['st'].="new xmlrpcval("; - $_xh[$parser]['vt']=$xmlrpcString; - $_xh[$parser]['ac']=""; - $_xh[$parser]['qt']=0; - $_xh[$parser]['lv']=1; - // look for a value: if this is still 1 by the - // time we reach the first data segment then the type is string - // by implication and we need to add in a quote - break; - - case "I4": - case "INT": - case "STRING": - case "BOOLEAN": - case "DOUBLE": - case "DATETIME.ISO8601": - case "BASE64": - $_xh[$parser]['ac']=""; // reset the accumulator - - if ($name=="DATETIME.ISO8601" || $name=="STRING") { - $_xh[$parser]['qt']=1; - if ($name=="DATETIME.ISO8601") - $_xh[$parser]['vt']=$xmlrpcDateTime; - } else if ($name=="BASE64") { - $_xh[$parser]['qt']=2; - } else { - // No quoting is required here -- but - // at the end of the element we must check - // for data format errors. - $_xh[$parser]['qt']=0; - } - break; - case "MEMBER": - $_xh[$parser]['ac']=""; - break; - default: - break; - } - - if ($name!="VALUE") $_xh[$parser]['lv']=0; -} - -function xmlrpc_ee($parser, $name) { - global $_xh,$xmlrpcTypes,$xmlrpcString; - - switch($name) { - case "STRUCT": - case "ARRAY": - if ($_xh[$parser]['cm'] && substr($_xh[$parser]['st'], -1) ==',') { - $_xh[$parser]['st']=substr($_xh[$parser]['st'],0,-1); - } - $_xh[$parser]['st'].=")"; - $_xh[$parser]['vt']=strtolower($name); - $_xh[$parser]['cm']--; - break; - case "NAME": - $_xh[$parser]['st'].= $_xh[$parser]['ac'] . "' => "; - break; - case "BOOLEAN": - // special case here: we translate boolean 1 or 0 into PHP - // constants true or false - if ($_xh[$parser]['ac']=='1') - $_xh[$parser]['ac']="true"; - else - $_xh[$parser]['ac']="false"; - $_xh[$parser]['vt']=strtolower($name); - // Drop through intentionally. - case "I4": - case "INT": - case "STRING": - case "DOUBLE": - case "DATETIME.ISO8601": - case "BASE64": - if ($_xh[$parser]['qt']==1) { - // we use double quotes rather than single so backslashification works OK - $_xh[$parser]['st'].="'". $_xh[$parser]['ac'] ."'"; - } else if ($_xh[$parser]['qt']==2) { - $_xh[$parser]['st'].="base64_decode('". $_xh[$parser]['ac'] ."')"; - } else if ($name=="BOOLEAN") { - $_xh[$parser]['st'].=$_xh[$parser]['ac']; - } else { - // we have an I4, INT or a DOUBLE - // we must check that only 0123456789-. are characters here - if (!ereg("^\-?[0123456789 \t\.]+$", $_xh[$parser]['ac'])) { - // TODO: find a better way of throwing an error - // than this! - error_log("XML-RPC: non numeric value received in INT or DOUBLE"); - $_xh[$parser]['st'].="ERROR_NON_NUMERIC_FOUND"; - } else { - // it's ok, add it on - $_xh[$parser]['st'].=$_xh[$parser]['ac']; - } +/* + Drupal XML-RPC library. Based on the IXR - The Incutio XML-RPC Library - (c) Incutio Ltd 2002-2005 + Version 1.7 (beta) - Simon Willison, 23rd May 2005 + Site: http://scripts.incutio.com/xmlrpc/ + Manual: http://scripts.incutio.com/xmlrpc/manual.php + This version is made available under the GNU GPL License +*/ + +function xmlrpc_value($data, $type = FALSE) { + $xmlrpc_value = new stdClass(); + $xmlrpc_value->data = $data; + if (!$type) { + $type = xmlrpc_value_calculate_type($xmlrpc_value); + } + $xmlrpc_value->type = $type; + if ($type == 'struct') { + /* Turn all the values in the array in to new xmlrpc_values */ + foreach ($xmlrpc_value->data as $key => $value) { + $xmlrpc_value->data[$key] = xmlrpc_value($value); + } + } + if ($type == 'array') { + for ($i = 0, $j = count($xmlrpc_value->data); $i < $j; $i++) { + $xmlrpc_value->data[$i] = xmlrpc_value($xmlrpc_value->data[$i]); + } + } + return $xmlrpc_value; +} + +function xmlrpc_value_calculate_type(&$xmlrpc_value) { + $type = gettype($xmlrpc_value->data); + switch ($type) { + case 'boolean': case 'double': + return $type; + case 'integer': + return 'int'; + case 'array': + return range(0, count($xmlrpc_value->data) - 1) === array_keys($xmlrpc_value->data) ? 'array' : 'struct'; + case 'object': + if ($xmlrpc_value->data->is_date) { + return 'date'; } - $_xh[$parser]['ac']=""; $_xh[$parser]['qt']=0; - $_xh[$parser]['lv']=3; // indicate we've found a value - break; - case "VALUE": - // deal with a string value - if (strlen($_xh[$parser]['ac'])>0 && - $_xh[$parser]['vt']==$xmlrpcString) { - $_xh[$parser]['st'].="'". $_xh[$parser]['ac'] . "'"; + if ($xmlrpc_value->data->is_base64) { + return 'base64'; } - // This if() detects if no scalar was inside - // and pads an empty "". - if($_xh[$parser]['st'][strlen($_xh[$parser]['st'])-1] == '(') { - $_xh[$parser]['st'].= "''"; - } - $_xh[$parser]['st'].=", '" . $_xh[$parser]['vt'] . "')"; - if ($_xh[$parser]['cm']) $_xh[$parser]['st'].=","; - break; - case "MEMBER": - $_xh[$parser]['ac']=""; $_xh[$parser]['qt']=0; - break; - case "DATA": - $_xh[$parser]['ac']=""; $_xh[$parser]['qt']=0; - break; - case "PARAM": - $_xh[$parser]['params'][]=$_xh[$parser]['st']; - break; - case "METHODNAME": - $_xh[$parser]['method']=ereg_replace("^[\n\r\t ]+", "", - $_xh[$parser]['ac']); - break; - case "BOOLEAN": - // special case here: we translate boolean 1 or 0 into PHP - // constants true or false - if ($_xh[$parser]['ac']=='1') - $_xh[$parser]['ac']="true"; - else - $_xh[$parser]['ac']="false"; - $_xh[$parser]['vt']=strtolower($name); - break; + $xmlrpc_value->data = get_object_vars($xmlrpc_value->data); + return 'struct'; default: - break; - } - // if it's a valid type name, set the type - if (isset($xmlrpcTypes[strtolower($name)])) { - $_xh[$parser]['vt']=strtolower($name); - } - -} - -function xmlrpc_cd($parser, $data) -{ - global $_xh; - - //if (ereg("^[\n\r \t]+$", $data)) return; - // print "adding [${data}]\n"; - - if ($_xh[$parser]['lv']!=3) { - // "lookforvalue==3" means that we've found an entire value - // and should discard any further character data - if ($_xh[$parser]['lv']==1) { - // if we've found text and we're just in a then - // turn quoting on, as this will be a string - $_xh[$parser]['qt']=1; - // and say we've found a value - $_xh[$parser]['lv']=2; - } - // replace characters that eval would - // do special things with - $_xh[$parser]['ac'].= xmlrpc_escape_php($data); - } -} - -/** - * Escapes a piece of text so it can be placed literally between single quotes - * as a string inside PHP code. - * - * A single slash is converted to a double slash, a single quote converted to - * a slash followed by a quote. - */ -function xmlrpc_escape_php($data) { - return str_replace(array('\\', "'"), - array('\\\\', "\\'"), - $data); -} - -function xmlrpc_dh($parser, $data) -{ - global $_xh; - if (substr($data, 0, 1) == "&" && substr($data, -1, 1) == ";") { - if ($_xh[$parser]['lv']==1) { - $_xh[$parser]['qt']=1; - $_xh[$parser]['lv']=2; - } - $_xh[$parser]['ac'].= xmlrpc_escape_php($data); - } -} - -class xmlrpc_client { - var $path; - var $server; - var $port; - var $errno; - var $errstring; - var $debug=0; - var $username=""; - var $password=""; - var $cert=""; - var $certpass=""; - - function xmlrpc_client($path, $server, $port=0) { - $this->port=$port; $this->server=$server; $this->path=$path; - } - - function setDebug($in) { - if ($in) { - $this->debug=1; - } else { - $this->debug=0; - } - } - - function setCredentials($u, $p) { - $this->username=$u; - $this->password=$p; - } - - function setCertificate($cert, $certpass) { - $this->cert = $cert; - $this->certpass = $certpass; - } - - function send($msg, $timeout=0, $method='http') { - // where msg is an xmlrpcmsg - $msg->debug=$this->debug; - - if ($method == 'https') { - return $this->sendPayloadHTTPS($msg, - $this->server, - $this->port, $timeout, - $this->username, $this->password, - $this->cert, - $this->certpass); - } else { - return $this->sendPayloadHTTP10($msg, $this->server, $this->port, - $timeout, $this->username, - $this->password); - } - } - - function sendPayloadHTTP10($msg, $server, $port, $timeout=0, - $username="", $password="") { - if ($port==0) $port=80; - if($timeout>0) - $fp=fsockopen($server, $port, - $this->errno, $this->errstr, $timeout); - else - $fp=fsockopen($server, $port, - $this->errno, $this->errstr); - if (!$fp) { - return 0; - } - // Only create the payload if it was not created previously - if(empty($msg->payload)) $msg->createPayload(); - - // thanks to Grant Rauscher - // for this - $credentials=""; - if ($username!="") { - $credentials="Authorization: Basic " . - base64_encode($username . ":" . $password) . "\r\n"; - } - - $op= "POST " . $this->path. " HTTP/1.0\r\nUser-Agent: PHP XMLRPC 1.0\r\n" . - "Host: ". $this->server . "\r\n" . - $credentials . - "Content-Type: text/xml\r\nContent-Length: " . - strlen($msg->payload) . "\r\n\r\n" . - $msg->payload; - - if (!fputs($fp, $op, strlen($op))) { - $this->errstr="Write error"; - return 0; - } - $resp=$msg->parseResponseFile($fp); - fclose($fp); - return $resp; - } - - // contributed by Justin Miller - // requires curl to be built into PHP - function sendPayloadHTTPS($msg, $server, $port, $timeout=0, - $username="", $password="", $cert="", - $certpass="") { - global $xmlrpcerr, $xmlrpcstr; - if ($port == 0) $port = 443; - - // Only create the payload if it was not created previously - if(empty($msg->payload)) $msg->createPayload(); - - if (!function_exists("curl_init")) { - $r=new xmlrpcresp(0, $xmlrpcerr["no_ssl"], - $xmlrpcstr["no_ssl"]); - return $r; - } - - $curl = curl_init("https://" . $server . ':' . $port . - $this->path); - - curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); - // results into variable - if ($this->debug) { - curl_setopt($curl, CURLOPT_VERBOSE, 1); - } - curl_setopt($curl, CURLOPT_USERAGENT, 'PHP XMLRPC 1.0'); - // required for XMLRPC - curl_setopt($curl, CURLOPT_POST, 1); - // post the data - curl_setopt($curl, CURLOPT_POSTFIELDS, $msg->payload); - // the data - curl_setopt($curl, CURLOPT_HEADER, 1); - // return the header too - curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: text/xml')); - // required for XMLRPC - if ($timeout) curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : - $timeout - 1); - // timeout is borked - if ($username && $password) curl_setopt($curl, CURLOPT_USERPWD, - "$username:$password"); - // set auth stuff - if ($cert) curl_setopt($curl, CURLOPT_SSLCERT, $cert); - // set cert file - if ($certpass) curl_setopt($curl, CURLOPT_SSLCERTPASSWD, - $certpass); - // set cert password - - $result = curl_exec($curl); - - if (!$result) { - $resp=new xmlrpcresp(0, - $xmlrpcerr["curl_fail"], - $xmlrpcstr["curl_fail"]. ": ". - curl_error($curl)); - } else { - $resp = $msg->parseResponse($result); - } - curl_close($curl); - return $resp; - } - -} // end class xmlrpc_client - -class xmlrpcresp { - var $xv; - var $fn; - var $fs; - var $hdrs; - - function xmlrpcresp($val, $fcode=0, $fstr="") { - if ($fcode!=0) { - $this->xv=0; - $this->fn=$fcode; - $this->fs=htmlspecialchars($fstr); - } else { - $this->xv=$val; - $this->fn=0; - } - } - - function faultCode() { - if (isset($this->fn)) - return $this->fn; - else - return 0; - } - - function faultString() { return $this->fs; } - function value() { return $this->xv; } - - function serialize() { - $rs="\n"; - if ($this->fn) { - $rs.=" - - - - faultCode - " . $this->fn . " - - - faultString - " . $this->fs . " - - - - "; - } else { - $rs.="\n\n" . $this->xv->serialize() . - "\n"; - } - $rs.="\n"; - return $rs; + return string; } } -class xmlrpcmsg { - var $payload; - var $methodname; - var $params=array(); - var $debug=0; - - function xmlrpcmsg($meth, $pars=0) { - $this->methodname=$meth; - if (is_array($pars) && sizeof($pars)>0) { - for($i=0; $iaddParam($pars[$i]); - } - } - - function xml_header() { - return "\n\n"; - } - - function xml_footer() { - return "\n"; - } - - function createPayload() { - $this->payload=$this->xml_header(); - $this->payload.="" . $this->methodname . "\n"; - // if (sizeof($this->params)) { - $this->payload.="\n"; - for($i=0; $iparams); $i++) { - $p=$this->params[$i]; - $this->payload.="\n" . $p->serialize() . - "\n"; - } - $this->payload.="\n"; - // } - $this->payload.=$this->xml_footer(); - $this->payload=str_replace("\n", "\r\n", $this->payload); - } - - function method($meth="") { - if ($meth!="") { - $this->methodname=$meth; - } - return $this->methodname; - } - - function serialize() { - $this->createPayload(); - return $this->payload; - } - - function addParam($par) { $this->params[]=$par; } - function getParam($i) { return $this->params[$i]; } - function getNumParams() { return sizeof($this->params); } - - function parseResponseFile($fp) { - $ipd=""; - - while($data=fread($fp, 32768)) { - $ipd.=$data; - } - return $this->parseResponse($ipd); - } - - function parseResponse($data="") { - global $_xh,$xmlrpcerr,$xmlrpcstr; - - - $parser = drupal_xml_parser_create($data); - - $_xh[$parser]=array(); - - $_xh[$parser]['st']=""; - $_xh[$parser]['cm']=0; - $_xh[$parser]['isf']=0; - $_xh[$parser]['ac']=""; - $_xh[$parser]['qt']=""; - $_xh[$parser]['ha']=""; - $_xh[$parser]['ac']=""; - - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true); - xml_set_element_handler($parser, "xmlrpc_se", "xmlrpc_ee"); - xml_set_character_data_handler($parser, "xmlrpc_cd"); - xml_set_default_handler($parser, "xmlrpc_dh"); - $xmlrpc_value=new xmlrpcval; - - if ($this->debug) -##print "
---GOT---\n" . htmlspecialchars($data) .  "\n---END---\n
"; -##print "

---GOT---\n" . nl2br(htmlspecialchars($data)) . "\n---END---\n

"; - print "

---GOT---\n" . nl2br($data) . "\n---END---\n

"; - if ($data=="") { - error_log("No response received from server."); - $r=new xmlrpcresp(0, $xmlrpcerr["no_data"], - $xmlrpcstr["no_data"]); - xml_parser_free($parser); - return $r; - } - // see if we got an HTTP 200 OK, else bomb - // but only do this if we're using the HTTP protocol. - if (ereg("^HTTP",$data) && - !ereg("^HTTP/[0-9\.]+ 200 ", $data)) { - $errstr= substr($data, 0, strpos($data, "\n")-1); - error_log("HTTP error, got response: " .$errstr); - $r=new xmlrpcresp(0, $xmlrpcerr["http_error"], - $xmlrpcstr["http_error"]. " (" . $errstr . ")"); - xml_parser_free($parser); - return $r; - } - - // if using HTTP, then gotta get rid of HTTP headers here - // and we store them in the 'ha' bit of our data array - if (ereg("^HTTP", $data)) { - $ar=explode("\r\n", $data); - $newdata=""; - $hdrfnd=0; - for ($i=0; $i0) { - $_xh[$parser]['ha'].=$ar[$i]. "\r\n"; - } else { - $hdrfnd=1; - } - } else { - $newdata.=$ar[$i] . "\r\n"; - } +function xmlrpc_value_get_xml($xmlrpc_value) { + /* Return XML for this value */ + switch ($xmlrpc_value->type) { + case 'boolean': + return ''. (($xmlrpc_value->data) ? '1' : '0') .''; + break; + case 'int': + return ''. $xmlrpc_value->data .''; + break; + case 'double': + return ''. $xmlrpc_value->data .''; + break; + case 'string': + return ''. check_plain($xmlrpc_value->data) .''; + break; + case 'array': + $return = ''."\n"; + foreach ($xmlrpc_value->data as $item) { + $return .= ' '. xmlrpc_value_get_xml($item) ."\n"; } - $data=$newdata; - } - - if (!xml_parse($parser, $data, sizeof($data))) { - // thanks to Peter Kocks - if((xml_get_current_line_number($parser)) == 1) - $errstr = "XML error at line 1, check URL"; - else - $errstr = sprintf("XML error: %s at line %d", - xml_error_string(xml_get_error_code($parser)), - xml_get_current_line_number($parser)); - error_log($errstr); - $r=new xmlrpcresp(0, $xmlrpcerr["invalid_return"], - $xmlrpcstr["invalid_return"]); - xml_parser_free($parser); - return $r; - } - xml_parser_free($parser); - if ($this->debug) { - print "
---EVALING---[" .
-        strlen($_xh[$parser]['st']) . " chars]---\n" .
-        htmlspecialchars($_xh[$parser]['st']) . ";\n---END---
"; - } - if (strlen($_xh[$parser]['st'])==0) { - // then something odd has happened - // and it's time to generate a client side error - // indicating something odd went on - $r=new xmlrpcresp(0, $xmlrpcerr["invalid_return"], - $xmlrpcstr["invalid_return"]); - } else { - eval('$v=' . $_xh[$parser]['st'] . '; $allOK=1;'); - if ($_xh[$parser]['isf']) { - $f=$v->structmem("faultCode"); - $fs=$v->structmem("faultString"); - $r=new xmlrpcresp($v, $f->scalarval(), - $fs->scalarval()); - } else { - $r=new xmlrpcresp($v); + $return .= '
'; + return $return; + break; + case 'struct': + $return = ''."\n"; + foreach ($xmlrpc_value->data as $name => $value) { + $return .= " ". check_plain($name) .""; + $return .= xmlrpc_value_get_xml($value)."\n"; } - } - $r->hdrs=split("\r?\n", $_xh[$parser]['ha']); - return $r; + $return .= ''; + return $return; + break; + case 'date': + return xmlrpc_date_get_xml($xmlrpc_value->data); + break; + case 'base64': + return xmlrpc_base64_get_xml($xmlrpc_value->data); + break; } - + return FALSE; } -class xmlrpcval { - var $me=array(); - var $mytype=0; +function xmlrpc_message($message) { + $xmlrpc_message = new stdClass(); + $xmlrpc_message->array_structs = array(); // The stack used to keep track of the current array/struct + $xmlrpc_message->array_structs_types = array(); // Stack keeping track of if things are structs or array + $xmlrpc_message->current_struct_name = array(); // A stack as well + $xmlrpc_message->message = $message; + return $xmlrpc_message; +} + +function xmlrpc_message_parse(&$xmlrpc_message) { + // first remove the XML declaration + $xmlrpc_message->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $xmlrpc_message->message); + if (trim($xmlrpc_message->message) == '') { + return FALSE; + } + $xmlrpc_message->_parser = xml_parser_create(); + // Set XML parser to take the case of tags in to account + xml_parser_set_option($xmlrpc_message->_parser, XML_OPTION_CASE_FOLDING, FALSE); + // Set XML parser callback functions + /* do not set object. $xmlrpc_message does not have member functions any more + xml_set_object($xmlrpc_message->_parser, $xmlrpc_message); */ + xml_set_element_handler($xmlrpc_message->_parser, 'xmlrpc_message_tag_open', 'xmlrpc_message_tag_close'); + xml_set_character_data_handler($xmlrpc_message->_parser, 'xmlrpc_message_cdata'); + xmlrpc_message_set($xmlrpc_message); + if (!xml_parse($xmlrpc_message->_parser, $xmlrpc_message->message)) { + return FALSE; + } + xml_parser_free($xmlrpc_message->_parser); + // Grab the error messages, if any + $xmlrpc_message = xmlrpc_message_get(); + if ($xmlrpc_message->messagetype == 'fault') { + $xmlrpc_message->fault_code = $xmlrpc_message->params[0]['faultCode']; + $xmlrpc_message->fault_string = $xmlrpc_message->params[0]['faultString']; + } + return TRUE; +} + +function xmlrpc_message_set($value = NULL) { + static $xmlrpc_message; + if ($value) { + $xmlrpc_message = $value; + } + return $xmlrpc_message; +} + +function xmlrpc_message_get() { + return xmlrpc_message_set(); +} + +function xmlrpc_message_tag_open($parser, $tag, $attr) { + $xmlrpc_message = xmlrpc_message_get(); + $xmlrpc_message->current_tag_contents = ''; + switch($tag) { + case 'methodCall': + case 'methodResponse': + case 'fault': + $xmlrpc_message->messagetype = $tag; + break; + /* Deal with stacks of arrays and structs */ + case 'data': + $xmlrpc_message->array_structs_types[] = 'array'; + $xmlrpc_message->array_structs[] = array(); + break; + case 'struct': + $xmlrpc_message->array_structs_types[] = 'struct'; + $xmlrpc_message->array_structs[] = array(); + break; + } + xmlrpc_message_set($xmlrpc_message); +} - function xmlrpcval($val=-1, $type="") { - global $xmlrpcTypes; - $this->me=array(); - $this->mytype=0; - if ($val!=-1 || $type!="") { - if ($type=="") $type="string"; - if ($xmlrpcTypes[$type]==1) { - $this->addScalar($val,$type); +function xmlrpc_message_cdata($parser, $cdata) { + $xmlrpc_message = xmlrpc_message_get(); + $xmlrpc_message->current_tag_contents .= $cdata; + xmlrpc_message_set($xmlrpc_message); +} + +function xmlrpc_message_tag_close($parser, $tag) { + $xmlrpc_message = xmlrpc_message_get(); + $value_flag = FALSE; + switch($tag) { + case 'int': + case 'i4': + $value = (int)trim($xmlrpc_message->current_tag_contents); + $value_flag = TRUE; + break; + case 'double': + $value = (double)trim($xmlrpc_message->current_tag_contents); + $value_flag = TRUE; + break; + case 'string': + $value = $xmlrpc_message->current_tag_contents; + $value_flag = TRUE; + break; + case 'dateTime.iso8601': + $value = xmlrpc_date(trim($xmlrpc_message->current_tag_contents)); + // $value = $iso->getTimestamp(); + $value_flag = TRUE; + break; + case 'value': + // "If no type is indicated, the type is string." + if (trim($xmlrpc_message->current_tag_contents) != '') { + $value = (string)$xmlrpc_message->current_tag_contents; + $value_flag = TRUE; } - else if ($xmlrpcTypes[$type]==2) - $this->addArray($val); - else if ($xmlrpcTypes[$type]==3) - $this->addStruct($val); - } + break; + case 'boolean': + $value = (boolean)trim($xmlrpc_message->current_tag_contents); + $value_flag = TRUE; + break; + case 'base64': + $value = base64_decode(trim($xmlrpc_message->current_tag_contents)); + $value_flag = TRUE; + break; + /* Deal with stacks of arrays and structs */ + case 'data': + case 'struct': + $value = array_pop($xmlrpc_message->array_structs ); + array_pop($xmlrpc_message->array_structs_types); + $value_flag = TRUE; + break; + case 'member': + array_pop($xmlrpc_message->current_struct_name); + break; + case 'name': + $xmlrpc_message->current_struct_name[] = trim($xmlrpc_message->current_tag_contents); + break; + case 'methodName': + $xmlrpc_message->methodname = trim($xmlrpc_message->current_tag_contents); + break; } - - function addScalar($val, $type="string") { - global $xmlrpcTypes, $xmlrpcBoolean; - - if ($this->mytype==1) { - echo "xmlrpcval: scalar can have only one value
"; - return 0; - } - $typeof=$xmlrpcTypes[$type]; - if ($typeof!=1) { - echo "xmlrpcval: not a scalar type (${typeof})
"; - return 0; - } - - if ($type==$xmlrpcBoolean) { - if (strcasecmp($val,"true")==0 || - $val==1 || ($val==true && - strcasecmp($val,"false"))) { - $val=1; + if ($value_flag) { + if (count($xmlrpc_message->array_structs ) > 0) { + // Add value to struct or array + if ($xmlrpc_message->array_structs_types[count($xmlrpc_message->array_structs_types)-1] == 'struct') { + // Add to struct + $xmlrpc_message->array_structs [count($xmlrpc_message->array_structs )-1][$xmlrpc_message->current_struct_name[count($xmlrpc_message->current_struct_name)-1]] = $value; } else { - $val=0; + // Add to array + $xmlrpc_message->array_structs [count($xmlrpc_message->array_structs )-1][] = $value; } - } - - if ($this->mytype==2) { - // we're adding to an array here - $ar=$this->me["array"]; - $ar[]=new xmlrpcval($val, $type); - $this->me["array"]=$ar; } else { - // a scalar, so set the value and remember we're scalar - $this->me[$type]=$val; - $this->mytype=$typeof; - } - return 1; - } - - function addArray($vals) { - global $xmlrpcTypes; - if ($this->mytype!=0) { - echo "xmlrpcval: already initialized as a [" . - $this->kindOf() . "]
"; - return 0; - } - - $this->mytype=$xmlrpcTypes["array"]; - $this->me["array"]=$vals; - return 1; - } - - function addStruct($vals) { - global $xmlrpcTypes; - if ($this->mytype!=0) { - echo "xmlrpcval: already initialized as a [" . - $this->kindOf() . "]
"; - return 0; - } - $this->mytype=$xmlrpcTypes["struct"]; - $this->me["struct"]=$vals; - return 1; - } - - function dump($ar) { - reset($ar); - while ( list( $key, $val ) = each( $ar ) ) { - echo "$key => $val
"; - if ($key == 'array') - while ( list( $key2, $val2 ) = each( $val ) ) { - echo "-- $key2 => $val2
"; - } - } - } - - function kindOf() { - switch($this->mytype) { - case 3: - return "struct"; - break; - case 2: - return "array"; - break; - case 1: - return "scalar"; - break; - default: - return "undef"; + // Just add as a paramater + $xmlrpc_message->params[] = $value; } } - - function serializedata($typ, $val) { - $rs=""; - global $xmlrpcTypes, $xmlrpcBase64, $xmlrpcString, - $xmlrpcBoolean; - switch($xmlrpcTypes[$typ]) { - case 3: - // struct - $rs.="\n"; - reset($val); - while(list($key2, $val2)=each($val)) { - $rs.="${key2}\n"; - $rs.=$this->serializeval($val2); - $rs.="\n"; - } - $rs.=""; - break; - case 2: - // array - $rs.="\n\n"; - for($i=0; $iserializeval($val[$i]); - } - $rs.="\n"; - break; - case 1: - switch ($typ) { - case $xmlrpcBase64: - $rs.="<${typ}>" . base64_encode($val) . ""; - break; - case $xmlrpcBoolean: - $rs.="<${typ}>" . ($val ? "1" : "0") . ""; - break; - case $xmlrpcString: - $rs.="<${typ}>" . htmlspecialchars($val). ""; - break; - default: - $rs.="<${typ}>${val}"; - } - break; - default: - break; - } - return $rs; - } - - function serialize() { - return $this->serializeval($this); - } - - function serializeval($o) { - global $xmlrpcTypes; - $rs=""; - $ar=$o->me; - reset($ar); - list($typ, $val) = each($ar); - $rs.=""; - $rs.=$this->serializedata($typ, $val); - $rs.="\n"; - return $rs; - } - - function structmem($m) { - $nv=$this->me["struct"][$m]; - return $nv; - } - - function structreset() { - reset($this->me["struct"]); - } - - function structeach() { - return each($this->me["struct"]); - } - - function getval() { - // UNSTABLE - global $xmlrpcBoolean, $xmlrpcBase64; - reset($this->me); - list($a,$b)=each($this->me); - // contributed by I Sofer, 2001-03-24 - // add support for nested arrays to scalarval - // i've created a new method here, so as to - // preserve back compatibility - - if (is_array($b)) { - foreach ($b as $id => $cont) { - $b[$id] = $cont->scalarval(); - } - } - - // add support for structures directly encoding php objects - if (is_object($b)) { - $t = get_object_vars($b); - foreach ($t as $id => $cont) { - $t[$id] = $cont->scalarval(); - } - foreach ($t as $id => $cont) { - eval('$b->'.$id.' = $cont;'); - } - } - // end contrib - return $b; - } - - function scalarval() { - global $xmlrpcBoolean, $xmlrpcBase64; - reset($this->me); - list($a,$b)=each($this->me); - return $b; - } - - function scalartyp() { - global $xmlrpcI4, $xmlrpcInt; - reset($this->me); - list($a,$b)=each($this->me); - if ($a==$xmlrpcI4) - $a=$xmlrpcInt; - return $a; - } - - function arraymem($m) { - $nv=$this->me["array"][$m]; - return $nv; - } - - function arraysize() { - reset($this->me); - list($a,$b)=each($this->me); - return sizeof($b); + if (!in_array($tag, array("data", "struct", "member"))) { + $xmlrpc_message->current_tag_contents = ''; } + xmlrpc_message_set($xmlrpc_message); } -// date helpers -function iso8601_encode($timet, $utc=0) { - // return an ISO8601 encoded string - // really, timezones ought to be supported - // but the XML-RPC spec says: - // - // "Don't assume a timezone. It should be specified by the server in its - // documentation what assumptions it makes about timezones." - // - // these routines always assume localtime unless - // $utc is set to 1, in which case UTC is assumed - // and an adjustment for locale is made when encoding - if (!$utc) { - $t=strftime("%Y%m%dT%H:%M:%S", $timet); - } else { - if (function_exists("gmstrftime")) - // gmstrftime doesn't exist in some versions - // of PHP - $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet); - else { - $t=strftime("%Y%m%dT%H:%M:%S", $timet-date("Z")); - } - } - return $t; +function xmlrpc_request($method, $args) { + $xmlrpc_request = new stdClass(); + $xmlrpc_request->method = $method; + $xmlrpc_request->args = $args; + $xmlrpc_request->xml = << + +{$xmlrpc_request->method} + + +EOD; + foreach ($xmlrpc_request->args as $arg) { + $xmlrpc_request->xml .= ''; + $v = xmlrpc_value($arg); + $xmlrpc_request->xml .= xmlrpc_value_get_xml($v); + $xmlrpc_request->xml .= "\n"; + } + $xmlrpc_request->xml .= ''; + return $xmlrpc_request; +} + + +function xmlrpc_error($code = NULL, $message = NULL) { + static $xmlrpc_error; + if ($code) { + $xmlrpc_error = new stdClass(); + $xmlrpc_error->is_error = TRUE; + $xmlrpc_error->code = $code; + $xmlrpc_error->message = $message; + } + return $xmlrpc_error; +} + +function xmlrpc_error_get_xml($xmlrpc_error) { + return << + + + + + faultCode + {$xmlrpc_error->code} + + + faultString + {$xmlrpc_error->message} + + + + + + +EOD; +} + +function xmlrpc_date($time) { + $xmlrpc_date = new stdClass(); + $xmlrpc_date->is_date = TRUE; + // $time can be a PHP timestamp or an ISO one + if (is_numeric($time)) { + $xmlrpc_date->year = date('Y', $time); + $xmlrpc_date->month = date('m', $time); + $xmlrpc_date->day = date('d', $time); + $xmlrpc_date->hour = date('H', $time); + $xmlrpc_date->minute = date('i', $time); + $xmlrpc_date->second = date('s', $time); + } + else { + $xmlrpc_date->year = substr($time, 0, 4); + $xmlrpc_date->month = substr($time, 4, 2); + $xmlrpc_date->day = substr($time, 6, 2); + $xmlrpc_date->hour = substr($time, 9, 2); + $xmlrpc_date->minute = substr($time, 12, 2); + $xmlrpc_date->second = substr($time, 15, 2); + } + return $xmlrpc_date; +} + +function xmlrpc_date_get_xml($xmlrpc_date) { + return ''. $xmlrpc_date->year . $xmlrpc_date->month . $xmlrpc_date->day .'T'. $xmlrpc_date->hour .':'. $xmlrpc_date->minute .':'. $xmlrpc_date->second .''; +} + +function xmlrpc_base64($data) { + $xmlrpc_base64 = new stdClass(); + $xmlrpc_base64->is_base64 = TRUE; + $xmlrpc_base64->data = $data; + return $xmlrpc_base64; } -function iso8601_decode($idate, $utc=0) { - // return a timet in the localtime, or UTC - $t=0; - if (ereg("([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})", - $idate, $regs)) { - if ($utc) { - $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); - } else { - $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); - } - } - return $t; +function xmlrpc_base64_get_xml($xmlrpc_base64) { + return ''. base64_encode($xmlrpc_base64->data) .''; } -/***************************************************************** - * _xmlrpc_decode takes a message in PHP xmlrpc object format and * - * translates it into native PHP types. * - * * - * author: Dan Libby (dan@libby.com) * - *****************************************************************/ -function _xmlrpc_decode($xmlrpc_val) { - $kind = $xmlrpc_val->kindOf(); - - if($kind == "scalar") { - return $xmlrpc_val->scalarval(); - } - else if($kind == "array") { - $size = $xmlrpc_val->arraysize(); - $arr = array(); - - for($i = 0; $i < $size; $i++) { - $arr[]=_xmlrpc_decode($xmlrpc_val->arraymem($i)); - } - return $arr; +/** + * Perform an XML-RPC request. + * + * @param $url + * An absolute URL of the XML-RPC server + * @param $method + * The method to be executed + * @param ... + * A variable number of arguments of the method. + * @return + * The return value of the method on success or FALSE. On FALSE, see + * xmlrpc_errno() and xmlrpc_error_msg(). + */ +function xmlrpc() { + $args = func_get_args(); + $url = array_shift($args); + $method = array_shift($args); + $xmlrpc_request = xmlrpc_request($method, $args); +// $request .= "Content-Type: text/xml$r"; + $result = drupal_http_request($url, array("Content-Type" => "text/xml"), 'POST', $xmlrpc_request->xml); + if ($result->code != 200) { + xmlrpc_error(-$result->code, $result->error); + return FALSE; + } + $message = xmlrpc_message($result->data); + // Now parse what we've got back + if (!xmlrpc_message_parse($message)) { + // XML error + xmlrpc_error(-32700, t('parse error. not well formed')); + return FALSE; + } + // Is the message a fault? + if ($message->messagetype == 'fault') { + xmlrpc_error($message->faultcode, $message->faultstring); + return FALSE; } - else if($kind == "struct") { - $xmlrpc_val->structreset(); - $arr = array(); + // Message must be OK + return $message->params[0]; +} - while(list($key,$value)=$xmlrpc_val->structeach()) { - $arr[$key] = _xmlrpc_decode($value); - } - return $arr; +/** + * Perform multiple calls in one request if possible. + * + * @param $url + * An absolute URL of the XML-RPC server + * @param $calls + * An array of calls. Each call is an array, where the first element + * is the method name, further elements are the arguments. + * @return + * An array of results. + */ +function xmlrpc_multicall() { + $args = func_get_args(); + $url = $args[0]; + foreach ($args[1] as $call) { + $method = array_shift($call); + $calls[] = array( + 'methodName' => $method, + 'params' => $call + ); } + return xmlrpc($url, 'system.multicall', $calls); } -/**************************************************************** - * _xmlrpc_encode takes native php types and encodes them into * - * xmlrpc PHP object format. * - * BUG: All sequential arrays are turned into structs. I don't * - * know of a good way to determine if an array is sequential * - * only. * - * * - * feature creep -- could support more types via optional type * - * argument. * - * * - * author: Dan Libby (dan@libby.com) * - ****************************************************************/ -function _xmlrpc_encode($php_val) { - global $xmlrpcInt; - global $xmlrpcDouble; - global $xmlrpcString; - global $xmlrpcArray; - global $xmlrpcStruct; - global $xmlrpcBoolean; - - $type = gettype($php_val); - $xmlrpc_val = new xmlrpcval; - - switch($type) { - case "array": - case "object": - $arr = array(); - while (list($k,$v) = each($php_val)) { - $arr[$k] = _xmlrpc_encode($v); - } - $xmlrpc_val->addStruct($arr); - break; - case "integer": - $xmlrpc_val->addScalar($php_val, $xmlrpcInt); - break; - case "double": - $xmlrpc_val->addScalar($php_val, $xmlrpcDouble); - break; - case "string": - $xmlrpc_val->addScalar($php_val, $xmlrpcString); - break; - // - // Add support for encoding/decoding of booleans, since they are supported in PHP - case "boolean": - $xmlrpc_val->addScalar($php_val, $xmlrpcBoolean); - break; - // - case "unknown type": - default: - // giancarlo pinerolo - // it has to return - // an empty object in case (which is already - // at this point), not a boolean. - break; - } - return $xmlrpc_val; +/** + * Returns the last XML-RPC client error number + */ +function xmlrpc_errno() { + $error = xmlrpc_error(); + return $error->code; } +/** + * Returns the last XML-RPC client error message + */ +function xmlrpc_error_msg() { + $error = xmlrpc_error(); + return $error->message; +} ?> Index: includes/xmlrpcs.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/xmlrpcs.inc,v retrieving revision 1.9.4.1 diff -u -F^f -r1.9.4.1 xmlrpcs.inc --- includes/xmlrpcs.inc 29 Jun 2005 19:53:59 -0000 1.9.4.1 +++ includes/xmlrpcs.inc 14 Aug 2005 23:48:56 -0000 @@ -1,301 +1,287 @@ -// $Id: xmlrpcs.inc,v 1.9.4.1 2005/06/29 19:53:59 dries Exp $ - -// Copyright (c) 1999,2000,2001 Edd Dumbill. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// -// * Neither the name of the "XML-RPC for PHP" nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -// REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -// OF THE POSSIBILITY OF SUCH DAMAGE. - -// XML RPC Server class -// requires: xmlrpc.inc - -// listMethods: either a string, or nothing -$_xmlrpcs_listMethods_sig=array(array($xmlrpcArray, $xmlrpcString), - array($xmlrpcArray)); -$_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch'; -function _xmlrpcs_listMethods($server, $m) { - global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap; - $v=new xmlrpcval(); - $dmap=$server->dmap; - $outAr=array(); - for(reset($dmap); list($key, $val)=each($dmap); ) { - $outAr[]=new xmlrpcval($key, "string"); - } - $dmap=$_xmlrpcs_dmap; - for(reset($dmap); list($key, $val)=each($dmap); ) { - $outAr[]=new xmlrpcval($key, "string"); - } - $v->addArray($outAr); - return new xmlrpcresp($v); -} - -$_xmlrpcs_methodSignature_sig=array(array($xmlrpcArray, $xmlrpcString)); -$_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)'; -function _xmlrpcs_methodSignature($server, $m) { - global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap; - - $methName=$m->getParam(0); - $methName=$methName->scalarval(); - if (ereg("^system\.", $methName)) { - $dmap=$_xmlrpcs_dmap; $sysCall=1; - } else { - $dmap=$server->dmap; $sysCall=0; - } - // print "\n"; - if (isset($dmap[$methName])) { - if ($dmap[$methName]["signature"]) { - $sigs=array(); - $thesigs=$dmap[$methName]["signature"]; - for($i=0; $igetParam(0); - $methName=$methName->scalarval(); - if (ereg("^system\.", $methName)) { - $dmap=$_xmlrpcs_dmap; $sysCall=1; - } else { - $dmap=$server->dmap; $sysCall=0; - } - // print "\n"; - if (isset($dmap[$methName])) { - if ($dmap[$methName]["docstring"]) { - $r=new xmlrpcresp(new xmlrpcval($dmap[$methName]["docstring"]), - "string"); - } else { - $r=new xmlrpcresp(new xmlrpcval("", "string")); + +function xmlrpc_server($callbacks) { + $xmlrpc_server = new stdClass(); + $defaults = array( + 'system.multicall' => 'xmlrpc_server_multicall', + array( + 'system.methodSignature', + 'xmlrpc_server_method_signature', + array('array', 'string'), + 'Returns an array describing the return type and required parameters of a method' + ), + array( + 'system.getCapabilities', + 'xmlrpc_server_get_capabilities', + array('struct'), + 'Returns a struct describing the XML-RPC specifications supported by this server' + ), + array( + 'system.listMethods', + 'xmlrpc_server_list_methods', + array('array'), + 'Returns an array of available methods on this server'), + array( + 'system.methodHelp', + 'xmlrpc_server_method_help', + array('string', 'string'), + 'Returns a documentation string for the specified method') + ); + // the order matters in the next line. which is the best? + foreach (array_merge($defaults, (array)$callbacks) as $key => $callback) { + // we could check for is_array($callback) + if (is_int($key)) { + $method = $callback[0]; + $xmlrpc_server->callbacks[$method] = $callback[1]; + $xmlrpc_server->signatures[$method] = $callback[2]; + $xmlrpc_server->help[$method] = $callback[3]; } - } else { - $r=new xmlrpcresp(0, - $xmlrpcerr["introspect_unknown"], - $xmlrpcstr["introspect_unknown"]); - } - return $r; -} - -$_xmlrpcs_dmap=array( - "system.listMethods" => - array("function" => "_xmlrpcs_listMethods", - "signature" => $_xmlrpcs_listMethods_sig, - "docstring" => $_xmlrpcs_listMethods_doc), - "system.methodHelp" => - array("function" => "_xmlrpcs_methodHelp", - "signature" => $_xmlrpcs_methodHelp_sig, - "docstring" => $_xmlrpcs_methodHelp_doc), - "system.methodSignature" => - array("function" => "_xmlrpcs_methodSignature", - "signature" => $_xmlrpcs_methodSignature_sig, - "docstring" => $_xmlrpcs_methodSignature_doc) - ); - -$_xmlrpc_debuginfo=""; -function xmlrpc_debugmsg($m) { - global $_xmlrpc_debuginfo; - $_xmlrpc_debuginfo=$_xmlrpc_debuginfo . $m . "\n"; -} - -class xmlrpc_server { - var $dmap=array(); - - function xmlrpc_server($dispMap, $serviceNow=1) { - // dispMap is a despatch array of methods - // mapped to function names and signatures - // if a method - // doesn't appear in the map then an unknown - // method error is generated - $this->dmap=$dispMap; - if ($serviceNow) { - $this->service(); + else { + $xmlrpc_server->callbacks[$key] = $callback; + $xmlrpc_server->signatures[$key] = ''; + $xmlrpc_server->help[$key] = ''; } } - function serializeDebug() { - global $_xmlrpc_debuginfo; - if ($_xmlrpc_debuginfo!="") - return "\n"; - else - return ""; - } - - function service() { - $r=$this->parseRequest(); - $payload="\n" . - $this->serializeDebug() . - $r->serialize(); - Header("Content-Type: text/xml\r\nContent-Length: " . - strlen($payload)); - print $payload; - } - - function verifySignature($in, $sig) { - for($i=0; $igetNumParams()+1) { - $itsOK=1; - for($n=0; $n<$in->getNumParams(); $n++) { - $p=$in->getParam($n); - // print "\n"; - if ($p->kindOf() == "scalar") { - $pt=$p->scalartyp(); - } else { - $pt=$p->kindOf(); - } - // $n+1 as first type of sig is return type - if ($pt != $cursig[$n+1]) { - $itsOK=0; - $pno=$n+1; $wanted=$cursig[$n+1]; $got=$pt; - break; - } - } - if ($itsOK) - return array(1); - } - } - return array(0, "Wanted ${wanted}, got ${got} at param ${pno})"); + $data = file_get_contents('php://input'); + if (!$data) { + die('XML-RPC server accepts POST requests only.'); + } + $xmlrpc_server->message = xmlrpc_message($data); + if (!xmlrpc_message_parse($xmlrpc_server->message)) { + xmlrpc_server_error(-32700, t('parse error. not well formed')); + } + if ($xmlrpc_server->message->messagetype != 'methodCall') { + xmlrpc_server_error(-32600, t('server error. invalid xml-rpc. not conforming to spec. Request must be a method_call')); + } + xmlrpc_server_set($xmlrpc_server); + $result = xmlrpc_server_call($xmlrpc_server, $xmlrpc_server->message->methodname, $xmlrpc_server->message->params); + // Is the result an error? + if ($result->is_error) { + xmlrpc_server_error($result); + } + // Encode the result + $r = xmlrpc_value($result); + // Create the XML + $xml = ' + + + + '. + xmlrpc_value_get_xml($r) + .' + + + + +'; + // Send it + xmlrpc_server_output($xml); +} + +function xmlrpc_server_error($error, $message = false) { + // Accepts either an error object or an error code and message + if ($message && !is_object($error)) { + $error = xmlrpc_error($error, $message); } + xmlrpc_server_output(xmlrpc_error_get_xml($error)); +} - function parseRequest($data="") { - global $_xh; - global $xmlrpcerr, $xmlrpcstr, $xmlrpcerrxml, $_xmlrpcs_dmap; +function xmlrpc_server_output($xml) { + $xml = ''."\n". $xml; + header('Connection: close'); + header('Content-Length: '. strlen($xml)); + header('Content-Type: text/xml'); + header('Date: '.date('r')); + echo $xml; + exit; +} + +function xmlrpc_server_list_methods() { + $xmlrpc_server = xmlrpc_server_get(); + return array_keys($xmlrpc_server->callbacks); +} +function xmlrpc_server_set($xmlrpc_server = NULL) { + static $server; + if (!isset($server)) { + $server = $xmlrpc_server; + } + return $server; +} +function xmlrpc_server_get() { + return xmlrpc_server_set(); +} - if ($data=="") { - $data=$GLOBALS["HTTP_RAW_POST_DATA"]; - } - $parser = drupal_xml_parser_create($data); - - $_xh[$parser]=array(); - $_xh[$parser]['st']=""; - $_xh[$parser]['cm']=0; - $_xh[$parser]['isf']=0; - $_xh[$parser]['params']=array(); - $_xh[$parser]['method']=""; - - // decompose incoming XML into request structure - - xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true); - xml_set_element_handler($parser, "xmlrpc_se", "xmlrpc_ee"); - xml_set_character_data_handler($parser, "xmlrpc_cd"); - xml_set_default_handler($parser, "xmlrpc_dh"); - if (!xml_parse($parser, $data, 1)) { - // return XML error as a faultCode - $r=new xmlrpcresp(0, - $xmlrpcerrxml+xml_get_error_code($parser), - sprintf("XML error: %s at line %d", - xml_error_string(xml_get_error_code($parser)), - xml_get_current_line_number($parser))); - xml_parser_free($parser); - } else { - xml_parser_free($parser); - $m=new xmlrpcmsg($_xh[$parser]['method']); - // now add parameters in - $plist=""; - for($i=0; $i\n"; - $plist.="$i - " . $_xh[$parser]['params'][$i]. " \n"; - $m->addParam(eval('return '. $_xh[$parser]['params'][$i] .';')); +function xmlrpc_server_multicall($methodcalls) { + // See http://www.xmlrpc.com/discuss/msgReader$1208 + $return = array(); + $xmlrpc_server = xmlrpc_server_get(); + foreach ($methodcalls as $call) { + $method = $call['methodName']; + $params = $call['params']; + if ($method == 'system.multicall') { + $result = xmlrpc_error(-32600, t('Recursive calls to system.multicall are forbidden')); + } else { + $result = xmlrpc_server_call($xmlrpc_server, $method, $params); } - // uncomment this to really see what the server's getting! - // xmlrpc_debugmsg($plist); - // now to deal with the method - $methName=$_xh[$parser]['method']; - if (ereg("^system\.", $methName)) { - $dmap=$_xmlrpcs_dmap; $sysCall=1; + if ($result->is_error) { + $return[] = array( + 'faultCode' => $result->code, + 'faultString' => $result->message + ); } else { - $dmap=$this->dmap; $sysCall=0; + $return[] = $result; } - if (isset($dmap[$methName]['function'])) { - // dispatch if exists - if (isset($dmap[$methName]['signature'])) { - $sr=$this->verifySignature($m, - $dmap[$methName]['signature'] ); + } + return $return; +} + + +function xmlrpc_server_get_capabilities() { + return array( + 'xmlrpc' => array( + 'specUrl' => 'http://www.xmlrpc.com/spec', + 'specVersion' => 1 + ), + 'faults_interop' => array( + 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', + 'specVersion' => 20010516 + ), + 'system.multicall' => array( + 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', + 'specVersion' => 1 + ), + 'introspection' => array( + 'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html', + 'specVersion' => 1 + ) + ); +} + + +function xmlrpc_server_call($xmlrpc_server, $methodname, $args) { + // Make sure it's in an array + if ($args && !is_array($args)) { + $args = array($args); + } + // Over-rides default call method, adds signature check + if (!isset($xmlrpc_server->callbacks[$methodname])) { + return xmlrpc_error(-32601, t('server error. requested method %methodname not specified.',array("%methodname" => $xmlrpc_server->message->methodname))); + } + $method = $xmlrpc_server->callbacks[$methodname]; + $signature = $xmlrpc_server->signatures[$methodname]; + + $ok = true; + if (is_array($signature)) { + $return_type = array_shift($signature); + // Check the number of arguments + if (count($args) != count($signature)) { + return xmlrpc_error(-32602, t('server error. wrong number of method parameters')); + } + // Check the argument types + foreach ($signature as $key => $type) { + $arg = $args[$key]; + switch ($type) { + case 'int': + case 'i4': + if (is_array($arg) || !is_int($arg)) { + $ok = false; + } + break; + case 'base64': + case 'string': + if (!is_string($arg)) { + $ok = false; + } + break; + case 'boolean': + if ($arg !== false && $arg !== true) { + $ok = false; + } + break; + case 'float': + case 'double': + if (!is_float($arg)) { + $ok = false; + } + break; + case 'date': + case 'dateTime.iso8601': + if (!$arg->is_date) { + $ok = false; + } + break; } - if ( (!isset($dmap[$methName]['signature'])) - || $sr[0]) { - $f = $dmap[$methName]['function']; - // if no signature or correct signature - if ($sysCall) { - $r = $f($this, $m); - } else { - $r = $f($m); - } - } else { - $r=new xmlrpcresp(0, - $xmlrpcerr["incorrect_params"], - $xmlrpcstr["incorrect_params"].": ". $sr[1]); + if (!$ok) { + return xmlrpc_error(-32602, t('server error. invalid method parameters')); } - } else { - // else prepare error response - $r=new xmlrpcresp(0, - $xmlrpcerr["unknown_method"], - $xmlrpcstr["unknown_method"]); } } - return $r; + // It passed the test - run the "real" method call + if (!isset($xmlrpc_server->callbacks[$methodname])) { + return xmlrpc_error(-32601, t('server error. requested method %methodname does not exist.',array("%methodname" => $methodname))); + } + + $method = $xmlrpc_server->callbacks[$methodname]; + // Perform the callback and send the response +/* + if (count($args) == 1) { + // If only one paramater just send that instead of the whole array + $args = $args[0]; + } +*/ + if (!function_exists($method)) { + return xmlrpc_error(-32601, t('server error. requested function %method does not exist.', array("%method" => $method))); } + // Call the function + return call_user_func_array($method, $args); +} - function echoInput() { - - // a debugging routine: just echos back the input - // packet as a string value - - $r=new xmlrpcresp; - $r->xv=new xmlrpcval( "'Aha said I: '" . $GLOBALS["HTTP_RAW_POST_DATA"], "string"); - print $r->serialize(); +function xmlrpc_server_method_signature($xmlrpc_server, $methodname) { + if (!isset($xmlrpc_server->callbacks[$methodname])) { + return xmlrpc_error(-32601, t('server error. requested method %methodname not specified.', array("%methodname" => $methodname))); + } + if (!is_array($xmlrpc_server->signature[$methodname])) { + return xmlrpc_error(-32601, t('server error. requested method %methodname signature not specified.', array("%methodname" => $methodname))); + } + // We should be returning an array of types + $return = array(); + foreach ($xmlrpc_server->signatures[$methodname] as $type) { + switch ($type) { + case 'string': + $return[] = 'string'; + break; + case 'int': + case 'i4': + $return[] = 42; + break; + case 'double': + $return[] = 3.1415; + break; + case 'dateTime.iso8601': + $return[] = xmlrpc_date(time()); + break; + case 'boolean': + $return[] = true; + break; + case 'base64': + $return[] =xmlrpc_base64('base64'); + break; + case 'array': + $return[] = array('array'); + break; + case 'struct': + $return[] = array('struct' => 'struct'); + break; + } } + return $return; +} + +function xmlrpc_server_method_help($xmlrpc_server, $method) { + return $xmlrpc_server->help[$method]; } ?> Index: modules/blogapi.module =================================================================== RCS file: /cvs/drupal/drupal/modules/blogapi.module,v retrieving revision 1.33 diff -u -F^f -r1.33 blogapi.module --- modules/blogapi.module 18 Oct 2004 18:40:53 -0000 1.33 +++ modules/blogapi.module 14 Aug 2005 23:48:56 -0000 @@ -1,5 +1,5 @@ 'Blogger API', '%metaweblogAPI' => 'MetaWeblog API', '%moveabletype' => 'Moveable Type API')); + return t('

This module adds support for several XML-RPC based blogging APIs. Specifically, it currently implements the %bloggerAPI, %metaweblogAPI, and most of the %moveabletype extensions. This allows users to contribute to Drupal using external GUI applications, which can often offer richer functionality that online forms based editing.

This module also allows site administrators to configure which node types can be posted via the external applications. So, for instance, users can post forum topics as well as blog posts. Where supported, the external applications will display each node type as a separate "blog".

', array('%bloggerAPI' => 'Blogger API', '%metaweblogAPI' => 'MetaWeblog API', '%moveabletype' => 'Movable Type API. ')); case 'admin/modules#description': - return t('Enable users to post using applications that support XML-RPC blog APIs.'); + return t('Allows users to post content using applications that support XML-RPC blog APIs.'); } } @@ -22,49 +22,128 @@ function blogapi_help($section) { * Implementation of hook_xmlrpc(). */ function blogapi_xmlrpc() { - $methods = array('blogger.getUsersBlogs' => array('function' => 'blogapi_get_users_blogs'), - 'blogger.getUserInfo' => array('function' => 'blogapi_get_user_info'), - 'blogger.newPost' => array('function' => 'blogapi_new_post'), - 'blogger.editPost' => array('function' => 'blogapi_edit_post'), - 'blogger.deletePost' => array('function' => 'blogapi_delete_post'), - 'blogger.getRecentPosts' => array('function' => 'blogapi_get_recent_posts'), - 'metaWeblog.newPost' => array('function' => 'blogapi_new_post'), - 'metaWeblog.editPost' => array('function' => 'blogapi_edit_post'), - 'metaWeblog.getPost' => array('function' => 'blogapi_get_post'), - 'metaWeblog.newMediaObject' => array('function' => 'blogapi_new_media_object'), - 'metaWeblog.getCategories' => array('function' => 'blogapi_get_category_list'), - 'metaWeblog.getRecentPosts' => array('function' => 'blogapi_get_recent_posts'), - 'mt.getRecentPostTitles' => array('function' => 'blogapi_get_recent_post_titles'), - 'mt.getCategoryList' => array('function' => 'blogapi_get_category_list'), - 'mt.getPostCategories' => array('function' => 'blogapi_get_post_categories'), - 'mt.setPostCategories' => array('function' => 'blogapi_set_post_categories'), - 'mt.supportedMethods' => array('function' => 'blogapi_supported_methods'), - 'mt.supportedTextFilters' => array('function' => 'blogapi_supported_text_filters'), - 'mt.getTrackbackPings' => array('function' => 'blogapi_get_trackback_pings'), - 'mt.publishPost' => array('function' => 'blogapi_publish_post') - ); - - return $methods; + return array( + array( + 'blogger.getUsersBlogs', + 'blogapi_blogger_get_users_blogs', + array('array', 'string', 'string', 'string'), + t('Returns a list of weblogs to which an author has posting privileges.')), + array( + 'blogger.getUserInfo', + 'blogapi_blogger_get_user_info', + array('struct', 'string', 'string', 'string'), + t('Returns information about an author in the system.')), + array( + 'blogger.newPost', + 'blogapi_blogger_new_post', + array('string', 'string', 'string', 'string', 'string', 'string', 'boolean'), + t('Creates a new post, and optionally publishes it.')), + array( + 'blogger.editPost', + 'blogapi_blogger_edit_post', + array('boolean', 'string', 'string', 'string', 'string', 'string', 'boolean'), + t('Updates the information about an existing post.')), + array( + 'blogger.getPost', + 'blogapi_blogger_get_post', + array('struct', 'string', 'string', 'string', 'string'), + t('Returns information about a specific post.')), + array( + 'blogger.deletePost', + 'blogapi_blogger_delete_post', + array('boolean', 'string', 'string', 'string', 'string', 'boolean'), + t('Deletes a post.')), + array( + 'blogger.getRecentPosts', + 'blogapi_blogger_get_recent_posts', + array('array', 'string', 'string', 'string', 'string', 'int'), + t('Returns a list of the most recent posts in the system.')), + array( + 'metaWeblog.newPost', + 'blogapi_metaweblog_new_post', + array('string', 'string', 'string', 'string', 'struct', 'boolean'), + t('Creates a new post, and optionally publishes it.')), + array( + 'metaWeblog.editPost', + 'blogapi_metaweblog_edit_post', + array('boolean', 'string', 'string', 'string', 'struct', 'boolean'), + t('Updates information about an existing post.')), + array( + 'metaWeblog.getPost', + 'blogapi_metaweblog_get_post', + array('struct', 'string', 'string', 'string'), + t('Returns information about a specific post.')), + array( + 'metaWeblog.newMediaObject', + 'blogapi_metaweblog_new_media_object', + array('string', 'string', 'string', 'string', 'struct'), + t('Uploads a file to your webserver.')), + array( + 'metaWeblog.getCategories', + 'blogapi_metaweblog_get_category_list', + array('struct', 'string', 'string', 'string'), + t('Returns a list of all categories to which the post is assigned.')), + array( + 'metaWeblog.getRecentPosts', + 'blogapi_metaweblog_get_recent_posts', + array('array', 'string', 'string', 'string', 'int'), + t('Returns a list of the most recent posts in the system.')), + array( + 'mt.getRecentPostTitles', + 'blogapi_mt_get_recent_post_titles', + array('array', 'string', 'string', 'string', 'int'), + t('Returns a bandwidth-friendly list of the most recent posts in the system.')), + array( + 'mt.getCategoryList', + 'blogapi_mt_get_category_list', + array('array', 'string', 'string', 'string'), + t('Returns a list of all categories defined in the weblog.')), + array( + 'mt.getPostCategories', + 'blogapi_mt_get_post_categories', + array('array', 'string', 'string', 'string'), + t('Returns a list of all categories to which the post is assigned.')), + array( + 'mt.setPostCategories', + 'blogapi_mt_set_post_categories', + array('boolean', 'string', 'string', 'string', 'array'), + t('Sets the categories for a post.')), + array( + 'mt.supportedMethods', + 'xmlrpc_server_list_methods', + array('array'), + t('Retrieve information about the XML-RPC methods supported by the server.')), + array( + 'mt.supportedTextFilters', + 'blogapi_mt_supported_text_filters', + array('array'), + t('Retrieve information about the text formatting plugins supported by the server.')), + array( + 'mt.getTrackbackPings', + 'blogapi_mt_get_trackback_pings', + array('array', 'string'), + t('Retrieve the list of TrackBack pings posted to a particular entry. This could be used to programmatically retrieve the list of pings for a particular entry, then iterate through each of those pings doing the same, until one has built up a graph of the web of entries referencing one another on a particular topic.')), + array( + 'mt.publishPost', + 'blogap_mti_publish_post', + array('boolean', 'string', 'string', 'string'), + t('Publish (rebuild) all of the static files related to an entry from your weblog. Equivalent to saving an entry in the system (but without the ping).'))); } /** * Blogging API callback. Finds the URL of a user's blog. */ -function blogapi_get_users_blogs($req_params) { - $params = blogapi_convert($req_params); - // Remove unused appkey from bloggerAPI. - if (count($params) == 6) { - $params = array_slice($params, 1); - } - $user = blogapi_validate_user($params[1], $params[2]); +function blogapi_blogger_get_users_blogs($appid, $username, $password) { + + $user = blogapi_validate_user($username, $password); if ($user->uid) { - $struct = new xmlrpcval(array('url' => new xmlrpcval(url('blog/' . $user->uid, NULL, NULL, true)), - 'blogid' => new xmlrpcval($user->uid), - 'blogName' => new xmlrpcval($user->name . "'s blog")), - 'struct'); - $resp = new xmlrpcval(array($struct), 'array'); - return new xmlrpcresp($resp); + $types = _blogapi_get_node_types(); + $structs = array(); + foreach ($types as $type) { + $structs[] = array('url' => url('blog/' . $user->uid, NULL, NULL, true), 'blogid' => $type, 'blogName' => $user->name . ": " . $type); + } + return $structs; } else { return blogapi_error($user); @@ -74,21 +153,18 @@ function blogapi_get_users_blogs($req_pa /** * Blogging API callback. Returns profile information about a user. */ -function blogapi_get_user_info($req_params) { - $params = blogapi_convert($req_params); - - $user = blogapi_validate_user($params[1], $params[2]); +function blogapi_blogger_get_user_info($appkey, $username, $password) { + $user = blogapi_validate_user($username, $password); if ($user->uid) { $name = explode(' ', $user->realname ? $user->realname : $user->name, 2); - $struct = new xmlrpcval(array('userid' => new xmlrpcval($user->uid, 'string'), - 'lastname' => new xmlrpcval($name[1], 'string'), - 'firstname' => new xmlrpcval($name[0], 'string'), - 'nickname' => new xmlrpcval($user->name, 'string'), - 'email' => new xmlrpcval($user->mail, 'string'), - 'url' => new xmlrpcval(url('blog/view/' . $user->uid, NULL, NULL, true), 'string')), - 'struct'); - return new xmlrpcresp($struct); + return array( + 'userid' => $user->uid, + 'lastname' => $name[1], + 'firstname' => $name[0], + 'nickname' => $user->name, + 'email' => $user->mail, + 'url' => url('blog/' . $user->uid, NULL, NULL, true)); } else { return blogapi_error($user); @@ -98,21 +174,16 @@ function blogapi_get_user_info($req_para /** * Blogging API callback. Inserts a new blog post as a node. */ -function blogapi_new_post($req_params) { - $params = blogapi_convert($req_params); - - // Remove unused appkey from bloggerAPI. - if (count($params) == 6) { - $params = array_slice($params, 1); - } - - $user = blogapi_validate_user($params[1], $params[2]); +function blogapi_blogger_new_post($appkey, $blogid, $username, $password, $content, $publish) { + $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } $edit = array(); - $edit['type'] = 'blog'; + $edit['type'] = _blogapi_blogid($blogid); + // get the node type defaults + $node_type_default = variable_get('node_options_'. $edit['type'], array('status', 'promote')); $edit['uid'] = $user->uid; $edit['name'] = $user->name; $edit['promote'] = variable_get('node_promote_blog', 1); @@ -120,17 +191,17 @@ function blogapi_new_post($req_params) { $edit['moderate'] = variable_get('node_moderate_blog', 0); $edit['revision'] = variable_get('node_revision_blog', 0); $edit['format'] = FILTER_FORMAT_DEFAULT; - $edit['status'] = $params[4]; + $edit['status'] = $publish; // check for bloggerAPI vs. metaWeblogAPI - if (is_array($params[3])) { - $edit['title'] = $params[3]['title']; - $edit['body'] = $params[3]['description']; - _blogapi_mt_extra($edit, $params[3]); + if (is_array($content)) { + $edit['title'] = $content['title']; + $edit['body'] = $content['description']; + _blogapi_mt_extra($edit, $content); } else { - $edit['title'] = blogapi_blogger_title($params[3]); - $edit['body'] = $params[3]; + $edit['title'] = blogapi_blogger_title($content); + $edit['body'] = $content; } if (!valid_input_data($edit['title'], $edit['body'])) { @@ -150,7 +221,7 @@ function blogapi_new_post($req_params) { $nid = node_save($node); if ($nid) { watchdog('special', t('%type: added %title using blog API.', array('%type' => ''. t($node->type) .'', '%title' => "$node->title")), l(t('view'), "node/$nid")); - return new xmlrpcresp(new xmlrpcval($nid, 'string')); + return $nid; } return blogapi_error(t('Error storing post.')); @@ -159,41 +230,39 @@ function blogapi_new_post($req_params) { /** * Blogging API callback. Modifies the specified blog node. */ -function blogapi_edit_post($req_params) { - $params = blogapi_convert($req_params); - if (count($params) == 6) { - $params = array_slice($params, 1); - } +function blogapi_blogger_edit_post($appkey, $postid, $username, $password, $content, $publish) { - $user = blogapi_validate_user($params[1], $params[2]); + $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } - $node = node_load(array('nid' => $params[0])); + $node = node_load(array('nid' => $postid)); if (!$node) { return blogapi_error(message_na()); } + // Let the teaser be re-generated. + unset($node->teaser); if (!node_access('update', $node)) { return blogapi_error(message_access()); } - $node->status = $params[4]; + $node->status = $publish; // check for bloggerAPI vs. metaWeblogAPI - if (is_array($params[3])) { - $node->title = $params[3]['title']; - $node->body = $params[3]['description']; - _blogapi_mt_extra($node, $params[3]); + if (is_array($content)) { + $node->title = $content['title']; + $node->body = $content['description']; + _blogapi_mt_extra($node, $content); } else { - $node->title = blogapi_blogger_title($params[3]); - $node->body = $params[3]; + $node->title = blogapi_blogger_title($content); + $node->body = $content; } - if (!valid_input_data($title, $body)) { + if (!valid_input_data($node->title, $node->body)) { return blogapi_error(t('Terminated request because of suspicious input data.')); } @@ -209,8 +278,8 @@ function blogapi_edit_post($req_params) } $nid = node_save($node); if ($nid) { - watchdog('special', t('%type: updated %title using blog API.', array('%type' => ''. t($node->type) .'', '%title' => "$node->title")), l(t('view'), "node/$nid")); - return new xmlrpcresp(new xmlrpcval(true, 'boolean')); + watchdog('special', t('%type: added %title using blog API.', array('%type' => ''. t($node->type) .'', '%title' => "$node->title")), l(t('view'), "node/$nid")); + return true; } return blogapi_error(t('Error storing post.')); @@ -219,48 +288,73 @@ function blogapi_edit_post($req_params) /** * Blogging API callback. Returns a specified blog node. */ -function blogapi_get_post($req_params) { - $params = blogapi_convert($req_params); - $user = blogapi_validate_user($params[1], $params[2]); +function blogapi_blogger_get_post($appkey, $postid, $username, $password) { + $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } - $node = node_load(array('nid' => $params[0])); - - $blog = _blogapi_get_post($node, true); + $node = node_load(array('nid' => $postid)); - return new xmlrpcresp($blog); + return _blogapi_get_post($node, true); } /** * Blogging API callback. Removes the specified blog node. */ -function blogapi_delete_post($req_params) { - $params = blogapi_convert($req_params); +function blogapi_blogger_delete_post($appkey, $postid, $username, $password, $content, $publish) { + $user = blogapi_validate_user($username, $password); + if (!$user->uid) { + return blogapi_error($user); + } + + node_delete(array('nid' => $postid, 'confirm' => 1)); + return true; +} - $user = blogapi_validate_user($params[2], $params[3]); +/** + * Blogging API callback. Returns the latest few postings in a user's blog. $bodies TRUE + * + * returns a bandwidth-friendly list. + */ +function blogapi_blogger_get_recent_posts($appkey, $blogid, $username, $password, $number_of_posts, $bodies = TRUE) { + // Remove unused appkey (from bloggerAPI). + $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } - $ret = node_delete(array('nid' => $params[1], 'confirm' => 1)); - return new xmlrpcresp(new xmlrpcval(true, 'boolean')); + $type = _blogapi_blogid($blogid); + $result = db_query_range('SELECT n.nid, n.title,'. ($bodies ? ' n.body,' : '') ." n.created, u.name FROM {node} n, {users} u WHERE n.uid=u.uid AND n.type = '%s' AND n.uid = %d ORDER BY n.created DESC", $type, $user->uid, 0, $number_of_posts); + while ($blog = db_fetch_object($result)) { + $blogs[] = _blogapi_get_post($blog, $bodies); + } + return $blogs; +} + +function blogapi_metaweblog_new_post($blogid, $username, $password, $content, $publish) { + return blogapi_blogger_new_post('0123456789ABCDEF', $blogid, $username, $password, $content, $publish); +} + +function blogapi_metaweblog_edit_post($postid, $username, $password, $content, $publish) { + return blogapi_blogger_edit_post('0123456789ABCDEF', $postid, $username, $password, $content, $publish); +} + +function blogapi_metaweblog_get_post($postid, $username, $password) { + return blogapi_blogger_get_post('01234567890ABCDEF', $postid, $username, $password); } /** * Blogging API callback. Inserts a file into Drupal. */ -function blogapi_new_media_object($req_params) { - $params = blogapi_convert($req_params); - - $user = blogapi_validate_user($params[1], $params[2]); +function blogapi_metaweblog_new_media_object($blogid, $username, $password, $file) { + $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } - $name = basename($params[3]['name']); - $data = $params[3]['bits']; + $name = basename($file['name']); + $data = $file['bits']; if (!$data) { return blogapi_error(t('No file sent.')); @@ -271,16 +365,15 @@ function blogapi_new_media_object($req_p } // Return the successful result. - $result = new xmlrpcval(array('url' => new xmlrpcval(file_create_url($file), 'string')), 'struct'); - return new xmlrpcresp($result); + return array('url' => file_create_url($file), 'struct'); } - /** * Blogging API callback. Returns a list of the taxonomy terms that can be * associated with a blog node. */ -function blogapi_get_category_list($req_params) { - $vocabularies = module_invoke('taxonomy', 'get_vocabularies', 'blog', 'vid'); +function blogapi_metaweblog_get_category_list($blogid, $username, $password) { + $type = _blogapi_blogid($blogid); + $vocabularies = module_invoke('taxonomy', 'get_vocabularies', $type, 'vid'); $categories = array(); if ($vocabularies) { foreach ($vocabularies as $vocabulary) { @@ -290,139 +383,102 @@ function blogapi_get_category_list($req_ foreach (module_invoke('taxonomy', 'get_parents', $term->tid, 'tid') as $parent) { $term_name = $parent->name . '/' . $term_name; } - $categories[] = new xmlrpcval(array('categoryName' => new xmlrpcval($term_name, 'string'), - 'categoryId' => new xmlrpcval($term->tid, 'string')), - 'struct'); + $categories[] = array('categoryName' => $term_name, 'categoryId' => $term->tid); } } } - return new xmlrpcresp(new xmlrpcval($categories, 'array')); + return $categories; +} + +function blogapi_metaweblog_get_recent_posts($blogid, $username, $password, $number_of_posts) { + return blogapi_blogger_get_recent_posts('0123456789ABCDEF', $blogid, $username, $password, $number_of_posts, TRUE); +} + +// see above +function blogapi_mt_get_recent_post_titles($blogid, $username, $password, $number_of_posts) { + return blogapi_blogger_get_recent_posts('0123456789ABCDEF', $blogid, $username, $password, $number_of_posts, FALSE); +} + +/* **** */ +function blogapi_mt_get_category_list($blogid, $username, $password) { + return blogapi_metaweblog_get_category_list($blogid, $username, $password); } /** * Blogging API callback. Returns a list of the taxonomy terms that are * assigned to a particular node. */ -function blogapi_get_post_categories($req_params) { - $params = blogapi_convert($req_params); - $user = blogapi_validate_user($params[1], $params[2]); +function blogapi_mt_get_post_categories($postid, $username, $password) { + $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } - $terms = module_invoke('taxonomy', 'node_get_terms', $params[0], 'tid'); + $terms = module_invoke('taxonomy', 'node_get_terms', $postid, 'tid'); $categories = array(); foreach ($terms as $term) { $term_name = $term->name; foreach (module_invoke('taxonomy', 'get_parents', $term->tid, 'tid') as $parent) { $term_name = $parent->name . '/' . $term_name; } - $categories[] = new xmlrpcval(array('categoryName' => new xmlrpcval($term_name, 'string'), - 'categoryId' => new xmlrpcval($term->tid, 'string'), - 'isPrimary' => new xmlrpcval(true, 'boolean')), - 'struct'); + $categories[] = array('categoryName' => $term_name, 'categoryId' => $term->tid, 'isPrimary' => true); } - return new xmlrpcresp(new xmlrpcval($categories, 'array')); + return $categories; } /** * Blogging API callback. Assigns taxonomy terms to a particular node. */ -function blogapi_set_post_categories($req_params) { - $params = blogapi_convert($req_params); - $user = blogapi_validate_user($params[1], $params[2]); +function blogapi_mt_set_post_categories($postid, $username, $password, $categories) { + $user = blogapi_validate_user($username, $password); if (!$user->uid) { return blogapi_error($user); } - $nid = $params[0]; + $nid = $postid; $terms = array(); - foreach ($params[3] as $category) { - $terms[] = $category['categoryId']->scalarval(); + foreach ($categories as $category) { + $terms[] = $category['categoryId']; } module_invoke('taxonomy', 'node_save', $nid, $terms); - return new xmlrpcresp(new xmlrpcval(true, 'boolean')); -} - -/** - * Blogging API callback. Returns the latest few postings in a user's blog. $bodies TRUE - * - * returns a bandwidth-friendly list. - */ -function blogapi_get_recent_posts($req_params, $bodies = TRUE) { - $params = blogapi_convert($req_params); - - // Remove unused appkey (from bloggerAPI). - if (count($params) == 5) { - $params = array_slice($params, 1); - } - $user = blogapi_validate_user($params[1], $params[2]); - if (!$user->uid) { - return blogapi_error($user); - } - - $result = db_query_range('SELECT n.nid, n.title,'. ($bodies ? ' n.body,' : '') ." n.created, u.name FROM {node} n, {users} u WHERE n.uid=u.uid AND n.type = 'blog' AND n.uid = %d ORDER BY n.created DESC", $user->uid, 0, $params[3]); - while ($blog = db_fetch_object($result)) { - $xmlrpcval = _blogapi_get_post($blog, $bodies); - $blogs[] = $xmlrpcval; - } - return new xmlrpcresp(new xmlrpcval($blogs, 'array')); -} - -// see above -function blogapi_get_recent_post_titles($req_params) { - return blogapi_get_recent_posts($req_params, TRUE); -} - -/** - * Blogging API callback. Sends a list of supported methods to the client. - */ -function blogapi_supported_methods($req_params) { - $methods = array_keys(blogapi_xmlrpc()); - - $output = array(); - foreach ($methods as $method) { - $output[] = new xmlrpcval($method, 'string'); - } - return new xmlrpcresp(new xmlrpcval($output, 'array')); + return true; } /** * Blogging API callback. Sends a list of available input formats. */ -function blogapi_supported_text_filters($req_params) { - global $user; - +function blogapi_mt_supported_text_filters() { // NOTE: we're only using anonymous' formats because the MT spec // does not allow for per-user formats. $formats = filter_formats(); $filters = array(); foreach ($formats as $format) { - $filter['key'] = new xmlrpcval($format->format, 'string'); - $filter['label'] = new xmlrpcval($format->name, 'string'); - $filters[] = new xmlrpcval($filter, 'struct'); + $filter['key'] = $format->format; + $filter['label'] = $format->name; + $filters[] = $filter; } - return new xmlrpcresp(new xmlrpcval($filters, 'array')); + return $filters; } /** * Blogging API callback. Can not be implemented without support from * trackback module. */ -function blogapi_get_trackback_pings($req_params) { +function blogapi_mt_get_trackback_pings() { return blogapi_error(t('Not implemented.')); } /** * Blogging API callback. Publishes the given node */ -function blogapi_publish_post($req_params) { - $params = blogapi_convert($req_params); - - $user = blogapi_validate_user($params[1], $params[2]); - $node = node_load(array('nid' => $params[0])); +function blogap_mti_publish_post($postid, $username, $password) { + $user = blogapi_validate_user($username, $password); + if (!$user->uid) { + return blogapi_error($user); + } + $node = node_load(array('nid' => $postid)); if (!$node) { return blogapi_error(t('Invalid post.')); } @@ -432,40 +488,27 @@ function blogapi_publish_post($req_param return blogapi_error(message_access()); } - node_save($node); - - return new xmlrpcresp(new xmlrpcval(true, 'boolean')); -} - - -/** - * Process the parameters to an XMLRPC callback, and return them as an array. - */ -function blogapi_convert($params) { - $cparams = array(); - $num_params= $params->getNumParams(); - - for ($i = 0; $i < $num_params; $i++) { - $sn = $params->getParam($i); - $cparams[] = $sn->getval(); + $terms = module_invoke('taxonomy', 'node_get_terms', $node->nid, 'tid'); + foreach ($terms as $term) { + $node->taxonomy[] = $term->tid; } + node_save($node); - return $cparams; + return true; } /** * Prepare an error message for returning to the XMLRPC caller. */ function blogapi_error($message) { - global $xmlrpcusererr; - + static $xmlrpcusererr; if (!is_array($message)) { $message = array($message); } $message = implode(' ', $message); - return new xmlrpcresp(0, $xmlrpcusererr + 1, strip_tags($message)); + return xmlrpc_error($xmlrpcusererr + 1, strip_tags($message)); } /** @@ -498,13 +541,20 @@ function blogapi_blogger_title(&$content $contents = ereg_replace('[^<]*', '', $contents); } else { - list($title, $rest) = explode("\n", $contents, 2); + list($title, $contents) = explode("\n", $contents, 2); } return $title; } function blogapi_settings() { $output = form_select(t('XML-RPC Engine'), 'blogapi_engine', variable_get('blogapi_engine', 0), array(0 => 'Blogger', 1 => 'MetaWeblog', 2 => 'Movabletype'), t('RSD or Really-Simple-Discovery is a mechanism which allows external blogger tools to discover the APIs they can use to interact with Drupal. Here you can set the preferred method for blogger tools to interact with your site. The common XML-RPC engines are Blogger, MetaWeblog and Movabletype. If you are not sure which is the correct setting, choose Blogger.')); + foreach (node_list() as $type) { + $node_types[$type] = node_invoke($type, 'node_name'); + if (in_array($type, array('blog'))) { + $defaults[] = $type; + } + } + $output .= form_checkboxes(t('Blog types'), "blogapi_node_types", variable_get('blogapi_node_types', $defaults), $node_types, t('Select the content types for which you wish to enable posting via blogapi. Each type will appear as a different "blog" in the client application (if supported).'), 0, 1); return $output; } @@ -512,11 +562,14 @@ function blogapi_menu($may_cache) { $items = array(); if ($_GET['q'] == variable_get('site_frontpage', 'node')) { - drupal_set_html_head(''); + drupal_add_link(array('rel' => 'EditURI', + 'type' => 'application/rsd+xml', + 'title' => t('RSD'), + 'href' => url('blogapi/rsd', NULL, NULL, TRUE))); } if ($may_cache) { - $items[] = array('path' => 'blogapi', 'title' => t('RSD'), 'callback' => 'blogapi_blogapi', 'access' => user_access('access_content'), 'type' => MENU_CALLBACK); + $items[] = array('path' => 'blogapi', 'title' => t('RSD'), 'callback' => 'blogapi_blogapi', 'access' => user_access('access content'), 'type' => MENU_CALLBACK); } return $items; @@ -620,7 +673,7 @@ function _blogapi_mt_extra(&$node, $stru // dateCreated if ($struct['dateCreated']) { - $node->created = iso8601_decode($struct['dateCreated'], 1); + $node->created = mktime($struct['dateCreated']->hour, $struct['dateCreated']->minute, $struct['dateCreated']->second, $struct['dateCreated']->month, $struct['dateCreated']->day, $struct['dateCreated']->year); } if ($was_array) { @@ -630,12 +683,12 @@ function _blogapi_mt_extra(&$node, $stru function _blogapi_get_post($node, $bodies = true) { $xmlrpcval = array ( - 'userid' => new xmlrpcval($node->name, 'string'), - 'dateCreated' => new xmlrpcval(iso8601_encode($node->created), 'dateTime.iso8601'), - 'title' => new xmlrpcval($node->title, 'string'), - 'postid' => new xmlrpcval($node->nid, 'string'), - 'link' => new xmlrpcval(url('node/'.$node->nid, NULL, NULL, true)), - 'permaLink' => new xmlrpcval(url('node/'.$node->nid, NULL, NULL, true)), + 'userid' => $node->name, + 'dateCreated' => xmlrpc_date($node->created), + 'title' => $node->title, + 'postid' => $node->nid, + 'link' => url('node/'.$node->nid, NULL, NULL, true), + 'permaLink' => url('node/'.$node->nid, NULL, NULL, true), ); if ($bodies) { if ($node->comment = 1) { @@ -645,13 +698,34 @@ function _blogapi_get_post($node, $bodie $comment = 1; } - $xmlrpcval['content'] = new xmlrpcval("$blog->title$node->body", 'string'); - $xmlrpcval['description'] = new xmlrpcval($node->body, 'string'); + $xmlrpcval['content'] = "$node->title$node->body"; + $xmlrpcval['description'] = $node->body; // Add MT specific fields - $xmlrpcval['mt_allow_comments'] = new xmlrpcval($comment, 'int'); - $xmlrpcval['mt_convert_breaks'] = new xmlrpcval($node->format, 'string'); + $xmlrpcval['mt_allow_comments'] = $comment; + $xmlrpcval['mt_convert_breaks'] = $node->format; + } + + return $xmlrpcval; +} + +function _blogapi_blogid($id) { + if (is_numeric($id)) { + return 'blog'; + } + else { + return $id; + } +} + +function _blogapi_get_node_types() { + $available_types = variable_get('blogapi_node_types', array('blog')); + $types = array(); + foreach (node_list() as $type) { + if (node_access('create', $type) && in_array($type, $available_types)) { + $types[] = $type; + } } - return new xmlrpcval($xmlrpcval, 'struct'); + return $types; } ?> Index: modules/drupal.module =================================================================== RCS file: /cvs/drupal/drupal/modules/drupal.module,v retrieving revision 1.90.2.2 diff -u -F^f -r1.90.2.2 drupal.module --- modules/drupal.module 30 Nov 2004 18:26:26 -0000 1.90.2.2 +++ modules/drupal.module 14 Aug 2005 23:48:57 -0000 @@ -70,22 +70,15 @@ function drupal_cron() { /** * Callback function from drupal_xmlrpc() called when another site pings this one. */ -function drupal_directory_ping($arguments) { +function drupal_directory_ping($link, $name, $mail, $slogan, $mission) { /* ** Parse our parameters: */ - $argument = $arguments->getparam(0); - $link = strip_tags($argument->scalarval()); - $argument = $arguments->getparam(1); - $name = trim(strip_tags($argument->scalarval())); - $argument = $arguments->getparam(2); - $mail = strip_tags($argument->scalarval()); - $argument = $arguments->getparam(3); - $slogan = strip_tags($argument->scalarval()); - $argument = $arguments->getparam(4); - $mission = strip_tags($argument->scalarval()); + foreach (array('link', 'name', 'mail', 'slogan', 'mission') as $key) { + $$key = strip_tags($$key); + } /* ** Update the data in our database and send back a reply: @@ -97,10 +90,10 @@ function drupal_directory_ping($argument watchdog('regular', t('Directory: ping from %name (%link).', array('%name' => "$name", '%link' => "$link"))); - return new xmlrpcresp(new xmlrpcval(1, 'int')); + return 1; } else { - return new xmlrpcresp(new xmlrpcval(0, 'int')); + return 0; } } @@ -125,7 +118,17 @@ function drupal_directory_page($sort = ' * Implementation of hook_xmlrpc(). */ function drupal_xmlrpc() { - return array('drupal.site.ping' => array('function' => 'drupal_directory_ping'), 'drupal.login' => array('function' => 'drupal_login')); + return array( + array( + 'drupal.site.ping', + 'drupal_directory_ping', + array('boolean', 'string', 'string', 'string', 'string', 'string'), + t('Handling ping request')), + array( + 'drupal.login', + 'drupal_login', + array('int', 'string', 'string'), + t('Logging into a drupal site'))); } /** @@ -138,11 +141,9 @@ function drupal_notify($server) { $client = new xmlrpc_client($url['path'], $url['host'], 80); - $message = new xmlrpcmsg('drupal.site.ping', array(new xmlrpcval($base_url, 'string'), new xmlrpcval(variable_get('site_name', ''), 'string'), new xmlrpcval(variable_get('site_mail', ''), 'string'), new xmlrpcval(variable_get('site_slogan', ''), 'string'), new xmlrpcval(variable_get('site_mission', ''), 'string'))); - - $result = $client->send($message, 5); + $result = xmlrpc($server, 'drupal.site.ping', $base_url, variable_get('site_name', ''), variable_get('site_mail', ''), variable_get('site_slogan', ''), variable_get('site_mission', '')); - if (!$result || $result->faultCode()) { + if ($result === FALSE) { watchdog('error', t('Failed to notify %url at %path: %error.', array('%url' => ''. $url["host"] .'', '%path' => ''. $url["path"] .'', '%error' => ''. $result->faultString() .''))); } @@ -167,19 +168,13 @@ function drupal_info($field = 0) { * Implementation of hook_auth(). */ function drupal_auth($username, $password, $server) { - - $message = new xmlrpcmsg('drupal.login', array(new xmlrpcval($username, 'string'), new xmlrpcval($password, 'string'))); - - // TODO remove hard coded Port 80 - // TODO manage server/path such that HTTP_HOST/xml.rpc.php is not assumed - $client = new xmlrpc_client('/xmlrpc.php', $server, 80); - $result = $client->send($message, 5); - if ($result && !$result->faultCode()) { - $value = $result->value(); - $login = $value->scalarval(); + $result = xmlrpc('http://'. $server .'/xmlrpc.php', 'drupal.login', $username, $password); + if ($result === FALSE) { + drupal_set_message(t('Error %code : %message', array('%code' => theme('placeholder', xmlrpc_errno()), '%message' => theme('placeholder', xmlrpc_error_msg()))), 'error'); + } + else { + return $result; } - - return $login; } /** @@ -207,17 +202,12 @@ function drupal_page_help() { * * Remote clients are usually other Drupal instances. */ -function drupal_login($arguments) { - $argument = $arguments->getparam(0); - $username = $argument->scalarval(); - $argument = $arguments->getparam(1); - $password = $argument->scalarval(); - +function drupal_login($username, $password) { if ($user = user_load(array('name' => $username, 'pass' => $password, 'status' => 1))) { - return new xmlrpcresp(new xmlrpcval($user->uid, 'int')); + return $user->uid; } else { - return new xmlrpcresp(new xmlrpcval(0, 'int')); + return 0; } } Index: modules/ping.module =================================================================== RCS file: /cvs/drupal/drupal/modules/ping.module,v retrieving revision 1.23 diff -u -F^f -r1.23 ping.module --- modules/ping.module 10 Oct 2004 19:43:23 -0000 1.23 +++ modules/ping.module 14 Aug 2005 23:48:57 -0000 @@ -60,52 +60,10 @@ function _ping_notify($name, $url) { */ function ping_ping($name = '', $url = '') { - $feed = url('node/feed', NULL, NULL, TRUE); - - $client = new xmlrpc_client('/RPC2', 'rpc.weblogs.com', 80); - - $message = new xmlrpcmsg('weblogUpdates.ping', array(new xmlrpcval($name), new xmlrpcval($url))); - - $result = $client->send($message); - - if (!$result || $result->faultCode()) { - watchdog('error', t('Failed to notify weblogs.com (site).')); + $result = xmlrpc('http://rpc.pingomatic.com', 'weblogUpdates.ping', $name, $url); + if ($result === FALSE) { + watchdog('directory ping', t('Failed to notify pingomatic.com (site).')); } - unset($client); - - $client = new xmlrpc_client('/RPC2', 'rssrpc.weblogs.com', 80); - - $message = new xmlrpcmsg('rssUpdate', array(new xmlrpcval($name), new xmlrpcval($feed))); - - $result = $client->send($message); - - if (!$result || $result->faultCode()) { - watchdog('error', t('Failed to notify weblogs.com (RSS).')); - } - - unset($client); - - $client = new xmlrpc_client('/', 'ping.blo.gs', 80); - - $message = new xmlrpcmsg('weblogUpdates.extendedPing', array(new xmlrpcval($name), new xmlrpcval($url), new xmlrpcval($url), new xmlrpcval($feed))); - - $result = $client->send($message); - - if (!$result || $result->faultCode()) { - watchdog('error', t('Failed to notify blo.gs.')); - } - - unset($client); - - $client = new xmlrpc_client('/rpc/ping', 'rpc.technorati.com', 80); - - $message = new xmlrpcmsg('weblogUpdates.ping', array(new xmlrpcval($name), new xmlrpcval($url))); - - $result = $client->send($message); - - if (!$result || $result->faultCode()) { - watchdog('error', t('Failed to notify technorati.com.')); - } } ?>