]> gitweb.fluxo.info Git - lorea/elgg.git/commitdiff
Standardized engine/lib/api.php
authorbrettp <brettp@36083f99-b078-4883-b0ff-0f9b5a30f544>
Wed, 14 Oct 2009 22:19:37 +0000 (22:19 +0000)
committerbrettp <brettp@36083f99-b078-4883-b0ff-0f9b5a30f544>
Wed, 14 Oct 2009 22:19:37 +0000 (22:19 +0000)
git-svn-id: http://code.elgg.org/elgg/trunk@3546 36083f99-b078-4883-b0ff-0f9b5a30f544

engine/lib/api.php

index 2ec36388b6447c5b711874f5811a071457147ad9..53af1af67d2cb6fe9367ae6cd196de296e7da02d 100644 (file)
 <?php
+/**
+ * Elgg API
+ * Functions and objects which make up the API engine.
+ *
+ * @package Elgg
+ * @subpackage Core
+ * @author Curverider Ltd <info@elgg.com>
+ * @link http://elgg.org/
+ */
+
+// Result classes /////////////////////////////////////////////////////////////////////////
+
+/**
+ * GenericResult Result superclass.
+ *
+ * @author Curverider Ltd <info@elgg.com>
+ * @package Elgg
+ * @subpackage Core
+ */
+abstract class GenericResult {
        /**
-        * Elgg API
-        * Functions and objects which make up the API engine.
-        * 
-        * @package Elgg
-        * @subpackage Core
-        * @author Curverider Ltd <info@elgg.com>
-        * @link http://elgg.org/
+        * The status of the result.
+        * @var int
         */
+       private $status_code;
 
-       // Result classes /////////////////////////////////////////////////////////////////////////
+       /**
+        * Message returned along with the status which is almost always an error message.
+        * This must be human readable, understandable and localised.
+        * @var string
+        */
+       private $message;
 
        /**
-        * GenericResult Result superclass.
-        * 
-        * @author Curverider Ltd <info@elgg.com>
-        * @package Elgg
-        * @subpackage Core
+        * Result store.
+        * Attach result specific informaton here.
+        *
+        * @var mixed. Should probably be an object of some sort.
         */
-       abstract class GenericResult
-       {       
-               /** 
-                * The status of the result.
-                * @var int
-                */
-               private $status_code;
-               
-               /**
-                * Message returned along with the status which is almost always an error message.
-                * This must be human readable, understandable and localised.
-                * @var string
-                */
-               private $message;
-               
-               /**
-                * Result store. 
-                * Attach result specific informaton here.
-                *
-                * @var mixed. Should probably be an object of some sort.
-                */
-               private $result;
-
-               /**
-                * Set a status code and optional message.
-                *
-                * @param int $status The status code.
-                * @param string $message The message.
-                */
-               protected function setStatusCode($status, $message = "")
-               {
-                       $this->status_code = $status;
-                       $this->message = $message;
-               }
-               
-               /**
-                * Set the result.
-                *
-                * @param mixed $result
-                */
-               protected function setResult($result) { $this->result = $result; }
-               
-               protected function getStatusCode() { return $this->status_code; }
-               protected function getStatusMessage() { return $this->message; }
-               protected function getResult() { return $this->result; }
-
-               /**
-                * Serialise to a standard class.
-                * 
-                * DEVNOTE: The API is only interested in data, we can not easily serialise
-                * custom classes without the need for 1) the other side being PHP, 2) you need to have the class
-                * definition installed, 3) its the right version! 
-                * 
-                * Therefore, I'm not bothering.
-                * 
-                * Override this to include any more specific information, however api results should be attached to the 
-                * class using setResult().
-                * 
-                * if $CONFIG->debug is set then additional information about the runtime environment and authentication will be
-                * returned.
-                * 
-                * @return stdClass Object containing the serialised result.
-                */
-               public function export()
-               {
-                       global $ERRORS, $CONFIG, $_PAM_HANDLERS_MSG;
-                       
-                       $result = new stdClass;
-                       
-                       $result->status = $this->getStatusCode();
-                       if ($this->getStatusMessage()!="") $result->message = $this->getStatusMessage();
-                       
-                       $resultdata = $this->getResult(); 
-                       if (isset($resultdata)) $result->result = $resultdata;
-
-                       if ((isset($CONFIG->debug)) && ($CONFIG->debug == true))
-                       {
-                               if (count($ERRORS))
-                                       $result->runtime_errors = $ERRORS;
-                                       
-                               if (count($_PAM_HANDLERS_MSG))
-                                       $result->pam = $_PAM_HANDLERS_MSG;
-                       }
+       private $result;
 
-                       return $result;
-               }
-       }
-       
        /**
-        * SuccessResult
-        * Generic success result class, extend if you want to do something special.
-        * 
-        * @author Curverider Ltd <info@elgg.com>
-        * @package Elgg
-        * @subpackage Core
+        * Set a status code and optional message.
+        *
+        * @param int $status The status code.
+        * @param string $message The message.
         */
-       class SuccessResult extends GenericResult
-       {
-               public static $RESULT_SUCCESS = 0;  // Do not change this from 0
-
-               public function SuccessResult($result)
-               {       
-                       $this->setResult($result);
-                       $this->setStatusCode(SuccessResult::$RESULT_SUCCESS);
-               }
-               
-               public static function getInstance($result)
-               {       
-                       // Return a new error object.
-                       return new SuccessResult($result);
-               }
+       protected function setStatusCode($status, $message = "") {
+               $this->status_code = $status;
+               $this->message = $message;
        }
-       
+
        /**
-        * ErrorResult
-        * The error result class.
-        * 
-        * @author Curverider Ltd <info@elgg.com>
-        * @package Elgg
-        * @subpackage Core
+        * Set the result.
+        *
+        * @param mixed $result
         */
-       class ErrorResult extends GenericResult
-       {
-               public static $RESULT_FAIL              = -1 ; // Fail with no specific code
-
-               public static $RESULT_FAIL_APIKEY_DISABLED = -30;
-               public static $RESULT_FAIL_APIKEY_INACTIVE = -31;
-               public static $RESULT_FAIL_APIKEY_INVALID = -32;
-               
-               public static $RESULT_FAIL_AUTHTOKEN = -20; // Invalid, expired or missing auth token
-
-               public function ErrorResult($message, $code = "", Exception $exception = NULL)
-               {
-                       if ($code == "")
-                               $code = ErrorResult::$RESULT_FAIL;
-                               
-                       if ($exception!=NULL)
-                               $this->setResult($exception->__toString());
-                               
-                       $this->setStatusCode($code, $message);
-               }
-                               
-               /**
-                * Get a new instance of the ErrorResult.
-                *
-                * @param string $message
-                * @param int $code
-                * @param Exception $exception Optional exception for generating a stack trace.
-                */
-               public static function getInstance($message, $code = "", Exception $exception = NULL)
-               {       
-                       // Return a new error object.
-                       return new ErrorResult($message, $code, $exception);
-               }
+       protected function setResult($result) {
+               $this->result = $result;
+       }
+
+       protected function getStatusCode() {
+               return $this->status_code;
+       }
+
+       protected function getStatusMessage() {
+               return $this->message;
        }
-       
-       // Caching of HMACs ///////////////////////////////////////////////////////////////////////     
-       
+
+       protected function getResult() {
+               return $this->result;
+       }
+
        /**
-        * ElggHMACCache
-        * Store cached data in a temporary database, only used by the HMAC stuff.
-        * 
-        * @author Curverider Ltd <info@elgg.com>
-        * @package Elgg
-        * @subpackage API
+        * Serialise to a standard class.
+        *
+        * DEVNOTE: The API is only interested in data, we can not easily serialise
+        * custom classes without the need for 1) the other side being PHP, 2) you need to have the class
+        * definition installed, 3) its the right version!
+        *
+        * Therefore, I'm not bothering.
+        *
+        * Override this to include any more specific information, however api results should be attached to the
+        * class using setResult().
+        *
+        * if $CONFIG->debug is set then additional information about the runtime environment and authentication will be
+        * returned.
+        *
+        * @return stdClass Object containing the serialised result.
         */
-       class ElggHMACCache extends ElggCache
-       {
-               /**
-                * Set the Elgg cache.
-                *
-                * @param int $max_age Maximum age in seconds, 0 if no limit.
-                */
-               function __construct($max_age = 0)
-               {
-                       $this->set_variable("max_age", $max_age);
+       public function export() {
+               global $ERRORS, $CONFIG, $_PAM_HANDLERS_MSG;
+
+               $result = new stdClass;
+
+               $result->status = $this->getStatusCode();
+               if ($this->getStatusMessage()!="") {
+                       $result->message = $this->getStatusMessage();
                }
-       
-               /**
-                * Save a key
-                *
-                * @param string $key
-                * @param string $data
-                * @return boolean
-                */
-               public function save($key, $data)
-               {
-                       global $CONFIG;
-                       
-                       $key = sanitise_string($key);
-                       $time = time();
-                       
-                       return insert_data("INSERT into {$CONFIG->dbprefix}hmac_cache (hmac, ts) VALUES ('$key', '$time')");
+
+               $resultdata = $this->getResult();
+               if (isset($resultdata)) {
+                       $result->result = $resultdata;
                }
-               
-               /**
-                * Load a key
-                *
-                * @param string $key
-                * @param int $offset
-                * @param int $limit
-                * @return string
-                */
-               public function load($key, $offset = 0, $limit = null)
-               {
-                       global $CONFIG;
-                       
-                       $key = sanitise_string($key);
-                       
-                       $row = get_data_row("SELECT * from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
-                       if ($row)
-                               return $row->hmac;
-                       
-                       return false;
+
+               if ((isset($CONFIG->debug)) && ($CONFIG->debug == true)) {
+                       if (count($ERRORS)) {
+                               $result->runtime_errors = $ERRORS;
+                       }
+
+                       if (count($_PAM_HANDLERS_MSG)) {
+                               $result->pam = $_PAM_HANDLERS_MSG;
+                       }
                }
-               
-               /**
-                * Invalidate a given key.
-                *
-                * @param string $key
-                * @return bool
-                */
-               public function delete($key)
-               {
-                       global $CONFIG;
-                       
-                       $key = sanitise_string($key);
-                       
-                       return delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
+
+               return $result;
+       }
+}
+
+/**
+ * SuccessResult
+ * Generic success result class, extend if you want to do something special.
+ *
+ * @author Curverider Ltd <info@elgg.com>
+ * @package Elgg
+ * @subpackage Core
+ */
+class SuccessResult extends GenericResult {
+       public static $RESULT_SUCCESS = 0;  // Do not change this from 0
+
+       public function SuccessResult($result) {
+               $this->setResult($result);
+               $this->setStatusCode(SuccessResult::$RESULT_SUCCESS);
+       }
+
+       public static function getInstance($result) {
+               // Return a new error object.
+               return new SuccessResult($result);
+       }
+}
+
+/**
+ * ErrorResult
+ * The error result class.
+ *
+ * @author Curverider Ltd <info@elgg.com>
+ * @package Elgg
+ * @subpackage Core
+ */
+class ErrorResult extends GenericResult {
+       // Fail with no specific code
+       public static $RESULT_FAIL = -1 ;
+
+       public static $RESULT_FAIL_APIKEY_DISABLED = -30;
+       public static $RESULT_FAIL_APIKEY_INACTIVE = -31;
+       public static $RESULT_FAIL_APIKEY_INVALID = -32;
+
+       // Invalid, expired or missing auth token
+       public static $RESULT_FAIL_AUTHTOKEN = -20;
+
+       public function ErrorResult($message, $code = "", Exception $exception = NULL) {
+               if ($code == "") {
+                       $code = ErrorResult::$RESULT_FAIL;
                }
-               
-               /**
-                * Clear out all the contents of the cache.
-                * 
-                * Not currently implemented in this cache type.
-                */
-               public function clear() { return true; }
-               
-               /**
-                * Clean out old stuff.
-                *
-                */
-               public function __destruct()
-               {
-                       global $CONFIG;
-                       
-                       $time = time();
-                       $age = (int)$this->get_variable("max_age");
-                       
-                       $expires = $time-$age;
-                       
-                       delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where ts<$expires");
+
+               if ($exception!=NULL) {
+                       $this->setResult($exception->__toString());
                }
+
+               $this->setStatusCode($code, $message);
        }
 
-       // API Call functions /////////////////////////////////////////////////////////////////////     
-       
-       /** 
-        * An array holding methods.
-        * The structure of this is 
-        *      $METHODS = array (
-        *              "api.method" => array (
-        *                      "function" = 'my_function_callback'
-        *                      "call_method" = 'GET' | 'POST'
-        *                      "parameters" = array (
-        *                              "variable" = array ( // NB, the order is the same as defined by your function callback
-        *                                      type => 'int' | 'bool' | 'float' | 'string'
-        *                                      required => true (default) | false  
-        *                              )
-        *                      )
-        *                      "require_auth_token" => true (default) | false
-        *                      "description" => "Some human readable description"
-        *              )
-        *  )
-        */
-       $METHODS = array();
-       
        /**
-        * Get the request method.
+        * Get a new instance of the ErrorResult.
+        *
+        * @param string $message
+        * @param int $code
+        * @param Exception $exception Optional exception for generating a stack trace.
         */
-       function get_call_method()
-       {
-               return $_SERVER['REQUEST_METHOD'];
+       public static function getInstance($message, $code = "", Exception $exception = NULL) {
+               // Return a new error object.
+               return new ErrorResult($message, $code, $exception);
        }
-       
+}
+
+// Caching of HMACs ///////////////////////////////////////////////////////////////////////
+
+/**
+ * ElggHMACCache
+ * Store cached data in a temporary database, only used by the HMAC stuff.
+ *
+ * @author Curverider Ltd <info@elgg.com>
+ * @package Elgg
+ * @subpackage API
+ */
+class ElggHMACCache extends ElggCache {
        /**
-        * This function analyses all expected parameters for a given method, returning them in an associated array from
-        * input. 
-        * 
-        * This ensures that they are sanitised and that no superfluous commands are registered. It also means that 
-        * hmacs work through the page handler.
+        * Set the Elgg cache.
         *
-        * @param string $method The method
-        * @return Array containing commands and values, including method and api
+        * @param int $max_age Maximum age in seconds, 0 if no limit.
         */
-       function get_parameters_for_method($method)
-       {
-               global $CONFIG, $METHODS;
-
-               $method = sanitise_string($method);
-               $sanitised = array();
-               
-               foreach ($CONFIG->input as $k => $v)
-               {
-                       if ((isset($METHODS[$method]['parameters'][$k])) || ($k == 'auth_token') || ($k == 'method'))
-                               $sanitised[$k] = get_input($k); // Make things go through the sanitiser 
-               }
-       
-               return $sanitised;
+       function __construct($max_age = 0) {
+               $this->set_variable("max_age", $max_age);
        }
-       
+
        /**
-        * Obtain a token for a user.
+        * Save a key
         *
-        * @param string $username The username
-        * @param string $password The password
+        * @param string $key
+        * @param string $data
+        * @return boolean
         */
-       function obtain_user_token($username, $password)
-       {
+       public function save($key, $data) {
                global $CONFIG;
-               
-               $site = $CONFIG->site_id;
-               $user = get_user_by_username($username);
+
+               $key = sanitise_string($key);
                $time = time();
-               $time += 60*60;
-               $token = md5(rand(). microtime() . $username . $password . $time . $site);
-               
-               if (!$user) return false; 
-               
-               if (insert_data("INSERT into {$CONFIG->dbprefix}users_apisessions (user_guid, site_guid, token, expires) values ({$user->guid}, $site, '$token', '$time') on duplicate key update token='$token', expires='$time'"))
-                       return $token;
-                       
-               return false;
+
+               return insert_data("INSERT into {$CONFIG->dbprefix}hmac_cache (hmac, ts) VALUES ('$key', '$time')");
        }
-       
+
        /**
-        * Validate a token against a given site. 
-        * 
-        * A token registered with one site can not be used from a different apikey(site), so be aware of this
-        * during development.
-        * 
-        * @param int $site The ID of the site
-        * @param string $token The Token.
-        * @return mixed The user id attached to the token or false.
+        * Load a key
+        *
+        * @param string $key
+        * @param int $offset
+        * @param int $limit
+        * @return string
         */
-       function validate_user_token($site, $token)
-       {
+       public function load($key, $offset = 0, $limit = null) {
                global $CONFIG;
-               
-               $site = (int)$site;
-               $token = sanitise_string($token);
-               
-               if (!$site) throw new ConfigurationException(elgg_echo('ConfigurationException:NoSiteID'));
-               
-               $time = time();
-               
-               $user = get_data_row("SELECT * from {$CONFIG->dbprefix}users_apisessions where token='$token' and site_guid=$site and $time < expires");
-               if ($user)
-                       return $user->user_guid;
-               
+
+               $key = sanitise_string($key);
+
+               $row = get_data_row("SELECT * from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
+               if ($row) {
+                       return $row->hmac;
+               }
+
                return false;
        }
-       
+
        /**
-        * Expose an arbitrary function as an api call.
-        * 
-        * Limitations: Currently can not expose functions which expect objects.
-        * 
-        * @param string $method The api name to expose this as, eg "myapi.dosomething"
-        * @param string $function Your function callback.
-        * @param array $parameters Optional list of parameters in the same order as in your function, with optional parameters last. 
-        *      This array should be in the format 
-        *   "variable" = array ( 
-        *                                      type => 'int' | 'bool' | 'float' | 'string' | 'array'
-        *                                      required => true (default) | false  
-        *       )
-        * @param string $description Optional human readable description of the function.
-        * @param string $call_method Define what call method should be used for this function.
-        * @param bool $require_auth_token Whether this requires a user authentication token or not (default is true).
-        * @param bool $anonymous Can anonymous (non-authenticated in any way) users execute this call. 
+        * Invalidate a given key.
+        *
+        * @param string $key
         * @return bool
         */
-       function expose_function($method, $function, array $parameters = NULL, $description = "", $call_method = "GET", $require_auth_token = true, $anonymous = false)
-       {
-               global $METHODS;
-               
-               if (
-                       ($method!="") &&
-                       ($function!="")
-               )
-               {
-                       $METHODS[$method] = array();
-                               
-                       $METHODS[$method]["function"] = $function;
-                       
-                       if ($parameters!=NULL)
-                               $METHODS[$method]["parameters"] = $parameters;
-                               
-                       $call_method = strtoupper($call_method);
-                       switch ($call_method)
-                       {
-                               case 'POST' : $METHODS[$method]["call_method"] = 'POST'; break;
-                               case 'GET' : $METHODS[$method]["call_method"] = 'GET'; break;
-                               default : 
-                                       throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:UnrecognisedMethod'), $method));
-                       }
-                               
-                       $METHODS[$method]["description"] = $description;
-                       
-                       $METHODS[$method]["require_auth_token"] = $require_auth_token;
-                       
-                       $METHODS[$method]["anonymous"] = $anonymous;
-                       
-                       return true;
-               }
-               
-               return false;   
+       public function delete($key) {
+               global $CONFIG;
+
+               $key = sanitise_string($key);
+
+               return delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where hmac='$key'");
        }
-       
+
+       /**
+        * Clear out all the contents of the cache.
+        *
+        * Not currently implemented in this cache type.
+        */
+       public function clear() {
+               return true;
+       }
+
        /**
-        * Executes a method.
-        * A method is a function which you have previously exposed using expose_function.
+        * Clean out old stuff.
         *
-        * @param string $method Method, e.g. "foo.bar"
-        * @param array $parameters Array of parameters in the format "variable" => "value", thse will be sanitised before being fed to your handler.
-        * @param string $token The authentication token to authorise this method call.
-        * @return GenericResult The result of the execution.
-        * @throws APIException, SecurityException
         */
-       function execute_method($method, array $parameters, $token = "")
-       {
-               global $METHODS, $CONFIG;
-               
-               // Sanity check
-               $method = sanitise_string($method); 
-               $token = sanitise_string($token); 
-               
-               // See if we can find the method handler
-               if ((isset($METHODS[$method]["function"])) && (is_callable($METHODS[$method]["function"])))
-               {
-                       // See if this is being made with the right call method
-                       if (strcmp(get_call_method(), $METHODS[$method]["call_method"])==0)
-                       {
-                               $serialised_parameters = "";
-                               
-                               
-                               // If we have parameters then we need to sanitise the parameters.
-                               if ((isset($METHODS[$method]["parameters"])) && (is_array($METHODS[$method]["parameters"]))) 
-                               {
-                                       foreach ($METHODS[$method]["parameters"] as $key => $value)
-                                       {
-                                               if (
-                                                       (is_array($value))                      // Check that this is an array
-                                                       && (isset($value['type']))              // Check we have a type defined
-                                               )
-                                               {
-                                                       // Check that the variable is present in the request
-
-                                                       if (
-                                                               (!isset($parameters[$key])) ||                          // No parameter
-                                                               ((!isset($value['required'])) || ($value['required']==true)) // Or not optional
-                                                       )
+       public function __destruct() {
+               global $CONFIG;
+
+               $time = time();
+               $age = (int)$this->get_variable("max_age");
+
+               $expires = $time-$age;
+
+               delete_data("DELETE from {$CONFIG->dbprefix}hmac_cache where ts<$expires");
+       }
+}
+
+// API Call functions /////////////////////////////////////////////////////////////////////
+
+/**
+ * An array holding methods.
+ * The structure of this is
+ *     $METHODS = array (
+ *             "api.method" => array (
+ *                     "function" = 'my_function_callback'
+ *                     "call_method" = 'GET' | 'POST'
+ *                     "parameters" = array (
+ *                             "variable" = array ( // NB, the order is the same as defined by your function callback
+ *                                     type => 'int' | 'bool' | 'float' | 'string'
+ *                                     required => true (default) | false
+ *                             )
+ *                     )
+ *                     "require_auth_token" => true (default) | false
+ *                     "description" => "Some human readable description"
+ *             )
+ *  )
+ */
+$METHODS = array();
+
+/**
+ * Get the request method.
+ */
+function get_call_method() {
+       return $_SERVER['REQUEST_METHOD'];
+}
+
+/**
+ * This function analyses all expected parameters for a given method, returning them in an associated array from
+ * input.
+ *
+ * This ensures that they are sanitised and that no superfluous commands are registered. It also means that
+ * hmacs work through the page handler.
+ *
+ * @param string $method The method
+ * @return Array containing commands and values, including method and api
+ */
+function get_parameters_for_method($method) {
+       global $CONFIG, $METHODS;
+
+       $method = sanitise_string($method);
+       $sanitised = array();
+
+       foreach ($CONFIG->input as $k => $v) {
+               if ((isset($METHODS[$method]['parameters'][$k])) || ($k == 'auth_token') || ($k == 'method')) {
+                       // Make things go through the sanitiser
+                       $sanitised[$k] = get_input($k);
+               }
+       }
+
+       return $sanitised;
+}
+
+/**
+ * Obtain a token for a user.
+ *
+ * @param string $username The username
+ * @param string $password The password
+ */
+function obtain_user_token($username, $password) {
+       global $CONFIG;
+
+       $site = $CONFIG->site_id;
+       $user = get_user_by_username($username);
+       $time = time();
+       $time += 60*60;
+       $token = md5(rand(). microtime() . $username . $password . $time . $site);
+
+       if (!$user) {
+               return false;
+       }
+
+       if (insert_data("INSERT into {$CONFIG->dbprefix}users_apisessions
+       (user_guid, site_guid, token, expires) values
+       ({$user->guid}, $site, '$token', '$time') on duplicate key update token='$token', expires='$time'")) {
+               return $token;
+       }
+
+
+       return false;
+}
+
+/**
+ * Validate a token against a given site.
+ *
+ * A token registered with one site can not be used from a different apikey(site), so be aware of this
+ * during development.
+ *
+ * @param int $site The ID of the site
+ * @param string $token The Token.
+ * @return mixed The user id attached to the token or false.
+ */
+function validate_user_token($site, $token) {
+       global $CONFIG;
+
+       $site = (int)$site;
+       $token = sanitise_string($token);
+
+       if (!$site) {
+               throw new ConfigurationException(elgg_echo('ConfigurationException:NoSiteID'));
+       }
+
+       $time = time();
+
+       $user = get_data_row("SELECT * from {$CONFIG->dbprefix}users_apisessions
+               where token='$token' and site_guid=$site and $time < expires");
+
+       if ($user) {
+               return $user->user_guid;
+       }
+
+       return false;
+}
+
+/**
+ * Expose an arbitrary function as an api call.
+ *
+ * Limitations: Currently can not expose functions which expect objects.
+ *
+ * @param string $method The api name to expose this as, eg "myapi.dosomething"
+ * @param string $function Your function callback.
+ * @param array $parameters Optional list of parameters in the same order as in your function, with optional parameters last.
+ *     This array should be in the format
+ *   "variable" = array (
+ *                                     type => 'int' | 'bool' | 'float' | 'string' | 'array'
+ *                                     required => true (default) | false
+ *      )
+ * @param string $description Optional human readable description of the function.
+ * @param string $call_method Define what call method should be used for this function.
+ * @param bool $require_auth_token Whether this requires a user authentication token or not (default is true).
+ * @param bool $anonymous Can anonymous (non-authenticated in any way) users execute this call.
+ * @return bool
+ */
+function expose_function($method, $function, array $parameters = NULL, $description = "", $call_method = "GET", $require_auth_token = true, $anonymous = false) {
+       global $METHODS;
+
+       if (($method!="") && ($function!="")) {
+               $METHODS[$method] = array();
+
+               $METHODS[$method]["function"] = $function;
+
+               if ($parameters!=NULL) {
+                       $METHODS[$method]["parameters"] = $parameters;
+               }
+
+               $call_method = strtoupper($call_method);
+               switch ($call_method) {
+                       case 'POST' :
+                               $METHODS[$method]["call_method"] = 'POST';
+                               break;
+                       case 'GET' :
+                               $METHODS[$method]["call_method"] = 'GET';
+                               break;
+                       default :
+                               throw new InvalidParameterException(sprintf(elgg_echo('InvalidParameterException:UnrecognisedMethod'), $method));
+               }
+
+               $METHODS[$method]["description"] = $description;
+
+               $METHODS[$method]["require_auth_token"] = $require_auth_token;
+
+               $METHODS[$method]["anonymous"] = $anonymous;
+
+               return true;
+       }
+
+       return false;
+}
+
+/**
+ * Executes a method.
+ * A method is a function which you have previously exposed using expose_function.
+ *
+ * @param string $method Method, e.g. "foo.bar"
+ * @param array $parameters Array of parameters in the format "variable" => "value", thse will be sanitised before being fed to your handler.
+ * @param string $token The authentication token to authorise this method call.
+ * @return GenericResult The result of the execution.
+ * @throws APIException, SecurityException
+ */
+function execute_method($method, array $parameters, $token = "") {
+       global $METHODS, $CONFIG;
+
+       // Sanity check
+       $method = sanitise_string($method);
+       $token = sanitise_string($token);
+
+       // See if we can find the method handler
+       if ((isset($METHODS[$method]["function"])) && (is_callable($METHODS[$method]["function"]))) {
+               // See if this is being made with the right call method
+               if (strcmp(get_call_method(), $METHODS[$method]["call_method"])==0) {
+                       $serialised_parameters = "";
+
+                       // If we have parameters then we need to sanitise the parameters.
+                       if ((isset($METHODS[$method]["parameters"])) && (is_array($METHODS[$method]["parameters"]))) {
+                               foreach ($METHODS[$method]["parameters"] as $key => $value) {
+
+                                       if ((is_array($value)) && (isset($value['type']))) {
+                                               // Check that the variable is present in the request
+                                               if ((!isset($parameters[$key]))
+                                               || ((!isset($value['required']))
+                                               || ($value['required']==true))) {
                                                                throw new APIException(sprintf(elgg_echo('APIException:MissingParameterInMethod'), $key, $method));
-                                                       else
-                                                       {
-                                                               // Avoid debug error
-                                                               if (isset($parameters[$key]))
-                                                               {
-                                                                       // Set variables casting to type.       
-                                                                       switch (strtolower($value['type']))
-                                                                       {
-                                                                               case 'int':
-                                                                               case 'integer' : $serialised_parameters .= "," . (int)trim($parameters[$key]); break;
-                                                                               case 'bool':
-                                                                               case 'boolean': 
-                                                                                                       if (strcasecmp(trim($parameters[$key]), "false")==0) 
-                                                                                                               $parameters[$key]='';
-                                                                                                                       
-                                                                                                       $serialised_parameters .= "," . (bool)trim($parameters[$key]); 
-                                                                                                       break;
-                                                                               case 'string': $serialised_parameters .= ",'" .  (string)mysql_real_escape_string(trim($parameters[$key])) . "'"; 
-                                                                                                       break;
-                                                                               case 'float': $serialised_parameters .= "," . (float)trim($parameters[$key]); 
-                                                                                                       break;
-                                                                               case 'array':
-                                                                                                               $array = "array(";
-                                                                                                               
-                                                                                                               if (is_array($parameters[$key]))
-                                                                                                               {
-                                                                                                                       foreach ($parameters[$key] as $k => $v)
-                                                                                                                       {
-                                                                                                                               $k = sanitise_string($k);
-                                                                                                                               $v = sanitise_string($v);
-                                                                                                                               
-                                                                                                                               $array .= "'$k'=>'$v',";
-                                                                                                                       }
-                                                                                                                       
-                                                                                                                       $array = trim($array,",");
-                                                                                                               }
-                                                                                                               else
-                                                                                                                       throw APIException(sprintf(elgg_echo('APIException:ParameterNotArray'), $key));
-                                                                                                                       
-                                                                                                               $array .= ")";
-                                                                                                               
-                                                                                                               $serialised_parameters .= $array;
-                                                                                                       break;
-                       
-                                                                               default : throw new APIException(sprintf(elgg_echo('APIException:UnrecognisedTypeCast'), $value['type'], $key, $method));
-                                                                       }
+                                                       } else {
+                                                       // Avoid debug error
+                                                       if (isset($parameters[$key])) {
+                                                               // Set variables casting to type.
+                                                               switch (strtolower($value['type'])) {
+                                                                       case 'int':
+                                                                       case 'integer' :
+                                                                               $serialised_parameters .= "," . (int)trim($parameters[$key]); break;
+                                                                       case 'bool':
+                                                                       case 'boolean':
+                                                                               if (strcasecmp(trim($parameters[$key]), "false")==0) {
+                                                                                       $parameters[$key]='';
+                                                                               }
+
+                                                                               $serialised_parameters .= "," . (bool)trim($parameters[$key]);
+                                                                               break;
+                                                                       case 'string':
+                                                                               $serialised_parameters .= ",'" .  (string)mysql_real_escape_string(trim($parameters[$key])) . "'";
+                                                                               break;
+                                                                       case 'float':
+                                                                               $serialised_parameters .= "," . (float)trim($parameters[$key]);
+                                                                               break;
+                                                                       case 'array':
+                                                                               $array = "array(";
+
+                                                                               if (is_array($parameters[$key])) {
+                                                                                       foreach ($parameters[$key] as $k => $v) {
+                                                                                               $k = sanitise_string($k);
+                                                                                               $v = sanitise_string($v);
+
+                                                                                               $array .= "'$k'=>'$v',";
+                                                                                       }
+
+                                                                                       $array = trim($array,",");
+                                                                               } else {
+                                                                                       throw APIException(sprintf(elgg_echo('APIException:ParameterNotArray'), $key));
+                                                                               }
+
+                                                                               $array .= ")";
+
+                                                                               $serialised_parameters .= $array;
+                                                                               break;
+
+                                                                       default :
+                                                                               throw new APIException(sprintf(elgg_echo('APIException:UnrecognisedTypeCast'), $value['type'], $key, $method));
                                                                }
                                                        }
                                                }
-                                               else
-                                                       throw new APIException(sprintf(elgg_echo('APIException:InvalidParameter'), $key, $method));
+                                       } else {
+                                               throw new APIException(sprintf(elgg_echo('APIException:InvalidParameter'), $key, $method));
                                        }
                                }
-                               
-                               // Execute function: Construct function and calling parameters
-                               $function = $METHODS[$method]["function"];
-                               $serialised_parameters = trim($serialised_parameters, ", ");
-                               
-                               $result = eval("return $function($serialised_parameters);");
-                       
-                               // Sanity check result
-                               if ($result instanceof GenericResult) // If this function returns an api result itself, just return it
-                                       return $result; 
-                                       
-                               if ($result === FALSE)
-                                       throw new APIException(sprintf(elgg_echo('APIException:FunctionParseError'), $function, $serialised_parameters));
-                                       
-                               if ($result ===  NULL)
-                                       throw new APIException(sprintf(elgg_echo('APIException:FunctionNoReturn'), $function, $serialised_parameters)); // If no value
-                               
-                               return SuccessResult::getInstance($result); // Otherwise assume that the call was successful and return it as a success object. 
-                        
-                       }       
-                       else
-                               throw new CallException(sprintf(elgg_echo('CallException:InvalidCallMethod'), $method, $METHODS[$method]["call_method"]));
+                       }
+
+                       // Execute function: Construct function and calling parameters
+                       $function = $METHODS[$method]["function"];
+                       $serialised_parameters = trim($serialised_parameters, ", ");
+
+                       $result = eval("return $function($serialised_parameters);");
+
+                       // Sanity check result
+                       // If this function returns an api result itself, just return it
+                       if ($result instanceof GenericResult) {
+                               return $result;
+                       }
+
+                       if ($result === FALSE) {
+                               throw new APIException(sprintf(elgg_echo('APIException:FunctionParseError'), $function, $serialised_parameters));
+                       }
+
+                       if ($result ===  NULL) {
+                               // If no value
+                               throw new APIException(sprintf(elgg_echo('APIException:FunctionNoReturn'), $function, $serialised_parameters));
+                       }
+
+                       // Otherwise assume that the call was successful and return it as a success object.
+                       return SuccessResult::getInstance($result);
+
+               } else {
+                       throw new CallException(sprintf(elgg_echo('CallException:InvalidCallMethod'), $method, $METHODS[$method]["call_method"]));
                }
-       
-               // Return an error if not found
-               throw new APIException(sprintf(elgg_echo('APIException:MethodCallNotImplemented'), $method)); 
        }
-               
-       // System functions ///////////////////////////////////////////////////////////////////////
-       
-       /**
-        * Simple api to return a list of all api's installed on the system.
-        */
-       function list_all_apis()
-       {
-               global $METHODS;
-               return $METHODS;
-       }
-       
-       // Expose some system api functions
-       expose_function("system.api.list", "list_all_apis", NULL, elgg_echo("system.api.list"), "GET", false);
 
-       /**
-        * The auth.gettoken API.
-        * This API call lets a user log in, returning an authentication token which can be used
-        * in leu of a username and password login from then on.
-        * 
-        * @param string username Username
-        * @param string password Clear text password
-        */
-       function auth_gettoken($username, $password)
-       {
-               if (authenticate($username, $password))
-               {
-                       $token = obtain_user_token($username, $password);
-                       if ($token)
-                               return $token;
+       // Return an error if not found
+       throw new APIException(sprintf(elgg_echo('APIException:MethodCallNotImplemented'), $method));
+}
+
+// System functions ///////////////////////////////////////////////////////////////////////
+
+/**
+ * Simple api to return a list of all api's installed on the system.
+ */
+function list_all_apis() {
+       global $METHODS;
+       return $METHODS;
+}
+
+// Expose some system api functions
+expose_function("system.api.list", "list_all_apis", NULL, elgg_echo("system.api.list"), "GET", false);
+
+/**
+ * The auth.gettoken API.
+ * This API call lets a user log in, returning an authentication token which can be used
+ * in leu of a username and password login from then on.
+ *
+ * @param string username Username
+ * @param string password Clear text password
+ */
+function auth_gettoken($username, $password) {
+       if (authenticate($username, $password)) {
+               $token = obtain_user_token($username, $password);
+               if ($token) {
+                       return $token;
                }
-               
-               throw new SecurityException(elgg_echo('SecurityException:authenticationfailed'));
        }
-       
-       // The authentication token api
-       expose_function("auth.gettoken", "auth_gettoken", array(
-               "username" => array (
-                       'type' => 'string'
-               ),
-               "password" => array (
-                       'type' => 'string'
-               )
-       ), elgg_echo('auth.gettoken'), "GET", false, false);
-       
-       
-       // PAM AUTH HMAC functions ////////////////////////////////////////////////////////////////
-       
-       /**
-        * Map various algorithms to their PHP equivs.
-        * This also gives us an easy way to disable algorithms.
-        * 
-        * @param string $algo The algorithm
-        * @return string The php algorithm
-        * @throws APIException if an algorithm is not supported. 
-        */
-       function map_api_hash($algo)
-       {
-               $algo = strtolower(sanitise_string($algo));
-               $supported_algos = array(
-                       "md5" => "md5", // TODO: Consider phasing this out
-                       "sha" => "sha1", // alias for sha1
-                       "sha1" => "sha1",
-                       "sha256" => "sha256"
-               );
-               
-               if (array_key_exists($algo, $supported_algos))
-                       return $supported_algos[$algo];
-                       
-               throw new APIException(sprintf(elgg_echo('APIException:AlgorithmNotSupported'), $algo));
+
+       throw new SecurityException(elgg_echo('SecurityException:authenticationfailed'));
+}
+
+// The authentication token api
+expose_function("auth.gettoken", "auth_gettoken", array(
+       "username" => array (
+               'type' => 'string'
+       ),
+       "password" => array (
+               'type' => 'string'
+       )
+), elgg_echo('auth.gettoken'), "GET", false, false);
+
+
+// PAM AUTH HMAC functions ////////////////////////////////////////////////////////////////
+
+/**
+ * Map various algorithms to their PHP equivs.
+ * This also gives us an easy way to disable algorithms.
+ *
+ * @param string $algo The algorithm
+ * @return string The php algorithm
+ * @throws APIException if an algorithm is not supported.
+ */
+function map_api_hash($algo) {
+       $algo = strtolower(sanitise_string($algo));
+       $supported_algos = array(
+               "md5" => "md5", // TODO: Consider phasing this out
+               "sha" => "sha1", // alias for sha1
+               "sha1" => "sha1",
+               "sha256" => "sha256"
+       );
+
+       if (array_key_exists($algo, $supported_algos)) {
+               return $supported_algos[$algo];
        }
-       
-       /**
-        * Calculate the HMAC for the query.
-        * This function signs an api request using the information provided and is then verified by
-        * searunner.
-        * 
-        * @param $algo string The HMAC algorithm used as stored in X-Searunner-hmac-algo.
-        * @param $time string String representation of unix time as stored in X-Searunner-time.
-        * @param $api_key string Your api key.
-        * @param $secret string Your secret key.
-        * @param $get_variables string URLEncoded string representation of the get variable parameters, eg "format=php&method=searunner.test".
-        * @param $post_hash string Optional sha1 hash of the post data.
-        * @return string The HMAC string.
-        */
-       function calculate_hmac($algo, $time, $api_key, $secret_key, $get_variables, $post_hash = "")
-       {
-               global $CONFIG;
-               
-               if ((isset($CONFIG)) && ($CONFIG->debug))
-                       error_log("HMAC Parts: $algo, $time, $api_key, $secret_key, $get_variables, $post_hash");
-               
-               $ctx = hash_init(map_api_hash($algo), HASH_HMAC, $secret_key);
-
-               hash_update($ctx, trim($time));
-               hash_update($ctx, trim($api_key));
-               hash_update($ctx, trim($get_variables));
-               if (trim($post_hash)!="") hash_update($ctx, trim($post_hash));
-
-               return hash_final($ctx);
+
+       throw new APIException(sprintf(elgg_echo('APIException:AlgorithmNotSupported'), $algo));
+}
+
+/**
+ * Calculate the HMAC for the query.
+ * This function signs an api request using the information provided and is then verified by
+ * searunner.
+ *
+ * @param $algo string The HMAC algorithm used as stored in X-Searunner-hmac-algo.
+ * @param $time string String representation of unix time as stored in X-Searunner-time.
+ * @param $api_key string Your api key.
+ * @param $secret string Your secret key.
+ * @param $get_variables string URLEncoded string representation of the get variable parameters, eg "format=php&method=searunner.test".
+ * @param $post_hash string Optional sha1 hash of the post data.
+ * @return string The HMAC string.
+ */
+function calculate_hmac($algo, $time, $api_key, $secret_key, $get_variables, $post_hash = "") {
+       global $CONFIG;
+
+       if ((isset($CONFIG)) && ($CONFIG->debug)) {
+               error_log("HMAC Parts: $algo, $time, $api_key, $secret_key, $get_variables, $post_hash");
        }
 
-       /**
-        * Calculate a hash for some post data.
-        * 
-        * TODO: Work out how to handle really large bits of data.
-        *
-        * @param $postdata string The post data.
-        * @param $algo string The algorithm used.
-        * @return string The hash.
-        */
-       function calculate_posthash($postdata, $algo)
-       {
-               $ctx = hash_init(map_api_hash($algo));
+       $ctx = hash_init(map_api_hash($algo), HASH_HMAC, $secret_key);
+
+       hash_update($ctx, trim($time));
+       hash_update($ctx, trim($api_key));
+       hash_update($ctx, trim($get_variables));
+       if (trim($post_hash)!="") {
+               hash_update($ctx, trim($post_hash));
+       }
+
+       return hash_final($ctx);
+}
+
+/**
+ * Calculate a hash for some post data.
+ *
+ * TODO: Work out how to handle really large bits of data.
+ *
+ * @param $postdata string The post data.
+ * @param $algo string The algorithm used.
+ * @return string The hash.
+ */
+function calculate_posthash($postdata, $algo) {
+       $ctx = hash_init(map_api_hash($algo));
+
+       hash_update($ctx, $postdata);
 
-               hash_update($ctx, $postdata);
+       return hash_final($ctx);
+}
 
-               return hash_final($ctx);
+/**
+ * This function will do two things. Firstly it verifys that a $hmac hasn't been seen before, and
+ * secondly it will add the given hmac to the cache.
+ *
+ * @param $hmac The hmac string.
+ * @return bool True if replay detected, false if not.
+ */
+function cache_hmac_check_replay($hmac) {
+       // cache lifetime is 25 hours (see time window in get_and_validate_api_headers() )
+       $cache = new ElggHMACCache(90000);
+
+       if (!$cache->load($hmac)) {
+               $cache->save($hmac, $hmac);
+
+               return false;
        }
-       
-       /**
-        * This function will do two things. Firstly it verifys that a $hmac hasn't been seen before, and 
-        * secondly it will add the given hmac to the cache.
-        * 
-        * @param $hmac The hmac string.
-        * @return bool True if replay detected, false if not.
-        */
-       function cache_hmac_check_replay($hmac)
-       {
-               $cache = new ElggHMACCache(90000); // cache lifetime is 25 hours (see time window in get_and_validate_api_headers() )
-       
-               if (!$cache->load($hmac))
-               {       
-                       $cache->save($hmac, $hmac);
-               
-                       return false;
-               }
-                               
-               return true;
+
+       return true;
+}
+
+/**
+ * Find an API User's details based on the provided public api key. These users are not users in the traditional sense.
+ *
+ * @param int $site_guid The GUID of the site.
+ * @param string $api_key The API Key
+ * @return mixed stdClass representing the database row or false.
+ */
+function get_api_user($site_guid, $api_key) {
+       global $CONFIG;
+
+       $api_key = sanitise_string($api_key);
+       $site_guid = (int)$site_guid;
+
+       return get_data_row("SELECT * from {$CONFIG->dbprefix}api_users where api_key='$api_key' and site_guid=$site_guid and active=1");
+}
+
+/**
+ * Revoke an api user key.
+ *
+ * @param int $site_guid The GUID of the site.
+ * @param string $api_key The API Key (public).
+ */
+function remove_api_user($site_guid, $api_key) {
+       global $CONFIG;
+
+       $keypair = get_api_user($site_guid, $api_key);
+       if ($keypair) {
+               return delete_data("DELETE from {$CONFIG->dbprefix}api_users where id={$keypair->id}");
        }
-       
-       /**
-        * Find an API User's details based on the provided public api key. These users are not users in the traditional sense.
-        *
-        * @param int $site_guid The GUID of the site.
-        * @param string $api_key The API Key
-        * @return mixed stdClass representing the database row or false.
-        */
-       function get_api_user($site_guid, $api_key)
-       {
-               global $CONFIG;
-               
-               $api_key = sanitise_string($api_key);
-               $site_guid = (int)$site_guid;
-               
-               return get_data_row("SELECT * from {$CONFIG->dbprefix}api_users where api_key='$api_key' and site_guid=$site_guid and active=1");       
+
+       return false;
+}
+
+/**
+ * Generate a new API user for a site, returning a new keypair on success.
+ *
+ * @param int $site_guid The GUID of the site.
+ */
+function create_api_user($site_guid) {
+       global $CONFIG;
+
+       $site_guid = (int)$site_guid;
+
+       $public = sha1(rand().$site_guid.microtime());
+       $secret = sha1(rand().$site_guid.microtime().$public);
+
+       $insert = insert_data("INSERT into {$CONFIG->dbprefix}api_users
+               (site_guid, api_key, secret) values
+               ($site_guid, '$public', '$secret')");
+
+       if ($insert) {
+               return get_api_user($site_guid, $public);
        }
-       
-       /**
-        * Revoke an api user key.
-        *
-        * @param int $site_guid The GUID of the site.
-        * @param string $api_key The API Key (public).
-        */
-       function remove_api_user($site_guid, $api_key)
-       {
-               global $CONFIG;
-               
-               $keypair = get_api_user($site_guid, $api_key);
-               if ($keypair)
-                       return delete_data("DELETE from {$CONFIG->dbprefix}api_users where id={$keypair->id}");
-                       
-               return false;
+
+       return false;
+}
+
+/**
+ * This function looks at the super-global variable $_SERVER and extracts the various
+ * header variables needed to pass to the validation functions after performing basic validation.
+ *
+ * @return stdClass Containing all the values.
+ * @throws APIException Detailing any error.
+ */
+function get_and_validate_api_headers() {
+       $result = new stdClass;
+
+       $result->method = get_call_method();
+       // Only allow these methods
+       if (($result->method != "GET") && ($result->method!= "POST")) {
+               throw new APIException(elgg_echo('APIException:NotGetOrPost'));
        }
-       
-       /**
-        * Generate a new API user for a site, returning a new keypair on success.
-        *
-        * @param int $site_guid The GUID of the site.
-        */
-       function create_api_user($site_guid)
-       {
-               global $CONFIG;
-               
-               $site_guid = (int)$site_guid;
-               
-               $public = sha1(rand().$site_guid.microtime());
-               $secret = sha1(rand().$site_guid.microtime().$public);
-               
-               if (insert_data("INSERT into {$CONFIG->dbprefix}api_users (site_guid, api_key, secret) values ($site_guid, '$public', '$secret')"))
-                       return get_api_user($site_guid, $public);
-                       
-               return false;
+
+       $result->api_key = $_SERVER['HTTP_X_ELGG_APIKEY'];
+       if ($result->api_key == "") {
+               throw new APIException(elgg_echo('APIException:MissingAPIKey'));
        }
-       
-       /**
-        * This function looks at the super-global variable $_SERVER and extracts the various
-        * header variables needed to pass to the validation functions after performing basic validation.
-        *
-        * @return stdClass Containing all the values.
-        * @throws APIException Detailing any error.
-        */
-       function get_and_validate_api_headers()
-       {
-               $result = new stdClass;
-               
-               $result->method = get_call_method(); 
-               if (($result->method != "GET") && ($result->method!= "POST")) // Only allow these methods
-                       throw new APIException(elgg_echo('APIException:NotGetOrPost'));
-
-               $result->api_key = $_SERVER['HTTP_X_ELGG_APIKEY'];
-               if ($result->api_key == "")
-                       throw new APIException(elgg_echo('APIException:MissingAPIKey'));
-               
-               $result->hmac = $_SERVER['HTTP_X_ELGG_HMAC'];
-               if ($result->hmac == "")
-                       throw new APIException(elgg_echo('APIException:MissingHmac'));
-               
-               $result->hmac_algo = $_SERVER['HTTP_X_ELGG_HMAC_ALGO'];
-               if ($result->hmac_algo == "")
-                       throw new APIException(elgg_echo('APIException:MissingHmacAlgo'));
-               
-               $result->time = $_SERVER['HTTP_X_ELGG_TIME'];
-               if ($result->time == "") 
-                       throw new APIException(elgg_echo('APIException:MissingTime')); 
-               if (($result->time<(microtime(true)-86400.00)) || ($result->time>(microtime(true)+86400.00))) // Basic timecheck, think about making this smaller if we get loads of users and the cache gets really big.
-                       throw new APIException(elgg_echo('APIException:TemporalDrift'));
-               
-               $result->get_variables = get_parameters_for_method(get_input('method')); //$_SERVER['QUERY_STRING'];
-               if ($result->get_variables == "")
-                       throw new APIException(elgg_echo('APIException:NoQueryString'));
-
-               if ($result->method=="POST")
-               {
-                       $result->posthash = $_SERVER['HTTP_X_ELGG_POSTHASH'];
-                       if ($result->posthash == "") 
-                               throw new APIException(elgg_echo('APIException:MissingPOSTHash'));
-                       
-                       $result->posthash_algo = $_SERVER['HTTP_X_ELGG_POSTHASH_ALGO'];
-                       if ($result->posthash_algo == "") 
-                               throw new APIException(elgg_echo('APIException:MissingPOSTAlgo'));
-                               
-                       $result->content_type = $_SERVER['CONTENT_TYPE'];
-                       if ($result->content_type == "")
-                               throw new APIException(elgg_echo('APIException:MissingContentType'));
-               }
-               
-               return $result;
+
+       $result->hmac = $_SERVER['HTTP_X_ELGG_HMAC'];
+       if ($result->hmac == "") {
+               throw new APIException(elgg_echo('APIException:MissingHmac'));
        }
-       
-       /**
-        * Return a sanitised form of the POST data sent to the script
-        *
-        * @return string
-        */
-       function get_post_data()
-       {
-               global $GLOBALS;
-               
-               $postdata = $GLOBALS['HTTP_RAW_POST_DATA'];
-
-               // Attempt another method to return post data (incase always_populate_raw_post_data is switched off)
-               if (!$postdata)
-               {
-                       $postdata = file_get_contents('php://input');
+
+       $result->hmac_algo = $_SERVER['HTTP_X_ELGG_HMAC_ALGO'];
+       if ($result->hmac_algo == "") {
+               throw new APIException(elgg_echo('APIException:MissingHmacAlgo'));
+       }
+
+       $result->time = $_SERVER['HTTP_X_ELGG_TIME'];
+       if ($result->time == "") {
+               throw new APIException(elgg_echo('APIException:MissingTime'));
+       }
+
+       // Basic timecheck, think about making this smaller if we get loads of users and the cache gets really big.
+       if (($result->time<(microtime(true)-86400.00)) || ($result->time>(microtime(true)+86400.00))) {
+               throw new APIException(elgg_echo('APIException:TemporalDrift'));
+       }
+
+       //$_SERVER['QUERY_STRING'];
+       $result->get_variables = get_parameters_for_method(get_input('method'));
+       if ($result->get_variables == "") {
+               throw new APIException(elgg_echo('APIException:NoQueryString'));
+       }
+
+       if ($result->method=="POST") {
+               $result->posthash = $_SERVER['HTTP_X_ELGG_POSTHASH'];
+               if ($result->posthash == "") {
+                       throw new APIException(elgg_echo('APIException:MissingPOSTHash'));
+               }
+
+               $result->posthash_algo = $_SERVER['HTTP_X_ELGG_POSTHASH_ALGO'];
+               if ($result->posthash_algo == "") {
+                       throw new APIException(elgg_echo('APIException:MissingPOSTAlgo'));
+               }
+
+               $result->content_type = $_SERVER['CONTENT_TYPE'];
+               if ($result->content_type == "") {
+                       throw new APIException(elgg_echo('APIException:MissingContentType'));
                }
-               
-               return $postdata;
        }
-       
-       // PAM functions //////////////////////////////////////////////////////////////////////////
 
-       /**
-        * Function that examines whether an authentication token is present returning true if it is, OR the requested 
-        * method doesn't require one.
-        * 
-        * If a token is present and a validated user id is returned, that user is logged in to the current session.
-        *
-        * @param unknown_type $credentials
-        */
-       function pam_auth_usertoken($credentials = NULL)
-       {
-               global $METHODS, $CONFIG;
-               
-               $method = get_input('method');
-               $token = get_input('auth_token');
-               
-               $validated_userid = validate_user_token($CONFIG->site_id, $token); 
-               
-               if ($validated_userid) {                        
-                       $u = get_entity($validated_userid);
-                       if (!$u) return false; // Could we get the user?
-                       if ( (!$u instanceof ElggUser)) return false; // Not an elgg user
-                       if ($u->isBanned()) return false; // User is banned
-                       if (!login($u)) return false; // Fail if we couldn't log the user in 
-                       
+       return $result;
+}
+
+/**
+ * Return a sanitised form of the POST data sent to the script
+ *
+ * @return string
+ */
+function get_post_data() {
+       global $GLOBALS;
+
+       $postdata = $GLOBALS['HTTP_RAW_POST_DATA'];
+
+       // Attempt another method to return post data (incase always_populate_raw_post_data is switched off)
+       if (!$postdata) {
+               $postdata = file_get_contents('php://input');
+       }
+
+       return $postdata;
+}
+
+// PAM functions //////////////////////////////////////////////////////////////////////////
+
+/**
+ * Function that examines whether an authentication token is present returning true if it is, OR the requested
+ * method doesn't require one.
+ *
+ * If a token is present and a validated user id is returned, that user is logged in to the current session.
+ *
+ * @param unknown_type $credentials
+ */
+function pam_auth_usertoken($credentials = NULL) {
+       global $METHODS, $CONFIG;
+
+       $method = get_input('method');
+       $token = get_input('auth_token');
+
+       $validated_userid = validate_user_token($CONFIG->site_id, $token);
+
+       if ($validated_userid) {
+               $u = get_entity($validated_userid);
+
+               // Could we get the user?
+               if (!$u) {
+                       return false;
+               }
+
+               // Not an elgg user
+               if ( (!$u instanceof ElggUser)) {
+                       return false;
+               }
+
+               // User is banned
+               if ($u->isBanned()) {
+                       return false;
+               }
+
+               // Fail if we couldn't log the user in
+               if (!login($u)) {
+                       return false;
                }
-               
-               if ((!$METHODS[$method]["require_auth_token"]) || ($validated_userid) || (isloggedin())) {
-                       return true;
-               } else
-                       throw new SecurityException(elgg_echo('SecurityException:AuthTokenExpired'), ErrorResult::$RESULT_FAIL_AUTHTOKEN);
-               
-               return false;
        }
-       
-       /**
-        * Test to see whether a given function has been declared as anonymous access (it doesn't require any auth token)
-        *
-        * @param unknown_type $credentials
-        */
-       function pam_auth_anonymous_method($credentials = NULL)
-       {
-               global $METHODS, $CONFIG;
-               
-               $method = get_input('method');
-               
-               if ((isset($METHODS[$method]["anonymous"])) && ($METHODS[$method]["anonymous"]))
-                       return true;
-               
-               return false;
+
+       if ((!$METHODS[$method]["require_auth_token"]) || ($validated_userid) || (isloggedin())) {
+               return true;
+       } else {
+               throw new SecurityException(elgg_echo('SecurityException:AuthTokenExpired'), ErrorResult::$RESULT_FAIL_AUTHTOKEN);
        }
-       
-       /**
-        * See if the user has a valid login sesson.
-        */
-       function pam_auth_session($credentials = NULL)
-       {
-               return isloggedin();
+
+       return false;
+}
+
+/**
+ * Test to see whether a given function has been declared as anonymous access (it doesn't require any auth token)
+ *
+ * @param unknown_type $credentials
+ */
+function pam_auth_anonymous_method($credentials = NULL) {
+       global $METHODS, $CONFIG;
+
+       $method = get_input('method');
+
+       if ((isset($METHODS[$method]["anonymous"])) && ($METHODS[$method]["anonymous"])) {
+               return true;
        }
-       
-       /**
-        * Secure authentication through headers and HMAC.
-        */
-       function pam_auth_hmac($credentials = NULL)
-       {
-               global $CONFIG;
-               
-               $api_header = get_and_validate_api_headers(); // Get api header
-               $api_user = get_api_user($CONFIG->site_id, $api_header->api_key); // Pull API user details
-       
-               if ($api_user)
-               {
-                       // Get the secret key
-                       $secret_key = $api_user->secret;
-               
-                       // Serialise parameters
-                       $encoded_params = array();
-                       foreach ($api_header->get_variables as $k => $v)
-                               $encoded_params[] = urlencode($k).'='.urlencode($v);
-                       $params = implode('&', $encoded_params);                
-                       
-                       // Validate HMAC
-                       $hmac = calculate_hmac($api_header->hmac_algo, 
-                                       $api_header->time, 
-                                       $api_header->api_key, 
-                                       $secret_key, 
-                                       $params, 
-                                       $api_header->method == 'POST' ? $api_header->posthash : "");
-               
-                       if ((strcmp(
-                               $api_header->hmac,
-                               $hmac   
-                       )==0) && ($api_header->hmac) && ($hmac))
-                       {
-                               // Now make sure this is not a replay
-                               if (!cache_hmac_check_replay($hmac)) 
-                               {
-                                       
-                                       // Validate post data
-                                       if ($api_header->method=="POST")
-                                       {
-                                               $postdata = get_post_data();
-                                               $calculated_posthash = calculate_posthash($postdata, $api_header->posthash_algo);
-
-                                               if (strcmp($api_header->posthash, $calculated_posthash)!=0)
-                                                       throw new SecurityException(sprintf(elgg_echo('SecurityException:InvalidPostHash'), $calculated_posthash, $api_header->posthash));
+
+       return false;
+}
+
+/**
+ * See if the user has a valid login sesson.
+ */
+function pam_auth_session($credentials = NULL) {
+       return isloggedin();
+}
+
+/**
+ * Secure authentication through headers and HMAC.
+ */
+function pam_auth_hmac($credentials = NULL) {
+       global $CONFIG;
+
+       // Get api header
+       $api_header = get_and_validate_api_headers();
+
+       // Pull API user details
+       $api_user = get_api_user($CONFIG->site_id, $api_header->api_key);
+
+       if ($api_user) {
+               // Get the secret key
+               $secret_key = $api_user->secret;
+
+               // Serialise parameters
+               $encoded_params = array();
+               foreach ($api_header->get_variables as $k => $v) {
+                       $encoded_params[] = urlencode($k).'='.urlencode($v);
+               }
+               $params = implode('&', $encoded_params);
+
+               // Validate HMAC
+               $hmac = calculate_hmac($api_header->hmac_algo,
+                       $api_header->time,
+                       $api_header->api_key,
+                       $secret_key,
+                       $params,
+                       $api_header->method == 'POST' ? $api_header->posthash : "");
+
+               if ((strcmp($api_header->hmac, $hmac) == 0) && ($api_header->hmac) && ($hmac)) {
+                       // Now make sure this is not a replay
+                       if (!cache_hmac_check_replay($hmac)) {
+
+                               // Validate post data
+                               if ($api_header->method=="POST") {
+                                       $postdata = get_post_data();
+                                       $calculated_posthash = calculate_posthash($postdata, $api_header->posthash_algo);
+
+                                       if (strcmp($api_header->posthash, $calculated_posthash)!=0) {
+                                               throw new SecurityException(sprintf(elgg_echo('SecurityException:InvalidPostHash'), $calculated_posthash, $api_header->posthash));
                                        }
-                                       
-                                       // If we've passed all the checks so far then we can be reasonably certain that the request is authentic, so return this fact to the PAM engine.
-                                       return true;
                                }
-                               else
-                                       throw new SecurityException(elgg_echo('SecurityException:DupePacket'));
+
+                               // If we've passed all the checks so far then we can be reasonably certain that the request is authentic, so return this fact to the PAM engine.
+                               return true;
+                       } else {
+                               throw new SecurityException(elgg_echo('SecurityException:DupePacket'));
                        }
-                       else 
-                               throw new SecurityException("HMAC is invalid.  {$api_header->hmac} != [calc]$hmac = {$api_header->hmac_algo}(**SECRET KEY**, time:{$api_header->time}, apikey:{$api_header->api_key}, get_vars:{$params}" . ($api_header->method=="POST"? "posthash:$api_header->posthash}" : ")"));
+               } else {
+                       throw new SecurityException("HMAC is invalid.  {$api_header->hmac} != [calc]$hmac = {$api_header->hmac_algo}(**SECRET KEY**, time:{$api_header->time}, apikey:{$api_header->api_key}, get_vars:{$params}" . ($api_header->method=="POST"? "posthash:$api_header->posthash}" : ")"));
                }
-               else
-                       throw new SecurityException(elgg_echo('SecurityException:InvalidAPIKey'),ErrorResult::$RESULT_FAIL_APIKEY_INVALID);
-                       
-               return false;
+       } else {
+               throw new SecurityException(elgg_echo('SecurityException:InvalidAPIKey'),ErrorResult::$RESULT_FAIL_APIKEY_INVALID);
        }
-       
-       /**
-        * A bit of a hack. Basically, this combines session and hmac, so that one of them must evaluate to true in order 
-        * to proceed.
-        * 
-        * This ensures that this and auth_token are evaluated separately.
-        *
-        * @param unknown_type $credentials
-        */
-       function pam_auth_session_or_hmac($credentials = NULL)
-       {
-               if (pam_auth_session($credentials))
-                       return true;
-                       
-               if (pam_auth_hmac($credentials))
-                       return true;
-                       
-               return false;
+
+       return false;
+}
+
+/**
+ * A bit of a hack. Basically, this combines session and hmac, so that one of them must evaluate to true in order
+ * to proceed.
+ *
+ * This ensures that this and auth_token are evaluated separately.
+ *
+ * @param unknown_type $credentials
+ */
+function pam_auth_session_or_hmac($credentials = NULL) {
+       if (pam_auth_session($credentials)) {
+               return true;
        }
-       
-       // Client api functions ///////////////////////////////////////////////////////////////////
-       
-       $APICLIENT_LAST_CALL = NULL; 
-       $APICLIENT_LAST_CALL_RAW = ""; 
-       $APICLIENT_LAST_ERROR = NULL; 
-       
-       /**
-        * Utility function to serialise a header array into its text representation.
-        * 
-        * @param $headers array The array of headers "key" => "value"
-        * @return string 
-        */
-       function serialise_api_headers(array $headers)
-       {
-               $headers_str = "";
 
-               foreach ($headers as $k => $v)
-                       $headers_str .= trim($k) . ": " . trim($v) . "\r\n";
+       if (pam_auth_hmac($credentials)) {
+               return true;
+       }
+
+       return false;
+}
+
+// Client api functions ///////////////////////////////////////////////////////////////////
 
-               return trim($headers_str);              
+$APICLIENT_LAST_CALL = NULL;
+$APICLIENT_LAST_CALL_RAW = "";
+$APICLIENT_LAST_ERROR = NULL;
+
+/**
+ * Utility function to serialise a header array into its text representation.
+ *
+ * @param $headers array The array of headers "key" => "value"
+ * @return string
+ */
+function serialise_api_headers(array $headers) {
+       $headers_str = "";
+
+       foreach ($headers as $k => $v) {
+               $headers_str .= trim($k) . ": " . trim($v) . "\r\n";
        }
-       
-       /**
-        * Send a raw API call to an elgg api endpoint.
-        *
-        * @param array $keys The api keys.
-        * @param string $url URL of the endpoint.
-        * @param array $call Associated array of "variable" => "value"
-        * @param string $method GET or POST
-        * @param string $post_data The post data
-        * @param string $content_type The content type
-        * @return stdClass The unserialised response object
-        */
-       function send_api_call(array $keys, $url, array $call, $method = 'GET', $post_data = '', $content_type = 'application/octet-stream')
-       {
-               global $APICLIENT_LAST_CALL, $APICLIENT_LAST_CALL_RAW, $APICLIENT_LAST_ERROR, $CONFIG; 
-               
-               $headers = array();
-               $encoded_params = array();
-               
-               $method = strtoupper($method);
-               switch (strtoupper($method))
-               {
-                       case 'GET' :
-                       case 'POST' :  break;
-                       default: throw new NotImplementedException(sprintf(elgg_echo('NotImplementedException:CallMethodNotImplemented'), $method));
-               }
-               
-               // Time
-               $time = microtime(true); 
-
-               // URL encode all the parameters, ensuring auth_token (if present) is at the end!
-               foreach ($call as $k => $v){
-                       if ($k!='auth_token')
-                               $encoded_params[] = urlencode($k).'='.urlencode($v);
-               }
-               if ($call['auth_token'])
-                       $encoded_params[] = urlencode('auth_token').'='.urlencode($call['auth_token']);
 
-               $params = implode('&', $encoded_params);
-               
-               // Put together the query string
-               $url = $url . "?" . $params;
-               
-               // Construct headers
-               $posthash = "";
-               if ($method == 'POST') $posthash = calculate_posthash($post_data, 'md5');
-               
-               if ((isset($keys['public'])) && (isset($keys['private'])))
-               {
-                       $headers['X-Elgg-apikey'] = $keys['public'];
-                       $headers['X-Elgg-time'] = $time;
-                       $headers['X-Elgg-hmac-algo'] = 'sha1';
-                       $headers['X-Elgg-hmac'] = calculate_hmac('sha1', 
-                                                                               $time,
-                                                                               $keys['public'],
-                                                                               $keys['private'],
-                                                                               $params,
-                                                                               $posthash
-                       );
-               }
-               if ($method == 'POST') 
-               {
-                       $headers['X-Elgg-posthash'] = $posthash;
-                       $headers['X-Elgg-posthash-algo'] = 'md5';
-                       
-                       $headers['Content-type'] = $content_type;
-                       $headers['Content-Length'] = strlen($post_data);
-               }
-               
-               // Opt array
-               $http_opts = array(
-                       'method' => $method,
-                       'header' => serialise_api_headers($headers)
-               );
-               if ($method == 'POST') $http_opts['content'] = $post_data;
-               
-               $opts = array('http' => $http_opts);
-               
-               // Send context
-               $context = stream_context_create($opts);
-               
-               // Send the query and get the result and decode.
-               if ((isset($CONFIG->debug)) && ($CONFIG->debug))
-                       error_log("APICALL: $url");
-               $APICLIENT_LAST_CALL_RAW = file_get_contents($url, false, $context);
-       
-               $APICLIENT_LAST_CALL = unserialize($APICLIENT_LAST_CALL_RAW);
-               
-               if (($APICLIENT_LAST_CALL) && ($APICLIENT_LAST_CALL->status!=0))
-                       $APICLIENT_LAST_ERROR = $APICLIENT_LAST_CALL;
-               
-               return $APICLIENT_LAST_CALL;
+       return trim($headers_str);
+}
+
+/**
+ * Send a raw API call to an elgg api endpoint.
+ *
+ * @param array $keys The api keys.
+ * @param string $url URL of the endpoint.
+ * @param array $call Associated array of "variable" => "value"
+ * @param string $method GET or POST
+ * @param string $post_data The post data
+ * @param string $content_type The content type
+ * @return stdClass The unserialised response object
+ */
+function send_api_call(array $keys, $url, array $call, $method = 'GET', $post_data = '', $content_type = 'application/octet-stream') {
+       global $APICLIENT_LAST_CALL, $APICLIENT_LAST_CALL_RAW, $APICLIENT_LAST_ERROR, $CONFIG;
+
+       $headers = array();
+       $encoded_params = array();
+
+       $method = strtoupper($method);
+       switch (strtoupper($method)) {
+               case 'GET' :
+               case 'POST' :
+                       break;
+               default:
+                       $msg = sprintf(elgg_echo('NotImplementedException:CallMethodNotImplemented'), $method);
+                       throw new NotImplementedException($msg);
        }
 
-       /**
-        * Send a GET call
-        *
-        * @param string $url URL of the endpoint.
-        * @param array $call Associated array of "variable" => "value"
-        * @param array $keys The keys dependant on chosen authentication method
-        * @return stdClass The unserialised response object
-        */
-       function send_api_get_call($url, array $call, array $keys) { return send_api_call($keys, $url, $call); }
-       
-       /**
-        * Send a GET call
-        *
-        * @param string $url URL of the endpoint.
-        * @param array $call Associated array of "variable" => "value"
-        * @param array $keys The keys dependant on chosen authentication method
-        * @param string $post_data The post data
-        * @param string $content_type The content type
-        * @return stdClass The unserialised response object
-        */
-       function send_api_post_call($url, array $call, array $keys, $post_data, $content_type = 'application/octet-stream') { return send_api_call($keys, $url, $call, 'POST', $post_data, $content_type); }
-       
-       /**
-        * Return a key array suitable for the API client using the standard authentication method based on api-keys and secret keys.
-        *
-        * @param string $secret_key Your secret key
-        * @param string $api_key Your api key
-        */
-       function get_standard_api_key_array($secret_key, $api_key) { return array('public' => $api_key, 'private' => $api_key); }
-       
-       // Error handler functions ////////////////////////////////////////////////////////////////
-       
-       /** Define a global array of errors */
-       $ERRORS = array();
-       
-       /**
-        * PHP Error handler function.
-        * This function acts as a wrapper to catch and report PHP error messages.
-        * 
-        * @see http://uk3.php.net/set-error-handler
-        * @param unknown_type $errno
-        * @param unknown_type $errmsg
-        * @param unknown_type $filename
-        * @param unknown_type $linenum
-        * @param unknown_type $vars
-        */
-       function __php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars)
-       {
-               global $ERRORS;
-               
-               $error = date("Y-m-d H:i:s (T)") . ": \"" . $errmsg . "\" in file " . $filename . " (line " . $linenum . ")";
-               
-               switch ($errno) {
-                       case E_USER_ERROR:
-                                       error_log("ERROR: " . $error);
-                                       $ERRORS[] = "ERROR: " .$error;
-                                       
-                                       // Since this is a fatal error, we want to stop any further execution but do so gracefully.
-                                       throw new Exception("ERROR: " . $error); 
-                               break;
-       
-                       case E_WARNING :
-                       case E_USER_WARNING : 
-                                       error_log("WARNING: " . $error);
-                                       $ERRORS[] = "WARNING: " .$error;
-                               break;
-       
-                       default:
-                               error_log("DEBUG: " . $error); 
-                               $ERRORS[] = "DEBUG: " .$error;
+       // Time
+       $time = microtime(true);
+
+       // URL encode all the parameters, ensuring auth_token (if present) is at the end!
+       foreach ($call as $k => $v){
+               if ($k!='auth_token') {
+                       $encoded_params[] = urlencode($k).'='.urlencode($v);
                }
        }
-       
-       /**
-        * PHP Exception handler.
-        * This is a generic exception handler for PHP exceptions. This will catch any 
-        * uncaught exception and return it as an ErrorResult in the requested format.
-        *
-        * @param Exception $exception
-        */
-       function __php_api_exception_handler($exception) {
-               
-               error_log("*** FATAL EXCEPTION (API) *** : " . $exception);
-                       
-               page_draw($exception->getMessage(), elgg_view("api/output",
-                       array('result' => ErrorResult::getInstance(
-                               $exception->getMessage(), 
-                               $exception->getCode() == 0 ? ErrorResult::$RESULT_FAIL : $exception->getCode(), 
-                               $exception)
-                       ))
+
+       if ($call['auth_token']) {
+               $encoded_params[] = urlencode('auth_token').'='.urlencode($call['auth_token']);
+       }
+
+       $params = implode('&', $encoded_params);
+
+       // Put together the query string
+       $url = $url . "?" . $params;
+
+       // Construct headers
+       $posthash = "";
+       if ($method == 'POST') {
+               $posthash = calculate_posthash($post_data, 'md5');
+       }
+
+       if ((isset($keys['public'])) && (isset($keys['private']))) {
+               $headers['X-Elgg-apikey'] = $keys['public'];
+               $headers['X-Elgg-time'] = $time;
+               $headers['X-Elgg-hmac-algo'] = 'sha1';
+               $headers['X-Elgg-hmac'] = calculate_hmac('sha1',
+                       $time,
+                       $keys['public'],
+                       $keys['private'],
+                       $params,
+                       $posthash
                );
        }
-       
-       // Initialisation & pagehandler ///////////////////////////////////////////////////////////
-       
-       /**
-        * Initialise the API subsystem.
-        *
-        */
-       function api_init()
-       {
-               // Register a page handler, so we can have nice URLs
-               register_page_handler('api','api_endpoint_handler');
+       if ($method == 'POST') {
+               $headers['X-Elgg-posthash'] = $posthash;
+               $headers['X-Elgg-posthash-algo'] = 'md5';
+
+               $headers['Content-type'] = $content_type;
+               $headers['Content-Length'] = strlen($post_data);
        }
-       
-       /**
-        * Register a page handler for the various API endpoints.
-        *
-        * @param array $page
-        */
-       function api_endpoint_handler($page) 
-       {
-               global $CONFIG;
-               
-               // Which view
-               if ($page[1])
-               {
-                       elgg_set_viewtype($page[1]);
-                       
+
+       // Opt array
+       $http_opts = array(
+               'method' => $method,
+               'header' => serialise_api_headers($headers)
+       );
+       if ($method == 'POST') {
+               $http_opts['content'] = $post_data;
+       }
+
+       $opts = array('http' => $http_opts);
+
+       // Send context
+       $context = stream_context_create($opts);
+
+       // Send the query and get the result and decode.
+       if ((isset($CONFIG->debug)) && ($CONFIG->debug)) {
+               error_log("APICALL: $url");
+       }
+       $APICLIENT_LAST_CALL_RAW = file_get_contents($url, false, $context);
+
+       $APICLIENT_LAST_CALL = unserialize($APICLIENT_LAST_CALL_RAW);
+
+       if (($APICLIENT_LAST_CALL) && ($APICLIENT_LAST_CALL->status!=0)) {
+               $APICLIENT_LAST_ERROR = $APICLIENT_LAST_CALL;
+       }
+
+       return $APICLIENT_LAST_CALL;
+}
+
+/**
+ * Send a GET call
+ *
+ * @param string $url URL of the endpoint.
+ * @param array $call Associated array of "variable" => "value"
+ * @param array $keys The keys dependant on chosen authentication method
+ * @return stdClass The unserialised response object
+ */
+function send_api_get_call($url, array $call, array $keys) {
+       return send_api_call($keys, $url, $call);
+}
+
+/**
+ * Send a GET call
+ *
+ * @param string $url URL of the endpoint.
+ * @param array $call Associated array of "variable" => "value"
+ * @param array $keys The keys dependant on chosen authentication method
+ * @param string $post_data The post data
+ * @param string $content_type The content type
+ * @return stdClass The unserialised response object
+ */
+function send_api_post_call($url, array $call, array $keys, $post_data, $content_type = 'application/octet-stream') {
+       return send_api_call($keys, $url, $call, 'POST', $post_data, $content_type);
+}
+
+/**
+ * Return a key array suitable for the API client using the standard authentication method based on api-keys and secret keys.
+ *
+ * @param string $secret_key Your secret key
+ * @param string $api_key Your api key
+ */
+function get_standard_api_key_array($secret_key, $api_key) {
+       return array('public' => $api_key, 'private' => $api_key);
+}
+
+// Error handler functions ////////////////////////////////////////////////////////////////
+
+/** Define a global array of errors */
+$ERRORS = array();
+
+/**
+ * PHP Error handler function.
+ * This function acts as a wrapper to catch and report PHP error messages.
+ *
+ * @see http://uk3.php.net/set-error-handler
+ * @param unknown_type $errno
+ * @param unknown_type $errmsg
+ * @param unknown_type $filename
+ * @param unknown_type $linenum
+ * @param unknown_type $vars
+ */
+function __php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars) {
+       global $ERRORS;
+
+       $error = date("Y-m-d H:i:s (T)") . ": \"" . $errmsg . "\" in file " . $filename . " (line " . $linenum . ")";
+
+       switch ($errno) {
+               case E_USER_ERROR:
+                       error_log("ERROR: " . $error);
+                       $ERRORS[] = "ERROR: " .$error;
+
+                       // Since this is a fatal error, we want to stop any further execution but do so gracefully.
+                       throw new Exception("ERROR: " . $error);
+                       break;
+
+               case E_WARNING :
+               case E_USER_WARNING :
+                       error_log("WARNING: " . $error);
+                       $ERRORS[] = "WARNING: " .$error;
+                       break;
+
+               default:
+                       error_log("DEBUG: " . $error);
+                       $ERRORS[] = "DEBUG: " .$error;
+       }
+}
+
+/**
+ * PHP Exception handler.
+ * This is a generic exception handler for PHP exceptions. This will catch any
+ * uncaught exception and return it as an ErrorResult in the requested format.
+ *
+ * @param Exception $exception
+ */
+function __php_api_exception_handler($exception) {
+
+       error_log("*** FATAL EXCEPTION (API) *** : " . $exception);
+
+       page_draw($exception->getMessage(), elgg_view("api/output",
+               array('result' => ErrorResult::getInstance(
+                       $exception->getMessage(),
+                       $exception->getCode() == 0 ? ErrorResult::$RESULT_FAIL : $exception->getCode(),
+                       $exception)
+               ))
+       );
+}
+
+// Initialisation & pagehandler ///////////////////////////////////////////////////////////
+
+/**
+ * Initialise the API subsystem.
+ *
+ */
+function api_init() {
+       // Register a page handler, so we can have nice URLs
+       register_page_handler('api','api_endpoint_handler');
+}
+
+/**
+ * Register a page handler for the various API endpoints.
+ *
+ * @param array $page
+ */
+function api_endpoint_handler($page) {
+       global $CONFIG;
+
+       // Which view
+       if ($page[1]) {
+               elgg_set_viewtype($page[1]);
+       }
+
+       // Which endpoint
+       if ($page[0]) {
+               switch ($page[0]) {
+                       case 'rest' :
+                       default : include($CONFIG->path . "services/api/rest.php");
                }
-               
-               // Which endpoint
-               if ($page[0])
-               {
-                       switch ($page[0])
-                       {
-                               case 'rest' :
-                               default : include($CONFIG->path . "services/api/rest.php");
-                       }
-               } 
        }
-       
-       
-       register_elgg_event_handler('init','system','api_init');
-       
-?>
\ No newline at end of file
+}
+
+register_elgg_event_handler('init','system','api_init');