826ceee02b58def5e1146e489b9d34b87f95cbd2
[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 // Build a tree from the item list and return it. Input and output data is
1222 // indexed by item id (nested items in output are recursively stored in 'kids'
1223 // key, which is in turn indexed by id. Functions, which are ready to handle
1224 // tree collapsion/expansion themselves, may request non-zero threshold value
1225 // for smaller resulting tree.
1226 function treeFromList (&$orig_nodelist, $threshold = 0, $return_main_payload = TRUE)
1227 {
1228 $tree = array();
1229 $nodelist = $orig_nodelist;
1230 // Array equivalent of traceEntity() function.
1231 $trace = array();
1232 // set kidc and kids only once
1233 foreach (array_keys ($nodelist) as $nodeid)
1234 {
1235 $nodelist[$nodeid]['kidc'] = 0;
1236 $nodelist[$nodeid]['kids'] = array();
1237 }
1238 do
1239 {
1240 $nextpass = FALSE;
1241 foreach (array_keys ($nodelist) as $nodeid)
1242 {
1243 // When adding a node to the working tree, book another
1244 // iteration, because the new item could make a way for
1245 // others onto the tree. Also remove any item added from
1246 // the input list, so iteration base shrinks.
1247 // First check if we can assign directly.
1248 if ($nodelist[$nodeid]['parent_id'] == NULL)
1249 {
1250 $tree[$nodeid] = $nodelist[$nodeid];
1251 $trace[$nodeid] = array(); // Trace to root node is empty
1252 unset ($nodelist[$nodeid]);
1253 $nextpass = TRUE;
1254 }
1255 // Now look if it fits somewhere on already built tree.
1256 elseif (isset ($trace[$nodelist[$nodeid]['parent_id']]))
1257 {
1258 // Trace to a node is a trace to its parent plus parent id.
1259 $trace[$nodeid] = $trace[$nodelist[$nodeid]['parent_id']];
1260 $trace[$nodeid][] = $nodelist[$nodeid]['parent_id'];
1261 pokeNode ($tree, $trace[$nodeid], $nodeid, $nodelist[$nodeid], $threshold);
1262 // path to any other node is made of all parent nodes plus the added node itself
1263 unset ($nodelist[$nodeid]);
1264 $nextpass = TRUE;
1265 }
1266 }
1267 }
1268 while ($nextpass);
1269 if (!$return_main_payload)
1270 return $nodelist;
1271 // update each input node with its backtrace route
1272 foreach ($trace as $nodeid => $route)
1273 $orig_nodelist[$nodeid]['trace'] = $route;
1274 return $tree;
1275 }
1276
1277 // Build a tree from the tag list and return everything _except_ the tree.
1278 // IOW, return taginfo items, which have parent_id set and pointing outside
1279 // of the "normal" tree, which originates from the root.
1280 function getOrphanedTags ()
1281 {
1282 global $taglist;
1283 return treeFromList ($taglist, 0, FALSE);
1284 }
1285
1286 // Return the list of missing implicit tags.
1287 function getImplicitTags ($oldtags)
1288 {
1289 global $taglist;
1290 $tmp = array();
1291 foreach ($oldtags as $taginfo)
1292 $tmp = array_merge ($tmp, $taglist[$taginfo['id']]['trace']);
1293 // don't call array_unique here, it is in the function we will call now
1294 return buildTagChainFromIds ($tmp);
1295 }
1296
1297 // Minimize the chain: exclude all implicit tags and return the result.
1298 // This function makes use of an external cache with a miss/hit ratio
1299 // about 3/7 (ticket:255).
1300 function getExplicitTagsOnly ($chain)
1301 {
1302 global $taglist, $tagRelCache;
1303 $ret = array();
1304 foreach (array_keys ($chain) as $keyA) // check each A
1305 {
1306 $tagidA = $chain[$keyA]['id'];
1307 // do not include A in result, if A is seen on the trace of any B!=A
1308 foreach (array_keys ($chain) as $keyB)
1309 {
1310 $tagidB = $chain[$keyB]['id'];
1311 if ($tagidA == $tagidB)
1312 continue;
1313 if (!isset ($tagRelCache[$tagidA][$tagidB]))
1314 $tagRelCache[$tagidA][$tagidB] = in_array ($tagidA, $taglist[$tagidB]['trace']);
1315 if ($tagRelCache[$tagidA][$tagidB] === TRUE) // A is ancestor of B
1316 continue 2; // skip this A
1317 }
1318 $ret[] = $chain[$keyA];
1319 }
1320 return $ret;
1321 }
1322
1323 // Check, if the given tag is present on the chain (will only work
1324 // for regular tags with tag ID set.
1325 function tagOnChain ($taginfo, $tagchain)
1326 {
1327 if (!isset ($taginfo['id']))
1328 return FALSE;
1329 foreach ($tagchain as $test)
1330 if ($test['id'] == $taginfo['id'])
1331 return TRUE;
1332 return FALSE;
1333 }
1334
1335 function tagNameOnChain ($tagname, $tagchain)
1336 {
1337 foreach ($tagchain as $test)
1338 if ($test['tag'] == $tagname)
1339 return TRUE;
1340 return FALSE;
1341 }
1342
1343 // Return TRUE, if two tags chains differ (order of tags doesn't matter).
1344 // Assume, that neither of the lists contains duplicates.
1345 // FIXME: a faster, than O(x^2) method is possible for this calculation.
1346 function tagChainCmp ($chain1, $chain2)
1347 {
1348 if (count ($chain1) != count ($chain2))
1349 return TRUE;
1350 foreach ($chain1 as $taginfo1)
1351 if (!tagOnChain ($taginfo1, $chain2))
1352 return TRUE;
1353 return FALSE;
1354 }
1355
1356 function redirectIfNecessary ()
1357 {
1358 global
1359 $trigger,
1360 $pageno,
1361 $tabno;
1362 @session_start();
1363 if
1364 (
1365 ! isset ($_REQUEST['tab']) and
1366 isset ($_SESSION['RTLT'][$pageno]) and
1367 getConfigVar ('SHOW_LAST_TAB') == 'yes' and
1368 permitted ($pageno, $_SESSION['RTLT'][$pageno]['tabname']) and
1369 time() - $_SESSION['RTLT'][$pageno]['time'] <= TAB_REMEMBER_TIMEOUT
1370 )
1371 redirectUser (buildRedirectURL ($pageno, $_SESSION['RTLT'][$pageno]['tabname']));
1372
1373 // check if we accidentaly got on a dynamic tab that shouldn't be shown for this object
1374 if
1375 (
1376 isset ($trigger[$pageno][$tabno]) and
1377 !strlen (call_user_func ($trigger[$pageno][$tabno]))
1378 )
1379 {
1380 $_SESSION['RTLT'][$pageno]['dont_remember'] = 1;
1381 redirectUser (buildRedirectURL ($pageno, 'default'));
1382 }
1383 if (isset ($_SESSION['RTLT'][$pageno]['dont_remember']) && is_array ($_SESSION['RTLT'][$pageno]))
1384 unset ($_SESSION['RTLT'][$pageno]['dont_remember']);
1385 // store the last visited tab name
1386 if (isset ($_REQUEST['tab']))
1387 $_SESSION['RTLT'][$pageno] = array ('tabname' => $tabno, 'time' => time());
1388 session_commit(); // if we are continuing to run, unlock session data
1389 }
1390
1391 function prepareNavigation()
1392 {
1393 global
1394 $pageno,
1395 $tabno;
1396 $pageno = (isset ($_REQUEST['page'])) ? $_REQUEST['page'] : 'index';
1397
1398 if (isset ($_REQUEST['tab']))
1399 $tabno = $_REQUEST['tab'];
1400 else
1401 $tabno = 'default';
1402 }
1403
1404 function fixContext ($target = NULL)
1405 {
1406 global
1407 $pageno,
1408 $auto_tags,
1409 $expl_tags,
1410 $impl_tags,
1411 $target_given_tags,
1412 $user_given_tags,
1413 $etype_by_pageno,
1414 $page;
1415
1416 if ($target !== NULL)
1417 {
1418 $target_given_tags = $target['etags'];
1419 // Don't reset autochain, because auth procedures could push stuff there in.
1420 // Another important point is to ignore 'user' realm, so we don't infuse effective
1421 // context with autotags of the displayed account.
1422 if ($target['realm'] != 'user')
1423 $auto_tags = array_merge ($auto_tags, $target['atags']);
1424 }
1425 elseif (array_key_exists ($pageno, $etype_by_pageno))
1426 {
1427 // Each page listed in the map above requires one uint argument.
1428 $target_realm = $etype_by_pageno[$pageno];
1429 assertUIntArg ($page[$pageno]['bypass']);
1430 $target_id = $_REQUEST[$page[$pageno]['bypass']];
1431 $target = spotEntity ($target_realm, $target_id);
1432 $target_given_tags = $target['etags'];
1433 if ($target['realm'] != 'user')
1434 $auto_tags = array_merge ($auto_tags, $target['atags']);
1435 }
1436 // Explicit and implicit chains should be normally empty at this point, so
1437 // overwrite the contents anyway.
1438 $expl_tags = mergeTagChains ($user_given_tags, $target_given_tags);
1439 $impl_tags = getImplicitTags ($expl_tags);
1440 }
1441
1442 # Merge e/i/a-tags of the given cell structures into current context, when
1443 # these aren't there yet.
1444 function spreadContext ($extracell)
1445 {
1446 global
1447 $auto_tags,
1448 $expl_tags,
1449 $impl_tags,
1450 $target_given_tags,
1451 $user_given_tags;
1452 foreach ($extracell['atags'] as $taginfo)
1453 if (! tagNameOnChain ($taginfo['tag'], $auto_tags))
1454 $auto_tags[] = $taginfo;
1455 $target_given_tags = mergeTagChains ($target_given_tags, $extracell['etags']);
1456 $expl_tags = mergeTagChains ($user_given_tags, $target_given_tags);
1457 $impl_tags = getImplicitTags ($expl_tags);
1458 }
1459
1460 # return a structure suitable for feeding into restoreContext()
1461 function getContext()
1462 {
1463 global
1464 $auto_tags,
1465 $expl_tags,
1466 $impl_tags,
1467 $target_given_tags;
1468 return array
1469 (
1470 'auto_tags' => $auto_tags,
1471 'expl_tags' => $expl_tags,
1472 'impl_tags' => $impl_tags,
1473 'target_given_tags' => $target_given_tags,
1474 );
1475 }
1476
1477 function restoreContext ($ctx)
1478 {
1479 global
1480 $auto_tags,
1481 $expl_tags,
1482 $impl_tags,
1483 $target_given_tags;
1484 $auto_tags = $ctx['auto_tags'];
1485 $expl_tags = $ctx['expl_tags'];
1486 $impl_tags = $ctx['impl_tags'];
1487 $target_given_tags = $ctx['target_given_tags'];
1488 }
1489
1490 // Take a list of user-supplied tag IDs to build a list of valid taginfo
1491 // records indexed by tag IDs (tag chain).
1492 function buildTagChainFromIds ($tagidlist)
1493 {
1494 global $taglist;
1495 $ret = array();
1496 foreach (array_unique ($tagidlist) as $tag_id)
1497 if (isset ($taglist[$tag_id]))
1498 $ret[] = $taglist[$tag_id];
1499 return $ret;
1500 }
1501
1502 function buildTagIdsFromChain ($tagchain)
1503 {
1504 $ret = array();
1505 foreach ($tagchain as $taginfo)
1506 $ret[] = $taginfo['id'];
1507 return array_unique ($ret);
1508 }
1509
1510 // Process a given tag tree and return only meaningful branches. The resulting
1511 // (sub)tree will have refcnt leaves on every last branch.
1512 function getObjectiveTagTree ($tree, $realm, $preselect)
1513 {
1514 $self = __FUNCTION__;
1515 $ret = array();
1516 foreach ($tree as $taginfo)
1517 {
1518 $subsearch = $self ($taginfo['kids'], $realm, $preselect);
1519 // If the current node addresses something, add it to the result
1520 // regardless of how many sub-nodes it features.
1521 if
1522 (
1523 isset ($taginfo['refcnt'][$realm]) or
1524 count ($subsearch) > 1 or
1525 in_array ($taginfo['id'], $preselect)
1526 )
1527 $ret[] = array
1528 (
1529 'id' => $taginfo['id'],
1530 'tag' => $taginfo['tag'],
1531 'parent_id' => $taginfo['parent_id'],
1532 'refcnt' => $taginfo['refcnt'],
1533 'kids' => $subsearch
1534 );
1535 else
1536 $ret = array_merge ($ret, $subsearch);
1537 }
1538 return $ret;
1539 }
1540
1541 // Preprocess tag tree to get only tags which can effectively reduce given filter result,
1542 // than passes shrinked tag tree to getObjectiveTagTree and return its result.
1543 // This makes sense only if andor mode is 'and', otherwise function does not modify tree.
1544 // 'Given filter' is a pair of $entity_list(filter result) and $preselect(filter data).
1545 // 'Effectively' means reduce to non-empty result.
1546 function getShrinkedTagTree($entity_list, $realm, $preselect) {
1547 global $tagtree;
1548 if ($preselect['andor'] != 'and' || empty($entity_list) && $preselect['is_empty'])
1549 return getObjectiveTagTree($tagtree, $realm, $preselect['tagidlist']);
1550
1551 $used_tags = array(); //associative, keys - tag ids, values - taginfos
1552 foreach ($entity_list as $entity)
1553 {
1554 foreach ($entity['etags'] as $etag)
1555 if (! array_key_exists($etag['id'], $used_tags))
1556 $used_tags[$etag['id']] = 1;
1557 else
1558 $used_tags[$etag['id']]++;
1559
1560 foreach ($entity['itags'] as $itag)
1561 if (! array_key_exists($itag['id'], $used_tags))
1562 $used_tags[$itag['id']] = 0;
1563 }
1564
1565 $shrinked_tree = shrinkSubtree($tagtree, $used_tags, $preselect, $realm);
1566 return getObjectiveTagTree($shrinked_tree, $realm, $preselect['tagidlist']);
1567 }
1568
1569 // deletes item from tag subtree unless it exists in $used_tags and not preselected
1570 function shrinkSubtree($tree, $used_tags, $preselect, $realm) {
1571 $self = __FUNCTION__;
1572
1573 foreach($tree as $i => &$item) {
1574 $item['kids'] = $self($item['kids'], $used_tags, $preselect, $realm);
1575 $item['kidc'] = count($item['kids']);
1576 if
1577 (
1578 ! array_key_exists($item['id'], $used_tags) &&
1579 ! in_array($item['id'], $preselect['tagidlist']) &&
1580 ! $item['kidc']
1581 )
1582 unset($tree[$i]);
1583 else {
1584 if (isset ($used_tags[$item['id']]) && $used_tags[$item['id']])
1585 $item['refcnt'][$realm] = $used_tags[$item['id']];
1586 else
1587 unset($item['refcnt'][$realm]);
1588 }
1589 }
1590 return $tree;
1591 }
1592
1593 // Get taginfo record by tag name, return NULL, if record doesn't exist.
1594 function getTagByName ($target_name)
1595 {
1596 global $taglist;
1597 foreach ($taglist as $taginfo)
1598 if ($taginfo['tag'] == $target_name)
1599 return $taginfo;
1600 return NULL;
1601 }
1602
1603 // Merge two chains, filtering dupes out. Return the resulting superset.
1604 function mergeTagChains ($chainA, $chainB)
1605 {
1606 // $ret = $chainA;
1607 // Reindex by tag id in any case.
1608 $ret = array();
1609 foreach ($chainA as $tag)
1610 $ret[$tag['id']] = $tag;
1611 foreach ($chainB as $tag)
1612 if (!isset ($ret[$tag['id']]))
1613 $ret[$tag['id']] = $tag;
1614 return $ret;
1615 }
1616
1617 # Return a list consisting of tag ID of the given tree node and IDs of all
1618 # nodes it contains.
1619 function getTagIDListForNode ($treenode)
1620 {
1621 $self = __FUNCTION__;
1622 $ret = array ($treenode['id']);
1623 foreach ($treenode['kids'] as $item)
1624 $ret = array_merge ($ret, $self ($item));
1625 return $ret;
1626 }
1627
1628 function getCellFilter ()
1629 {
1630 global $sic;
1631 global $pageno;
1632 $andor_used = FALSE;
1633 @session_start();
1634 // 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.
1635 if (isset($_REQUEST['andor']))
1636 $andor_used = TRUE;
1637 if ($andor_used || array_key_exists ('clear-cf', $_REQUEST))
1638 unset($_SESSION[$pageno]); // delete saved filter
1639
1640 // otherwise inject saved filter to the $_REQUEST and $sic vars
1641 elseif (isset ($_SESSION[$pageno]['filter']) and is_array ($_SESSION[$pageno]['filter']) and getConfigVar ('STATIC_FILTER') == 'yes')
1642 foreach (array('andor', 'cfe', 'cft[]', 'cfp[]', 'nft[]', 'nfp[]') as $param)
1643 {
1644 $param = str_replace ('[]', '', $param, $is_array);
1645 if (! isset ($_REQUEST[$param]) and isset ($_SESSION[$pageno]['filter'][$param]) and (!$is_array or is_array ($_SESSION[$pageno]['filter'][$param])))
1646 {
1647 $_REQUEST[$param] = $_SESSION[$pageno]['filter'][$param];
1648 if (! $is_array)
1649 $sic[$param] = $_REQUEST[$param];
1650 }
1651 }
1652
1653 $ret = array
1654 (
1655 'tagidlist' => array(),
1656 'tnamelist' => array(),
1657 'pnamelist' => array(),
1658 'negatedlist' => array(),
1659 'andor' => '',
1660 'text' => '',
1661 'extratext' => '',
1662 'expression' => array(),
1663 'urlextra' => '', // Just put text here and let makeHref call urlencode().
1664 'is_empty' => TRUE,
1665 );
1666 switch (TRUE)
1667 {
1668 case (!isset ($_REQUEST['andor'])):
1669 $andor = getConfigVar ('FILTER_DEFAULT_ANDOR');
1670 break;
1671 case ($_REQUEST['andor'] == 'and'):
1672 case ($_REQUEST['andor'] == 'or'):
1673 $_SESSION[$pageno]['filter']['andor'] = $_REQUEST['andor'];
1674 $ret['andor'] = $andor = $_REQUEST['andor'];
1675 break;
1676 default:
1677 showWarning ('Invalid and/or switch value in submitted form');
1678 return NULL;
1679 }
1680 // Both tags and predicates, which don't exist, should be
1681 // handled somehow. Discard them silently for now.
1682 global $taglist, $pTable;
1683 foreach (array ('cft', 'cfp', 'nft', 'nfp') as $param)
1684 if (isset ($_REQUEST[$param]) and is_array ($_REQUEST[$param]))
1685 {
1686 $_SESSION[$pageno]['filter'][$param] = $_REQUEST[$param];
1687 foreach ($_REQUEST[$param] as $req_key)
1688 {
1689 if (strpos ($param, 'ft') !== FALSE)
1690 {
1691 // param is a taglist
1692 if (! isset ($taglist[$req_key]))
1693 continue;
1694 $ret['tagidlist'][] = $req_key;
1695 $ret['tnamelist'][] = $taglist[$req_key]['tag'];
1696 $text = '{' . $taglist[$req_key]['tag'] . '}';
1697 }
1698 else
1699 {
1700 // param is a predicate list
1701 if (! isset ($pTable[$req_key]))
1702 continue;
1703 $ret['pnamelist'][] = $req_key;
1704 $text = '[' . $req_key . ']';
1705 }
1706 if (strpos ($param, 'nf') === 0)
1707 {
1708 $text = "not $text";
1709 $ret['negatedlist'][] = $req_key;
1710 }
1711 if (! empty ($ret['text']))
1712 {
1713 $andor_used = TRUE;
1714 $ret['text'] .= " $andor ";
1715 }
1716 $ret['text'] .= $text;
1717 $ret['urlextra'] .= '&' . $param . '[]=' . $req_key;
1718 }
1719 }
1720 // Extra text comes from TEXTAREA and is easily screwed by standard escaping function.
1721 if (isset ($sic['cfe']))
1722 {
1723 $_SESSION[$pageno]['filter']['cfe'] = $sic['cfe'];
1724 // Only consider extra text, when it is a correct RackCode expression.
1725 $parse = spotPayload ($sic['cfe'], 'SYNT_EXPR');
1726 if ($parse['result'] == 'ACK')
1727 {
1728 $ret['extratext'] = trim ($sic['cfe']);
1729 $ret['urlextra'] .= '&cfe=' . $ret['extratext'];
1730 }
1731 }
1732 $finaltext = array();
1733 if (strlen ($ret['text']))
1734 $finaltext[] = '(' . $ret['text'] . ')';
1735 if (strlen ($ret['extratext']))
1736 $finaltext[] = '(' . $ret['extratext'] . ')';
1737 $andor_used = $andor_used || (count($finaltext) > 1);
1738 $finaltext = implode (' ' . $andor . ' ', $finaltext);
1739 if (strlen ($finaltext))
1740 {
1741 $ret['is_empty'] = FALSE;
1742 $parse = spotPayload ($finaltext, 'SYNT_EXPR');
1743 $ret['expression'] = $parse['result'] == 'ACK' ? $parse['load'] : NULL;
1744 // It's not quite fair enough to put the blame of the whole text onto
1745 // non-empty "extra" portion of it, but it's the only user-generated portion
1746 // of it, thus the most probable cause of parse error.
1747 if (strlen ($ret['extratext']))
1748 $ret['extraclass'] = $parse['result'] == 'ACK' ? 'validation-success' : 'validation-error';
1749 }
1750 if (! $andor_used)
1751 $ret['andor'] = getConfigVar ('FILTER_DEFAULT_ANDOR');
1752 else
1753 $ret['urlextra'] .= '&andor=' . $ret['andor'];
1754 return $ret;
1755 }
1756
1757 function buildRedirectURL ($nextpage = NULL, $nexttab = NULL, $moreArgs = array())
1758 {
1759 global $page, $pageno, $tabno;
1760 if ($nextpage === NULL)
1761 $nextpage = $pageno;
1762 if ($nexttab === NULL)
1763 $nexttab = $tabno;
1764 $url = "index.php?page=${nextpage}&tab=${nexttab}";
1765 if (isset ($page[$nextpage]['bypass']))
1766 $url .= '&' . $page[$nextpage]['bypass'] . '=' . $_REQUEST[$page[$nextpage]['bypass']];
1767 if (isset ($page[$nextpage]['bypass_tabs']))
1768 foreach ($page[$nextpage]['bypass_tabs'] as $param_name)
1769 if (isset ($_REQUEST[$param_name]))
1770 $url .= '&' . urlencode ($param_name) . '=' . urlencode ($_REQUEST[$param_name]);
1771
1772 if (count ($moreArgs) > 0)
1773 foreach ($moreArgs as $arg => $value)
1774 if (is_array ($value))
1775 foreach ($value as $v)
1776 $url .= '&' . urlencode ($arg . '[]') . '=' . urlencode ($v);
1777 elseif ($arg != 'module')
1778 $url .= '&' . urlencode ($arg) . '=' . urlencode ($value);
1779 return $url;
1780 }
1781
1782 function redirectUser ($url)
1783 {
1784 global $log_messages;
1785 if (! empty ($log_messages))
1786 {
1787 @session_start();
1788 $_SESSION['log'] = $log_messages;
1789 }
1790 header ("Location: " . $url);
1791 die;
1792 }
1793
1794 function getRackCodeStats ()
1795 {
1796 global $rackCode;
1797 $defc = $grantc = $modc = 0;
1798 foreach ($rackCode as $s)
1799 switch ($s['type'])
1800 {
1801 case 'SYNT_DEFINITION':
1802 $defc++;
1803 break;
1804 case 'SYNT_GRANT':
1805 $grantc++;
1806 break;
1807 case 'SYNT_CTXMOD':
1808 $modc++;
1809 break;
1810 default:
1811 break;
1812 }
1813 $ret = array
1814 (
1815 'Definition sentences' => $defc,
1816 'Grant sentences' => $grantc,
1817 'Context mod sentences' => $modc
1818 );
1819 return $ret;
1820 }
1821
1822 function getRackImageWidth ()
1823 {
1824 global $rtwidth;
1825 return 3 + $rtwidth[0] + $rtwidth[1] + $rtwidth[2] + 3;
1826 }
1827
1828 function getRackImageHeight ($units)
1829 {
1830 return 3 + 3 + $units * 2;
1831 }
1832
1833 // Indicate occupation state of each IP address: none, ordinary or problematic.
1834 function markupIPAddrList (&$addrlist)
1835 {
1836 foreach (array_keys ($addrlist) as $ip_bin)
1837 {
1838 $refc = array
1839 (
1840 'shared' => 0, // virtual
1841 'virtual' => 0, // loopback
1842 'regular' => 0, // connected host
1843 'router' => 0 // connected gateway
1844 );
1845 foreach ($addrlist[$ip_bin]['allocs'] as $a)
1846 $refc[$a['type']]++;
1847 $nvirtloopback = ($refc['shared'] + $refc['virtual'] + $refc['router'] > 0) ? 1 : 0; // modulus of virtual + shared + router
1848 $nreserved = ($addrlist[$ip_bin]['reserved'] == 'yes') ? 1 : 0; // only one reservation is possible ever
1849 $nrealms = $nreserved + $nvirtloopback + $refc['regular']; // last is connected allocation
1850
1851 if ($nrealms == 1)
1852 $addrlist[$ip_bin]['class'] = 'trbusy';
1853 elseif ($nrealms > 1)
1854 $addrlist[$ip_bin]['class'] = 'trerror';
1855 elseif (! empty ($addrlist[$ip_bin]['vslist']) or ! empty ($addrlist[$ip_bin]['rsplist']))
1856 $addrlist[$ip_bin]['class'] = 'trbusy';
1857 else
1858 $addrlist[$ip_bin]['class'] = '';
1859 }
1860 }
1861
1862 // Scan the given address list (returned by scanIPv4Space/scanIPv6Space) and return a list of all routers found.
1863 function findRouters ($addrlist)
1864 {
1865 $ret = array();
1866 foreach ($addrlist as $addr)
1867 foreach ($addr['allocs'] as $alloc)
1868 if ($alloc['type'] == 'router')
1869 $ret[] = array
1870 (
1871 'id' => $alloc['object_id'],
1872 'iface' => $alloc['name'],
1873 'dname' => $alloc['object_name'],
1874 'ip_bin' => $addr['ip_bin'],
1875 );
1876 return $ret;
1877 }
1878
1879 // Assist in tag chain sorting.
1880 function taginfoCmp ($tagA, $tagB)
1881 {
1882 return $tagA['ci'] - $tagB['ci'];
1883 }
1884
1885 // Compare networks. When sorting a tree, the records on the list will have
1886 // distinct base IP addresses.
1887 // "The comparison function must return an integer less than, equal to, or greater
1888 // than zero if the first argument is considered to be respectively less than,
1889 // equal to, or greater than the second." (c) PHP manual
1890 function IPNetworkCmp ($netA, $netB)
1891 {
1892 $ret = strcmp ($netA['ip_bin'], $netB['ip_bin']);
1893 if ($ret == 0)
1894 $ret = $netA['mask'] < $netB['mask'] ? -1 : ($netA['mask'] > $netB['mask'] ? 1 : 0);
1895 if ($ret == -1 and $netA['ip_bin'] === ($netB['ip_bin'] & $netA['mask_bin']))
1896 $ret = -2;
1897 return $ret;
1898 }
1899
1900 function IPNetContainsOrEqual ($netA, $netB)
1901 {
1902 $res = IPNetworkCmp ($netA, $netB);
1903 return ($res == -2 || $res == 0);
1904 }
1905
1906 function IPNetContains ($netA, $netB)
1907 {
1908 return (-2 == IPNetworkCmp ($netA, $netB));
1909 }
1910
1911 function ip_in_range ($ip_bin, $range)
1912 {
1913 return ($ip_bin & $range['mask_bin']) === $range['ip_bin'];
1914 }
1915
1916 // Modify the given tag tree so, that each level's items are sorted alphabetically.
1917 function sortTree (&$tree, $sortfunc = '')
1918 {
1919 if (!strlen ($sortfunc))
1920 return;
1921 $self = __FUNCTION__;
1922 usort ($tree, $sortfunc);
1923 // Don't make a mistake of directly iterating over the items of current level, because this way
1924 // the sorting will be performed on a _copy_ if each item, not the item itself.
1925 foreach (array_keys ($tree) as $tagid)
1926 $self ($tree[$tagid]['kids'], $sortfunc);
1927 }
1928
1929 function iptree_fill (&$netdata)
1930 {
1931 if (!isset ($netdata['kids']) or !count ($netdata['kids']))
1932 return;
1933 // If we really have nested prefixes, they must fit into the tree.
1934 $worktree = $netdata;
1935 foreach ($netdata['kids'] as $pfx)
1936 iptree_embed ($worktree, $pfx);
1937 $netdata['kids'] = iptree_construct ($worktree);
1938 $netdata['kidc'] = count ($netdata['kids']);
1939 }
1940
1941 function iptree_construct ($node)
1942 {
1943 $self = __FUNCTION__;
1944
1945 if (!isset ($node['right']))
1946 {
1947 if (!isset ($node['kids']))
1948 {
1949 $node['kids'] = array();
1950 $node['kidc'] = 0;
1951 $node['name'] = '';
1952 }
1953 return array ($node);
1954 }
1955 else
1956 return array_merge ($self ($node['left']), $self ($node['right']));
1957 }
1958
1959 function ip_format ($ip_bin)
1960 {
1961 switch (strlen ($ip_bin))
1962 {
1963 case 4: return ip4_format ($ip_bin);
1964 case 16: return ip6_format ($ip_bin);
1965 default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
1966 }
1967 }
1968
1969 function ip4_format ($ip_bin)
1970 {
1971 if (4 != ($len = strlen ($ip_bin)))
1972 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
1973 return implode ('.', unpack ('C*', $ip_bin));
1974 }
1975
1976 function ip6_format ($ip_bin)
1977 {
1978 // maybe this is IPv6-to-IPv4 address?
1979 if (substr ($ip_bin, 0, 12) == "\0\0\0\0\0\0\0\0\0\0\xff\xff")
1980 return '::ffff:' . implode ('.', unpack ('C*', substr ($ip_bin, 12, 4)));
1981
1982 $result = array();
1983 $hole_index = NULL;
1984 $max_hole_index = NULL;
1985 $hole_length = 0;
1986 $max_hole_length = 0;
1987
1988 for ($i = 0; $i < 8; $i++)
1989 {
1990 $unpacked = unpack ('n', substr ($ip_bin, $i * 2, 2));
1991 $value = array_shift ($unpacked);
1992 $result[] = dechex ($value & 0xffff);
1993 if ($value != 0)
1994 {
1995 unset ($hole_index);
1996 $hole_length = 0;
1997 }
1998 else
1999 {
2000 if (! isset ($hole_index))
2001 $hole_index = $i;
2002 if (++$hole_length >= $max_hole_length)
2003 {
2004 $max_hole_index = $hole_index;
2005 $max_hole_length = $hole_length;
2006 }
2007 }
2008 }
2009 if (isset ($max_hole_index))
2010 {
2011 array_splice ($result, $max_hole_index, $max_hole_length, array (''));
2012 if ($max_hole_index == 0 && $max_hole_length == 8)
2013 return '::';
2014 elseif ($max_hole_index == 0)
2015 return ':' . implode (':', $result);
2016 elseif ($max_hole_index + $max_hole_length == 8)
2017 return implode (':', $result) . ':';
2018 }
2019 return implode (':', $result);
2020 }
2021
2022 function ip_parse ($ip)
2023 {
2024 if (FALSE !== strpos ($ip, ':'))
2025 return ip6_parse ($ip);
2026 else
2027 return ip4_parse ($ip);
2028 }
2029
2030 function ip4_parse ($ip)
2031 {
2032 if (FALSE === ($int = ip2long ($ip)))
2033 throw new InvalidArgException ('ip', $ip, "Invalid IPv4 address");
2034 else
2035 return pack ('N', $int);
2036 }
2037
2038 // returns 16-byte string ip_bin
2039 // throws exception if unable to parse
2040 function ip6_parse ($ip)
2041 {
2042 do {
2043 if (empty ($ip))
2044 break;
2045
2046 $result = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; // 16 bytes
2047 // remove one of double beginning/tailing colons
2048 if (substr ($ip, 0, 2) == '::')
2049 $ip = substr ($ip, 1);
2050 elseif (substr ($ip, -2, 2) == '::')
2051 $ip = substr ($ip, 0, strlen ($ip) - 1);
2052
2053 $tokens = explode (':', $ip);
2054 $last_token = $tokens[count ($tokens) - 1];
2055 $split = explode ('.', $last_token);
2056 if (count ($split) == 4)
2057 {
2058 $hex_tokens = array();
2059 $hex_tokens[] = dechex ($split[0] * 256 + $split[1]);
2060 $hex_tokens[] = dechex ($split[2] * 256 + $split[3]);
2061 array_splice ($tokens, -1, 1, $hex_tokens);
2062 }
2063 if (count ($tokens) > 8)
2064 break;
2065 for ($i = 0; $i < count ($tokens); $i++)
2066 {
2067 if ($tokens[$i] != '')
2068 {
2069 if (! set_word_value ($result, $i, $tokens[$i]))
2070 break;
2071 }
2072 else
2073 {
2074 $k = 8; //index in result string (last word)
2075 for ($j = count ($tokens) - 1; $j > $i; $j--) // $j is an index in $tokens for reverse walk
2076 if ($tokens[$j] == '')
2077 break;
2078 elseif (! set_word_value ($result, --$k, $tokens[$j]))
2079 break;
2080 if ($i != $j)
2081 break; //error, more than 1 '::' range
2082 break;
2083 }
2084 }
2085 if (! isset ($k) && count ($tokens) != 8)
2086 break;
2087 return $result;
2088 } while (FALSE);
2089
2090 throw new InvalidArgException ('ip', $ip, "Invalid IPv6 address");
2091 }
2092
2093 function ip_get_arpa ($ip_bin)
2094 {
2095 switch (strlen ($ip_bin))
2096 {
2097 case 4: return ip4_get_arpa ($ip_bin);
2098 case 16: return ip6_get_arpa ($ip_bin);
2099 default: throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2100 }
2101 }
2102
2103 function ip4_get_arpa ($ip_bin)
2104 {
2105 $ret = '';
2106 for ($i = 3; $i >= 0; $i--)
2107 $ret .= ord($ip_bin[$i]) . '.';
2108 return $ret . 'in-addr.arpa';
2109 }
2110
2111 function ip6_get_arpa ($ip_bin)
2112 {
2113 $ret = '';
2114 $hex = implode ('', unpack('H32', $ip_bin));
2115 for ($i = 31; $i >= 0; $i--)
2116 $ret .= $hex[$i] . '.';
2117 return $ret . 'ip6.arpa';
2118 }
2119
2120 function set_word_value (&$haystack, $nword, $hexvalue)
2121 {
2122 // check that $hexvalue is like /^[0-9a-fA-F]*$/
2123 for ($i = 0; $i < strlen ($hexvalue); $i++)
2124 {
2125 $char = ord ($hexvalue[$i]);
2126 if (! ($char >= 0x30 && $char <= 0x39 || $char >= 0x41 && $char <= 0x46 || $char >=0x61 && $char <= 0x66))
2127 return FALSE;
2128 }
2129 $haystack = substr_replace ($haystack, pack ('n', hexdec ($hexvalue)), $nword * 2, 2);
2130 return TRUE;
2131 }
2132
2133 // returns binary IP or FALSE
2134 function ip_checkparse ($ip)
2135 {
2136 try
2137 {
2138 return ip_parse ($ip);
2139 }
2140 catch (InvalidArgException $e)
2141 {
2142 return FALSE;
2143 }
2144 }
2145
2146 // returns binary IP or FALSE
2147 function ip4_checkparse ($ip)
2148 {
2149 try
2150 {
2151 return ip4_parse ($ip);
2152 }
2153 catch (InvalidArgException $e)
2154 {
2155 return FALSE;
2156 }
2157 }
2158
2159 // returns binary IP or FALSE
2160 function ip6_checkparse ($ip)
2161 {
2162 try
2163 {
2164 return ip6_parse ($ip);
2165 }
2166 catch (InvalidArgException $e)
2167 {
2168 return FALSE;
2169 }
2170 }
2171
2172 function ip4_int2bin ($ip_int)
2173 {
2174 return pack ('N', $ip_int);
2175 }
2176
2177 function ip4_bin2int ($ip_bin)
2178 {
2179 if (4 != strlen ($ip_bin))
2180 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2181 $list = unpack ('N', $ip_bin);
2182 return array_shift ($list);
2183 }
2184
2185 // Use this function only when you need to export binary ip out of PHP running context (e.g., DB)
2186 // !DO NOT perform arithmetic and bitwise operations with the result of this function!
2187 function ip4_bin2db ($ip_bin)
2188 {
2189 $ip_int = ip4_bin2int ($ip_bin);
2190 if ($ip_int < 0)
2191 return sprintf ('%u', 0x00000000 + $ip_int);
2192 else
2193 return $ip_int;
2194 }
2195
2196 function ip_last ($net)
2197 {
2198 return $net['ip_bin'] | ~$net['mask_bin'];
2199 }
2200
2201 function ip_next ($ip_bin)
2202 {
2203 $ret = $ip_bin;
2204 $p = 1;
2205 for ($i = strlen ($ret) - 1; $i >= 0; $i--)
2206 {
2207 $oct = $p + ord ($ret[$i]);
2208 $ret[$i] = chr ($oct & 0xff);
2209 if ($oct <= 255 and $oct >= 0)
2210 break;
2211 }
2212 return $ret;
2213 }
2214
2215 function ip_prev ($ip_bin)
2216 {
2217 $ret = $ip_bin;
2218 $p = -1;
2219 for ($i = strlen ($ret) - 1; $i >= 0; $i--)
2220 {
2221 $oct = $p + ord ($ret[$i]);
2222 $ret[$i] = chr ($oct & 0xff);
2223 if ($oct <= 255 and $oct >= 0)
2224 break;
2225 }
2226 return $ret;
2227 }
2228
2229 function ip4_range_size ($range)
2230 {
2231 return ip4_mask_size ($range['mask']);
2232 }
2233
2234 function ip4_mask_size ($mask)
2235 {
2236 return (0xffffffff >> $mask) + 1;
2237 }
2238
2239 // returns array with keys 'ip', 'ip_bin', 'mask', 'mask_bin'
2240 function constructIPRange ($ip_bin, $mask)
2241 {
2242 $node = array();
2243 switch (strlen ($ip_bin))
2244 {
2245 case 4: // IPv4
2246 if ($mask < 0 || $mask > 32)
2247 throw new InvalidArgException ('mask', $mask, "Invalid v4 prefix length");
2248 $node['mask_bin'] = ip4_mask ($mask);
2249 $node['mask'] = $mask;
2250 $node['ip_bin'] = $ip_bin & $node['mask_bin'];
2251 $node['ip'] = ip4_format ($node['ip_bin']);
2252 break;
2253 case 16: // IPv6
2254 if ($mask < 0 || $mask > 128)
2255 throw new InvalidArgException ('mask', $mask, "Invalid v6 prefix length");
2256 $node['mask_bin'] = ip6_mask ($mask);
2257 $node['mask'] = $mask;
2258 $node['ip_bin'] = $ip_bin & $node['mask_bin'];
2259 $node['ip'] = ip6_format ($node['ip_bin']);
2260 break;
2261 default:
2262 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2263 }
2264 return $node;
2265 }
2266
2267 // Return minimal IP address structure
2268 function constructIPAddress ($ip_bin)
2269 {
2270 // common v4/v6 part
2271 $ret = array
2272 (
2273 'ip' => ip_format ($ip_bin),
2274 'ip_bin' => $ip_bin,
2275 'name' => '',
2276 'reserved' => 'no',
2277 'allocs' => array(),
2278 'vslist' => array(),
2279 'rsplist' => array(),
2280 );
2281
2282 // specific v4 part
2283 if (strlen ($ip_bin) == 4)
2284 $ret = array_merge
2285 (
2286 $ret,
2287 array
2288 (
2289 'outpf' => array(),
2290 'inpf' => array(),
2291 )
2292 );
2293 return $ret;
2294 }
2295
2296 function iptree_embed (&$node, $pfx)
2297 {
2298 $self = __FUNCTION__;
2299
2300 // hit?
2301 if (0 == IPNetworkCmp ($node, $pfx))
2302 {
2303 $node = $pfx;
2304 return;
2305 }
2306 if ($node['mask'] == $pfx['mask'])
2307 throw new RackTablesError ('the recurring loop lost control', RackTablesError::INTERNAL);
2308
2309 // split?
2310 if (!isset ($node['right']))
2311 {
2312 $node['left'] = constructIPRange ($node['ip_bin'], $node['mask'] + 1);
2313 $node['right'] = constructIPRange (ip_last ($node), $node['mask'] + 1);
2314 }
2315
2316 if (IPNetContainsOrEqual ($node['left'], $pfx))
2317 $self ($node['left'], $pfx);
2318 elseif (IPNetContainsOrEqual ($node['right'], $pfx))
2319 $self ($node['right'], $pfx);
2320 else
2321 throw new RackTablesError ('cannot decide between left and right', RackTablesError::INTERNAL);
2322 }
2323
2324 function treeApplyFunc (&$tree, $func = '', $stopfunc = '')
2325 {
2326 if (!strlen ($func))
2327 return;
2328 $self = __FUNCTION__;
2329 foreach (array_keys ($tree) as $key)
2330 {
2331 $func ($tree[$key]);
2332 if (strlen ($stopfunc) and $stopfunc ($tree[$key]))
2333 continue;
2334 $self ($tree[$key]['kids'], $func);
2335 }
2336 }
2337
2338 function nodeIsCollapsed ($node)
2339 {
2340 return $node['symbol'] == 'node-collapsed';
2341 }
2342
2343 // sets 'addrlist', 'own_addrlist', 'addrc', 'own_addrc' keys of $node
2344 // 'addrc' and 'own_addrc' are sizes of 'addrlist' and 'own_addrlist', respectively
2345 // 'own_addrlist' is empty if the node network has no children
2346 function loadIPAddrList (&$node)
2347 {
2348 $node['addrlist'] = scanIPSpace (array (array ('first' => $node['ip_bin'], 'last' => ip_last ($node))));
2349
2350 if (! isset ($node['id']))
2351 $node['own_addrlist'] = $node['addrlist'];
2352 else
2353 {
2354 if ($node['kidc'] == 0)
2355 $node['own_addrlist'] = $node['addrlist'];
2356 //$node['own_addrlist'] = array();
2357 else
2358 {
2359 $node['own_addrlist'] = array();
2360 // node has childs
2361 foreach ($node['spare_ranges'] as $mask => $spare_list)
2362 foreach ($spare_list as $spare_ip)
2363 {
2364 $spare_range = constructIPRange ($spare_ip, $mask);
2365 foreach ($node['addrlist'] as $bin_ip => $addr)
2366 if (($bin_ip & $spare_range['mask_bin']) == $spare_range['ip_bin'])
2367 $node['own_addrlist'][$bin_ip] = $addr;
2368 }
2369 }
2370 }
2371 $node['addrc'] = count ($node['addrlist']);
2372 $node['own_addrc'] = count ($node['own_addrlist']);
2373 }
2374
2375 function getIPv4OwnRangeSize ($range)
2376 {
2377 if (empty ($range['spare_ranges']))
2378 return ip4_range_size ($range);
2379 else
2380 {
2381 $ret = 0;
2382 foreach ($range['spare_ranges'] as $mask => $spare_list)
2383 $ret += count ($spare_list) * ip4_mask_size ($mask);
2384 return $ret;
2385 }
2386 }
2387
2388 // returns the array of structure described by constructIPAddress
2389 function getIPAddress ($ip_bin)
2390 {
2391 $scanres = scanIPSpace (array (array ('first' => $ip_bin, 'last' => $ip_bin)));
2392 if (empty ($scanres))
2393 return constructIPAddress ($ip_bin);
2394 markupIPAddrList ($scanres);
2395 return $scanres[$ip_bin];
2396 }
2397
2398 function getIPv4Address ($ip_bin)
2399 {
2400 if (strlen ($ip_bin) != 4)
2401 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2402 return getIPAddress ($ip_bin);
2403 }
2404
2405 function getIPv6Address ($ip_bin)
2406 {
2407 if (strlen ($ip_bin) != 16)
2408 throw new InvalidArgException ('ip_bin', $ip_bin, "Invalid binary IP");
2409 return getIPAddress ($ip_bin);
2410 }
2411
2412 function makeIPTree ($netlist)
2413 {
2414 // treeFromList() requires parent_id to be correct for an item to get onto the tree,
2415 // so perform necessary pre-processing to calculate parent_id of each item of $netlist.
2416 $stack = array();
2417 foreach ($netlist as $net_id => &$net)
2418 {
2419 while (! empty ($stack))
2420 {
2421 $top_id = $stack[count ($stack) - 1];
2422 if (! IPNetContains ($netlist[$top_id], $net)) // unless $net is a child of stack top
2423 array_pop ($stack);
2424 else
2425 {
2426 $net['parent_id'] = $top_id;
2427 break;
2428 }
2429 }
2430 if (empty ($stack))
2431 $net['parent_id'] = NULL;
2432 array_push ($stack, $net_id);
2433 }
2434 unset ($stack);
2435
2436 $tree = treeFromList ($netlist); // medium call
2437 sortTree ($tree, 'IPNetworkCmp');
2438 return $tree;
2439 }
2440
2441 function prepareIPTree ($netlist, $expanded_id = 0)
2442 {
2443 $tree = makeIPTree ($netlist);
2444 // complement the tree before markup to make the spare networks have "symbol" set
2445 treeApplyFunc ($tree, 'iptree_fill');
2446 iptree_markup_collapsion ($tree, getConfigVar ('TREE_THRESHOLD'), $expanded_id);
2447 return $tree;
2448 }
2449
2450 # Traverse IPv4/IPv6 tree and return a list of all networks, which
2451 # exist in DB and don't have any sub-networks.
2452 function getTerminalNetworks ($tree)
2453 {
2454 $self = __FUNCTION__;
2455 $ret = array();
2456 foreach ($tree as $node)
2457 if ($node['kidc'] == 0 and isset ($node['realm']))
2458 $ret[] = $node;
2459 else
2460 $ret = array_merge ($ret, $self ($node['kids']));
2461 return $ret;
2462 }
2463
2464 // Check all items of the tree recursively, until the requested target id is
2465 // found. Mark all items leading to this item as "expanded", collapsing all
2466 // the rest, which exceed the given threshold (if the threshold is given).
2467 function iptree_markup_collapsion (&$tree, $threshold = 1024, $target = 0)
2468 {
2469 $self = __FUNCTION__;
2470 $ret = FALSE;
2471 foreach (array_keys ($tree) as $key)
2472 {
2473 $here = ($target === 'ALL' or ($target > 0 and isset ($tree[$key]['id']) and $tree[$key]['id'] == $target));
2474 $below = $self ($tree[$key]['kids'], $threshold, $target);
2475 $expand_enabled = ($target !== 'NONE');
2476 if (!$tree[$key]['kidc']) // terminal node
2477 $tree[$key]['symbol'] = 'spacer';
2478 elseif ($expand_enabled and $tree[$key]['kidc'] < $threshold)
2479 $tree[$key]['symbol'] = 'node-expanded-static';
2480 elseif ($expand_enabled and ($here or $below))
2481 $tree[$key]['symbol'] = 'node-expanded';
2482 else
2483 $tree[$key]['symbol'] = 'node-collapsed';
2484 $ret = ($ret or $here or $below); // parentheses are necessary for this to be computed correctly
2485 }
2486 return $ret;
2487 }
2488
2489 // Convert entity name to human-readable value
2490 function formatEntityName ($name) {
2491 switch ($name)
2492 {
2493 case 'ipv4net':
2494 return 'IPv4 Network';
2495 case 'ipv6net':
2496 return 'IPv6 Network';
2497 case 'ipv4rspool':
2498 return 'IPv4 RS Pool';
2499 case 'ipv4vs':
2500 return 'IPv4 Virtual Service';
2501 case 'object':
2502 return 'Object';
2503 case 'rack':
2504 return 'Rack';
2505 case 'location':
2506 return 'Location';
2507 case 'user':
2508 return 'User';
2509 }
2510 return 'invalid';
2511 }
2512
2513 // Display hrefs for all of a file's parents. If scissors are requested,
2514 // prepend cutting button to each of them.
2515 function serializeFileLinks ($links, $scissors = FALSE)
2516 {
2517 $comma = '';
2518 $ret = '';
2519 foreach ($links as $link_id => $li)
2520 {
2521 switch ($li['entity_type'])
2522 {
2523 case 'ipv4net':
2524 $params = "page=ipv4net&id=";
2525 break;
2526 case 'ipv6net':
2527 $params = "page=ipv6net&id=";
2528 break;
2529 case 'ipv4rspool':
2530 $params = "page=ipv4rspool&pool_id=";
2531 break;
2532 case 'ipv4vs':
2533 $params = "page=ipv4vs&vs_id=";
2534 break;
2535 case 'object':
2536 $params = "page=object&object_id=";
2537 break;
2538 case 'rack':
2539 $params = "page=rack&rack_id=";
2540 break;
2541 case 'location':
2542 $params = "page=location&location_id=";
2543 break;
2544 case 'user':
2545 $params = "page=user&user_id=";
2546 break;
2547 }
2548 $ret .= $comma;
2549 if ($scissors)
2550 {
2551 $ret .= "<a href='" . makeHrefProcess(array('op'=>'unlinkFile', 'link_id'=>$link_id)) . "'";
2552 $ret .= getImageHREF ('cut') . '</a> ';
2553 }
2554 $ret .= sprintf("<a href='index.php?%s%s'>%s</a>", $params, $li['entity_id'], $li['name']);
2555 $comma = '<br>';
2556 }
2557 return $ret;
2558 }
2559
2560 // Convert filesize to appropriate unit and make it human-readable
2561 function formatFileSize ($bytes) {
2562 // bytes
2563 if($bytes < 1024) // bytes
2564 return "${bytes} bytes";
2565
2566 // kilobytes
2567 if ($bytes < 1024000)
2568 return sprintf ("%.1fk", round (($bytes / 1024), 1));
2569
2570 // megabytes
2571 return sprintf ("%.1f MB", round (($bytes / 1024000), 1));
2572 }
2573
2574 // Reverse of formatFileSize, it converts human-readable value to bytes
2575 function convertToBytes ($value) {
2576 $value = trim($value);
2577 $last = strtolower($value[strlen($value)-1]);
2578 switch ($last)
2579 {
2580 case 'g':
2581 $value *= 1024;
2582 case 'm':
2583 $value *= 1024;
2584 case 'k':
2585 $value *= 1024;
2586 }
2587
2588 return $value;
2589 }
2590
2591 // make "A" HTML element
2592 function mkA ($text, $nextpage, $bypass = NULL, $nexttab = NULL)
2593 {
2594 global $page, $tab;
2595 if ($text == '')
2596 throw new InvalidArgException ('text', $text);
2597 if (! array_key_exists ($nextpage, $page))
2598 throw new InvalidArgException ('nextpage', $nextpage, 'not found');
2599 $args = array ('page' => $nextpage);
2600 if ($nexttab !== NULL)
2601 {
2602 if (! array_key_exists ($nexttab, $tab[$nextpage]))
2603 throw new InvalidArgException ('nexttab', $nexttab, 'not found');
2604 $args['tab'] = $nexttab;
2605 }
2606 if (array_key_exists ('bypass', $page[$nextpage]))
2607 {
2608 if ($bypass === NULL)
2609 throw new InvalidArgException ('bypass', '(NULL)');
2610 $args[$page[$nextpage]['bypass']] = $bypass;
2611 }
2612 return '<a href="' . makeHref ($args) . '">' . $text . '</a>';
2613 }
2614
2615 // make "HREF" HTML attribute
2616 function makeHref ($params = array())
2617 {
2618 $tmp = array();
2619 foreach ($params as $key => $value)
2620 {
2621 if (is_array ($value))
2622 $key .= "[]";
2623 else
2624 $value = array ($value);
2625 if (!count ($value))
2626 $tmp[] = urlencode ($key) . '=';
2627 else
2628 foreach ($value as $sub_value)
2629 $tmp[] = urlencode ($key) . '=' . urlencode ($sub_value);
2630 }
2631 return 'index.php?' . implode ('&', $tmp);
2632 }
2633
2634 function makeHrefProcess ($params = array())
2635 {
2636 global $pageno, $tabno, $page;
2637 $tmp = array();
2638 if (! array_key_exists ('page', $params))
2639 $params['page'] = $pageno;
2640 if (! array_key_exists ('tab', $params))
2641 $params['tab'] = $tabno;
2642 if (isset ($page[$pageno]['bypass']))
2643 {
2644 $bypass = $page[$pageno]['bypass'];
2645 if (! array_key_exists ($bypass, $params) and array_key_exists ($bypass, $_REQUEST))
2646 $params[$bypass] = $_REQUEST[$bypass];
2647 }
2648 foreach ($params as $key => $value)
2649 $tmp[] = urlencode ($key) . '=' . urlencode ($value);
2650 return '?module=redirect&' . implode ('&', $tmp);
2651 }
2652
2653 function makeHrefForHelper ($helper_name, $params = array())
2654 {
2655 $ret = '?module=popup&helper=' . $helper_name;
2656 foreach($params as $key=>$value)
2657 $ret .= '&'.urlencode($key).'='.urlencode($value);
2658 return $ret;
2659 }
2660
2661 // Process the given list of records to build data suitable for printNiftySelect()
2662 // (like it was formerly executed by printSelect()). Screen out vendors according
2663 // to VENDOR_SIEVE, if object type ID is provided. However, the OPTGROUP with already
2664 // selected OPTION is protected from being screened.
2665 function cookOptgroups ($recordList, $object_type_id = 0, $existing_value = 0)
2666 {
2667 $ret = array();
2668 // Always keep "other" OPTGROUP at the SELECT bottom.
2669 $therest = array();
2670 foreach ($recordList as $dict_key => $dict_value)
2671 if (strpos ($dict_value, '%GSKIP%') !== FALSE)
2672 {
2673 $tmp = explode ('%GSKIP%', $dict_value, 2);
2674 $ret[$tmp[0]][$dict_key] = $tmp[1];
2675 }
2676 elseif (strpos ($dict_value, '%GPASS%') !== FALSE)
2677 {
2678 $tmp = explode ('%GPASS%', $dict_value, 2);
2679 $ret[$tmp[0]][$dict_key] = $tmp[1];
2680 }
2681 else
2682 $therest[$dict_key] = $dict_value;
2683 if ($object_type_id != 0)
2684 {
2685 $screenlist = array();
2686 foreach (explode (';', getConfigVar ('VENDOR_SIEVE')) as $sieve)
2687 if (preg_match ("/^([^@]+)(@${object_type_id})?\$/", trim ($sieve), $regs)){
2688 $screenlist[] = $regs[1];
2689 }
2690 foreach (array_keys ($ret) as $vendor)
2691 if (in_array ($vendor, $screenlist))
2692 {
2693 $ok_to_screen = TRUE;
2694 if ($existing_value)
2695 foreach (array_keys ($ret[$vendor]) as $recordkey)
2696 if ($recordkey == $existing_value)
2697 {
2698 $ok_to_screen = FALSE;
2699 break;
2700 }
2701 if ($ok_to_screen)
2702 unset ($ret[$vendor]);
2703 }
2704 }
2705 $ret['other'] = $therest;
2706 return $ret;
2707 }
2708
2709 function unix2dos ($text)
2710 {
2711 return str_replace ("\n", "\r\n", $text);
2712 }
2713
2714 function buildPredicateTable ($parsetree)
2715 {
2716 $ret = array();
2717 foreach ($parsetree as $sentence)
2718 if ($sentence['type'] == 'SYNT_DEFINITION')
2719 $ret[$sentence['term']] = $sentence['definition'];
2720 // Now we have predicate table filled in with the latest definitions of each
2721 // particular predicate met. This isn't as chik, as on-the-fly predicate
2722 // overloading during allow/deny scan, but quite sufficient for this task.
2723 return $ret;
2724 }
2725
2726 // Take a list of records and filter against given RackCode expression. Return
2727 // the original list intact, if there was no filter requested, but return an
2728 // empty list, if there was an error.
2729 function filterCellList ($list_in, $expression = array())
2730 {
2731 if ($expression === NULL)
2732 return array();
2733 if (!count ($expression))
2734 return $list_in;
2735 $list_out = array();
2736 foreach ($list_in as $item_key => $item_value)
2737 if (TRUE === judgeCell ($item_value, $expression))
2738 $list_out[$item_key] = $item_value;
2739 return $list_out;
2740 }
2741
2742 function eval_expression ($expr, $tagchain, $ptable, $silent = FALSE)
2743 {
2744 $self = __FUNCTION__;
2745 switch ($expr['type'])
2746 {
2747 // Return true, if given tag is present on the tag chain.
2748 case 'LEX_TAG':
2749 case 'LEX_AUTOTAG':
2750 foreach ($tagchain as $tagInfo)
2751 if ($expr['load'] == $tagInfo['tag'])
2752 return TRUE;
2753 return FALSE;
2754 case 'LEX_PREDICATE': // Find given predicate in the symbol table and evaluate it.
2755 $pname = $expr['load'];
2756 if (!isset ($ptable[$pname]))
2757 {
2758 if (!$silent)
2759 showWarning ("Predicate '${pname}' is referenced before declaration");
2760 return NULL;
2761 }
2762 return $self ($ptable[$pname], $tagchain, $ptable);
2763 case 'LEX_TRUE':
2764 return TRUE;
2765 case 'LEX_FALSE':
2766 return FALSE;
2767 case 'SYNT_NOT_EXPR':
2768 $tmp = $self ($expr['load'], $tagchain, $ptable);
2769 if ($tmp === TRUE)
2770 return FALSE;
2771 elseif ($tmp === FALSE)
2772 return TRUE;
2773 else
2774 return $tmp;
2775 case 'SYNT_AND_EXPR': // binary AND
2776 if (FALSE == $self ($expr['left'], $tagchain, $ptable))
2777 return FALSE; // early failure
2778 return $self ($expr['right'], $tagchain, $ptable);
2779 case 'SYNT_EXPR': // binary OR
2780 if (TRUE == $self ($expr['left'], $tagchain, $ptable))
2781 return TRUE; // early success
2782 return $self ($expr['right'], $tagchain, $ptable);
2783 default:
2784 if (!$silent)
2785 showWarning ("Evaluation error, cannot process expression type '${expr['type']}'");
2786 return NULL;
2787 break;
2788 }
2789 }
2790
2791 // Tell, if the given expression is true for the given entity. Take complete record on input.
2792 function judgeCell ($cell, $expression)
2793 {
2794 global $pTable;
2795 return eval_expression
2796 (
2797 $expression,
2798 array_merge
2799 (
2800 $cell['etags'],
2801 $cell['itags'],
2802 $cell['atags']
2803 ),
2804 $pTable,
2805 TRUE
2806 );
2807 }
2808
2809 function judgeContext ($expression)
2810 {
2811 global $pTable, $expl_tags, $impl_tags, $auto_tags;
2812 return eval_expression
2813 (
2814 $expression,
2815 array_merge
2816 (
2817 $expl_tags,
2818 $impl_tags,
2819 $auto_tags
2820 ),
2821 $pTable,
2822 TRUE
2823 );
2824 }
2825
2826 // Tell, if a constraint from config option permits given record.
2827 // An undefined $cell means current context.
2828 function considerConfiguredConstraint ($cell, $varname)
2829 {
2830 if (!strlen (getConfigVar ($varname)))
2831 return TRUE; // no restriction
2832 global $parseCache;
2833 if (!isset ($parseCache[$varname]))
2834 // getConfigVar() doesn't re-read the value from DB because of its
2835 // own cache, so there is no race condition here between two calls.
2836 $parseCache[$varname] = spotPayload (getConfigVar ($varname), 'SYNT_EXPR');
2837 if ($parseCache[$varname]['result'] != 'ACK')
2838 return FALSE; // constraint set, but cannot be used due to compilation error
2839 if (isset ($cell))
2840 return judgeCell ($cell, $parseCache[$varname]['load']);
2841 else
2842 return judgeContext ($parseCache[$varname]['load']);
2843 }
2844
2845 // Tell, if the given arbitrary RackCode text addresses the given record
2846 // (an empty text matches any record).
2847 // An undefined $cell means current context.
2848 function considerGivenConstraint ($cell, $filtertext)
2849 {
2850 if ($filtertext == '')
2851 return TRUE;
2852 $parse = spotPayload ($filtertext, 'SYNT_EXPR');
2853 if ($parse['result'] != 'ACK')
2854 throw new InvalidRequestArgException ('filtertext', $filtertext, 'RackCode parsing error');
2855 if (isset ($cell))
2856 return judgeCell ($cell, $parse['load']);
2857 else
2858 return judgeContext ($parse['load']);
2859 }
2860
2861 // Return list of records in the given realm, which conform to
2862 // the given RackCode expression. If the realm is unknown or text
2863 // doesn't validate as a RackCode expression, return NULL.
2864 // Otherwise (successful scan) return a list of all matched
2865 // records, even if the list is empty (array() !== NULL). If the
2866 // text is an empty string, return all found records in the given
2867 // realm.
2868 function scanRealmByText ($realm = NULL, $ftext = '')
2869 {
2870 switch ($realm)
2871 {
2872 case 'object':
2873 case 'rack':
2874 case 'user':
2875 case 'ipv4net':
2876 case 'ipv6net':
2877 case 'file':
2878 case 'ipv4vs':
2879 case 'ipv4rspool':
2880 case 'vst':
2881 if (!strlen ($ftext = trim ($ftext)))
2882 $fexpr = array();
2883 else
2884 {
2885 $fparse = spotPayload ($ftext, 'SYNT_EXPR');
2886 if ($fparse['result'] != 'ACK')
2887 return NULL;
2888 $fexpr = $fparse['load'];
2889 }
2890 return filterCellList (listCells ($realm), $fexpr);
2891 default:
2892 throw new InvalidArgException ('$realm', $realm);
2893 }
2894
2895 }
2896
2897 function getVSTOptions()
2898 {
2899 $ret = array();
2900 foreach (listCells ('vst') as $vst)
2901 $ret[$vst['id']] = niftyString ($vst['description'], 30, FALSE);
2902 return $ret;
2903 }
2904
2905 # Return a 2-dimensional array in the format understood by getNiftySelect(),
2906 # which would contain all VLANs of all VLAN domains except domains, which IDs
2907 # are present in $except_domains argument.
2908 function getAllVLANOptions ($except_domains = array())
2909 {
2910 $ret = array();
2911 foreach (getVLANDomainStats() as $domain)
2912 if (! in_array ($domain['id'], $except_domains))
2913 foreach (getDomainVLANs ($domain['id']) as $vlan)
2914 $ret[$domain['description']]["${domain['id']}-${vlan['vlan_id']}"] =
2915 "${vlan['vlan_id']} (${vlan['netc']}) ${vlan['vlan_descr']}";
2916 return $ret;
2917 }
2918
2919 // Let's have this debug helper here to enable debugging of process.php w/o interface.php.
2920 function dump ($var)
2921 {
2922 echo '<div align=left><pre>';
2923 print_r ($var);
2924 echo '</pre></div>';
2925 }
2926
2927 function getTagChart ($limit = 0, $realm = 'total', $special_tags = array())
2928 {
2929 global $taglist;
2930 // first build top-N chart...
2931 $toplist = array();
2932 foreach ($taglist as $taginfo)
2933 if (isset ($taginfo['refcnt'][$realm]))
2934 $toplist[$taginfo['id']] = $taginfo['refcnt'][$realm];
2935 arsort ($toplist, SORT_NUMERIC);
2936 $ret = array();
2937 $done = 0;
2938 foreach (array_keys ($toplist) as $tag_id)
2939 {
2940 $ret[$tag_id] = $taglist[$tag_id];
2941 if (++$done == $limit)
2942 break;
2943 }
2944 // ...then make sure, that every item of the special list is shown
2945 // (using the same sort order)
2946 $extra = array();
2947 foreach ($special_tags as $taginfo)
2948 if (!array_key_exists ($taginfo['id'], $ret))
2949 $extra[$taginfo['id']] = $taglist[$taginfo['id']]['refcnt'][$realm];
2950 arsort ($extra, SORT_NUMERIC);
2951 foreach (array_keys ($extra) as $tag_id)
2952 $ret[] = $taglist[$tag_id];
2953 return $ret;
2954 }
2955
2956 function decodeObjectType ($objtype_id, $style = 'r')
2957 {
2958 static $types = array();
2959 if (!count ($types))
2960 $types = array
2961 (
2962 'r' => readChapter (CHAP_OBJTYPE),
2963 'a' => readChapter (CHAP_OBJTYPE, 'a'),
2964 'o' => readChapter (CHAP_OBJTYPE, 'o')
2965 );
2966 return $types[$style][$objtype_id];
2967 }
2968
2969 function isolatedPermission ($p, $t, $cell)
2970 {
2971 // This function is called from both "file" page and a number of other pages,
2972 // which have already fixed security context and authorized the user for it.
2973 // OTOH, it is necessary here to authorize against the current file, which
2974 // means saving the current context and building a new one.
2975 global
2976 $expl_tags,
2977 $impl_tags,
2978 $target_given_tags,
2979 $auto_tags;
2980 // push current context
2981 $orig_expl_tags = $expl_tags;
2982 $orig_impl_tags = $impl_tags;
2983 $orig_target_given_tags = $target_given_tags;
2984 $orig_auto_tags = $auto_tags;
2985 // retarget
2986 fixContext ($cell);
2987 // remember decision
2988 $ret = permitted ($p, $t);
2989 // pop context
2990 $expl_tags = $orig_expl_tags;
2991 $impl_tags = $orig_impl_tags;
2992 $target_given_tags = $orig_target_given_tags;
2993 $auto_tags = $orig_auto_tags;
2994 return $ret;
2995 }
2996
2997 function getPortListPrefs()
2998 {
2999 $ret = array();
3000 if (0 >= ($ret['iif_pick'] = getConfigVar ('DEFAULT_PORT_IIF_ID')))
3001 $ret['iif_pick'] = 1;
3002 $ret['oif_picks'] = array();