<?php

/**
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package fusionLib
 * @copyright Copyright (c) 2013 fusionLib. All rights reserved.
 * @link http://fusionlib.com
 */

/**
 * flApiService Class
 *
 * Base class for API services.
 *
 * @package fusionLib
 */
class flApiService {
	/**
	 * The database object to use.
	 *
	 * @var flDb
	 */
	protected $db;

	/**
	 * The API server object.
	 *
	 * @var flApiServer
	 */
	protected $apiServer;

	/**
	 * The request object.
	 *
	 * @var flRequest
	 */
	protected $request;

	/**
	 * The response object.
	 *
	 * @var flResponse
	 */
	protected $response;

	/**
	 * Construct the service object.
	 *
	 * @param flDb $db The database object.
	 * @param flApiServer $apiServer The API server object.
	 * @param flRequest $request The request object.
	 * @param flResponse $response The response object.
	 */
	function __construct($db, $apiServer, $request, $response) {
		$this->db = $db;
		$this->apiServer = $apiServer;
		$this->request = $request;
		$this->response = $response;
	}
}

/**
 * flApiServer Class
 *
 * Class to handle RESTful API requests.
 *
 * @package fusionLib
 */
class flApiServer {

	/**
	 * The database object.
	 *
	 * @var flDb
	 */
	protected $db;

	/**
	 * Array of class names to use for specific services.
	 *
	 * @var array
	 */
	protected $serviceClass = array();

	/**
	 * Permissions granted to the API key.
	 *
	 * @var array
	 */
	protected $perms = array();

	/**
	 * Create the object.
	 *
	 * @param flDb $db The database object to use.
	 */
	function __construct($db) {
		$this->db = $db;
	}

	/**
	 * Register the class to use for a service with the API server.
	 *
	 * @param string $service The name of the service.
	 * @param string $class The name of the class providing the service.
	 */
	function registerServiceClass($service, $class) {
		$this->serviceClass[$service] = $class;
	}

	/**
	 * Process the request.
	 *
	 * @param flRequest $request The request object.
	 * @param flResponse $response The response object.
	 * @throws flExceptionBadRequest
	 * @throws flExceptionForbidden
	 */
	function processRequest($request, $response) {

		// Check for the Authorization header
		if(!isset($_SERVER['PHP_AUTH_USER'])) {
			$response->setStatusCode(401)
				->setHeader('WWW-Authenticate', 'Basic realm="clearFusionCMS API')
				->sendResponse();
			return;
		}

		// Verify that the username is a valid API key
		if(!$this->db->fetchOne('SELECT COUNT(*) FROM fl_apikeys WHERE apikey=?', $_SERVER['PHP_AUTH_USER']))
			throw new flExceptionForbidden();

		// Attempt to create the class
		$ver =  $request->getParam('apiver');
		$service = $request->getParam('service');
		$cls = (isset($this->serviceClass[$service]) ? $this->serviceClass[$service] . 'V' : $service . 'ApiV') . $ver;
		try {
			// Load the permissions granted to the key
			$this->perms = array('__flLoggedInUser__' => '__flLoggedInUser__');
			$result = $this->db->query('SELECT DISTINCT permission FROM fl_apikeyroles LEFT JOIN fl_roleperms USING(roleid) WHERE apikey=?', $_SERVER['PHP_AUTH_USER']);
			while($row = $result->fetch())
				$this->perms[$row['permission']] = $row['permission'];

			// Create the service
			$obj = new $cls($this->db, $this, $request, $response);
			$method = $request->getParam('method') . ucfirst(strtolower($_SERVER['REQUEST_METHOD']));
			if(!method_exists($obj, $method))
				throw new flExceptionBadRequest();
			else {
				// Setup common response parameters
				$response->noCache()->setContentType('application/json');

				// Invoke the method
				$obj->$method();
			}
		} catch(flExceptionLoadClass $e) {
			throw new flExceptionBadRequest();
		}
	}

	/**
	 * Test if the API key has been granted one or more permission.
	 *
	 * @param string|array $perm The permission(s) to test for.
	 * @return bool true if the permission(s) have been granted; else false.
	 */
	function hasPermission($perm) {

		// If parameters given rather than array then get args
		if(!is_array($perm))
			$perm = func_get_args();

		if(!count($perm))
			return true;

		foreach($perm as $p) {
			if(!isset($this->perms[$p]))
				return false;
		}

		return true;
	}

	/**
	 * Get the permissions assigned to the API key.
	 *
	 * @return array Array of permissions assigned to the API Key.
	 */
	function getPermissions() {
		return $this->perms;
	}
}
