[ Index ]

PHP Cross Reference of DokuWiki

title

Body

[close]

/inc/Remote/ -> JsonRpcServer.php (source)

   1  <?php
   2  
   3  namespace dokuwiki\Remote;
   4  
   5  /**
   6   * Provide the Remote XMLRPC API as a JSON based API
   7   */
   8  class JsonRpcServer
   9  {
  10      protected $remote;
  11  
  12      /** @var float The XML-RPC Version. 0 is our own simplified variant */
  13      protected $version = 0;
  14  
  15      /**
  16       * JsonRpcServer constructor.
  17       */
  18      public function __construct()
  19      {
  20          $this->remote = new Api();
  21      }
  22  
  23      /**
  24       * Serve the request
  25       *
  26       * @param string $body Should only be set for testing, otherwise the request body is read from php://input
  27       * @return mixed
  28       * @throws RemoteException
  29       */
  30      public function serve($body = '')
  31      {
  32          global $conf;
  33          global $INPUT;
  34  
  35          if (!$conf['remote']) {
  36              http_status(404);
  37              throw new RemoteException("JSON-RPC server not enabled.", -32605);
  38          }
  39          if (!empty($conf['remotecors'])) {
  40              header('Access-Control-Allow-Origin: ' . $conf['remotecors']);
  41          }
  42          if ($INPUT->server->str('REQUEST_METHOD') !== 'POST') {
  43              http_status(405);
  44              header('Allow: POST');
  45              throw new RemoteException("JSON-RPC server only accepts POST requests.", -32606);
  46          }
  47          if ($INPUT->server->str('CONTENT_TYPE') !== 'application/json') {
  48              http_status(415);
  49              throw new RemoteException("JSON-RPC server only accepts application/json requests.", -32606);
  50          }
  51  
  52          try {
  53              if ($body === '') {
  54                  $body = file_get_contents('php://input');
  55              }
  56              if ($body !== '') {
  57                  $data = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
  58              } else {
  59                  $data = [];
  60              }
  61          } catch (\Exception $e) {
  62              http_status(400);
  63              throw new RemoteException("JSON-RPC server only accepts valid JSON.", -32700);
  64          }
  65  
  66          return $this->createResponse($data);
  67      }
  68  
  69      /**
  70       * This executes the method and returns the result
  71       *
  72       * This should handle all JSON-RPC versions and our simplified version
  73       *
  74       * @link https://en.wikipedia.org/wiki/JSON-RPC
  75       * @link https://www.jsonrpc.org/specification
  76       * @param array $data
  77       * @return array
  78       * @throws RemoteException
  79       */
  80      protected function createResponse($data)
  81      {
  82          global $INPUT;
  83          $return = [];
  84  
  85          if (isset($data['method'])) {
  86              // this is a standard conform request (at least version 1.0)
  87              $method = $data['method'];
  88              $params = $data['params'] ?? [];
  89              $this->version = 1;
  90  
  91              // always return the same ID
  92              if (isset($data['id'])) $return['id'] = $data['id'];
  93  
  94              // version 2.0 request
  95              if (isset($data['jsonrpc'])) {
  96                  $return['jsonrpc'] = $data['jsonrpc'];
  97                  $this->version = (float)$data['jsonrpc'];
  98              }
  99  
 100              // version 1.1 request
 101              if (isset($data['version'])) {
 102                  $return['version'] = $data['version'];
 103                  $this->version = (float)$data['version'];
 104              }
 105          } else {
 106              // this is a simplified request
 107              $method = $INPUT->server->str('PATH_INFO');
 108              $method = trim($method, '/');
 109              $params = $data;
 110              $this->version = 0;
 111          }
 112  
 113          // excute the method
 114          $return['result'] = $this->call($method, $params);
 115          $this->addErrorData($return); // handles non-error info
 116          return $return;
 117      }
 118  
 119      /**
 120       * Create an error response
 121       *
 122       * @param \Exception $exception
 123       * @return array
 124       */
 125      public function returnError($exception)
 126      {
 127          $return = [];
 128          $this->addErrorData($return, $exception);
 129          return $return;
 130      }
 131  
 132      /**
 133       * Depending on the requested version, add error data to the response
 134       *
 135       * @param array $response
 136       * @param \Exception|null $e
 137       * @return void
 138       */
 139      protected function addErrorData(&$response, $e = null)
 140      {
 141          if ($e !== null) {
 142              // error occured, add to response
 143              $response['error'] = [
 144                  'code' => $e->getCode(),
 145                  'message' => $e->getMessage()
 146              ];
 147          } else {
 148              // no error, act according to version
 149              if ($this->version > 0 && $this->version < 2) {
 150                  // version 1.* wants null
 151                  $response['error'] = null;
 152              } elseif ($this->version < 1) {
 153                  // simplified version wants success
 154                  $response['error'] = [
 155                      'code' => 0,
 156                      'message' => 'success'
 157                  ];
 158              }
 159              // version 2 wants no error at all
 160          }
 161      }
 162  
 163      /**
 164       * Call an API method
 165       *
 166       * @param string $methodname
 167       * @param array $args
 168       * @return mixed
 169       * @throws RemoteException
 170       */
 171      public function call($methodname, $args)
 172      {
 173          try {
 174              return $this->remote->call($methodname, $args);
 175          } catch (AccessDeniedException $e) {
 176              if (!isset($_SERVER['REMOTE_USER'])) {
 177                  http_status(401);
 178                  throw new RemoteException("server error. not authorized to call method $methodname", -32603);
 179              } else {
 180                  http_status(403);
 181                  throw new RemoteException("server error. forbidden to call the method $methodname", -32604);
 182              }
 183          } catch (RemoteException $e) {
 184              http_status(400);
 185              throw $e;
 186          }
 187      }
 188  }