bbf2115ee26dac45dc8254f10c3e07210879c567
[racktables] / wwwroot / inc / exceptions.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 define
8 (
9 'IMG_100x10_PBAR_ERROR', // 100x10, red on pink, "progr. bar error"
10 'iVBORw0KGgoAAAANSUhEUgAAAGQAAAAKCAYAAABCHPt+AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A' .
11 '/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDERYTJrBhF8sAAACvSURBVEjH' .
12 '7VdRDoAgCMXmQbz/qbhJfdnMQQiDTZ3vL6MHvEA03Yg3rIRSABBhV1xwMBXyp/JatFVYq7La1Hft' .
13 'N709xcXxWLqE4tbGr+GXdNDqw8STxSS0z9S695ZD+e05pXhHt8RRHqtebIdoRPASM2K+ePi18Gjz' .
14 'Yuwz7AKpM2cpmjPUVx3qf0OIqyLKvl+POMp6+R3Jy9oxnD4C0nsPiTrfb35viO2QiOF6foYKD57g' .
15 'f1uXQb2mAAAAAElFTkSuQmCC'
16 );
17 define
18 (
19 'IMG_100x10_PHP_GD_NA', // 100x10, red on pink: "PHP-GD is N/A"
20 'iVBORw0KGgoAAAANSUhEUgAAAGQAAAAKCAYAAABCHPt+AAAAw0lEQVRIx+1XwQ3AIAjUxkHcfyo3' .
21 'aV8kligino2mkvRhERHOQ/R3Src7soxcv4o2xuW3GGDBpfTW5WP+T7K1+OJSW6/lR7t/zfhzQDQg' .
22 'IGwp4dJ8rkMkp2cNmgvwu0fJ4kFKQUs6ApdAo6+2Tk0HYgOeIfmGSxS32iJtLMBZmQRgSZgWVAug' .
23 'VomSmDF6OfPTL905mgQDm4XgVpNS8L31vMdPa75UurRNxtaA1BLSUx7RJzoHhXeLJRYPlC1/HoYT' .
24 'uy+DPGgNeJs7N+Q1AAAAAElFTkSuQmCC'
25 );
26 define
27 (
28 'IMG_100x10_ARG_ERROR', // 100x10, red on pink: "argument error"
29 'iVBORw0KGgoAAAANSUhEUgAAAGQAAAAKCAYAAABCHPt+AAAApElEQVRIx+1WUQ6AIAjF1kG8/6m4' .
30 'iX2xNUYKSKnN9xc2RODxSAWxwN+RMwDiEqEesDEVzq6uI1D3tWxk5x1L30//cp/Wu+5nElM0b/G8' .
31 '18FKf0GkhNbsUsIsfnnhLPfXRpbWlzY2ZyFiGTJKF2b0NWRkeTo/GpEiPZHgv8sQSRdqdksCJb3Q' .
32 'xCuNIauvlr51IH269i60fq7HkJ5tbGNgQXYRTLgAuI9qHc8aWHAAAAAASUVORK5CYII='
33 );
34 define
35 (
36 'IMG_45x90_PHP_GD_NA', // 45x90, red on pink: "ERROR: PHP-GD is not available"
37 'iVBORw0KGgoAAAANSUhEUgAAAC0AAABaCAYAAAAsPd/jAAABmElEQVRo3u2aS47DIAxA24qD9P6n' .
38 '4iadFVKG8T+hTKuH1AUBk4dtAja9v3p/3T6sPG4fWIAGGmiggQYaaKCB/kbo9qv2fP7t0bvcNp57' .
39 'bWdlXWhPaH7Zsa61DahjPSq71T00QK9Ilhc1HTWl5QIZMEtWGafmHhnfz1giKPsom1t7gdR2rFtg' .
40 '1rgm9Fgo43cWfF6AEWuOfsr4d1IIQAMNNNBAAw000ED/t2jcC7Ok8CoSqUdiPys6F8ZrKdBo8GrJ' .
41 'SoGAF51P7XvcY55QNG0hajoabWci6qtlTPfwZhz1YwusGMG3t7tCdr0IE2pb/HmO9JNWJBoH+hLo' .
42 'So5uO3Q2E7pQGV9wfeFpREo6Vq8qpERj0Jq177Q3uHQYmjcU7fkS6Fnbq3z9cp/ufQtsXdPVT59l' .
43 'oaRPs40D/VZo55LHXRul260Vh37cIxLYajtZtL8VJ3qblNJ+Lu8R7a/1s+SNeuz/Htb5YeWG5P4L' .
44 'IQsU7V+dqLGoW0gj2mnsrKa9nIhyzGUbBxpooIEGGmiggQYaaKCBBhpooAPlB5v19Mx7m4CJAAAA' .
45 'AElFTkSuQmCC'
46 );
47 define
48 (
49 'IMG_45x90_RACK_NOT_FOUND', // 45x90, red on pink: "ERROR: rack doesn't exist"
50 'iVBORw0KGgoAAAANSUhEUgAAAC0AAABaCAYAAAAsPd/jAAABgElEQVRo3u2ZWxLCIAxFtcNC3P+q' .
51 'upP6xQyDSQgkWq2HGT8oj56GwCXxfuz7cfuxst1+sAANNNBAAw000EADfUXo8vLk8Xjtte9yW30+' .
52 'aouOHUKPBvUva+taW4Vq696xp7qHBjgqwsoXV0dtKS0XmAGzxgrzrLvHjO/PrIRj7BZabu0FUltb' .
53 't8CseU3oulHqLwreb0DPatZ+wvx3UghAAw000EADDTTQQCeVlSABS39F3iMSiGrB6SgYngyCS+iT' .
54 '+5dJuQsLagE4Dh3ZYIvAuT5dIeovEs2fbukerk/ELFicaBzoFOig9F7X0s583wVlXPvaGbmWct3e' .
55 'vzqUZGVxK1Y7kUeu27p0Dkt92/7G+X2uuHz8lue5QI1cJR1auxtoltN8NQKq+DQyDvSp0G+Q//dD' .
56 'W0fc4gf9UTSuybWllKMrwYTolCVgTYJHEXhC+iDH0pKYaMBJcl5SN5fX0sETpSwBa3Jt3dISfRoZ' .
57 'BxpooIEGGmiggQYaaKCBBhpooB3lCc5C4kpZ4AwVAAAAAElFTkSuQmCC'
58 );
59 define
60 (
61 'IMG_45x90_MALF_REQ', // 45x90, red on pink: "ERROR: malformed request"
62 'iVBORw0KGgoAAAANSUhEUgAAAC0AAABaCAYAAAAsPd/jAAABdUlEQVRo3u2aSw6FIAxF9YWFuP9V' .
63 'sRPfiMQoLS0gojkkDvifQPHS6rrHuC8vS7/lhQlooIEGGmiggQYa6C9Ch0vJtl1bxZivS+Wluta+' .
64 'RehSp/Nkx7xUl6COeWvfR81DAiylzM4HU0NpKzUT8IBpfTPj1JuHx/Y9O2Ho+2vabmmCXN0xr4Fp' .
65 '46rQ6aCkpxX8fAAtu5naZcZfCSEADTTQQAMNNNBAA13piViuuU3e+AgfkbhHybmVnE+Ls5sLKUi+' .
66 'YVXcwxKLOOZLsZAzsGWc22260wGzhiJClwm0ENcNh3b8Smtnw5jwxoGeHtp5eFnp52Xcs501Et/w' .
67 'Pq9/T1sk3lM+1dW0t8QPge54jx4DnQu6x2iK9iPjQAM9+X3kQzLu8ZYlSS993JfGznGc3vWhu0x7' .
68 '/UnJE1f+RAg9bKzZVp1+ox26lxxL4zg+6AfzRB459pRrki7YdF8ZL/wRg7jMfA1lpYEGGmiggQYa' .
69 'aKCBBhpooIHW0h8839XO8L3K4AAAAABJRU5ErkJggg=='
70 );
71 define
72 (
73 'IMG_1x1_BLACK', // 1x1, single black pixel
74 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj' .
75 'YGBgAAAABAABJzQnCgAAAABJRU5ErkJggg=='
76 );
77 define
78 (
79 'IMG_76x17_ERROR', // 76x17, red on white: "ERROR"
80 'iVBORw0KGgoAAAANSUhEUgAAAEwAAAARCAYAAAB3h0oCAAAAAXNSR0IArs4c6QAAALBJREFUWMPt' .
81 'WFsOwCAIG4v3vzL7WEyWxQdVwM1A4l/F2iHVETPzESGOMyTAInURRP0suUhb2FIho/jWXO38w4KN' .
82 'LPDGEt2jlgPBZxFKc2o8UT7Lj6SkAmfw1nx+28MkVWQlcjT9EOwjLqnpaNImi+I1j/YSl5RY/gx+' .
83 'VCCF/MnkCz4JZQtvEUXx1nyW9jCUlPVLbTJ/3MO2dsnWRq2Nwl2wTarM51rhsVEnDhT/w7C4APaJ' .
84 'ZhkIGYaUAAAAAElFTkSuQmCC'
85 );
86
87 // The default approach is to treat an error as fatal, in which case
88 // some message is output and the user is left there. Inheriting classes
89 // represent more specific cases, some of which can be handled in a
90 // "softer" way (see below).
91 class RackTablesError extends Exception
92 {
93 const INTERNAL = 0; // the default code
94 const DB_WRITE_FAILED = 3;
95 const NOT_AUTHENTICATED = 4;
96 const MISCONFIGURED = 6;
97 protected final function genHTMLPage ($title, $text)
98 {
99 global $helpdesk_banner, $debug_mode;
100 if (isset ($debug_mode) && $debug_mode && $this->code != RackTablesError::NOT_AUTHENTICATED)
101 {
102 // in debug mode, dump backtrace instead of standard page
103 // NOT_AUTHENTICATED exception does not need debugging, so it is skipped
104 printGenericException ($this);
105 return;
106 }
107
108 header ('Content-Type: text/html; charset=UTF-8');
109 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'."\n";
110 echo '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">'."\n";
111 echo "<head><title>${title}</title>";
112 echo "</head><body>${text}";
113 if (isset ($helpdesk_banner))
114 echo '<hr>' . $helpdesk_banner;
115 echo '</body></html>';
116 }
117 protected static function formatString ($string)
118 {
119 return isCLIMode() ? $string : stringForLabel ($string, 80);
120 }
121 public function dispatch()
122 {
123 $msgheader = array
124 (
125 self::MISCONFIGURED => 'Configuration error',
126 self::INTERNAL => 'Internal error',
127 self::DB_WRITE_FAILED => 'Database write failed',
128 );
129 $msgbody = array
130 (
131 self::MISCONFIGURED => '<h2>Configuration error</h2><br>' . $this->message,
132 self::INTERNAL => '<h2>Internal error</h2><br>' . $this->message,
133 self::DB_WRITE_FAILED => '<h2>Database write failed</h2><br>' . $this->message,
134 );
135 switch ($this->code)
136 {
137 case self::NOT_AUTHENTICATED:
138 header ('WWW-Authenticate: Basic realm="' . getConfigVar ('enterprise') . ' RackTables access"');
139 header ('HTTP/1.1 401 Unauthorized');
140 $this->genHTMLPage ('Not authenticated', '<h2>This system requires authentication. You should use a username and a password.</h2>');
141 break;
142 case self::MISCONFIGURED:
143 case self::INTERNAL:
144 case self::DB_WRITE_FAILED:
145 $this->genHTMLPage ($msgheader[$this->code], $msgbody[$this->code]);
146 break;
147 default:
148 throw new RackTablesError ('Dispatching error, unknown code ' . $this->code, RackTablesError::INTERNAL);
149 }
150 }
151 }
152
153 class EntityNotFoundException extends RackTablesError
154 {
155 function __construct ($realm, $id)
156 {
157 parent::__construct ("Record '${realm}'#'${id}' does not exist");
158 }
159 public function dispatch()
160 {
161 global $debug_mode;
162 if ($debug_mode)
163 {
164 printGenericException ($this);
165 return;
166 }
167 showError ($this->message);
168 redirectUser (buildRedirectURL ('index', 'default'));
169 }
170 }
171
172 class ERetryNeeded extends RackTablesError
173 {
174 function __construct ($message)
175 {
176 $this->code = parent::INTERNAL;
177 parent::__construct ($message);
178 }
179 }
180
181 class InvalidArgException extends RackTablesError
182 {
183 // derive an instance of InvalidRequestArgException
184 function newIRAE ($argname = NULL)
185 {
186 if ($argname === NULL)
187 return new InvalidRequestArgException ($this->name, $this->value, $this->reason);
188 return new InvalidRequestArgException ($argname, $_REQUEST[$argname], $this->reason);
189 }
190 function __construct ($name, $value, $reason = NULL)
191 {
192 $message = 'Argument \'' . self::formatString ($name) . '\'' .
193 ' of value ' . self::formatString (var_export ($value, TRUE), 200) .
194 ' is invalid' . (is_null ($reason) ? '' : ' (' . self::formatString ($reason, 100) . ')') .
195 '.';
196 parent::__construct ($message, parent::INTERNAL);
197 $this->name = $name;
198 $this->value = $value;
199 $this->reason = $reason;
200 }
201 }
202
203 // this simplifies construction and helps in catching "soft"
204 // errors (invalid input from the user)
205 class InvalidRequestArgException extends InvalidArgException
206 {
207 public function dispatch()
208 {
209 RackTablesError::genHTMLPage ('Assertion failed', '<h2>Assertion failed</h2><br>' . $this->message);
210 }
211 }
212
213 // this wraps certain known PDO errors and is caught in process.php
214 // as a "soft" error
215 class RTDatabaseError extends RackTablesError
216 {
217 public function dispatch()
218 {
219 RackTablesError::genHTMLPage ('Database soft error', '<h2>Database soft error</h2><br>' . $this->message);
220 }
221 }
222
223 // gateway failure is a common case of a "soft" error, some functions do catch this
224 class RTGatewayError extends RackTablesError
225 {
226 public function dispatch()
227 {
228 RackTablesError::genHTMLPage ('Gateway error', '<h2>Gateway error</h2><br>' . $this->message);
229 }
230 }
231
232 # "Permission denied" is a very common case, which in some situations is
233 # treated as a "soft" error.
234 class RTPermissionDenied extends RackTablesError
235 {
236 public function dispatch()
237 {
238 global $pageno, $tabno,
239 $user_given_tags,
240 $target_given_tags,
241 $auto_tags,
242 $expl_tags,
243 $impl_tags;
244 header ('Content-Type: text/html; charset=UTF-8');
245 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' . "\n";
246 echo '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">' . "\n";
247 echo "<head><title>RackTables: access denied</title>\n";
248 printPageHeaders();
249 echo '</head><body>';
250 echo "<table border=1 cellspacing=0 cellpadding=3 width='50%' align=center>\n";
251 echo '<tr><th colspan=2><h3>' . getImageHREF ('DENIED') . ' access denied ';
252 echo getImageHREF ('DENIED') . '</h3></th></tr>';
253 echo '<tr><th width="50%" class=tagchain>User given tags:</th><td class=tagchain>';
254 echo serializeTags ($user_given_tags) . "&nbsp;</td></tr>\n";
255 echo '<tr><th width="50%" class=tagchain>Target given tags:</th><td class=tagchain>';
256 echo serializeTags ($target_given_tags) . "&nbsp;</td></tr>\n";
257 echo '<tr><th width="50%" class=tagchain>Effective explicit tags:</th><td class=tagchain>';
258 echo serializeTags ($expl_tags) . "&nbsp;</td></tr>\n";
259 echo '<tr><th width="50%" class=tagchain>Effective implicit tags:</th><td class=tagchain>';
260 echo serializeTags ($impl_tags) . "&nbsp;</td></tr>\n";
261 echo '<tr><th width="50%" class=tagchain>Automatic tags:</th><td class=tagchain>';
262 echo serializeTags ($auto_tags) . "&nbsp;</td></tr>\n";
263 echo "<tr><th width='50%' class=tdright>Requested page:</th><td class=tdleft>${pageno}</td></tr>\n";
264 echo "<tr><th width='50%' class=tdright>Requested tab:</th><td class=tdleft>${tabno}</td></tr>\n";
265 echo "<tr><td colspan=2 align=center>Click <a href='index.php?logout'>here</a> to logout.</td></tr>\n";
266 echo "</table>\n";
267 echo '</body></html>';
268 }
269 }
270
271 class RackCodeError extends RackTablesError
272 {
273 protected $lineno;
274 function __construct ($message, $lineno = 'unknown')
275 {
276 # RackCodeError without a catch-block is very likely an internal error
277 parent::__construct ($message, self::INTERNAL);
278 $this->lineno = $lineno;
279 }
280 public function dispatch()
281 {
282 parent::genHTMLPage ('RackCode error', '<h2>RackCode error on line ' . $this->lineno . '</h2><br>' . $this->message);
283 }
284 }
285
286 // Whether there is a failure to produce a normal image, this class will emit one
287 // of the hardcoded last-resort images without the dependency on PHP-GD.
288 class RTImageError extends RackTablesError
289 {
290 protected $imgbin;
291 function __construct ($subject = NULL)
292 {
293 $map = array
294 (
295 'pbar_error' => IMG_100x10_PBAR_ERROR,
296 'pbar_php_gd_error' => IMG_100x10_PHP_GD_NA,
297 'pbar_arg_error' => IMG_100x10_ARG_ERROR,
298 'rack_php_gd_error' => IMG_45x90_PHP_GD_NA,
299 'rack_not_found' => IMG_45x90_RACK_NOT_FOUND,
300 'rack_arg_error' => IMG_45x90_MALF_REQ,
301 'access_denied' => IMG_1x1_BLACK,
302 );
303 $this->imgbin = base64_decode (array_fetch ($map, $subject, IMG_76x17_ERROR));
304 }
305 public function dispatch()
306 {
307 header ('Content-Type: image/png');
308 echo $this->imgbin;
309 }
310 }
311
312 function dumpArray ($arr)
313 {
314 echo '<table class="exceptionParametersDump">';
315 foreach ($arr as $key => $value)
316 echo '<tr><th>' . stringForTD ($key) . '</th><td>' . stringForTD ($value, 100) . '</td></tr>';
317 echo '</table>';
318 }
319
320 function stringTrace ($trace)
321 {
322 $ret = '';
323 foreach ($trace as $line)
324 {
325 if (isset ($line['file']) && isset ($line['line']))
326 $ret .= $line['file'] . ':' . $line['line'] . ' ';
327 $ret .= $line['function'] . '(';
328 $f = TRUE;
329 if (isset ($line['args']) && is_array ($line['args']))
330 foreach ($line['args'] as $arg)
331 {
332 if (! $f)
333 $ret .= ', ';
334 if (is_string ($arg))
335 $printarg = '\'' . $arg . '\'';
336 elseif (is_null ($arg))
337 $printarg = 'NULL';
338 elseif (is_array ($arg))
339 $printarg = print_r ($arg, 1);
340 elseif (is_object ($arg))
341 $printarg = 'Object(' . get_class ($arg) . ')';
342 else
343 $printarg = $arg;
344 $ret .= $printarg;
345 $f = FALSE;
346 }
347 $ret .= ")\n";
348 }
349 return $ret;
350 }
351
352 function printPDOException ($e)
353 {
354 header ('HTTP/1.1 500 Internal Server Error');
355 header ('Content-Type: text/html; charset=UTF-8');
356 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' . "\n";
357 echo '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">' . "\n";
358 echo "<head><title> PDO Exception </title>\n";
359 echo "<link rel=stylesheet type='text/css' href='?module=chrome&uri=css/pi.css' />\n";
360 echo "<link rel=icon href='?module=chrome&uri=pix/favicon.ico' type='image/x-icon' />\n";
361 echo '</head> <body>';
362 echo '<h2>Pdo exception: ' . get_class ($e) . '</h2><code>' . $e->getMessage() . '</code> (<code>' . $e->getCode() . '</code>)';
363 echo '<p>at file <code>' . $e->getFile() . '</code>, line <code>' . $e->getLine() . '</code></p><pre>';
364 echo stringTrace ($e->getTrace());
365 echo '</pre>';
366 echo '<h2>Error info:</h2>';
367 echo '<pre>';
368 print_r ($e->errorInfo);
369 echo '</pre>';
370 echo '<h2>Parameters:</h2>';
371 echo '<h3>GET</h3>';
372 dumpArray ($_GET);
373 echo '<h3>POST</h3>';
374 dumpArray ($_POST);
375 echo '<h3>COOKIE</h3>';
376 dumpArray ($_COOKIE);
377 echo '</body></html>';
378 }
379
380 function printGenericException ($e)
381 {
382 header('HTTP/1.1 500 Internal Server Error');
383 header ('Content-Type: text/html; charset=UTF-8');
384 echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' . "\n";
385 echo '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">' . "\n";
386 echo "<head><title> Exception </title>\n";
387 echo "<link rel=stylesheet type='text/css' href='?module=chrome&uri=css/pi.css' />\n";
388 echo "<link rel=icon href='?module=chrome&uri=pix/favicon.ico' type='image/x-icon' />\n";
389 echo '</head> <body>';
390 echo '<h2>Uncaught exception: ' . get_class ($e) . '</h2><code>' . $e->getMessage() . '</code> (<code>' . $e->getCode() . '</code>)';
391 echo '<p>at file <code>' . $e->getFile() . '</code>, line <code>' . $e->getLine() . '</code></p><pre>';
392 echo stringTrace ($e->getTrace());
393 echo '</pre>';
394 echo '<h2>Parameters:</h2>';
395 echo '<h3>GET</h3>';
396 dumpArray ($_GET);
397 echo '<h3>POST</h3>';
398 dumpArray ($_POST);
399 echo '<h3>COOKIE</h3>';
400 dumpArray ($_COOKIE);
401 echo '</body></html>';
402 }
403
404 function printException ($e)
405 {
406 if ($e instanceof RackTablesError)
407 $e->dispatch();
408 elseif ($e instanceof PDOException)
409 printPDOException ($e);
410 else
411 printGenericException ($e);
412 }
413
414 ?>