r5023 ability to override and extend AJAX dispatchers in plugins.
[racktables] / wwwroot / index.php
1 <?php
2
3 # This file is a part of RackTables, a datacenter and server room management
4 # framework. See accompanying file "COPYING" for the full copyright and
5 # licensing information.
6
7 ob_start();
8 require_once 'inc/pre-init.php';
9 try {
10 // Code block below is a module request dispatcher. Turning it into a
11 // function will break things because of the way require() works.
12 switch (TRUE)
13 {
14 case ! array_key_exists ('module', $_REQUEST):
15 case 'interface' == $_REQUEST['module']:
16 require_once 'inc/interface.php';
17 // init.php has to be included after interface.php, otherwise the bits
18 // set by local.php get lost
19 require_once 'inc/init.php';
20 prepareNavigation();
21 // Security context is built on the requested page/tab/bypass data,
22 // do not override.
23 fixContext();
24 redirectIfNecessary();
25 assertPermission();
26 header ('Content-Type: text/html; charset=UTF-8');
27 // call the main handler - page or tab handler.
28 if (isset ($tabhandler[$pageno][$tabno]))
29 {
30 if (! function_exists ($tabhandler[$pageno][$tabno]))
31 throw new RackTablesError ("Missing handler function for node '${pageno}-${tabno}'", RackTablesError::INTERNAL);
32 call_user_func ($tabhandler[$pageno][$tabno], getBypassValue());
33 }
34 elseif (isset ($page[$pageno]['handler']))
35 {
36 if (! function_exists ($page[$pageno]['handler']))
37 throw new RackTablesError ("Missing handler function for node '${pageno}'", RackTablesError::INTERNAL);
38 $page[$pageno]['handler'] ($tabno);
39 }
40 else
41 throw new RackTablesError ("Failed to find handler for page '${pageno}', tab '${tabno}'", RackTablesError::INTERNAL);
42 // Embed the current text in OB into interface layout (the latter also
43 // empties color message buffer).
44 $contents = ob_get_contents();
45 ob_clean();
46 renderInterfaceHTML ($pageno, $tabno, $contents);
47 break;
48 case 'chrome' == $_REQUEST['module']:
49 require_once 'inc/init.php';
50 require_once 'inc/solutions.php';
51 genericAssertion ('uri', 'string');
52 proxyStaticURI ($_REQUEST['uri']);
53 break;
54 case 'download' == $_REQUEST['module']:
55 require_once 'inc/init.php';
56 $pageno = 'file';
57 $tabno = 'download';
58 fixContext();
59 assertPermission();
60 $file = getFile (getBypassValue());
61 header("Content-Type: {$file['type']}");
62 header("Content-Length: {$file['size']}");
63 if (! array_key_exists ('asattach', $_REQUEST) or $_REQUEST['asattach'] != 'no')
64 header("Content-Disposition: attachment; filename={$file['name']}");
65 echo $file['contents'];
66 break;
67 case 'image' == $_REQUEST['module']:
68 # The difference between "image" and "download" ways to serve the same
69 # picture file is that the former is used in <IMG SRC=...> construct,
70 # and the latter is accessed as a standalone URL and can reply with any
71 # Content-type. Hence "image" module indicates failures with internally
72 # built images, and "download" can return a full-fledged "permission
73 # denied" or "exception" HTML page instead of the file requested.
74 require_once 'inc/init.php'; // for authentication check
75 require_once 'inc/solutions.php';
76 try
77 {
78 dispatchImageRequest();
79 }
80 catch (RTPermissionDenied $e)
81 {
82 ob_clean();
83 renderAccessDeniedImage();
84 }
85 catch (Exception $e)
86 {
87 ob_clean();
88 renderErrorImage();
89 }
90 break;
91 case 'svg' == $_REQUEST['module']:
92 require_once 'inc/init.php';
93 require_once 'inc/solutions.php';
94 header ('Content-Type: image/svg+xml');
95 echo '<?xml version="1.0" encoding="iso-8859-1" standalone="no"?>' . "\n";
96 echo '<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">' . "\n";
97 try
98 {
99 genericAssertion ('view', 'string');
100 if (! array_key_exists ($_REQUEST['view'], $svghandler))
101 throw new InvalidRequestArgException ('view', $_REQUEST['view'], 'undefined view');
102 if (! function_exists ($svghandler[$_REQUEST['view']]))
103 throw new RackTablesError ('missing handler function', RackTablesError::INTERNAL);
104 call_user_func ($svghandler[$_REQUEST['view']]);
105 }
106 catch (RTPermissionDenied $e)
107 {
108 ob_clean();
109 printSVGMessageBar ('permission denied', array ('fill' => 'white'), array ('fill' => 'black', 'stroke' => 'gray'));
110 }
111 catch (InvalidRequestArgException $e)
112 {
113 ob_clean();
114 printSVGMessageBar ('malformed HTTP request', array(), array ('fill' => 'yellow', 'stroke' => 'black'));
115 }
116 catch (EntityNotFoundException $e)
117 {
118 ob_clean();
119 printSVGMessageBar ('no such record', array(), array ('fill' => 'yellow', 'stroke' => 'black'));
120 }
121 catch (RackTablesError $e)
122 {
123 ob_clean();
124 printSVGMessageBar ('RT error: ' . $e->getMessage(), array(), array ('fill' => 'red', 'stroke' => 'black'));
125 }
126 catch (Exception $e)
127 {
128 ob_clean();
129 printSVGMessageBar ('unknown error', array(), array ('fill' => 'red', 'stroke' => 'black'));
130 }
131 break;
132 case 'progressbar' == $_REQUEST['module']:
133 # Unlike images (and like static content), progress bars are processed
134 # without a permission check, but only for authenticated users.
135 require_once 'inc/init.php';
136 require_once 'inc/solutions.php';
137 try
138 {
139 genericAssertion ('done', 'uint0');
140 // 'progressbar's never change, make browser cache the result
141 if (checkCachedResponse (0, CACHE_DURATION))
142 break;
143 renderProgressBarImage ($_REQUEST['done']);
144 }
145 catch (Exception $e)
146 {
147 ob_clean();
148 renderProgressBarError();
149 }
150 break;
151 case 'progressbar4' == $_REQUEST['module']:
152 # Unlike images (and like static content), progress bars are processed
153 # without a permission check, but only for authenticated users.
154 require_once 'inc/init.php';
155 require_once 'inc/solutions.php';
156 try
157 {
158 renderProgressBar4Image ($_REQUEST['px1'], $_REQUEST['px2'], $_REQUEST['px3']);
159 }
160 catch (Exception $e)
161 {
162 ob_clean();
163 renderProgressBarError();
164 }
165 break;
166 case 'ajax' == $_REQUEST['module']:
167 require_once 'inc/init.php';
168 require_once 'inc/ajax-interface.php';
169 require_once 'inc/solutions.php';
170 try
171 {
172 genericAssertion ('ac', 'string');
173 $ac = $_REQUEST['ac'];
174 if (isset ($ajaxhandler[$ac]))
175 $ajaxhandler[$ac]();
176 else
177 {
178 ob_clean();
179 echo "NAK\nMalformed request";
180 }
181 }
182 catch (RTPermissionDenied $e)
183 {
184 ob_clean();
185 # FIXME: the remote client could be expecting JSON data instead
186 echo "NAK\nPermission denied";
187 }
188 catch (Exception $e)
189 {
190 ob_clean();
191 echo "NAK\nRuntime exception: ". $e->getMessage();
192 }
193 break;
194 case 'redirect' == $_REQUEST['module']:
195 // Include init after ophandlers/snmp, not before, so local.php can redefine things.
196 require_once 'inc/ophandlers.php';
197 // snmp.php is an exception, it is treated by a special hack
198 if (isset ($_REQUEST['op']) and $_REQUEST['op'] == 'querySNMPData')
199 require_once 'inc/snmp.php';
200 require_once 'inc/init.php';
201 try
202 {
203 genericAssertion ('op', 'string');
204 $op = $_REQUEST['op'];
205 prepareNavigation();
206 $location = buildRedirectURL();
207 // FIXME: find a better way to handle this error
208 if ($op == 'addFile' && !isset($_FILES['file']['error']))
209 throw new RackTablesError ('File upload error, check upload_max_filesize in php.ini', RackTablesError::MISCONFIGURED);
210 fixContext();
211 if
212 (
213 !isset ($ophandler[$pageno][$tabno][$op]) or
214 !function_exists ($ophandler[$pageno][$tabno][$op])
215 )
216 throw new RackTablesError ("Invalid navigation data for '${pageno}-${tabno}-${op}'", RackTablesError::INTERNAL);
217 // We have a chance to handle an error before starting HTTP header.
218 if (!isset ($delayauth["${pageno}-${tabno}-${op}"]))
219 assertPermission();
220 # Call below does the job of bypass argument assertion, if such is required,
221 # so the ophandler function doesn't have to re-assert this portion of its
222 # arguments. And it would be even better to pass returned value to ophandler,
223 # so it is not necessary to remember the name of bypass in it.
224 getBypassValue();
225 if (strlen ($redirect_to = call_user_func ($ophandler[$pageno][$tabno][$op])))
226 $location = $redirect_to;
227 }
228 // known "soft" failures require a short error message
229 catch (InvalidRequestArgException $e)
230 {
231 ob_clean();
232 showError ($e->getMessage());
233 }
234 catch (RTDatabaseError $e)
235 {
236 ob_clean();
237 showError ('Database error: ' . $e->getMessage());
238 }
239 catch (RTPermissionDenied $e)
240 {
241 ob_clean();
242 showError ('Operation not permitted');
243 }
244 redirectUser ($location);
245 // any other error requires no special handling and will be caught outside
246 break;
247 case 'popup' == $_REQUEST['module']:
248 require_once 'inc/popup.php';
249 require_once 'inc/init.php';
250 renderPopupHTML();
251 break;
252 case 'upgrade' == $_REQUEST['module']:
253 require_once 'inc/config.php'; // for CODE_VERSION
254 require_once 'inc/dictionary.php';
255 require_once 'inc/upgrade.php';
256 renderUpgraderHTML();
257 break;
258 case 'installer' == $_REQUEST['module']:
259 require_once 'inc/dictionary.php';
260 require_once 'inc/config.php';
261 require_once 'inc/install.php';
262 renderInstallerHTML();
263 break;
264 default:
265 throw new InvalidRequestArgException ('module', $_REQUEST['module']);
266 }
267 ob_end_flush();
268 }
269 catch (Exception $e)
270 {
271 ob_end_clean();
272 printException ($e);
273 }
274 ?>