Ticket #4899: class.xmlrpc-wp-xmlrpc-server.patch
| File class.xmlrpc-wp-xmlrpc-server.patch, 146.5 KB (added by , 19 years ago) |
|---|
-
class/IXR.php
1 <?php 2 /* 3 IXR - The Inutio XML-RPC Library - (c) Incutio Ltd 2002-2005 4 Version 1.7 (beta) - Simon Willison, 23rd May 2005 5 Site: http://scripts.incutio.com/xmlrpc/ 6 Manual: http://scripts.incutio.com/xmlrpc/manual.php 7 Made available under the BSD License: http://www.opensource.org/licenses/bsd-license.php 8 */ 9 10 class IXR_Value { 11 var $data; 12 var $type; 13 function IXR_Value ($data, $type = false) { 14 $this->data = $data; 15 if (!$type) { 16 $type = $this->calculateType(); 17 } 18 $this->type = $type; 19 if ($type == 'struct') { 20 /* Turn all the values in the array in to new IXR_Value objects */ 21 foreach ($this->data as $key => $value) { 22 $this->data[$key] = new IXR_Value($value); 23 } 24 } 25 if ($type == 'array') { 26 for ($i = 0, $j = count($this->data); $i < $j; $i++) { 27 $this->data[$i] = new IXR_Value($this->data[$i]); 28 } 29 } 30 } 31 function calculateType() { 32 if ($this->data === true || $this->data === false) { 33 return 'boolean'; 34 } 35 if (is_integer($this->data)) { 36 return 'int'; 37 } 38 if (is_double($this->data)) { 39 return 'double'; 40 } 41 // Deal with IXR object types base64 and date 42 if (is_object($this->data) && is_a($this->data, 'IXR_Date')) { 43 return 'date'; 44 } 45 if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) { 46 return 'base64'; 47 } 48 // If it is a normal PHP object convert it in to a struct 49 if (is_object($this->data)) { 50 51 $this->data = get_object_vars($this->data); 52 return 'struct'; 53 } 54 if (!is_array($this->data)) { 55 return 'string'; 56 } 57 /* We have an array - is it an array or a struct ? */ 58 if ($this->isStruct($this->data)) { 59 return 'struct'; 60 } else { 61 return 'array'; 62 } 63 } 64 function getXml() { 65 /* Return XML for this value */ 66 switch ($this->type) { 67 case 'boolean': 68 return '<boolean>'.(($this->data) ? '1' : '0').'</boolean>'; 69 break; 70 case 'int': 71 return '<int>'.$this->data.'</int>'; 72 break; 73 case 'double': 74 return '<double>'.$this->data.'</double>'; 75 break; 76 case 'string': 77 return '<string>'.htmlspecialchars($this->data).'</string>'; 78 break; 79 case 'array': 80 $return = '<array><data>'."\n"; 81 foreach ($this->data as $item) { 82 $return .= ' <value>'.$item->getXml()."</value>\n"; 83 } 84 $return .= '</data></array>'; 85 return $return; 86 break; 87 case 'struct': 88 $return = '<struct>'."\n"; 89 foreach ($this->data as $name => $value) { 90 $name = htmlspecialchars($name); 91 $return .= " <member><name>$name</name><value>"; 92 $return .= $value->getXml()."</value></member>\n"; 93 } 94 $return .= '</struct>'; 95 return $return; 96 break; 97 case 'date': 98 case 'base64': 99 return $this->data->getXml(); 100 break; 101 } 102 return false; 103 } 104 function isStruct($array) { 105 /* Nasty function to check if an array is a struct or not */ 106 $expected = 0; 107 foreach ($array as $key => $value) { 108 if ((string)$key != (string)$expected) { 109 return true; 110 } 111 $expected++; 112 } 113 return false; 114 } 115 } 116 117 118 class IXR_Message { 119 var $message; 120 var $messageType; // methodCall / methodResponse / fault 121 var $faultCode; 122 var $faultString; 123 var $methodName; 124 var $params; 125 // Current variable stacks 126 var $_arraystructs = array(); // The stack used to keep track of the current array/struct 127 var $_arraystructstypes = array(); // Stack keeping track of if things are structs or array 128 var $_currentStructName = array(); // A stack as well 129 var $_param; 130 var $_value; 131 var $_currentTag; 132 var $_currentTagContents; 133 // The XML parser 134 var $_parser; 135 function IXR_Message ($message) { 136 $this->message = $message; 137 } 138 function parse() { 139 // first remove the XML declaration 140 $this->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $this->message); 141 if (trim($this->message) == '') { 142 return false; 143 } 144 $this->_parser = xml_parser_create(); 145 // Set XML parser to take the case of tags in to account 146 xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false); 147 // Set XML parser callback functions 148 xml_set_object($this->_parser, $this); 149 xml_set_element_handler($this->_parser, 'tag_open', 'tag_close'); 150 xml_set_character_data_handler($this->_parser, 'cdata'); 151 if (!xml_parse($this->_parser, $this->message)) { 152 /* die(sprintf('XML error: %s at line %d', 153 xml_error_string(xml_get_error_code($this->_parser)), 154 xml_get_current_line_number($this->_parser))); */ 155 return false; 156 } 157 xml_parser_free($this->_parser); 158 // Grab the error messages, if any 159 if ($this->messageType == 'fault') { 160 $this->faultCode = $this->params[0]['faultCode']; 161 $this->faultString = $this->params[0]['faultString']; 162 } 163 return true; 164 } 165 function tag_open($parser, $tag, $attr) { 166 $this->_currentTagContents = ''; 167 $this->currentTag = $tag; 168 switch($tag) { 169 case 'methodCall': 170 case 'methodResponse': 171 case 'fault': 172 $this->messageType = $tag; 173 break; 174 /* Deal with stacks of arrays and structs */ 175 case 'data': // data is to all intents and puposes more interesting than array 176 $this->_arraystructstypes[] = 'array'; 177 $this->_arraystructs[] = array(); 178 break; 179 case 'struct': 180 $this->_arraystructstypes[] = 'struct'; 181 $this->_arraystructs[] = array(); 182 break; 183 } 184 } 185 function cdata($parser, $cdata) { 186 $this->_currentTagContents .= $cdata; 187 } 188 function tag_close($parser, $tag) { 189 $valueFlag = false; 190 switch($tag) { 191 case 'int': 192 case 'i4': 193 $value = (int) trim($this->_currentTagContents); 194 $valueFlag = true; 195 break; 196 case 'double': 197 $value = (double) trim($this->_currentTagContents); 198 $valueFlag = true; 199 break; 200 case 'string': 201 $value = $this->_currentTagContents; 202 $valueFlag = true; 203 break; 204 case 'dateTime.iso8601': 205 $value = new IXR_Date(trim($this->_currentTagContents)); 206 // $value = $iso->getTimestamp(); 207 $valueFlag = true; 208 break; 209 case 'value': 210 // "If no type is indicated, the type is string." 211 if (trim($this->_currentTagContents) != '') { 212 $value = (string)$this->_currentTagContents; 213 $valueFlag = true; 214 } 215 break; 216 case 'boolean': 217 $value = (boolean) trim($this->_currentTagContents); 218 $valueFlag = true; 219 break; 220 case 'base64': 221 $value = base64_decode( trim( $this->_currentTagContents ) ); 222 $valueFlag = true; 223 break; 224 /* Deal with stacks of arrays and structs */ 225 case 'data': 226 case 'struct': 227 $value = array_pop($this->_arraystructs); 228 array_pop($this->_arraystructstypes); 229 $valueFlag = true; 230 break; 231 case 'member': 232 array_pop($this->_currentStructName); 233 break; 234 case 'name': 235 $this->_currentStructName[] = trim($this->_currentTagContents); 236 break; 237 case 'methodName': 238 $this->methodName = trim($this->_currentTagContents); 239 break; 240 } 241 if ($valueFlag) { 242 if (count($this->_arraystructs) > 0) { 243 // Add value to struct or array 244 if ($this->_arraystructstypes[count($this->_arraystructstypes)-1] == 'struct') { 245 // Add to struct 246 $this->_arraystructs[count($this->_arraystructs)-1][$this->_currentStructName[count($this->_currentStructName)-1]] = $value; 247 } else { 248 // Add to array 249 $this->_arraystructs[count($this->_arraystructs)-1][] = $value; 250 } 251 } else { 252 // Just add as a paramater 253 $this->params[] = $value; 254 } 255 } 256 $this->_currentTagContents = ''; 257 } 258 } 259 260 261 class IXR_Server { 262 var $data; 263 var $callbacks = array(); 264 var $message; 265 var $capabilities; 266 function IXR_Server($callbacks = false, $data = false) { 267 $this->setCapabilities(); 268 if ($callbacks) { 269 $this->callbacks = $callbacks; 270 } 271 $this->setCallbacks(); 272 $this->serve($data); 273 } 274 function serve($data = false) { 275 if (!$data) { 276 global $HTTP_RAW_POST_DATA; 277 if (!$HTTP_RAW_POST_DATA) { 278 die('XML-RPC server accepts POST requests only.'); 279 } 280 $data = $HTTP_RAW_POST_DATA; 281 } 282 $this->message = new IXR_Message($data); 283 if (!$this->message->parse()) { 284 $this->error(-32700, 'parse error. not well formed'); 285 } 286 if ($this->message->messageType != 'methodCall') { 287 $this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall'); 288 } 289 $result = $this->call($this->message->methodName, $this->message->params); 290 // Is the result an error? 291 if (is_a($result, 'IXR_Error')) { 292 $this->error($result); 293 } 294 // Encode the result 295 $r = new IXR_Value($result); 296 $resultxml = $r->getXml(); 297 // Create the XML 298 $xml = <<<EOD 299 <methodResponse> 300 <params> 301 <param> 302 <value> 303 $resultxml 304 </value> 305 </param> 306 </params> 307 </methodResponse> 308 309 EOD; 310 // Send it 311 $this->output($xml); 312 } 313 function call($methodname, $args) { 314 if (!$this->hasMethod($methodname)) { 315 return new IXR_Error(-32601, 'server error. requested method '.$methodname.' does not exist.'); 316 } 317 $method = $this->callbacks[$methodname]; 318 // Perform the callback and send the response 319 if (count($args) == 1) { 320 // If only one paramater just send that instead of the whole array 321 $args = $args[0]; 322 } 323 // Are we dealing with a function or a method? 324 if (substr($method, 0, 5) == 'this:') { 325 // It's a class method - check it exists 326 $method = substr($method, 5); 327 if (!method_exists($this, $method)) { 328 return new IXR_Error(-32601, 'server error. requested class method "'.$method.'" does not exist.'); 329 } 330 // Call the method 331 $result = $this->$method($args); 332 } else { 333 // It's a function - does it exist? 334 if (is_array($method)) { 335 if (!method_exists($method[0], $method[1])) { 336 return new IXR_Error(-32601, 'server error. requested object method "'.$method[1].'" does not exist.'); 337 } 338 } else if (!function_exists($method)) { 339 return new IXR_Error(-32601, 'server error. requested function "'.$method.'" does not exist.'); 340 } 341 // Call the function 342 $result = call_user_func($method, $args); 343 } 344 return $result; 345 } 346 347 function error($error, $message = false) { 348 // Accepts either an error object or an error code and message 349 if ($message && !is_object($error)) { 350 $error = new IXR_Error($error, $message); 351 } 352 $this->output($error->getXml()); 353 } 354 function output($xml) { 355 $xml = '<?xml version="1.0"?>'."\n".$xml; 356 $length = strlen($xml); 357 header('Connection: close'); 358 header('Content-Length: '.$length); 359 header('Content-Type: text/xml'); 360 header('Date: '.date('r')); 361 echo $xml; 362 exit; 363 } 364 function hasMethod($method) { 365 return in_array($method, array_keys($this->callbacks)); 366 } 367 function setCapabilities() { 368 // Initialises capabilities array 369 $this->capabilities = array( 370 'xmlrpc' => array( 371 'specUrl' => 'http://www.xmlrpc.com/spec', 372 'specVersion' => 1 373 ), 374 'faults_interop' => array( 375 'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php', 376 'specVersion' => 20010516 377 ), 378 'system.multicall' => array( 379 'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208', 380 'specVersion' => 1 381 ), 382 ); 383 } 384 function getCapabilities($args) { 385 return $this->capabilities; 386 } 387 function setCallbacks() { 388 $this->callbacks['system.getCapabilities'] = 'this:getCapabilities'; 389 $this->callbacks['system.listMethods'] = 'this:listMethods'; 390 $this->callbacks['system.multicall'] = 'this:multiCall'; 391 } 392 function listMethods($args) { 393 // Returns a list of methods - uses array_reverse to ensure user defined 394 // methods are listed before server defined methods 395 return array_reverse(array_keys($this->callbacks)); 396 } 397 function multiCall($methodcalls) { 398 // See http://www.xmlrpc.com/discuss/msgReader$1208 399 $return = array(); 400 foreach ($methodcalls as $call) { 401 $method = $call['methodName']; 402 $params = $call['params']; 403 if ($method == 'system.multicall') { 404 $result = new IXR_Error(-32600, 'Recursive calls to system.multicall are forbidden'); 405 } else { 406 $result = $this->call($method, $params); 407 } 408 if (is_a($result, 'IXR_Error')) { 409 $return[] = array( 410 'faultCode' => $result->code, 411 'faultString' => $result->message 412 ); 413 } else { 414 $return[] = array($result); 415 } 416 } 417 return $return; 418 } 419 } 420 421 class IXR_Request { 422 var $method; 423 var $args; 424 var $xml; 425 function IXR_Request($method, $args) { 426 $this->method = $method; 427 $this->args = $args; 428 $this->xml = <<<EOD 429 <?xml version="1.0"?> 430 <methodCall> 431 <methodName>{$this->method}</methodName> 432 <params> 433 434 EOD; 435 foreach ($this->args as $arg) { 436 $this->xml .= '<param><value>'; 437 $v = new IXR_Value($arg); 438 $this->xml .= $v->getXml(); 439 $this->xml .= "</value></param>\n"; 440 } 441 $this->xml .= '</params></methodCall>'; 442 } 443 function getLength() { 444 return strlen($this->xml); 445 } 446 function getXml() { 447 return $this->xml; 448 } 449 } 450 451 452 class IXR_Client { 453 var $server; 454 var $port; 455 var $path; 456 var $useragent; 457 var $response; 458 var $message = false; 459 var $debug = false; 460 var $timeout; 461 // Storage place for an error message 462 var $error = false; 463 function IXR_Client($server, $path = false, $port = 80, $timeout = false) { 464 if (!$path) { 465 // Assume we have been given a URL instead 466 $bits = parse_url($server); 467 $this->server = $bits['host']; 468 $this->port = isset($bits['port']) ? $bits['port'] : 80; 469 $this->path = isset($bits['path']) ? $bits['path'] : '/'; 470 // Make absolutely sure we have a path 471 if (!$this->path) { 472 $this->path = '/'; 473 } 474 } else { 475 $this->server = $server; 476 $this->path = $path; 477 $this->port = $port; 478 } 479 $this->useragent = 'Incutio XML-RPC'; 480 $this->timeout = $timeout; 481 } 482 function query() { 483 $args = func_get_args(); 484 $method = array_shift($args); 485 $request = new IXR_Request($method, $args); 486 $length = $request->getLength(); 487 $xml = $request->getXml(); 488 $r = "\r\n"; 489 $request = "POST {$this->path} HTTP/1.0$r"; 490 $request .= "Host: {$this->server}$r"; 491 $request .= "Content-Type: text/xml$r"; 492 $request .= "User-Agent: {$this->useragent}$r"; 493 $request .= "Content-length: {$length}$r$r"; 494 $request .= $xml; 495 // Now send the request 496 if ($this->debug) { 497 echo '<pre>'.htmlspecialchars($request)."\n</pre>\n\n"; 498 } 499 if ($this->timeout) { 500 $fp = @fsockopen($this->server, $this->port, $errno, $errstr, $this->timeout); 501 } else { 502 $fp = @fsockopen($this->server, $this->port, $errno, $errstr); 503 } 504 if (!$fp) { 505 $this->error = new IXR_Error(-32300, "transport error - could not open socket: $errno $errstr"); 506 return false; 507 } 508 fputs($fp, $request); 509 $contents = ''; 510 $gotFirstLine = false; 511 $gettingHeaders = true; 512 while (!feof($fp)) { 513 $line = fgets($fp, 4096); 514 if (!$gotFirstLine) { 515 // Check line for '200' 516 if (strstr($line, '200') === false) { 517 $this->error = new IXR_Error(-32300, 'transport error - HTTP status code was not 200'); 518 return false; 519 } 520 $gotFirstLine = true; 521 } 522 if (trim($line) == '') { 523 $gettingHeaders = false; 524 } 525 if (!$gettingHeaders) { 526 $contents .= trim($line)."\n"; 527 } 528 } 529 if ($this->debug) { 530 echo '<pre>'.htmlspecialchars($contents)."\n</pre>\n\n"; 531 } 532 // Now parse what we've got back 533 $this->message = new IXR_Message($contents); 534 if (!$this->message->parse()) { 535 // XML error 536 $this->error = new IXR_Error(-32700, 'parse error. not well formed'); 537 return false; 538 } 539 // Is the message a fault? 540 if ($this->message->messageType == 'fault') { 541 $this->error = new IXR_Error($this->message->faultCode, $this->message->faultString); 542 return false; 543 } 544 // Message must be OK 545 return true; 546 } 547 function getResponse() { 548 // methodResponses can only have one param - return that 549 return $this->message->params[0]; 550 } 551 function isError() { 552 return (is_object($this->error)); 553 } 554 function getErrorCode() { 555 return $this->error->code; 556 } 557 function getErrorMessage() { 558 return $this->error->message; 559 } 560 } 561 562 563 class IXR_Error { 564 var $code; 565 var $message; 566 function IXR_Error($code, $message) { 567 $this->code = $code; 568 $this->message = $message; 569 } 570 function getXml() { 571 $xml = <<<EOD 572 <methodResponse> 573 <fault> 574 <value> 575 <struct> 576 <member> 577 <name>faultCode</name> 578 <value><int>{$this->code}</int></value> 579 </member> 580 <member> 581 <name>faultString</name> 582 <value><string>{$this->message}</string></value> 583 </member> 584 </struct> 585 </value> 586 </fault> 587 </methodResponse> 588 589 EOD; 590 return $xml; 591 } 592 } 593 594 595 class IXR_Date { 596 var $year; 597 var $month; 598 var $day; 599 var $hour; 600 var $minute; 601 var $second; 602 function IXR_Date($time) { 603 // $time can be a PHP timestamp or an ISO one 604 if (is_numeric($time)) { 605 $this->parseTimestamp($time); 606 } else { 607 $this->parseIso($time); 608 } 609 } 610 function parseTimestamp($timestamp) { 611 $this->year = date('Y', $timestamp); 612 $this->month = date('m', $timestamp); 613 $this->day = date('d', $timestamp); 614 $this->hour = date('H', $timestamp); 615 $this->minute = date('i', $timestamp); 616 $this->second = date('s', $timestamp); 617 } 618 function parseIso($iso) { 619 $this->year = substr($iso, 0, 4); 620 $this->month = substr($iso, 4, 2); 621 $this->day = substr($iso, 6, 2); 622 $this->hour = substr($iso, 9, 2); 623 $this->minute = substr($iso, 12, 2); 624 $this->second = substr($iso, 15, 2); 625 $this->timezone = substr($iso, 17); 626 } 627 function getIso() { 628 return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second.$this->timezone; 629 } 630 function getXml() { 631 return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>'; 632 } 633 function getTimestamp() { 634 return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); 635 } 636 } 637 638 639 class IXR_Base64 { 640 var $data; 641 function IXR_Base64($data) { 642 $this->data = $data; 643 } 644 function getXml() { 645 return '<base64>'.base64_encode($this->data).'</base64>'; 646 } 647 } 648 649 650 class IXR_IntrospectionServer extends IXR_Server { 651 var $signatures; 652 var $help; 653 function IXR_IntrospectionServer() { 654 $this->setCallbacks(); 655 $this->setCapabilities(); 656 $this->capabilities['introspection'] = array( 657 'specUrl' => 'http://xmlrpc.usefulinc.com/doc/reserved.html', 658 'specVersion' => 1 659 ); 660 $this->addCallback( 661 'system.methodSignature', 662 'this:methodSignature', 663 array('array', 'string'), 664 'Returns an array describing the return type and required parameters of a method' 665 ); 666 $this->addCallback( 667 'system.getCapabilities', 668 'this:getCapabilities', 669 array('struct'), 670 'Returns a struct describing the XML-RPC specifications supported by this server' 671 ); 672 $this->addCallback( 673 'system.listMethods', 674 'this:listMethods', 675 array('array'), 676 'Returns an array of available methods on this server' 677 ); 678 $this->addCallback( 679 'system.methodHelp', 680 'this:methodHelp', 681 array('string', 'string'), 682 'Returns a documentation string for the specified method' 683 ); 684 } 685 function addCallback($method, $callback, $args, $help) { 686 $this->callbacks[$method] = $callback; 687 $this->signatures[$method] = $args; 688 $this->help[$method] = $help; 689 } 690 function call($methodname, $args) { 691 // Make sure it's in an array 692 if ($args && !is_array($args)) { 693 $args = array($args); 694 } 695 // Over-rides default call method, adds signature check 696 if (!$this->hasMethod($methodname)) { 697 return new IXR_Error(-32601, 'server error. requested method "'.$this->message->methodName.'" not specified.'); 698 } 699 $method = $this->callbacks[$methodname]; 700 $signature = $this->signatures[$methodname]; 701 $returnType = array_shift($signature); 702 // Check the number of arguments 703 if (count($args) != count($signature)) { 704 return new IXR_Error(-32602, 'server error. wrong number of method parameters'); 705 } 706 // Check the argument types 707 $ok = true; 708 $argsbackup = $args; 709 for ($i = 0, $j = count($args); $i < $j; $i++) { 710 $arg = array_shift($args); 711 $type = array_shift($signature); 712 switch ($type) { 713 case 'int': 714 case 'i4': 715 if (is_array($arg) || !is_int($arg)) { 716 $ok = false; 717 } 718 break; 719 case 'base64': 720 case 'string': 721 if (!is_string($arg)) { 722 $ok = false; 723 } 724 break; 725 case 'boolean': 726 if ($arg !== false && $arg !== true) { 727 $ok = false; 728 } 729 break; 730 case 'float': 731 case 'double': 732 if (!is_float($arg)) { 733 $ok = false; 734 } 735 break; 736 case 'date': 737 case 'dateTime.iso8601': 738 if (!is_a($arg, 'IXR_Date')) { 739 $ok = false; 740 } 741 break; 742 } 743 if (!$ok) { 744 return new IXR_Error(-32602, 'server error. invalid method parameters'); 745 } 746 } 747 // It passed the test - run the "real" method call 748 return parent::call($methodname, $argsbackup); 749 } 750 function methodSignature($method) { 751 if (!$this->hasMethod($method)) { 752 return new IXR_Error(-32601, 'server error. requested method "'.$method.'" not specified.'); 753 } 754 // We should be returning an array of types 755 $types = $this->signatures[$method]; 756 $return = array(); 757 foreach ($types as $type) { 758 switch ($type) { 759 case 'string': 760 $return[] = 'string'; 761 break; 762 case 'int': 763 case 'i4': 764 $return[] = 42; 765 break; 766 case 'double': 767 $return[] = 3.1415; 768 break; 769 case 'dateTime.iso8601': 770 $return[] = new IXR_Date(time()); 771 break; 772 case 'boolean': 773 $return[] = true; 774 break; 775 case 'base64': 776 $return[] = new IXR_Base64('base64'); 777 break; 778 case 'array': 779 $return[] = array('array'); 780 break; 781 case 'struct': 782 $return[] = array('struct' => 'struct'); 783 break; 784 } 785 } 786 return $return; 787 } 788 function methodHelp($method) { 789 return $this->help[$method]; 790 } 791 } 792 793 794 class IXR_ClientMulticall extends IXR_Client { 795 var $calls = array(); 796 function IXR_ClientMulticall($server, $path = false, $port = 80) { 797 parent::IXR_Client($server, $path, $port); 798 $this->useragent = 'The Incutio XML-RPC PHP Library (multicall client)'; 799 } 800 function addCall() { 801 $args = func_get_args(); 802 $methodName = array_shift($args); 803 $struct = array( 804 'methodName' => $methodName, 805 'params' => $args 806 ); 807 $this->calls[] = $struct; 808 } 809 function query() { 810 // Prepare multicall, then call the parent::query() method 811 return parent::query('system.multicall', $this->calls); 812 } 813 } 814 No newline at end of file -
class/WP/XmlRpc/Server.php
1 <?php 2 3 class WP_XmlRpc_Server extends IXR_Server { 4 5 /** 6 * WP_XmlRpc_Server() - 7 * 8 * 9 */ 10 function WP_XmlRpc_Server() { 11 $this->methods = array( 12 // WordPress API 13 'wp.getPage' => 'this:wp_getPage', 14 'wp.getPages' => 'this:wp_getPages', 15 'wp.newPage' => 'this:wp_newPage', 16 'wp.deletePage' => 'this:wp_deletePage', 17 'wp.editPage' => 'this:wp_editPage', 18 'wp.getPageList' => 'this:wp_getPageList', 19 'wp.getAuthors' => 'this:wp_getAuthors', 20 'wp.getCategories' => 'this:mw_getCategories', // Alias 21 'wp.newCategory' => 'this:wp_newCategory', 22 'wp.suggestCategories' => 'this:wp_suggestCategories', 23 'wp.uploadFile' => 'this:mw_newMediaObject', // Alias 24 25 // Blogger API 26 'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs', 27 'blogger.getUserInfo' => 'this:blogger_getUserInfo', 28 'blogger.getPost' => 'this:blogger_getPost', 29 'blogger.getRecentPosts' => 'this:blogger_getRecentPosts', 30 'blogger.getTemplate' => 'this:blogger_getTemplate', 31 'blogger.setTemplate' => 'this:blogger_setTemplate', 32 'blogger.newPost' => 'this:blogger_newPost', 33 'blogger.editPost' => 'this:blogger_editPost', 34 'blogger.deletePost' => 'this:blogger_deletePost', 35 36 // MetaWeblog API (with MT extensions to structs) 37 'metaWeblog.newPost' => 'this:mw_newPost', 38 'metaWeblog.editPost' => 'this:mw_editPost', 39 'metaWeblog.getPost' => 'this:mw_getPost', 40 'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts', 41 'metaWeblog.getCategories' => 'this:mw_getCategories', 42 'metaWeblog.newMediaObject' => 'this:mw_newMediaObject', 43 44 // MetaWeblog API aliases for Blogger API 45 // see http://www.xmlrpc.com/stories/storyReader$2460 46 'metaWeblog.deletePost' => 'this:blogger_deletePost', 47 'metaWeblog.getTemplate' => 'this:blogger_getTemplate', 48 'metaWeblog.setTemplate' => 'this:blogger_setTemplate', 49 'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs', 50 51 // MovableType API 52 'mt.getCategoryList' => 'this:mt_getCategoryList', 53 'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles', 54 'mt.getPostCategories' => 'this:mt_getPostCategories', 55 'mt.setPostCategories' => 'this:mt_setPostCategories', 56 'mt.supportedMethods' => 'this:mt_supportedMethods', 57 'mt.supportedTextFilters' => 'this:mt_supportedTextFilters', 58 'mt.getTrackbackPings' => 'this:mt_getTrackbackPings', 59 'mt.publishPost' => 'this:mt_publishPost', 60 61 // PingBack 62 'pingback.ping' => 'this:pingback_ping', 63 'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks', 64 65 'demo.sayHello' => 'this:sayHello', 66 'demo.addTwoNumbers' => 'this:addTwoNumbers' 67 ); 68 $this->methods = apply_filters('xmlrpc_methods', $this->methods); 69 $this->IXR_Server($this->methods); 70 } 71 72 function sayHello($args) { 73 return 'Hello!'; 74 } 75 76 function addTwoNumbers($args) { 77 $number1 = $args[0]; 78 $number2 = $args[1]; 79 return $number1 + $number2; 80 } 81 82 function login_pass_ok($user_login, $user_pass) { 83 if (!user_pass_ok($user_login, $user_pass)) { 84 $this->error = new IXR_Error(403, __('Bad login/pass combination.')); 85 return false; 86 } 87 return true; 88 } 89 90 function escape(&$array) { 91 global $wpdb; 92 93 if(!is_array($array)) { 94 return($wpdb->escape($array)); 95 } 96 else { 97 foreach ( (array) $array as $k => $v ) { 98 if (is_array($v)) { 99 $this->escape($array[$k]); 100 } else if (is_object($v)) { 101 //skip 102 } else { 103 $array[$k] = $wpdb->escape($v); 104 } 105 } 106 } 107 } 108 109 /** 110 * WordPress XML-RPC API 111 * wp_getPage 112 */ 113 function wp_getPage($args) { 114 $this->escape($args); 115 116 $blog_id = (int) $args[0]; 117 $page_id = (int) $args[1]; 118 $username = $args[2]; 119 $password = $args[3]; 120 121 if(!$this->login_pass_ok($username, $password)) { 122 return($this->error); 123 } 124 125 // Lookup page info. 126 $page = get_page($page_id); 127 128 // If we found the page then format the data. 129 if($page->ID && ($page->post_type == "page")) { 130 // Get all of the page content and link. 131 $full_page = get_extended($page->post_content); 132 $link = post_permalink($page->ID); 133 134 // Get info the page parent if there is one. 135 $parent_title = ""; 136 if(!empty($page->post_parent)) { 137 $parent = get_page($page->post_parent); 138 $parent_title = $parent->post_title; 139 } 140 141 // Determine comment and ping settings. 142 $allow_comments = ("open" == $page->comment_status) ? 1 : 0; 143 $allow_pings = ("open" == $page->ping_status) ? 1 : 0; 144 145 // Format page date. 146 $page_date = mysql2date("Ymd\TH:i:s", $page->post_date); 147 $page_date_gmt = mysql2date("Ymd\TH:i:s", $page->post_date_gmt); 148 149 // Pull the categories info together. 150 $categories = array(); 151 foreach(wp_get_post_categories($page->ID) as $cat_id) { 152 $categories[] = get_cat_name($cat_id); 153 } 154 155 // Get the author info. 156 $author = get_userdata($page->post_author); 157 158 $page_struct = array( 159 "dateCreated" => new IXR_Date($page_date), 160 "userid" => $page->post_author, 161 "page_id" => $page->ID, 162 "page_status" => $page->post_status, 163 "description" => $full_page["main"], 164 "title" => $page->post_title, 165 "link" => $link, 166 "permaLink" => $link, 167 "categories" => $categories, 168 "excerpt" => $page->post_excerpt, 169 "text_more" => $full_page["extended"], 170 "mt_allow_comments" => $allow_comments, 171 "mt_allow_pings" => $allow_pings, 172 "wp_slug" => $page->post_name, 173 "wp_password" => $page->post_password, 174 "wp_author" => $author->display_name, 175 "wp_page_parent_id" => $page->post_parent, 176 "wp_page_parent_title" => $parent_title, 177 "wp_page_order" => $page->menu_order, 178 "wp_author_id" => $author->ID, 179 "wp_author_display_name" => $author->display_name, 180 "date_created_gmt" => new IXR_Date($page_date_gmt) 181 ); 182 183 return($page_struct); 184 } 185 // If the page doesn't exist indicate that. 186 else { 187 return(new IXR_Error(404, __("Sorry, no such page."))); 188 } 189 } 190 191 /** 192 * WordPress XML-RPC API 193 * wp_getPages 194 */ 195 function wp_getPages($args) { 196 $this->escape($args); 197 198 $blog_id = (int) $args[0]; 199 $username = $args[1]; 200 $password = $args[2]; 201 202 if(!$this->login_pass_ok($username, $password)) { 203 return($this->error); 204 } 205 206 // Lookup info on pages. 207 $pages = get_pages(); 208 $num_pages = count($pages); 209 210 // If we have pages, put together their info. 211 if($num_pages >= 1) { 212 $pages_struct = array(); 213 214 for($i = 0; $i < $num_pages; $i++) { 215 $page = wp_xmlrpc_server::wp_getPage(array( 216 $blog_id, $pages[$i]->ID, $username, $password 217 )); 218 $pages_struct[] = $page; 219 } 220 221 return($pages_struct); 222 } 223 // If no pages were found return an error. 224 else { 225 return(array()); 226 } 227 } 228 229 /** 230 * WordPress XML-RPC API 231 * wp_newPage 232 */ 233 function wp_newPage($args) { 234 // Items not escaped here will be escaped in newPost. 235 $username = $this->escape($args[1]); 236 $password = $this->escape($args[2]); 237 $page = $args[3]; 238 $publish = $args[4]; 239 240 if(!$this->login_pass_ok($username, $password)) { 241 return($this->error); 242 } 243 244 // Set the user context and check if they are allowed 245 // to add new pages. 246 $user = set_current_user(0, $username); 247 if(!current_user_can("publish_pages")) { 248 return(new IXR_Error(401, __("Sorry, you can not add new pages."))); 249 } 250 251 // Mark this as content for a page. 252 $args[3]["post_type"] = "page"; 253 254 // Let mw_newPost do all of the heavy lifting. 255 return($this->mw_newPost($args)); 256 } 257 258 /** 259 * WordPress XML-RPC API 260 * wp_deletePage 261 */ 262 function wp_deletePage($args) { 263 $this->escape($args); 264 265 $blog_id = (int) $args[0]; 266 $username = $args[1]; 267 $password = $args[2]; 268 $page_id = (int) $args[3]; 269 270 if(!$this->login_pass_ok($username, $password)) { 271 return($this->error); 272 } 273 274 // Get the current page based on the page_id and 275 // make sure it is a page and not a post. 276 $actual_page = wp_get_single_post($page_id, ARRAY_A); 277 if( 278 !$actual_page 279 || ($actual_page["post_type"] != "page") 280 ) { 281 return(new IXR_Error(404, __("Sorry, no such page."))); 282 } 283 284 // Set the user context and make sure they can delete pages. 285 set_current_user(0, $username); 286 if(!current_user_can("delete_page", $page_id)) { 287 return(new IXR_Error(401, __("Sorry, you do not have the right to delete this page."))); 288 } 289 290 // Attempt to delete the page. 291 $result = wp_delete_post($page_id); 292 if(!$result) { 293 return(new IXR_Error(500, __("Failed to delete the page."))); 294 } 295 296 return(true); 297 } 298 299 /** 300 * WordPress XML-RPC API 301 * wp_editPage 302 */ 303 function wp_editPage($args) { 304 // Items not escaped here will be escaped in editPost. 305 $blog_id = (int) $args[0]; 306 $page_id = (int) $this->escape($args[1]); 307 $username = $this->escape($args[2]); 308 $password = $this->escape($args[3]); 309 $content = $args[4]; 310 $publish = $args[5]; 311 312 if(!$this->login_pass_ok($username, $password)) { 313 return($this->error); 314 } 315 316 // Get the page data and make sure it is a page. 317 $actual_page = wp_get_single_post($page_id, ARRAY_A); 318 if( 319 !$actual_page 320 || ($actual_page["post_type"] != "page") 321 ) { 322 return(new IXR_Error(404, __("Sorry, no such page."))); 323 } 324 325 // Set the user context and make sure they are allowed to edit pages. 326 set_current_user(0, $username); 327 if(!current_user_can("edit_page", $page_id)) { 328 return(new IXR_Error(401, __("Sorry, you do not have the right to edit this page."))); 329 } 330 331 // Mark this as content for a page. 332 $content["post_type"] = "page"; 333 334 // Arrange args in the way mw_editPost understands. 335 $args = array( 336 $page_id, 337 $username, 338 $password, 339 $content, 340 $publish 341 ); 342 343 // Let mw_editPost do all of the heavy lifting. 344 return($this->mw_editPost($args)); 345 } 346 347 /** 348 * WordPress XML-RPC API 349 * wp_getPageList 350 */ 351 function wp_getPageList($args) { 352 global $wpdb; 353 354 $this->escape($args); 355 356 $blog_id = (int) $args[0]; 357 $username = $args[1]; 358 $password = $args[2]; 359 360 if(!$this->login_pass_ok($username, $password)) { 361 return($this->error); 362 } 363 364 // Get list of pages ids and titles 365 $page_list = $wpdb->get_results(" 366 SELECT ID page_id, 367 post_title page_title, 368 post_parent page_parent_id, 369 post_date_gmt, 370 post_date 371 FROM {$wpdb->posts} 372 WHERE post_type = 'page' 373 ORDER BY ID 374 "); 375 376 // The date needs to be formated properly. 377 $num_pages = count($page_list); 378 for($i = 0; $i < $num_pages; $i++) { 379 $post_date = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date); 380 $post_date_gmt = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date_gmt); 381 382 $page_list[$i]->dateCreated = new IXR_Date($post_date); 383 $page_list[$i]->date_created_gmt = new IXR_Date($post_date_gmt); 384 385 unset($page_list[$i]->post_date_gmt); 386 unset($page_list[$i]->post_date); 387 } 388 389 return($page_list); 390 } 391 392 /** 393 * WordPress XML-RPC API 394 * wp_getAuthors 395 */ 396 function wp_getAuthors($args) { 397 global $wpdb; 398 399 $this->escape($args); 400 401 $blog_id = (int) $args[0]; 402 $username = $args[1]; 403 $password = $args[2]; 404 405 if(!$this->login_pass_ok($username, $password)) { 406 return($this->error); 407 } 408 409 return(get_users_of_blog()); 410 } 411 412 /** 413 * WordPress XML-RPC API 414 * wp_newCategory 415 */ 416 function wp_newCategory($args) { 417 $this->escape($args); 418 419 $blog_id = (int) $args[0]; 420 $username = $args[1]; 421 $password = $args[2]; 422 $category = $args[3]; 423 424 if(!$this->login_pass_ok($username, $password)) { 425 return($this->error); 426 } 427 428 // Set the user context and make sure they are 429 // allowed to add a category. 430 set_current_user(0, $username); 431 if(!current_user_can("manage_categories", $page_id)) { 432 return(new IXR_Error(401, __("Sorry, you do not have the right to add a category."))); 433 } 434 435 // If no slug was provided make it empty so that 436 // WordPress will generate one. 437 if(empty($category["slug"])) { 438 $category["slug"] = ""; 439 } 440 441 // If no parent_id was provided make it empty 442 // so that it will be a top level page (no parent). 443 if ( !isset($category["parent_id"]) ) 444 $category["parent_id"] = ""; 445 446 // If no description was provided make it empty. 447 if(empty($category["description"])) { 448 $category["description"] = ""; 449 } 450 451 $new_category = array( 452 "cat_name" => $category["name"], 453 "category_nicename" => $category["slug"], 454 "category_parent" => $category["parent_id"], 455 "category_description" => $category["description"] 456 ); 457 458 $cat_id = wp_insert_category($new_category); 459 if(!$cat_id) { 460 return(new IXR_Error(500, __("Sorry, the new category failed."))); 461 } 462 463 return($cat_id); 464 } 465 466 /** 467 * WordPress XML-RPC API 468 * wp_suggestCategories 469 */ 470 function wp_suggestCategories($args) { 471 global $wpdb; 472 473 $this->escape($args); 474 475 $blog_id = (int) $args[0]; 476 $username = $args[1]; 477 $password = $args[2]; 478 $category = $args[3]; 479 $max_results = (int) $args[4]; 480 481 if(!$this->login_pass_ok($username, $password)) { 482 return($this->error); 483 } 484 485 $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category); 486 $category_suggestions = get_categories($args); 487 488 return($category_suggestions); 489 } 490 491 492 /* Blogger API functions 493 * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/ 494 */ 495 496 497 /* blogger.getUsersBlogs will make more sense once we support multiple blogs */ 498 function blogger_getUsersBlogs($args) { 499 500 $this->escape($args); 501 502 $user_login = $args[1]; 503 $user_pass = $args[2]; 504 505 if (!$this->login_pass_ok($user_login, $user_pass)) { 506 return $this->error; 507 } 508 509 set_current_user(0, $user_login); 510 $is_admin = current_user_can('level_8'); 511 512 $struct = array( 513 'isAdmin' => $is_admin, 514 'url' => get_option('home') . '/', 515 'blogid' => '1', 516 'blogName' => get_option('blogname') 517 ); 518 519 return array($struct); 520 } 521 522 523 /* blogger.getUsersInfo gives your client some info about you, so you don't have to */ 524 function blogger_getUserInfo($args) { 525 526 $this->escape($args); 527 528 $user_login = $args[1]; 529 $user_pass = $args[2]; 530 531 if (!$this->login_pass_ok($user_login, $user_pass)) { 532 return $this->error; 533 } 534 535 $user_data = get_userdatabylogin($user_login); 536 537 $struct = array( 538 'nickname' => $user_data->nickname, 539 'userid' => $user_data->ID, 540 'url' => $user_data->user_url, 541 'email' => $user_data->user_email, 542 'lastname' => $user_data->last_name, 543 'firstname' => $user_data->first_name 544 ); 545 546 return $struct; 547 } 548 549 550 /* blogger.getPost ...gets a post */ 551 function blogger_getPost($args) { 552 553 $this->escape($args); 554 555 $post_ID = (int) $args[1]; 556 $user_login = $args[2]; 557 $user_pass = $args[3]; 558 559 if (!$this->login_pass_ok($user_login, $user_pass)) { 560 return $this->error; 561 } 562 563 $user_data = get_userdatabylogin($user_login); 564 $post_data = wp_get_single_post($post_ID, ARRAY_A); 565 566 $categories = implode(',', wp_get_post_categories($post_ID)); 567 568 $content = '<title>'.stripslashes($post_data['post_title']).'</title>'; 569 $content .= '<category>'.$categories.'</category>'; 570 $content .= stripslashes($post_data['post_content']); 571 572 $struct = array( 573 'userid' => $post_data['post_author'], 574 'dateCreated' => new IXR_Date(mysql2date('Ymd\TH:i:s', $post_data['post_date'])), 575 'content' => $content, 576 'postid' => $post_data['ID'] 577 ); 578 579 return $struct; 580 } 581 582 583 /* blogger.getRecentPosts ...gets recent posts */ 584 function blogger_getRecentPosts($args) { 585 586 global $wpdb; 587 588 $this->escape($args); 589 590 $blog_ID = (int) $args[1]; /* though we don't use it yet */ 591 $user_login = $args[2]; 592 $user_pass = $args[3]; 593 $num_posts = $args[4]; 594 595 if (!$this->login_pass_ok($user_login, $user_pass)) { 596 return $this->error; 597 } 598 599 $posts_list = wp_get_recent_posts($num_posts); 600 601 if (!$posts_list) { 602 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 603 return $this->error; 604 } 605 606 foreach ($posts_list as $entry) { 607 608 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date']); 609 $categories = implode(',', wp_get_post_categories($entry['ID'])); 610 611 $content = '<title>'.stripslashes($entry['post_title']).'</title>'; 612 $content .= '<category>'.$categories.'</category>'; 613 $content .= stripslashes($entry['post_content']); 614 615 $struct[] = array( 616 'userid' => $entry['post_author'], 617 'dateCreated' => new IXR_Date($post_date), 618 'content' => $content, 619 'postid' => $entry['ID'], 620 ); 621 622 } 623 624 $recent_posts = array(); 625 for ($j=0; $j<count($struct); $j++) { 626 array_push($recent_posts, $struct[$j]); 627 } 628 629 return $recent_posts; 630 } 631 632 633 /* blogger.getTemplate returns your blog_filename */ 634 function blogger_getTemplate($args) { 635 636 $this->escape($args); 637 638 $blog_ID = (int) $args[1]; 639 $user_login = $args[2]; 640 $user_pass = $args[3]; 641 $template = $args[4]; /* could be 'main' or 'archiveIndex', but we don't use it */ 642 643 if (!$this->login_pass_ok($user_login, $user_pass)) { 644 return $this->error; 645 } 646 647 set_current_user(0, $user_login); 648 if ( !current_user_can('edit_themes') ) { 649 return new IXR_Error(401, __('Sorry, this user can not edit the template.')); 650 } 651 652 /* warning: here we make the assumption that the blog's URL is on the same server */ 653 $filename = get_option('home') . '/'; 654 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename); 655 656 $f = fopen($filename, 'r'); 657 $content = fread($f, filesize($filename)); 658 fclose($f); 659 660 /* so it is actually editable with a windows/mac client */ 661 // FIXME: (or delete me) do we really want to cater to bad clients at the expense of good ones by BEEPing up their line breaks? commented. $content = str_replace("\n", "\r\n", $content); 662 663 return $content; 664 } 665 666 667 /* blogger.setTemplate updates the content of blog_filename */ 668 function blogger_setTemplate($args) { 669 670 $this->escape($args); 671 672 $blog_ID = (int) $args[1]; 673 $user_login = $args[2]; 674 $user_pass = $args[3]; 675 $content = $args[4]; 676 $template = $args[5]; /* could be 'main' or 'archiveIndex', but we don't use it */ 677 678 if (!$this->login_pass_ok($user_login, $user_pass)) { 679 return $this->error; 680 } 681 682 set_current_user(0, $user_login); 683 if ( !current_user_can('edit_themes') ) { 684 return new IXR_Error(401, __('Sorry, this user can not edit the template.')); 685 } 686 687 /* warning: here we make the assumption that the blog's URL is on the same server */ 688 $filename = get_option('home') . '/'; 689 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename); 690 691 if ($f = fopen($filename, 'w+')) { 692 fwrite($f, $content); 693 fclose($f); 694 } else { 695 return new IXR_Error(500, __('Either the file is not writable, or something wrong happened. The file has not been updated.')); 696 } 697 698 return true; 699 } 700 701 702 /* blogger.newPost ...creates a new post */ 703 function blogger_newPost($args) { 704 705 global $wpdb; 706 707 $this->escape($args); 708 709 $blog_ID = (int) $args[1]; /* though we don't use it yet */ 710 $user_login = $args[2]; 711 $user_pass = $args[3]; 712 $content = $args[4]; 713 $publish = $args[5]; 714 715 if (!$this->login_pass_ok($user_login, $user_pass)) { 716 return $this->error; 717 } 718 719 $cap = ($publish) ? 'publish_posts' : 'edit_posts'; 720 $user = set_current_user(0, $user_login); 721 if ( !current_user_can($cap) ) 722 return new IXR_Error(401, __('Sorry, you are not allowed to post on this blog.')); 723 724 $post_status = ($publish) ? 'publish' : 'draft'; 725 726 $post_author = $user->ID; 727 728 $post_title = xmlrpc_getposttitle($content); 729 $post_category = xmlrpc_getpostcategory($content); 730 $post_content = xmlrpc_removepostdata($content); 731 732 $post_date = current_time('mysql'); 733 $post_date_gmt = current_time('mysql', 1); 734 735 $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status'); 736 737 $post_ID = wp_insert_post($post_data); 738 739 if (!$post_ID) { 740 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.')); 741 } 742 $this->attach_uploads( $post_ID, $post_content ); 743 744 logIO('O', "Posted ! ID: $post_ID"); 745 746 return $post_ID; 747 } 748 749 750 /* blogger.editPost ...edits a post */ 751 function blogger_editPost($args) { 752 753 global $wpdb; 754 755 $this->escape($args); 756 757 $post_ID = (int) $args[1]; 758 $user_login = $args[2]; 759 $user_pass = $args[3]; 760 $content = $args[4]; 761 $publish = $args[5]; 762 763 if (!$this->login_pass_ok($user_login, $user_pass)) { 764 return $this->error; 765 } 766 767 $actual_post = wp_get_single_post($post_ID,ARRAY_A); 768 769 if (!$actual_post) { 770 return new IXR_Error(404, __('Sorry, no such post.')); 771 } 772 773 $this->escape($actual_post); 774 775 set_current_user(0, $user_login); 776 if ( !current_user_can('edit_post', $post_ID) ) 777 return new IXR_Error(401, __('Sorry, you do not have the right to edit this post.')); 778 779 extract($actual_post, EXTR_SKIP); 780 781 if ( ('publish' == $post_status) && !current_user_can('publish_posts') ) 782 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.')); 783 784 $post_title = xmlrpc_getposttitle($content); 785 $post_category = xmlrpc_getpostcategory($content); 786 $post_content = xmlrpc_removepostdata($content); 787 788 $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt'); 789 790 $result = wp_update_post($postdata); 791 792 if (!$result) { 793 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.')); 794 } 795 $this->attach_uploads( $ID, $post_content ); 796 797 return true; 798 } 799 800 801 /* blogger.deletePost ...deletes a post */ 802 function blogger_deletePost($args) { 803 804 global $wpdb; 805 806 $this->escape($args); 807 808 $post_ID = (int) $args[1]; 809 $user_login = $args[2]; 810 $user_pass = $args[3]; 811 $publish = $args[4]; 812 813 if (!$this->login_pass_ok($user_login, $user_pass)) { 814 return $this->error; 815 } 816 817 $actual_post = wp_get_single_post($post_ID,ARRAY_A); 818 819 if (!$actual_post) { 820 return new IXR_Error(404, __('Sorry, no such post.')); 821 } 822 823 set_current_user(0, $user_login); 824 if ( !current_user_can('edit_post', $post_ID) ) 825 return new IXR_Error(401, __('Sorry, you do not have the right to delete this post.')); 826 827 $result = wp_delete_post($post_ID); 828 829 if (!$result) { 830 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be deleted.')); 831 } 832 833 return true; 834 } 835 836 837 838 /* MetaWeblog API functions 839 * specs on wherever Dave Winer wants them to be 840 */ 841 842 /* metaweblog.newPost creates a post */ 843 function mw_newPost($args) { 844 845 global $wpdb, $post_default_category; 846 847 $this->escape($args); 848 849 $blog_ID = (int) $args[0]; // we will support this in the near future 850 $user_login = $args[1]; 851 $user_pass = $args[2]; 852 $content_struct = $args[3]; 853 $publish = $args[4]; 854 855 if (!$this->login_pass_ok($user_login, $user_pass)) { 856 return $this->error; 857 } 858 859 $cap = ($publish) ? 'publish_posts' : 'edit_posts'; 860 $user = set_current_user(0, $user_login); 861 if ( !current_user_can($cap) ) 862 return new IXR_Error(401, __('Sorry, you are not allowed to post on this blog.')); 863 864 // The post_type defaults to post, but could also be page. 865 $post_type = "post"; 866 if( 867 !empty($content_struct["post_type"]) 868 && ($content_struct["post_type"] == "page") 869 ) { 870 $post_type = "page"; 871 } 872 873 // Let WordPress generate the post_name (slug) unless 874 // one has been provided. 875 $post_name = ""; 876 if(isset($content_struct["wp_slug"])) { 877 $post_name = $content_struct["wp_slug"]; 878 } 879 880 // Only use a password if one was given. 881 if(isset($content_struct["wp_password"])) { 882 $post_password = $content_struct["wp_password"]; 883 } 884 885 // Only set a post parent if one was provided. 886 if(isset($content_struct["wp_page_parent_id"])) { 887 $post_parent = $content_struct["wp_page_parent_id"]; 888 } 889 890 // Only set the menu_order if it was provided. 891 if(isset($content_struct["wp_page_order"])) { 892 $menu_order = $content_struct["wp_page_order"]; 893 } 894 895 $post_author = $user->ID; 896 897 // If an author id was provided then use it instead. 898 if( 899 isset($content_struct["wp_author_id"]) 900 && ($user->ID != $content_struct["wp_author_id"]) 901 ) { 902 switch($post_type) { 903 case "post": 904 if(!current_user_can("edit_others_posts")) { 905 return(new IXR_Error(401, __("You are not allowed to post as this user"))); 906 } 907 break; 908 case "page": 909 if(!current_user_can("edit_others_pages")) { 910 return(new IXR_Error(401, __("You are not allowed to create pages as this user"))); 911 } 912 break; 913 default: 914 return(new IXR_Error(401, __("Invalid post type."))); 915 break; 916 } 917 $post_author = $content_struct["wp_author_id"]; 918 } 919 920 $post_title = $content_struct['title']; 921 $post_content = apply_filters( 'content_save_pre', $content_struct['description'] ); 922 $post_status = $publish ? 'publish' : 'draft'; 923 924 $post_excerpt = $content_struct['mt_excerpt']; 925 $post_more = $content_struct['mt_text_more']; 926 927 $tags_input = $content_struct['mt_keywords']; 928 929 if(isset($content_struct["mt_allow_comments"])) { 930 if(!is_numeric($content_struct["mt_allow_comments"])) { 931 switch($content_struct["mt_allow_comments"]) { 932 case "closed": 933 $comment_status = "closed"; 934 break; 935 case "open": 936 $comment_status = "open"; 937 break; 938 default: 939 $comment_status = get_option("default_comment_status"); 940 break; 941 } 942 } 943 else { 944 switch((int) $content_struct["mt_allow_comments"]) { 945 case 0: 946 $comment_status = "closed"; 947 break; 948 case 1: 949 $comment_status = "open"; 950 break; 951 default: 952 $comment_status = get_option("default_comment_status"); 953 break; 954 } 955 } 956 } 957 else { 958 $comment_status = get_option("default_comment_status"); 959 } 960 961 if(isset($content_struct["mt_allow_pings"])) { 962 if(!is_numeric($content_struct["mt_allow_pings"])) { 963 switch($content_struct['mt_allow_pings']) { 964 case "closed": 965 $ping_status = "closed"; 966 break; 967 case "open": 968 $ping_status = "open"; 969 break; 970 default: 971 $ping_status = get_option("default_ping_status"); 972 break; 973 } 974 } 975 else { 976 switch((int) $content_struct["mt_allow_pings"]) { 977 case 0: 978 $ping_status = "closed"; 979 break; 980 case 1: 981 $ping_status = "open"; 982 break; 983 default: 984 $ping_status = get_option("default_ping_status"); 985 break; 986 } 987 } 988 } 989 else { 990 $ping_status = get_option("default_ping_status"); 991 } 992 993 if ($post_more) { 994 $post_content = $post_content . "\n<!--more-->\n" . $post_more; 995 } 996 997 $to_ping = $content_struct['mt_tb_ping_urls']; 998 if ( is_array($to_ping) ) 999 $to_ping = implode(' ', $to_ping); 1000 1001 // Do some timestamp voodoo 1002 $dateCreatedd = $content_struct['dateCreated']; 1003 if (!empty($dateCreatedd)) { 1004 $dateCreated = $dateCreatedd->getIso(); 1005 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 1006 $post_date_gmt = iso8601_to_datetime($dateCreated, GMT); 1007 } else { 1008 $post_date = current_time('mysql'); 1009 $post_date_gmt = current_time('mysql', 1); 1010 } 1011 1012 $catnames = $content_struct['categories']; 1013 logIO('O', 'Post cats: ' . printr($catnames,true)); 1014 $post_category = array(); 1015 1016 if (is_array($catnames)) { 1017 foreach ($catnames as $cat) { 1018 $post_category[] = get_cat_ID($cat); 1019 } 1020 } 1021 1022 // We've got all the data -- post it: 1023 $postdata = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'to_ping', 'post_type', 'post_name', 'post_password', 'post_parent', 'menu_order', 'tags_input'); 1024 1025 $post_ID = wp_insert_post($postdata); 1026 1027 if (!$post_ID) { 1028 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.')); 1029 } 1030 1031 $this->attach_uploads( $post_ID, $post_content ); 1032 1033 logIO('O', "Posted ! ID: $post_ID"); 1034 1035 return strval($post_ID); 1036 } 1037 1038 function attach_uploads( $post_ID, $post_content ) { 1039 global $wpdb; 1040 1041 // find any unattached files 1042 $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '-1' AND post_type = 'attachment'" ); 1043 if( is_array( $attachments ) ) { 1044 foreach( $attachments as $file ) { 1045 if( strpos( $post_content, $file->guid ) !== false ) { 1046 $wpdb->query( "UPDATE {$wpdb->posts} SET post_parent = '$post_ID' WHERE ID = '{$file->ID}'" ); 1047 } 1048 } 1049 } 1050 } 1051 1052 /* metaweblog.editPost ...edits a post */ 1053 function mw_editPost($args) { 1054 1055 global $wpdb, $post_default_category; 1056 1057 $this->escape($args); 1058 1059 $post_ID = (int) $args[0]; 1060 $user_login = $args[1]; 1061 $user_pass = $args[2]; 1062 $content_struct = $args[3]; 1063 $publish = $args[4]; 1064 1065 if (!$this->login_pass_ok($user_login, $user_pass)) { 1066 return $this->error; 1067 } 1068 1069 $user = set_current_user(0, $user_login); 1070 1071 // The post_type defaults to post, but could also be page. 1072 $post_type = "post"; 1073 if( 1074 !empty($content_struct["post_type"]) 1075 && ($content_struct["post_type"] == "page") 1076 ) { 1077 $post_type = "page"; 1078 } 1079 1080 // Edit page caps are checked in editPage. Just check post here. 1081 if ( ( 'post' == $post_type ) && !current_user_can('edit_post', $post_ID) ) 1082 return new IXR_Error(401, __('Sorry, you can not edit this post.')); 1083 1084 $postdata = wp_get_single_post($post_ID, ARRAY_A); 1085 1086 // If there is no post data for the give post id, stop 1087 // now and return an error. Other wise a new post will be 1088 // created (which was the old behavior). 1089 if(empty($postdata["ID"])) { 1090 return(new IXR_Error(404, __("Invalid post id."))); 1091 } 1092 1093 $this->escape($postdata); 1094 extract($postdata, EXTR_SKIP); 1095 1096 // Let WordPress manage slug if none was provided. 1097 $post_name = ""; 1098 if(isset($content_struct["wp_slug"])) { 1099 $post_name = $content_struct["wp_slug"]; 1100 } 1101 1102 // Only use a password if one was given. 1103 if(isset($content_struct["wp_password"])) { 1104 $post_password = $content_struct["wp_password"]; 1105 } 1106 1107 // Only set a post parent if one was given. 1108 if(isset($content_struct["wp_page_parent_id"])) { 1109 $post_parent = $content_struct["wp_page_parent_id"]; 1110 } 1111 1112 // Only set the menu_order if it was given. 1113 if(isset($content_struct["wp_page_order"])) { 1114 $menu_order = $content_struct["wp_page_order"]; 1115 } 1116 1117 $post_author = $postdata["post_author"]; 1118 1119 // Only set the post_author if one is set. 1120 if( 1121 isset($content_struct["wp_author_id"]) 1122 && ($user->ID != $content_struct["wp_author_id"]) 1123 ) { 1124 switch($post_type) { 1125 case "post": 1126 if(!current_user_can("edit_others_posts")) { 1127 return(new IXR_Error(401, __("You are not allowed to change the post author as this user."))); 1128 } 1129 break; 1130 case "page": 1131 if(!current_user_can("edit_others_pages")) { 1132 return(new IXR_Error(401, __("You are not allowed to change the page author as this user."))); 1133 } 1134 break; 1135 default: 1136 return(new IXR_Error(401, __("Invalid post type."))); 1137 break; 1138 } 1139 $post_author = $content_struct["wp_author_id"]; 1140 } 1141 1142 if(isset($content_struct["mt_allow_comments"])) { 1143 if(!is_numeric($content_struct["mt_allow_comments"])) { 1144 switch($content_struct["mt_allow_comments"]) { 1145 case "closed": 1146 $comment_status = "closed"; 1147 break; 1148 case "open": 1149 $comment_status = "open"; 1150 break; 1151 default: 1152 $comment_status = get_option("default_comment_status"); 1153 break; 1154 } 1155 } 1156 else { 1157 switch((int) $content_struct["mt_allow_comments"]) { 1158 case 0: 1159 $comment_status = "closed"; 1160 break; 1161 case 1: 1162 $comment_status = "open"; 1163 break; 1164 default: 1165 $comment_status = get_option("default_comment_status"); 1166 break; 1167 } 1168 } 1169 } 1170 1171 if(isset($content_struct["mt_allow_pings"])) { 1172 if(!is_numeric($content_struct["mt_allow_pings"])) { 1173 switch($content_struct["mt_allow_pings"]) { 1174 case "closed": 1175 $ping_status = "closed"; 1176 break; 1177 case "open": 1178 $ping_status = "open"; 1179 break; 1180 default: 1181 $ping_status = get_option("default_ping_status"); 1182 break; 1183 } 1184 } 1185 else { 1186 switch((int) $content_struct["mt_allow_pings"]) { 1187 case 0: 1188 $ping_status = "closed"; 1189 break; 1190 case 1: 1191 $ping_status = "open"; 1192 break; 1193 default: 1194 $ping_status = get_option("default_ping_status"); 1195 break; 1196 } 1197 } 1198 } 1199 1200 $post_title = $content_struct['title']; 1201 $post_content = apply_filters( 'content_save_pre', $content_struct['description'] ); 1202 $catnames = $content_struct['categories']; 1203 1204 $post_category = array(); 1205 1206 if (is_array($catnames)) { 1207 foreach ($catnames as $cat) { 1208 $post_category[] = get_cat_ID($cat); 1209 } 1210 } 1211 1212 $post_excerpt = $content_struct['mt_excerpt']; 1213 $post_more = $content_struct['mt_text_more']; 1214 $post_status = $publish ? 'publish' : 'draft'; 1215 1216 $tags_input = $content_struct['mt_keywords']; 1217 1218 if ( ('publish' == $post_status) ) { 1219 if ( ( 'page' == $post_type ) && !current_user_can('publish_pages') ) 1220 return new IXR_Error(401, __('Sorry, you do not have the right to publish this page.')); 1221 else if ( !current_user_can('publish_posts') ) 1222 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.')); 1223 } 1224 1225 if ($post_more) { 1226 $post_content = $post_content . "\n<!--more-->\n" . $post_more; 1227 } 1228 1229 $to_ping = $content_struct['mt_tb_ping_urls']; 1230 if ( is_array($to_ping) ) 1231 $to_ping = implode(' ', $to_ping); 1232 1233 // Do some timestamp voodoo 1234 $dateCreatedd = $content_struct['dateCreated']; 1235 if (!empty($dateCreatedd)) { 1236 $dateCreated = $dateCreatedd->getIso(); 1237 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 1238 $post_date_gmt = iso8601_to_datetime($dateCreated . "Z", GMT); 1239 } else { 1240 $post_date = $postdata['post_date']; 1241 $post_date_gmt = $postdata['post_date_gmt']; 1242 } 1243 1244 // We've got all the data -- post it: 1245 $newpost = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'post_date', 'post_date_gmt', 'to_ping', 'post_name', 'post_password', 'post_parent', 'menu_order', 'post_author', 'tags_input'); 1246 1247 $result = wp_update_post($newpost); 1248 if (!$result) { 1249 return new IXR_Error(500, __('Sorry, your entry could not be edited. Something wrong happened.')); 1250 } 1251 $this->attach_uploads( $ID, $post_content ); 1252 1253 logIO('O',"(MW) Edited ! ID: $post_ID"); 1254 1255 return true; 1256 } 1257 1258 1259 /* metaweblog.getPost ...returns a post */ 1260 function mw_getPost($args) { 1261 1262 global $wpdb; 1263 1264 $this->escape($args); 1265 1266 $post_ID = (int) $args[0]; 1267 $user_login = $args[1]; 1268 $user_pass = $args[2]; 1269 1270 if (!$this->login_pass_ok($user_login, $user_pass)) { 1271 return $this->error; 1272 } 1273 1274 $postdata = wp_get_single_post($post_ID, ARRAY_A); 1275 1276 if ($postdata['post_date'] != '') { 1277 1278 $post_date = mysql2date('Ymd\TH:i:s', $postdata['post_date']); 1279 $post_date_gmt = mysql2date('Ymd\TH:i:s', $postdata['post_date_gmt']); 1280 1281 $categories = array(); 1282 $catids = wp_get_post_categories($post_ID); 1283 foreach($catids as $catid) { 1284 $categories[] = get_cat_name($catid); 1285 } 1286 1287 $tagnames = array(); 1288 $tags = wp_get_post_tags( $post_ID ); 1289 if ( !empty( $tags ) ) { 1290 foreach ( $tags as $tag ) { 1291 $tagnames[] = $tag->name; 1292 } 1293 $tagnames = implode( ', ', $tagnames ); 1294 } else { 1295 $tagnames = ''; 1296 } 1297 1298 $post = get_extended($postdata['post_content']); 1299 $link = post_permalink($postdata['ID']); 1300 1301 // Get the author info. 1302 $author = get_userdata($postdata['post_author']); 1303 1304 $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0; 1305 $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0; 1306 1307 $resp = array( 1308 'dateCreated' => new IXR_Date($post_date), 1309 'userid' => $postdata['post_author'], 1310 'postid' => $postdata['ID'], 1311 'description' => $post['main'], 1312 'title' => $postdata['post_title'], 1313 'link' => $link, 1314 'permaLink' => $link, 1315 // commented out because no other tool seems to use this 1316 // 'content' => $entry['post_content'], 1317 'categories' => $categories, 1318 'mt_excerpt' => $postdata['post_excerpt'], 1319 'mt_text_more' => $post['extended'], 1320 'mt_allow_comments' => $allow_comments, 1321 'mt_allow_pings' => $allow_pings, 1322 'mt_keywords' => $tagnames, 1323 'wp_slug' => $postdata['post_name'], 1324 'wp_password' => $postdata['post_password'], 1325 'wp_author_id' => $author->ID, 1326 'wp_author_display_name' => $author->display_name, 1327 'date_created_gmt' => new IXR_Date($post_date_gmt) 1328 ); 1329 1330 return $resp; 1331 } else { 1332 return new IXR_Error(404, __('Sorry, no such post.')); 1333 } 1334 } 1335 1336 1337 /* metaweblog.getRecentPosts ...returns recent posts */ 1338 function mw_getRecentPosts($args) { 1339 1340 $this->escape($args); 1341 1342 $blog_ID = (int) $args[0]; 1343 $user_login = $args[1]; 1344 $user_pass = $args[2]; 1345 $num_posts = (int) $args[3]; 1346 1347 if (!$this->login_pass_ok($user_login, $user_pass)) { 1348 return $this->error; 1349 } 1350 1351 $posts_list = wp_get_recent_posts($num_posts); 1352 1353 if (!$posts_list) { 1354 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 1355 return $this->error; 1356 } 1357 1358 foreach ($posts_list as $entry) { 1359 1360 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date']); 1361 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt']); 1362 1363 $categories = array(); 1364 $catids = wp_get_post_categories($entry['ID']); 1365 foreach($catids as $catid) { 1366 $categories[] = get_cat_name($catid); 1367 } 1368 1369 $tagnames = array(); 1370 $tags = wp_get_post_tags( $entry['ID'] ); 1371 if ( !empty( $tags ) ) { 1372 foreach ( $tags as $tag ) { 1373 $tagnames[] = $tag->name; 1374 } 1375 $tagnames = implode( ', ', $tagnames ); 1376 } else { 1377 $tagnames = ''; 1378 } 1379 1380 $post = get_extended($entry['post_content']); 1381 $link = post_permalink($entry['ID']); 1382 1383 // Get the post author info. 1384 $author = get_userdata($entry['post_author']); 1385 1386 $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0; 1387 $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0; 1388 1389 $struct[] = array( 1390 'dateCreated' => new IXR_Date($post_date), 1391 'userid' => $entry['post_author'], 1392 'postid' => $entry['ID'], 1393 'description' => $post['main'], 1394 'title' => $entry['post_title'], 1395 'link' => $link, 1396 'permaLink' => $link, 1397 // commented out because no other tool seems to use this 1398 // 'content' => $entry['post_content'], 1399 'categories' => $categories, 1400 'mt_excerpt' => $entry['post_excerpt'], 1401 'mt_text_more' => $post['extended'], 1402 'mt_allow_comments' => $allow_comments, 1403 'mt_allow_pings' => $allow_pings, 1404 'mt_keywords' => $tagnames, 1405 'wp_slug' => $entry['post_name'], 1406 'wp_password' => $entry['post_password'], 1407 'wp_author_id' => $author->ID, 1408 'wp_author_display_name' => $author->display_name, 1409 'date_created_gmt' => new IXR_Date($post_date_gmt) 1410 ); 1411 1412 } 1413 1414 $recent_posts = array(); 1415 for ($j=0; $j<count($struct); $j++) { 1416 array_push($recent_posts, $struct[$j]); 1417 } 1418 1419 return $recent_posts; 1420 } 1421 1422 1423 /* metaweblog.getCategories ...returns the list of categories on a given blog */ 1424 function mw_getCategories($args) { 1425 1426 global $wpdb; 1427 1428 $this->escape($args); 1429 1430 $blog_ID = (int) $args[0]; 1431 $user_login = $args[1]; 1432 $user_pass = $args[2]; 1433 1434 if (!$this->login_pass_ok($user_login, $user_pass)) { 1435 return $this->error; 1436 } 1437 1438 $categories_struct = array(); 1439 1440 if ( $cats = get_categories('get=all') ) { 1441 foreach ( $cats as $cat ) { 1442 $struct['categoryId'] = $cat->term_id; 1443 $struct['parentId'] = $cat->parent; 1444 $struct['description'] = $cat->name; 1445 $struct['categoryName'] = $cat->name; 1446 $struct['htmlUrl'] = wp_specialchars(get_category_link($cat->term_id)); 1447 $struct['rssUrl'] = wp_specialchars(get_category_rss_link(false, $cat->term_id, $cat->name)); 1448 1449 $categories_struct[] = $struct; 1450 } 1451 } 1452 1453 return $categories_struct; 1454 } 1455 1456 1457 /* metaweblog.newMediaObject uploads a file, following your settings */ 1458 function mw_newMediaObject($args) { 1459 // adapted from a patch by Johann Richard 1460 // http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/ 1461 1462 global $wpdb; 1463 1464 $blog_ID = (int) $args[0]; 1465 $user_login = $wpdb->escape($args[1]); 1466 $user_pass = $wpdb->escape($args[2]); 1467 $data = $args[3]; 1468 1469 $name = sanitize_file_name( $data['name'] ); 1470 $type = $data['type']; 1471 $bits = $data['bits']; 1472 1473 logIO('O', '(MW) Received '.strlen($bits).' bytes'); 1474 1475 if ( !$this->login_pass_ok($user_login, $user_pass) ) 1476 return $this->error; 1477 1478 set_current_user(0, $user_login); 1479 if ( !current_user_can('upload_files') ) { 1480 logIO('O', '(MW) User does not have upload_files capability'); 1481 $this->error = new IXR_Error(401, __('You are not allowed to upload files to this site.')); 1482 return $this->error; 1483 } 1484 1485 if ( $upload_err = apply_filters( "pre_upload_error", false ) ) 1486 return new IXR_Error(500, $upload_err); 1487 1488 if(!empty($data["overwrite"]) && ($data["overwrite"] == true)) { 1489 // Get postmeta info on the object. 1490 $old_file = $wpdb->get_row(" 1491 SELECT ID 1492 FROM {$wpdb->posts} 1493 WHERE post_title = '{$name}' 1494 AND post_type = 'attachment' 1495 "); 1496 1497 // Delete previous file. 1498 wp_delete_attachment($old_file->ID); 1499 1500 // Make sure the new name is different by pre-pending the 1501 // previous post id. 1502 $filename = preg_replace("/^wpid\d+-/", "", $name); 1503 $name = "wpid{$old_file->ID}-{$filename}"; 1504 } 1505 1506 $upload = wp_upload_bits($name, $type, $bits, $overwrite); 1507 if ( ! empty($upload['error']) ) { 1508 $errorString = sprintf(__('Could not write file %1$s (%2$s)'), $name, $upload['error']); 1509 logIO('O', '(MW) ' . $errorString); 1510 return new IXR_Error(500, $errorString); 1511 } 1512 // Construct the attachment array 1513 // attach to post_id -1 1514 $post_id = -1; 1515 $attachment = array( 1516 'post_title' => $name, 1517 'post_content' => '', 1518 'post_type' => 'attachment', 1519 'post_parent' => $post_id, 1520 'post_mime_type' => $type, 1521 'guid' => $upload[ 'url' ] 1522 ); 1523 1524 // Save the data 1525 $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id ); 1526 wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) ); 1527 1528 return apply_filters( 'wp_handle_upload', array( 'file' => $name, 'url' => $upload[ 'url' ], 'type' => $type ) ); 1529 } 1530 1531 1532 /* MovableType API functions 1533 * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html 1534 */ 1535 1536 /* mt.getRecentPostTitles ...returns recent posts' titles */ 1537 function mt_getRecentPostTitles($args) { 1538 1539 $this->escape($args); 1540 1541 $blog_ID = (int) $args[0]; 1542 $user_login = $args[1]; 1543 $user_pass = $args[2]; 1544 $num_posts = (int) $args[3]; 1545 1546 if (!$this->login_pass_ok($user_login, $user_pass)) { 1547 return $this->error; 1548 } 1549 1550 $posts_list = wp_get_recent_posts($num_posts); 1551 1552 if (!$posts_list) { 1553 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 1554 return $this->error; 1555 } 1556 1557 foreach ($posts_list as $entry) { 1558 1559 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date']); 1560 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt']); 1561 1562 $struct[] = array( 1563 'dateCreated' => new IXR_Date($post_date), 1564 'userid' => $entry['post_author'], 1565 'postid' => $entry['ID'], 1566 'title' => $entry['post_title'], 1567 'date_created_gmt' => new IXR_Date($post_date_gmt) 1568 ); 1569 1570 } 1571 1572 $recent_posts = array(); 1573 for ($j=0; $j<count($struct); $j++) { 1574 array_push($recent_posts, $struct[$j]); 1575 } 1576 1577 return $recent_posts; 1578 } 1579 1580 1581 /* mt.getCategoryList ...returns the list of categories on a given blog */ 1582 function mt_getCategoryList($args) { 1583 1584 global $wpdb; 1585 1586 $this->escape($args); 1587 1588 $blog_ID = (int) $args[0]; 1589 $user_login = $args[1]; 1590 $user_pass = $args[2]; 1591 1592 if (!$this->login_pass_ok($user_login, $user_pass)) { 1593 return $this->error; 1594 } 1595 1596 $categories_struct = array(); 1597 1598 // FIXME: can we avoid using direct SQL there? 1599 if ( $cats = get_categories('hide_empty=0&hierarchical=0') ) { 1600 foreach ($cats as $cat) { 1601 $struct['categoryId'] = $cat->term_id; 1602 $struct['categoryName'] = $cat->name; 1603 1604 $categories_struct[] = $struct; 1605 } 1606 } 1607 1608 return $categories_struct; 1609 } 1610 1611 1612 /* mt.getPostCategories ...returns a post's categories */ 1613 function mt_getPostCategories($args) { 1614 1615 $this->escape($args); 1616 1617 $post_ID = (int) $args[0]; 1618 $user_login = $args[1]; 1619 $user_pass = $args[2]; 1620 1621 if (!$this->login_pass_ok($user_login, $user_pass)) { 1622 return $this->error; 1623 } 1624 1625 $categories = array(); 1626 $catids = wp_get_post_categories(intval($post_ID)); 1627 // first listed category will be the primary category 1628 $isPrimary = true; 1629 foreach($catids as $catid) { 1630 $categories[] = array( 1631 'categoryName' => get_cat_name($catid), 1632 'categoryId' => (string) $catid, 1633 'isPrimary' => $isPrimary 1634 ); 1635 $isPrimary = false; 1636 } 1637 1638 return $categories; 1639 } 1640 1641 1642 /* mt.setPostCategories ...sets a post's categories */ 1643 function mt_setPostCategories($args) { 1644 1645 $this->escape($args); 1646 1647 $post_ID = (int) $args[0]; 1648 $user_login = $args[1]; 1649 $user_pass = $args[2]; 1650 $categories = $args[3]; 1651 1652 if (!$this->login_pass_ok($user_login, $user_pass)) { 1653 return $this->error; 1654 } 1655 1656 set_current_user(0, $user_login); 1657 if ( !current_user_can('edit_post', $post_ID) ) 1658 return new IXR_Error(401, __('Sorry, you can not edit this post.')); 1659 1660 foreach($categories as $cat) { 1661 $catids[] = $cat['categoryId']; 1662 } 1663 1664 wp_set_post_categories($post_ID, $catids); 1665 1666 return true; 1667 } 1668 1669 1670 /* mt.supportedMethods ...returns an array of methods supported by this server */ 1671 function mt_supportedMethods($args) { 1672 1673 $supported_methods = array(); 1674 foreach($this->methods as $key=>$value) { 1675 $supported_methods[] = $key; 1676 } 1677 1678 return $supported_methods; 1679 } 1680 1681 1682 /* mt.supportedTextFilters ...returns an empty array because we don't 1683 support per-post text filters yet */ 1684 function mt_supportedTextFilters($args) { 1685 return apply_filters('xmlrpc_text_filters', array()); 1686 } 1687 1688 1689 /* mt.getTrackbackPings ...returns trackbacks sent to a given post */ 1690 function mt_getTrackbackPings($args) { 1691 1692 global $wpdb; 1693 1694 $post_ID = intval($args); 1695 1696 $actual_post = wp_get_single_post($post_ID, ARRAY_A); 1697 1698 if (!$actual_post) { 1699 return new IXR_Error(404, __('Sorry, no such post.')); 1700 } 1701 1702 $comments = $wpdb->get_results("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = $post_ID"); 1703 1704 if (!$comments) { 1705 return array(); 1706 } 1707 1708 $trackback_pings = array(); 1709 foreach($comments as $comment) { 1710 if ( 'trackback' == $comment->comment_type ) { 1711 $content = $comment->comment_content; 1712 $title = substr($content, 8, (strpos($content, '</strong>') - 8)); 1713 $trackback_pings[] = array( 1714 'pingTitle' => $title, 1715 'pingURL' => $comment->comment_author_url, 1716 'pingIP' => $comment->comment_author_IP 1717 ); 1718 } 1719 } 1720 1721 return $trackback_pings; 1722 } 1723 1724 1725 /* mt.publishPost ...sets a post's publish status to 'publish' */ 1726 function mt_publishPost($args) { 1727 1728 $this->escape($args); 1729 1730 $post_ID = (int) $args[0]; 1731 $user_login = $args[1]; 1732 $user_pass = $args[2]; 1733 1734 if (!$this->login_pass_ok($user_login, $user_pass)) { 1735 return $this->error; 1736 } 1737 1738 set_current_user(0, $user_login); 1739 if ( !current_user_can('edit_post', $post_ID) ) 1740 return new IXR_Error(401, __('Sorry, you can not edit this post.')); 1741 1742 $postdata = wp_get_single_post($post_ID,ARRAY_A); 1743 1744 $postdata['post_status'] = 'publish'; 1745 1746 // retain old cats 1747 $cats = wp_get_post_categories($post_ID); 1748 $postdata['post_category'] = $cats; 1749 $this->escape($postdata); 1750 1751 $result = wp_update_post($postdata); 1752 1753 return $result; 1754 } 1755 1756 1757 1758 /* PingBack functions 1759 * specs on www.hixie.ch/specs/pingback/pingback 1760 */ 1761 1762 /* pingback.ping gets a pingback and registers it */ 1763 function pingback_ping($args) { 1764 global $wpdb, $wp_version; 1765 1766 $this->escape($args); 1767 1768 $pagelinkedfrom = $args[0]; 1769 $pagelinkedto = $args[1]; 1770 1771 $title = ''; 1772 1773 $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom); 1774 $pagelinkedto = preg_replace('#&([^amp\;])#is', '&$1', $pagelinkedto); 1775 1776 $error_code = -1; 1777 1778 // Check if the page linked to is in our site 1779 $pos1 = strpos($pagelinkedto, str_replace(array('http://www.','http://','https://www.','https://'), '', get_option('home'))); 1780 if( !$pos1 ) 1781 return new IXR_Error(0, __('Is there no link to us?')); 1782 1783 // let's find which post is linked to 1784 // FIXME: does url_to_postid() cover all these cases already? 1785 // if so, then let's use it and drop the old code. 1786 $urltest = parse_url($pagelinkedto); 1787 if ($post_ID = url_to_postid($pagelinkedto)) { 1788 $way = 'url_to_postid()'; 1789 } elseif (preg_match('#p/[0-9]{1,}#', $urltest['path'], $match)) { 1790 // the path defines the post_ID (archives/p/XXXX) 1791 $blah = explode('/', $match[0]); 1792 $post_ID = (int) $blah[1]; 1793 $way = 'from the path'; 1794 } elseif (preg_match('#p=[0-9]{1,}#', $urltest['query'], $match)) { 1795 // the querystring defines the post_ID (?p=XXXX) 1796 $blah = explode('=', $match[0]); 1797 $post_ID = (int) $blah[1]; 1798 $way = 'from the querystring'; 1799 } elseif (isset($urltest['fragment'])) { 1800 // an #anchor is there, it's either... 1801 if (intval($urltest['fragment'])) { 1802 // ...an integer #XXXX (simpliest case) 1803 $post_ID = (int) $urltest['fragment']; 1804 $way = 'from the fragment (numeric)'; 1805 } elseif (preg_match('/post-[0-9]+/',$urltest['fragment'])) { 1806 // ...a post id in the form 'post-###' 1807 $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']); 1808 $way = 'from the fragment (post-###)'; 1809 } elseif (is_string($urltest['fragment'])) { 1810 // ...or a string #title, a little more complicated 1811 $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']); 1812 $sql = "SELECT ID FROM $wpdb->posts WHERE post_title RLIKE '$title'"; 1813 if (! ($post_ID = $wpdb->get_var($sql)) ) { 1814 // returning unknown error '0' is better than die()ing 1815 return new IXR_Error(0, ''); 1816 } 1817 $way = 'from the fragment (title)'; 1818 } 1819 } else { 1820 // TODO: Attempt to extract a post ID from the given URL 1821 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.')); 1822 } 1823 $post_ID = (int) $post_ID; 1824 1825 1826 logIO("O","(PB) URL='$pagelinkedto' ID='$post_ID' Found='$way'"); 1827 1828 $post = get_post($post_ID); 1829 1830 if ( !$post ) // Post_ID not found 1831 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.')); 1832 1833 if ( $post_ID == url_to_postid($pagelinkedfrom) ) 1834 return new IXR_Error(0, __('The source URL and the target URL cannot both point to the same resource.')); 1835 1836 // Check if pings are on 1837 if ( 'closed' == $post->ping_status ) 1838 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.')); 1839 1840 // Let's check that the remote site didn't already pingback this entry 1841 $result = $wpdb->get_results("SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$post_ID' AND comment_author_url = '$pagelinkedfrom'"); 1842 1843 if ( $wpdb->num_rows ) // We already have a Pingback from this URL 1844 return new IXR_Error(48, __('The pingback has already been registered.')); 1845 1846 // very stupid, but gives time to the 'from' server to publish ! 1847 sleep(1); 1848 1849 // Let's check the remote site 1850 $linea = wp_remote_fopen( $pagelinkedfrom ); 1851 if ( !$linea ) 1852 return new IXR_Error(16, __('The source URL does not exist.')); 1853 1854 // Work around bug in strip_tags(): 1855 $linea = str_replace('<!DOC', '<DOC', $linea); 1856 $linea = preg_replace( '/[\s\r\n\t]+/', ' ', $linea ); // normalize spaces 1857 $linea = preg_replace( "/ <(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $linea ); 1858 1859 preg_match('|<title>([^<]*?)</title>|is', $linea, $matchtitle); 1860 $title = $matchtitle[1]; 1861 if ( empty( $title ) ) 1862 return new IXR_Error(32, __('We cannot find a title on that page.')); 1863 1864 $linea = strip_tags( $linea, '<a>' ); // just keep the tag we need 1865 1866 $p = explode( "\n\n", $linea ); 1867 1868 $preg_target = preg_quote($pagelinkedto); 1869 1870 foreach ( $p as $para ) { 1871 if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link? 1872 preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context); 1873 1874 // If the URL isn't in a link context, keep looking 1875 if ( empty($context) ) 1876 continue; 1877 1878 // We're going to use this fake tag to mark the context in a bit 1879 // the marker is needed in case the link text appears more than once in the paragraph 1880 $excerpt = preg_replace('|\</?wpcontext\>|', '', $para); 1881 1882 // prevent really long link text 1883 if ( strlen($context[1]) > 100 ) 1884 $context[1] = substr($context[1], 0, 100) . '...'; 1885 1886 $marker = '<wpcontext>'.$context[1].'</wpcontext>'; // set up our marker 1887 $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker 1888 $excerpt = strip_tags($excerpt, '<wpcontext>'); // strip all tags but our context marker 1889 $excerpt = trim($excerpt); 1890 $preg_marker = preg_quote($marker); 1891 $excerpt = preg_replace("|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt); 1892 $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper 1893 break; 1894 } 1895 } 1896 1897 if ( empty($context) ) // Link to target not found 1898 return new IXR_Error(17, __('The source URL does not contain a link to the target URL, and so cannot be used as a source.')); 1899 1900 $pagelinkedfrom = preg_replace('#&([^amp\;])#is', '&$1', $pagelinkedfrom); 1901 1902 $context = '[...] ' . wp_specialchars( $excerpt ) . ' [...]'; 1903 $original_pagelinkedfrom = $pagelinkedfrom; 1904 $pagelinkedfrom = $wpdb->escape( $pagelinkedfrom ); 1905 $original_title = $title; 1906 1907 $comment_post_ID = (int) $post_ID; 1908 $comment_author = $title; 1909 $this->escape($comment_author); 1910 $comment_author_url = $pagelinkedfrom; 1911 $comment_content = $context; 1912 $this->escape($comment_content); 1913 $comment_type = 'pingback'; 1914 1915 $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_content', 'comment_type'); 1916 1917 $comment_ID = wp_new_comment($commentdata); 1918 do_action('pingback_post', $comment_ID); 1919 1920 return sprintf(__('Pingback from %1$s to %2$s registered. Keep the web talking! :-)'), $pagelinkedfrom, $pagelinkedto); 1921 } 1922 1923 1924 /* pingback.extensions.getPingbacks returns an array of URLs 1925 that pingbacked the given URL 1926 specs on http://www.aquarionics.com/misc/archives/blogite/0198.html */ 1927 function pingback_extensions_getPingbacks($args) { 1928 1929 global $wpdb; 1930 1931 $this->escape($args); 1932 1933 $url = $args; 1934 1935 $post_ID = url_to_postid($url); 1936 if (!$post_ID) { 1937 // We aren't sure that the resource is available and/or pingback enabled 1938 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.')); 1939 } 1940 1941 $actual_post = wp_get_single_post($post_ID, ARRAY_A); 1942 1943 if (!$actual_post) { 1944 // No such post = resource not found 1945 return new IXR_Error(32, __('The specified target URL does not exist.')); 1946 } 1947 1948 $comments = $wpdb->get_results("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = $post_ID"); 1949 1950 if (!$comments) { 1951 return array(); 1952 } 1953 1954 $pingbacks = array(); 1955 foreach($comments as $comment) { 1956 if ( 'pingback' == $comment->comment_type ) 1957 $pingbacks[] = $comment->comment_author_url; 1958 } 1959 1960 return $pingbacks; 1961 } 1962 } 1963 No newline at end of file -
class/WP/XmlRpc/Server.php
1 <?php 2 3 class WP_XmlRpc_Server extends IXR_Server { 4 5 /** 6 * WP_XmlRpc_Server() - 7 * 8 * 9 */ 10 function WP_XmlRpc_Server() { 11 $this->methods = array( 12 // WordPress API 13 'wp.getPage' => 'this:wp_getPage', 14 'wp.getPages' => 'this:wp_getPages', 15 'wp.newPage' => 'this:wp_newPage', 16 'wp.deletePage' => 'this:wp_deletePage', 17 'wp.editPage' => 'this:wp_editPage', 18 'wp.getPageList' => 'this:wp_getPageList', 19 'wp.getAuthors' => 'this:wp_getAuthors', 20 'wp.getCategories' => 'this:mw_getCategories', // Alias 21 'wp.newCategory' => 'this:wp_newCategory', 22 'wp.suggestCategories' => 'this:wp_suggestCategories', 23 'wp.uploadFile' => 'this:mw_newMediaObject', // Alias 24 25 // Blogger API 26 'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs', 27 'blogger.getUserInfo' => 'this:blogger_getUserInfo', 28 'blogger.getPost' => 'this:blogger_getPost', 29 'blogger.getRecentPosts' => 'this:blogger_getRecentPosts', 30 'blogger.getTemplate' => 'this:blogger_getTemplate', 31 'blogger.setTemplate' => 'this:blogger_setTemplate', 32 'blogger.newPost' => 'this:blogger_newPost', 33 'blogger.editPost' => 'this:blogger_editPost', 34 'blogger.deletePost' => 'this:blogger_deletePost', 35 36 // MetaWeblog API (with MT extensions to structs) 37 'metaWeblog.newPost' => 'this:mw_newPost', 38 'metaWeblog.editPost' => 'this:mw_editPost', 39 'metaWeblog.getPost' => 'this:mw_getPost', 40 'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts', 41 'metaWeblog.getCategories' => 'this:mw_getCategories', 42 'metaWeblog.newMediaObject' => 'this:mw_newMediaObject', 43 44 // MetaWeblog API aliases for Blogger API 45 // see http://www.xmlrpc.com/stories/storyReader$2460 46 'metaWeblog.deletePost' => 'this:blogger_deletePost', 47 'metaWeblog.getTemplate' => 'this:blogger_getTemplate', 48 'metaWeblog.setTemplate' => 'this:blogger_setTemplate', 49 'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs', 50 51 // MovableType API 52 'mt.getCategoryList' => 'this:mt_getCategoryList', 53 'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles', 54 'mt.getPostCategories' => 'this:mt_getPostCategories', 55 'mt.setPostCategories' => 'this:mt_setPostCategories', 56 'mt.supportedMethods' => 'this:mt_supportedMethods', 57 'mt.supportedTextFilters' => 'this:mt_supportedTextFilters', 58 'mt.getTrackbackPings' => 'this:mt_getTrackbackPings', 59 'mt.publishPost' => 'this:mt_publishPost', 60 61 // PingBack 62 'pingback.ping' => 'this:pingback_ping', 63 'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks', 64 65 'demo.sayHello' => 'this:sayHello', 66 'demo.addTwoNumbers' => 'this:addTwoNumbers' 67 ); 68 $this->methods = apply_filters('xmlrpc_methods', $this->methods); 69 $this->IXR_Server($this->methods); 70 } 71 72 function sayHello($args) { 73 return 'Hello!'; 74 } 75 76 function addTwoNumbers($args) { 77 $number1 = $args[0]; 78 $number2 = $args[1]; 79 return $number1 + $number2; 80 } 81 82 function login_pass_ok($user_login, $user_pass) { 83 if (!user_pass_ok($user_login, $user_pass)) { 84 $this->error = new IXR_Error(403, __('Bad login/pass combination.')); 85 return false; 86 } 87 return true; 88 } 89 90 function escape(&$array) { 91 global $wpdb; 92 93 if(!is_array($array)) { 94 return($wpdb->escape($array)); 95 } 96 else { 97 foreach ( (array) $array as $k => $v ) { 98 if (is_array($v)) { 99 $this->escape($array[$k]); 100 } else if (is_object($v)) { 101 //skip 102 } else { 103 $array[$k] = $wpdb->escape($v); 104 } 105 } 106 } 107 } 108 109 /** 110 * WordPress XML-RPC API 111 * wp_getPage 112 */ 113 function wp_getPage($args) { 114 $this->escape($args); 115 116 $blog_id = (int) $args[0]; 117 $page_id = (int) $args[1]; 118 $username = $args[2]; 119 $password = $args[3]; 120 121 if(!$this->login_pass_ok($username, $password)) { 122 return($this->error); 123 } 124 125 // Lookup page info. 126 $page = get_page($page_id); 127 128 // If we found the page then format the data. 129 if($page->ID && ($page->post_type == "page")) { 130 // Get all of the page content and link. 131 $full_page = get_extended($page->post_content); 132 $link = post_permalink($page->ID); 133 134 // Get info the page parent if there is one. 135 $parent_title = ""; 136 if(!empty($page->post_parent)) { 137 $parent = get_page($page->post_parent); 138 $parent_title = $parent->post_title; 139 } 140 141 // Determine comment and ping settings. 142 $allow_comments = ("open" == $page->comment_status) ? 1 : 0; 143 $allow_pings = ("open" == $page->ping_status) ? 1 : 0; 144 145 // Format page date. 146 $page_date = mysql2date("Ymd\TH:i:s", $page->post_date); 147 $page_date_gmt = mysql2date("Ymd\TH:i:s", $page->post_date_gmt); 148 149 // Pull the categories info together. 150 $categories = array(); 151 foreach(wp_get_post_categories($page->ID) as $cat_id) { 152 $categories[] = get_cat_name($cat_id); 153 } 154 155 // Get the author info. 156 $author = get_userdata($page->post_author); 157 158 $page_struct = array( 159 "dateCreated" => new IXR_Date($page_date), 160 "userid" => $page->post_author, 161 "page_id" => $page->ID, 162 "page_status" => $page->post_status, 163 "description" => $full_page["main"], 164 "title" => $page->post_title, 165 "link" => $link, 166 "permaLink" => $link, 167 "categories" => $categories, 168 "excerpt" => $page->post_excerpt, 169 "text_more" => $full_page["extended"], 170 "mt_allow_comments" => $allow_comments, 171 "mt_allow_pings" => $allow_pings, 172 "wp_slug" => $page->post_name, 173 "wp_password" => $page->post_password, 174 "wp_author" => $author->display_name, 175 "wp_page_parent_id" => $page->post_parent, 176 "wp_page_parent_title" => $parent_title, 177 "wp_page_order" => $page->menu_order, 178 "wp_author_id" => $author->ID, 179 "wp_author_display_name" => $author->display_name, 180 "date_created_gmt" => new IXR_Date($page_date_gmt) 181 ); 182 183 return($page_struct); 184 } 185 // If the page doesn't exist indicate that. 186 else { 187 return(new IXR_Error(404, __("Sorry, no such page."))); 188 } 189 } 190 191 /** 192 * WordPress XML-RPC API 193 * wp_getPages 194 */ 195 function wp_getPages($args) { 196 $this->escape($args); 197 198 $blog_id = (int) $args[0]; 199 $username = $args[1]; 200 $password = $args[2]; 201 202 if(!$this->login_pass_ok($username, $password)) { 203 return($this->error); 204 } 205 206 // Lookup info on pages. 207 $pages = get_pages(); 208 $num_pages = count($pages); 209 210 // If we have pages, put together their info. 211 if($num_pages >= 1) { 212 $pages_struct = array(); 213 214 for($i = 0; $i < $num_pages; $i++) { 215 $page = wp_xmlrpc_server::wp_getPage(array( 216 $blog_id, $pages[$i]->ID, $username, $password 217 )); 218 $pages_struct[] = $page; 219 } 220 221 return($pages_struct); 222 } 223 // If no pages were found return an error. 224 else { 225 return(array()); 226 } 227 } 228 229 /** 230 * WordPress XML-RPC API 231 * wp_newPage 232 */ 233 function wp_newPage($args) { 234 // Items not escaped here will be escaped in newPost. 235 $username = $this->escape($args[1]); 236 $password = $this->escape($args[2]); 237 $page = $args[3]; 238 $publish = $args[4]; 239 240 if(!$this->login_pass_ok($username, $password)) { 241 return($this->error); 242 } 243 244 // Set the user context and check if they are allowed 245 // to add new pages. 246 $user = set_current_user(0, $username); 247 if(!current_user_can("publish_pages")) { 248 return(new IXR_Error(401, __("Sorry, you can not add new pages."))); 249 } 250 251 // Mark this as content for a page. 252 $args[3]["post_type"] = "page"; 253 254 // Let mw_newPost do all of the heavy lifting. 255 return($this->mw_newPost($args)); 256 } 257 258 /** 259 * WordPress XML-RPC API 260 * wp_deletePage 261 */ 262 function wp_deletePage($args) { 263 $this->escape($args); 264 265 $blog_id = (int) $args[0]; 266 $username = $args[1]; 267 $password = $args[2]; 268 $page_id = (int) $args[3]; 269 270 if(!$this->login_pass_ok($username, $password)) { 271 return($this->error); 272 } 273 274 // Get the current page based on the page_id and 275 // make sure it is a page and not a post. 276 $actual_page = wp_get_single_post($page_id, ARRAY_A); 277 if( 278 !$actual_page 279 || ($actual_page["post_type"] != "page") 280 ) { 281 return(new IXR_Error(404, __("Sorry, no such page."))); 282 } 283 284 // Set the user context and make sure they can delete pages. 285 set_current_user(0, $username); 286 if(!current_user_can("delete_page", $page_id)) { 287 return(new IXR_Error(401, __("Sorry, you do not have the right to delete this page."))); 288 } 289 290 // Attempt to delete the page. 291 $result = wp_delete_post($page_id); 292 if(!$result) { 293 return(new IXR_Error(500, __("Failed to delete the page."))); 294 } 295 296 return(true); 297 } 298 299 /** 300 * WordPress XML-RPC API 301 * wp_editPage 302 */ 303 function wp_editPage($args) { 304 // Items not escaped here will be escaped in editPost. 305 $blog_id = (int) $args[0]; 306 $page_id = (int) $this->escape($args[1]); 307 $username = $this->escape($args[2]); 308 $password = $this->escape($args[3]); 309 $content = $args[4]; 310 $publish = $args[5]; 311 312 if(!$this->login_pass_ok($username, $password)) { 313 return($this->error); 314 } 315 316 // Get the page data and make sure it is a page. 317 $actual_page = wp_get_single_post($page_id, ARRAY_A); 318 if( 319 !$actual_page 320 || ($actual_page["post_type"] != "page") 321 ) { 322 return(new IXR_Error(404, __("Sorry, no such page."))); 323 } 324 325 // Set the user context and make sure they are allowed to edit pages. 326 set_current_user(0, $username); 327 if(!current_user_can("edit_page", $page_id)) { 328 return(new IXR_Error(401, __("Sorry, you do not have the right to edit this page."))); 329 } 330 331 // Mark this as content for a page. 332 $content["post_type"] = "page"; 333 334 // Arrange args in the way mw_editPost understands. 335 $args = array( 336 $page_id, 337 $username, 338 $password, 339 $content, 340 $publish 341 ); 342 343 // Let mw_editPost do all of the heavy lifting. 344 return($this->mw_editPost($args)); 345 } 346 347 /** 348 * WordPress XML-RPC API 349 * wp_getPageList 350 */ 351 function wp_getPageList($args) { 352 global $wpdb; 353 354 $this->escape($args); 355 356 $blog_id = (int) $args[0]; 357 $username = $args[1]; 358 $password = $args[2]; 359 360 if(!$this->login_pass_ok($username, $password)) { 361 return($this->error); 362 } 363 364 // Get list of pages ids and titles 365 $page_list = $wpdb->get_results(" 366 SELECT ID page_id, 367 post_title page_title, 368 post_parent page_parent_id, 369 post_date_gmt, 370 post_date 371 FROM {$wpdb->posts} 372 WHERE post_type = 'page' 373 ORDER BY ID 374 "); 375 376 // The date needs to be formated properly. 377 $num_pages = count($page_list); 378 for($i = 0; $i < $num_pages; $i++) { 379 $post_date = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date); 380 $post_date_gmt = mysql2date("Ymd\TH:i:s", $page_list[$i]->post_date_gmt); 381 382 $page_list[$i]->dateCreated = new IXR_Date($post_date); 383 $page_list[$i]->date_created_gmt = new IXR_Date($post_date_gmt); 384 385 unset($page_list[$i]->post_date_gmt); 386 unset($page_list[$i]->post_date); 387 } 388 389 return($page_list); 390 } 391 392 /** 393 * WordPress XML-RPC API 394 * wp_getAuthors 395 */ 396 function wp_getAuthors($args) { 397 global $wpdb; 398 399 $this->escape($args); 400 401 $blog_id = (int) $args[0]; 402 $username = $args[1]; 403 $password = $args[2]; 404 405 if(!$this->login_pass_ok($username, $password)) { 406 return($this->error); 407 } 408 409 return(get_users_of_blog()); 410 } 411 412 /** 413 * WordPress XML-RPC API 414 * wp_newCategory 415 */ 416 function wp_newCategory($args) { 417 $this->escape($args); 418 419 $blog_id = (int) $args[0]; 420 $username = $args[1]; 421 $password = $args[2]; 422 $category = $args[3]; 423 424 if(!$this->login_pass_ok($username, $password)) { 425 return($this->error); 426 } 427 428 // Set the user context and make sure they are 429 // allowed to add a category. 430 set_current_user(0, $username); 431 if(!current_user_can("manage_categories", $page_id)) { 432 return(new IXR_Error(401, __("Sorry, you do not have the right to add a category."))); 433 } 434 435 // If no slug was provided make it empty so that 436 // WordPress will generate one. 437 if(empty($category["slug"])) { 438 $category["slug"] = ""; 439 } 440 441 // If no parent_id was provided make it empty 442 // so that it will be a top level page (no parent). 443 if ( !isset($category["parent_id"]) ) 444 $category["parent_id"] = ""; 445 446 // If no description was provided make it empty. 447 if(empty($category["description"])) { 448 $category["description"] = ""; 449 } 450 451 $new_category = array( 452 "cat_name" => $category["name"], 453 "category_nicename" => $category["slug"], 454 "category_parent" => $category["parent_id"], 455 "category_description" => $category["description"] 456 ); 457 458 $cat_id = wp_insert_category($new_category); 459 if(!$cat_id) { 460 return(new IXR_Error(500, __("Sorry, the new category failed."))); 461 } 462 463 return($cat_id); 464 } 465 466 /** 467 * WordPress XML-RPC API 468 * wp_suggestCategories 469 */ 470 function wp_suggestCategories($args) { 471 global $wpdb; 472 473 $this->escape($args); 474 475 $blog_id = (int) $args[0]; 476 $username = $args[1]; 477 $password = $args[2]; 478 $category = $args[3]; 479 $max_results = (int) $args[4]; 480 481 if(!$this->login_pass_ok($username, $password)) { 482 return($this->error); 483 } 484 485 $args = array('get' => 'all', 'number' => $max_results, 'name__like' => $category); 486 $category_suggestions = get_categories($args); 487 488 return($category_suggestions); 489 } 490 491 492 /* Blogger API functions 493 * specs on http://plant.blogger.com/api and http://groups.yahoo.com/group/bloggerDev/ 494 */ 495 496 497 /* blogger.getUsersBlogs will make more sense once we support multiple blogs */ 498 function blogger_getUsersBlogs($args) { 499 500 $this->escape($args); 501 502 $user_login = $args[1]; 503 $user_pass = $args[2]; 504 505 if (!$this->login_pass_ok($user_login, $user_pass)) { 506 return $this->error; 507 } 508 509 set_current_user(0, $user_login); 510 $is_admin = current_user_can('level_8'); 511 512 $struct = array( 513 'isAdmin' => $is_admin, 514 'url' => get_option('home') . '/', 515 'blogid' => '1', 516 'blogName' => get_option('blogname') 517 ); 518 519 return array($struct); 520 } 521 522 523 /* blogger.getUsersInfo gives your client some info about you, so you don't have to */ 524 function blogger_getUserInfo($args) { 525 526 $this->escape($args); 527 528 $user_login = $args[1]; 529 $user_pass = $args[2]; 530 531 if (!$this->login_pass_ok($user_login, $user_pass)) { 532 return $this->error; 533 } 534 535 $user_data = get_userdatabylogin($user_login); 536 537 $struct = array( 538 'nickname' => $user_data->nickname, 539 'userid' => $user_data->ID, 540 'url' => $user_data->user_url, 541 'email' => $user_data->user_email, 542 'lastname' => $user_data->last_name, 543 'firstname' => $user_data->first_name 544 ); 545 546 return $struct; 547 } 548 549 550 /* blogger.getPost ...gets a post */ 551 function blogger_getPost($args) { 552 553 $this->escape($args); 554 555 $post_ID = (int) $args[1]; 556 $user_login = $args[2]; 557 $user_pass = $args[3]; 558 559 if (!$this->login_pass_ok($user_login, $user_pass)) { 560 return $this->error; 561 } 562 563 $user_data = get_userdatabylogin($user_login); 564 $post_data = wp_get_single_post($post_ID, ARRAY_A); 565 566 $categories = implode(',', wp_get_post_categories($post_ID)); 567 568 $content = '<title>'.stripslashes($post_data['post_title']).'</title>'; 569 $content .= '<category>'.$categories.'</category>'; 570 $content .= stripslashes($post_data['post_content']); 571 572 $struct = array( 573 'userid' => $post_data['post_author'], 574 'dateCreated' => new IXR_Date(mysql2date('Ymd\TH:i:s', $post_data['post_date'])), 575 'content' => $content, 576 'postid' => $post_data['ID'] 577 ); 578 579 return $struct; 580 } 581 582 583 /* blogger.getRecentPosts ...gets recent posts */ 584 function blogger_getRecentPosts($args) { 585 586 global $wpdb; 587 588 $this->escape($args); 589 590 $blog_ID = (int) $args[1]; /* though we don't use it yet */ 591 $user_login = $args[2]; 592 $user_pass = $args[3]; 593 $num_posts = $args[4]; 594 595 if (!$this->login_pass_ok($user_login, $user_pass)) { 596 return $this->error; 597 } 598 599 $posts_list = wp_get_recent_posts($num_posts); 600 601 if (!$posts_list) { 602 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 603 return $this->error; 604 } 605 606 foreach ($posts_list as $entry) { 607 608 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date']); 609 $categories = implode(',', wp_get_post_categories($entry['ID'])); 610 611 $content = '<title>'.stripslashes($entry['post_title']).'</title>'; 612 $content .= '<category>'.$categories.'</category>'; 613 $content .= stripslashes($entry['post_content']); 614 615 $struct[] = array( 616 'userid' => $entry['post_author'], 617 'dateCreated' => new IXR_Date($post_date), 618 'content' => $content, 619 'postid' => $entry['ID'], 620 ); 621 622 } 623 624 $recent_posts = array(); 625 for ($j=0; $j<count($struct); $j++) { 626 array_push($recent_posts, $struct[$j]); 627 } 628 629 return $recent_posts; 630 } 631 632 633 /* blogger.getTemplate returns your blog_filename */ 634 function blogger_getTemplate($args) { 635 636 $this->escape($args); 637 638 $blog_ID = (int) $args[1]; 639 $user_login = $args[2]; 640 $user_pass = $args[3]; 641 $template = $args[4]; /* could be 'main' or 'archiveIndex', but we don't use it */ 642 643 if (!$this->login_pass_ok($user_login, $user_pass)) { 644 return $this->error; 645 } 646 647 set_current_user(0, $user_login); 648 if ( !current_user_can('edit_themes') ) { 649 return new IXR_Error(401, __('Sorry, this user can not edit the template.')); 650 } 651 652 /* warning: here we make the assumption that the blog's URL is on the same server */ 653 $filename = get_option('home') . '/'; 654 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename); 655 656 $f = fopen($filename, 'r'); 657 $content = fread($f, filesize($filename)); 658 fclose($f); 659 660 /* so it is actually editable with a windows/mac client */ 661 // FIXME: (or delete me) do we really want to cater to bad clients at the expense of good ones by BEEPing up their line breaks? commented. $content = str_replace("\n", "\r\n", $content); 662 663 return $content; 664 } 665 666 667 /* blogger.setTemplate updates the content of blog_filename */ 668 function blogger_setTemplate($args) { 669 670 $this->escape($args); 671 672 $blog_ID = (int) $args[1]; 673 $user_login = $args[2]; 674 $user_pass = $args[3]; 675 $content = $args[4]; 676 $template = $args[5]; /* could be 'main' or 'archiveIndex', but we don't use it */ 677 678 if (!$this->login_pass_ok($user_login, $user_pass)) { 679 return $this->error; 680 } 681 682 set_current_user(0, $user_login); 683 if ( !current_user_can('edit_themes') ) { 684 return new IXR_Error(401, __('Sorry, this user can not edit the template.')); 685 } 686 687 /* warning: here we make the assumption that the blog's URL is on the same server */ 688 $filename = get_option('home') . '/'; 689 $filename = preg_replace('#https?://.+?/#', $_SERVER['DOCUMENT_ROOT'].'/', $filename); 690 691 if ($f = fopen($filename, 'w+')) { 692 fwrite($f, $content); 693 fclose($f); 694 } else { 695 return new IXR_Error(500, __('Either the file is not writable, or something wrong happened. The file has not been updated.')); 696 } 697 698 return true; 699 } 700 701 702 /* blogger.newPost ...creates a new post */ 703 function blogger_newPost($args) { 704 705 global $wpdb; 706 707 $this->escape($args); 708 709 $blog_ID = (int) $args[1]; /* though we don't use it yet */ 710 $user_login = $args[2]; 711 $user_pass = $args[3]; 712 $content = $args[4]; 713 $publish = $args[5]; 714 715 if (!$this->login_pass_ok($user_login, $user_pass)) { 716 return $this->error; 717 } 718 719 $cap = ($publish) ? 'publish_posts' : 'edit_posts'; 720 $user = set_current_user(0, $user_login); 721 if ( !current_user_can($cap) ) 722 return new IXR_Error(401, __('Sorry, you are not allowed to post on this blog.')); 723 724 $post_status = ($publish) ? 'publish' : 'draft'; 725 726 $post_author = $user->ID; 727 728 $post_title = xmlrpc_getposttitle($content); 729 $post_category = xmlrpc_getpostcategory($content); 730 $post_content = xmlrpc_removepostdata($content); 731 732 $post_date = current_time('mysql'); 733 $post_date_gmt = current_time('mysql', 1); 734 735 $post_data = compact('blog_ID', 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status'); 736 737 $post_ID = wp_insert_post($post_data); 738 739 if (!$post_ID) { 740 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.')); 741 } 742 $this->attach_uploads( $post_ID, $post_content ); 743 744 logIO('O', "Posted ! ID: $post_ID"); 745 746 return $post_ID; 747 } 748 749 750 /* blogger.editPost ...edits a post */ 751 function blogger_editPost($args) { 752 753 global $wpdb; 754 755 $this->escape($args); 756 757 $post_ID = (int) $args[1]; 758 $user_login = $args[2]; 759 $user_pass = $args[3]; 760 $content = $args[4]; 761 $publish = $args[5]; 762 763 if (!$this->login_pass_ok($user_login, $user_pass)) { 764 return $this->error; 765 } 766 767 $actual_post = wp_get_single_post($post_ID,ARRAY_A); 768 769 if (!$actual_post) { 770 return new IXR_Error(404, __('Sorry, no such post.')); 771 } 772 773 $this->escape($actual_post); 774 775 set_current_user(0, $user_login); 776 if ( !current_user_can('edit_post', $post_ID) ) 777 return new IXR_Error(401, __('Sorry, you do not have the right to edit this post.')); 778 779 extract($actual_post, EXTR_SKIP); 780 781 if ( ('publish' == $post_status) && !current_user_can('publish_posts') ) 782 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.')); 783 784 $post_title = xmlrpc_getposttitle($content); 785 $post_category = xmlrpc_getpostcategory($content); 786 $post_content = xmlrpc_removepostdata($content); 787 788 $postdata = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt'); 789 790 $result = wp_update_post($postdata); 791 792 if (!$result) { 793 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be edited.')); 794 } 795 $this->attach_uploads( $ID, $post_content ); 796 797 return true; 798 } 799 800 801 /* blogger.deletePost ...deletes a post */ 802 function blogger_deletePost($args) { 803 804 global $wpdb; 805 806 $this->escape($args); 807 808 $post_ID = (int) $args[1]; 809 $user_login = $args[2]; 810 $user_pass = $args[3]; 811 $publish = $args[4]; 812 813 if (!$this->login_pass_ok($user_login, $user_pass)) { 814 return $this->error; 815 } 816 817 $actual_post = wp_get_single_post($post_ID,ARRAY_A); 818 819 if (!$actual_post) { 820 return new IXR_Error(404, __('Sorry, no such post.')); 821 } 822 823 set_current_user(0, $user_login); 824 if ( !current_user_can('edit_post', $post_ID) ) 825 return new IXR_Error(401, __('Sorry, you do not have the right to delete this post.')); 826 827 $result = wp_delete_post($post_ID); 828 829 if (!$result) { 830 return new IXR_Error(500, __('For some strange yet very annoying reason, this post could not be deleted.')); 831 } 832 833 return true; 834 } 835 836 837 838 /* MetaWeblog API functions 839 * specs on wherever Dave Winer wants them to be 840 */ 841 842 /* metaweblog.newPost creates a post */ 843 function mw_newPost($args) { 844 845 global $wpdb, $post_default_category; 846 847 $this->escape($args); 848 849 $blog_ID = (int) $args[0]; // we will support this in the near future 850 $user_login = $args[1]; 851 $user_pass = $args[2]; 852 $content_struct = $args[3]; 853 $publish = $args[4]; 854 855 if (!$this->login_pass_ok($user_login, $user_pass)) { 856 return $this->error; 857 } 858 859 $cap = ($publish) ? 'publish_posts' : 'edit_posts'; 860 $user = set_current_user(0, $user_login); 861 if ( !current_user_can($cap) ) 862 return new IXR_Error(401, __('Sorry, you are not allowed to post on this blog.')); 863 864 // The post_type defaults to post, but could also be page. 865 $post_type = "post"; 866 if( 867 !empty($content_struct["post_type"]) 868 && ($content_struct["post_type"] == "page") 869 ) { 870 $post_type = "page"; 871 } 872 873 // Let WordPress generate the post_name (slug) unless 874 // one has been provided. 875 $post_name = ""; 876 if(isset($content_struct["wp_slug"])) { 877 $post_name = $content_struct["wp_slug"]; 878 } 879 880 // Only use a password if one was given. 881 if(isset($content_struct["wp_password"])) { 882 $post_password = $content_struct["wp_password"]; 883 } 884 885 // Only set a post parent if one was provided. 886 if(isset($content_struct["wp_page_parent_id"])) { 887 $post_parent = $content_struct["wp_page_parent_id"]; 888 } 889 890 // Only set the menu_order if it was provided. 891 if(isset($content_struct["wp_page_order"])) { 892 $menu_order = $content_struct["wp_page_order"]; 893 } 894 895 $post_author = $user->ID; 896 897 // If an author id was provided then use it instead. 898 if( 899 isset($content_struct["wp_author_id"]) 900 && ($user->ID != $content_struct["wp_author_id"]) 901 ) { 902 switch($post_type) { 903 case "post": 904 if(!current_user_can("edit_others_posts")) { 905 return(new IXR_Error(401, __("You are not allowed to post as this user"))); 906 } 907 break; 908 case "page": 909 if(!current_user_can("edit_others_pages")) { 910 return(new IXR_Error(401, __("You are not allowed to create pages as this user"))); 911 } 912 break; 913 default: 914 return(new IXR_Error(401, __("Invalid post type."))); 915 break; 916 } 917 $post_author = $content_struct["wp_author_id"]; 918 } 919 920 $post_title = $content_struct['title']; 921 $post_content = apply_filters( 'content_save_pre', $content_struct['description'] ); 922 $post_status = $publish ? 'publish' : 'draft'; 923 924 $post_excerpt = $content_struct['mt_excerpt']; 925 $post_more = $content_struct['mt_text_more']; 926 927 $tags_input = $content_struct['mt_keywords']; 928 929 if(isset($content_struct["mt_allow_comments"])) { 930 if(!is_numeric($content_struct["mt_allow_comments"])) { 931 switch($content_struct["mt_allow_comments"]) { 932 case "closed": 933 $comment_status = "closed"; 934 break; 935 case "open": 936 $comment_status = "open"; 937 break; 938 default: 939 $comment_status = get_option("default_comment_status"); 940 break; 941 } 942 } 943 else { 944 switch((int) $content_struct["mt_allow_comments"]) { 945 case 0: 946 $comment_status = "closed"; 947 break; 948 case 1: 949 $comment_status = "open"; 950 break; 951 default: 952 $comment_status = get_option("default_comment_status"); 953 break; 954 } 955 } 956 } 957 else { 958 $comment_status = get_option("default_comment_status"); 959 } 960 961 if(isset($content_struct["mt_allow_pings"])) { 962 if(!is_numeric($content_struct["mt_allow_pings"])) { 963 switch($content_struct['mt_allow_pings']) { 964 case "closed": 965 $ping_status = "closed"; 966 break; 967 case "open": 968 $ping_status = "open"; 969 break; 970 default: 971 $ping_status = get_option("default_ping_status"); 972 break; 973 } 974 } 975 else { 976 switch((int) $content_struct["mt_allow_pings"]) { 977 case 0: 978 $ping_status = "closed"; 979 break; 980 case 1: 981 $ping_status = "open"; 982 break; 983 default: 984 $ping_status = get_option("default_ping_status"); 985 break; 986 } 987 } 988 } 989 else { 990 $ping_status = get_option("default_ping_status"); 991 } 992 993 if ($post_more) { 994 $post_content = $post_content . "\n<!--more-->\n" . $post_more; 995 } 996 997 $to_ping = $content_struct['mt_tb_ping_urls']; 998 if ( is_array($to_ping) ) 999 $to_ping = implode(' ', $to_ping); 1000 1001 // Do some timestamp voodoo 1002 $dateCreatedd = $content_struct['dateCreated']; 1003 if (!empty($dateCreatedd)) { 1004 $dateCreated = $dateCreatedd->getIso(); 1005 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 1006 $post_date_gmt = iso8601_to_datetime($dateCreated, GMT); 1007 } else { 1008 $post_date = current_time('mysql'); 1009 $post_date_gmt = current_time('mysql', 1); 1010 } 1011 1012 $catnames = $content_struct['categories']; 1013 logIO('O', 'Post cats: ' . printr($catnames,true)); 1014 $post_category = array(); 1015 1016 if (is_array($catnames)) { 1017 foreach ($catnames as $cat) { 1018 $post_category[] = get_cat_ID($cat); 1019 } 1020 } 1021 1022 // We've got all the data -- post it: 1023 $postdata = compact('post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'to_ping', 'post_type', 'post_name', 'post_password', 'post_parent', 'menu_order', 'tags_input'); 1024 1025 $post_ID = wp_insert_post($postdata); 1026 1027 if (!$post_ID) { 1028 return new IXR_Error(500, __('Sorry, your entry could not be posted. Something wrong happened.')); 1029 } 1030 1031 $this->attach_uploads( $post_ID, $post_content ); 1032 1033 logIO('O', "Posted ! ID: $post_ID"); 1034 1035 return strval($post_ID); 1036 } 1037 1038 function attach_uploads( $post_ID, $post_content ) { 1039 global $wpdb; 1040 1041 // find any unattached files 1042 $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '-1' AND post_type = 'attachment'" ); 1043 if( is_array( $attachments ) ) { 1044 foreach( $attachments as $file ) { 1045 if( strpos( $post_content, $file->guid ) !== false ) { 1046 $wpdb->query( "UPDATE {$wpdb->posts} SET post_parent = '$post_ID' WHERE ID = '{$file->ID}'" ); 1047 } 1048 } 1049 } 1050 } 1051 1052 /* metaweblog.editPost ...edits a post */ 1053 function mw_editPost($args) { 1054 1055 global $wpdb, $post_default_category; 1056 1057 $this->escape($args); 1058 1059 $post_ID = (int) $args[0]; 1060 $user_login = $args[1]; 1061 $user_pass = $args[2]; 1062 $content_struct = $args[3]; 1063 $publish = $args[4]; 1064 1065 if (!$this->login_pass_ok($user_login, $user_pass)) { 1066 return $this->error; 1067 } 1068 1069 $user = set_current_user(0, $user_login); 1070 1071 // The post_type defaults to post, but could also be page. 1072 $post_type = "post"; 1073 if( 1074 !empty($content_struct["post_type"]) 1075 && ($content_struct["post_type"] == "page") 1076 ) { 1077 $post_type = "page"; 1078 } 1079 1080 // Edit page caps are checked in editPage. Just check post here. 1081 if ( ( 'post' == $post_type ) && !current_user_can('edit_post', $post_ID) ) 1082 return new IXR_Error(401, __('Sorry, you can not edit this post.')); 1083 1084 $postdata = wp_get_single_post($post_ID, ARRAY_A); 1085 1086 // If there is no post data for the give post id, stop 1087 // now and return an error. Other wise a new post will be 1088 // created (which was the old behavior). 1089 if(empty($postdata["ID"])) { 1090 return(new IXR_Error(404, __("Invalid post id."))); 1091 } 1092 1093 $this->escape($postdata); 1094 extract($postdata, EXTR_SKIP); 1095 1096 // Let WordPress manage slug if none was provided. 1097 $post_name = ""; 1098 if(isset($content_struct["wp_slug"])) { 1099 $post_name = $content_struct["wp_slug"]; 1100 } 1101 1102 // Only use a password if one was given. 1103 if(isset($content_struct["wp_password"])) { 1104 $post_password = $content_struct["wp_password"]; 1105 } 1106 1107 // Only set a post parent if one was given. 1108 if(isset($content_struct["wp_page_parent_id"])) { 1109 $post_parent = $content_struct["wp_page_parent_id"]; 1110 } 1111 1112 // Only set the menu_order if it was given. 1113 if(isset($content_struct["wp_page_order"])) { 1114 $menu_order = $content_struct["wp_page_order"]; 1115 } 1116 1117 $post_author = $postdata["post_author"]; 1118 1119 // Only set the post_author if one is set. 1120 if( 1121 isset($content_struct["wp_author_id"]) 1122 && ($user->ID != $content_struct["wp_author_id"]) 1123 ) { 1124 switch($post_type) { 1125 case "post": 1126 if(!current_user_can("edit_others_posts")) { 1127 return(new IXR_Error(401, __("You are not allowed to change the post author as this user."))); 1128 } 1129 break; 1130 case "page": 1131 if(!current_user_can("edit_others_pages")) { 1132 return(new IXR_Error(401, __("You are not allowed to change the page author as this user."))); 1133 } 1134 break; 1135 default: 1136 return(new IXR_Error(401, __("Invalid post type."))); 1137 break; 1138 } 1139 $post_author = $content_struct["wp_author_id"]; 1140 } 1141 1142 if(isset($content_struct["mt_allow_comments"])) { 1143 if(!is_numeric($content_struct["mt_allow_comments"])) { 1144 switch($content_struct["mt_allow_comments"]) { 1145 case "closed": 1146 $comment_status = "closed"; 1147 break; 1148 case "open": 1149 $comment_status = "open"; 1150 break; 1151 default: 1152 $comment_status = get_option("default_comment_status"); 1153 break; 1154 } 1155 } 1156 else { 1157 switch((int) $content_struct["mt_allow_comments"]) { 1158 case 0: 1159 $comment_status = "closed"; 1160 break; 1161 case 1: 1162 $comment_status = "open"; 1163 break; 1164 default: 1165 $comment_status = get_option("default_comment_status"); 1166 break; 1167 } 1168 } 1169 } 1170 1171 if(isset($content_struct["mt_allow_pings"])) { 1172 if(!is_numeric($content_struct["mt_allow_pings"])) { 1173 switch($content_struct["mt_allow_pings"]) { 1174 case "closed": 1175 $ping_status = "closed"; 1176 break; 1177 case "open": 1178 $ping_status = "open"; 1179 break; 1180 default: 1181 $ping_status = get_option("default_ping_status"); 1182 break; 1183 } 1184 } 1185 else { 1186 switch((int) $content_struct["mt_allow_pings"]) { 1187 case 0: 1188 $ping_status = "closed"; 1189 break; 1190 case 1: 1191 $ping_status = "open"; 1192 break; 1193 default: 1194 $ping_status = get_option("default_ping_status"); 1195 break; 1196 } 1197 } 1198 } 1199 1200 $post_title = $content_struct['title']; 1201 $post_content = apply_filters( 'content_save_pre', $content_struct['description'] ); 1202 $catnames = $content_struct['categories']; 1203 1204 $post_category = array(); 1205 1206 if (is_array($catnames)) { 1207 foreach ($catnames as $cat) { 1208 $post_category[] = get_cat_ID($cat); 1209 } 1210 } 1211 1212 $post_excerpt = $content_struct['mt_excerpt']; 1213 $post_more = $content_struct['mt_text_more']; 1214 $post_status = $publish ? 'publish' : 'draft'; 1215 1216 $tags_input = $content_struct['mt_keywords']; 1217 1218 if ( ('publish' == $post_status) ) { 1219 if ( ( 'page' == $post_type ) && !current_user_can('publish_pages') ) 1220 return new IXR_Error(401, __('Sorry, you do not have the right to publish this page.')); 1221 else if ( !current_user_can('publish_posts') ) 1222 return new IXR_Error(401, __('Sorry, you do not have the right to publish this post.')); 1223 } 1224 1225 if ($post_more) { 1226 $post_content = $post_content . "\n<!--more-->\n" . $post_more; 1227 } 1228 1229 $to_ping = $content_struct['mt_tb_ping_urls']; 1230 if ( is_array($to_ping) ) 1231 $to_ping = implode(' ', $to_ping); 1232 1233 // Do some timestamp voodoo 1234 $dateCreatedd = $content_struct['dateCreated']; 1235 if (!empty($dateCreatedd)) { 1236 $dateCreated = $dateCreatedd->getIso(); 1237 $post_date = get_date_from_gmt(iso8601_to_datetime($dateCreated)); 1238 $post_date_gmt = iso8601_to_datetime($dateCreated . "Z", GMT); 1239 } else { 1240 $post_date = $postdata['post_date']; 1241 $post_date_gmt = $postdata['post_date_gmt']; 1242 } 1243 1244 // We've got all the data -- post it: 1245 $newpost = compact('ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'post_date', 'post_date_gmt', 'to_ping', 'post_name', 'post_password', 'post_parent', 'menu_order', 'post_author', 'tags_input'); 1246 1247 $result = wp_update_post($newpost); 1248 if (!$result) { 1249 return new IXR_Error(500, __('Sorry, your entry could not be edited. Something wrong happened.')); 1250 } 1251 $this->attach_uploads( $ID, $post_content ); 1252 1253 logIO('O',"(MW) Edited ! ID: $post_ID"); 1254 1255 return true; 1256 } 1257 1258 1259 /* metaweblog.getPost ...returns a post */ 1260 function mw_getPost($args) { 1261 1262 global $wpdb; 1263 1264 $this->escape($args); 1265 1266 $post_ID = (int) $args[0]; 1267 $user_login = $args[1]; 1268 $user_pass = $args[2]; 1269 1270 if (!$this->login_pass_ok($user_login, $user_pass)) { 1271 return $this->error; 1272 } 1273 1274 $postdata = wp_get_single_post($post_ID, ARRAY_A); 1275 1276 if ($postdata['post_date'] != '') { 1277 1278 $post_date = mysql2date('Ymd\TH:i:s', $postdata['post_date']); 1279 $post_date_gmt = mysql2date('Ymd\TH:i:s', $postdata['post_date_gmt']); 1280 1281 $categories = array(); 1282 $catids = wp_get_post_categories($post_ID); 1283 foreach($catids as $catid) { 1284 $categories[] = get_cat_name($catid); 1285 } 1286 1287 $tagnames = array(); 1288 $tags = wp_get_post_tags( $post_ID ); 1289 if ( !empty( $tags ) ) { 1290 foreach ( $tags as $tag ) { 1291 $tagnames[] = $tag->name; 1292 } 1293 $tagnames = implode( ', ', $tagnames ); 1294 } else { 1295 $tagnames = ''; 1296 } 1297 1298 $post = get_extended($postdata['post_content']); 1299 $link = post_permalink($postdata['ID']); 1300 1301 // Get the author info. 1302 $author = get_userdata($postdata['post_author']); 1303 1304 $allow_comments = ('open' == $postdata['comment_status']) ? 1 : 0; 1305 $allow_pings = ('open' == $postdata['ping_status']) ? 1 : 0; 1306 1307 $resp = array( 1308 'dateCreated' => new IXR_Date($post_date), 1309 'userid' => $postdata['post_author'], 1310 'postid' => $postdata['ID'], 1311 'description' => $post['main'], 1312 'title' => $postdata['post_title'], 1313 'link' => $link, 1314 'permaLink' => $link, 1315 // commented out because no other tool seems to use this 1316 // 'content' => $entry['post_content'], 1317 'categories' => $categories, 1318 'mt_excerpt' => $postdata['post_excerpt'], 1319 'mt_text_more' => $post['extended'], 1320 'mt_allow_comments' => $allow_comments, 1321 'mt_allow_pings' => $allow_pings, 1322 'mt_keywords' => $tagnames, 1323 'wp_slug' => $postdata['post_name'], 1324 'wp_password' => $postdata['post_password'], 1325 'wp_author_id' => $author->ID, 1326 'wp_author_display_name' => $author->display_name, 1327 'date_created_gmt' => new IXR_Date($post_date_gmt) 1328 ); 1329 1330 return $resp; 1331 } else { 1332 return new IXR_Error(404, __('Sorry, no such post.')); 1333 } 1334 } 1335 1336 1337 /* metaweblog.getRecentPosts ...returns recent posts */ 1338 function mw_getRecentPosts($args) { 1339 1340 $this->escape($args); 1341 1342 $blog_ID = (int) $args[0]; 1343 $user_login = $args[1]; 1344 $user_pass = $args[2]; 1345 $num_posts = (int) $args[3]; 1346 1347 if (!$this->login_pass_ok($user_login, $user_pass)) { 1348 return $this->error; 1349 } 1350 1351 $posts_list = wp_get_recent_posts($num_posts); 1352 1353 if (!$posts_list) { 1354 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 1355 return $this->error; 1356 } 1357 1358 foreach ($posts_list as $entry) { 1359 1360 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date']); 1361 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt']); 1362 1363 $categories = array(); 1364 $catids = wp_get_post_categories($entry['ID']); 1365 foreach($catids as $catid) { 1366 $categories[] = get_cat_name($catid); 1367 } 1368 1369 $tagnames = array(); 1370 $tags = wp_get_post_tags( $entry['ID'] ); 1371 if ( !empty( $tags ) ) { 1372 foreach ( $tags as $tag ) { 1373 $tagnames[] = $tag->name; 1374 } 1375 $tagnames = implode( ', ', $tagnames ); 1376 } else { 1377 $tagnames = ''; 1378 } 1379 1380 $post = get_extended($entry['post_content']); 1381 $link = post_permalink($entry['ID']); 1382 1383 // Get the post author info. 1384 $author = get_userdata($entry['post_author']); 1385 1386 $allow_comments = ('open' == $entry['comment_status']) ? 1 : 0; 1387 $allow_pings = ('open' == $entry['ping_status']) ? 1 : 0; 1388 1389 $struct[] = array( 1390 'dateCreated' => new IXR_Date($post_date), 1391 'userid' => $entry['post_author'], 1392 'postid' => $entry['ID'], 1393 'description' => $post['main'], 1394 'title' => $entry['post_title'], 1395 'link' => $link, 1396 'permaLink' => $link, 1397 // commented out because no other tool seems to use this 1398 // 'content' => $entry['post_content'], 1399 'categories' => $categories, 1400 'mt_excerpt' => $entry['post_excerpt'], 1401 'mt_text_more' => $post['extended'], 1402 'mt_allow_comments' => $allow_comments, 1403 'mt_allow_pings' => $allow_pings, 1404 'mt_keywords' => $tagnames, 1405 'wp_slug' => $entry['post_name'], 1406 'wp_password' => $entry['post_password'], 1407 'wp_author_id' => $author->ID, 1408 'wp_author_display_name' => $author->display_name, 1409 'date_created_gmt' => new IXR_Date($post_date_gmt) 1410 ); 1411 1412 } 1413 1414 $recent_posts = array(); 1415 for ($j=0; $j<count($struct); $j++) { 1416 array_push($recent_posts, $struct[$j]); 1417 } 1418 1419 return $recent_posts; 1420 } 1421 1422 1423 /* metaweblog.getCategories ...returns the list of categories on a given blog */ 1424 function mw_getCategories($args) { 1425 1426 global $wpdb; 1427 1428 $this->escape($args); 1429 1430 $blog_ID = (int) $args[0]; 1431 $user_login = $args[1]; 1432 $user_pass = $args[2]; 1433 1434 if (!$this->login_pass_ok($user_login, $user_pass)) { 1435 return $this->error; 1436 } 1437 1438 $categories_struct = array(); 1439 1440 if ( $cats = get_categories('get=all') ) { 1441 foreach ( $cats as $cat ) { 1442 $struct['categoryId'] = $cat->term_id; 1443 $struct['parentId'] = $cat->parent; 1444 $struct['description'] = $cat->name; 1445 $struct['categoryName'] = $cat->name; 1446 $struct['htmlUrl'] = wp_specialchars(get_category_link($cat->term_id)); 1447 $struct['rssUrl'] = wp_specialchars(get_category_rss_link(false, $cat->term_id, $cat->name)); 1448 1449 $categories_struct[] = $struct; 1450 } 1451 } 1452 1453 return $categories_struct; 1454 } 1455 1456 1457 /* metaweblog.newMediaObject uploads a file, following your settings */ 1458 function mw_newMediaObject($args) { 1459 // adapted from a patch by Johann Richard 1460 // http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/ 1461 1462 global $wpdb; 1463 1464 $blog_ID = (int) $args[0]; 1465 $user_login = $wpdb->escape($args[1]); 1466 $user_pass = $wpdb->escape($args[2]); 1467 $data = $args[3]; 1468 1469 $name = sanitize_file_name( $data['name'] ); 1470 $type = $data['type']; 1471 $bits = $data['bits']; 1472 1473 logIO('O', '(MW) Received '.strlen($bits).' bytes'); 1474 1475 if ( !$this->login_pass_ok($user_login, $user_pass) ) 1476 return $this->error; 1477 1478 set_current_user(0, $user_login); 1479 if ( !current_user_can('upload_files') ) { 1480 logIO('O', '(MW) User does not have upload_files capability'); 1481 $this->error = new IXR_Error(401, __('You are not allowed to upload files to this site.')); 1482 return $this->error; 1483 } 1484 1485 if ( $upload_err = apply_filters( "pre_upload_error", false ) ) 1486 return new IXR_Error(500, $upload_err); 1487 1488 if(!empty($data["overwrite"]) && ($data["overwrite"] == true)) { 1489 // Get postmeta info on the object. 1490 $old_file = $wpdb->get_row(" 1491 SELECT ID 1492 FROM {$wpdb->posts} 1493 WHERE post_title = '{$name}' 1494 AND post_type = 'attachment' 1495 "); 1496 1497 // Delete previous file. 1498 wp_delete_attachment($old_file->ID); 1499 1500 // Make sure the new name is different by pre-pending the 1501 // previous post id. 1502 $filename = preg_replace("/^wpid\d+-/", "", $name); 1503 $name = "wpid{$old_file->ID}-{$filename}"; 1504 } 1505 1506 $upload = wp_upload_bits($name, $type, $bits, $overwrite); 1507 if ( ! empty($upload['error']) ) { 1508 $errorString = sprintf(__('Could not write file %1$s (%2$s)'), $name, $upload['error']); 1509 logIO('O', '(MW) ' . $errorString); 1510 return new IXR_Error(500, $errorString); 1511 } 1512 // Construct the attachment array 1513 // attach to post_id -1 1514 $post_id = -1; 1515 $attachment = array( 1516 'post_title' => $name, 1517 'post_content' => '', 1518 'post_type' => 'attachment', 1519 'post_parent' => $post_id, 1520 'post_mime_type' => $type, 1521 'guid' => $upload[ 'url' ] 1522 ); 1523 1524 // Save the data 1525 $id = wp_insert_attachment( $attachment, $upload[ 'file' ], $post_id ); 1526 wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) ); 1527 1528 return apply_filters( 'wp_handle_upload', array( 'file' => $name, 'url' => $upload[ 'url' ], 'type' => $type ) ); 1529 } 1530 1531 1532 /* MovableType API functions 1533 * specs on http://www.movabletype.org/docs/mtmanual_programmatic.html 1534 */ 1535 1536 /* mt.getRecentPostTitles ...returns recent posts' titles */ 1537 function mt_getRecentPostTitles($args) { 1538 1539 $this->escape($args); 1540 1541 $blog_ID = (int) $args[0]; 1542 $user_login = $args[1]; 1543 $user_pass = $args[2]; 1544 $num_posts = (int) $args[3]; 1545 1546 if (!$this->login_pass_ok($user_login, $user_pass)) { 1547 return $this->error; 1548 } 1549 1550 $posts_list = wp_get_recent_posts($num_posts); 1551 1552 if (!$posts_list) { 1553 $this->error = new IXR_Error(500, __('Either there are no posts, or something went wrong.')); 1554 return $this->error; 1555 } 1556 1557 foreach ($posts_list as $entry) { 1558 1559 $post_date = mysql2date('Ymd\TH:i:s', $entry['post_date']); 1560 $post_date_gmt = mysql2date('Ymd\TH:i:s', $entry['post_date_gmt']); 1561 1562 $struct[] = array( 1563 'dateCreated' => new IXR_Date($post_date), 1564 'userid' => $entry['post_author'], 1565 'postid' => $entry['ID'], 1566 'title' => $entry['post_title'], 1567 'date_created_gmt' => new IXR_Date($post_date_gmt) 1568 ); 1569 1570 } 1571 1572 $recent_posts = array(); 1573 for ($j=0; $j<count($struct); $j++) { 1574 array_push($recent_posts, $struct[$j]); 1575 } 1576 1577 return $recent_posts; 1578 } 1579 1580 1581 /* mt.getCategoryList ...returns the list of categories on a given blog */ 1582 function mt_getCategoryList($args) { 1583 1584 global $wpdb; 1585 1586 $this->escape($args); 1587 1588 $blog_ID = (int) $args[0]; 1589 $user_login = $args[1]; 1590 $user_pass = $args[2]; 1591 1592 if (!$this->login_pass_ok($user_login, $user_pass)) { 1593 return $this->error; 1594 } 1595 1596 $categories_struct = array(); 1597 1598 // FIXME: can we avoid using direct SQL there? 1599 if ( $cats = get_categories('hide_empty=0&hierarchical=0') ) { 1600 foreach ($cats as $cat) { 1601 $struct['categoryId'] = $cat->term_id; 1602 $struct['categoryName'] = $cat->name; 1603 1604 $categories_struct[] = $struct; 1605 } 1606 } 1607 1608 return $categories_struct; 1609 } 1610 1611 1612 /* mt.getPostCategories ...returns a post's categories */ 1613 function mt_getPostCategories($args) { 1614 1615 $this->escape($args); 1616 1617 $post_ID = (int) $args[0]; 1618 $user_login = $args[1]; 1619 $user_pass = $args[2]; 1620 1621 if (!$this->login_pass_ok($user_login, $user_pass)) { 1622 return $this->error; 1623 } 1624 1625 $categories = array(); 1626 $catids = wp_get_post_categories(intval($post_ID)); 1627 // first listed category will be the primary category 1628 $isPrimary = true; 1629 foreach($catids as $catid) { 1630 $categories[] = array( 1631 'categoryName' => get_cat_name($catid), 1632 'categoryId' => (string) $catid, 1633 'isPrimary' => $isPrimary 1634 ); 1635 $isPrimary = false; 1636 } 1637 1638 return $categories; 1639 } 1640 1641 1642 /* mt.setPostCategories ...sets a post's categories */ 1643 function mt_setPostCategories($args) { 1644 1645 $this->escape($args); 1646 1647 $post_ID = (int) $args[0]; 1648 $user_login = $args[1]; 1649 $user_pass = $args[2]; 1650 $categories = $args[3]; 1651 1652 if (!$this->login_pass_ok($user_login, $user_pass)) { 1653 return $this->error; 1654 } 1655 1656 set_current_user(0, $user_login); 1657 if ( !current_user_can('edit_post', $post_ID) ) 1658 return new IXR_Error(401, __('Sorry, you can not edit this post.')); 1659 1660 foreach($categories as $cat) { 1661 $catids[] = $cat['categoryId']; 1662 } 1663 1664 wp_set_post_categories($post_ID, $catids); 1665 1666 return true; 1667 } 1668 1669 1670 /* mt.supportedMethods ...returns an array of methods supported by this server */ 1671 function mt_supportedMethods($args) { 1672 1673 $supported_methods = array(); 1674 foreach($this->methods as $key=>$value) { 1675 $supported_methods[] = $key; 1676 } 1677 1678 return $supported_methods; 1679 } 1680 1681 1682 /* mt.supportedTextFilters ...returns an empty array because we don't 1683 support per-post text filters yet */ 1684 function mt_supportedTextFilters($args) { 1685 return apply_filters('xmlrpc_text_filters', array()); 1686 } 1687 1688 1689 /* mt.getTrackbackPings ...returns trackbacks sent to a given post */ 1690 function mt_getTrackbackPings($args) { 1691 1692 global $wpdb; 1693 1694 $post_ID = intval($args); 1695 1696 $actual_post = wp_get_single_post($post_ID, ARRAY_A); 1697 1698 if (!$actual_post) { 1699 return new IXR_Error(404, __('Sorry, no such post.')); 1700 } 1701 1702 $comments = $wpdb->get_results("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = $post_ID"); 1703 1704 if (!$comments) { 1705 return array(); 1706 } 1707 1708 $trackback_pings = array(); 1709 foreach($comments as $comment) { 1710 if ( 'trackback' == $comment->comment_type ) { 1711 $content = $comment->comment_content; 1712 $title = substr($content, 8, (strpos($content, '</strong>') - 8)); 1713 $trackback_pings[] = array( 1714 'pingTitle' => $title, 1715 'pingURL' => $comment->comment_author_url, 1716 'pingIP' => $comment->comment_author_IP 1717 ); 1718 } 1719 } 1720 1721 return $trackback_pings; 1722 } 1723 1724 1725 /* mt.publishPost ...sets a post's publish status to 'publish' */ 1726 function mt_publishPost($args) { 1727 1728 $this->escape($args); 1729 1730 $post_ID = (int) $args[0]; 1731 $user_login = $args[1]; 1732 $user_pass = $args[2]; 1733 1734 if (!$this->login_pass_ok($user_login, $user_pass)) { 1735 return $this->error; 1736 } 1737 1738 set_current_user(0, $user_login); 1739 if ( !current_user_can('edit_post', $post_ID) ) 1740 return new IXR_Error(401, __('Sorry, you can not edit this post.')); 1741 1742 $postdata = wp_get_single_post($post_ID,ARRAY_A); 1743 1744 $postdata['post_status'] = 'publish'; 1745 1746 // retain old cats 1747 $cats = wp_get_post_categories($post_ID); 1748 $postdata['post_category'] = $cats; 1749 $this->escape($postdata); 1750 1751 $result = wp_update_post($postdata); 1752 1753 return $result; 1754 } 1755 1756 1757 1758 /* PingBack functions 1759 * specs on www.hixie.ch/specs/pingback/pingback 1760 */ 1761 1762 /* pingback.ping gets a pingback and registers it */ 1763 function pingback_ping($args) { 1764 global $wpdb, $wp_version; 1765 1766 $this->escape($args); 1767 1768 $pagelinkedfrom = $args[0]; 1769 $pagelinkedto = $args[1]; 1770 1771 $title = ''; 1772 1773 $pagelinkedfrom = str_replace('&', '&', $pagelinkedfrom); 1774 $pagelinkedto = preg_replace('#&([^amp\;])#is', '&$1', $pagelinkedto); 1775 1776 $error_code = -1; 1777 1778 // Check if the page linked to is in our site 1779 $pos1 = strpos($pagelinkedto, str_replace(array('http://www.','http://','https://www.','https://'), '', get_option('home'))); 1780 if( !$pos1 ) 1781 return new IXR_Error(0, __('Is there no link to us?')); 1782 1783 // let's find which post is linked to 1784 // FIXME: does url_to_postid() cover all these cases already? 1785 // if so, then let's use it and drop the old code. 1786 $urltest = parse_url($pagelinkedto); 1787 if ($post_ID = url_to_postid($pagelinkedto)) { 1788 $way = 'url_to_postid()'; 1789 } elseif (preg_match('#p/[0-9]{1,}#', $urltest['path'], $match)) { 1790 // the path defines the post_ID (archives/p/XXXX) 1791 $blah = explode('/', $match[0]); 1792 $post_ID = (int) $blah[1]; 1793 $way = 'from the path'; 1794 } elseif (preg_match('#p=[0-9]{1,}#', $urltest['query'], $match)) { 1795 // the querystring defines the post_ID (?p=XXXX) 1796 $blah = explode('=', $match[0]); 1797 $post_ID = (int) $blah[1]; 1798 $way = 'from the querystring'; 1799 } elseif (isset($urltest['fragment'])) { 1800 // an #anchor is there, it's either... 1801 if (intval($urltest['fragment'])) { 1802 // ...an integer #XXXX (simpliest case) 1803 $post_ID = (int) $urltest['fragment']; 1804 $way = 'from the fragment (numeric)'; 1805 } elseif (preg_match('/post-[0-9]+/',$urltest['fragment'])) { 1806 // ...a post id in the form 'post-###' 1807 $post_ID = preg_replace('/[^0-9]+/', '', $urltest['fragment']); 1808 $way = 'from the fragment (post-###)'; 1809 } elseif (is_string($urltest['fragment'])) { 1810 // ...or a string #title, a little more complicated 1811 $title = preg_replace('/[^a-z0-9]/i', '.', $urltest['fragment']); 1812 $sql = "SELECT ID FROM $wpdb->posts WHERE post_title RLIKE '$title'"; 1813 if (! ($post_ID = $wpdb->get_var($sql)) ) { 1814 // returning unknown error '0' is better than die()ing 1815 return new IXR_Error(0, ''); 1816 } 1817 $way = 'from the fragment (title)'; 1818 } 1819 } else { 1820 // TODO: Attempt to extract a post ID from the given URL 1821 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.')); 1822 } 1823 $post_ID = (int) $post_ID; 1824 1825 1826 logIO("O","(PB) URL='$pagelinkedto' ID='$post_ID' Found='$way'"); 1827 1828 $post = get_post($post_ID); 1829 1830 if ( !$post ) // Post_ID not found 1831 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.')); 1832 1833 if ( $post_ID == url_to_postid($pagelinkedfrom) ) 1834 return new IXR_Error(0, __('The source URL and the target URL cannot both point to the same resource.')); 1835 1836 // Check if pings are on 1837 if ( 'closed' == $post->ping_status ) 1838 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.')); 1839 1840 // Let's check that the remote site didn't already pingback this entry 1841 $result = $wpdb->get_results("SELECT * FROM $wpdb->comments WHERE comment_post_ID = '$post_ID' AND comment_author_url = '$pagelinkedfrom'"); 1842 1843 if ( $wpdb->num_rows ) // We already have a Pingback from this URL 1844 return new IXR_Error(48, __('The pingback has already been registered.')); 1845 1846 // very stupid, but gives time to the 'from' server to publish ! 1847 sleep(1); 1848 1849 // Let's check the remote site 1850 $linea = wp_remote_fopen( $pagelinkedfrom ); 1851 if ( !$linea ) 1852 return new IXR_Error(16, __('The source URL does not exist.')); 1853 1854 // Work around bug in strip_tags(): 1855 $linea = str_replace('<!DOC', '<DOC', $linea); 1856 $linea = preg_replace( '/[\s\r\n\t]+/', ' ', $linea ); // normalize spaces 1857 $linea = preg_replace( "/ <(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/", "\n\n", $linea ); 1858 1859 preg_match('|<title>([^<]*?)</title>|is', $linea, $matchtitle); 1860 $title = $matchtitle[1]; 1861 if ( empty( $title ) ) 1862 return new IXR_Error(32, __('We cannot find a title on that page.')); 1863 1864 $linea = strip_tags( $linea, '<a>' ); // just keep the tag we need 1865 1866 $p = explode( "\n\n", $linea ); 1867 1868 $preg_target = preg_quote($pagelinkedto); 1869 1870 foreach ( $p as $para ) { 1871 if ( strpos($para, $pagelinkedto) !== false ) { // it exists, but is it a link? 1872 preg_match("|<a[^>]+?".$preg_target."[^>]*>([^>]+?)</a>|", $para, $context); 1873 1874 // If the URL isn't in a link context, keep looking 1875 if ( empty($context) ) 1876 continue; 1877 1878 // We're going to use this fake tag to mark the context in a bit 1879 // the marker is needed in case the link text appears more than once in the paragraph 1880 $excerpt = preg_replace('|\</?wpcontext\>|', '', $para); 1881 1882 // prevent really long link text 1883 if ( strlen($context[1]) > 100 ) 1884 $context[1] = substr($context[1], 0, 100) . '...'; 1885 1886 $marker = '<wpcontext>'.$context[1].'</wpcontext>'; // set up our marker 1887 $excerpt= str_replace($context[0], $marker, $excerpt); // swap out the link for our marker 1888 $excerpt = strip_tags($excerpt, '<wpcontext>'); // strip all tags but our context marker 1889 $excerpt = trim($excerpt); 1890 $preg_marker = preg_quote($marker); 1891 $excerpt = preg_replace("|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt); 1892 $excerpt = strip_tags($excerpt); // YES, again, to remove the marker wrapper 1893 break; 1894 } 1895 } 1896 1897 if ( empty($context) ) // Link to target not found 1898 return new IXR_Error(17, __('The source URL does not contain a link to the target URL, and so cannot be used as a source.')); 1899 1900 $pagelinkedfrom = preg_replace('#&([^amp\;])#is', '&$1', $pagelinkedfrom); 1901 1902 $context = '[...] ' . wp_specialchars( $excerpt ) . ' [...]'; 1903 $original_pagelinkedfrom = $pagelinkedfrom; 1904 $pagelinkedfrom = $wpdb->escape( $pagelinkedfrom ); 1905 $original_title = $title; 1906 1907 $comment_post_ID = (int) $post_ID; 1908 $comment_author = $title; 1909 $this->escape($comment_author); 1910 $comment_author_url = $pagelinkedfrom; 1911 $comment_content = $context; 1912 $this->escape($comment_content); 1913 $comment_type = 'pingback'; 1914 1915 $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_content', 'comment_type'); 1916 1917 $comment_ID = wp_new_comment($commentdata); 1918 do_action('pingback_post', $comment_ID); 1919 1920 return sprintf(__('Pingback from %1$s to %2$s registered. Keep the web talking! :-)'), $pagelinkedfrom, $pagelinkedto); 1921 } 1922 1923 1924 /* pingback.extensions.getPingbacks returns an array of URLs 1925 that pingbacked the given URL 1926 specs on http://www.aquarionics.com/misc/archives/blogite/0198.html */ 1927 function pingback_extensions_getPingbacks($args) { 1928 1929 global $wpdb; 1930 1931 $this->escape($args); 1932 1933 $url = $args; 1934 1935 $post_ID = url_to_postid($url); 1936 if (!$post_ID) { 1937 // We aren't sure that the resource is available and/or pingback enabled 1938 return new IXR_Error(33, __('The specified target URL cannot be used as a target. It either doesn\'t exist, or it is not a pingback-enabled resource.')); 1939 } 1940 1941 $actual_post = wp_get_single_post($post_ID, ARRAY_A); 1942 1943 if (!$actual_post) { 1944 // No such post = resource not found 1945 return new IXR_Error(32, __('The specified target URL does not exist.')); 1946 } 1947 1948 $comments = $wpdb->get_results("SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = $post_ID"); 1949 1950 if (!$comments) { 1951 return array(); 1952 } 1953 1954 $pingbacks = array(); 1955 foreach($comments as $comment) { 1956 if ( 'pingback' == $comment->comment_type ) 1957 $pingbacks[] = $comment->comment_author_url; 1958 } 1959 1960 return $pingbacks; 1961 } 1962 } 1963 No newline at end of file -
xmlrpc.php
1 <?php 2 3 /** 4 * logIO() - Write a message to the xmlrpc log file. 5 * 6 * @param string $io Stream mode, 'I' for Input and everything else is Output 7 * @param string $msg The string to write to the log file. Prefer meaningful message. 8 * @global $xmlrpc_logging 9 * @return bool Only returns true or assume error had occurred. 10 */ 11 function logIO($io,$msg) { 12 global $xmlrpc_logging; 13 if ($xmlrpc_logging) { 14 $fp = fopen("../xmlrpc.log","a+"); 15 $date = gmdate("Y-m-d H:i:s "); 16 $iot = ($io == "I") ? " Input: " : " Output: "; 17 fwrite($fp, "\n\n".$date.$iot.$msg); 18 fclose($fp); 19 } 20 return true; 21 } 22 23 /** 24 * starify() - Return star characters equal to the length of $string 25 * 26 * @param string $string Message to get same length of star characters. 27 * @return string Numbers of stars equal to $string length 28 */ 29 function starify($string) { 30 $i = strlen($string); 31 return str_repeat('*', $i); 32 } 33 No newline at end of file
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)