1<?php
2
3class DokuAJAXColidingParamException extends Exception {
4	private $param = '';
5	public function param() { return $this->param; }
6	public function __construct($param) { $this->param = $param; }
7}
8
9
10/**
11 * The base class for all AJAX handlers
12 * @author Junling Ma <junlingm@gmail.com>
13 */
14abstract class Doku_AJAX {
15	private $required = array();
16	private $optional = array();
17
18	/**
19	 * returns the name of the component
20	 * Note different types of components can have identical names,
21	 * but components of the same type cannot.
22	 * @return string the name of the component.
23	 */
24	abstract public function name();
25
26	/**
27	 * return an error mesage to the client
28	 * @param numeric $code the error code;
29	 * @param string msg (optional) the error message
30	 */
31	protected function error($code, $msg='') {
32		if (function_exists('http_response_code') && !$msg) {
33			http_response_code($code);
34			exit;
35		}
36		if (!$msg) {
37	        switch ($code) {
38	            case 200: $msg = 'OK'; break;
39	            case 400: $msg = 'Bad request'; break;
40	            case 401: $msg = 'Unauthorized'; break;
41	            case 403: $msg = 'Forbidden'; break;
42	            case 404: $msg = 'Not Found'; break;
43	            case 416: $msg = 'Requested Range Not Satisfiable'; break;
44	            default: $code = 200; $msg = "OK"; break;
45        	}
46        }
47        header("HTTP/1.0 $code $msg");
48        exit;
49	}
50
51	/**
52	 * send the result back to the client and exit
53	 * @param mixed $result the result to send back to the client
54	 */
55	protected function respond($result) {
56		header('Content-Type: application/json');
57		$json = new JSON();
58		echo $json->encode($result);
59		exit;
60	}
61
62	/**
63	 * check whether the caller has enough auth level
64	 * @param array params the array of aprameters for this call,
65	 * with the keys as the parameter names and the values as the parameter values
66	 * @return bool whether the call is authorized
67	 */
68	abstract protected function auth($params);
69
70	/**
71	 * call the ajax function
72	 * @param array $params the array of parameters passed in,
73	 * with the keys as parameter names and the value as their values;
74	 * @return mixed the value to be returned to the client;
75	 */
76	abstract protected function call($params);
77
78	/**
79	 * the default constructor
80	 * @param array $required (optional) array of required parameters with
81	 * the variable name as the key and its type as the value.
82	 * @param array $optional (optional) array of optional parameters with
83	 * the variable name as the key and its type as the value.
84	 */
85	public function __construct($required=array(), $optional=array()) {
86		$this->required = $required;
87		$this->optional = $optional;
88	}
89
90	/**
91	 * handles the call
92	 */
93	public function handle() {
94		$params = array();
95		$types = array();
96		foreach ($this->required as $var => $type) {
97			$params[$var] = TRUE;
98			$types[$var] = $type;
99		}
100		foreach ($this->optional as $var => $type) {
101			if (isset($vars[$var])) throw new DokuAJAXColidingParamException($var);
102			$params[$var] = FALSE;
103			$types[$var] = $type;
104		}
105		$vars = array();
106
107		global $INPUT;
108		// check security token
109		$sectok = $INPUT->str('sectok');
110		if (!checkSecurityToken($sectok))
111			$this->error(403);
112		// check the presence of parameters
113		$json = new JSON;
114		foreach ($params as $var => $req) {
115			if ($INPUT->has($var)) {
116				$value = $INPUT->param($var);
117				if (is_array($value)) $type = 'array';
118				else if (is_string($value)) {
119					if ($types[$var] == 'array') {
120						$value = $json->decode($value);
121						$type = is_array($value) ? 'array' : NULL;
122					} else $type = 'string';
123				}
124				else if (is_integer($value)) $type = 'int';
125				else if (is_float($value)) $type = 'float';
126				else if (is_bool($value)) $type = 'bool';
127				else $type = NULL;
128				if ($type !== $types[$var]) {
129					$this->error(400, $var . ' is expected to be ' . $types[$var] . ' : '.$type);
130				}
131				$vars[$var] = $value;
132			} else if ($req) $this->error(400);
133		}
134
135		if (!$this->auth($vars)) $this->error(403);
136		$this->respond($this->call($vars));
137	}
138}