1 <?php
2 
3 class 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  */
14 abstract 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 }