]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/protected/Class/API.php
471e77f6c15c8cac5ff1401c604fe5bae42f1173
[bacula/bacula] / gui / baculum / protected / Class / API.php
1 <?php
2 /*
3  * Bacula(R) - The Network Backup Solution
4  * Baculum   - Bacula web interface
5  *
6  * Copyright (C) 2013-2016 Kern Sibbald
7  *
8  * The main author of Baculum is Marcin Haba.
9  * The original author of Bacula is Kern Sibbald, with contributions
10  * from many others, a complete list can be found in the file AUTHORS.
11  *
12  * You may use this file and others of this release according to the
13  * license defined in the LICENSE file, which includes the Affero General
14  * Public License, v3.0 ("AGPLv3") and some additional permissions and
15  * terms pursuant to its AGPLv3 Section 7.
16  *
17  * This notice must be preserved when any source code is
18  * conveyed and/or propagated.
19  *
20  * Bacula(R) is a registered trademark of Kern Sibbald.
21  */
22
23 Prado::using('Application.Class.Errors');
24
25 /**
26  * Internal API client module.
27  *
28  * @author Marcin Haba
29  */
30 class API extends TModule {
31
32         /**
33          * API version (used in HTTP header)
34          */
35         const API_VERSION = '0.1';
36
37         /**
38          * Store configuration data from settings file
39          *
40          * @access protected
41          */
42         protected $app_cfg;
43
44         /**
45          * These errors are allowed in API response and they do not cause
46          * disturb application working (no direction to error page)
47          *
48          * @access private
49          */
50         private $allowed_errors = array(
51                 GenericError::ERROR_NO_ERRORS,
52                 BconsoleError::ERROR_INVALID_COMMAND,
53                 PoolError::ERROR_NO_VOLUMES_IN_POOL_TO_UPDATE
54         );
55
56         /**
57          * Get connection request handler.
58          * For data requests is used cURL interface.
59          *
60          * @access public
61          * @return resource connection handler on success, false on errors
62          */
63         public function getConnection() {
64                 $ch = curl_init();
65                 $userpwd = sprintf('%s:%s', $this->app_cfg['baculum']['login'], $this->app_cfg['baculum']['password']);
66                 curl_setopt($ch, CURLOPT_USERPWD, $userpwd);
67                 curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
68                 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
69                 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
70                 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
71                 curl_setopt($ch, CURLOPT_COOKIE, 'PHPSESSID=' . md5(session_id()));
72                 return $ch;
73         }
74
75         /**
76          * Get API specific headers used in HTTP requests.
77          *
78          * @access private
79          * @return API specific headers
80          */
81         private function getAPIHeaders() {
82                 $headers = array(
83                         'X-Baculum-API: ' . self::API_VERSION,
84                         'X-Baculum-User: ' . $this->Application->User->getName(),
85                         'X-Baculum-Pwd: ' . $this->Application->User->getPwd(),
86                         'Accept: application/json'
87                 );
88                 return $headers;
89         }
90
91         /**
92          * Initializes API module (framework module constructor)
93          *
94          * @access public
95          * @param TXmlElement $config API module configuration
96          */
97         public function init($config) {
98                 $this->initSessionCache();
99                 $this->app_cfg = $this->Application->getModule('configuration')->getApplicationConfig();
100         }
101
102         /**
103          * Get URL to use by internal API client's request.
104          *
105          * @access private
106          * @return string URL to internal API server
107          */
108         private function getURL() {
109                 $protocol = !empty($_SERVER['HTTPS']) ? 'https' : 'http';
110                 $host = $_SERVER['SERVER_NAME'];
111                 $port = $_SERVER['SERVER_PORT'];
112
113                 // support for document root subdirectory
114                 $urlPrefix = $this->Application->getModule('friendly-url')->getUrlPrefix();
115
116                 $url = sprintf('%s://%s:%d%s/', $protocol, $host, $port, $urlPrefix);
117                 return $url;
118         }
119
120         /**
121          * Set URL parameters and prepare URL to request send.
122          *
123          * @access private
124          * @param string &$url reference to URL string variable
125          */
126         private function setUrlParams(&$url) {
127                 $url .= (preg_match('/\?/', $url) === 1 ? '&' : '?');
128                 $url .= 'director=';
129                 if (array_key_exists('director', $_SESSION)) {
130                         $url .= $_SESSION['director'];
131                 }
132
133                 $this->Application->getModule('logging')->log(
134                         __FUNCTION__,
135                         PHP_EOL . PHP_EOL . 'EXECUTE URL ==> ' . $url . ' <==' . PHP_EOL . PHP_EOL,
136                         Logging::CATEGORY_APPLICATION,
137                         __FILE__,
138                         __LINE__
139                 );
140         }
141
142         /**
143          * Internal API GET request.
144          *
145          * @access public
146          * @param array $params GET params to send in request
147          * @param bool $use_cache if true then try to use session cache, if false then always use fresh data
148          * @return object stdClass with request result as two properties: 'output' and 'error'
149          */
150         public function get(array $params, $use_cache = false) {
151                 $cached = null;
152                 $ret = null;
153                 if ($use_cache === true) {
154                         $cached = $this->getSessionCache($params);
155                 }
156                 if (!is_null($cached)) {
157                         $ret = $cached;
158                 } else {
159                         $url = $this->getURL() . implode('/', $params);
160                         $this->setUrlParams($url);
161                         $ch = $this->getConnection();
162                         curl_setopt($ch, CURLOPT_URL, $url);
163                         curl_setopt($ch, CURLOPT_HTTPHEADER, $this->getAPIHeaders());
164                         $result = curl_exec($ch);
165                         curl_close($ch);
166                         $ret = $this->preParseOutput($result);
167                         if ($use_cache === true && $ret->error === 0) {
168                                 $this->setSessionCache($params, $ret);
169                         }
170                 }
171                 return $ret;
172         }
173
174         /**
175          * Internal API SET request.
176          *
177          * @access public
178          * @param array $params GET params to send in request
179          * @param array $options POST params to send in request
180          * @return object stdClass with request result as two properties: 'output' and 'error'
181          */
182         public function set(array $params, array $options) {
183                 $url = $this->getURL() . implode('/', $params);
184                 $this->setUrlParams($url);
185                 $data = http_build_query(array('update' => $options));
186                 $ch = $this->getConnection();
187                 curl_setopt($ch, CURLOPT_URL, $url);
188                 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
189                 curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge(
190                         $this->getAPIHeaders(),
191                         array('X-HTTP-Method-Override: PUT', 'Content-Length: ' . strlen($data), 'Expect:')
192                 ));
193                 curl_setopt($ch, CURLOPT_POST, true);
194                 curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
195                 $result = curl_exec($ch);
196                 curl_close($ch);
197                 return $this->preParseOutput($result);
198         }
199
200         /**
201          * Internal API CREATE request.
202          *
203          * @access public
204          * @param array $params GET params to send in request
205          * @param array $options POST params to send in request
206          * @return object stdClass with request result as two properties: 'output' and 'error'
207          */
208         public function create(array $params, array $options) {
209                 $url = $this->getURL() . implode('/', $params);
210                 $this->setUrlParams($url);
211                 $data = http_build_query(array('create' => $options));
212                 $ch = $this->getConnection();
213                 curl_setopt($ch, CURLOPT_URL, $url);
214                 curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge($this->getAPIHeaders(), array('Expect:')));
215                 curl_setopt($ch, CURLOPT_POST, true);
216                 curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
217                 $result = curl_exec($ch);
218                 curl_close($ch);
219                 return $this->preParseOutput($result);
220         }
221
222         /**
223          * Internal API REMOVE request.
224          *
225          * @access public
226          * @param array $params GET params to send in request
227          * @return object stdClass with request result as two properties: 'output' and 'error'
228          */
229         public function remove(array $params) {
230                 $url = $this->getURL() . implode('/', $params);
231                 $this->setUrlParams($url);
232                 $ch = $this->getConnection();
233                 curl_setopt($ch, CURLOPT_URL, $url);
234                 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'DELETE');
235                 curl_setopt($ch, CURLOPT_HTTPHEADER, array_merge($this->getAPIHeaders(), array('X-HTTP-Method-Override: DELETE')));
236                 $result = curl_exec($ch);
237                 curl_close($ch);
238                 return $this->preParseOutput($result);
239         }
240
241         /**
242          * Initially parse and prepare every Internal API response.
243          * If a error occurs then redirect to appropriate error page.
244          *
245          * @access private
246          * @param string $result response output as JSON string (not object yet)
247          * @return object stdClass parsed response with two top level properties 'output' and 'error'
248          */
249         private function preParseOutput($result) {
250                 // first write log with that what comes
251                 $this->Application->getModule('logging')->log(
252                         __FUNCTION__,
253                         $result,
254                         Logging::CATEGORY_APPLICATION,
255                         __FILE__,
256                         __LINE__
257                 );
258
259                 // decode JSON to object
260                 $resource = json_decode($result);
261
262                 $error = null;
263
264                 if(is_object($resource) && property_exists($resource, 'error')) {
265                         if(!in_array($resource->error, $this->allowed_errors)) {
266                                 $error = $resource->error;
267                         }
268                 } else {
269                         $error = AuthorizationError::ERROR_AUTHORIZATION_TO_WEBGUI_PROBLEM;
270                 }
271
272                 $this->Application->getModule('logging')->log(
273                         __FUNCTION__,
274                         $resource,
275                         Logging::CATEGORY_APPLICATION,
276                         __FILE__,
277                         __LINE__
278                 );
279
280                 // if other than allowed errors exist then show error page (redirect)
281                 if(!is_null($error)) {
282                         // Note! Redirection to error page takes place here.
283                         $this->Response->redirect(
284                                 $this->Service->constructUrl(
285                                         'BaculumError',
286                                         array('error' => $error),
287                                         false
288                                 )
289                         );
290                 }
291
292                 return $resource;
293         }
294
295         /**
296          * Initialize session cache.
297          *
298          * @access public
299          * @param bool $force if true then cache is force initialized
300          * @return none
301          */
302         public function initSessionCache($force = false) {
303                 if (!isset($_SESSION) || !array_key_exists('cache', $_SESSION) || !is_array($_SESSION['cache']) || $force === true) {
304                         $_SESSION['cache'] = array();
305                 }
306         }
307
308         /**
309          * Get session cache value by params.
310          *
311          * @access private
312          * @param array $params command parameters as numeric array
313          * @return mixed if cache exists then returned is cached data, otherwise null
314          */
315         private function getSessionCache(array $params) {
316                 $cached = null;
317                 $key = $this->getSessionKey($params);
318                 if ($this->isSessionValue($key)) {
319                         $cached = $_SESSION['cache'][$key];
320                 }
321                 return $cached;
322         }
323
324         /**
325          * Save data to session cache.
326          *
327          * @access private
328          * @param array $params command parameters as numeric array
329          * @param mixed $value value to save in cache
330          * @return none
331          */
332         private function setSessionCache(array $params, $value) {
333                 $key = $this->getSessionKey($params);
334                 $_SESSION['cache'][$key] = $value;
335         }
336
337         /**
338          * Get session key by command parameters.
339          *
340          * @access private
341          * @param array $params command parameters as numeric array
342          * @return string session key for given command
343          */
344         private function getSessionKey(array $params) {
345                 $key = implode(';', $params);
346                 $key = base64_encode($key);
347                 return $key;
348         }
349
350         /**
351          * Check if session key exists in session cache.
352          *
353          * @access private
354          * @param string $key session key
355          * @return bool true if session key exists, otherwise false
356          */
357         private function isSessionValue($key) {
358                 $is_value = array_key_exists($key, $_SESSION['cache']);
359                 return $is_value;
360         }
361 }
362 ?>