Skip to content

Commit a2bc35c

Browse files
author
Marius Burkard
committed
- implemented REST handler
1 parent 06e6440 commit a2bc35c

File tree

2 files changed

+193
-0
lines changed

2 files changed

+193
-0
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
<?php
2+
3+
/*
4+
Copyright (c) 2017, Marius Burkard, pixcept KG
5+
All rights reserved.
6+
7+
Redistribution and use in source and binary forms, with or without modification,
8+
are permitted provided that the following conditions are met:
9+
10+
* Redistributions of source code must retain the above copyright notice,
11+
this list of conditions and the following disclaimer.
12+
* Redistributions in binary form must reproduce the above copyright notice,
13+
this list of conditions and the following disclaimer in the documentation
14+
and/or other materials provided with the distribution.
15+
* Neither the name of ISPConfig nor the names of its contributors
16+
may be used to endorse or promote products derived from this software without
17+
specific prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22+
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23+
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26+
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28+
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
30+
*/
31+
32+
33+
class ISPConfigRESTHandler {
34+
private $methods = array();
35+
private $classes = array();
36+
37+
private $api_version = 1;
38+
39+
public function __construct() {
40+
global $app;
41+
42+
// load main remoting file
43+
$app->load('remoting');
44+
45+
// load all remote classes and get their methods
46+
$dir = dirname(realpath(__FILE__)) . '/remote.d';
47+
$d = opendir($dir);
48+
while($f = readdir($d)) {
49+
if($f == '.' || $f == '..') continue;
50+
if(!is_file($dir . '/' . $f) || substr($f, strrpos($f, '.')) != '.php') continue;
51+
52+
$name = substr($f, 0, strpos($f, '.'));
53+
54+
include $dir . '/' . $f;
55+
$class_name = 'remoting_' . $name;
56+
if(class_exists($class_name, false)) {
57+
$this->classes[$class_name] = new $class_name();
58+
foreach(get_class_methods($this->classes[$class_name]) as $method) {
59+
$this->methods[$method] = $class_name;
60+
}
61+
}
62+
}
63+
closedir($d);
64+
65+
// add main methods
66+
$this->methods['login'] = 'remoting';
67+
$this->methods['logout'] = 'remoting';
68+
$this->methods['get_function_list'] = 'remoting';
69+
70+
// create main class
71+
$this->classes['remoting'] = new remoting(array_keys($this->methods));
72+
}
73+
74+
private function _return_error($code, $codename, $message) {
75+
header('HTTP/1.1 ' . $code . ' ' . $codename);
76+
print '<!DOCTYPE html>
77+
<html lang="en">
78+
<head>
79+
<title>
80+
ERROR ' . $code . ': ' . $codename . '
81+
</title>
82+
</head>
83+
<body>
84+
<h1>' . $code . ': ' . $codename . '</h1>
85+
<p>' . htmlentities($message, ENT_QUOTES, 'UTF-8') . '</p>
86+
</body>
87+
</html>';
88+
exit;
89+
}
90+
91+
private function _return_json($code, $data = '') {
92+
93+
header('HTTP/1.1 ' . $code . ' OK');
94+
if(!is_array($data) && !is_object($data)) {
95+
header('Content-Type: text/plain; charset="utf-8"');
96+
print $data;
97+
} else {
98+
header('Content-Type: application/json; charset="utf-8"');
99+
print json_encode($data);
100+
}
101+
exit;
102+
}
103+
104+
public function run() {
105+
// check called http method
106+
107+
$method = '';
108+
$return_code = 0;
109+
$http_method = (isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : '');
110+
if($http_method == 'POST') {
111+
$method = 'add';
112+
$return_code = 201;
113+
} elseif($http_method == 'GET') {
114+
$method = 'get';
115+
$return_code = 200;
116+
} elseif($http_method == 'PUT') {
117+
$method = 'update';
118+
$return_code = 204;
119+
} elseif($http_method == 'DELETE') {
120+
$method = 'delete';
121+
$return_code = 204;
122+
} else {
123+
$this->_return_error(400, 'INVALID REQUEST', 'Invalid request');
124+
}
125+
126+
$params = array();
127+
if($http_method == 'POST' || $http_method == 'PUT') {
128+
$raw = file_get_contents("php://input");
129+
$json = json_decode($raw, true);
130+
if(!is_array($json)) $this->_return_error(400, 'INVALID REQUEST', 'The JSON data sent to the api is invalid');
131+
}
132+
133+
// get URL
134+
$url_path = (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '');
135+
if(!preg_match('^\/?remote\/api\/v(\d+)\/(\w+)(?:\/(\d+)|\/)?(?:\?.*)$/', $url_path, $parts)) {
136+
$this->_return_error(400, 'INVALID REQUEST', 'The url you called is not a valid REST url.');
137+
}
138+
$this->api_version = $parts[1];
139+
if($this->api_version != 1) {
140+
$this->_return_error(400, 'INVALID REQUEST', 'Invalid API version called.');
141+
}
142+
$section = $parts[2];
143+
$primary_id = (isset($parts[3]) ? $parts[3] : 0);
144+
$qry = (isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '');
145+
$get = array();
146+
parse_str($qry, $get);
147+
148+
$method = $section . '_' . $method;
149+
150+
151+
if(array_key_exists($method, $this->methods) == false) {
152+
$this->_return_error(400, 'INVALID REQUEST', 'Method ' . $method . ' does not exist');
153+
}
154+
155+
$class_name = $this->methods[$method];
156+
if(array_key_exists($class_name, $this->classes) == false) {
157+
$this->_return_error(400, 'INVALID REQUEST', 'Class ' . $class_name . ' does not exist');
158+
}
159+
160+
if(method_exists($this->classes[$class_name], $method) == false) {
161+
$this->_return_error(400, 'INVALID REQUEST', 'Method ' . $method . ' does not exist in the class it was expected (' . $class_name . ')');
162+
}
163+
164+
$methObj = new ReflectionMethod($this->classes[$class_name], $method);
165+
foreach($methObj->getParameters() as $param) {
166+
$pname = $param->name;
167+
if($pname == 'session_id') $params[] = (isset($get['session_id']) ? $get['session_id'] : '');
168+
elseif($pname == 'primary_id' && $primary_id) $params[] = $primary_id;
169+
elseif(isset($json[$pname])) $params[] = $json[$pname];
170+
else $params[] = null;
171+
}
172+
173+
try {
174+
$this->_return_json($return_code, call_user_func_array(array($this->classes[$class_name], $method), $params));
175+
} catch(SoapFault $e) {
176+
$this->_return_error(500, 'REQUEST ERROR', $e->getMessage());
177+
}
178+
}
179+
180+
}
181+
182+
?>

interface/web/remote/rest.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
require_once '../../lib/config.inc.php';
4+
$conf['start_session'] = false;
5+
require_once '../../lib/app.inc.php';
6+
7+
$app->load('rest_handler');
8+
$rest_handler = new ISPConfigRESTHandler();
9+
$rest_handler->run();
10+
11+
?>

0 commit comments

Comments
 (0)