3e3ff80b43853162c21276b990f159213b459bac
[racktables] / wwwroot / inc / solutions.php
1 <?php
2 /*
3
4 The purpose of this file is to contain functions, which generate a complete
5 HTTP response body and are either "dead ends" or depend on just a small
6 amount of other code (which should eventually be placed in a sort of
7 "first order" library file).
8
9 */
10
11 define ('RE_STATIC_URI', '#^([[:alpha:]]+)/(?:[[:alpha:]]+/)*[[:alnum:]\._-]+\.([[:alpha:]]+)$#');
12
13 function dispatchImageRequest()
14 {
15 genericAssertion ('img', 'string');
16 global $pageno, $tabno;
17 switch ($_REQUEST['img'])
18 {
19 case 'minirack': // rack security context
20 $pageno = 'rack';
21 $tabno = 'default';
22 fixContext();
23 assertPermission();
24 renderRackThumb (getBypassValue());
25 break;
26 case 'preview': // file security context
27 $pageno = 'file';
28 $tabno = 'download';
29 fixContext();
30 assertPermission();
31 renderFilePreview (getBypassValue());
32 break;
33 case 'cactigraph':
34 $pageno = 'object';
35 $tabno = 'cacti';
36 fixContext();
37 assertPermission();
38 genericAssertion ('graph_id', 'uint');
39 if (! array_key_exists ($_REQUEST['graph_id'], getCactiGraphsForObject (getBypassValue())))
40 throw new InvalidRequestArgException ('graph_id', $_REQUEST['graph_id']);
41 proxyCactiRequest ($_REQUEST['graph_id']);
42 break;
43 default:
44 renderErrorImage();
45 }
46 }
47
48 function renderErrorImage ()
49 {
50 header("Content-type: image/png");
51 // "ERROR", 76x17, red on white
52 echo base64_decode
53 (
54 'iVBORw0KGgoAAAANSUhEUgAAAEwAAAARCAYAAAB3h0oCAAAAAXNSR0IArs4c6QAAALBJREFUWMPt' .
55 'WFsOwCAIG4v3vzL7WEyWxQdVwM1A4l/F2iHVETPzESGOMyTAInURRP0suUhb2FIho/jWXO38w4KN' .
56 'LPDGEt2jlgPBZxFKc2o8UT7Lj6SkAmfw1nx+28MkVWQlcjT9EOwjLqnpaNImi+I1j/YSl5RY/gx+' .
57 'VCCF/MnkCz4JZQtvEUXx1nyW9jCUlPVLbTJ/3MO2dsnWRq2Nwl2wTarM51rhsVEnDhT/w7C4APaJ' .
58 'ZhkIGYaUAAAAAElFTkSuQmCC'
59 );
60 }
61
62 function renderAccessDeniedImage()
63 {
64 header ('Content-type: image/png');
65 // 1x1, single black pixel
66 echo base64_decode
67 (
68 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj' .
69 'YGBgAAAABAABJzQnCgAAAABJRU5ErkJggg=='
70 );
71 }
72
73 // Having a local caching array speeds things up. A little.
74 function colorFromHex ($image, $hex)
75 {
76 static $colorcache = array ();
77 if (isset ($colorcache[$hex]))
78 return $colorcache[$hex];
79 $r = hexdec ('0x' . substr ($hex, 0, 2));
80 $g = hexdec ('0x' . substr ($hex, 2, 2));
81 $b = hexdec ('0x' . substr ($hex, 4, 2));
82 $c = imagecolorallocate ($image, $r, $g, $b);
83 $colorcache[$hex] = $c;
84 return $c;
85 }
86
87 function renderRackThumb ($rack_id = 0)
88 {
89 // Don't call DB extra times, hence we are most probably not the
90 // only script wishing to access the same data now.
91 if (NULL !== ($thumbcache = loadThumbCache ($rack_id)))
92 {
93 header("Content-type: image/png");
94 echo $thumbcache;
95 return;
96 }
97 ob_start();
98 if (FALSE !== generateMiniRack ($rack_id))
99 {
100 $capture = ob_get_clean();
101 header("Content-type: image/png");
102 echo $capture;
103 usePreparedExecuteBlade
104 (
105 'REPLACE INTO RackThumbnail SET rack_id=?, thumb_data=?',
106 array ($rack_id, base64_encode ($capture))
107 );
108 return;
109 }
110 // error text in the buffer
111 ob_end_flush();
112 }
113
114 // Output a binary string containing the PNG minirack. Indicate error with return code.
115 function generateMiniRack ($rack_id)
116 {
117 if (NULL === ($rackData = spotEntity ('rack', $rack_id)))
118 return FALSE;
119 amplifyCell ($rackData);
120 markupObjectProblems ($rackData);
121 global $rtwidth;
122 $rtdepth = 9;
123 $offset[0] = 3;
124 $offset[1] = 3 + $rtwidth[0];
125 $offset[2] = 3 + $rtwidth[0] + $rtwidth[1];
126 $totalheight = 3 + 3 + $rackData['height'] * 2;
127 $totalwidth = $offset[2] + $rtwidth[2] + 3;
128 $img = @imagecreatetruecolor ($totalwidth, $totalheight)
129 or die("Cannot Initialize new GD image stream");
130 # It was measured, that caching palette in an array is faster, than
131 # calling colorFromHex() multiple times. It matters, when user's
132 # browser is trying to fetch many minirack images in parallel.
133 $color = array
134 (
135 'F' => colorFromHex ($img, '8fbfbf'),
136 'A' => colorFromHex ($img, 'bfbfbf'),
137 'U' => colorFromHex ($img, 'bf8f8f'),
138 'T' => colorFromHex ($img, '408080'),
139 'Th' => colorFromHex ($img, '80ffff'),
140 'Tw' => colorFromHex ($img, '804040'),
141 'Thw' => colorFromHex ($img, 'ff8080'),
142 'black' => colorFromHex ($img, '000000'),
143 'gray' => colorFromHex ($img, 'c0c0c0'),
144 );
145 $border_color = ($rackData['has_problems'] == 'yes') ? $color['Thw'] : $color['gray'];
146 imagerectangle ($img, 0, 0, $totalwidth - 1, $totalheight - 1, $color['black']);
147 imagerectangle ($img, 1, 1, $totalwidth - 2, $totalheight - 2, $border_color);
148 imagerectangle ($img, 2, 2, $totalwidth - 3, $totalheight - 3, $color['black']);
149 for ($unit_no = 1; $unit_no <= $rackData['height']; $unit_no++)
150 {
151 for ($locidx = 0; $locidx < 3; $locidx++)
152 {
153 $colorcode = $rackData[$unit_no][$locidx]['state'];
154 if (isset ($rackData[$unit_no][$locidx]['hl']))
155 $colorcode = $colorcode . $rackData[$unit_no][$locidx]['hl'];
156 imagerectangle
157 (
158 $img,
159 $offset[$locidx],
160 3 + ($rackData['height'] - $unit_no) * 2,
161 $offset[$locidx] + $rtwidth[$locidx] - 1,
162 3 + ($rackData['height'] - $unit_no) * 2 + 1,
163 $color[$colorcode]
164 );
165 }
166 }
167 imagepng ($img);
168 imagedestroy ($img);
169 return TRUE;
170 }
171
172 function renderProgressBarImage ($done)
173 {
174 if ($done > 100)
175 throw new InvalidArgException ('done', $done);
176 $img = @imagecreatetruecolor (100, 10);
177 switch (isset ($_REQUEST['theme']) ? $_REQUEST['theme'] : 'rackspace')
178 {
179 case 'sparenetwork':
180 $color['T'] = colorFromHex ($img, '808080');
181 $color['F'] = colorFromHex ($img, 'c0c0c0');
182 break;
183 case 'rackspace': // teal
184 default:
185 $color['T'] = colorFromHex ($img, '408080');
186 $color['F'] = colorFromHex ($img, '8fbfbf');
187 }
188 imagefilledrectangle ($img, 0, 0, $done, 10, $color['T']);
189 imagefilledrectangle ($img, $done, 0, 100, 10, $color['F']);
190 for ($x = 20; $x <= 80; $x += 20)
191 {
192 $cc = $x > $done ? $color['T'] : $color['F'];
193 imagesetpixel ($img, $x, 0, $cc);
194 imagesetpixel ($img, $x, 1, $cc);
195 imagesetpixel ($img, $x, 4, $cc);
196 imagesetpixel ($img, $x, 5, $cc);
197 imagesetpixel ($img, $x, 8, $cc);
198 imagesetpixel ($img, $x, 9, $cc);
199 }
200 header("Content-type: image/png");
201 imagepng ($img);
202 imagedestroy ($img);
203 }
204
205 function renderProgressBarError()
206 {
207 header ('Content-type: image/png');
208 // 100x10, red on pink, "progr. bar error"
209 echo base64_decode
210 (
211 'iVBORw0KGgoAAAANSUhEUgAAAGQAAAAKCAYAAABCHPt+AAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A' .
212 '/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sDERYTJrBhF8sAAACvSURBVEjH' .
213 '7VdRDoAgCMXmQbz/qbhJfdnMQQiDTZ3vL6MHvEA03Yg3rIRSABBhV1xwMBXyp/JatFVYq7La1Hft' .
214 'N709xcXxWLqE4tbGr+GXdNDqw8STxSS0z9S695ZD+e05pXhHt8RRHqtebIdoRPASM2K+ePi18Gjz' .
215 'Yuwz7AKpM2cpmjPUVx3qf0OIqyLKvl+POMp6+R3Jy9oxnD4C0nsPiTrfb35viO2QiOF6foYKD57g' .
216 'f1uXQb2mAAAAAElFTkSuQmCC'
217 );
218 }
219
220 function renderFilePreview ($file_id)
221 {
222 if ($image = getFileCache ($file_id)) //Cache Hit
223 {
224 header("Content-type: image/jpeg");
225 echo $image;
226 return;
227 }
228 //Cache Miss
229 $file = getFile ($file_id);
230 $image = imagecreatefromstring ($file['contents']);
231 unset ($file);
232 $width = imagesx ($image);
233 $height = imagesy ($image);
234 if ($width > getConfigVar ('PREVIEW_IMAGE_MAXPXS') or $height > getConfigVar ('PREVIEW_IMAGE_MAXPXS'))
235 {
236 $ratio = getConfigVar ('PREVIEW_IMAGE_MAXPXS') / max ($width, $height);
237 $newwidth = $width * $ratio;
238 $newheight = $height * $ratio;
239 $resampled = imagecreatetruecolor ($newwidth, $newheight);
240 imagecopyresampled ($resampled, $image, 0, 0, 0, 0, $newwidth, $newheight, $width, $height);
241 imagedestroy ($image);
242 $image = $resampled;
243 unset ($resampled);
244 }
245 header ('Content-type: image/jpeg');
246 ob_start();
247 imagejpeg ($image);
248 imagedestroy ($image);
249 commitAddFileCache ($file_id, ob_get_flush());
250 }
251
252 function printStatic404()
253 {
254 header ('404 Not Found');
255 ?><!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
256 <html><head>
257 <title>404 Not Found</title>
258 </head><body>
259 <h1>Not Found</h1>
260 <p>The requested file was not found in this instance.</p>
261 <hr>
262 <address>RackTables static content proxy</address>
263 </body></html><?php
264 exit;
265 }
266
267 function proxyStaticURI ($URI)
268 {
269 $content_type = array
270 (
271 'css' => 'text/css',
272 'js' => 'text/javascript',
273 'html' => 'text/html',
274 'png' => 'image/png',
275 'gif' => 'image/gif',
276 'jpg' => 'image/jpeg',
277 'jpeg' => 'image/jpeg',
278 'ico' => 'image/x-icon',
279 );
280 $matches = array();
281 if
282 (
283 ! preg_match (RE_STATIC_URI, $URI, $matches)
284 or ! in_array ($matches[1], array ('pix', 'css', 'js'))
285 or ! array_key_exists (strtolower ($matches[2]), $content_type)
286 )
287 printStatic404();
288 global $local_staticdir, $racktables_staticdir;
289 if (isset ($local_staticdir))
290 $fh = @fopen ("${local_staticdir}/${URI}", 'r');
291 if (! isset ($fh) or FALSE === $fh)
292 $fh = @fopen ("${racktables_staticdir}/${URI}", 'r');
293 if (FALSE === $fh)
294 printStatic404();
295 if (FALSE !== $stat = fstat ($fh))
296 if (checkCachedResponse (max ($stat['mtime'], $stat['ctime']), 0))
297 exit;
298 header ('Content-type: ' . $content_type[$matches[2]]);
299 fpassthru ($fh);
300 fclose ($fh);
301 }
302
303 function proxyCactiRequest ($graph_id)
304 {
305 $ret = array();
306 $cacti_url = getConfigVar('CACTI_URL');
307 $cacti_user = getConfigVar('CACTI_USERNAME');
308 $cacti_pass = getConfigVar('CACTI_USERPASS');
309 $url = $cacti_url . "/graph_image.php?action=view&local_graph_id=" . $graph_id;
310 $postvars = "action=login&login_username=" . $cacti_user . "&login_password=" . $cacti_pass;
311
312 $session = curl_init();
313 // curl_setopt ($session, CURLOPT_VERBOSE, TRUE);
314
315 // Initial options up here so a specific type can override them
316 curl_setopt ($session, CURLOPT_FOLLOWLOCATION, FALSE);
317 curl_setopt ($session, CURLOPT_TIMEOUT, 10);
318 curl_setopt ($session, CURLOPT_RETURNTRANSFER, TRUE);
319 curl_setopt ($session, CURLOPT_URL, $url);
320
321 if (isset($_SESSION['CACTICOOKIE'][$cacti_url]))
322 curl_setopt ($session, CURLOPT_COOKIE, $_SESSION['CACTICOOKIE'][$cacti_url]);
323
324 // Request the image
325 $ret['contents'] = curl_exec ($session);
326 $ret['type'] = curl_getinfo ($session, CURLINFO_CONTENT_TYPE);
327 $ret['size'] = curl_getinfo ($session, CURLINFO_SIZE_DOWNLOAD);
328
329 // Not an image, probably the login page
330 if (preg_match('/^text\/html.*/i', $ret['type'])) {
331 // Request to set the cookies
332 curl_setopt ($session, CURLOPT_HEADER, TRUE);
333 curl_setopt ($session, CURLOPT_COOKIE, ""); // clear the old cookie
334 $headers = curl_exec ($session);
335
336 // Get the cookies from the headers
337 preg_match('/Set-Cookie: ([^;]*)/i', $headers, $cookies);
338 array_shift($cookies); // Remove 'Set-Cookie: ...' value
339 $cookie_header = implode(";", $cookies);
340 $_SESSION['CACTICOOKIE'][$cacti_url] = $cookie_header; // store for later use by this user
341
342 // POST Login
343 curl_setopt ($session, CURLOPT_COOKIE, $cookie_header);
344 curl_setopt ($session, CURLOPT_HEADER, FALSE);
345 curl_setopt ($session, CURLOPT_POST, TRUE);
346 curl_setopt ($session, CURLOPT_POSTFIELDS, $postvars);
347 curl_exec ($session);
348
349 // Request the image
350 curl_setopt ($session, CURLOPT_HTTPGET, TRUE);
351 $ret['contents'] = curl_exec ($session);
352 $ret['type'] = curl_getinfo ($session, CURLINFO_CONTENT_TYPE);
353 $ret['size'] = curl_getinfo ($session, CURLINFO_SIZE_DOWNLOAD);
354 }
355
356 curl_close ($session);
357
358 if ($ret['type'] != NULL)
359 header("Content-Type: {$ret['type']}");
360 if ($ret['size'] > 0)
361 header("Content-Length: {$ret['size']}");
362
363 echo $ret['contents'];
364 }
365
366 function printSVGMessageBar ($text = 'lost message', $textattrs = array(), $rectattrs = array())
367 {
368 $mytextattrs = array
369 (
370 'fill' => 'black',
371 'x' => '85',
372 'y' => '15',
373 'font-size' => '100%',
374 'text-anchor' => 'middle',
375 'font-family' => 'monospace',
376 'font-weight' => 'bold',
377 );
378 $myrectattrs = array
379 (
380 'fill' => 'white',
381 'stroke' => 'black',
382 'x' => '0',
383 'y' => '0',
384 'width' => '170px',
385 'height' => '20px',
386 'stroke-width' => '1px',
387 );
388 foreach ($textattrs as $k => $v)
389 $mytextattrs[$k] = $v;
390 foreach ($rectattrs as $k => $v)
391 $myrectattrs[$k] = $v;
392 echo "<svg width='" . $myrectattrs['width'] . "' height='" . $myrectattrs['height'] . "' version='1.1' ";
393 echo "xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'>\n";
394 echo '<rect';
395 foreach ($myrectattrs as $k => $v)
396 echo " ${k}='${v}'";
397 echo " />\n<text";
398 foreach ($mytextattrs as $k => $v)
399 echo " ${k}='${v}'";
400 echo ">${text}</text>\n";
401 echo "</svg>\n";
402 }
403
404 ?>