1 <?php
4 class jsonRPC {
6 private $curlHandler = NULL;
7 private $config;
8 private $id;
9 private $lastStats = array();
10 private $lastResult = array();
11 private $lastAction = "none";
14 private $connectUrl = "";
15 private $username = "";
16 private $userPassword = "";
17 private $authModeDigest = FALSE;
20 /*! \brief This function is used by the property editor and checks the
21 * given rpc connection informations.
22 */
23 public static function testConnectionProperties($message,$class,$name,$value, $type)
24 {
25 global $config;
27 // Get currently used connection usernamem and password.
28 // We use the temporary values, due to the fact, that we do not want to test
29 // the current values, we want to test the modified values.
30 $user = $config->configRegistry->getProperty('core','gosaRpcUser');
31 $username = $user->getValue($temporaryValue = TRUE);
32 $passwd = $config->configRegistry->getProperty('core','gosaRpcPassword');
33 $passwdString = $passwd->getValue($temporaryValue = TRUE);
35 $connection = new jsonRPC($config, $value, $username, $passwdString);
36 if(!$connection->success() && $message){
37 msg_dialog::display(_("Warning"),
38 sprintf(_("The rpc connection (%s) specified for '%s:%s' is invalid! Error was: %s."),
39 bold($value),bold($class),bold($name), bold($connection->get_error())),
40 WARNING_DIALOG);
42 }
44 return($connection->success());
45 }
48 /*! \brief Constructs a new jsonRPC handle which is connected to a given URL.
49 * It can either connect using a rpc method or via auth method digest.
50 * @param object The gosa configuration object (class_config)
51 * @param string The url to connect to.
52 * @param string The username for authentication
53 * @param string The password to use for authentication
54 * @param boolean Whether to use DIGEST authentication or not.
55 * @return
56 */
57 public function __construct($config, $connectUrl, $username, $userPassword, $authModeDigest=FALSE)
58 {
59 $this->config = $config;
60 $this->id = 0;
62 // Get connection data
63 $this->connectUrl = $connectUrl;
64 $this->username = $username;
65 $this->userPassword = $userPassword;
66 $this->authModeDigest = $authModeDigest;
68 // Put some usefull info in the logs
69 DEBUG (DEBUG_RPC, __LINE__, __FUNCTION__, __FILE__,bold($this->connectUrl), "Initiated RPC ");
70 DEBUG (DEBUG_RPC, __LINE__, __FUNCTION__, __FILE__,bold($this->username), "RPC user: ");
71 DEBUG (DEBUG_RPC, __LINE__, __FUNCTION__, __FILE__,bold($this->userPassword),"RPC password: ");
72 DEBUG (DEBUG_RPC, __LINE__, __FUNCTION__, __FILE__,bold($this->authModeDigest),"Digest Auth (0: No, 1: Yes): ");
74 $this->__login();
75 }
78 /*! \brief
79 * @param
80 * @return
81 */
82 private function __login()
83 {
84 // Init Curl handler
85 $this->curlHandler = curl_init($this->connectUrl);
87 // Set curl options
88 curl_setopt($this->curlHandler, CURLOPT_URL , $this->connectUrl);
89 curl_setopt($this->curlHandler, CURLOPT_POST , TRUE);
90 curl_setopt($this->curlHandler, CURLOPT_RETURNTRANSFER ,TRUE);
91 curl_setopt($this->curlHandler, CURLOPT_HTTPHEADER , array('Content-Type: application/json'));
92 curl_setopt($this->curlHandler, CURLOPT_SSL_VERIFYPEER, FALSE);
94 // Try to login
95 if($this->authModeDigest){
96 if(!empty($this->username)){
97 curl_setopt($this->curlHandler, CURLOPT_USERPWD , "{$this->username}:{$this->userPassword}");
98 }
100 curl_setopt($this->curlHandler, CURLOPT_HTTPAUTH , CURLAUTH_ANYSAFE);
101 }else{
102 curl_setopt($this->curlHandler, CURLOPT_COOKIESESSION , TRUE);
103 curl_setopt($this->curlHandler, CURLOPT_COOKIEFILE, 'cookiefile.txt');
104 if(!empty($this->username))
105 $this->login($this->username, $this->userPassword);
106 }
107 }
110 /*! \brief Returns the last HTTP status code.
111 * @return int The last status code.
112 */
113 public function getHTTPstatusCode()
114 {
115 return((isset($this->lastStats['http_code']))? $this->lastStats['http_code'] : -1 );
116 }
119 /*! \brief Returns the last error string.
120 * @return string The last error message.
121 */
122 public function get_error()
123 {
124 if($this->lastStats['http_code'] != 200){
125 $error = $this->getHttpStatusCodeMessage($this->lastStats['http_code']);
126 if(isset($this->lastResult['error']['error']) && is_array($this->lastResult['error']['error'])){
127 $err = $this->lastResult['error']['error'];
128 $message = call_user_func_array(sprintf,$err);
129 $error .= $message;
130 }elseif(isset($this->lastResult['error']['message'])){
131 $error .= ": ".$this->lastResult['error']['message'];
132 }
133 return($error);
134 }else{
135 return(curl_error($this->curlHandler));
136 }
137 }
141 /*! \brief Returns TRUE if the last action was successfull else FALSE.
142 * @return boolean TRUE on success else FALSE.
143 */
144 public function success()
145 {
146 return(curl_errno($this->curlHandler) == 0 ||
147 (isset($this->lastStats['http_code']) && $this->lastStats['http_code'] == 200));
148 }
151 /*! \brief The class destructor, it destroys open rpc handles if needed.
152 */
153 public function __destruct()
154 {
155 if($this->curlHandler){
156 curl_close($this->curlHandler);
157 }
158 }
161 /*! \brief This is some kind of catch-all method, all unknown method names will
162 * will be interpreted as rpc request.
163 * If you call "$this->blafasel" this method will initiate an rpc request
164 * for method 'blafasel'.
165 * @param string method The rpc method to execute.
166 * @param params array The parameter to use.
167 * @return mixed The request result.
168 */
169 public function __call($method,$params)
170 {
171 // Check if handle is still valid!
172 if(!$this->curlHandler && $this->lastAction != 'login'){
173 $this->__login();
174 }
176 // Start request
177 DEBUG (DEBUG_RPC, __LINE__, __FUNCTION__, __FILE__,"{$method}", "Calling: ");
178 $response = $this->request($method,$params);
179 if($this->success()){
180 DEBUG (DEBUG_RPC, __LINE__, __FUNCTION__, __FILE__,
181 (is_array($response['result']))?$response['result']:bold($response['result']), "Result: ");
182 }else{
183 DEBUG (DEBUG_RPC, __LINE__, __FUNCTION__, __FILE__,bold($this->get_error())."<br>".$response, "Result (FAILED): ");
184 }
186 global $config;
187 $debugLevel = $config->get_cfg_value('core', 'debugLevel');
188 if($debugLevel & DEBUG_RPC){
189 print_a(array('CALLED:' => array($method => $params)));
190 print_a(array('RESPONSE' => $response));
191 }
192 $return = $response['result'];
194 // Inspect the result and replace predefined statements with their
195 // coresponding classes.
196 $return = $this->inspectJsonResult($return);
198 return($return);
199 }
203 public function inspectJsonResult($result)
204 {
205 // Check for remove objects we've to create
206 if(is_array($result) && isset($result['__jsonclass__']) && class_available('remoteObject')){
208 // Get all relevant class informations
209 $classDef = $result['__jsonclass__'][1];
210 $type = $classDef[0];
211 $ref_id = $classDef[1];
212 $object_id = $classDef[2];
213 $methods = $classDef[3];
214 $properties = $classDef[4];
216 // Prepare values
217 $values = array();
218 foreach($properties as $prop){
219 $values[$prop] = NULL;
220 if(isset($res[$prop])) $values[$prop] = $res[$prop];
221 }
223 // Build up remote object
224 $object = new remoteObject($rpc, $type, $properties, $values, $methods, $object_id, $ref_id);
225 return($object);
226 }
227 return($result);
228 }
238 /*! \brief This method finally initiates the real RPC requests and handles
239 * the result from the server.
240 * @param string method The method to call
241 * @param array params The paramter to use.
242 * @return mixed The server response.
243 */
244 private function request($method, $params)
245 {
246 // Set last action
247 $this->lastAction = $method;
249 // Reset stats of last request.
250 $this->lastStats = array();
252 // Validate input values
253 if (!is_scalar($method)) trigger_error('jsonRPC::__call requires a scalar value as first parameter!');
254 if (is_array($params)) {
255 $params = array_values($params);
256 } else {
257 trigger_error('jsonRPC::__call requires an array value as second parameter!');
258 }
260 // prepares the request
261 $this->id ++;
262 $request = json_encode(array('method' => $method,'params' => $params,'id' => $this->id));
264 // Set curl options
265 curl_setopt($this->curlHandler, CURLOPT_POSTFIELDS , $request);
266 $response = curl_exec($this->curlHandler);
267 $response = json_decode($response,true);
269 // Set current result stats.
270 $this->lastStats = curl_getinfo($this->curlHandler);
271 $this->lastResult = $response;
273 return($response);
274 }
277 /*! \brief Returns the HTTP status message for a given HTTP status code.
278 * @param int code The status to code to return a message for.
279 * @return string The corresponding status message.
280 */
281 public static function getHttpStatusCodeMessage($code)
282 {
283 $codes = array(
284 '100' => 'Continue',
285 '101' => 'Switching Protocols',
286 '102' => 'Processing',
287 '200' => 'OK',
288 '201' => 'Created',
289 '202' => 'Accepted',
290 '203' => 'Non-Authoritative Information',
291 '204' => 'No Content',
292 '205' => 'Reset Content',
293 '206' => 'Partial Content',
294 '207' => 'Multi-Status',
295 '300' => 'Multiple Choice',
296 '301' => 'Moved Permanently',
297 '302' => 'Found',
298 '303' => 'See Other',
299 '304' => 'Not Modified',
300 '305' => 'Use Proxy',
301 '306' => 'reserved',
302 '307' => 'Temporary Redirect',
303 '400' => 'Bad Request',
304 '401' => 'Unauthorized',
305 '402' => 'Payment Required',
306 '403' => 'Forbidden',
307 '404' => 'Not Found',
308 '405' => 'Method Not Allowed',
309 '406' => 'Not Acceptable',
310 '407' => 'Proxy Authentication Required',
311 '408' => 'Request Time-out',
312 '409' => 'Conflict',
313 '410' => 'Gone',
314 '411' => 'Length Required',
315 '412' => 'Precondition Failed',
316 '413' => 'Request Entity Too Large',
317 '414' => 'Request-URI Too Long',
318 '415' => 'Unsupported Media Type',
319 '416' => 'Requested range not satisfiable',
320 '417' => 'Expectation Failed',
321 '421' => 'There are too many connections from your internet address',
322 '422' => 'Unprocessable Entity',
323 '423' => 'Locked',
324 '424' => 'Failed Dependency',
325 '425' => 'Unordered Collection',
326 '426' => 'Upgrade Required',
327 '500' => 'Internal Server Error',
328 '501' => 'Not Implemented',
329 '502' => 'Bad Gateway',
330 '503' => 'Service Unavailable',
331 '504' => 'Gateway Time-out',
332 '505' => 'HTTP Version not supported',
333 '506' => 'Variant Also Negotiates',
334 '507' => 'Insufficient Storage',
335 '509' => 'Bandwidth Limit Exceeded',
336 '510' => 'Not Extended');
337 return((isset($codes[$code]))? $codes[$code] : sprintf(_("Unknown HTTP status code '%s'!"), $code));
338 }
339 }
340 ?>