Files
typecho/var/IXR/Server.php
joyqi 3caebb3b20 v1.3.0 (#1661)
* Add feed widget

* add feed render

* Add CommentPage widget

* New theme (#1390)

* 调整忽略目录

* add theme

* fix theme scss build

Co-authored-by: fen <f3nb0x@gmail.com>

* s/is_writeable/is_writable/g

* New upgrade method

* merge new fixes from master

* add pgsql ssl mode support (ref #1600) (#1623)

* Feat/code refactor (#1626)

* remove all magic methods, add type for class properties

* refactor codes

* fix all

* refactor code

* fix type

* fix all

* fix request is method

* fix all

* fix router

* fix get page

* fix 1.3.0 upgrade

* [feat] support high resolution avatar

* fix types in i18n component

* Implement Ctrl+S or Command+S for save draft (#1628)

* Implement Ctrl+S or Command+S for save draft

* rename

* add Typecho.savePost

* fix upload file size

* add new uploader

* replace new uploader

* fix textarea change

* fix preview

* refactor post edit

* fix issue

* fix page edit

---------

Co-authored-by: joyqi <joyqi@segmentfault.com>
Co-authored-by: joyqi <magike.net@gmail.com>

* fix #1632

* Add svg to image types

* Feat/tree pages (#1646)

* add tree trait

* finish category tree trait

* support select fields

* fix select fields

* refactor admin trait

* fix draft status

* Add new contents type "revision"

* minor refactor

* add more tree view abstracts

* add tree trait to pages

* get ready for tree view pages

* improve page edit

* fix revision

* fix slug

* add router params delegate

* fix params delegate

* fix

* fix

* fix all

* fix all

* fix tree

* fix page link

* fix feed

* fix page

* fix permalink

* fix permalink input

* fix offset query

* Support IDN (#1629)

* Support IDN

* use js

* Optimize code

* Optimize code

* fix URL script

* remove unnecessary use

---------

Co-authored-by: joyqi <joyqi@segmentfault.com>

* fix input element

* fix #1651, close #1653

* Use json instead of serialize (#1624)

* Use json instead of serialize

* Fix Upgrade code

* add tree trait

* finish category tree trait

* support select fields

* fix select fields

* refactor admin trait

* fix draft status

* Add new contents type "revision"

* minor refactor

* add more tree view abstracts

* add tree trait to pages

* get ready for tree view pages

* improve page edit

* fix revision

* fix slug

* add router params delegate

* fix params delegate

* fix

* fix

* fix all

* fix all

* fix tree

* fix page link

* fix feed

* fix page

* fix permalink

* fix permalink input

* fix offset query

* Fix typo

* remove proxy methods

* remove unnecessary useage

---------

Co-authored-by: joyqi <joyqi@segmentfault.com>
Co-authored-by: joyqi <magike.net@gmail.com>

* Fix Prevent XSS vulnerability in default theme (#1654)

* Fix Prevent XSS vulnerability in default theme

* Update var/Typecho/Db/Adapter/Pdo.php

* fix the getter

---------

Co-authored-by: joyqi <joyqi@segmentfault.com>

* add throwCallback to widget response

* fix: cut down fields when selecting recent posts

* fix typo errors

* fix typo errors

* fix http client cookie

* add throw finish

* fix theme lang

* fix default theme

* fix query

* add open graph and twitter card support
add canonical link

* fix canonical link meta

* fix theme classic-22

* remove unnecessary scss file when packaging

* init plugin signal

* improve: remove feather-icon js file

* fix: typo

* improve: post detail layout

* fix tags saving

* improve: nav search

* fix: theme screenshot

* fix: theme page layout

* remove php 7.2/7.3 env

---------

Co-authored-by: fen <f3nb0x@gmail.com>
Co-authored-by: Lu Fei <52o@qq52o.cn>
2023-12-30 23:02:25 +08:00

338 lines
8.6 KiB
PHP

<?php
namespace IXR;
use Typecho\Widget\Exception as WidgetException;
/**
* IXR服务器
*
* @package IXR
*/
class Server
{
/**
* 回调函数
*
* @var array
*/
private array $callbacks;
/**
* 默认参数
*
* @var array
*/
private array $capabilities;
/**
* @var Hook
*/
private Hook $hook;
/**
* 构造函数
*
* @param array $callbacks 回调函数
*/
public function __construct(array $callbacks = [])
{
$this->setCapabilities();
$this->callbacks = $callbacks;
$this->setCallbacks();
}
/**
* 获取默认参数
*
* @access public
* @return array
*/
public function getCapabilities(): array
{
return $this->capabilities;
}
/**
* 列出所有方法
*
* @access public
* @return array
*/
public function listMethods(): array
{
// Returns a list of methods - uses array_reverse to ensure user defined
// methods are listed before server defined methods
return array_reverse(array_keys($this->callbacks));
}
/**
* 一次处理多个请求
*
* @param array $methodcalls
* @return array
*/
public function multiCall(array $methodcalls): array
{
// See http://www.xmlrpc.com/discuss/msgReader$1208
$return = [];
foreach ($methodcalls as $call) {
$method = $call['methodName'];
$params = $call['params'];
if ($method == 'system.multicall') {
$result = new Error(-32600, 'Recursive calls to system.multicall are forbidden');
} else {
$result = $this->call($method, $params);
}
if (is_a($result, 'Error')) {
$return[] = [
'faultCode' => $result->code,
'faultString' => $result->message
];
} else {
$return[] = [$result];
}
}
return $return;
}
/**
* @param string $methodName
* @return string|Error
*/
public function methodHelp(string $methodName)
{
if (!$this->hasMethod($methodName)) {
return new Error(-32601, 'server error. requested method ' . $methodName . ' does not exist.');
}
[$object, $method] = $this->callbacks[$methodName];
try {
$ref = new \ReflectionMethod($object, $method);
$doc = $ref->getDocComment();
return $doc ?: '';
} catch (\ReflectionException $e) {
return '';
}
}
/**
* @param Hook $hook
*/
public function setHook(Hook $hook)
{
$this->hook = $hook;
}
/**
* 呼叫内部方法
*
* @param string $methodName 方法名
* @param array $args 参数
* @return mixed
*/
private function call(string $methodName, array $args)
{
if (!$this->hasMethod($methodName)) {
return new Error(-32601, 'server error. requested method ' . $methodName . ' does not exist.');
}
$method = $this->callbacks[$methodName];
if (!is_callable($method)) {
return new Error(
-32601,
'server error. requested class method "' . $methodName . '" does not exist.'
);
}
[$object, $objectMethod] = $method;
try {
$ref = new \ReflectionMethod($object, $objectMethod);
$requiredArgs = $ref->getNumberOfRequiredParameters();
if (count($args) < $requiredArgs) {
return new Error(
-32602,
'server error. requested class method "' . $methodName . '" require ' . $requiredArgs . ' params.'
);
}
foreach ($ref->getParameters() as $key => $parameter) {
if ($parameter->hasType() && !settype($args[$key], $parameter->getType()->getName())) {
return new Error(
-32602,
'server error. requested class method "'
. $methodName . '" ' . $key . ' param has wrong type.'
);
}
}
if (isset($this->hook)) {
$result = $this->hook->beforeRpcCall($methodName, $ref, $args);
if (isset($result)) {
return $result;
}
}
$result = call_user_func_array($method, $args);
if (isset($this->hook)) {
$this->hook->afterRpcCall($methodName, $result);
}
return $result;
} catch (\ReflectionException $e) {
return new Error(
-32601,
'server error. requested class method "' . $methodName . '" does not exist.'
);
} catch (Exception $e) {
return new Error(
$e->getCode(),
$e->getMessage()
);
} catch (WidgetException $e) {
return new Error(
-32001,
$e->getMessage()
);
} catch (\Exception $e) {
return new Error(
-32001,
'server error. requested class method "' . $methodName . '" failed.'
);
}
}
/**
* 抛出错误
*
* @access private
* @param integer|Error $error 错误代码
* @param string|null $message 错误消息
* @return void
*/
private function error($error, ?string $message = null)
{
// Accepts either an error object or an error code and message
if (!$error instanceof Error) {
$error = new Error($error, $message);
}
$this->output($error->getXml());
}
/**
* 输出xml
*
* @access private
* @param string $xml 输出xml
*/
private function output(string $xml)
{
$xml = '<?xml version="1.0"?>' . "\n" . $xml;
$length = strlen($xml);
header('Connection: close');
header('Content-Length: ' . $length);
header('Content-Type: text/xml');
header('Date: ' . date('r'));
echo $xml;
exit;
}
/**
* 是否存在方法
*
* @access private
* @param string $method 方法名
* @return bool
*/
private function hasMethod(string $method): bool
{
return in_array($method, array_keys($this->callbacks));
}
/**
* 设置默认参数
*
* @access public
* @return void
*/
private function setCapabilities()
{
// Initialises capabilities array
$this->capabilities = [
'xmlrpc' => [
'specUrl' => 'http://www.xmlrpc.com/spec',
'specVersion' => 1
],
'faults_interop' => [
'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
'specVersion' => 20010516
],
'system.multicall' => [
'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
'specVersion' => 1
],
];
}
/**
* 设置默认方法
*
* @access private
* @return void
*/
private function setCallbacks()
{
$this->callbacks['system.getCapabilities'] = [$this, 'getCapabilities'];
$this->callbacks['system.listMethods'] = [$this, 'listMethods'];
$this->callbacks['system.multicall'] = [$this, 'multiCall'];
$this->callbacks['system.methodHelp'] = [$this, 'methodHelp'];
}
/**
* 服务入口
*/
public function serve()
{
$message = new Message(file_get_contents('php://input') ?: '');
if (!$message->parse()) {
$this->error(-32700, 'parse error. not well formed');
} elseif ($message->messageType != 'methodCall') {
$this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall');
}
$result = $this->call($message->methodName, $message->params);
// Is the result an error?
if ($result instanceof Error) {
$this->error($result);
}
// Encode the result
$r = new Value($result);
$resultXml = $r->getXml();
// Create the XML
$xml = <<<EOD
<methodResponse>
<params>
<param>
<value>
$resultXml
</value>
</param>
</params>
</methodResponse>
EOD;
// Send it
$this->output($xml);
}
}