1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (at your option) any later version. 10** 11** This program is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22class CJsonRpc { 23 24 const VERSION = '2.0'; 25 26 public $json; 27 28 /** 29 * API client to use for making requests. 30 * 31 * @var CApiClient 32 */ 33 protected $apiClient; 34 35 private $_error; 36 private $_response; 37 private $_error_list; 38 private $_zbx2jsonErrors; 39 private $_jsonDecoded; 40 41 /** 42 * Constructor. 43 * 44 * @param CApiClient $apiClient 45 * @param string $data 46 */ 47 public function __construct(CApiClient $apiClient, $data) { 48 $this->apiClient = $apiClient; 49 50 $this->json = new CJson(); 51 $this->initErrors(); 52 53 $this->_error = false; 54 $this->_response = []; 55 $this->_jsonDecoded = $this->json->decode($data, true); 56 } 57 58 /** 59 * Executes API requests. 60 * 61 * @return string JSON encoded value 62 */ 63 public function execute() { 64 if ($this->json->hasError()) { 65 $this->jsonError(null, '-32700', null, null, true); 66 return $this->json->encode($this->_response[0]); 67 } 68 69 if (!is_array($this->_jsonDecoded) || $this->_jsonDecoded == []) { 70 $this->jsonError(null, '-32600', null, null, true); 71 return $this->json->encode($this->_response[0]); 72 } 73 74 foreach (zbx_toArray($this->_jsonDecoded) as $call) { 75 $call = is_array($call) ? $call : [$call]; 76 77 // notification 78 if (!array_key_exists('id', $call)) { 79 $call['id'] = null; 80 } 81 82 if (!$this->validate($call)) { 83 continue; 84 } 85 86 list($api, $method) = array_merge(explode('.', $call['method']), [null, null]); 87 $result = $this->apiClient->callMethod($api, $method, $call['params'], 88 array_key_exists('auth', $call) ? $call['auth'] : null 89 ); 90 91 $this->processResult($call, $result); 92 } 93 94 if (is_array($this->_jsonDecoded) 95 && array_keys($this->_jsonDecoded) === range(0, count($this->_jsonDecoded) - 1)) { 96 // Return response as encoded batch if $this->_jsonDecoded is associative array. 97 return $this->json->encode($this->_response); 98 } 99 100 return $this->json->encode($this->_response[0]); 101 } 102 103 public function validate($call) { 104 if (!isset($call['jsonrpc'])) { 105 $this->jsonError($call['id'], '-32600', _('JSON-rpc version is not specified.'), null, true); 106 return false; 107 } 108 109 if ($call['jsonrpc'] != self::VERSION) { 110 $this->jsonError($call['id'], '-32600', _s('Expecting JSON-rpc version 2.0, "%s" is given.', $call['jsonrpc']), null, true); 111 return false; 112 } 113 114 if (!isset($call['method'])) { 115 $this->jsonError($call['id'], '-32600', _('JSON-rpc method is not defined.')); 116 return false; 117 } 118 119 if (isset($call['params']) && !is_array($call['params'])) { 120 $this->jsonError($call['id'], '-32602', _('JSON-rpc params is not an Array.')); 121 return false; 122 } 123 124 return true; 125 } 126 127 public function processResult($call, CApiClientResponse $response) { 128 if ($response->errorCode) { 129 $errno = $this->_zbx2jsonErrors[$response->errorCode]; 130 131 $this->jsonError($call['id'], $errno, $response->errorMessage, $response->debug); 132 } 133 else { 134 // Notifications MUST NOT be answered 135 if ($call['id'] === null) { 136 return; 137 } 138 139 $formedResp = [ 140 'jsonrpc' => self::VERSION, 141 'result' => $response->data, 142 'id' => $call['id'] 143 ]; 144 145 $this->_response[] = $formedResp; 146 } 147 } 148 149 private function jsonError($id, $errno, $data = null, $debug = null, $force_err = false) { 150 // Notifications MUST NOT be answered, but error MUST be generated on JSON parse error 151 if (is_null($id) && !$force_err) { 152 return; 153 } 154 155 $this->_error = true; 156 157 if (!isset($this->_error_list[$errno])) { 158 $data = _s('JSON-rpc error generation failed. No such error "%s".', $errno); 159 $errno = '-32400'; 160 } 161 162 $error = $this->_error_list[$errno]; 163 164 if (!is_null($data)) { 165 $error['data'] = $data; 166 } 167 if (!is_null($debug)) { 168 $error['debug'] = $debug; 169 } 170 171 172 $formed_error = [ 173 'jsonrpc' => self::VERSION, 174 'error' => $error, 175 'id' => $id 176 ]; 177 178 $this->_response[] = $formed_error; 179 } 180 181 private function initErrors() { 182 $this->_error_list = [ 183 '-32700' => [ 184 'code' => -32700, 185 'message' => _('Parse error'), 186 'data' => _('Invalid JSON. An error occurred on the server while parsing the JSON text.') 187 ], 188 '-32600' => [ 189 'code' => -32600, 190 'message' => _('Invalid Request.'), 191 'data' => _('The received JSON is not a valid JSON-RPC Request.') 192 ], 193 '-32601' => [ 194 'code' => -32601, 195 'message' => _('Method not found.'), 196 'data' => _('The requested remote-procedure does not exist / is not available') 197 ], 198 '-32602' => [ 199 'code' => -32602, 200 'message' => _('Invalid params.'), 201 'data' => _('Invalid method parameters.') 202 ], 203 '-32603' => [ 204 'code' => -32603, 205 'message' => _('Internal error.'), 206 'data' => _('Internal JSON-RPC error.') 207 ], 208 '-32500' => [ 209 'code' => -32500, 210 'message' => _('Application error.'), 211 'data' => _('No details') 212 ], 213 '-32400' => [ 214 'code' => -32400, 215 'message' => _('System error.'), 216 'data' => _('No details') 217 ], 218 '-32300' => [ 219 'code' => -32300, 220 'message' => _('Transport error.'), 221 'data' => _('No details') 222 ] 223 ]; 224 225 $this->_zbx2jsonErrors = [ 226 ZBX_API_ERROR_NO_METHOD => '-32601', 227 ZBX_API_ERROR_PARAMETERS => '-32602', 228 ZBX_API_ERROR_NO_AUTH => '-32602', 229 ZBX_API_ERROR_PERMISSIONS => '-32500', 230 ZBX_API_ERROR_INTERNAL => '-32500' 231 ]; 232 } 233} 234