function handle_rest_call() { global $api_desc; set_error_handler("rest_error_handler"); $magic = ini_get("magic_quotes_gpc"); $path = explode('/', $_SERVER['PATH_INFO']); while (sizeof($path) && !$path[0]) { array_shift($path); } // get function name $funcname = implode(".", $path); // find function descriptor list($funcname, $func_desc, $php_func) = api_get_function_descriptor($funcname); // check request method if (strtolower($_SERVER['REQUEST_METHOD']) != $func_desc['type']) { api_error("Request method " . $_SERVER['REQUEST_METHOD'] . " is incorrect for this function; " . strtoupper($func_desc['type']) . " is required.", 'invalid_request_method'); } // check and parse input $args_desc = $func_desc['args']; $args = array(); foreach ($args_desc as $arg_name => $arg_desc) { // get value $v = @$_REQUEST[$arg_name]; if ($v === NULL) { continue; } // missing keys will be detected during validation // massage it into required type $arg_type = $arg_desc['type']; switch ($arg_type) { case 'string': case 'enum': if ($magic) { $v = stripslashes($v); } break; case 'boolean': switch ($v) { case 'true': $v = TRUE; break; case 'false': $v = FALSE; break; default: api_error("Invalid boolean value '{$v}' (must be 'true' or 'false') passed as argument {$arg_name} to function {$funcname}", "validation_invalid_value"); } break; case 'int': $v = intval($v); break; case 'float': $v = floatval($v); break; default: api_error("Argument type '{$arg_type}' not supported in REST mode"); } // and store $args[$arg_name] = $v; } // validate input validate_content($args, array("type" => "hash", "content" => $func_desc['args']), "REST input", "auto"); // call function try { $ret = $php_func($args); // check output type if (gettype($ret) != 'array') { api_error("Returned data from {$php_func} should be an array, but received " . gettype($ret) . " instead."); } $validate = TRUE; if ($path[0] == 'peopleaggregator') { if (!array_key_exists("success", $ret)) { api_error("Missing 'success' field in returned data from {$php_func}."); } if (!$ret['success']) { $validate = FALSE; } } // check output if ($validate) { validate_content($ret, $func_desc['return'], "REST response", "auto", true); } } catch (PAException $e) { $ret = api_err_from_exception($e); Logger::log("An exception occurred in an API call: code " . $e->getCode() . ", message " . $e->getMessage() . "\n" . $e->getTraceAsString(), LOGGER_ERROR); } // if we got this var, it validates return array($ret, $func_desc); }
function call($methodname, $args) { Logger::log("XML-RPC call: {$methodname}"); list($methodname, $func_desc, $php_func) = api_get_function_descriptor($methodname); // make sure $args is a 1-elem array containing a hash if (gettype($args) != "array") { api_error("Parameters should be in an array", 'validation_request_wrapper'); } switch ($func_desc['argstyle']) { case 'positional': $arg = array(); $argorder = $func_desc['argorder']; if (count($args) != count($argorder)) { api_error("Incorrect number of arguments; expected " . count($argorder), 'validation_incorrect_number_of_arguments'); } for ($i = 0; $i < count($args); ++$i) { $arg[$argorder[$i]] = $args[$i]; } break; case 'named': if (sizeof($args) != 1) { api_error("You should only send a single parameter in your XML-RPC request: a struct, containing all the required keys.", 'validation_request_wrapper'); } $arg = $args[0]; if (gettype($arg) != "array") { api_error("Expected a single parameter containing an XML-RPC struct, but got a value of type '" . gettype($arg) . "' instead.", 'validation_request_wrapper'); } break; default: api_error("Invalid argument style " . $func_desc['argstyle']); } // var_dump($func_desc['args']); // validate the struct validate_content($arg, array("type" => "hash", "content" => $func_desc['args']), "input to XML-RPC function", "auto"); // call function, capturing any output - which might include errors ob_start("xmlrpc_ob_end"); try { $ret = $php_func($arg); // check output if ($ret['success']) { validate_content($ret, $func_desc['return'], "XML-RPC response - not your fault!", "auto"); } } catch (PAException $e) { $ret = api_err_from_exception($e); Logger::log("An exception occurred in an API call: code " . $e->getCode() . ", message " . $e->getMessage() . "\n" . $e->getTraceAsString(), LOGGER_ERROR); } return $ret; }
function validate_content(&$v, $desc, $context, $src_encoding, $convert_output = false, $path = '') { if (!$src_encoding) { $src_encoding = "auto"; } global $type_map; $t = gettype($v); $expected_type = array_key_exists($desc['type'], $type_map) ? $type_map[$desc['type']] : $desc['type']; if ($t != $expected_type) { api_error("Validation error ({$context}): expected type {$expected_type} at position '{$path}', got {$t}", 'validation_incorrect_type'); } switch ($desc['type']) { case 'hash': // check that all specified keys are correct $content = $desc['content']; foreach ($content as $k_name => $k_desc) { // if the key is missing: // - do nothing if it's marked as optional, // - replace with the default if one is given, // - otherwise fail with validation_missing_key if (!array_key_exists($k_name, $v)) { if (@$k_desc["optional"]) { continue; } $dflt = @$k_desc['default']; if ($dflt === NULL) { api_error("Validation error ({$context}, {$path}): key {$k_name} is required", 'validation_missing_key'); } $v[$k_name] = $dflt; } validate_content($v[$k_name], $k_desc, $context, $src_encoding, $convert_output, "{$path}/{$k_name}"); } if (!$desc['allow_extra_keys']) { // check for unknown (not allowed) keys foreach ($v as $k_name => $k_value) { if (!array_key_exists($k_name, $content) && strpos($k_name, "__") !== 0) { api_error("Validation error ({$context}, {$path}): key {$k_name} is not allowed", 'validation_extra_key'); } } } break; case 'array': foreach ($v as &$i) { validate_content($i, $desc['item'], $context, $src_encoding, $convert_output, "{$path}/item"); } break; case 'enum': if (!in_array($v, $desc['values'])) { api_error("Validation error ({$context}, {$path}): '{$v}' is not a valid enumeration value at position '{$path}'"); } break; case 'int': $mi = @$desc['min']; if ($mi !== NULL && $v < $mi) { api_error("Validation error ({$context}, {$path}): value at {$path} ({$v}) must be >= {$mi}", 'validation_out_of_range'); } $mx = @$desc['max']; if ($mx !== NULL && $v > $mx) { api_error("Validation error ({$context}, {$path}): value at {$path} ({$v}) must be <= {$mx}", 'validation_out_of_range'); } break; case 'string': // convert char encoding $v = api_force_utf8($v, $src_encoding); break; case 'date': if (!$v instanceof IXR_Date) { api_error("Validation error ({$context}, {$path}): '{$v}' must be an IXR_Date object"); } $v = $v->year . '-' . $v->month . '-' . $v->day; break; case 'datetime': if (!$v instanceof IXR_Date) { api_error("Validation error ({$context}, {$path}): '{$v}' must be an IXR_Date object"); } $v = $v->getIso(); break; } }