r5103 Do not fetch locations list and build location tree each time request is processed
[racktables-incomplete-works] / wwwroot / inc / functions.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 /*
8 *
9 * This file is a library of computational functions for RackTables.
10 *
11 */
12
13 $loclist[0] = 'front';
14 $loclist[1] = 'interior';
15 $loclist[2] = 'rear';
16 $loclist['front'] = 0;
17 $loclist['interior'] = 1;
18 $loclist['rear'] = 2;
19 $template[0] = array (TRUE, TRUE, TRUE);
20 $template[1] = array (TRUE, TRUE, FALSE);
21 $template[2] = array (FALSE, TRUE, TRUE);
22 $template[3] = array (TRUE, FALSE, FALSE);
23 $template[4] = array (FALSE, TRUE, FALSE);
24 $template[5] = array (FALSE, FALSE, TRUE);
25 $templateWidth[0] = 3;
26 $templateWidth[1] = 2;
27 $templateWidth[2] = 2;
28 $templateWidth[3] = 1;
29 $templateWidth[4] = 1;
30 $templateWidth[5] = 1;
31
32 define ('CHAP_OBJTYPE', 1);
33 define ('CHAP_PORTTYPE', 2);
34 // The latter matches both SunOS and Linux-styled formats.
35 define ('RE_L2_IFCFG', '/^[0-9a-f]{1,2}(:[0-9a-f]{1,2}){5}$/i');
36 define ('RE_L2_CISCO', '/^[0-9a-f]{4}(\.[0-9a-f]{4}){2}$/i');
37 define ('RE_L2_HUAWEI', '/^[0-9a-f]{4}(-[0-9a-f]{4}){2}$/i');
38 define ('RE_L2_SOLID', '/^[0-9a-f]{12}$/i');
39 define ('RE_L2_IPCFG', '/^[0-9a-f]{2}(-[0-9a-f]{2}){5}$/i');
40 define ('RE_L2_WWN_COLON', '/^[0-9a-f]{1,2}(:[0-9a-f]{1,2}){7}$/i');
41 define ('RE_L2_WWN_HYPHEN', '/^[0-9a-f]{2}(-[0-9a-f]{2}){7}$/i');
42 define ('RE_L2_WWN_SOLID', '/^[0-9a-f]{16}$/i');
43 define ('RE_IP4_ADDR', '#^[0-9]{1,3}(\.[0-9]{1,3}){3}$#');
44 define ('RE_IP4_NET', '#^[0-9]{1,3}(\.[0-9]{1,3}){3}/[0-9]{1,2}$#');
45 define ('E_8021Q_NOERROR', 0);
46 define ('E_8021Q_VERSION_CONFLICT', 101);
47 define ('E_8021Q_PULL_REMOTE_ERROR', 102);
48 define ('E_8021Q_PUSH_REMOTE_ERROR', 103);
49 define ('E_8021Q_SYNC_DISABLED', 104);
50 define ('VLAN_MIN_ID', 1);
51 define ('VLAN_MAX_ID', 4094);
52 define ('VLAN_DFL_ID', 1);
53 define ('TAB_REMEMBER_TIMEOUT', 300);
54
55 // Entity type by page number mapping is 1:1 atm, but may change later.
56 $etype_by_pageno = array
57 (
58 'ipv4net' => 'ipv4net',
59 'ipv6net' => 'ipv6net',
60 'ipv4rspool' => 'ipv4rspool',
61 'ipv4vs' => 'ipv4vs',
62 'object' => 'object',
63 'rack' => 'rack',
64 'location' => 'location',
65 'user' => 'user',
66 'file' => 'file',
67 'vst' => 'vst',
68 );
69
70 // Rack thumbnail image width summands: "front", "interior" and "rear" elements w/o surrounding border.
71 $rtwidth = array
72 (
73 0 => 9,
74 1 => 21,
75 2 => 9
76 );
77
78 $location_obj_types = array
79 (
80 1560,
81 1561,
82 1562
83 );
84
85 // 802.1Q deploy queue titles
86 $dqtitle = array
87 (
88 'sync_aging' => 'Normal, aging',
89 'resync_aging' => 'Version conflict, aging',
90 'sync_ready' => 'Normal, ready for sync',
91 'resync_ready' => 'Version conflict, ready for retry',
92 'failed' => 'Failed',
93 'disabled' => 'Sync disabled',
94 'done' => 'Up to date',
95 );
96
97 $wdm_packs = array
98 (
99 '1000cwdm80' => array
100 (
101 'title' => '1000Base-CWDM80 (8 channels)',
102 'iif_ids' => array (3, 4),
103 'oif_ids' => array (1209, 1210, 1211, 1212, 1213, 1214, 1215, 1216),
104 ),
105 '1000dwdm80' => array // ITU channels 20~61
106 (
107 'title' => '1000Base-DWDM80 (42 channels)',
108 'iif_ids' => array (3, 4),
109 'oif_ids' => array
110 (
111 1217, 1218, 1219, 1220, 1221, 1222, 1223, 1224, 1225, 1226,
112 1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236,
113 1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, 1246,
114 1247, 1248, 1249, 1250, 1251, 1252, 1253, 1254, 1255, 1256,
115 1257, 1258
116 ),
117 ),
118 '10000dwdm80' => array // same channels for 10GE
119 (
120 'title' => '10GBase-ZR-DWDM80 (42 channels)',
121 'iif_ids' => array (9, 6, 5, 8, 7),
122 'oif_ids' => array
123 (
124 1259, 1260, 1261, 1262, 1263, 1264, 1265, 1266, 1267, 1268,
125 1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276, 1277, 1278,
126 1279, 1280, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288,
127 1289, 1290, 1291, 1292, 1293, 1294, 1295, 1296, 1297, 1298,
128 1299, 1300
129 ),
130 ),
131 '10000dwdm40' => array
132 (
133 'title' => '10GBase-ER-DWDM40 (42 channels)',
134 'iif_ids' => array (9, 6, 5, 8, 7),
135 'oif_ids' => array
136 (
137 1425, 1426, 1427, 1428, 1429, 1430, 1431, 1432, 1433, 1434,
138 1435, 1436, 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1444,
139 1445, 1446, 1447, 1448, 1449, 1450, 1451, 1452, 1453, 1454,
140 1455, 1456, 1457, 1458, 1459, 1460, 1461, 1462, 1463, 1464,
141 1465, 1466
142 ),
143 ),
144 );
145
146 $log_messages = array(); // messages waiting for displaying
147
148 // This function assures that specified argument was passed
149 // and is a number greater than zero.
150 function assertUIntArg ($argname, $allow_zero = FALSE)
151 {
152 if (!isset ($_REQUEST[$argname]))
153 throw new InvalidRequestArgException($argname, '', 'parameter is missing');
154 if (!is_numeric ($_REQUEST[$argname]))
155 throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'parameter is not a number');
156 if ($_REQUEST[$argname] < 0)
157 throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'parameter is less than zero');
158 if (!$allow_zero and $_REQUEST[$argname] == 0)
159 throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'parameter is zero');
160 }
161
162 function isInteger ($arg, $allow_zero = FALSE)
163 {
164 if (! is_numeric ($arg))
165 return FALSE;
166 if (! $allow_zero and ! $arg)
167 return FALSE;
168 return TRUE;
169 }
170
171 // make sure the arg is a parsable date
172 function assertDateArg ($argname, $ok_if_empty = FALSE)
173 {
174 assertStringArg ($argname, $ok_if_empty);
175 // different versions of PHP return false/-1
176 if ($_REQUEST[$argname] != '' and strtotime ($_REQUEST[$argname]) <= 0)
177 throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'parameter is not a parsable date');
178 }
179
180
181 // This function assures that specified argument was passed
182 // and is a non-empty string.
183 function assertStringArg ($argname, $ok_if_empty = FALSE)
184 {
185 if (!isset ($_REQUEST[$argname]))
186 throw new InvalidRequestArgException($argname, '', 'parameter is missing');
187 if (!is_string ($_REQUEST[$argname]))
188 throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'parameter is not a string');
189 if (!$ok_if_empty and !strlen ($_REQUEST[$argname]))
190 throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'parameter is an empty string');
191 }
192
193 function assertBoolArg ($argname, $ok_if_empty = FALSE)
194 {
195 if (!isset ($_REQUEST[$argname]))
196 throw new InvalidRequestArgException($argname, '', 'parameter is missing');
197 if (!is_string ($_REQUEST[$argname]) or $_REQUEST[$argname] != 'on')
198 throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'parameter is not a string');
199 if (!$ok_if_empty and !strlen ($_REQUEST[$argname]))
200 throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'parameter is an empty string');
201 }
202
203 // function returns binary IP address, or throws an exception
204 function assertIPArg ($argname)
205 {
206 assertStringArg ($argname, FALSE);
207 try
208 {
209 return ip_parse ($_REQUEST[$argname]);
210 }
211 catch (InvalidArgException $e)
212 {
213 throw new InvalidRequestArgException ($argname, $_REQUEST[$argname], $e->getMessage());
214 }
215 }
216
217 // function returns binary IPv4 address, or throws an exception
218 function assertIPv4Arg ($argname)
219 {
220 try
221 {
222 return ip4_parse ($_REQUEST[$argname]);
223 }
224 catch (InvalidArgException $e)
225 {
226 throw new InvalidRequestArgException ($argname, $_REQUEST[$argname], $e->getMessage());
227 }
228 }
229
230 // function returns binary IPv6 address, or throws an exception
231 function assertIPv6Arg ($argname)
232 {
233 try
234 {
235 return ip6_parse ($_REQUEST[$argname]);
236 }
237 catch (InvalidArgException $e)
238 {
239 throw new InvalidRequestArgException ($argname, $_REQUEST[$argname], $e->getMessage());
240 }
241 }
242
243 function assertPCREArg ($argname)
244 {
245 assertStringArg ($argname, TRUE); // empty pattern is Ok
246 if (FALSE === preg_match ($_REQUEST[$argname], 'test'))
247 throw new InvalidRequestArgException($argname, $_REQUEST[$argname], 'PCRE validation failed');
248 }
249
250 function isPCRE ($arg)
251 {
252 if (! isset ($arg) or FALSE === @preg_match ($arg, 'test'))
253 return FALSE;
254 return TRUE;
255 }
256
257 function genericAssertion ($argname, $argtype)
258 {
259 global $sic;
260 switch ($argtype)
261 {
262 case 'string':
263 assertStringArg ($argname);
264 break;
265 case 'string0':
266 assertStringArg ($argname, TRUE);
267 break;
268 case 'uint':
269 assertUIntArg ($argname);
270 break;
271 case 'uint-uint':
272 assertStringArg ($argname);
273 if (1 != preg_match ('/^[1-9][0-9]*-[1-9][0-9]*$/', $_REQUEST[$argname]))
274 throw new InvalidRequestArgException ($argname, $_REQUEST[$argname], 'illegal format');
275 break;
276 case 'uint0':
277 assertUIntArg ($argname, TRUE);
278 break;
279 case 'inet':
280 assertIPArg ($argname);
281 break;
282 case 'inet4':
283 assertIPv4Arg ($argname);
284 break;
285 case 'inet6':
286 assertIPv6Arg ($argname);
287 break;
288 case 'l2address':
289 assertStringArg ($argname);
290 case 'l2address0':
291 assertStringArg ($argname, TRUE);
292 try
293 {
294 l2addressForDatabase ($sic[$argname]);
295 }
296 catch (InvalidArgException $e)
297 {
298 throw new InvalidRequestArgException ($argname, $sic[$argname], 'malformed MAC/WWN address');
299 }
300 break;
301 case 'tag':
302 assertStringArg ($argname);
303 if (!validTagName ($sic[$argname]))
304 throw new InvalidRequestArgException ($argname, $sic[$argname], 'Invalid tag name');
305 break;
306 case 'pcre':
307 assertPCREArg ($argname);
308 break;
309 case 'json':
310 assertStringArg ($argname);
311 if (NULL === json_decode ($sic[$argname], TRUE))
312 throw new InvalidRequestArgException ($argname, '(omitted)', 'Invalid JSON code received from client');
313 break;
314 case 'array':
315 if (! array_key_exists ($argname, $_REQUEST))
316 throw new InvalidRequestArgException ($argname, '(missing argument)');
317 if (! is_array ($_REQUEST[$argname]))
318 throw new InvalidRequestArgException ($argname, '(omitted)', 'argument is not an array');
319 break;
320 case 'enum/attr_type':
321 assertStringArg ($argname);
322 if (!in_array ($sic[$argname], array ('uint', 'float', 'string', 'dict','date')))
323 throw new InvalidRequestArgException ($argname, $sic[$argname], 'Unknown value');
324 break;
325 case 'enum/vlan_type':
326 assertStringArg ($argname);
327 // "Alien" type is not valid until the logic is fixed to implement it in full.
328 if (!in_array ($sic[$argname], array ('ondemand', 'compulsory')))
329 throw new InvalidRequestArgException ($argname, $sic[$argname], 'Unknown value');
330 break;
331 case 'enum/wdmstd':
332 assertStringArg ($argname);
333 global $wdm_packs;
334 if (! array_key_exists ($sic[$argname], $wdm_packs))
335 throw new InvalidRequestArgException ($argname, $sic[$argname], 'Unknown value');
336 break;
337 case 'enum/ipproto':
338 assertStringArg ($argname);
339 global $vs_proto;
340 if (!array_key_exists ($sic[$argname], $vs_proto))
341 throw new InvalidRequestArgException ($argname, $sic[$argname], 'Unknown value');
342 break;
343 case 'enum/alloc_type':
344 assertStringArg ($argname);
345 if (!in_array ($sic[$argname], array ('regular', 'shared', 'virtual', 'router')))
346 throw new InvalidRequestArgException ($argname, $sic[$argname], 'Unknown value');
347 break;
348 case 'enum/dqcode':
349 assertStringArg ($argname);
350 global $dqtitle;
351 if (! array_key_exists ($sic[$argname], $dqtitle))
352 throw new InvalidRequestArgException ($argname, $sic[$argname], 'Unknown value');
353 break;
354 case 'iif':
355 if (!array_key_exists ($sic[$argname], getPortIIFOptions()))
356 throw new InvalidRequestArgException ($argname, $sic[$argname], 'Unknown value');
357 break;
358 case 'vlan':
359 case 'vlan1':
360 genericAssertion ($argname, 'uint');
361 if ($argtype == 'vlan' and $sic[$argname] == VLAN_DFL_ID)
362 throw new InvalidRequestArgException ($argname, $sic[$argname], 'default VLAN cannot be changed');
363 if ($sic[$argname] > VLAN_MAX_ID or $sic[$argname] < VLAN_MIN_ID)
364 throw new InvalidRequestArgException ($argname, $sic[$argname], 'out of valid range');
365 break;
366 case 'rackcode/expr':
367 genericAssertion ($argname, 'string0');
368 if ($sic[$argname] == '')
369 return;
370 $parse = spotPayload ($sic[$argname], 'SYNT_EXPR');
371 if ($parse['result'] != 'ACK')
372 throw new InvalidRequestArgException ($argname, $sic[$argname], 'RackCode parsing error');
373 break;
374 default:
375 throw new InvalidArgException ('argtype', $argtype); // comes not from user's input
376 }
377 }
378
379 // return HTML form checkbox value (TRUE of FALSE) by name of its input control
380 function isCheckSet ($input_name, $mode = 'bool')
381 {
382 $value = isset ($_REQUEST[$input_name]) && $_REQUEST[$input_name] == 'on';
383 switch ($mode)
384 {
385 case 'bool' : return $value;
386 case 'yesno': return $value ? 'yes' : 'no';
387 default: throw new InvalidArgException ('mode', $mode);
388 }
389 }
390
391 // Validate and return "bypass" value for the current context, if one is
392 // defined for it, or NULL otherwise.
393 function getBypassValue()
394 {
395 global $page, $pageno, $sic;
396 if (!array_key_exists ('bypass', $page[$pageno]))
397 return NULL;
398 if (!array_key_exists ('bypass_type', $page[$pageno]))
399 throw new RackTablesError ("Internal structure error at node '${pageno}' (bypass_type is not set)", RackTablesError::INTERNAL);
400 genericAssertion ($page[$pageno]['bypass'], $page[$pageno]['bypass_type']);
401 return $sic[$page[$pageno]['bypass']];
402 }
403
404 // Objects of some types should be explicitly shown as
405 // anonymous (labelless). This function is a single place where the
406 // decision about displayed name is made.
407 function setDisplayedName (&$cell)
408 {
409 if ($cell['realm'] == 'object')
410 {
411 if ($cell['name'] != '')
412 $cell['dname'] = $cell['name'];
413 else
414 $cell['dname'] = '[' . decodeObjectType ($cell['objtype_id'], 'o') . ']';
415 }
416 elseif ($cell['realm'] == 'ipv4vs')
417 {
418 if ($cell['proto'] == 'MARK')
419 $cell['dname'] = "fwmark: " . implode ('', unpack('N', substr ($cell['vip_bin'], 0, 4)));
420 else
421 $cell['dname'] = $cell['vip'] . ':' . $cell['vport'] . '/' . $cell['proto'];
422 }
423 }
424
425 // This function finds height of solid rectangle of atoms, which are all
426 // assigned to the same object. Rectangle base is defined by specified
427 // template.
428 function rectHeight ($rackData, $startRow, $template_idx)
429 {
430 $height = 0;
431 // The first met object_id is used to match all the folowing IDs.
432 $object_id = 0;
433 global $template;
434 do
435 {
436 for ($locidx = 0; $locidx < 3; $locidx++)
437 {
438 // At least one value in template is TRUE, but the following block
439 // can meet 'skipped' atoms. Let's ensure we have something after processing
440 // the first row.
441 if ($template[$template_idx][$locidx])
442 {
443 if (isset ($rackData[$startRow - $height][$locidx]['skipped']))
444 break 2;
445 if (isset ($rackData[$startRow - $height][$locidx]['rowspan']))
446 break 2;
447 if (isset ($rackData[$startRow - $height][$locidx]['colspan']))
448 break 2;
449 if ($rackData[$startRow - $height][$locidx]['state'] != 'T')
450 break 2;
451 if ($object_id == 0)
452 $object_id = $rackData[$startRow - $height][$locidx]['object_id'];
453 if ($object_id != $rackData[$startRow - $height][$locidx]['object_id'])
454 break 2;
455 }
456 }
457 // If the first row can't offer anything, bail out.
458 if ($height == 0 and $object_id == 0)
459 break;
460 $height++;
461 }
462 while ($startRow - $height > 0);
463 # echo "for startRow==${startRow} and template==(" . ($template[$template_idx][0] ? 'T' : 'F');
464 # echo ', ' . ($template[$template_idx][1] ? 'T' : 'F') . ', ' . ($template[$template_idx][2] ? 'T' : 'F');
465 # echo ") height==${height}<br>\n";
466 return $height;
467 }
468
469 // This function marks atoms to be avoided by rectHeight() and assigns rowspan/colspan
470 // attributes.
471 function markSpan (&$rackData, $startRow, $maxheight, $template_idx)
472 {
473 global $template, $templateWidth;
474 $colspan = 0;
475 for ($height = 0; $height < $maxheight; $height++)
476 {
477 for ($locidx = 0; $locidx < 3; $locidx++)
478 {
479 if ($template[$template_idx][$locidx])
480 {
481 // Add colspan/rowspan to the first row met and mark the following ones to skip.
482 // Explicitly show even single-cell spanned atoms, because rectHeight()
483 // is expeciting this data for correct calculation.
484 if ($colspan != 0)
485 $rackData[$startRow - $height][$locidx]['skipped'] = TRUE;
486 else
487 {
488 $colspan = $templateWidth[$template_idx];
489 if ($colspan >= 1)
490 $rackData[$startRow - $height][$locidx]['colspan'] = $colspan;
491 if ($maxheight >= 1)
492 $rackData[$startRow - $height][$locidx]['rowspan'] = $maxheight;
493 }
494 }
495 }
496 }
497 return;
498 }
499
500 // This function sets rowspan/solspan/skipped atom attributes for renderRack()
501 // What we actually have to do is to find _all_ possible rectangles for each unit
502 // and then select the widest of those with the maximal square.
503 function markAllSpans (&$rackData)
504 {
505 for ($i = $rackData['height']; $i > 0; $i--)
506 while (markBestSpan ($rackData, $i));
507 }
508
509 // Calculate height of 6 possible span templates (array is presorted by width
510 // descending) and mark the best (if any).
511 function markBestSpan (&$rackData, $i)
512 {
513 global $template, $templateWidth;
514 for ($j = 0; $j < 6; $j++)
515 {
516 $height[$j] = rectHeight ($rackData, $i, $j);
517 $square[$j] = $height[$j] * $templateWidth[$j];
518 }
519 // find the widest rectangle of those with maximal height
520 $maxsquare = max ($square);
521 if (!$maxsquare)
522 return FALSE;
523 $best_template_index = 0;
524 for ($j = 0; $j < 6; $j++)
525 if ($square[$j] == $maxsquare)
526 {
527 $best_template_index = $j;
528 $bestheight = $height[$j];
529 break;
530 }
531 // distribute span marks
532 markSpan ($rackData, $i, $bestheight, $best_template_index);
533 return TRUE;
534 }
535
536 // We can mount 'F' atoms and unmount our own 'T' atoms.
537 function applyObjectMountMask (&$rackData, $object_id)
538 {
539 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
540 for ($locidx = 0; $locidx < 3; $locidx++)
541 switch ($rackData[$unit_no][$locidx]['state'])
542 {
543 case 'F':
544 $rackData[$unit_no][$locidx]['enabled'] = TRUE;
545 break;
546 case 'T':
547 $rackData[$unit_no][$locidx]['enabled'] = ($rackData[$unit_no][$locidx]['object_id'] == $object_id);
548 break;
549 default:
550 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
551 }
552 }
553
554 // Design change means transition between 'F' and 'A' and back.
555 function applyRackDesignMask (&$rackData)
556 {
557 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
558 for ($locidx = 0; $locidx < 3; $locidx++)
559 switch ($rackData[$unit_no][$locidx]['state'])
560 {
561 case 'F':
562 case 'A':
563 $rackData[$unit_no][$locidx]['enabled'] = TRUE;
564 break;
565 default:
566 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
567 }
568 }
569
570 // The same for 'F' and 'U'.
571 function applyRackProblemMask (&$rackData)
572 {
573 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
574 for ($locidx = 0; $locidx < 3; $locidx++)
575 switch ($rackData[$unit_no][$locidx]['state'])
576 {
577 case 'F':
578 case 'U':
579 $rackData[$unit_no][$locidx]['enabled'] = TRUE;
580 break;
581 default:
582 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
583 }
584 }
585
586 // This function highlights specified object (and removes previous highlight).
587 function highlightObject (&$rackData, $object_id)
588 {
589 // Also highlight parent objects
590 $parents = getEntityRelatives ('parents', 'object', $object_id);
591 $parent_ids = array();
592 foreach ($parents as $parent)
593 $parent_ids[] = $parent['entity_id'];
594
595 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
596 for ($locidx = 0; $locidx < 3; $locidx++)
597 if
598 (
599 $rackData[$unit_no][$locidx]['state'] == 'T' and
600 ($rackData[$unit_no][$locidx]['object_id'] == $object_id or in_array($rackData[$unit_no][$locidx]['object_id'], $parent_ids))
601 )
602 $rackData[$unit_no][$locidx]['hl'] = 'h';
603 else
604 unset ($rackData[$unit_no][$locidx]['hl']);
605 }
606
607 // This function marks atoms to selected or not depending on their current state.
608 function markupAtomGrid (&$data, $checked_state)
609 {
610 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
611 for ($locidx = 0; $locidx < 3; $locidx++)
612 {
613 if (!($data[$unit_no][$locidx]['enabled'] === TRUE))
614 continue;
615 if ($data[$unit_no][$locidx]['state'] == $checked_state)
616 $data[$unit_no][$locidx]['checked'] = ' checked';
617 else
618 $data[$unit_no][$locidx]['checked'] = '';
619 }
620 }
621
622 // This function is almost a clone of processGridForm(), but doesn't save anything to database
623 // Return value is the changed rack data.
624 // Here we assume that correct filter has already been applied, so we just
625 // set or unset checkbox inputs w/o changing atom state.
626 function mergeGridFormToRack (&$rackData)
627 {
628 $rack_id = $rackData['id'];
629 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
630 for ($locidx = 0; $locidx < 3; $locidx++)
631 {
632 if ($rackData[$unit_no][$locidx]['enabled'] != TRUE)
633 continue;
634 $inputname = "atom_${rack_id}_${unit_no}_${locidx}";
635 if (isset ($_REQUEST[$inputname]) and $_REQUEST[$inputname] == 'on')
636 $rackData[$unit_no][$locidx]['checked'] = ' checked';
637 else
638 $rackData[$unit_no][$locidx]['checked'] = '';
639 }
640 }
641
642 // wrapper around ip4_mask and ip6_mask
643 // netmask conversion from length to binary string
644 // v4/v6 mode is toggled by $is_ipv6 parameter
645 // Throws exception if $prefix_len is invalid
646 function ip_mask ($prefix_len, $is_ipv6)
647 {
648 if ($is_ipv6)
649 return ip6_mask ($prefix_len);
650 else
651 return ip4_mask ($prefix_len);
652 }
653
654 // netmask conversion from length to binary string
655 // Throws exception if $prefix_len is invalid
656 function ip4_mask ($prefix_len)
657 {
658 static $mask = array
659 (
660 "\x00\x00\x00\x00", // 0
661 "\x80\x00\x00\x00", // 1
662 "\xC0\x00\x00\x00", // 2
663 "\xE0\x00\x00\x00", // 3
664 "\xF0\x00\x00\x00", // 4
665 "\xF8\x00\x00\x00", // 5
666 "\xFC\x00\x00\x00", // 6
667 "\xFE\x00\x00\x00", // 7
668 "\xFF\x00\x00\x00", // 8
669 "\xFF\x80\x00\x00", // 9
670 "\xFF\xC0\x00\x00", // 10
671 "\xFF\xE0\x00\x00", // 11
672 "\xFF\xF0\x00\x00", // 12
673 "\xFF\xF8\x00\x00", // 13
674 "\xFF\xFC\x00\x00", // 14
675 "\xFF\xFE\x00\x00", // 15
676 "\xFF\xFF\x00\x00", // 16
677 "\xFF\xFF\x80\x00", // 17
678 "\xFF\xFF\xC0\x00", // 18
679 "\xFF\xFF\xE0\x00", // 19
680 "\xFF\xFF\xF0\x00", // 20
681 "\xFF\xFF\xF8\x00", // 21
682 "\xFF\xFF\xFC\x00", // 22
683 "\xFF\xFF\xFE\x00", // 23
684 "\xFF\xFF\xFF\x00", // 24
685 "\xFF\xFF\xFF\x80", // 25
686 "\xFF\xFF\xFF\xC0", // 26
687 "\xFF\xFF\xFF\xE0", // 27
688 "\xFF\xFF\xFF\xF0", // 28
689 "\xFF\xFF\xFF\xF8", // 29
690 "\xFF\xFF\xFF\xFC", // 30
691 "\xFF\xFF\xFF\xFE", // 31
692 "\xFF\xFF\xFF\xFF", // 32
693 );
694
695 if ($prefix_len >= 0 and $prefix_len <= 32)
696 return $mask[$prefix_len];
697 else
698 throw new InvalidArgException ('prefix_len', $prefix_len);
699 }
700
701 // netmask conversion from length to binary string
702 // Throws exception if $prefix_len is invalid
703 function ip6_mask ($prefix_len)
704 {
705 static $mask = array
706 (
707 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 0
708 "\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 1
709 "\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 2
710 "\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 3
711 "\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 4
712 "\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 5
713 "\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 6
714 "\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 7
715 "\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 8
716 "\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 9
717 "\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 10
718 "\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 11
719 "\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 12
720 "\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 13
721 "\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 14
722 "\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 15
723 "\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 16
724 "\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 17
725 "\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 18
726 "\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 19
727 "\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 20
728 "\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 21
729 "\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 22
730 "\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 23
731 "\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 24
732 "\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 25
733 "\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 26
734 "\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 27
735 "\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 28
736 "\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 29
737 "\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 30
738 "\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 31
739 "\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 32
740 "\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 33
741 "\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 34
742 "\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 35
743 "\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 36
744 "\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 37
745 "\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 38
746 "\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 39
747 "\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 40
748 "\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 41
749 "\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 42
750 "\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 43
751 "\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 44
752 "\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 45
753 "\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 46
754 "\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 47
755 "\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 48
756 "\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 49
757 "\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 50
758 "\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 51
759 "\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 52
760 "\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 53
761 "\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 54
762 "\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 55
763 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00", // 56
764 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00\x00", // 57
765 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00\x00", // 58
766 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00\x00", // 59
767 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00\x00", // 60
768 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00\x00", // 61
769 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00\x00", // 62
770 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00\x00", // 63
771 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00", // 64
772 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00\x00", // 65
773 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00\x00", // 66
774 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00\x00", // 67
775 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00\x00", // 68
776 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00\x00", // 69
777 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00\x00", // 70
778 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00\x00", // 71
779 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00", // 72
780 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00\x00", // 73
781 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00\x00", // 74
782 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00\x00", // 75
783 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00\x00", // 76
784 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00\x00", // 77
785 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00\x00", // 78
786 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00\x00", // 79
787 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00", // 80
788 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00\x00", // 81
789 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00\x00", // 82
790 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00\x00", // 83
791 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00\x00", // 84
792 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00\x00", // 85
793 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00\x00", // 86
794 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00\x00", // 87
795 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00", // 88
796 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00\x00", // 89
797 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00\x00", // 90
798 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00\x00", // 91
799 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00\x00", // 92
800 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00\x00", // 93
801 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00\x00", // 94
802 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00\x00", // 95
803 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00", // 96
804 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00\x00", // 97
805 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00\x00", // 98
806 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00\x00", // 99
807 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00\x00", // 100
808 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00\x00", // 101
809 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00\x00", // 102
810 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00\x00", // 103
811 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00", // 104
812 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00\x00", // 105
813 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00\x00", // 106
814 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00\x00", // 107
815 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00\x00", // 108
816 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00\x00", // 109
817 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00\x00", // 110
818 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00\x00", // 111
819 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00", // 112
820 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80\x00", // 113
821 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0\x00", // 114
822 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0\x00", // 115
823 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0\x00", // 116
824 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8\x00", // 117
825 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC\x00", // 118
826 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE\x00", // 119
827 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00", // 120
828 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x80", // 121
829 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xC0", // 122
830 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE0", // 123
831 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF0", // 124
832 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xF8", // 125
833 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFC", // 126
834 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFE", // 127
835 "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", // 128
836 );
837
838 if ($prefix_len >= 0 and $prefix_len <= 128)
839 return $mask[$prefix_len];
840 else
841 throw new InvalidArgException ('prefix_len', $prefix_len);
842 }
843
844 // This function looks up 'has_problems' flag for 'T' atoms
845 // and modifies 'hl' key. May be, this should be better done
846 // in amplifyCell(). We don't honour 'skipped' key, because
847 // the function is also used for thumb creation.
848 function markupObjectProblems (&$rackData)
849 {
850 for ($i = $rackData['height']; $i > 0; $i--)
851 for ($locidx = 0; $locidx < 3; $locidx++)
852 if ($rackData[$i][$locidx]['state'] == 'T')
853 {
854 $object = spotEntity ('object', $rackData[$i][$locidx]['object_id']);
855 if ($object['has_problems'] == 'yes')
856 {
857 // Object can be already highlighted.
858 if (isset ($rackData[$i][$locidx]['hl']))
859 $rackData[$i][$locidx]['hl'] = $rackData[$i][$locidx]['hl'] . 'w';
860 else
861 $rackData[$i][$locidx]['hl'] = 'w';
862 }
863 }
864 }
865
866 // Return a uniformly (010203040506 or 0102030405060708) formatted address, if it is present
867 // in the provided string, an empty string for an empty string or raise an exception.
868 function l2addressForDatabase ($string)
869 {
870 $string = strtoupper ($string);
871 switch (TRUE)
872 {
873 case ($string == '' or preg_match (RE_L2_SOLID, $string) or preg_match (RE_L2_WWN_SOLID, $string)):
874 return $string;
875 case (preg_match (RE_L2_IFCFG, $string) or preg_match (RE_L2_WWN_COLON, $string)):
876 // reformat output of SunOS ifconfig
877 $ret = '';
878 foreach (explode (':', $string) as $byte)
879 $ret .= (strlen ($byte) == 1 ? '0' : '') . $byte;
880 return $ret;
881 case (preg_match (RE_L2_CISCO, $string)):
882 return str_replace ('.', '', $string);
883 case (preg_match (RE_L2_HUAWEI, $string)):
884 return str_replace ('-', '', $string);
885 case (preg_match (RE_L2_IPCFG, $string) or preg_match (RE_L2_WWN_HYPHEN, $string)):
886 return str_replace ('-', '', $string);
887 default:
888 throw new InvalidArgException ('$string', $string, 'malformed MAC/WWN address');
889 }
890 }
891
892 function l2addressFromDatabase ($string)
893 {
894 switch (strlen ($string))
895 {
896 case 12: // Ethernet
897 case 16: // FireWire/Fibre Channel
898 $ret = implode (':', str_split ($string, 2));
899 break;
900 default:
901 $ret = $string;
902 break;
903 }
904 return $ret;
905 }
906
907 // The following 2 functions return previous and next rack IDs for
908 // a given rack ID. The order of racks is the same as in renderRackspace()
909 // or renderRow().
910 function getPrevIDforRack ($row_id, $rack_id)
911 {
912 $rackList = listCells ('rack', $row_id);
913 doubleLink ($rackList);
914 if (isset ($rackList[$rack_id]['prev_key']))
915 return $rackList[$rack_id]['prev_key'];
916 return NULL;
917 }
918
919 function getNextIDforRack ($row_id, $rack_id)
920 {
921 $rackList = listCells ('rack', $row_id);
922 doubleLink ($rackList);
923 if (isset ($rackList[$rack_id]['next_key']))
924 return $rackList[$rack_id]['next_key'];
925 return NULL;
926 }
927
928 // This function finds previous and next array keys for each array key and
929 // modifies its argument accordingly.
930 function doubleLink (&$array)
931 {
932 $prev_key = NULL;
933 foreach (array_keys ($array) as $key)
934 {
935 if ($prev_key)
936 {
937 $array[$key]['prev_key'] = $prev_key;
938 $array[$prev_key]['next_key'] = $key;
939 }
940 $prev_key = $key;
941 }
942 }
943
944 function sortTokenize ($a, $b)
945 {
946 $aold='';
947 while ($a != $aold)
948 {
949 $aold=$a;
950 $a = preg_replace('/[^a-zA-Z0-9]/',' ',$a);
951 $a = preg_replace('/([0-9])([a-zA-Z])/','\\1 \\2',$a);
952 $a = preg_replace('/([a-zA-Z])([0-9])/','\\1 \\2',$a);
953 }
954
955 $bold='';
956 while ($b != $bold)
957 {
958 $bold=$b;
959 $b = preg_replace('/[^a-zA-Z0-9]/',' ',$b);
960 $b = preg_replace('/([0-9])([a-zA-Z])/','\\1 \\2',$b);
961 $b = preg_replace('/([a-zA-Z])([0-9])/','\\1 \\2',$b);
962 }
963
964
965
966 $ar = explode(' ', $a);
967 $br = explode(' ', $b);
968 for ($i=0; $i<count($ar) && $i<count($br); $i++)
969 {
970 $ret = 0;
971 if (is_numeric($ar[$i]) and is_numeric($br[$i]))
972 $ret = ($ar[$i]==$br[$i])?0:($ar[$i]<$br[$i]?-1:1);
973 else
974 $ret = strcasecmp($ar[$i], $br[$i]);
975 if ($ret != 0)
976 return $ret;
977 }
978 if ($i<count($ar))
979 return 1;
980 if ($i<count($br))
981 return -1;
982 return 0;
983 }
984
985 // This function returns an array of single element of object's FQDN attribute,
986 // if FQDN is set. The next choice is object's common name, if it looks like a
987 // hostname. Otherwise an array of all 'regular' IP addresses of the
988 // object is returned (which may appear 0 and more elements long).
989 function findAllEndpoints ($object_id, $fallback = '')
990 {
991 foreach (getAttrValues ($object_id) as $record)
992 if ($record['id'] == 3 && strlen ($record['value'])) // FQDN
993 return array ($record['value']);
994 $regular = array();
995 foreach (getObjectIPv4AllocationList ($object_id) as $ip_bin => $alloc)
996 if ($alloc['type'] == 'regular')
997 $regular[] = ip4_format ($ip_bin);
998 // FIXME: add IPv6 allocations to this list
999 if (!count ($regular) && strlen ($fallback))
1000 return array ($fallback);
1001 return $regular;
1002 }
1003
1004 // Some records in the dictionary may be written as plain text or as Wiki
1005 // link in the following syntax:
1006 // 1. word
1007 // 2. [[word URL]] // FIXME: this isn't working
1008 // 3. [[word word word | URL]]
1009 // This function parses the line in $record['value'] and modifies $record:
1010 // $record['o_value'] is set to be the first part of link (word word word)
1011 // $record['a_value'] is the same, but with %GPASS and %GSKIP macros applied
1012 // $record['href'] is set to URL if it is specified in the input value
1013 function parseWikiLink (&$record)
1014 {
1015 if (! preg_match ('/^\[\[(.+)\]\]$/', $record['value'], $matches))
1016 $record['o_value'] = $record['value'];
1017 else
1018 {
1019 $s = explode ('|', $matches[1]);
1020 if (isset ($s[1]))
1021 $record['href'] = trim ($s[1]);
1022 $record['o_value'] = trim ($s[0]);
1023 }
1024 $record['a_value'] = execGMarker ($record['o_value']);
1025 }
1026
1027 // FIXME: should this be saved as "P-data"?
1028 function execGMarker ($line)
1029 {
1030 return preg_replace ('/^.+%GSKIP%/', '', preg_replace ('/^(.+)%GPASS%/', '\\1 ', $line));
1031 }
1032
1033 // rackspace usage for a single rack
1034 // (T + W + U) / (height * 3 - A)
1035 function getRSUforRack ($data)
1036 {
1037 $counter = array ('A' => 0, 'U' => 0, 'T' => 0, 'W' => 0, 'F' => 0);
1038 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
1039 for ($locidx = 0; $locidx < 3; $locidx++)
1040 $counter[$data[$unit_no][$locidx]['state']]++;
1041 return ($counter['T'] + $counter['W'] + $counter['U']) / ($counter['T'] + $counter['W'] + $counter['U'] + $counter['F']);
1042 }
1043
1044 // Same for row.
1045 function getRSUforRow ($rowData)
1046 {
1047 if (!count ($rowData))
1048 return 0;
1049 $counter = array ('A' => 0, 'U' => 0, 'T' => 0, 'W' => 0, 'F' => 0);
1050 $total_height = 0;
1051 foreach (array_keys ($rowData) as $rack_id)
1052 {
1053 $data = spotEntity ('rack', $rack_id);
1054 amplifyCell ($data);
1055 $total_height += $data['height'];
1056 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
1057 for ($locidx = 0; $locidx < 3; $locidx++)
1058 $counter[$data[$unit_no][$locidx]['state']]++;
1059 }
1060 return ($counter['T'] + $counter['W'] + $counter['U']) / ($counter['T'] + $counter['W'] + $counter['U'] + $counter['F']);
1061 }
1062
1063 // Find any URL in a string and replace it with a clickable link
1064 // Adopted from UrlLinker: https://bitbucket.org/kwi/urllinker/src
1065 function string_insert_hrefs ($s)
1066 {
1067 if (getConfigVar ('DETECT_URLS') != 'yes')
1068 return $s;
1069
1070 $rexProtocol = '(https?://)?';
1071 $rexDomain = '(?:[-a-zA-Z0-9]{1,63}\.)+[a-zA-Z][-a-zA-Z0-9]{1,62}';
1072 $rexIp = '(?:[1-9][0-9]{0,2}\.|0\.){3}(?:[1-9][0-9]{0,2}|0)'; // doesn't support IPv6 addresses
1073 $rexPort = '(:[0-9]{1,5})?';
1074 $rexPath = '(/[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]*?)?';
1075 $rexQuery = '(\?[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]+?)?';
1076 $rexFragment = '(#[!$-/0-9:;=@_\':;!a-zA-Z\x7f-\xff]+?)?';
1077 $rexUsername = '[^]\\\\\x00-\x20\"(),:-<>[\x7f-\xff]{1,64}';
1078 $rexPassword = $rexUsername; // allow the same characters as in the username
1079 $rexUrl = "$rexProtocol(?:($rexUsername)(:$rexPassword)?@)?($rexDomain|$rexIp)($rexPort$rexPath$rexQuery$rexFragment)";
1080 $rexUrlLinker = "{\\b$rexUrl(?=[?.!,;:\"]?(\s|$))}";
1081
1082 $html = '';
1083 $position = 0;
1084 while (preg_match($rexUrlLinker, $s, $match, PREG_OFFSET_CAPTURE, $position))
1085 {
1086 list($url, $urlPosition) = $match[0];
1087
1088 // Add the text leading up to the URL.
1089 $html .= substr($s, $position, $urlPosition - $position);
1090
1091 $protocol = $match[1][0];
1092 $username = $match[2][0];
1093 $password = $match[3][0];
1094 $domain = $match[4][0];
1095 $afterDomain = $match[5][0]; // everything following the domain
1096 $port = $match[6][0];
1097 $path = $match[7][0];
1098
1099 // Do not permit implicit protocol if a password is specified, as
1100 // this causes too many errors (e.g. "my email:foo@example.org").
1101 if (!$protocol && $password)
1102 {
1103 $html .= htmlspecialchars($username);
1104
1105 // Continue text parsing at the ':' following the "username".
1106 $position = $urlPosition + strlen($username);
1107 continue;
1108 }
1109
1110 if (!$protocol && $username && !$password && !$afterDomain)
1111 {
1112 // Looks like an email address.
1113 $emailUrl = TRUE;
1114 $completeUrl = "mailto:$url";
1115 $linkText = $url;
1116 }
1117 else
1118 {
1119 // Prepend http:// if no protocol specified
1120 $completeUrl = $protocol ? $url : "http://$url";
1121 $linkText = "$protocol$domain$port$path";
1122 }
1123
1124 $linkHtml = '<a href="' . htmlspecialchars($completeUrl) . '">'
1125 . htmlspecialchars($linkText)
1126 . '</a>';
1127
1128 // It's not an e-mail address, provide an additional link with a new window/tab as the target
1129 if (!isset($emailUrl))
1130 $linkHtml .= ' [<a href="' . htmlspecialchars($completeUrl) . '" target="_blank">^</a>]';
1131 unset($emailUrl);
1132
1133 // Add the hyperlink.
1134 $html .= $linkHtml;
1135
1136 // Continue text parsing from after the URL.
1137 $position = $urlPosition + strlen($url);
1138 }
1139
1140 // Add the remainder of the text.
1141 $html .= substr($s, $position);
1142 return $html;
1143 }
1144
1145 // Parse AUTOPORTS_CONFIG and return a list of generated pairs (port_type, port_name)
1146 // for the requested object_type_id.
1147 function getAutoPorts ($type_id)
1148 {
1149 $ret = array();
1150 $typemap = explode (';', str_replace (' ', '', getConfigVar ('AUTOPORTS_CONFIG')));
1151 foreach ($typemap as $equation)
1152 {
1153 $tmp = explode ('=', $equation);
1154 if (count ($tmp) != 2)
1155 continue;
1156 $objtype_id = $tmp[0];
1157 if ($objtype_id != $type_id)
1158 continue;
1159 $portlist = $tmp[1];
1160 foreach (explode ('+', $portlist) as $product)
1161 {
1162 $tmp = explode ('*', $product);
1163 if (count ($tmp) > 4 || count ($tmp) < 3)
1164 continue;
1165 # format: <number of ports>*<port_type_id>[*<sprintf_name>*<startnumber>]
1166 $nports = $tmp[0];
1167 $port_type = $tmp[1];
1168 $format = $tmp[2];
1169 $startnum = isset ($tmp[3]) ? $tmp[3] : 0;
1170 for ($i = 0; $i < $nports; $i++)
1171 $ret[] = array ('type' => $port_type, 'name' => @sprintf ($format, $i + $startnum));
1172 }
1173 }
1174 return $ret;
1175 }
1176
1177 // Use pre-served trace to traverse the tree, then place given node where it belongs.
1178 function pokeNode (&$tree, $trace, $key, $value, $threshold = 0)
1179 {
1180 // This function needs the trace to be followed FIFO-way. The fastest
1181 // way to do so is to use array_push() for putting values into the
1182 // list and array_shift() for getting them out. This exposed up to 11%
1183 // performance gain compared to other patterns of array_push/array_unshift/
1184 // array_reverse/array_pop/array_shift conjunction.
1185 $myid = array_shift ($trace);
1186 if (!count ($trace)) // reached the target
1187 {
1188 if (!$threshold or ($threshold and $tree[$myid]['kidc'] + 1 < $threshold))
1189 $tree[$myid]['kids'][$key] = $value;
1190 // Reset accumulated records once, when the limit is reached, not each time
1191 // after that.
1192 if (++$tree[$myid]['kidc'] == $threshold)
1193 $tree[$myid]['kids'] = array();
1194 }
1195 else // not yet
1196 {
1197 $self = __FUNCTION__;
1198 $self ($tree[$myid]['kids'], $trace, $key, $value, $threshold);
1199 }
1200 }
1201
1202 // Likewise traverse the tree with the trace and return the final node.
1203 function peekNode ($tree, $trace, $target_id)
1204 {
1205 $self = __FUNCTION__;
1206 if (NULL === ($next = array_shift ($trace))) // warm
1207 {
1208 foreach ($tree as $node)
1209 if (array_key_exists ('id', $node) and $node['id'] == $target_id) // hot
1210 return $node;
1211 }
1212 else // cold
1213 {
1214 foreach ($tree as $node)
1215 if (array_key_exists ('id', $node) and $node['id'] == $next) // warmer
1216 return $self ($node['kids'], $trace, $target_id);
1217 }
1218 throw new RackTablesError ('inconsistent tree data', RackTablesError::INTERNAL);
1219 }
1220
1221 function treeItemCmp ($a, $b)
1222 {
1223 return $a['__tree_index'] - $b['__tree_index'];
1224 }
1225
1226 // Build a tree from the item list and return it. Input and output data is
1227 // indexed by item id (nested items in output are recursively stored in 'kids'
1228 // key, which is in turn indexed by id. Functions, which are ready to handle
1229 // tree collapsion/expansion themselves, may request non-zero threshold value
1230 // for smaller resulting tree.
1231 function treeFromList (&$orig_nodelist, $threshold = 0, $return_main_payload = TRUE)
1232 {
1233 $tree = array();
1234 $nodelist = $orig_nodelist;
1235
1236 // index the tree items by their order in $orig_nodelist
1237 $ti = 0;
1238 foreach ($nodelist as &$node)
1239 $node['__tree_index'] = $ti++;
1240
1241 // Array equivalent of traceEntity() function.
1242 $trace = array();
1243 // set kidc and kids only once
1244 foreach (array_keys ($nodelist) as $nodeid)
1245 {
1246 $nodelist[$nodeid]['kidc'] = 0;
1247 $nodelist[$nodeid]['kids'] = array();
1248 }
1249 do
1250 {
1251 $nextpass = FALSE;
1252 foreach (array_keys ($nodelist) as $nodeid)
1253 {
1254 // When adding a node to the working tree, book another
1255 // iteration, because the new item could make a way for
1256 // others onto the tree. Also remove any item added from
1257 // the input list, so iteration base shrinks.
1258 // First check if we can assign directly.
1259 if ($nodelist[$nodeid]['parent_id'] == NULL)
1260 {
1261 $tree[$nodeid] = $nodelist[$nodeid];
1262 $trace[$nodeid] = array(); // Trace to root node is empty
1263 unset ($nodelist[$nodeid]);
1264 $nextpass = TRUE;
1265 }
1266 // Now look if it fits somewhere on already built tree.
1267 elseif (isset ($trace[$nodelist[$nodeid]['parent_id']]))
1268 {
1269 // Trace to a node is a trace to its parent plus parent id.
1270 $trace[$nodeid] = $trace[$nodelist[$nodeid]['parent_id']];
1271 $trace[$nodeid][] = $nodelist[$nodeid]['parent_id'];
1272 pokeNode ($tree, $trace[$nodeid], $nodeid, $nodelist[$nodeid], $threshold);
1273 // path to any other node is made of all parent nodes plus the added node itself
1274 unset ($nodelist[$nodeid]);
1275 $nextpass = TRUE;
1276 }
1277 }
1278 }
1279 while ($nextpass);
1280 if (!$return_main_payload)
1281 return $nodelist;
1282 // update each input node with its backtrace route
1283 foreach ($trace as $nodeid => $route)
1284 $orig_nodelist[$nodeid]['trace'] = $route;
1285 sortTree ($tree, 'treeItemCmp'); // sort the resulting tree by the order in original list
1286 return $tree;
1287 }
1288
1289 // Build a tree from the tag list and return everything _except_ the tree.
1290 // IOW, return taginfo items, which have parent_id set and pointing outside
1291 // of the "normal" tree, which originates from the root.
1292 function getOrphanedTags ()
1293 {
1294 global $taglist;
1295 return treeFromList ($taglist, 0, FALSE);
1296 }
1297
1298 // Return the list of missing implicit tags.
1299 function getImplicitTags ($oldtags)
1300 {
1301 global $taglist;
1302 $tmp = array();
1303 foreach ($oldtags as $taginfo)
1304 $tmp = array_merge ($tmp, $taglist[$taginfo['id']]['trace']);
1305 // don't call array_unique here, it is in the function we will call now
1306 return buildTagChainFromIds ($tmp);
1307 }
1308
1309 // Minimize the chain: exclude all implicit tags and return the result.
1310 // This function makes use of an external cache with a miss/hit ratio
1311 // about 3/7 (ticket:255).
1312 function getExplicitTagsOnly ($chain)
1313 {
1314 global $taglist, $tagRelCache;
1315 $ret = array();
1316 foreach (array_keys ($chain) as $keyA) // check each A
1317 {
1318 $tagidA = $chain[$keyA]['id'];
1319 // do not include A in result, if A is seen on the trace of any B!=A
1320 foreach (array_keys ($chain) as $keyB)
1321 {
1322 $tagidB = $chain[$keyB]['id'];
1323 if ($tagidA == $tagidB)
1324 continue;
1325 if (!isset ($tagRelCache[$tagidA][$tagidB]))
1326 $tagRelCache[$tagidA][$tagidB] = in_array ($tagidA, $taglist[$tagidB]['trace']);
1327 if ($tagRelCache[$tagidA][$tagidB] === TRUE) // A is ancestor of B
1328 continue 2; // skip this A
1329 }
1330 $ret[] = $chain[$keyA];
1331 }
1332 return $ret;
1333 }
1334
1335 // Check, if the given tag is present on the chain (will only work
1336 // for regular tags with tag ID set.
1337 function tagOnChain ($taginfo, $tagchain)
1338 {
1339 if (!isset ($taginfo['id']))
1340 return FALSE;
1341 foreach ($tagchain as $test)
1342 if ($test['id'] == $taginfo['id'])
1343 return TRUE;
1344 return FALSE;
1345 }
1346
1347 function tagNameOnChain ($tagname, $tagchain)
1348 {
1349 foreach ($tagchain as $test)
1350 if ($test['tag'] == $tagname)
1351 return TRUE;
1352 return FALSE;
1353 }
1354
1355 // Return TRUE, if two tags chains differ (order of tags doesn't matter).
1356 // Assume, that neither of the lists contains duplicates.
1357 // FIXME: a faster, than O(x^2) method is possible for this calculation.
1358 function tagChainCmp ($chain1, $chain2)
1359 {
1360 if (count ($chain1) != count ($chain2))
1361 return TRUE;
1362 foreach ($chain1 as $taginfo1)
1363 if (!tagOnChain ($taginfo1, $chain2))
1364 return TRUE;
1365 return FALSE;
1366 }
1367
1368 function redirectIfNecessary ()
1369 {
1370 global
1371 $trigger,
1372 $pageno,
1373 $tabno;
1374 @session_start();
1375 if
1376 (
1377 ! isset ($_REQUEST['tab']) and
1378 isset ($_SESSION['RTLT'][$pageno]) and
1379 getConfigVar ('SHOW_LAST_TAB') == 'yes' and
1380 permitted ($pageno, $_SESSION['RTLT'][$pageno]['tabname']) and
1381 time() - $_SESSION['RTLT'][$pageno]['time'] <= TAB_REMEMBER_TIMEOUT
1382 )
1383 redirectUser (buildRedirectURL ($pageno, $_SESSION['RTLT'][$pageno]['tabname']));
1384
1385 // check if we accidentaly got on a dynamic tab that shouldn't be shown for this object
1386 if
1387 (
1388 isset ($trigger[$pageno][$tabno]) and
1389 !strlen (call_user_func ($trigger[$pageno][$tabno]))
1390 )
1391 {
1392 $_SESSION['RTLT'][$pageno]['dont_remember'] = 1;
1393 redirectUser (buildRedirectURL ($pageno, 'default'));
1394 }
1395 if (isset ($_SESSION['RTLT'][$pageno]['dont_remember']) && is_array ($_SESSION['RTLT'][$pageno]))
1396 unset ($_SESSION['RTLT'][$pageno]['dont_remember']);
1397 // store the last visited tab name
1398 if (isset ($_REQUEST['tab']))
1399 $_SESSION['RTLT'][$pageno] = array ('tabname' => $tabno, 'time' => time());
1400 session_commit(); // if we are continuing to run, unlock session data
1401 }
1402
1403 function prepareNavigation()
1404 {
1405 global
1406 $pageno,
1407 $tabno;
1408 $pageno = (isset ($_REQUEST['page'])) ? $_REQUEST['page'] : 'index';
1409
1410 if (isset ($_REQUEST['tab']))
1411 $tabno = $_REQUEST['tab'];
1412 else
1413 $tabno = 'default';
1414 }
1415
1416 function fixContext ($target = NULL)
1417 {
1418 global
1419 $pageno,
1420 $auto_tags,
1421 $expl_tags,
1422 $impl_tags,
1423 $target_given_tags,
1424 $user_given_tags,
1425 $etype_by_pageno,
1426 $page;
1427
1428 if ($target !== NULL)
1429 {
1430 $target_given_tags = $target['etags'];
1431 // Don't reset autochain, because auth procedures could push stuff there in.
1432 // Another important point is to ignore 'user' realm, so we don't infuse effective
1433 // context with autotags of the displayed account.
1434 if ($target['realm'] != 'user')
1435 $auto_tags = array_merge ($auto_tags, $target['atags']);
1436 }
1437 elseif (array_key_exists ($pageno, $etype_by_pageno))
1438 {
1439 // Each page listed in the map above requires one uint argument.
1440 $target_realm = $etype_by_pageno[$pageno];
1441 assertUIntArg ($page[$pageno]['bypass']);
1442 $target_id = $_REQUEST[$page[$pageno]['bypass']];
1443 $target = spotEntity ($target_realm, $target_id);
1444 $target_given_tags = $target['etags'];
1445 if ($target['realm'] != 'user')
1446 $auto_tags = array_merge ($auto_tags, $target['atags']);
1447 }
1448 // Explicit and implicit chains should be normally empty at this point, so
1449 // overwrite the contents anyway.
1450 $expl_tags = mergeTagChains ($user_given_tags, $target_given_tags);
1451 $impl_tags = getImplicitTags ($expl_tags);
1452 }
1453
1454 # Merge e/i/a-tags of the given cell structures into current context, when
1455 # these aren't there yet.
1456 function spreadContext ($extracell)
1457 {
1458 global
1459 $auto_tags,
1460 $expl_tags,
1461 $impl_tags,
1462 $target_given_tags,
1463 $user_given_tags;
1464 foreach ($extracell['atags'] as $taginfo)
1465 if (! tagNameOnChain ($taginfo['tag'], $auto_tags))
1466 $auto_tags[] = $taginfo;
1467 $target_given_tags = mergeTagChains ($target_given_tags, $extracell['etags']);
1468 $expl_tags = mergeTagChains ($user_given_tags, $target_given_tags);
1469 $impl_tags = getImplicitTags ($expl_tags);
1470 }
1471
1472 # return a structure suitable for feeding into restoreContext()
1473 function getContext()
1474 {
1475 global
1476 $auto_tags,
1477 $expl_tags,
1478 $impl_tags,
1479 $target_given_tags;
1480 return array
1481 (
1482 'auto_tags' => $auto_tags,
1483 'expl_tags' => $expl_tags,
1484 'impl_tags' => $impl_tags,
1485 'target_given_tags' => $target_given_tags,
1486 );
1487 }
1488
1489 function restoreContext ($ctx)
1490 {
1491 global
1492 $auto_tags,
1493 $expl_tags,
1494 $impl_tags,
1495 $target_given_tags;
1496 $auto_tags = $ctx['auto_tags'];
1497 $expl_tags = $ctx['expl_tags'];
1498 $impl_tags = $ctx['impl_tags'];
1499 $target_given_tags = $ctx['target_given_tags'];
1500 }
1501
1502 // Take a list of user-supplied tag IDs to build a list of valid taginfo
1503 // records indexed by tag IDs (tag chain).
1504 function buildTagChainFromIds ($tagidlist)
1505 {
1506 global $taglist;
1507 $ret = array();
1508 foreach (array_unique ($tagidlist) as $tag_id)
1509 if (isset ($taglist[$tag_id]))
1510 $ret[] = $taglist[$tag_id];
1511 return $ret;
1512 }
1513
1514 function buildTagIdsFromChain ($tagchain)
1515 {
1516 $ret = array();
1517 foreach ($tagchain as $taginfo)
1518 $ret[] = $taginfo['id'];
1519 return array_unique ($ret);
1520 }
1521
1522 // Process a given tag tree and return only meaningful branches. The resulting
1523 // (sub)tree will have refcnt leaves on every last branch.
1524 function getObjectiveTagTree ($tree, $realm, $preselect)
1525 {
1526 $self = __FUNCTION__;
1527 $ret = array();
1528 foreach ($tree as $taginfo)
1529 {
1530 $subsearch = $self ($taginfo['kids'], $realm, $preselect);
1531 // If the current node addresses something, add it to the result
1532 // regardless of how many sub-nodes it features.
1533 if
1534 (
1535 isset ($taginfo['refcnt'][$realm]) or
1536 count ($subsearch) > 1 or
1537 in_array ($taginfo['id'], $preselect)
1538 )
1539 $ret[] = array
1540 (
1541 'id' => $taginfo['id'],
1542 'tag' => $taginfo['tag'],
1543 'parent_id' => $taginfo['parent_id'],
1544 'refcnt' => $taginfo['refcnt'],
1545 'kids' => $subsearch
1546 );
1547 else
1548 $ret = array_merge ($ret, $subsearch);
1549 }
1550 return $ret;
1551 }
1552
1553 // Preprocess tag tree to get only tags which can effectively reduce given filter result,
1554 // than passes shrinked tag tree to getObjectiveTagTree and return its result.
1555 // This makes sense only if andor mode is 'and', otherwise function does not modify tree.
1556 // 'Given filter' is a pair of $entity_list(filter result) and $preselect(filter data).
1557 // 'Effectively' means reduce to non-empty result.
1558 function getShrinkedTagTree($entity_list, $realm, $preselect) {
1559 global $tagtree;
1560 if ($preselect['andor'] != 'and' || empty($entity_list) && $preselect['is_empty'])
1561 return getObjectiveTagTree($tagtree, $realm, $preselect['tagidlist']);
1562
1563 $used_tags = array(); //associative, keys - tag ids, values - taginfos
1564 foreach ($entity_list as $entity)
1565 {
1566 foreach ($entity['etags'] as $etag)
1567 if (! array_key_exists($etag['id'], $used_tags))
1568 $used_tags[$etag['id']] = 1;
1569 else
1570 $used_tags[$etag['id']]++;
1571
1572 foreach ($entity['itags'] as $itag)
1573 if (! array_key_exists($itag['id'], $used_tags))
1574 $used_tags[$itag['id']] = 0;
1575 }
1576
1577 $shrinked_tree = shrinkSubtree($tagtree, $used_tags, $preselect, $realm);
1578 return getObjectiveTagTree($shrinked_tree, $realm, $preselect['tagidlist']);
1579 }
1580
1581 // deletes item from tag subtree unless it exists in $used_tags and not preselected
1582 function shrinkSubtree($tree, $used_tags, $preselect, $realm) {
1583 $self = __FUNCTION__;
1584
1585 foreach($tree as $i => &$item) {
1586 $item['kids'] = $self($item['kids'], $used_tags, $preselect, $realm);
1587 $item['kidc'] = count($item['kids']);
1588 if
1589 (
1590 ! array_key_exists($item['id'], $used_tags) &&
1591 ! in_array($item['id'], $preselect['tagidlist']) &&
1592 ! $item['kidc']
1593 )
1594 unset($tree[$i]);
1595 else {
1596 if (isset ($used_tags[$item['id']]) && $used_tags[$item['id']])
1597 $item['refcnt'][$realm] = $used_tags[$item['id']];
1598 else
1599 unset($item['refcnt'][$realm]);
1600 }
1601 }
1602 return $tree;
1603 }
1604
1605 // Get taginfo record by tag name, return NULL, if record doesn't exist.
1606 function getTagByName ($target_name)
1607 {
1608 global $taglist;
1609 foreach ($taglist as $taginfo)
1610 if ($taginfo['tag'] == $target_name)
1611 return $taginfo;
1612 return NULL;
1613 }
1614
1615 // Merge two chains, filtering dupes out. Return the resulting superset.
1616 function mergeTagChains ($chainA, $chainB)
1617 {
1618 // $ret = $chainA;
1619 // Reindex by tag id in any case.
1620 $ret = array();
1621 foreach ($chainA as $tag)
1622 $ret[$tag['id']] = $tag;
1623 foreach ($chainB as $tag)
1624 if (!isset ($ret[$tag['id']]))
1625 $ret[$tag['id']] = $tag;
1626 return $ret;
1627 }
1628
1629 # Return a list consisting of tag ID of the given tree node and IDs of all
1630 # nodes it contains.
1631 function getTagIDListForNode ($treenode)
1632 {
1633 $self = __FUNCTION__;
1634 $ret = array ($treenode['id']);
1635 foreach ($treenode['kids'] as $item)
1636 $ret = array_merge ($ret, $self ($item));
1637 return $ret;
1638 }
1639
1640 function getCellFilter ()
1641 {
1642 global $sic;
1643 global $pageno;
1644 $andor_used = FALSE;
1645 @session_start();
1646 // if the page is submitted we get an andor value so we know they are trying to start a new filter or clearing the existing one.
1647 if (isset($_REQUEST['andor']))
1648 $andor_used = TRUE;
1649 if ($andor_used || array_key_exists ('clear-cf', $_REQUEST))
1650 unset($_SESSION[$pageno]); // delete saved filter
1651
1652 // otherwise inject saved filter to the $_REQUEST and $sic vars
1653 elseif (isset ($_SESSION[$pageno]['filter']) and is_array ($_SESSION[$pageno]['filter']) and getConfigVar ('STATIC_FILTER') == 'yes')
1654 foreach (array('andor', 'cfe', 'cft[]', 'cfp[]', 'nft[]', 'nfp[]') as $param)
1655 {
1656 $param = str_replace ('[]', '', $param, $is_array);
1657 if (! isset ($_REQUEST[$param]) and isset ($_SESSION[$pageno]['filter'][$param]) and (!$is_array or is_array ($_SESSION[$pageno]['filter'][$param])))
1658 {
1659 $_REQUEST[$param] = $_SESSION[$pageno]['filter'][$param];
1660 if (! $is_array)
1661 $sic[$param] = $_REQUEST[$param];
1662 }
1663 }
1664
1665 $ret = array
1666 (
1667 'tagidlist' => array(),
1668 'tnamelist' => array(),
1669 'pnamelist' => array(),
1670 'negatedlist' => array(),
1671 'andor' => '',
1672 'text' => '',
1673 'extratext' => '',
1674 'expression' => array(),
1675 'urlextra' => '', // Just put text here and let makeHref call urlencode().
1676 'is_empty' => TRUE,
1677 );
1678 switch (TRUE)
1679 {
1680 case (!isset ($_REQUEST['andor'])):
1681 $andor = getConfigVar ('FILTER_DEFAULT_ANDOR');
1682 break;
1683 case ($_REQUEST['andor'] == 'and'):
1684 case ($_REQUEST['andor'] == 'or'):
1685 $_SESSION[$pageno]['filter']['andor'] = $_REQUEST['andor'];
1686 $ret['andor'] = $andor = $_REQUEST['andor'];
1687 break;
1688 default:
1689 showWarning ('Invalid and/or switch value in submitted form');
1690 return NULL;
1691 }
1692 // Both tags and predicates, which don't exist, should be
1693 // handled somehow. Discard them silently for now.
1694 global $taglist, $pTable;
1695 foreach (array ('cft', 'cfp', 'nft', 'nfp') as $param)
1696 if (isset ($_REQUEST[$param]) and is_array ($_REQUEST[$param]))
1697 {
1698 $_SESSION[$pageno]['filter'][$param] = $_REQUEST[$param];
1699 foreach ($_REQUEST[$param] as $req_key)
1700 {
1701 if (strpos ($param, 'ft') !== FALSE)
1702 {
1703 // param is a taglist
1704 if (! isset ($taglist[$req_key]))
1705 continue;
1706 $ret['tagidlist'][] = $req_key;
1707 $ret['tnamelist'][] = $taglist[$req_key]['tag'];
1708 $text = '{' . $taglist[$req_key]['tag'] . '}';
1709 }
1710 else
1711 {
1712 // param is a predicate list
1713 if (! isset ($pTable[$req_key]))
1714 continue;
1715 $ret['pnamelist'][] = $req_key;
1716 $text = '[' . $req_key . ']';
1717 }
1718 if (strpos ($param, 'nf') === 0)
1719 {
1720 $text = "not $text";
1721 $ret['negatedlist'][] = $req_key;
1722 }
1723 if (! empty ($ret['text']))
1724 {
1725 $andor_used = TRUE;
1726 $ret['text'] .= " $andor ";
1727 }
1728 $ret['text'] .= $text;
1729 $ret['urlextra'] .= '&' . $param . '[]=' . $req_key;
1730 }
1731 }
1732 // Extra text comes from TEXTAREA and is easily screwed by standard escaping function.
1733 if (isset ($sic['cfe']))
1734 {
1735 $_SESSION[$pageno]['filter']['cfe'] = $sic['cfe'];
1736 // Only consider extra text, when it is a correct RackCode expression.
1737 $parse = spotPayload ($sic['cfe'], 'SYNT_EXPR');
1738 if ($parse['result'] == 'ACK')
1739 {
1740 $ret['extratext'] = trim ($sic['cfe']);
1741 $ret['urlextra'] .= '&cfe=' . $ret['extratext'];
1742 }
1743 }
1744 $finaltext = array();
1745 if (strlen ($ret['text']))
1746 $finaltext[] = '(' . $ret['text'] . ')';
1747 if (strlen ($ret['extratext']))
1748 $finaltext[] = '(' . $ret['extratext'] . ')';
1749 $andor_used = $andor_used || (count($finaltext) > 1);
1750 $finaltext = implode (' ' . $andor . ' ', $finaltext);
1751 if (strlen ($finaltext))
1752 {
1753 $ret['is_empty'] = FALSE;
1754 $parse = spotPayload ($finaltext, 'SYNT_EXPR');
1755 $ret['expression'] = $parse['result'] == 'ACK' ? $parse['load'] : NULL;
1756 // It's not quite fair enough to put the blame of the whole text onto
1757 // non-empty "extra" portion of it, but it's the only user-generated portion
1758 // of it, thus the most probable cause of parse error.
1759 if (strlen ($ret['extratext']))
1760 $ret['extraclass'] = $parse['result'] == 'ACK' ? 'validation-success' : 'validation-error';
1761 }
1762 if (! $andor_used)
1763 $ret['andor'] = getConfigVar ('FILTER_DEFAULT_ANDOR');
1764 else
1765 $ret['urlextra'] .= '&andor=' . $ret['andor'];
1766 return $ret;
1767 }
1768
1769 function buildRedirectURL ($nextpage = NULL, $nexttab = NULL, $moreArgs = array())
1770 {
1771 global $page, $pageno, $tabno;
1772 if ($nextpage === NULL)
1773 $nextpage = $pageno;
1774 if ($nexttab === NULL)
1775 $nexttab = $tabno;
1776 $url = "index.php?page=${nextpage}&tab=${nexttab}";
1777 if (isset ($page[$nextpage]['bypass']))
1778 $url .= '&' . $page[$nextpage]['bypass'] . '=' . $_REQUEST[$page[$nextpage]['bypass']];
1779 if (isset ($page[$nextpage]['bypass_tabs']))
1780 foreach ($page[$nextpage]['bypass_tabs'] as $param_name)
1781 if (isset ($_REQUEST[$param_name]))
1782 $url .= '&' . urlencode ($param_name) . '=' . urlencode ($_REQUEST[$param_name]);
1783
1784 if (count ($moreArgs) > 0)
1785 foreach ($moreArgs as $arg => $value)
1786 if (is_array ($value))
1787 foreach ($value as $v)
1788 $url .= '&' . urlencode ($arg . '[]') . '=' . urlencode ($v);
1789 elseif ($arg != 'module')
1790 $url .= '&' . urlencode ($arg) . '=' . urlencode ($value);
1791 return $url;
1792 }
1793
1794 function redirectUser ($url)
1795 {
1796 global $log_messages;
1797 if (! empty ($log_messages))
1798 {
1799 @session_start();
1800 $_SESSION['log'] = $log_messages;
1801 }
1802 header ("Location: " . $url);
1803 die;
1804 }
1805
1806 function getRackCodeStats ()
1807 {
1808 global $rackCode;
1809 $defc = $grantc = $modc = 0;
1810 foreach ($rackCode as $s)
1811 switch ($s['type'])
1812 {
1813 case 'SYNT_DEFINITION':
1814 $defc++;
1815 break;
1816 case 'SYNT_GRANT':
1817 $grantc++;
1818 break;
1819 case 'SYNT_CTXMOD':
1820 $modc++;
1821 break;
1822 default:
1823 break;
1824 }
1825 $ret = array
1826 (
1827 'Definition sentences' => $defc,
1828 'Grant sentences' => $grantc,
1829 'Context mod sentences' => $modc
1830 );
1831 return $ret;
1832 }
1833
1834 function getRackImageWidth ()
1835 {
1836 global $rtwidth;
1837 return 3 + $rtwidth[0] + $rtwidth[1] + $rtwidth[2] + 3;
1838 }
1839
1840 function getRackImageHeight ($units)
1841 {
1842 return 3 + 3 + $units * 2;
1843 }
1844
1845 // Indicate occupation state of each IP address: none, ordinary or problematic.
1846 function markupIPAddrList (&$addrlist)
1847 {
1848 foreach (array_keys ($addrlist) as $ip_bin)
1849 {
1850 $refc = array
1851 (
1852 'shared' => 0, // virtual
1853 'virtual' => 0, // loopback
1854 'regular' => 0, // connected host
1855 'router' => 0 // connected gateway
1856 );
1857 foreach ($addrlist[$ip_bin]['allocs'] as $a)
1858 $refc[$a['type']]++;
1859 $nvirtloopback = ($refc['shared'] + $refc['virtual'] + $refc['router'] > 0) ? 1 : 0; // modulus of virtual + shared + router
1860 $nreserved = ($addrlist[$ip_bin]['reserved'] == 'yes') ? 1 : 0; // only one reservation is possible ever
1861 $nrealms = $nreserved + $nvirtloopback + $refc['regular']; // last is connected allocation
1862
1863 if ($nrealms == 1)
1864 $addrlist[$ip_bin]['class'] = 'trbusy';
1865 elseif ($nrealms > 1)
1866 $addrlist[$ip_bin]['class'] = 'trerror';
1867 elseif (! empty ($addrlist[$ip_bin]['vslist']) or ! empty ($addrlist[$ip_bin]['rsplist']))
1868 $addrlist[$ip_bin]['class'] = 'trbusy';
1869 else
1870 $addrlist[$ip_bin]['class'] = '';
1871 }
1872 }
1873
1874 // Scan the given address list (returned by scanIPv4Space/scanIPv6Space) and return a list of all routers found.
1875 function findRouters ($addrlist)
1876 {
1877 $ret = array();
1878 foreach ($addrlist as $addr)
1879 foreach ($addr['allocs'] as $alloc)
1880 if ($alloc['type'] == 'router')
1881 $ret[] = array
1882 (
1883 'id' => $alloc['object_id'],
1884 'iface' => $alloc['name'],
1885 'dname' => $alloc['object_name'],
1886 'ip_bin' => $addr['ip_bin'],
1887 );
1888 return $ret;
1889 }
1890
1891 // Compare networks. When sorting a tree, the records on the list will have
1892 // distinct base IP addresses.
1893 // "The comparison function must return an integer less than, equal to, or greater
1894 // than zero if the first argument is considered to be respectively less than,
1895 // equal to, or greater than the second." (c) PHP manual
1896 function IPNetworkCmp ($netA, $netB)
1897 {
1898 $ret = strcmp ($netA['ip_bin'], $netB['ip_bin']);
1899 if ($ret == 0)
1900 $ret = $netA['mask'] < $netB['mask'] ? -1 : ($netA['mask'] > $netB['mask'] ? 1 : 0);
1901 if ($ret == -1 and $netA['ip_bin'] === ($netB['ip_bin'] & $netA['mask_bin']))
1902 $ret = -2;
1903 return $ret;
1904 }
1905
1906 function IPNetContainsOrEqual ($netA, $netB)
1907 {
1908 $res = IPNetworkCmp ($netA, $netB);
1909 return ($res == -2 || $res == 0);
1910 }
1911
1912 function IPNetContains ($netA, $netB)
1913 {
1914 return (-2 == IPNetworkCmp ($netA, $netB));
1915 }
1916
1917 function ip_in_range ($ip_bin, $range)
1918 {
1919 return ($ip_bin & $range['mask_bin']) === $range['ip_bin'];
1920 }
1921
1922 // Modify the given tag tree so, that each level's items are sorted alphabetically.
1923 function sortTree (&$tree, $sortfunc = '')
1924 {
1925 if (!strlen ($sortfunc))
1926 return;
1927 $self = __FUNCTION__;
1928 usort ($tree, $sortfunc);
1929 // Don't make a mistake of directly iterating over the items of current level, because this way
1930 // the sorting will be performed on a _copy_ if each item, not the item itself.
1931 foreach (array_keys ($tree) as $tagid)
1932 $self ($tree[$tagid]['kids'], $sortfunc);
1933 }
1934
1935 function iptree_fill (&$netdata)
1936 {
1937 if (!isset ($netdata['kids']) or !count ($netdata['kids']))
1938 return;
1939 // If we really have nested prefixes, they must fit into the tree.
1940 $worktree = $netdata;
1941 foreach ($netdata['kids'] as $pfx)
1942 iptree_embed ($worktree, $pfx);
1943 $netdata['kids'] = iptree_construct ($worktree);
1944 $netdata['kidc'] = count ($netdata['kids']);
1945 }
1946
1947 function iptree_construct ($node)
1948 {
1949 $self = __FUNCTION__;
1950
1951 if (!isset ($node['right']))
1952 {
1953 if (!isset ($node['kids']))
1954 {
1955 $node['kids'] = array();
1956 $node['kidc'] = 0;
1957 $node['name'] = '';
1958 }
1959 return array ($node);
1960 }
1961 else
1962 return array_merge ($self ($node['left']), $self ($node['right']));
1963 }
1964
1965 function ip_format ($ip_bin)
1966 {
1967 switch (strlen ($ip_bin))
1968 {
1969 case 4: return ip4_format ($ip_bin);
1970 case 16: return ip6_format ($ip_bin);
1971 default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
1972 }
1973 }
1974
1975 function ip4_format ($ip_bin)
1976 {
1977 if (4 != ($len = strlen ($ip_bin)))
1978 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
1979 return implode ('.', unpack ('C*', $ip_bin));
1980 }
1981
1982 function ip6_format ($ip_bin)
1983 {
1984 // maybe this is IPv6-to-IPv4 address?
1985 if (substr ($ip_bin, 0, 12) == "\0\0\0\0\0\0\0\0\0\0\xff\xff")
1986 return '::ffff:' . implode ('.', unpack ('C*', substr ($ip_bin, 12, 4)));
1987
1988 $result = array();
1989 $hole_index = NULL;
1990 $max_hole_index = NULL;
1991 $hole_length = 0;
1992 $max_hole_length = 0;
1993
1994 for ($i = 0; $i < 8; $i++)
1995 {
1996 $unpacked = unpack ('n', substr ($ip_bin, $i * 2, 2));
1997 $value = array_shift ($unpacked);
1998 $result[] = dechex ($value & 0xffff);
1999 if ($value != 0)
2000 {
2001 unset ($hole_index);
2002 $hole_length = 0;
2003 }
2004 else
2005 {
2006 if (! isset ($hole_index))
2007 $hole_index = $i;
2008 if (++$hole_length >= $max_hole_length)
2009 {
2010 $max_hole_index = $hole_index;
2011 $max_hole_length = $hole_length;
2012 }
2013 }
2014 }
2015 if (isset ($max_hole_index))
2016 {
2017 array_splice ($result, $max_hole_index, $max_hole_length, array (''));
2018 if ($max_hole_index == 0 && $max_hole_length == 8)
2019 return '::';
2020 elseif ($max_hole_index == 0)
2021 return ':' . implode (':', $result);
2022 elseif ($max_hole_index + $max_hole_length == 8)
2023 return implode (':', $result) . ':';
2024 }
2025 return implode (':', $result);
2026 }
2027
2028 function ip_parse ($ip)
2029 {
2030 if (FALSE !== strpos ($ip, ':'))
2031 return ip6_parse ($ip);
2032 else
2033 return ip4_parse ($ip);
2034 }
2035
2036 function ip4_parse ($ip)
2037 {
2038 if (FALSE === ($int = ip2long ($ip)))
2039 throw new InvalidArgException ('ip', $ip, "Invalid IPv4 address");
2040 else
2041 return pack ('N', $int);
2042 }
2043
2044 // returns 16-byte string ip_bin
2045 // throws exception if unable to parse
2046 function ip6_parse ($ip)
2047 {
2048 do {
2049 if (empty ($ip))
2050 break;
2051
2052 $result = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; // 16 bytes
2053 // remove one of double beginning/tailing colons
2054 if (substr ($ip, 0, 2) == '::')
2055 $ip = substr ($ip, 1);
2056 elseif (substr ($ip, -2, 2) == '::')
2057 $ip = substr ($ip, 0, strlen ($ip) - 1);
2058
2059 $tokens = explode (':', $ip);
2060 $last_token = $tokens[count ($tokens) - 1];
2061 $split = explode ('.', $last_token);
2062 if (count ($split) == 4)
2063 {
2064 $hex_tokens = array();
2065 $hex_tokens[] = dechex ($split[0] * 256 + $split[1]);
2066 $hex_tokens[] = dechex ($split[2] * 256 + $split[3]);
2067 array_splice ($tokens, -1, 1, $hex_tokens);
2068 }
2069 if (count ($tokens) > 8)
2070 break;
2071 for ($i = 0; $i < count ($tokens); $i++)
2072 {
2073 if ($tokens[$i] != '')
2074 {
2075 if (! set_word_value ($result, $i, $tokens[$i]))
2076 break;
2077 }
2078 else
2079 {
2080 $k = 8; //index in result string (last word)
2081 for ($j = count ($tokens) - 1; $j > $i; $j--) // $j is an index in $tokens for reverse walk
2082 if ($tokens[$j] == '')
2083 break;
2084 elseif (! set_word_value ($result, --$k, $tokens[$j]))
2085 break;
2086 if ($i != $j)
2087 break; //error, more than 1 '::' range
2088 break;
2089 }
2090 }
2091 if (! isset ($k) && count ($tokens) != 8)
2092 break;
2093 return $result;
2094 } while (FALSE);
2095
2096 throw new InvalidArgException ('ip', $ip, "Invalid IPv6 address");
2097 }
2098
2099 function ip_get_arpa ($ip_bin)
2100 {
2101 switch (strlen ($ip_bin))
2102 {
2103 case 4: return ip4_get_arpa ($ip_bin);
2104 case 16: return ip6_get_arpa ($ip_bin);
2105 default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2106 }
2107 }
2108
2109 function ip4_get_arpa ($ip_bin)
2110 {
2111 $ret = '';
2112 for ($i = 3; $i >= 0; $i--)
2113 $ret .= ord($ip_bin[$i]) . '.';
2114 return $ret . 'in-addr.arpa';
2115 }
2116
2117 function ip6_get_arpa ($ip_bin)
2118 {
2119 $ret = '';
2120 $hex = implode ('', unpack('H32', $ip_bin));
2121 for ($i = 31; $i >= 0; $i--)
2122 $ret .= $hex[$i] . '.';
2123 return $ret . 'ip6.arpa';
2124 }
2125
2126 function set_word_value (&$haystack, $nword, $hexvalue)
2127 {
2128 // check that $hexvalue is like /^[0-9a-fA-F]*$/
2129 for ($i = 0; $i < strlen ($hexvalue); $i++)
2130 {
2131 $char = ord ($hexvalue[$i]);
2132 if (! ($char >= 0x30 && $char <= 0x39 || $char >= 0x41 && $char <= 0x46 || $char >=0x61 && $char <= 0x66))
2133 return FALSE;
2134 }
2135 $haystack = substr_replace ($haystack, pack ('n', hexdec ($hexvalue)), $nword * 2, 2);
2136 return TRUE;
2137 }
2138
2139 // returns binary IP or FALSE
2140 function ip_checkparse ($ip)
2141 {
2142 try
2143 {
2144 return ip_parse ($ip);
2145 }
2146 catch (InvalidArgException $e)
2147 {
2148 return FALSE;
2149 }
2150 }
2151
2152 // returns binary IP or FALSE
2153 function ip4_checkparse ($ip)
2154 {
2155 try
2156 {
2157 return ip4_parse ($ip);
2158 }
2159 catch (InvalidArgException $e)
2160 {
2161 return FALSE;
2162 }
2163 }
2164
2165 // returns binary IP or FALSE
2166 function ip6_checkparse ($ip)
2167 {
2168 try
2169 {
2170 return ip6_parse ($ip);
2171 }
2172 catch (InvalidArgException $e)
2173 {
2174 return FALSE;
2175 }
2176 }
2177
2178 function ip4_int2bin ($ip_int)
2179 {
2180 return pack ('N', $ip_int);
2181 }
2182
2183 function ip4_bin2int ($ip_bin)
2184 {
2185 if (4 != strlen ($ip_bin))
2186 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2187 $list = unpack ('N', $ip_bin);
2188 return array_shift ($list);
2189 }
2190
2191 // Use this function only when you need to export binary ip out of PHP running context (e.g., DB)
2192 // !DO NOT perform arithmetic and bitwise operations with the result of this function!
2193 function ip4_bin2db ($ip_bin)
2194 {
2195 $ip_int = ip4_bin2int ($ip_bin);
2196 if ($ip_int < 0)
2197 return sprintf ('%u', 0x00000000 + $ip_int);
2198 else
2199 return $ip_int;
2200 }
2201
2202 function ip_last ($net)
2203 {
2204 return $net['ip_bin'] | ~$net['mask_bin'];
2205 }
2206
2207 function ip_next ($ip_bin)
2208 {
2209 $ret = $ip_bin;
2210 $p = 1;
2211 for ($i = strlen ($ret) - 1; $i >= 0; $i--)
2212 {
2213 $oct = $p + ord ($ret[$i]);
2214 $ret[$i] = chr ($oct & 0xff);
2215 if ($oct <= 255 and $oct >= 0)
2216 break;
2217 }
2218 return $ret;
2219 }
2220
2221 function ip_prev ($ip_bin)
2222 {
2223 $ret = $ip_bin;
2224 $p = -1;
2225 for ($i = strlen ($ret) - 1; $i >= 0; $i--)
2226 {
2227 $oct = $p + ord ($ret[$i]);
2228 $ret[$i] = chr ($oct & 0xff);
2229 if ($oct <= 255 and $oct >= 0)
2230 break;
2231 }
2232 return $ret;
2233 }
2234
2235 function ip4_range_size ($range)
2236 {
2237 return ip4_mask_size ($range['mask']);
2238 }
2239
2240 function ip4_mask_size ($mask)
2241 {
2242 return (0xffffffff >> $mask) + 1;
2243 }
2244
2245 // returns array with keys 'ip', 'ip_bin', 'mask', 'mask_bin'
2246 function constructIPRange ($ip_bin, $mask)
2247 {
2248 $node = array();
2249 switch (strlen ($ip_bin))
2250 {
2251 case 4: // IPv4
2252 if ($mask < 0 || $mask > 32)
2253 throw new InvalidArgException ('mask', $mask, "Invalid v4 prefix length");
2254 $node['mask_bin'] = ip4_mask ($mask);
2255 $node['mask'] = $mask;
2256 $node['ip_bin'] = $ip_bin & $node['mask_bin'];
2257 $node['ip'] = ip4_format ($node['ip_bin']);
2258 break;
2259 case 16: // IPv6
2260 if ($mask < 0 || $mask > 128)
2261 throw new InvalidArgException ('mask', $mask, "Invalid v6 prefix length");
2262 $node['mask_bin'] = ip6_mask ($mask);
2263 $node['mask'] = $mask;
2264 $node['ip_bin'] = $ip_bin & $node['mask_bin'];
2265 $node['ip'] = ip6_format ($node['ip_bin']);
2266 break;
2267 default:
2268 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2269 }
2270 return $node;
2271 }
2272
2273 // Return minimal IP address structure
2274 function constructIPAddress ($ip_bin)
2275 {
2276 // common v4/v6 part
2277 $ret = array
2278 (
2279 'ip' => ip_format ($ip_bin),
2280 'ip_bin' => $ip_bin,
2281 'name' => '',
2282 'reserved' => 'no',
2283 'allocs' => array(),
2284 'vslist' => array(),
2285 'rsplist' => array(),
2286 );
2287
2288 // specific v4 part
2289 if (strlen ($ip_bin) == 4)
2290 $ret = array_merge
2291 (
2292 $ret,
2293 array
2294 (
2295 'outpf' => array(),
2296 'inpf' => array(),
2297 )
2298 );
2299 return $ret;
2300 }
2301
2302 function iptree_embed (&$node, $pfx)
2303 {
2304 $self = __FUNCTION__;
2305
2306 // hit?
2307 if (0 == IPNetworkCmp ($node, $pfx))
2308 {
2309 $node = $pfx;
2310 return;
2311 }
2312 if ($node['mask'] == $pfx['mask'])
2313 throw new RackTablesError ('the recurring loop lost control', RackTablesError::INTERNAL);
2314
2315 // split?
2316 if (!isset ($node['right']))
2317 {
2318 $node['left'] = constructIPRange ($node['ip_bin'], $node['mask'] + 1);
2319 $node['right'] = constructIPRange (ip_last ($node), $node['mask'] + 1);
2320 }
2321
2322 if (IPNetContainsOrEqual ($node['left'], $pfx))
2323 $self ($node['left'], $pfx);
2324 elseif (IPNetContainsOrEqual ($node['right'], $pfx))
2325 $self ($node['right'], $pfx);
2326 else
2327 throw new RackTablesError ('cannot decide between left and right', RackTablesError::INTERNAL);
2328 }
2329
2330 function treeApplyFunc (&$tree, $func = '', $stopfunc = '')
2331 {
2332 if (!strlen ($func))
2333 return;
2334 $self = __FUNCTION__;
2335 foreach (array_keys ($tree) as $key)
2336 {
2337 $func ($tree[$key]);
2338 if (strlen ($stopfunc) and $stopfunc ($tree[$key]))
2339 continue;
2340 $self ($tree[$key]['kids'], $func);
2341 }
2342 }
2343
2344 function nodeIsCollapsed ($node)
2345 {
2346 return $node['symbol'] == 'node-collapsed';
2347 }
2348
2349 // sets 'addrlist', 'own_addrlist', 'addrc', 'own_addrc' keys of $node
2350 // 'addrc' and 'own_addrc' are sizes of 'addrlist' and 'own_addrlist', respectively
2351 // 'own_addrlist' is empty if the node network has no children
2352 function loadIPAddrList (&$node)
2353 {
2354 $node['addrlist'] = scanIPSpace (array (array ('first' => $node['ip_bin'], 'last' => ip_last ($node))));
2355
2356 if (! isset ($node['id']))
2357 $node['own_addrlist'] = $node['addrlist'];
2358 else
2359 {
2360 if ($node['kidc'] == 0)
2361 $node['own_addrlist'] = $node['addrlist'];
2362 //$node['own_addrlist'] = array();
2363 else
2364 {
2365 $node['own_addrlist'] = array();
2366 // node has childs
2367 foreach ($node['spare_ranges'] as $mask => $spare_list)
2368 foreach ($spare_list as $spare_ip)
2369 {
2370 $spare_range = constructIPRange ($spare_ip, $mask);
2371 foreach ($node['addrlist'] as $bin_ip => $addr)
2372 if (($bin_ip & $spare_range['mask_bin']) == $spare_range['ip_bin'])
2373 $node['own_addrlist'][$bin_ip] = $addr;
2374 }
2375 }
2376 }
2377 $node['addrc'] = count ($node['addrlist']);
2378 $node['own_addrc'] = count ($node['own_addrlist']);
2379 }
2380
2381 function getIPv4OwnRangeSize ($range)
2382 {
2383 if (empty ($range['spare_ranges']))
2384 return ip4_range_size ($range);
2385 else
2386 {
2387 $ret = 0;
2388 foreach ($range['spare_ranges'] as $mask => $spare_list)
2389 $ret += count ($spare_list) * ip4_mask_size ($mask);
2390 return $ret;
2391 }
2392 }
2393
2394 // returns the array of structure described by constructIPAddress
2395 function getIPAddress ($ip_bin)
2396 {
2397 $scanres = scanIPSpace (array (array ('first' => $ip_bin, 'last' => $ip_bin)));
2398 if (empty ($scanres))
2399 return constructIPAddress ($ip_bin);
2400 markupIPAddrList ($scanres);
2401 return $scanres[$ip_bin];
2402 }
2403
2404 function getIPv4Address ($ip_bin)
2405 {
2406 if (strlen ($ip_bin) != 4)
2407 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2408 return getIPAddress ($ip_bin);
2409 }
2410
2411 function getIPv6Address ($ip_bin)
2412 {
2413 if (strlen ($ip_bin) != 16)
2414 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2415 return getIPAddress ($ip_bin);
2416 }
2417
2418 function makeIPTree ($netlist)
2419 {
2420 // treeFromList() requires parent_id to be correct for an item to get onto the tree,
2421 // so perform necessary pre-processing to calculate parent_id of each item of $netlist.
2422 $stack = array();
2423 foreach ($netlist as $net_id => &$net)
2424 {
2425 while (! empty ($stack))
2426 {
2427 $top_id = $stack[count ($stack) - 1];
2428 if (! IPNetContains ($netlist[$top_id], $net)) // unless $net is a child of stack top
2429 array_pop ($stack);
2430 else
2431 {
2432 $net['parent_id'] = $top_id;
2433 break;
2434 }
2435 }
2436 if (empty ($stack))
2437 $net['parent_id'] = NULL;
2438 array_push ($stack, $net_id);
2439 }
2440 unset ($stack);
2441
2442 $tree = treeFromList ($netlist); // medium call
2443 return $tree;
2444 }
2445
2446 function prepareIPTree ($netlist, $expanded_id = 0)
2447 {
2448 $tree = makeIPTree ($netlist);
2449 // complement the tree before markup to make the spare networks have "symbol" set
2450 treeApplyFunc ($tree, 'iptree_fill');
2451 iptree_markup_collapsion ($tree, getConfigVar ('TREE_THRESHOLD'), $expanded_id);
2452 return $tree;
2453 }
2454
2455 # Traverse IPv4/IPv6 tree and return a list of all networks, which
2456 # exist in DB and don't have any sub-networks.
2457 function getTerminalNetworks ($tree)
2458 {
2459 $self = __FUNCTION__;
2460 $ret = array();
2461 foreach ($tree as $node)
2462 if ($node['kidc'] == 0 and isset ($node['realm']))
2463 $ret[] = $node;
2464 else
2465 $ret = array_merge ($ret, $self ($node['kids']));
2466 return $ret;
2467 }
2468
2469 // Check all items of the tree recursively, until the requested target id is
2470 // found. Mark all items leading to this item as "expanded", collapsing all
2471 // the rest, which exceed the given threshold (if the threshold is given).
2472 function iptree_markup_collapsion (&$tree, $threshold = 1024, $target = 0)
2473 {
2474 $self = __FUNCTION__;
2475 $ret = FALSE;
2476 foreach (array_keys ($tree) as $key)
2477 {
2478 $here = ($target === 'ALL' or ($target > 0 and isset ($tree[$key]['id']) and $tree[$key]['id'] == $target));
2479 $below = $self ($tree[$key]['kids'], $threshold, $target);
2480 $expand_enabled = ($target !== 'NONE');
2481 if (!$tree[$key]['kidc']) // terminal node
2482 $tree[$key]['symbol'] = 'spacer';
2483 elseif ($expand_enabled and $tree[$key]['kidc'] < $threshold)
2484 $tree[$key]['symbol'] = 'node-expanded-static';
2485 elseif ($expand_enabled and ($here or $below))
2486 $tree[$key]['symbol'] = 'node-expanded';
2487 else
2488 $tree[$key]['symbol'] = 'node-collapsed';
2489 $ret = ($ret or $here or $below); // parentheses are necessary for this to be computed correctly
2490 }
2491 return $ret;
2492 }
2493
2494 // Convert entity name to human-readable value
2495 function formatEntityName ($name) {
2496 switch ($name)
2497 {
2498 case 'ipv4net':
2499 return 'IPv4 Network';
2500 case 'ipv6net':
2501 return 'IPv6 Network';
2502 case 'ipv4rspool':
2503 return 'IPv4 RS Pool';
2504 case 'ipv4vs':
2505 return 'IPv4 Virtual Service';
2506 case 'object':
2507 return 'Object';
2508 case 'rack':
2509 return 'Rack';
2510 case 'location':
2511 return 'Location';
2512 case 'user':
2513 return 'User';
2514 }
2515 return 'invalid';
2516 }
2517
2518 // Display hrefs for all of a file's parents. If scissors are requested,
2519 // prepend cutting button to each of them.
2520 function serializeFileLinks ($links, $scissors = FALSE)
2521 {
2522 $comma = '';
2523 $ret = '';
2524 foreach ($links as $link_id => $li)
2525 {
2526 switch ($li['entity_type'])
2527 {
2528 case 'ipv4net':
2529 $params = "page=ipv4net&id=";
2530 break;
2531 case 'ipv6net':
2532 $params = "page=ipv6net&id=";
2533 break;
2534 case 'ipv4rspool':
2535 $params = "page=ipv4rspool&pool_id=";
2536 break;
2537 case 'ipv4vs':
2538 $params = "page=ipv4vs&vs_id=";
2539 break;
2540 case 'object':
2541 $params = "page=object&object_id=";
2542 break;
2543 case 'rack':
2544 $params = "page=rack&rack_id=";
2545 break;
2546 case 'location':
2547 $params = "page=location&location_id=";
2548 break;
2549 case 'user':
2550 $params = "page=user&user_id=";
2551 break;
2552 }
2553 $ret .= $comma;
2554 if ($scissors)
2555 {
2556 $ret .= "<a href='" . makeHrefProcess(array('op'=>'unlinkFile', 'link_id'=>$link_id)) . "'";
2557 $ret .= getImageHREF ('cut') . '</a> ';
2558 }
2559 $ret .= sprintf("<a href='index.php?%s%s'>%s</a>", $params, $li['entity_id'], $li['name']);
2560 $comma = '<br>';
2561 }
2562 return $ret;
2563 }
2564
2565 // Convert filesize to appropriate unit and make it human-readable
2566 function formatFileSize ($bytes) {
2567 // bytes
2568 if($bytes < 1024) // bytes
2569 return "${bytes} bytes";
2570
2571 // kilobytes
2572 if ($bytes < 1024000)
2573 return sprintf ("%.1fk", round (($bytes / 1024), 1));
2574
2575 // megabytes
2576 return sprintf ("%.1f MB", round (($bytes / 1024000), 1));
2577 }
2578
2579 // Reverse of formatFileSize, it converts human-readable value to bytes
2580 function convertToBytes ($value) {
2581 $value = trim($value);
2582 $last = strtolower($value[strlen($value)-1]);
2583 switch ($last)
2584 {
2585 case 'g':
2586 $value *= 1024;
2587 case 'm':
2588 $value *= 1024;
2589 case 'k':
2590 $value *= 1024;
2591 }
2592
2593 return $value;
2594 }
2595
2596 // make "A" HTML element
2597 function mkA ($text, $nextpage, $bypass = NULL, $nexttab = NULL)
2598 {
2599 global $page, $tab;
2600 if ($text == '')
2601 throw new InvalidArgException ('text', $text);
2602 if (! array_key_exists ($nextpage, $page))
2603 throw new InvalidArgException ('nextpage', $nextpage, 'not found');
2604 $args = array ('page' => $nextpage);
2605 if ($nexttab !== NULL)
2606 {
2607 if (! array_key_exists ($nexttab, $tab[$nextpage]))
2608 throw new InvalidArgException ('nexttab', $nexttab, 'not found');
2609 $args['tab'] = $nexttab;
2610 }
2611 if (array_key_exists ('bypass', $page[$nextpage]))
2612 {
2613 if ($bypass === NULL)
2614 throw new InvalidArgException ('bypass', '(NULL)');
2615 $args[$page[$nextpage]['bypass']] = $bypass;
2616 }
2617 return '<a href="' . makeHref ($args) . '">' . $text . '</a>';
2618 }
2619
2620 // make "HREF" HTML attribute
2621 function makeHref ($params = array())
2622 {
2623 $tmp = array();
2624 foreach ($params as $key => $value)
2625 {
2626 if (is_array ($value))
2627 $key .= "[]";
2628 else
2629 $value = array ($value);
2630 if (!count ($value))
2631 $tmp[] = urlencode ($key) . '=';
2632 else
2633 foreach ($value as $sub_value)
2634 $tmp[] = urlencode ($key) . '=' . urlencode ($sub_value);
2635 }
2636 return 'index.php?' . implode ('&', $tmp);
2637 }
2638
2639 function makeHrefProcess ($params = array())
2640 {
2641 global $pageno, $tabno, $page;
2642 $tmp = array();
2643 if (! array_key_exists ('page', $params))
2644 $params['page'] = $pageno;
2645 if (! array_key_exists ('tab', $params))
2646 $params['tab'] = $tabno;
2647 if (isset ($page[$pageno]['bypass']))
2648 {
2649 $bypass = $page[$pageno]['bypass'];
2650 if (! array_key_exists ($bypass, $params) and array_key_exists ($bypass, $_REQUEST))
2651 $params[$bypass] = $_REQUEST[$bypass];
2652 }
2653 foreach ($params as $key => $value)
2654 $tmp[] = urlencode ($key) . '=' . urlencode ($value);
2655 return '?module=redirect&' . implode ('&', $tmp);
2656 }
2657
2658 function makeHrefForHelper ($helper_name, $params = array())
2659 {
2660 $ret = '?module=popup&helper=' . $helper_name;
2661 foreach($params as $key=>$value)
2662 $ret .= '&'.urlencode($key).'='.urlencode($value);
2663 return $ret;
2664 }
2665
2666 // Process the given list of records to build data suitable for printNiftySelect()
2667 // (like it was formerly executed by printSelect()). Screen out vendors according
2668 // to VENDOR_SIEVE, if object type ID is provided. However, the OPTGROUP with already
2669 // selected OPTION is protected from being screened.
2670 function cookOptgroups ($recordList, $object_type_id = 0, $existing_value = 0)
2671 {
2672 $ret = array();
2673 // Always keep "other" OPTGROUP at the SELECT bottom.
2674 $therest = array();
2675 foreach ($recordList as $dict_key => $dict_value)
2676 if (strpos ($dict_value, '%GSKIP%') !== FALSE)
2677 {
2678 $tmp = explode ('%GSKIP%', $dict_value, 2);
2679 $ret[$tmp[0]][$dict_key] = $tmp[1];
2680 }
2681 elseif (strpos ($dict_value, '%GPASS%') !== FALSE)
2682 {
2683 $tmp = explode ('%GPASS%', $dict_value, 2);
2684 $ret[$tmp[0]][$dict_key] = $tmp[1];
2685 }
2686 else
2687 $therest[$dict_key] = $dict_value;
2688 if ($object_type_id != 0)
2689 {
2690 $screenlist = array();
2691 foreach (explode (';', getConfigVar ('VENDOR_SIEVE')) as $sieve)
2692 if (preg_match ("/^([^@]+)(@${object_type_id})?\$/", trim ($sieve), $regs)){
2693 $screenlist[] = $regs[1];
2694 }
2695 foreach (array_keys ($ret) as $vendor)
2696 if (in_array ($vendor, $screenlist))
2697 {
2698 $ok_to_screen = TRUE;
2699 if ($existing_value)
2700 foreach (array_keys ($ret[$vendor]) as $recordkey)
2701 if ($recordkey == $existing_value)
2702 {
2703 $ok_to_screen = FALSE;
2704 break;
2705 }
2706 if ($ok_to_screen)
2707 unset ($ret[$vendor]);
2708 }
2709 }
2710 $ret['other'] = $therest;
2711 return $ret;
2712 }
2713
2714 function unix2dos ($text)
2715 {
2716 return str_replace ("\n", "\r\n", $text);
2717 }
2718
2719 function buildPredicateTable ($parsetree)
2720 {
2721 $ret = array();
2722 foreach ($parsetree as $sentence)
2723 if ($sentence['type'] == 'SYNT_DEFINITION')
2724 $ret[$sentence['term']] = $sentence['definition'];
2725 // Now we have predicate table filled in with the latest definitions of each
2726 // particular predicate met. This isn't as chik, as on-the-fly predicate
2727 // overloading during allow/deny scan, but quite sufficient for this task.
2728 return $ret;
2729 }
2730
2731 // Take a list of records and filter against given RackCode expression. Return
2732 // the original list intact, if there was no filter requested, but return an
2733 // empty list, if there was an error.
2734 function filterCellList ($list_in, $expression = array())
2735 {
2736 if ($expression === NULL)
2737 return array();
2738 if (!count ($expression))
2739 return $list_in;
2740 $list_out = array();
2741 foreach ($list_in as $item_key => $item_value)
2742 if (TRUE === judgeCell ($item_value, $expression))
2743 $list_out[$item_key] = $item_value;
2744 return $list_out;
2745 }
2746
2747 function eval_expression ($expr, $tagchain, $ptable, $silent = FALSE)
2748 {
2749 $self = __FUNCTION__;
2750 switch ($expr['type'])
2751 {
2752 // Return true, if given tag is present on the tag chain.
2753 case 'LEX_TAG':
2754 case 'LEX_AUTOTAG':
2755 foreach ($tagchain as $tagInfo)
2756 if ($expr['load'] == $tagInfo['tag'])
2757 return TRUE;
2758 return FALSE;
2759 case 'LEX_PREDICATE': // Find given predicate in the symbol table and evaluate it.
2760 $pname = $expr['load'];
2761 if (!isset ($ptable[$pname]))
2762 {
2763 if (!$silent)
2764 showWarning ("Predicate '${pname}' is referenced before declaration");
2765 return NULL;
2766 }
2767 return $self ($ptable[$pname], $tagchain, $ptable);
2768 case 'LEX_TRUE':
2769 return TRUE;
2770 case 'LEX_FALSE':
2771 return FALSE;
2772 case 'SYNT_NOT_EXPR':
2773 $tmp = $self ($expr['load'], $tagchain, $ptable);
2774 if ($tmp === TRUE)
2775 return FALSE;
2776 elseif ($tmp === FALSE)
2777 return TRUE;
2778 else
2779 return $tmp;
2780 case 'SYNT_AND_EXPR': // binary AND
2781 if (FALSE == $self ($expr['left'], $tagchain, $ptable))
2782 return FALSE; // early failure
2783 return $self ($expr['right'], $tagchain, $ptable);
2784 case 'SYNT_EXPR': // binary OR
2785 if (TRUE == $self ($expr['left'], $tagchain, $ptable))
2786 return TRUE; // early success
2787 return $self ($expr['right'], $tagchain, $ptable);
2788 default:
2789 if (!$silent)
2790 showWarning ("Evaluation error, cannot process expression type '${expr['type']}'");
2791 return NULL;
2792 break;
2793 }
2794 }
2795
2796 // Tell, if the given expression is true for the given entity. Take complete record on input.
2797 function judgeCell ($cell, $expression)
2798 {
2799 global $pTable;
2800 return eval_expression
2801 (
2802 $expression,
2803 array_merge
2804 (
2805 $cell['etags'],
2806 $cell['itags'],
2807 $cell['atags']
2808 ),
2809 $pTable,
2810 TRUE
2811 );
2812 }
2813
2814 function judgeContext ($expression)
2815 {
2816 global $pTable, $expl_tags, $impl_tags, $auto_tags;
2817 return eval_expression
2818 (
2819 $expression,
2820 array_merge
2821 (
2822 $expl_tags,
2823 $impl_tags,
2824 $auto_tags
2825 ),
2826 $pTable,
2827 TRUE
2828 );
2829 }
2830
2831 // Tell, if a constraint from config option permits given record.
2832 // An undefined $cell means current context.
2833 function considerConfiguredConstraint ($cell, $varname)
2834 {
2835 if (!strlen (getConfigVar ($varname)))
2836 return TRUE; // no restriction
2837 global $parseCache;
2838 if (!isset ($parseCache[$varname]))
2839 // getConfigVar() doesn't re-read the value from DB because of its
2840 // own cache, so there is no race condition here between two calls.
2841 $parseCache[$varname] = spotPayload (getConfigVar ($varname), 'SYNT_EXPR');
2842 if ($parseCache[$varname]['result'] != 'ACK')
2843 return FALSE; // constraint set, but cannot be used due to compilation error
2844 if (isset ($cell))
2845 return judgeCell ($cell, $parseCache[$varname]['load']);
2846 else
2847 return judgeContext ($parseCache[$varname]['load']);
2848 }
2849
2850 // Tell, if the given arbitrary RackCode text addresses the given record
2851 // (an empty text matches any record).
2852 // An undefined $cell means current context.
2853 function considerGivenConstraint ($cell, $filtertext)
2854 {
2855 if ($filtertext == '')
2856 return TRUE;
2857 $parse = spotPayload ($filtertext, 'SYNT_EXPR');
2858 if ($parse['result'] != 'ACK')
2859 throw new InvalidRequestArgException ('filtertext', $filtertext, 'RackCode parsing error');
2860 if (isset ($cell))
2861 return judgeCell ($cell, $parse['load']);
2862 else
2863 return judgeContext ($parse['load']);
2864 }
2865
2866 // Return list of records in the given realm, which conform to
2867 // the given RackCode expression. If the realm is unknown or text
2868 // doesn't validate as a RackCode expression, return NULL.
2869 // Otherwise (successful scan) return a list of all matched
2870 // records, even if the list is empty (array() !== NULL). If the
2871 // text is an empty string, return all found records in the given
2872 // realm.
2873 function scanRealmByText ($realm = NULL, $ftext = '')
2874 {
2875 switch ($realm)
2876 {
2877 case 'object':
2878 case 'rack':
2879 case 'user':
2880 case 'ipv4net':
2881 case 'ipv6net':
2882 case 'file':
2883 case 'ipv4vs':
2884 case 'ipv4rspool':
2885 case 'vst':
2886 if (!strlen ($ftext = trim ($ftext)))
2887 $fexpr = array();
2888 else
2889 {
2890 $fparse = spotPayload ($ftext, 'SYNT_EXPR');
2891 if ($fparse['result'] != 'ACK')
2892 return NULL;
2893 $fexpr = $fparse['load'];
2894 }
2895 return filterCellList (listCells ($realm), $fexpr);
2896 default:
2897 throw new InvalidArgException ('$realm', $realm);
2898 }
2899
2900 }
2901
2902 function getVSTOptions()
2903 {
2904 $ret = array();
2905 foreach (listCells ('vst') as $vst)
2906 $ret[$vst['id']] = niftyString ($vst['description'], 30, FALSE);
2907 return $ret;
2908 }
2909
2910 # Return a 2-dimensional array in the format understood by getNiftySelect(),
2911 # which would contain all VLANs of all VLAN domains except domains, which IDs
2912 # are present in $except_domains argument.
2913 function getAllVLANOptions ($except_domains = array())
2914 {
2915 $ret = array();
2916 foreach (getVLANDomainStats() as $domain)
2917 if (! in_array ($domain['id'], $except_domains))
2918 foreach (getDomainVLANs ($domain['id']) as $vlan)
2919 $ret[$domain['description']]["${domain['id']}-${vlan['vlan_id']}"] =
2920 "${vlan['vlan_id']} (${vlan['netc']}) ${vlan['vlan_descr']}";
2921 return $ret;
2922 }
2923
2924 // Let's have this debug helper here to enable debugging of process.php w/o interface.php.
2925 function dump ($var)
2926 {
2927 echo '<div align=left><pre>';
2928 print_r ($var);
2929 echo '</pre></div>';
2930 }
2931
2932 function getTagChart ($limit = 0, $realm = 'total', $special_tags = array())
2933 {
2934 global $taglist;
2935 // first build top-N chart...
2936 $toplist = array();
2937 foreach ($taglist as $taginfo)
2938 if (isset ($taginfo['refcnt'][$realm]))
2939 $toplist[$taginfo['id']] = $taginfo['refcnt'][$realm];
2940 arsort ($toplist, SORT_NUMERIC);
2941 $ret = array();
2942 $done = 0;
2943 foreach (array_keys ($toplist) as $tag_id)
2944 {
2945 $ret[$tag_id] = $taglist[$tag_id];
2946 if (++$done == $limit)
2947 break;
2948 }
2949 // ...then make sure, that every item of the special list is shown
2950 // (using the same sort order)
2951 $extra = array();
2952 foreach ($special_tags as $taginfo)
2953 if (!array_key_exists ($taginfo['id'], $ret))
2954 $extra[$taginfo['id']] = $taglist[$taginfo['id']]['refcnt'][$realm];
2955 arsort ($extra, SORT_NUMERIC);
2956 foreach (array_keys ($extra) as $tag_id)
2957 $ret[] = $taglist[$tag_id];
2958 return $ret;
2959 }
2960
2961 function decodeObjectType ($objtype_id, $style = 'r')
2962 {
2963 static $types = array();
2964 if (!count ($types))
2965 $types = array
2966 (
2967 'r' => readChapter (CHAP_OBJTYPE),
2968 'a' => readChapter (CHAP_OBJTYPE, 'a'),
2969 'o' => readChapter (CHAP_OBJTYPE, 'o')
2970 );
2971 return $types[$style][$objtype_id];
2972 }
2973
2974 function isolatedPermission ($p, $t, $cell)
2975 {
2976 // This function is called from both "file" page and a number of other pages,
2977 // which have already fixed security context and authorized the user for it.
2978 // OTOH, it is necessary here to authorize against the current file, which
2979 // means saving the current context and building a new one.
2980 global
2981 $expl_tags,
2982 $impl_tags,
2983 $target_given_tags,
2984 $auto_tags;
2985 // push current context
2986 $orig_expl_tags = $expl_tags;
2987 $orig_impl_tags = $impl_tags;
2988 $orig_target_given_tags = $target_given_tags;
2989 $orig_auto_tags = $auto_tags;
2990 // retarget
2991 fixContext ($cell);
2992 // remember decision
2993 $ret = permitted ($p, $t);
2994 // pop context
2995 $expl_tags = $orig_expl_tags;
2996 $impl_tags = $orig_impl_tags;
2997 $target_given_tags = $orig_target_given_tags;
2998 $auto_tags = $orig_auto_tags;
2999 return $ret;
3000 }
3001
3002 function getPortListPrefs()
3003 {
3004 $ret = array();
3005 if (0 >= ($ret['iif_pick'] = getConfigVar ('DEFAULT_PORT_IIF_ID')))
3006 $ret['iif_pick'] = 1;
3007 $ret['oif_picks'] = array();