r1652 + upgrade auth seems fixed now
[racktables] / inc / functions.php
CommitLineData
b325120a 1<?php
e673ee24
DO
2/*
3*
4* This file is a library of computational functions for RackTables.
5*
6*/
7
8$loclist[0] = 'front';
9$loclist[1] = 'interior';
10$loclist[2] = 'rear';
11$loclist['front'] = 0;
12$loclist['interior'] = 1;
13$loclist['rear'] = 2;
14$template[0] = array (TRUE, TRUE, TRUE);
15$template[1] = array (TRUE, TRUE, FALSE);
16$template[2] = array (FALSE, TRUE, TRUE);
17$template[3] = array (TRUE, FALSE, FALSE);
18$template[4] = array (FALSE, TRUE, FALSE);
19$template[5] = array (FALSE, FALSE, TRUE);
20$templateWidth[0] = 3;
21$templateWidth[1] = 2;
22$templateWidth[2] = 2;
23$templateWidth[3] = 1;
24$templateWidth[4] = 1;
25$templateWidth[5] = 1;
26
27// Objects of some types should be explicitly shown as
28// anonymous (labelless). This function is a single place where the
29// decision about displayed name is made.
30function displayedName ($objectData)
31{
32 if ($objectData['name'] != '')
33 return $objectData['name'];
9c0b0016 34 elseif (in_array ($objectData['objtype_id'], explode (',', getConfigVar ('NAMEFUL_OBJTYPES'))))
a0ec6295 35 return "ANONYMOUS " . $objectData['objtype_name'];
e673ee24 36 else
a0ec6295 37 return "[${objectData['objtype_name']}]";
e673ee24
DO
38}
39
40// This function finds height of solid rectangle of atoms, which are all
41// assigned to the same object. Rectangle base is defined by specified
42// template.
43function rectHeight ($rackData, $startRow, $template_idx)
44{
45 $height = 0;
46 // The first met object_id is used to match all the folowing IDs.
47 $object_id = 0;
48 global $template;
49 do
50 {
51 for ($locidx = 0; $locidx < 3; $locidx++)
52 {
53 // At least one value in template is TRUE, but the following block
54 // can meet 'skipped' atoms. Let's ensure we have something after processing
55 // the first row.
56 if ($template[$template_idx][$locidx])
57 {
58 if (isset ($rackData[$startRow - $height][$locidx]['skipped']))
59 break 2;
93e02204
DO
60 if (isset ($rackData[$startRow - $height][$locidx]['rowspan']))
61 break 2;
62 if (isset ($rackData[$startRow - $height][$locidx]['colspan']))
63 break 2;
e673ee24
DO
64 if ($rackData[$startRow - $height][$locidx]['state'] != 'T')
65 break 2;
66 if ($object_id == 0)
67 $object_id = $rackData[$startRow - $height][$locidx]['object_id'];
68 if ($object_id != $rackData[$startRow - $height][$locidx]['object_id'])
69 break 2;
70 }
71 }
72 // If the first row can't offer anything, bail out.
73 if ($height == 0 and $object_id == 0)
74 break;
75 $height++;
76 }
77 while ($startRow - $height > 0);
93e02204
DO
78# echo "for startRow==${startRow} and template==(" . ($template[$template_idx][0] ? 'T' : 'F');
79# echo ', ' . ($template[$template_idx][1] ? 'T' : 'F') . ', ' . ($template[$template_idx][2] ? 'T' : 'F');
80# echo ") height==${height}<br>\n";
e673ee24
DO
81 return $height;
82}
83
84// This function marks atoms to be avoided by rectHeight() and assigns rowspan/colspan
85// attributes.
86function markSpan (&$rackData, $startRow, $maxheight, $template_idx)
87{
88 global $template, $templateWidth;
89 $colspan = 0;
90 for ($height = 0; $height < $maxheight; $height++)
91 {
92 for ($locidx = 0; $locidx < 3; $locidx++)
93 {
94 if ($template[$template_idx][$locidx])
95 {
96 // Add colspan/rowspan to the first row met and mark the following ones to skip.
93e02204
DO
97 // Explicitly show even single-cell spanned atoms, because rectHeight()
98 // is expeciting this data for correct calculation.
e673ee24
DO
99 if ($colspan != 0)
100 $rackData[$startRow - $height][$locidx]['skipped'] = TRUE;
101 else
102 {
103 $colspan = $templateWidth[$template_idx];
93e02204 104 if ($colspan >= 1)
e673ee24 105 $rackData[$startRow - $height][$locidx]['colspan'] = $colspan;
93e02204 106 if ($maxheight >= 1)
e673ee24
DO
107 $rackData[$startRow - $height][$locidx]['rowspan'] = $maxheight;
108 }
109 }
110 }
111 }
112 return;
113}
114
93e02204
DO
115// This function sets rowspan/solspan/skipped atom attributes for renderRack()
116// What we actually have to do is to find _all_ possible rectangles for each unit
117// and then select the widest of those with the maximal square.
e673ee24
DO
118function markAllSpans (&$rackData = NULL)
119{
120 if ($rackData == NULL)
121 {
61e269b5 122 showError ('Invalid rackData', __FUNCTION__);
e673ee24
DO
123 return;
124 }
125 for ($i = $rackData['height']; $i > 0; $i--)
93e02204
DO
126 while (markBestSpan ($rackData, $i));
127}
128
129// Calculate height of 6 possible span templates (array is presorted by width
130// descending) and mark the best (if any).
131function markBestSpan (&$rackData, $i)
132{
133 global $template, $templateWidth;
134 for ($j = 0; $j < 6; $j++)
e673ee24 135 {
93e02204
DO
136 $height[$j] = rectHeight ($rackData, $i, $j);
137 $square[$j] = $height[$j] * $templateWidth[$j];
138 }
139 // find the widest rectangle of those with maximal height
140 $maxsquare = max ($square);
141 if (!$maxsquare)
142 return FALSE;
143 $best_template_index = 0;
144 for ($j = 0; $j < 6; $j++)
145 if ($square[$j] == $maxsquare)
e673ee24 146 {
93e02204
DO
147 $best_template_index = $j;
148 $bestheight = $height[$j];
149 break;
e673ee24 150 }
93e02204
DO
151 // distribute span marks
152 markSpan ($rackData, $i, $bestheight, $best_template_index);
153 return TRUE;
e673ee24
DO
154}
155
156function delRow ($row_id = 0)
157{
158 if ($row_id == 0)
159 {
61e269b5 160 showError ('Not all required args are present.', __FUNCTION__);
e673ee24
DO
161 return;
162 }
163 if (!isset ($_REQUEST['confirmed']) || $_REQUEST['confirmed'] != 'true')
164 {
165 echo "Press <a href='?op=del_row&row_id=${row_id}&confirmed=true'>here</a> to confirm rack row deletion.";
166 return;
167 }
168 global $dbxlink;
169 echo 'Deleting rack row information: ';
170 $result = $dbxlink->query ("update RackRow set deleted = 'yes' where id=${row_id} limit 1");
171 if ($result->rowCount() != 1)
172 {
61e269b5 173 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1', __FUNCTION__);
e673ee24
DO
174 return;
175 }
176 echo 'OK<br>';
177 recordHistory ('RackRow', "id = ${row_id}");
178 echo "Information was deleted. You may return to <a href='?op=list_rows&editmode=on'>rack row list</a>.";
179}
180
181function delRack ($rack_id = 0)
182{
183 if ($rack_id == 0)
184 {
61e269b5 185 showError ('Not all required args are present.', __FUNCTION__);
e673ee24
DO
186 return;
187 }
188 if (!isset ($_REQUEST['confirmed']) || $_REQUEST['confirmed'] != 'true')
189 {
190 echo "Press <a href='?op=del_rack&rack_id=${rack_id}&confirmed=true'>here</a> to confirm rack deletion.";
191 return;
192 }
193 global $dbxlink;
194 echo 'Deleting rack information: ';
195 $result = $dbxlink->query ("update Rack set deleted = 'yes' where id=${rack_id} limit 1");
196 if ($result->rowCount() != 1)
197 {
61e269b5 198 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1', __FUNCTION__);
e673ee24
DO
199 return;
200 }
201 echo 'OK<br>';
202 recordHistory ('Rack', "id = ${rack_id}");
203 echo "Information was deleted. You may return to <a href='?op=list_racks&editmode=on'>rack list</a>.";
204}
205
206function delObject ($object_id = 0)
207{
208 if ($object_id == 0)
209 {
61e269b5 210 showError ('Not all required args are present.', __FUNCTION__);
e673ee24
DO
211 return;
212 }
213 if (!isset ($_REQUEST['confirmed']) || $_REQUEST['confirmed'] != 'true')
214 {
215 echo "Press <a href='?op=del_object&object_id=${object_id}&confirmed=true'>here</a> to confirm object deletion.";
216 return;
217 }
218 global $dbxlink;
219 echo 'Deleting object information: ';
220 $result = $dbxlink->query ("update RackObject set deleted = 'yes' where id=${object_id} limit 1");
221 if ($result->rowCount() != 1)
222 {
61e269b5 223 showError ('Marked ' . $result.rowCount() . ' rows as deleted, but expected 1', __FUNCTION__);
e673ee24
DO
224 return;
225 }
226 echo 'OK<br>';
227 recordHistory ('RackObject', "id = ${object_id}");
228 echo "Information was deleted. You may return to <a href='?op=list_objects&editmode=on'>object list</a>.";
229}
230
231// We can mount 'F' atoms and unmount our own 'T' atoms.
232function applyObjectMountMask (&$rackData, $object_id)
233{
234 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
235 for ($locidx = 0; $locidx < 3; $locidx++)
236 switch ($rackData[$unit_no][$locidx]['state'])
237 {
238 case 'F':
239 $rackData[$unit_no][$locidx]['enabled'] = TRUE;
240 break;
241 case 'T':
242 $rackData[$unit_no][$locidx]['enabled'] = ($rackData[$unit_no][$locidx]['object_id'] == $object_id);
243 break;
244 default:
245 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
246 }
247}
248
249// Design change means transition between 'F' and 'A' and back.
250function applyRackDesignMask (&$rackData)
251{
252 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
253 for ($locidx = 0; $locidx < 3; $locidx++)
254 switch ($rackData[$unit_no][$locidx]['state'])
255 {
256 case 'F':
257 case 'A':
258 $rackData[$unit_no][$locidx]['enabled'] = TRUE;
259 break;
260 default:
261 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
262 }
263}
264
265// The same for 'F' and 'U'.
266function applyRackProblemMask (&$rackData)
267{
268 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
269 for ($locidx = 0; $locidx < 3; $locidx++)
270 switch ($rackData[$unit_no][$locidx]['state'])
271 {
272 case 'F':
273 case 'U':
274 $rackData[$unit_no][$locidx]['enabled'] = TRUE;
275 break;
276 default:
277 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
278 }
279}
280
281// This mask should allow toggling 'T' and 'W' on object's rackspace.
282function applyObjectProblemMask (&$rackData)
283{
284 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
285 for ($locidx = 0; $locidx < 3; $locidx++)
286 switch ($rackData[$unit_no][$locidx]['state'])
287 {
288 case 'T':
289 case 'W':
290 $rackData[$unit_no][$locidx]['enabled'] = ($rackData[$unit_no][$locidx]['object_id'] == $object_id);
291 break;
292 default:
293 $rackData[$unit_no][$locidx]['enabled'] = FALSE;
294 }
295}
296
297// This function highlights specified object (and removes previous highlight).
298function highlightObject (&$rackData, $object_id)
299{
300 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
301 for ($locidx = 0; $locidx < 3; $locidx++)
302 if
303 (
304 $rackData[$unit_no][$locidx]['state'] == 'T' and
305 $rackData[$unit_no][$locidx]['object_id'] == $object_id
306 )
307 $rackData[$unit_no][$locidx]['hl'] = 'h';
308 else
309 unset ($rackData[$unit_no][$locidx]['hl']);
310}
311
312// This function marks atoms to selected or not depending on their current state.
313function markupAtomGrid (&$data, $checked_state)
314{
315 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
316 for ($locidx = 0; $locidx < 3; $locidx++)
317 {
318 if (!($data[$unit_no][$locidx]['enabled'] === TRUE))
319 continue;
320 if ($data[$unit_no][$locidx]['state'] == $checked_state)
321 $data[$unit_no][$locidx]['checked'] = ' checked';
322 else
323 $data[$unit_no][$locidx]['checked'] = '';
324 }
325}
326
327// This function is almost a clone of processGridForm(), but doesn't save anything to database
328// Return value is the changed rack data.
329// Here we assume that correct filter has already been applied, so we just
330// set or unset checkbox inputs w/o changing atom state.
331function mergeGridFormToRack (&$rackData)
332{
333 $rack_id = $rackData['id'];
334 for ($unit_no = $rackData['height']; $unit_no > 0; $unit_no--)
335 for ($locidx = 0; $locidx < 3; $locidx++)
336 {
337 if ($rackData[$unit_no][$locidx]['enabled'] != TRUE)
338 continue;
339 $inputname = "atom_${rack_id}_${unit_no}_${locidx}";
340 if (isset ($_REQUEST[$inputname]) and $_REQUEST[$inputname] == 'on')
341 $rackData[$unit_no][$locidx]['checked'] = ' checked';
342 else
343 $rackData[$unit_no][$locidx]['checked'] = '';
344 }
345}
346
347function binMaskFromDec ($maskL)
348{
349 $binmask=0;
350 for ($i=0; $i<$maskL; $i++)
351 {
352 $binmask*=2;
353 $binmask+=1;
354 }
355 for ($i=$maskL; $i<32; $i++)
356 {
357 $binmask*=2;
358 }
359 return $binmask;
360}
361
362function binInvMaskFromDec ($maskL)
363{
364 $binmask=0;
365 for ($i=0; $i<$maskL; $i++)
366 {
367 $binmask*=2;
368 }
369 for ($i=$maskL; $i<32; $i++)
370 {
371 $binmask*=2;
372 $binmask+=1;
373 }
374 return $binmask;
375}
376
2dee289e 377function addRange ($range='', $name='', $is_bcast = FALSE)
e673ee24 378{
2a201216
DY
379 // $range is in x.x.x.x/x format, split into ip/mask vars
380 $rangeArray = explode('/', $range);
381 $ip = $rangeArray[0];
382 $mask = $rangeArray[1];
383
c87fd3fe
DO
384 if (empty ($ip) or empty ($mask))
385 return "Invalid IP subnet '${range}'";
e673ee24
DO
386 $ipL = ip2long($ip);
387 $maskL = ip2long($mask);
388 if ($ipL == -1 || $ipL === FALSE)
389 return 'Bad ip address';
390 if ($mask < 32 && $mask > 0)
391 $maskL = $mask;
392 else
393 {
394 $maskB = decbin($maskL);
395 if (strlen($maskB)!=32)
396 return 'Bad mask';
397 $ones=0;
398 $zeroes=FALSE;
399 foreach( str_split ($maskB) as $digit)
400 {
401 if ($digit == '0')
402 $zeroes = TRUE;
403 if ($digit == '1')
404 {
405 $ones++;
406 if ($zeroes == TRUE)
407 return 'Bad mask';
408 }
409 }
410 $maskL = $ones;
411 }
412 $binmask = binMaskFromDec($maskL);
413 $ipL = $ipL & $binmask;
414
415 $query =
416 "select ".
417 "id, ip, mask, name ".
418 "from IPRanges ";
419
420
421 global $dbxlink;
422
423 $result = $dbxlink->query ($query);
424
425 while ($row = $result->fetch (PDO::FETCH_ASSOC))
426 {
427 $otherip = $row['ip'];
428 $othermask = binMaskFromDec($row['mask']);
429// echo "checking $otherip & $othermask ".($otherip & $othermask)." == $ipL & $othermask ".($ipL & $othermask)." ".decbin($otherip)." ".decbin($othermask)." ".decbin($otherip & $othermask)." ".decbin($ipL)." ".decbin($othermask)." ".decbin($ipL & $othermask)."\n";
430// echo "checking $otherip & $binmask ".($otherip & $binmask)." == $ipL & $binmask ".($ipL & $binmask)." ".decbin($otherip)." ".decbin($binmask)." ".decbin($otherip & $binmask)." ".decbin($ipL)." ".decbin($binmask)." ".decbin($ipL & $binmask)."\n";
431// echo "\n";
432// flush();
433 if (($otherip & $othermask) == ($ipL & $othermask))
434 return "This subnet intersects with ".long2ip($row['ip'])."/${row['mask']}";
435 if (($otherip & $binmask) == ($ipL & $binmask))
436 return "This subnet intersects with ".long2ip($row['ip'])."/${row['mask']}";
437 }
438 $result->closeCursor();
439 $query =
440 "insert into IPRanges set ip=".sprintf('%u', $ipL).", mask='$maskL', name='$name'";
441 $result = $dbxlink->exec ($query);
2dee289e
DO
442
443 if ($is_bcast and $maskL < 31)
444 {
445 $network_addr = long2ip ($ipL);
446 $broadcast_addr = long2ip ($ipL | binInvMaskFromDec ($maskL));
447 updateAddress ($network_addr, 'network', 'yes');
448 updateAddress ($broadcast_addr, 'broadcast', 'yes');
449 }
e673ee24
DO
450 return '';
451}
452
453function getIPRange ($id = 0)
454{
455 global $dbxlink;
456 $query =
457 "select ".
458 "id as IPRanges_id, ".
459 "INET_NTOA(ip) as IPRanges_ip, ".
e673ee24
DO
460 "mask as IPRanges_mask, ".
461 "name as IPRanges_name ".
462 "from IPRanges ".
463 "where id = '$id'";
464 $result = $dbxlink->query ($query);
465 if ($result == NULL)
e673ee24 466 return NULL;
5c43a978 467 $ret = array();
55cc3e4e
DO
468 $row = $result->fetch (PDO::FETCH_ASSOC);
469 if ($row == NULL)
470 return $ret;
471 $ret['id'] = $row['IPRanges_id'];
472 $ret['ip'] = $row['IPRanges_ip'];
7e039e88 473 $ret['ip_bin'] = ip2long ($row['IPRanges_ip']);
55cc3e4e
DO
474 $ret['mask_bin'] = binMaskFromDec($row['IPRanges_mask']);
475 $ret['mask_bin_inv'] = binInvMaskFromDec($row['IPRanges_mask']);
476 $ret['name'] = $row['IPRanges_name'];
477 $ret['mask'] = $row['IPRanges_mask'];
478 $ret['addrlist'] = array();
55cc3e4e 479 $result->closeCursor();
7e039e88
DO
480 // We risk losing some significant bits in an unsigned 32bit integer,
481 // unless it is converted to a string.
482 $db_first = "'" . sprintf ('%u', 0x00000000 + $ret['ip_bin'] & $ret['mask_bin']) . "'";
483 $db_last = "'" . sprintf ('%u', 0x00000000 + $ret['ip_bin'] | ($ret['mask_bin_inv'])) . "'";
5c43a978
DO
484
485 // Don't try to build up the whole structure in a single pass. Request
486 // the list of user comments and reservations and merge allocations in
487 // at a latter point.
55cc3e4e 488 $query =
7e039e88
DO
489 "select INET_NTOA(ip) as ip, name, reserved from IPAddress " .
490 "where ip between ${db_first} and ${db_last} " .
5c43a978
DO
491 "and (reserved = 'yes' or name != '')";
492 $ipa_res = $dbxlink->query ($query);
493 if ($ipa_res == NULL)
55cc3e4e 494 return $ret;
5c43a978 495 while ($row = $ipa_res->fetch (PDO::FETCH_ASSOC))
55cc3e4e 496 {
7e039e88
DO
497 $ip_bin = ip2long ($row['ip']);
498 $ret['addrlist'][$ip_bin] = $row;
5c43a978
DO
499 $tmp = array();
500 foreach (array ('ip', 'name', 'reserved') as $cname)
501 $tmp[$cname] = $row[$cname];
502 $tmp['references'] = array();
7e039e88 503 $ret['addrlist'][$ip_bin] = $tmp;
5c43a978
DO
504 }
505 $ipa_res->closeCursor();
e673ee24 506
5c43a978 507 $query =
dda31a62
DO
508 "select INET_NTOA(ipb.ip) as ip, ro.id as object_id, " .
509 "ro.name as object_name, ipb.name, ipb.type, objtype_id, " .
510 "dict_value as objtype_name from " .
5c43a978 511 "IPBonds as ipb inner join RackObject as ro on ipb.object_id = ro.id " .
dda31a62 512 "left join Dictionary on objtype_id=dict_key natural join Chapter " .
7e039e88 513 "where ip between ${db_first} and ${db_last} " .
dda31a62 514 "and chapter_name = 'RackObjectType'" .
5c43a978
DO
515 "order by ipb.type, object_name";
516 $ipb_res = $dbxlink->query ($query);
517 while ($row = $ipb_res->fetch (PDO::FETCH_ASSOC))
518 {
7e039e88
DO
519 $ip_bin = ip2long ($row['ip']);
520 if (!isset ($ret['addrlist'][$ip_bin]))
55cc3e4e 521 {
7e039e88
DO
522 $ret['addrlist'][$ip_bin] = array();
523 $ret['addrlist'][$ip_bin]['ip'] = $row['ip'];
524 $ret['addrlist'][$ip_bin]['name'] = '';
525 $ret['addrlist'][$ip_bin]['reserved'] = 'no';
526 $ret['addrlist'][$ip_bin]['references'] = array();
e673ee24 527 }
5c43a978 528 $tmp = array();
dda31a62 529 foreach (array ('object_id', 'type', 'name') as $cname)
5c43a978 530 $tmp[$cname] = $row[$cname];
dda31a62
DO
531 $quasiobject['name'] = $row['object_name'];
532 $quasiobject['objtype_id'] = $row['objtype_id'];
533 $quasiobject['objtype_name'] = $row['objtype_name'];
534 $tmp['object_name'] = displayedName ($quasiobject);
7e039e88 535 $ret['addrlist'][$ip_bin]['references'][] = $tmp;
e673ee24 536 }
5c43a978
DO
537 $ipb_res->closeCursor();
538
e673ee24
DO
539 return $ret;
540}
541
dda31a62
DO
542// Don't require any records in IPAddress, but if there is one,
543// merge the data between getting allocation list. Collect enough data
544// to call displayedName() ourselves.
e673ee24
DO
545function getIPAddress ($ip=0)
546{
547 $ret = array();
e673ee24
DO
548 $ret['bonds'] = array();
549 $ret['outpf'] = array();
550 $ret['inpf'] = array();
551 $ret['exists'] = 0;
dda31a62 552 $ret['name'] = '';
e673ee24
DO
553 $ret['reserved'] = 'no';
554 global $dbxlink;
ebdce584 555 $query1 =
e673ee24 556 "select ".
dda31a62 557 "name, reserved ".
e673ee24 558 "from IPAddress ".
dda31a62 559 "where ip = INET_ATON('$ip') and (reserved = 'yes' or name != '')";
ebdce584
DO
560 $result1 = $dbxlink->query ($query1);
561 if ($result1 == NULL)
e673ee24 562 return NULL;
ebdce584 563 if ($row = $result1->fetch (PDO::FETCH_ASSOC))
e673ee24 564 {
959c4f4c 565 $ret['exists'] = 1;
959c4f4c
DO
566 $ret['name'] = $row['name'];
567 $ret['reserved'] = $row['reserved'];
e673ee24 568 }
ebdce584 569 $result1->closeCursor();
e673ee24 570
ebdce584 571 $query2 =
dda31a62
DO
572 "select ".
573 "IPBonds.object_id as object_id, ".
574 "IPBonds.name as name, ".
575 "IPBonds.type as type, ".
576 "objtype_id, dict_value as objtype_name, " .
577 "RackObject.name as object_name ".
578 "from IPBonds join RackObject on IPBonds.object_id=RackObject.id ".
579 "left join Dictionary on objtype_id=dict_key natural join Chapter " .
580 "where IPBonds.ip=INET_ATON('$ip') ".
581 "and chapter_name = 'RackObjectType' " .
582 "order by RackObject.id, IPBonds.name";
ebdce584 583 $result2 = $dbxlink->query ($query2);
dda31a62 584 $count=0;
ebdce584 585 while ($row = $result2->fetch (PDO::FETCH_ASSOC))
e673ee24 586 {
dda31a62
DO
587 $ret['bonds'][$count]['object_id'] = $row['object_id'];
588 $ret['bonds'][$count]['name'] = $row['name'];
589 $ret['bonds'][$count]['type'] = $row['type'];
590 $qo = array();
591 $qo['name'] = $row['object_name'];
592 $qo['objtype_id'] = $row['objtype_id'];
593 $qo['objtype_name'] = $row['objtype_name'];
594 $ret['bonds'][$count]['object_name'] = displayedName ($qo);
595 $count++;
596 $ret['exists'] = 1;
e673ee24 597 }
ebdce584 598 $result2->closeCursor();
e673ee24
DO
599
600 return $ret;
601}
602
603function bindIpToObject ($ip='', $object_id=0, $name='', $type='')
604{
605 global $dbxlink;
606
607 $range = getRangeByIp($ip);
e673ee24
DO
608 if (!$range)
609 return 'Non-existant ip address. Try adding IP range first';
610
20a92aa2
DO
611 $result = useInsertBlade
612 (
613 'IPBonds',
614 array
615 (
dda31a62 616 'ip' => "INET_ATON('$ip')",
20a92aa2
DO
617 'object_id' => "'${object_id}'",
618 'name' => "'${name}'",
619 'type' => "'${type}'"
620 )
621 );
622 return $result ? '' : 'useInsertBlade() failed in bindIpToObject()';
e673ee24
DO
623}
624
625// This function looks up 'has_problems' flag for 'T' atoms
626// and modifies 'hl' key. May be, this should be better done
627// in getRackData(). We don't honour 'skipped' key, because
628// the function is also used for thumb creation.
629function markupObjectProblems (&$rackData)
630{
631 for ($i = $rackData['height']; $i > 0; $i--)
632 for ($locidx = 0; $locidx < 3; $locidx++)
633 if ($rackData[$i][$locidx]['state'] == 'T')
634 {
635 $object = getObjectInfo ($rackData[$i][$locidx]['object_id']);
636 if ($object['has_problems'] == 'yes')
637 {
638 // Object can be already highlighted.
639 if (isset ($rackData[$i][$locidx]['hl']))
640 $rackData[$i][$locidx]['hl'] = $rackData[$i][$locidx]['hl'] . 'w';
641 else
642 $rackData[$i][$locidx]['hl'] = 'w';
643 }
644 }
645}
646
647function search_cmpObj ($a, $b)
648{
649 return ($a['score'] > $b['score'] ? -1 : 1);
650}
651
652// This function performs search and then calculates score for each result.
653// Given previous search results in $objects argument, it adds new results
654// to the array and updates score for existing results, if it is greater than
655// existing score.
656function mergeSearchResults (&$objects, $terms, $fieldname)
657{
658 global $dbxlink;
659 $query =
660 "select name, label, asset_no, barcode, ro.id, dict_key as objtype_id, " .
661 "dict_value as objtype_name, asset_no from RackObject as ro inner join Dictionary " .
662 "on objtype_id = dict_key natural join Chapter where chapter_name = 'RackObjectType' and ";
663 $count = 0;
664 foreach (explode (' ', $terms) as $term)
665 {
666 if ($count) $query .= ' or ';
667 $query .= "${fieldname} like '%$term%'";
668 $count++;
669 }
670 $query .= "";
671 $result = $dbxlink->query($query);
672 if ($result == NULL)
673 {
61e269b5 674 showError ("SQL query failed", __FUNCTION__);
e673ee24
DO
675 return NULL;
676 }
677// FIXME: this dead call was executed 4 times per 1 object search!
678// $typeList = getObjectTypeList();
679 $clist = array ('id', 'name', 'label', 'asset_no', 'barcode', 'objtype_id', 'objtype_name');
680 while ($row = $result->fetch(PDO::FETCH_ASSOC))
681 {
682 foreach ($clist as $cname)
683 $object[$cname] = $row[$cname];
684 $object['score'] = 0;
685 $object['dname'] = displayedName ($object);
686 unset ($object['objtype_id']);
687 foreach (explode (' ', $terms) as $term)
688 if (strstr ($object['name'], $term))
689 $object['score'] += 1;
690 unset ($object['name']);
691 if (!isset ($objects[$row['id']]))
692 $objects[$row['id']] = $object;
693 elseif ($objects[$row['id']]['score'] < $object['score'])
694 $objects[$row['id']]['score'] = $object['score'];
695 }
696 return $objects;
697}
698
699function getSearchResults ($terms)
700{
701 $objects = array();
702 mergeSearchResults ($objects, $terms, 'name');
703 mergeSearchResults ($objects, $terms, 'label');
704 mergeSearchResults ($objects, $terms, 'asset_no');
705 mergeSearchResults ($objects, $terms, 'barcode');
706 if (count ($objects) == 1)
707 usort ($objects, 'search_cmpObj');
708 return $objects;
709}
710
711// This function removes all colons and dots from a string.
712function l2addressForDatabase ($string)
713{
714 if (empty ($string))
715 return 'NULL';
716 $pieces = explode (':', $string);
717 // This workaround is for SunOS ifconfig.
718 foreach ($pieces as &$byte)
719 if (strlen ($byte) == 1)
720 $byte = '0' . $byte;
721 // And this workaround is for PHP.
722 unset ($byte);
723 $string = implode ('', $pieces);
724 $pieces = explode ('.', $string);
725 $string = implode ('', $pieces);
726 $string = strtoupper ($string);
727 return "'$string'";
728}
729
730function l2addressFromDatabase ($string)
731{
732 switch (strlen ($string))
733 {
734 case 12: // Ethernet
735 case 16: // FireWire
736 $ret = implode (':', str_split ($string, 2));
737 break;
738 default:
739 $ret = $string;
740 break;
741 }
742 return $ret;
743}
744
745// The following 2 functions return previous and next rack IDs for
746// a given rack ID. The order of racks is the same as in renderRackspace()
747// or renderRow().
748function getPrevIDforRack ($row_id = 0, $rack_id = 0)
749{
750 if ($row_id <= 0 or $rack_id <= 0)
751 {
61e269b5 752 showError ('Invalid arguments passed', __FUNCTION__);
e673ee24
DO
753 return NULL;
754 }
755 $rackList = getRacksForRow ($row_id);
756 doubleLink ($rackList);
757 if (isset ($rackList[$rack_id]['prev_key']))
758 return $rackList[$rack_id]['prev_key'];
759 return NULL;
760}
761
762function getNextIDforRack ($row_id = 0, $rack_id = 0)
763{
764 if ($row_id <= 0 or $rack_id <= 0)
765 {
61e269b5 766 showError ('Invalid arguments passed', __FUNCTION__);
e673ee24
DO
767 return NULL;
768 }
769 $rackList = getRacksForRow ($row_id);
770 doubleLink ($rackList);
771 if (isset ($rackList[$rack_id]['next_key']))
772 return $rackList[$rack_id]['next_key'];
773 return NULL;
774}
775
776// This function finds previous and next array keys for each array key and
777// modifies its argument accordingly.
778function doubleLink (&$array)
779{
780 $prev_key = NULL;
781 foreach (array_keys ($array) as $key)
782 {
783 if ($prev_key)
784 {
785 $array[$key]['prev_key'] = $prev_key;
786 $array[$prev_key]['next_key'] = $key;
787 }
788 $prev_key = $key;
789 }
790}
791
792// After applying usort() to a rack list we will lose original array keys.
793// This function restores the keys so they are equal to rack IDs.
794function restoreRackIDs ($racks)
795{
796 $ret = array();
797 foreach ($racks as $rack)
798 $ret[$rack['id']] = $rack;
799 return $ret;
800}
801
802function sortTokenize ($a, $b)
803{
804 $aold='';
805 while ($a != $aold)
806 {
807 $aold=$a;
808 $a = ereg_replace('[^a-zA-Z0-9]',' ',$a);
809 $a = ereg_replace('([0-9])([a-zA-Z])','\\1 \\2',$a);
810 $a = ereg_replace('([a-zA-Z])([0-9])','\\1 \\2',$a);
811 }
812
813 $bold='';
814 while ($b != $bold)
815 {
816 $bold=$b;
817 $b = ereg_replace('[^a-zA-Z0-9]',' ',$b);
818 $b = ereg_replace('([0-9])([a-zA-Z])','\\1 \\2',$b);
819 $b = ereg_replace('([a-zA-Z])([0-9])','\\1 \\2',$b);
820 }
821
822
823
824 $ar = explode(' ', $a);
825 $br = explode(' ', $b);
826 for ($i=0; $i<count($ar) && $i<count($br); $i++)
827 {
828 $ret = 0;
829 if (is_numeric($ar[$i]) and is_numeric($br[$i]))
830 $ret = ($ar[$i]==$br[$i])?0:($ar[$i]<$br[$i]?-1:1);
831 else
832 $ret = strcasecmp($ar[$i], $br[$i]);
833 if ($ret != 0)
834 return $ret;
835 }
836 if ($i<count($ar))
837 return 1;
838 if ($i<count($br))
839 return -1;
840 return 0;
841}
842
843function sortByName ($a, $b)
844{
845 return sortTokenize($a['name'], $b['name']);
846}
847
6a3a37b2
DO
848function sortRacks ($a, $b)
849{
850 return sortTokenize($a['row_name'] . ': ' . $a['name'], $b['row_name'] . ': ' . $b['name']);
851}
852
e673ee24
DO
853function eq ($a, $b)
854{
855 return $a==$b;
856}
857
858function neq ($a, $b)
859{
860 return $a!=$b;
861}
862
863function countRefsOfType ($refs, $type, $eq)
864{
865 $count=0;
866 foreach ($refs as $ref)
867 {
868 if ($eq($ref['type'], $type))
869 $count++;
870 }
871 return $count;
872}
873
874function sortEmptyPorts ($a, $b)
875{
876 $objname_cmp = sortTokenize($a['Object_name'], $b['Object_name']);
877 if ($objname_cmp == 0)
878 {
879 return sortTokenize($a['Port_name'], $b['Port_name']);
880 }
881 return $objname_cmp;
882}
883
884function sortObjectAddressesAndNames ($a, $b)
885{
886 $objname_cmp = sortTokenize($a['object_name'], $b['object_name']);
887 if ($objname_cmp == 0)
888 {
889 $objname_cmp = sortTokenize($a['port_name'], $b['port_name']);
890 if ($objname_cmp == 0)
891 {
892 sortTokenize($a['ip'], $b['ip']);
893 }
894 return $objname_cmp;
895 }
896 return $objname_cmp;
897}
898
899
900
901function sortAddresses ($a, $b)
902{
903 $name_cmp = sortTokenize($a['name'], $b['name']);
904 if ($name_cmp == 0)
905 {
906 return sortTokenize($a['ip'], $b['ip']);
907 }
908 return $name_cmp;
909}
910
911// This function expands port compat list into a matrix.
912function buildPortCompatMatrixFromList ($portTypeList, $portCompatList)
913{
914 $matrix = array();
915 // Create type matrix and markup compatible types.
916 foreach (array_keys ($portTypeList) as $type1)
917 foreach (array_keys ($portTypeList) as $type2)
918 $matrix[$type1][$type2] = FALSE;
919 foreach ($portCompatList as $pair)
920 $matrix[$pair['type1']][$pair['type2']] = TRUE;
921 return $matrix;
922}
923
924function newPortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description)
925{
926 global $dbxlink;
927
928 $range = getRangeByIp($localip);
929 if (!$range)
930 return "$localip: Non existant ip";
931
932 $range = getRangeByIp($remoteip);
933 if (!$range)
934 return "$remoteip: Non existant ip";
935
936 if ( ($localport <= 0) or ($localport >= 65536) )
937 return "$localport: invaild port";
938
939 if ( ($remoteport <= 0) or ($remoteport >= 65536) )
940 return "$remoteport: invaild port";
941
942 $query =
943 "insert into PortForwarding set object_id='$object_id', localip=INET_ATON('$localip'), remoteip=INET_ATON('$remoteip'), localport='$localport', remoteport='$remoteport', proto='$proto', description='$description'";
944 $result = $dbxlink->exec ($query);
945
946 return '';
947}
948
949function deletePortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto)
950{
951 global $dbxlink;
952
953 $query =
954 "delete from PortForwarding where object_id='$object_id' and localip=INET_ATON('$localip') and remoteip=INET_ATON('$remoteip') and localport='$localport' and remoteport='$remoteport' and proto='$proto'";
955 $result = $dbxlink->exec ($query);
956 return '';
957}
958
959function updatePortForwarding($object_id, $localip, $localport, $remoteip, $remoteport, $proto, $description)
960{
961 global $dbxlink;
962
963 $query =
964 "update PortForwarding set description='$description' where object_id='$object_id' and localip=INET_ATON('$localip') and remoteip=INET_ATON('$remoteip') and localport='$localport' and remoteport='$remoteport' and proto='$proto'";
965 $result = $dbxlink->exec ($query);
966 return '';
967}
968
969function getObjectForwards($object_id)
970{
971 global $dbxlink;
972
973 $ret = array();
974 $ret['out'] = array();
975 $ret['in'] = array();
976 $query =
977 "select ".
978 "dict_value as proto, ".
979 "proto as proto_bin, ".
980 "INET_NTOA(localip) as localip, ".
981 "localport, ".
982 "INET_NTOA(remoteip) as remoteip, ".
983 "remoteport, ".
f28fbe8b
DO
984 "ipa1.name as local_addr_name, " .
985 "ipa2.name as remote_addr_name, " .
e673ee24
DO
986 "description ".
987 "from PortForwarding inner join Dictionary on proto = dict_key natural join Chapter ".
f28fbe8b
DO
988 "left join IPAddress as ipa1 on PortForwarding.localip = ipa1.ip " .
989 "left join IPAddress as ipa2 on PortForwarding.remoteip = ipa2.ip " .
e673ee24
DO
990 "where object_id='$object_id' and chapter_name = 'Protocols' ".
991 "order by localip, localport, proto, remoteip, remoteport";
992 $result2 = $dbxlink->query ($query);
993 $count=0;
994 while ($row = $result2->fetch (PDO::FETCH_ASSOC))
995 {
f28fbe8b
DO
996 foreach (array ('proto', 'proto_bin', 'localport', 'localip', 'remoteport', 'remoteip', 'description', 'local_addr_name', 'remote_addr_name') as $cname)
997 $ret['out'][$count][$cname] = $row[$cname];
e673ee24
DO
998 $count++;
999 }
1000 $result2->closeCursor();
1001
1002 $query =
1003 "select ".
1004 "dict_value as proto, ".
1005 "proto as proto_bin, ".
1006 "INET_NTOA(localip) as localip, ".
1007 "localport, ".
1008 "INET_NTOA(remoteip) as remoteip, ".
1009 "remoteport, ".
1010 "PortForwarding.object_id as object_id, ".
1011 "RackObject.name as object_name, ".
1012 "description ".
f28fbe8b 1013 "from ((PortForwarding join IPBonds on remoteip=IPBonds.ip) join RackObject on PortForwarding.object_id=RackObject.id) inner join Dictionary on proto = dict_key natural join Chapter ".
e673ee24
DO
1014 "where IPBonds.object_id='$object_id' and chapter_name = 'Protocols' ".
1015 "order by remoteip, remoteport, proto, localip, localport";
1016 $result3 = $dbxlink->query ($query);
1017 $count=0;
1018 while ($row = $result3->fetch (PDO::FETCH_ASSOC))
1019 {
603ad85f 1020 foreach (array ('proto', 'proto_bin', 'localport', 'localip', 'remoteport', 'remoteip', 'object_id', 'object_name', 'description') as $cname)
f28fbe8b 1021 $ret['in'][$count][$cname] = $row[$cname];
e673ee24
DO
1022 $count++;
1023 }
1024 $result3->closeCursor();
1025
1026 return $ret;
1027}
1028
c31cd72c 1029// This function returns an array of single element of object's FQDN attribute,
f321b50a
DO
1030// if FQDN is set. The next choice is object's common name, if it looks like a
1031// hostname. Otherwise an array of all 'regular' IP addresses of the
c31cd72c 1032// object is returned (which may appear 0 and more elements long).
f321b50a 1033function findAllEndpoints ($object_id, $fallback = '')
c31cd72c
DO
1034{
1035 $values = getAttrValues ($object_id);
1036 foreach ($values as $record)
f321b50a 1037 if ($record['name'] == 'FQDN' && !empty ($record['value']))
c31cd72c
DO
1038 return array ($record['value']);
1039 $addresses = getObjectAddresses ($object_id);
1040 $regular = array();
1041 foreach ($addresses as $idx => $address)
1042 if ($address['type'] == 'regular')
fa8112d1 1043 $regular[] = $address['ip'];
f321b50a
DO
1044 if (!count ($regular) && !empty ($fallback))
1045 return array ($fallback);
c31cd72c
DO
1046 return $regular;
1047}
1048
83ba6670
DO
1049// Some records in the dictionary may be written as plain text or as Wiki
1050// link in the following syntax:
1051// 1. word
1052// 2. [[word URL]] // FIXME: this isn't working
1053// 3. [[word word word | URL]]
1054// This function parses the line and returns text suitable for either A
1055// (rendering <A HREF>) or O (for <OPTION>).
1056function parseWikiLink ($line, $which)
1057{
010231c2 1058 if (preg_match ('/^\[\[.+\]\]$/', $line) == 0)
83ba6670 1059 return $line;
010231c2
DO
1060 $line = preg_replace ('/^\[\[(.+)\]\]$/', '$1', $line);
1061 $s = explode ('|', $line);
83ba6670
DO
1062 $o_value = trim ($s[0]);
1063 $a_value = trim ($s[1]);
1064 if ($which == 'a')
010231c2 1065 return "<a href='${a_value}'>${o_value}</a>";
83ba6670
DO
1066 if ($which == 'o')
1067 return $o_value;
1068}
1069
2de490b7
DO
1070function buildVServiceName ($vsinfo = NULL)
1071{
1072 if ($vsinfo == NULL)
1073 {
1074 showError ('NULL argument', __FUNCTION__);
1075 return NULL;
1076 }
8d790216 1077 return $vsinfo['vip'] . ':' . $vsinfo['vport'] . '/' . $vsinfo['proto'];
2de490b7
DO
1078}
1079
177b1e9b
DO
1080// rackspace usage for a single rack
1081// (T + W + U) / (height * 3 - A)
11df133a 1082function getRSUforRack ($data = NULL)
177b1e9b 1083{
11df133a 1084 if ($data == NULL)
177b1e9b
DO
1085 {
1086 showError ('Invalid argument', __FUNCTION__);
1087 return NULL;
1088 }
6ffba290 1089 $counter = array ('A' => 0, 'U' => 0, 'T' => 0, 'W' => 0, 'F' => 0);
9e60f7df
DO
1090 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
1091 for ($locidx = 0; $locidx < 3; $locidx++)
1092 $counter[$data[$unit_no][$locidx]['state']]++;
dfa3c075 1093 return ($counter['T'] + $counter['W'] + $counter['U']) / ($counter['T'] + $counter['W'] + $counter['U'] + $counter['F']);
177b1e9b
DO
1094}
1095
11df133a
DO
1096// Same for row.
1097function getRSUforRackRow ($rowData = NULL)
1098{
bb26a59e 1099 if ($rowData === NULL)
11df133a
DO
1100 {
1101 showError ('Invalid argument', __FUNCTION__);
1102 return NULL;
1103 }
bb26a59e
DO
1104 if (!count ($rowData))
1105 return 0;
11df133a 1106 $counter = array ('A' => 0, 'U' => 0, 'T' => 0, 'W' => 0, 'F' => 0);
f81a2012 1107 $total_height = 0;
dfa3c075
DO
1108 foreach (array_keys ($rowData) as $rack_id)
1109 {
1110 $data = getRackData ($rack_id);
1111 $total_height += $data['height'];
11df133a
DO
1112 for ($unit_no = $data['height']; $unit_no > 0; $unit_no--)
1113 for ($locidx = 0; $locidx < 3; $locidx++)
1114 $counter[$data[$unit_no][$locidx]['state']]++;
dfa3c075
DO
1115 }
1116 return ($counter['T'] + $counter['W'] + $counter['U']) / ($counter['T'] + $counter['W'] + $counter['U'] + $counter['F']);
11df133a
DO
1117}
1118
f1a0477d
DO
1119function getObjectCount ($rackData)
1120{
1121 $objects = array();
1122 for ($i = $rackData['height']; $i > 0; $i--)
1123 for ($locidx = 0; $locidx < 3; $locidx++)
1124 if
1125 (
1126 $rackData[$i][$locidx]['state'] == 'T' and
1127 !in_array ($rackData[$i][$locidx]['object_id'], $objects)
1128 )
1129 $objects[] = $rackData[$i][$locidx]['object_id'];
1130 return count ($objects);
1131}
1132
9af110b4
DO
1133// Perform substitutions and return resulting string
1134function apply_macros ($macros, $subject)
1135{
1136 $ret = $subject;
1137 foreach ($macros as $search => $replace)
1138 $ret = str_replace ($search, $replace, $ret);
1139 return $ret;
1140}
1141
1142// Make sure the string is always wrapped with LF characters
1143function lf_wrap ($str)
1144{
1145 $ret = trim ($str, "\r\n");
1146 if (!empty ($ret))
1147 $ret .= "\n";
1148 return $ret;
1149}
1150
e6e7d8b3
DO
1151// Adopted from Mantis BTS code.
1152function string_insert_hrefs ($s)
1153{
1154 if (getConfigVar ('DETECT_URLS') != 'yes')
1155 return $s;
1156 # Find any URL in a string and replace it by a clickable link
1157 $s = preg_replace( '/(([[:alpha:]][-+.[:alnum:]]*):\/\/(%[[:digit:]A-Fa-f]{2}|[-_.!~*\';\/?%^\\\\:@&={\|}+$#\(\),\[\][:alnum:]])+)/se',
1158 "'<a href=\"'.rtrim('\\1','.').'\">\\1</a> [<a href=\"'.rtrim('\\1','.').'\" target=\"_blank\">^</a>]'",
1159 $s);
1160 $s = preg_replace( '/\b' . email_regex_simple() . '\b/i',
1161 '<a href="mailto:\0">\0</a>',
1162 $s);
1163 return $s;
1164}
1165
1166// Idem.
1167function email_regex_simple ()
1168{
1169 return "(([a-z0-9!#*+\/=?^_{|}~-]+(?:\.[a-z0-9!#*+\/=?^_{|}~-]+)*)" . # recipient
1170 "\@((?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?))"; # @domain
1171}
1172
e673ee24 1173?>