Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
condition_user_ip_edit_fns.inc
1 <?php
17 require_once SQ_CORE_PACKAGE_PATH.'/system/conditions/condition/condition_edit_fns.inc';
18 require_once SQ_FUDGE_PATH.'/csv/csv.inc';
19 require_once dirname(__FILE__).'/condition_user_ip.inc';
20 
21 
36 {
37 
38 
44  function __construct()
45  {
46  parent::__construct();
47 
48  }//end constructor
49 
50 
62  public static function paintEditInterface(Array $condition_data, Backend_Outputter $o, $prefix, $write_access)
63  {
64  if (!isset($condition_data['user_ip_ranges'])) {
65  $condition_data['user_ip_ranges'] = Array();
66  }
67 
68  if (!isset($condition_data['default_grant'])) {
69  $condition_data['default_grant'] = FALSE;
70  }
71 
72  if ($write_access) {
73  $condition_data['user_ip_ranges']['new'] = Array('ip_address' => NULL, 'subnet' => NULL, 'grant' => 1);
74  }
75 
76  $grant_options = Array(1 => translate('grant'), 0 => translate('deny'));
77 
78  if (!$write_access && empty($condition_data['user_ip_ranges'])) {
79  ?><em><?php echo translate('condition_user_ip_no_conditions_set'); ?></em><?php
80  } else {
81  ?>
82  <table style="width: auto" class="sq-backend-table">
83  <tr>
84  <?php if ($write_access) {
85  ?>
86  <th></th>
87  <?php
88  }
89  ?>
90  <th><?php echo translate('network_ip_address'); ?></th>
91  <th></th>
92  <th><?php echo translate('subnet_mask'); ?></th>
93  <th><?php echo translate('action'); ?></th>
94  <?php if ($write_access) {
95  ?>
96  <th><?php echo translate('delete_question'); ?></th>
97  <?php
98  }
99  ?>
100  </tr>
101  <?php foreach ($condition_data['user_ip_ranges'] as $index => $ranges) {
102  ?>
103  <tr>
104  <?php if ($write_access) {
105  ?>
106  <td>
107  <?php
108  if ($index === 'new') {
109  echo '<strong>'.translate('new_question').'</strong>';
110  }
111  ?>
112  </td>
113  <?php
114  }
115  ?>
116  <td>
117  <?php
118  if ($write_access) {
119  self::paintIPAddressField($prefix.'['.$index.'][ip_address]', $ranges['ip_address']);
120  } else {
121  echo $ranges['ip_address'];
122  }
123  ?>
124  </td>
125  <td>
126  /
127  </td>
128  <td>
129  <?php
130  if ($write_access) {
131  self::paintSubnetMaskField($prefix.'['.$index.'][subnet]', $ranges['subnet']);
132  } else {
133  echo $ranges['subnet'];
134  }
135  ?>
136  </td>
137  <td>
138  <?php
139  if ($write_access) {
140  ?><?php
141  combo_box($prefix.'['.$index.'][grant]', $grant_options, FALSE, Array($ranges['grant']));
142  ?><?php
143  } else {
144  ?><strong style="color: <?php echo $ranges['grant'] ? 'green' : 'red' ?>"><?php
145  echo $grant_options[$ranges['grant']];
146  ?></strong><?php
147  }
148  ?></td>
149  <td style="text-align: center">
150  <?php
151  if ($write_access && ($index !== 'new')) {
152  check_box($prefix.'['.$index.'][delete]', FALSE);
153  } else {
154  echo '&nbsp;';
155  }
156  ?></td>
157  </tr>
158  <?php
159  }//end foreach
160  ?>
161  </table>
162  <?php
163  }//end else
164 
165  echo '<p>';
166  echo translate('condition_user_ip_default_grant').': ';
167  if ($write_access) {
168  combo_box($prefix.'[default_grant]', $grant_options, FALSE, Array($condition_data['default_grant']));
169  } else {
170  ?><strong style="color: <?php echo $condition_data['default_grant'] ? 'green' : 'red' ?>"><?php
171  echo $grant_options[$condition_data['default_grant']];
172  ?></strong><?php
173 
174  }
175  echo '</p>';
176 
177  echo '<p>';
178  echo '<b>'.translate('condition_user_ip_import_from_file').':</b>&nbsp;';
179  file_upload($prefix.'_import');
180  echo '<br/>'.translate('condition_user_ip_import_from_file_explain').'</p>';
181 
182  }//end paintEditInterface()
183 
184 
194  public static function processEditInterface(Backend_Outputter $o, $prefix)
195  {
196  if (isset($_FILES[$prefix.'_import']) && ($_FILES[$prefix.'_import']['error'] == 0)) {
197  $file_name = $_FILES[$prefix.'_import']['tmp_name'];
198  if (is_uploaded_file($file_name)) {
199  $condition_data = self::importRulesFromCSV($file_name);
200  if ($condition_data !== FALSE) {
201  $condition_data['default_grant'] = $_POST[$prefix]['default_grant'];
202  return $condition_data;
203  } else {
204  trigger_localised_error('CORE0249', E_USER_WARNING);
205  }
206  }
207  }
208 
209  // construct and return data required by this condition
210  // as found in the post data, array required consists of
211  // 'match' and 'condition_data'
212  $ip_ranges = Array();
213 
214  foreach ($_POST[$prefix] as $index => $post_data) {
215  if (!isset($post_data['delete'])) {
216  $this_row = Array(
217  'ip_address' => self::processIPAddressField($post_data['ip_address']),
218  'subnet' => self::processSubnetMaskField($post_data['subnet']),
219  'grant' => $post_data['grant'],
220  );
221 
222  // IP address not filled in => continue with life
223  if (empty($this_row['ip_address'])) continue;
224 
225  // see if there is an exact duplicate, skip it if there is
226  if (in_array($this_row, $ip_ranges)) continue;
227 
228  // now flip the grant field to see if there is a IP/subnet dupe
229  // of the other kind of rule
230  $this_row['grant'] = !$this_row['grant'];
231  if (in_array($this_row, $ip_ranges)) {
232  trigger_localised_error('CORE0250', E_USER_WARNING, $this_row['ip_address'], $this_row['subnet']);
233  continue;
234  }
235 
236  // Check to see whether the ip address is a valid network address
237  // for the subnet mask in question
238  if (self::_doesIPBeginSubnet($this_row['ip_address'], $this_row['subnet'])) {
239  // put grant back to where it was before and store it
240  $this_row['grant'] = $post_data['grant'];
241  $ip_ranges[] = $this_row;
242  } else {
243  trigger_localised_error('CORE0248', E_USER_WARNING, $this_row['ip_address'], $this_row['subnet']);
244  }
245  }//end if
246  }//end foreach
247 
248  $results = Array(
249  'user_ip_ranges' => $ip_ranges,
250  'default_grant' => $_POST[$prefix]['default_grant'],
251  );
252 
253  return $results;
254 
255  }//end processEditInterface()
256 
257 
268  public static function paintIPAddressField($prefix, $value=NULL)
269  {
270  if ($value == NULL) $value = '...';
271  $value = explode('.', $value);
272 
273  text_box($prefix.'[0]', $value[0], 3, 0, 3);
274  echo '.';
275  text_box($prefix.'[1]', $value[1], 3, 0, 3);
276  echo '.';
277  text_box($prefix.'[2]', $value[2], 3, 0, 3);
278  echo '.';
279  text_box($prefix.'[3]', $value[3], 3, 0, 3);
280 
281  }//end paintIPAddressField()
282 
283 
292  public static function processIPAddressField($octets)
293  {
294  // if there is a blank in any field - reject the IP address
295  for ($i = 0; $i <= 3; $i++) {
296  if ($octets[$i] == '') return FALSE;
297  }
298 
299  // non-integer bulldust filter
300  $octets[0] = (int)$octets[0];
301  $octets[1] = (int)$octets[1];
302  $octets[2] = (int)$octets[2];
303  $octets[3] = (int)$octets[3];
304 
305  for ($i = 0; $i <= 3; $i++) {
306  if (($octets[$i] < 0) || ($octets[$i] > 255)) {
307  return FALSE;
308  }
309  }
310 
311  return implode('.', $octets);
312 
313  }//end processIPAddressField()
314 
315 
326  public static function paintSubnetMaskField($prefix, $value=NULL)
327  {
328  // Save all the possible bitmasks so we can use them in a drop-down
329  $possible_bitmasks = self::_getPossibleSubnetOctets();
330 
331  if ($value == NULL) $value = '255.255.255.255';
332  $value = explode('.', $value);
333 
334  combo_box($prefix.'[0]', $possible_bitmasks, FALSE, Array($value[0]));
335  echo '.';
336  combo_box($prefix.'[1]', $possible_bitmasks, FALSE, Array($value[1]));
337  echo '.';
338  combo_box($prefix.'[2]', $possible_bitmasks, FALSE, Array($value[2]));
339  echo '.';
340  combo_box($prefix.'[3]', $possible_bitmasks, FALSE, Array($value[3]));
341 
342  }//end paintSubnetMaskField()
343 
344 
353  public static function processSubnetMaskField($octets)
354  {
355  // non-integer bulldust filter
356  $octets[0] = (int)$octets[0];
357  $octets[1] = (int)$octets[1];
358  $octets[2] = (int)$octets[2];
359  $octets[3] = (int)$octets[3];
360 
361  // Make sure we don't have a weird bitmask like '255.255.128.224' -
362  // once we've got a ZERO bit the remaining bits must be ZERO also
363  $ip_address = implode('.', $octets);
364 
365  if (self::_isValidSubnetMask($ip_address)) {
366  return $ip_address;
367  } else {
368  trigger_error('Subnet mask entered is not valid', E_USER_WARNING);
369  return NULL;
370  }
371 
372  }//end processSubnetMaskField()
373 
374 
389  public static function convertSubnetIPtoCIDR($subnet_ip)
390  {
391  if (!self::_isValidSubnetMask($subnet_ip)) {
392  return FALSE;
393  }
394 
395  $octets = explode('.', $subnet_ip);
396  $cidr_value = 0;
397 
398  for ($i = 0; $i <= 3; $i++) {
399  if ($octets[$i] == 0) break;
400  while ($octets[$i] > 0) {
401  if ($octets[$i] % 2 == 1) $cidr_value++;
402  $octets[$i] >>= 1;
403  }
404  }
405 
406  return $cidr_value;
407 
408  }//end convertSubnetIPtoCIDR()
409 
410 
424  public static function convertCIDRtoSubnetIP($cidr_value)
425  {
426  $cidr_value = (int)$cidr_value;
427  if (($cidr_value < 0) || ($cidr_value > 32)) {
428  return FALSE;
429  }
430 
431  $octets = Array(0, 0, 0, 0);
432  $current_octet = 0;
433 
434  while ($cidr_value > 0) {
435  if ($cidr_value > 8) {
436  // might as well get rid of the 255's as a whole block
437  $cidr_value -= 8;
438  $octets[$current_octet] = 255;
439  $current_octet++;
440  } else {
441  // not enough bits left for a 255?
442  for ($i = 0; $i < 8; $i++) {
443  $octets[$current_octet] <<= 1;
444  if ($cidr_value > 0) {
445  $octets[$current_octet]++;
446  $cidr_value--;
447  }
448  }
449  }
450  }
451 
452  return implode('.', $octets);
453 
454  }//end convertCIDRtoSubnetIP()
455 
456 
470  public static function isInSubnet($ip_address, $network_ip, $subnet_ip)
471  {
472  $ip_octets = explode('.', $ip_address);
473  $network_octets = explode('.', $network_ip);
474  $subnet_octets = explode('.', $subnet_ip);
475 
476  for ($i = 0; $i <= 3; $i++) {
477  $masked_octet = (int)$ip_octets[$i] & (int)$subnet_octets[$i];
478  if ($masked_octet != (int)$network_octets[$i]) {
479  return FALSE;
480  }
481  }
482 
483  return TRUE;
484 
485  }//end isInSubnet()
486 
487 
496  protected static function _isValidSubnetMask($subnet_ip)
497  {
498  $bitmasks = self::_getPossibleSubnetOctets();
499 
500  $octets = explode('.', $subnet_ip);
501  $zero_bit_found = FALSE;
502 
503  for ($i = 0; $i <= 3; $i++) {
504  if ($zero_bit_found && ($octets[$i] != 0)) {
505  // If we found a zero bit in a previous octet, the rest of
506  // the subnet mask must be ZERO
507  return FALSE;
508  } else if ($octets[$i] != 255) {
509  // We've found a zero bit here - keep note of it
510  $zero_bit_found = TRUE;
511  }
512 
513  // If the octet is not a valid subnet octet, this IP fails it
514  if (!isset($bitmasks[$octets[$i]])) return FALSE;
515  }
516 
517  return TRUE;
518 
519  }//end _isValidSubnetMask()
520 
521 
530  public static function sortByCIDROrder($ip_ranges)
531  {
532  $ip_ranges_by_cidr = Array();
533 
534  foreach ($ip_ranges as $ip_range) {
535  $cidr_value = self::convertSubnetIPtoCIDR($ip_range['subnet']);
536  $ip_ranges_by_cidr[$cidr_value][] = $ip_range;
537  }
538 
539  ksort($ip_ranges_by_cidr);
540 
541  $ip_ranges = Array();
542  foreach ($ip_ranges_by_cidr as $cidr_ip_range) {
543  $ip_ranges = array_merge($ip_ranges, $cidr_ip_range);
544  }
545 
546  return $ip_ranges;
547 
548  }//end sortByCIDROrder()
549 
550 
557  protected static function _getPossibleSubnetOctets()
558  {
559  $bitmasks = Array(
560  0 => '0',
561  128 => '128',
562  192 => '192',
563  224 => '224',
564  240 => '240',
565  248 => '248',
566  252 => '252',
567  254 => '254',
568  255 => '255',
569  );
570  return $bitmasks;
571 
572  }//end _getPossibleSubnetOctets()
573 
574 
587  protected static function _doesIPBeginSubnet($ip_address, $subnet_ip)
588  {
589  $ip_octets = explode('.', $ip_address);
590  $subnet_octets = explode('.', $subnet_ip);
591 
592  for ($i = 0; $i <= 3; $i++) {
593  if (($ip_octets[$i] & (~(int)$subnet_octets[$i])) != 0) {
594  return FALSE;
595  }
596  }
597 
598  return TRUE;
599 
600  }//end _doesIPBeginSubnet()
601 
602 
612  public static function importRulesFromCSV($file_name)
613  {
614  // minimum and maximum values allowed in each field
615  // CSV format: first 4 fields = IP octets, fifth = subnet CIDR, sixth = grant/deny
616  $min_values = Array(0, 0, 0, 0, 1, 0);
617  $max_values = Array(255, 255, 255, 255, 32, 1);
618 
619  $condition_data = Array('user_ip_ranges' => Array());
620  $condition_data['default_grant'] = 0;
621 
622  $csv = new CSV();
623  $csv->setFilepath($file_name);
624  $csv->import();
625  $lines = $csv->values;
626 
627  // where we will be building our IP ranges
628  $ip_ranges = Array();
629 
630  foreach ($lines as $line_fields) {
631  // doesn't have correct number of fields...
632  if (count($line_fields) != 6) return FALSE;
633 
634  foreach ($line_fields as $index => $value) {
635  if (!(is_numeric($value) && ($value >= $min_values[$index]) && ($value <= $max_values[$index]))) {
636  return FALSE;
637  }
638  }
639 
640  $ip_address = implode('.', array_slice($line_fields, 0, 4));
641  $subnet_mask = self::convertCIDRtoSubnetIP($line_fields[4]);
642  $grant = $line_fields[5];
643 
644  $ip_range_field = Array(
645  'ip_address' => $ip_address,
646  'subnet' => $subnet_mask,
647  'grant' => $grant,
648  );
649 
650  // this is already in the file - ignore it
651  if (in_array($ip_range_field, $ip_ranges)) continue;
652 
653  // this has already been seen with a different grant!!
654  // complain, then ignore it
655  $ip_range_field['grant'] = !$ip_range_field['grant'];
656  if (in_array($ip_range_field, $ip_ranges)) {
657  trigger_localised_error('CORE0250', E_USER_WARNING, $ip_range_field['ip_address'], $ip_range_field['subnet']);
658  continue;
659  }
660 
661  // is it a valid combination?
662  if (!self::_doesIPBeginSubnet($ip_range_field['ip_address'], $ip_range_field['subnet'])) {
663  trigger_localised_error('CORE0248', E_USER_WARNING, $ip_range_field['ip_address'], $ip_range_field['subnet']);
664  continue;
665  }
666 
667  // flip the grant back to what it was, and add it to the list
668  $ip_range_field['grant'] = !$ip_range_field['grant'];
669  $ip_ranges[] = $ip_range_field;
670  }//end foreach
671 
672  $condition_data['user_ip_ranges'] = $ip_ranges;
673 
674  return $condition_data;
675 
676  }//end importRulesFromCSV()
677 
678 
679 }//end class
680 
681 ?>