diff --git a/include/class.api.php b/include/class.api.php index bccf81f12cd397f017873fc5329c3b65b411207e..67f47b63586c85b75b2d4d249d07b2c2356538d2 100644 --- a/include/class.api.php +++ b/include/class.api.php @@ -156,23 +156,29 @@ class API { * in the database, and methods for parsing and validating data sent in the * API request. */ + class ApiController { + var $apikey; + function requireApiKey() { # Validate the API key -- required to be sent via the X-API-Key # header - if (!isset($_SERVER['HTTP_X_API_KEY']) || !isset($_SERVER['REMOTE_ADDR'])) - Http::response(403, "API key required"); - elseif (!($key=API::lookupByKey($_SERVER['HTTP_X_API_KEY'], $_SERVER['REMOTE_ADDR'])) - || !$key->isActive() - || $key->getIPAddr()!=$_SERVER['REMOTE_ADDR']) - Http::response(401, "API key not found/active or source IP not authorized"); + + if(!($key=$this->getApiKey())) + return $this->exerr(401, 'API key required'); + elseif (!$key->isActive() || $key->getIPAddr()!=$_SERVER['REMOTE_ADDR']) + return $this->exerr(401, 'API key not found/active or source IP not authorized'); return $key; } function getApiKey() { - return $this->requireApiKey(); + + if (!$this->apikey && isset($_SERVER['HTTP_X_API_KEY']) && isset($_SERVER['REMOTE_ADDR'])) + $this->apikey = API::lookupByKey($_SERVER['HTTP_X_API_KEY'], $_SERVER['REMOTE_ADDR']); + + return $this->apikey; } /** @@ -181,20 +187,43 @@ class ApiController { * work will be done for XML requests */ function getRequest($format) { - if (!($stream = @fopen("php://input", "r"))) - Http::response(400, "Unable to read request body"); - if ($format == "xml") { - if (!function_exists("xml_parser_create")) - Http::response(500, "XML extension not supported"); - $tree = new ApiXmlDataParser(); - } elseif ($format == "json") { - $tree = new ApiJsonDataParser(); + + $input = (substr(php_sapi_name(), 0, 3) == 'cli')?'php://stdin':'php://input'; + + if (!($stream = @fopen($input, 'r'))) + $this->exerr(400, "Unable to read request body"); + + $parser = null; + switch(strtolower($format)) { + case 'xml': + if (!function_exists('xml_parser_create')) + $this->exerr(501, 'XML extension not supported'); + + $parser = new ApiXmlDataParser(); + break; + case 'json': + $parser = new ApiJsonDataParser(); + break; + case 'email': + $parser = new ApiEmailDataParser(); + break; + default: + $this->exerr(415, 'Unsupported data format'); } - if (!($data = $tree->parse($stream))) - Http::response(400, $tree->lastError()); + + if (!($data = $parser->parse($stream))) + $this->exerr(400, $parser->lastError()); + $this->validate($data, $this->getRequestStructure($format)); + return $data; } + + function getEmailRequest() { + return $this->getRequest('email'); + } + + /** * Structure to validate the request against -- must be overridden to be * useful @@ -216,9 +245,37 @@ class ApiController { } elseif (in_array($key, $structure)) { continue; } - Http::response(400, "$prefix$key: Unexpected data received"); + $this->exerr(400, "$prefix$key: Unexpected data received"); } } + + /** + * API error & logging and response! + * + */ + + /* If possible - DO NOT - overwrite the method downstream */ + function exerr($code, $error='') { + global $ost; + + if($error && is_array($error)) + $error = Format::array_implode(": ", "\n", $error); + + //Include api key - if available. + if($_SERVER['HTTP_X_API_KEY']) + $error.="\n\n*[".$_SERVER['HTTP_X_API_KEY']."]*\n"; + + $ost->logWarning("API Error ($code)", $error, false); + + $this->response($code, $error); //Responder should exit... + return false; + } + + //Default response method - can be overwritten in subclasses. + function response($code, $resp) { + Http::response($code, $resp); + exit(); + } } include_once "class.xml.php"; @@ -232,6 +289,10 @@ class ApiXmlDataParser extends XmlDataParser { * XML data types */ function fixup($current) { + + if($current['ticket']) + $current = $current['ticket']; + if (!is_array($current)) return $current; foreach ($current as $key=>&$value) { @@ -295,6 +356,7 @@ class ApiJsonDataParser extends JsonDataParser { "data" => $contents, "type" => ($type) ? $type : "text/plain", "name" => key($info)); + # XXX: Handle decoding here?? if (substr($extra, -6) == "base64") $info["encoding"] = "base64"; # Handle 'charset' hint in $extra, such as @@ -302,8 +364,8 @@ class ApiJsonDataParser extends JsonDataParser { # Convert to utf-8 since it's the encoding scheme # for the database. Otherwise, assume utf-8 list($param,$charset) = explode('=', $extra); - if ($param == 'charset' && function_exists('iconv')) - $contents = iconv($charset, "UTF-8", $contents); + if ($param == 'charset' && $charset) + $contents = Formart::utf8encode($contents, $charset); } } unset($value);