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