FusionDirectory
functions.inc
Go to the documentation of this file.
1 <?php
2 /*
3  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
4  Copyright (C) 2003-2010 Cajus Pollmeier
5  Copyright (C) 2011-2016 FusionDirectory
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21 
27 /* Define common locations and variables */
28 require_once('variables.inc');
29 
30 /* Include required files */
31 require_once(CACHE_DIR.'/'.CLASS_CACHE);
32 require_once('functions_debug.inc');
33 require_once('accept-to-gettext.inc');
34 
44 function fusiondirectory_autoload ($class_name)
45 {
46  global $class_mapping, $BASE_DIR, $config;
47 
48  if ($class_mapping === NULL) {
49  if (isset($config) && is_object($config) &&
50  $config->get_cfg_value('displayerrors') == 'TRUE') {
51  list($trace, ) = html_trace();
52  echo $trace;
53  echo "<br/>\n";
54  }
55  echo sprintf(_("Fatal error: no class locations defined - please run '%s' to fix this"), "<b>fusiondirectory-configuration-manager --update-cache</b>");
56  exit;
57  }
58 
59  /* Do not try to autoload smarty classes */
60  if (strpos($class_name, 'Smarty_') === 0) {
61  return;
62  }
63 
64  if (strpos($class_name, 'FusionDirectory\\') === 0) {
65  $class_name = preg_replace('/^.+\\\\([^\\\\]+)$/', '\\1', "$class_name");
66  }
67 
68  if (isset($class_mapping["$class_name"])) {
69  require_once($BASE_DIR.'/'.$class_mapping["$class_name"]);
70  } else {
71  logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $class_name, 'Could not load');
72  if (isset($config) && is_object($config) &&
73  $config->get_cfg_value('displayerrors') == 'TRUE') {
74  list($trace, ) = html_trace();
75  echo $trace;
76  echo "<br/>\n";
77  }
78  echo sprintf(_("Fatal error: cannot instantiate class '%s' - try running '%s' to fix this"), $class_name, "<b>fusiondirectory-configuration-manager --update-cache</b>");
79  exit;
80  }
81 }
82 spl_autoload_register('fusiondirectory_autoload');
83 
84 
92 function class_available ($name)
93 {
94  global $class_mapping;
95  return isset($class_mapping[$name]);
96 }
97 
98 
108 function plugin_available ($plugin)
109 {
110  global $class_mapping, $BASE_DIR;
111 
112  if (!isset($class_mapping[$plugin])) {
113  return FALSE;
114  } else {
115  return is_readable($BASE_DIR.'/'.$class_mapping[$plugin]);
116  }
117 }
118 
141 function DEBUG ($level, $line, $function, $file, $data, $info = '')
142 {
143  logging::debug($level, $line, $function, $file, $data, $info);
144 }
145 
149 function copynotice ()
150 {
151  return sprintf(htmlescape(_('%s 2002-%d %sThe FusionDirectory team, %s%s')), '&copy;', date('Y'), '<a href="http://www.fusiondirectory.org">', FD_VERSION, '</a>');
152 }
153 
174 function get_template_path ($filename = '', $plugin = FALSE, $path = '')
175 {
176  global $config, $BASE_DIR;
177  $default_theme = 'breezy';
178 
179  /* Set theme */
180  if (isset($config)) {
181  $theme = $config->get_cfg_value('theme', $default_theme);
182  } else {
183  $theme = $default_theme;
184  }
185 
186  /* Return path for empty filename */
187  if ($filename == '') {
188  return "themes/$theme/";
189  }
190 
191  /* Return plugin dir or root directory? */
192  if ($plugin) {
193  if ($path == '') {
194  $path = session::get('plugin_dir');
195  $nf = preg_replace('!^'.$BASE_DIR.'/!', '', preg_replace('/^\.\.\//', '', $path));
196  } else {
197  $nf = preg_replace('!^'.$BASE_DIR.'/!', '', $path);
198  }
199  $paths = [
200  "$BASE_DIR/ihtml/themes/$theme/$nf/$filename",
201  "$BASE_DIR/ihtml/themes/$default_theme/$nf/$filename",
202  "$BASE_DIR/ihtml/themes/default/$nf/$filename",
203  $path."/$filename"
204  ];
205  } else {
206  $paths = [
207  "themes/$theme/$filename",
208  "$BASE_DIR/ihtml/themes/$theme/$filename",
209  "themes/$default_theme/$filename",
210  "$BASE_DIR/ihtml/themes/$default_theme/$filename",
211  "themes/default/$filename",
212  "$BASE_DIR/ihtml/themes/default/$filename",
213  $filename
214  ];
215  }
216 
217  foreach ($paths as $path) {
218  if (file_exists($path)) {
219  return $path;
220  }
221  }
222 
223  return end($paths);
224 }
225 
226 
239 function array_remove_entries (array $needles, array $haystack): array
240 {
241  return array_values(array_udiff($haystack, $needles, 'array_cmp_recursive'));
242 }
243 
244 
257 function array_remove_entries_ics (array $needles, array $haystack): array
258 {
259  // strcasecmp will work, because we only compare ASCII values here
260  return array_values(array_udiff($haystack, $needles, 'strcasecmp'));
261 }
262 
275 function array_merge_unique (array $ar1, array $ar2): array
276 {
277  return array_values(array_unique(array_merge($ar1, $ar2)));
278 }
279 
280 
288 function fusiondirectory_log ($message)
289 {
290  global $ui;
291 
292  /* Preset to something reasonable */
293  $username = '[unauthenticated]';
294 
295  /* Replace username if object is present */
296  if (isset($ui)) {
297  if ($ui->uid != '') {
298  $username = '['.$ui->uid.']';
299  } else {
300  $username = '[unknown]';
301  }
302  }
303 
304  syslog(LOG_INFO, "FusionDirectory $username: $message");
305 }
306 
312 function &get_userinfo ()
313 {
314  global $ui;
315 
316  return $ui;
317 }
318 
324 function &get_smarty ()
325 {
326  global $smarty;
327 
328  return $smarty;
329 }
330 
349 function convert_department_dn ($dn, $base = NULL)
350 {
351  global $config;
352 
353  if ($base == NULL) {
354  $base = $config->current['BASE'];
355  }
356 
357  /* Build a sub-directory style list of the tree level
358  specified in $dn */
359  $dn = preg_replace('/'.preg_quote($base, '/')."$/i", '', $dn);
360  if (empty($dn)) {
361  return '/';
362  }
363 
364  $dep = '';
365  foreach (explode(',', $dn) as $rdn) {
366  $dep = preg_replace("/^[^=]+=/", '', $rdn).'/'.$dep;
367  }
368 
369  /* Return and remove accidently trailing slashes */
370  return trim($dep, '/');
371 }
372 
391 function get_ou ($name)
392 {
393  global $config;
394 
395  $map = [
396  'fusiondirectoryRDN' => 'ou=fusiondirectory,',
397  'lockRDN' => 'ou=locks,',
398  'recoveryTokenRDN' => 'ou=recovery,',
399  'reminderTokenRDN' => 'ou=reminder,',
400  'roleRDN' => 'ou=roles,',
401  'ogroupRDN' => 'ou=groups,',
402  'applicationRDN' => 'ou=apps,',
403  'systemRDN' => 'ou=systems,',
404  'serverRDN' => 'ou=servers,ou=systems,',
405  'terminalRDN' => 'ou=terminals,ou=systems,',
406  'workstationRDN' => 'ou=workstations,ou=systems,',
407  'printerRDN' => 'ou=printers,ou=systems,',
408  'phoneRDN' => 'ou=phones,ou=systems,',
409  'componentRDN' => 'ou=netdevices,ou=systems,',
410  'mobilePhoneRDN' => 'ou=mobile,ou=systems,',
411 
412  'inventoryRDN' => 'ou=inventory,',
413 
414  'ipmiRDN' => 'ou=ipmi,',
415 
416  'faxBlocklistRDN' => 'ou=gofax,ou=systems,',
417  'aclRoleRDN' => 'ou=aclroles,',
418  'phoneMacroRDN' => 'ou=macros,ou=asterisk,ou=configs,ou=systems,',
419  'phoneConferenceRDN' => 'ou=conferences,ou=asterisk,ou=configs,ou=systems,',
420 
421  'faiBaseRDN' => 'ou=fai,ou=configs,ou=systems,',
422  'faiScriptRDN' => 'ou=scripts,',
423  'faiHookRDN' => 'ou=hooks,',
424  'faiTemplateRDN' => 'ou=templates,',
425  'faiVariableRDN' => 'ou=variables,',
426  'faiProfileRDN' => 'ou=profiles,',
427  'faiPackageRDN' => 'ou=packages,',
428  'faiPartitionRDN' => 'ou=disk,',
429 
430  'debconfRDN' => 'ou=debconf,',
431 
432  'supannStructuresRDN' => 'ou=structures,',
433 
434  'sudoRDN' => 'ou=sudoers,',
435 
436  'netgroupRDN' => 'ou=netgroup,',
437 
438  'deviceRDN' => 'ou=devices,',
439 
440  'aliasRDN' => 'ou=alias,',
441 
442  'dsaRDN' => 'ou=dsa,',
443 
444  'mimetypeRDN' => 'ou=mime,'
445  ];
446 
447  /* Preset ou... */
448  if ($config->get_cfg_value($name, '_not_set_') != '_not_set_') {
449  $ou = $config->get_cfg_value($name);
450  } elseif (isset($map[$name])) {
451  return $map[$name];
452  } else {
453  return NULL;
454  }
455 
456  if ($ou != '') {
457  if (!preg_match('/^[^=]+=[^=]+/', $ou)) {
458  $ou = "ou=$ou";
459  } else {
460  $ou = "$ou";
461  }
462 
463  if (preg_match('/'.preg_quote($config->current['BASE'], '/').'$/', $ou)) {
464  return $ou;
465  } else {
466  if (preg_match('/,$/', $ou)) {
467  return $ou;
468  } else {
469  return "$ou,";
470  }
471  }
472  } else {
473  return '';
474  }
475 }
476 
484 function get_people_ou ()
485 {
486  return get_ou('userRDN');
487 }
488 
500 function get_base_from_people ($dn)
501 {
502  global $config;
503 
504  $pattern = "/^[^,]+,".preg_quote(get_people_ou(), '/')."/i";
505  $base = preg_replace($pattern, '', $dn);
506 
507  /* Set to base, if we're not on a correct subtree */
508  $departmentInfo = $config->getDepartmentInfo();
509  if (!isset($departmentInfo[$base])) {
510  $base = $config->current['BASE'];
511  }
512 
513  return $base;
514 }
515 
516 
526 function strict_uid_mode ()
527 {
528  global $config;
529 
530  if (isset($config)) {
531  return ($config->get_cfg_value('strictNamingRules') == 'TRUE');
532  }
533  return TRUE;
534 }
535 
549 function to_string ($value)
550 {
551  /* If this is an array, generate a text blob */
552  if (is_array($value)) {
553  $ret = '';
554  foreach ($value as $line) {
555  $ret .= $line."<br>\n";
556  }
557  return $ret;
558  } else {
559  return $value;
560  }
561 }
562 
572 function rewrite ($s)
573 {
574  /* Rewrite german 'umlauts' and spanish 'accents'
575  to get better results */
576  static $REWRITE = [
577  'ä' => 'ae',
578  'ö' => 'oe',
579  'ü' => 'ue',
580  'Ä' => 'Ae',
581  'Ö' => 'Oe',
582  'Ü' => 'Ue',
583  'ß' => 'ss',
584  'á' => 'a',
585  'é' => 'e',
586  'í' => 'i',
587  'ó' => 'o',
588  'ú' => 'u',
589  'Á' => 'A',
590  'É' => 'E',
591  'Í' => 'I',
592  'Ó' => 'O',
593  'Ú' => 'U',
594  'ñ' => 'ny',
595  'Ñ' => 'Ny',
596  ];
597 
598  foreach ($REWRITE as $key => $val) {
599  $s = str_replace($key, $val, $s);
600  }
601 
602  return $s;
603 }
604 
605 
614 function dn2base ($dn, $ou = NULL)
615 {
616  if ($ou === NULL) {
617  if (get_people_ou() != '') {
618  $dn = preg_replace('/,'.get_people_ou().'/i', ',', $dn);
619  }
620  if (get_ou('groupRDN') != '') {
621  $dn = preg_replace('/,'.get_ou('groupRDN').'/i', ',', $dn);
622  }
623  } else {
624  $dn = preg_replace("/,$ou/i", ',', $dn);
625  }
626 
627  return preg_replace('/^[^,]+,/i', '', $dn);
628 }
629 
640 function check_command ($cmdline)
641 {
642  $cmd = preg_replace("/ .*$/", '', $cmdline);
643 
644  /* Check if command exists in filesystem */
645  if (!file_exists($cmd)) {
646  return FALSE;
647  }
648 
649  /* Check if command is executable */
650  if (!is_executable($cmd)) {
651  return FALSE;
652  }
653 
654  return TRUE;
655 }
656 
664 function normalize_netmask ($netmask)
665 {
666  /* Check for notation of netmask */
667  if (!preg_match('/^([0-9]+\.){3}[0-9]+$/', $netmask)) {
668  $num = (int)($netmask);
669  $netmask = "";
670 
671  for ($byte = 0; $byte < 4; $byte++) {
672  $result = 0;
673 
674  for ($i = 7; $i >= 0; $i--) {
675  if ($num-- > 0) {
676  $result += 2 ** $i;
677  }
678  }
679 
680  $netmask .= $result.".";
681  }
682 
683  return preg_replace('/\.$/', '', $netmask);
684  }
685 
686  return $netmask;
687 }
688 
689 
714 function netmask_to_bits ($netmask)
715 {
716  $nm = explode('.', $netmask, 4);
717 
718  $res = 0;
719  for ($n = 0; $n < 4; $n++) {
720  $start = 255;
721 
722  for ($i = 0; $i < 8; $i++) {
723  if ($start == (int)($nm[$n])) {
724  $res += 8 - $i;
725  break;
726  }
727  $start -= 2 ** $i;
728  }
729  }
730 
731  return $res;
732 }
733 
746 function to_byte ($value)
747 {
748  $value = strtolower(trim($value));
749 
750  if (!is_numeric(substr($value, -1))) {
751  switch (substr($value, -1)) {
752  case 'g':
753  $mult = 1073741824;
754  break;
755  case 'm':
756  $mult = 1048576;
757  break;
758  case 'k':
759  $mult = 1024;
760  break;
761  default:
762  return $value;
763  }
764 
765  return $mult * (int)substr($value, 0, -1);
766  } else {
767  return $value;
768  }
769 }
770 
780 function humanReadableSize ($bytes, $precision = 2)
781 {
782  $format = [
783  _('%sB'),
784  _('%sKiB'),
785  _('%sMiB'),
786  _('%sGiB'),
787  _('%sTiB'),
788  _('%sPiB'),
789  _('%sEiB'),
790  _('%sZiB'),
791  _('%sYiB')
792  ];
793  if ($bytes == 0) {
794  return sprintf($format[0], '0');
795  }
796  $base = log($bytes) / log(1024);
797 
798  return sprintf($format[floor($base)], round(1024 ** ($base - floor($base)), $precision));
799 }
800 
801 
814 function in_array_ics ($value, array $items)
815 {
816  return preg_grep('/^'.preg_quote($value, '/').'$/i', $items);
817 }
818 
826 function validate ($string)
827 {
828  return strip_tags(str_replace('\0', '', $string));
829 }
830 
831 
842 function rmdirRecursive ($path, $followLinks = FALSE)
843 {
844  $dir = opendir($path);
845  while ($entry = readdir($dir)) {
846  if (is_file($path."/".$entry) || ((!$followLinks) && is_link($path."/".$entry))) {
847  unlink($path."/".$entry);
848  } elseif (is_dir($path."/".$entry) && ($entry != '.') && ($entry != '..')) {
849  rmdirRecursive($path."/".$entry);
850  }
851  }
852  closedir($dir);
853  return rmdir($path);
854 }
855 
856 
869 function scan_directory ($path, $sort_desc = FALSE)
870 {
871  $ret = FALSE;
872 
873  /* is this a dir ? */
874  /* is this path a readable one */
875  if (is_dir($path) && is_readable($path)) {
876  /* Get contents and write it into an array */
877  $ret = [];
878 
879  $dir = opendir($path);
880 
881  /* Is this a correct result ?*/
882  if ($dir) {
883  while ($fp = readdir($dir)) {
884  $ret[] = $fp;
885  }
886  }
887  }
888  /* Sort array ascending , like scandir */
889  sort($ret);
890 
891  /* Sort descending if parameter is sort_desc is set */
892  if ($sort_desc) {
893  $ret = array_reverse($ret);
894  }
895 
896  return $ret;
897 }
898 
899 
905 function clean_smarty_compile_dir ($directory)
906 {
907  if (is_dir($directory) && is_readable($directory)) {
908  // Set revision filename to REVISION
909  $revision_file = $directory."/REVISION";
910 
911  /* Is there a stamp containing the current revision? */
912  if (file_exists($revision_file)) {
913  // check for "$config->...['CONFIG']/revision" and the
914  // contents should match the revision number
915  if (!compare_revision($revision_file, FD_VERSION)) {
916  // If revision differs, clean compile directory
917  foreach (scan_directory($directory) as $file) {
918  if (($file == '.') || ($file == '..')) {
919  continue;
920  }
921  if (is_file($directory.'/'.$file) && !unlink($directory.'/'.$file)) {
922  $error = new FusionDirectoryError(
923  htmlescape(sprintf(
924  _('File "%s" could not be deleted. Try "fusiondirectory-configuration-manager --check-directories" to fix permissions.'),
925  $directory."/".$file
926  ))
927  );
928  $error->display();
929  }
930  }
931  } else {
932  // Revision matches, nothing to do
933  }
934  }
935  /* If the file does not exists or has just been deleted */
936  if (!file_exists($revision_file)) {
937  // create revision file
938  create_revision($revision_file, FD_VERSION);
939  }
940  }
941 }
942 
954 function create_revision ($revision_file, $revision)
955 {
956  $result = FALSE;
957 
958  if (is_dir(dirname($revision_file)) && is_writable(dirname($revision_file))) {
959  $fh = fopenWithErrorHandling($revision_file, 'w');
960  if (is_array($fh)) {
961  $error = new FusionDirectoryError(
962  htmlescape(_('Cannot write to revision file:')).'<br/>'.
963  implode(
964  '<br/>',
965  array_map('htmlescape', $fh)
966  )
967  );
968  $error->display();
969  return $result;
970  } else {
971  if (fwrite($fh, $revision)) {
972  $result = TRUE;
973  }
974  fclose($fh);
975  }
976  }
977 
978  if (!$result) {
979  $error = new FusionDirectoryError(htmlescape(_('Cannot write to revision file!')));
980  $error->display();
981  }
982 
983  return $result;
984 }
985 
996 function compare_revision ($revision_file, $revision)
997 {
998  // FALSE means revision differs
999  $result = FALSE;
1000 
1001  if (file_exists($revision_file) && is_readable($revision_file)) {
1002  // Open file
1003  $fh = fopenWithErrorHandling($revision_file, 'r');
1004  if (is_array($fh)) {
1005  $error = new FusionDirectoryError(
1006  htmlescape(_('Cannot read revision file:')).'<br/>'.
1007  implode(
1008  '<br/>',
1009  array_map('htmlescape', $fh)
1010  )
1011  );
1012  $error->display();
1013  } else {
1014  // Compare File contents with current revision
1015  if ($revision == fread($fh, filesize($revision_file))) {
1016  $result = TRUE;
1017  }
1018  // Close file
1019  fclose($fh);
1020  }
1021  }
1022 
1023  return $result;
1024 }
1025 
1026 
1045 function array_key_ics ($ikey, array $items)
1046 {
1047  $tmp = array_change_key_case($items, CASE_LOWER);
1048  $ikey = strtolower($ikey);
1049  if (isset($tmp[$ikey])) {
1050  return $tmp[$ikey];
1051  }
1052 
1053  return '';
1054 }
1055 
1056 
1066 function array_differs (array $src, array $dst): bool
1067 {
1068  /* If the count is differing, the arrays differ */
1069  if (count($src) != count($dst)) {
1070  return TRUE;
1071  }
1072 
1073  return (count(array_diff($src, $dst)) != 0);
1074 }
1075 
1085 function array_differs_recursive ($src, $dst): bool
1086 {
1087  return (array_cmp_recursive($src, $dst) !== 0);
1088 }
1089 
1099 function array_cmp_recursive ($src, $dst): int
1100 {
1101  if (is_array($src)) {
1102  if (!is_array($dst)) {
1103  return 1;
1104  }
1105  if (count($src) != count($dst)) {
1106  return count($src) - count($dst);
1107  }
1108  foreach ($src as $key => $value) {
1109  if (!isset($dst[$key])) {
1110  return 1;
1111  }
1112  if (($cmp = array_cmp_recursive($dst[$key], $value)) !== 0) {
1113  return $cmp;
1114  }
1115  }
1116  return 0;
1117  }
1118  return strcmp($src, $dst);
1119 }
1120 
1127 function normalizeLdap ($input)
1128 {
1129  trigger_error('deprecated, use ldap_escape_f');
1130  return addcslashes($input, '*()\\/');
1131 }
1132 
1138 function check_schema (array $cfg)
1139 {
1140  $checks = [];
1141 
1142  /* Get objectclasses */
1143  $ldapObj = new LDAP($cfg['admin'], $cfg['password'], $cfg['connection'], FALSE, $cfg['tls']);
1144  $ldap = new ldapMultiplexer($ldapObj);
1145  $objectclasses = $ldap->get_objectclasses(TRUE);
1146  if (count($objectclasses) == 0) {
1147  $warning = new FusionDirectoryWarning(htmlescape(_('Cannot get schema information from server. No schema check possible!')));
1148  $warning->display();
1149  return $checks;
1150  }
1151 
1152  /* This is the default block used for each entry.
1153  * to avoid unset indexes.
1154  */
1155  $def_check = [
1156  'SCHEMA_FILE' => '',
1157  'CLASSES_REQUIRED' => [],
1158  'STATUS' => FALSE,
1159  'IS_MUST_HAVE' => FALSE,
1160  'MSG' => '',
1161  'INFO' => ''
1162  ];
1163 
1164  /* FusionDirectory core schemas */
1165 
1166  /* core-fd */
1167  $checks['core-fd'] = $def_check;
1168 
1169  $checks['core-fd']['SCHEMA_FILE'] = 'core-fd.schema';
1170  $checks['core-fd']['CLASSES_REQUIRED'] = ['fdLockEntry'];
1171  $checks['core-fd']['IS_MUST_HAVE'] = TRUE;
1172  $checks['core-fd']['INFO'] = _('Main FusionDirectory schema');
1173 
1174  /* core-fd-conf */
1175  $checks['core-fd-conf'] = $def_check;
1176 
1177  $checks['core-fd-conf']['SCHEMA_FILE'] = 'core-fd-conf.schema';
1178  $checks['core-fd-conf']['CLASSES_REQUIRED'] = ['fusionDirectoryConf'];
1179  $checks['core-fd-conf']['IS_MUST_HAVE'] = TRUE;
1180  $checks['core-fd-conf']['INFO'] = _('Schema used to store FusionDirectory configuration');
1181 
1182  /* ldapns */
1183  $checks['ldapns'] = $def_check;
1184 
1185  $checks['ldapns']['SCHEMA_FILE'] = 'ldapns.schema';
1186  $checks['ldapns']['CLASSES_REQUIRED'] = ['hostObject'];
1187  $checks['ldapns']['IS_MUST_HAVE'] = FALSE;
1188  $checks['ldapns']['INFO'] = _('Used to store trust mode information in users or groups.');
1189 
1190  /* template-fd */
1191  $checks['template-fd'] = $def_check;
1192 
1193  $checks['template-fd']['SCHEMA_FILE'] = 'template-fd.schema';
1194  $checks['template-fd']['CLASSES_REQUIRED'] = ['fdTemplate'];
1195  $checks['template-fd']['IS_MUST_HAVE'] = FALSE;
1196  $checks['template-fd']['INFO'] = _('Used to store templates.');
1197 
1198  if (class_available('posixAccount')) {
1199  /* nis */
1200  $checks['nis'] = $def_check;
1201 
1202  $checks['nis']['SCHEMA_FILE'] = 'nis.schema';
1203  $checks['nis']['CLASSES_REQUIRED'] = ['posixAccount'];
1204  $checks['nis']['IS_MUST_HAVE'] = FALSE;
1205  $checks['nis']['INFO'] = _('Used to store POSIX information.');
1206  }
1207 
1208  foreach ($checks as $name => $value) {
1209  foreach ($value['CLASSES_REQUIRED'] as $class) {
1210  if (!isset($objectclasses[$class])) {
1211  $checks[$name]['STATUS'] = FALSE;
1212  if ($value['IS_MUST_HAVE']) {
1213  $checks[$name]['MSG'] = sprintf(_('Missing required object class "%s"!'), $class);
1214  } else {
1215  $checks[$name]['MSG'] = sprintf(_('Missing optional object class "%s"!'), $class);
1216  }
1217  } else {
1218  $checks[$name]['STATUS'] = TRUE;
1219  $checks[$name]['MSG'] = sprintf(_('Class(es) available'));
1220  }
1221  }
1222  }
1223 
1224  $checks['posixGroup'] = $def_check;
1225 
1226  $checks['posixGroup']['SCHEMA_FILE'] = 'nis.schema';
1227  $checks['posixGroup']['CLASSES_REQUIRED'] = ['posixGroup'];
1228  $checks['posixGroup']['STATUS'] = TRUE;
1229  $checks['posixGroup']['MSG'] = '';
1230  $checks['posixGroup']['INFO'] = '';
1231 
1232  if (isset($objectclasses['posixGroup'])) {
1233  $checks['posixGroup']['IS_MUST_HAVE'] = TRUE;
1234 
1235  /* Depending on mixed groups plugin installation status, we need different schema configurations */
1236  if (class_available('mixedGroup') && isset($objectclasses['posixGroup']['STRUCTURAL'])) {
1237  $checks['posixGroup']['STATUS'] = FALSE;
1238  $checks['posixGroup']['MSG'] = _('You have installed the mixed groups plugin, but your schema configuration does not support this.');
1239  $checks['posixGroup']['INFO'] = _('In order to use mixed groups the objectClass "posixGroup" must be AUXILIARY');
1240  } elseif (!class_available('mixedGroup') && !isset($objectclasses['posixGroup']['STRUCTURAL'])) {
1241  $checks['posixGroup']['STATUS'] = FALSE;
1242  $checks['posixGroup']['MSG'] = _('Your schema is configured to support mixed groups, but this plugin is not present.');
1243  $checks['posixGroup']['INFO'] = _('The objectClass "posixGroup" must be STRUCTURAL');
1244  }
1245  }
1246 
1247  return $checks;
1248 }
1249 
1250 
1261 function get_post ($name)
1262 {
1263  if (!isset($_POST[$name])) {
1264  trigger_error("Requested POST value (".$name.") does not exists, you should add a check to prevent this message.");
1265  return FALSE;
1266  }
1267 
1268  return validate($_POST[$name]);
1269 }
1270 
1274 function get_correct_class_name ($cls)
1275 {
1276  global $class_mapping;
1277  if (isset($class_mapping) && is_array($class_mapping)) {
1278  foreach (array_keys($class_mapping) as $class) {
1279  if (preg_match("/^".$cls."$/i", $class)) {
1280  return $class;
1281  }
1282  }
1283  }
1284  return FALSE;
1285 }
1286 
1287 
1302 function change_password ($dn, $password, $hash = "")
1303 {
1304  $userTabs = objects::open($dn, 'user');
1305  $userTab = $userTabs->getBaseObject();
1306  $userTab->userPassword = [
1307  $hash,
1308  $password,
1309  $password,
1310  $userTab->userPassword,
1311  $userTab->attributesAccess['userPassword']->isLocked()
1312  ];
1313  $userTabs->update();
1314  $error = $userTabs->save();
1315  if (!empty($error)) {
1316  return $error;
1317  }
1318 
1319  return TRUE;
1320 }
1321 
1334 function getEntryCSN (string $dn): string
1335 {
1336  global $config;
1337  if (empty($dn) || !is_object($config)) {
1338  return '';
1339  }
1340 
1341  /* Get attribute that we should use as serial number */
1342  $attr = $config->get_cfg_value('modificationDetectionAttribute');
1343  if ($attr != '') {
1344  $ldap = $config->get_ldap_link();
1345  $ldap->cat($dn, [$attr]);
1346  $attrs = $ldap->fetch();
1347  if (isset($attrs[$attr][0])) {
1348  return $attrs[$attr][0];
1349  }
1350  }
1351  return '';
1352 }
1353 
1363 function send_binary_content ($data, $name, $type = "application/octet-stream")
1364 {
1365  header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
1366  header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
1367  header("Cache-Control: no-cache");
1368  header("Pragma: no-cache");
1369  header("Cache-Control: post-check=0, pre-check=0");
1370  header("Content-type: ".$type);
1371 
1372  /* Strip name if it is a complete path */
1373  if (preg_match("/\//", $name)) {
1374  $name = basename($name);
1375  }
1376 
1377  /* force download dialog */
1378  header('Content-Disposition: attachment; filename="'.$name.'"');
1379 
1380  echo $data;
1381  exit();
1382 }
1383 
1394 function xmlentities ($str)
1395 {
1396  if (is_string($str)) {
1397  return htmlspecialchars($str, ENT_QUOTES);
1398  } elseif (is_array($str)) {
1399  foreach ($str as $name => $value) {
1400  $str[$name] = xmlentities($value);
1401  }
1402  }
1403  return $str;
1404 }
1405 
1409 function get_random_char ()
1410 {
1411  $randno = rand(0, 63);
1412  if ($randno < 12) {
1413  // Digits, '/' and '.'
1414  return chr($randno + 46);
1415  } elseif ($randno < 38) {
1416  // Uppercase
1417  return chr($randno + 53);
1418  } else {
1419  // Lowercase
1420  return chr($randno + 59);
1421  }
1422 }
1423 
1431 function cred_decrypt ($input, $password)
1432 {
1433  /************************* Inspired by Crypt/CBC.pm *******************************/
1434  $input = pack('H*', $input);
1435  if (substr($input, 0, 8) != 'Salted__') {
1436  throw new FusionDirectoryException("Invalid hash header: expected 'Salted__', found '".substr($input, 0, 8)."'");
1437  }
1438  $salt = substr($input, 8, 8);
1439  $input = substr($input, 16);
1440 
1441  $key_len = 32;
1442  $iv_len = openssl_cipher_iv_length('aes-256-cbc');
1443 
1444  $data = '';
1445  $d = '';
1446  while (strlen($data) < $key_len + $iv_len) {
1447  $d = md5($d . $password . $salt, TRUE);
1448  $data .= $d;
1449  }
1450  $key = substr($data, 0, $key_len);
1451  $iv = substr($data, $key_len, $iv_len);
1452 
1453  return openssl_decrypt($input, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
1454 }
1455 
1465 function isIpInNet ($ip, $net, $mask)
1466 {
1467  // Move to long ints
1468  $ip = ip2long($ip);
1469  $net = ip2long($net);
1470  $mask = ip2long($mask);
1471 
1472  // Mask given IP with mask. If it returns "net", we're in...
1473  return (($ip & $mask) == $net);
1474 }
1475 
1479 function expandIPv6 ($ip)
1480 {
1481  $hex = unpack('H*hex', inet_pton($ip));
1482  return substr(preg_replace('/([A-f0-9]{4})/', "$1:", $hex['hex']), 0, -1);
1483 }
1484 
1485 /* Mark the occurance of a string with a span */
1486 function mark ($needle, $haystack)
1487 {
1488  $result = '';
1489 
1490  while (preg_match('/^(.*)('.preg_quote($needle).')(.*)$/i', $haystack, $matches)) {
1491  $result .= $matches[1].'<span class="mark">'.$matches[2].'</span>';
1492  $haystack = $matches[3];
1493  }
1494 
1495  return $result.$haystack;
1496 }
1497 
1498 function reset_errors ()
1499 {
1500  session::set('errorsAlreadyPosted', []);
1501 }
1502 
1503 function load_all_classes ()
1504 {
1505  global $BASE_DIR, $class_list, $class_mapping;
1506  /* Initially load all classes */
1507  $class_list = get_declared_classes();
1508  foreach ($class_mapping as $class => $path) {
1509  if (!in_array($class, $class_list)) {
1510  if (is_readable("$BASE_DIR/$path")) {
1511  require_once("$BASE_DIR/$path");
1512  } else {
1513  throw new FatalError(
1514  sprintf(
1515  htmlescape(_('Cannot locate file "%s" - please run "%s" to fix this')),
1516  htmlescape("$BASE_DIR/$path"),
1517  '<b>fusiondirectory-configuration-manager --update-cache</b>'
1518  )
1519  );
1520  }
1521  }
1522  }
1523 }
1524 
1525 function ldap_escape_f ($str, $ignore = '')
1526 {
1527  return ldap_escape($str, $ignore, LDAP_ESCAPE_FILTER);
1528 }
1529 
1530 function ldap_escape_dn ($str, $ignore = '')
1531 {
1532  return ldap_escape($str, $ignore, LDAP_ESCAPE_DN);
1533 }
1534 
1535 function mail_utf8 ($to, $from_user, $from_email, $subject, $message, $replyto_user = NULL, $replyto_email = NULL, $type = 'plain')
1536 {
1537  $subject = "=?UTF-8?B?".base64_encode($subject)."?=";
1538 
1539  if ($replyto_user === NULL) {
1540  $replyto_user = $from_user;
1541  }
1542 
1543  if ($replyto_email === NULL) {
1544  $replyto_email = $from_email;
1545  }
1546 
1547  if ($from_user) {
1548  $from_user = "=?UTF-8?B?".base64_encode($from_user)."?=";
1549  $headers = "From: $from_user <$from_email>\r\n";
1550  } else {
1551  $headers = "From: <$from_email>\r\n";
1552  }
1553 
1554  if ($replyto_email) {
1555  if ($replyto_user) {
1556  $replyto_user = "=?UTF-8?B?".base64_encode($replyto_user)."?=";
1557  $headers .= "Reply-To: $replyto_user <$replyto_email>\r\n";
1558  } else {
1559  $headers .= "Reply-To: <$replyto_email>\r\n";
1560  }
1561  }
1562 
1563  $headers .= "MIME-Version: 1.0" . "\r\n" .
1564  "Content-type: text/$type; charset=UTF-8" . "\r\n";
1565 
1566  $additional_parameters = "-f".$from_email;
1567 
1568  return mail($to, $subject, $message, $headers, $additional_parameters);
1569 }
1570 
1571 /* Calls fopen, gives errors as an array if any, file handle if successful */
1572 function fopenWithErrorHandling (...$args)
1573 {
1574  $errors = [];
1575  set_error_handler(
1576  function (int $errno, string $errstr, string $errfile, int $errline, array $errcontext) use (&$errors): bool
1577  {
1578  $errors[] = $errstr;
1579 
1580  return TRUE;
1581  }
1582  );
1583  $fh = @fopen(...$args);
1584  restore_error_handler();
1585  if ($fh !== FALSE) {
1586  return $fh;
1587  }
1588  return $errors;
1589 }
1590 
1591 // Check to see if it exists in case PHP has this function later
1592 if (!function_exists('mb_substr_replace')) {
1593  // Same parameters as substr_replace with the extra encoding parameter
1594  function mb_substr_replace (string $string, string $replacement, $start, $length = NULL, $encoding = NULL)
1595  {
1596  if ($encoding === NULL) {
1597  $encoding = mb_internal_encoding();
1598  }
1599  if ($length === NULL) {
1600  return mb_substr($string, 0, $start, $encoding).
1601  $replacement;
1602  } else {
1603  return mb_substr($string, 0, $start, $encoding).
1604  $replacement.
1605  mb_substr($string, $start + $length, NULL, $encoding);
1606  }
1607  }
1608 }
const CACHE_DIR
Global cache dir.
Definition: variables.inc:55
cred_decrypt($input, $password)
Decrypt a string with RIJNDAEL_128.
Definition: functions.inc:1431
get_random_char()
Returns a random char.
Definition: functions.inc:1409
const FD_VERSION
FusionDirectory Version.
rmdirRecursive($path, $followLinks=FALSE)
Recursively delete a path in the file system.
Definition: functions.inc:842
htmlescape(string $str)
Escape string for HTML output.
Definition: php_setup.inc:32
const CLASS_CACHE
name of the class.cache file
Definition: variables.inc:85
in_array_ics($value, array $items)
Check if a value exists in an array (case-insensitive)
Definition: functions.inc:814
getEntryCSN(string $dn)
Get the Change Sequence Number of a certain DN.
Definition: functions.inc:1334
normalize_netmask($netmask)
Put netmask in n.n.n.n format.
Definition: functions.inc:664
array_differs_recursive($src, $dst)
Definition: functions.inc:1085
get_ou($name)
Get the OU of a certain RDN.
Definition: functions.inc:391
compare_revision($revision_file, $revision)
Compare the revision file.
Definition: functions.inc:996
get_template_path($filename='', $plugin=FALSE, $path='')
Return themed path for specified base file.
Definition: functions.inc:174
static get($name)
Accessor of a session var.
scan_directory($path, $sort_desc=FALSE)
Get directory content information.
Definition: functions.inc:869
send_binary_content($data, $name, $type="application/octet-stream")
Initialize a file download with given content, name and data type.
Definition: functions.inc:1363
& get_userinfo()
Return the current userinfo object.
Definition: functions.inc:312
fusiondirectory_log($message)
Generate a system log info.
Definition: functions.inc:288
fusiondirectory_autoload($class_name)
Does autoloading for classes used in FusionDirectory.
Definition: functions.inc:44
get_correct_class_name($cls)
Return class name in correct case.
Definition: functions.inc:1274
static set($name, $value)
Set a value in a session.
get_base_from_people($dn)
Return a base from a given user DN.
Definition: functions.inc:500
array_cmp_recursive($src, $dst)
Definition: functions.inc:1099
check_schema(array $cfg)
Check if LDAP schema matches the requirements.
Definition: functions.inc:1138
& get_smarty()
Get global smarty object.
Definition: functions.inc:324
copynotice()
Return HTML safe copyright notice.
Definition: functions.inc:149
isIpInNet($ip, $net, $mask)
Test if an ip is the network range.
Definition: functions.inc:1465
netmask_to_bits($netmask)
Return the number of set bits in the netmask.
Definition: functions.inc:714
static open(string $dn, string $type)
Create the tab object for the given dn.
static debug(int $level, int $line, string $function, string $file, $data, string $info='')
Debug output method.
Parent class for all exceptions thrown in FusionDirectory.
array_differs(array $src, array $dst)
Definition: functions.inc:1066
to_string($value)
Return a string/HTML representation of an array.
Definition: functions.inc:549
dn2base($dn, $ou=NULL)
Return the base of a given DN.
Definition: functions.inc:614
change_password($dn, $password, $hash="")
Change the password of a given DN.
Definition: functions.inc:1302
This class contains all ldap function needed to make ldap operations easy.
Definition: class_ldap.inc:34
create_revision($revision_file, $revision)
Create the revision file.
Definition: functions.inc:954
Fatal error class. Does not extend FusionDirectoryError.
rewrite($s)
Function to rewrite some problematic characters.
Definition: functions.inc:572
xmlentities($str)
Encode special string characters.
Definition: functions.inc:1394
validate($string)
Removes malicious characters from a (POST) string.
Definition: functions.inc:826
strict_uid_mode()
Check if strict naming rules are configured.
Definition: functions.inc:526
array_key_ics($ikey, array $items)
Lookup a key in an array case-insensitive.
Definition: functions.inc:1045
get_people_ou()
Get the OU for users.
Definition: functions.inc:484
check_command($cmdline)
Check if a given command exists and is executable.
Definition: functions.inc:640
get_post($name)
Returns contents of the given POST variable and check magic quotes settings.
Definition: functions.inc:1261
expandIPv6($ip)
Expands an IP v6.
Definition: functions.inc:1479
clean_smarty_compile_dir($directory)
Clean the smarty compile dir.
Definition: functions.inc:905
normalizeLdap($input)
Escape all LDAP filter relevant characters.
Definition: functions.inc:1127
array_merge_unique(array $ar1, array $ar2)
Definition: functions.inc:275
Parent class for all errors in FusionDirectory.
convert_department_dn($dn, $base=NULL)
Convert a department DN to a sub-directory style list.
Definition: functions.inc:349
to_byte($value)
Convert various data sizes to bytes.
Definition: functions.inc:746
plugin_available($plugin)
Check if plugin is available.
Definition: functions.inc:108
array_remove_entries_ics(array $needles, array $haystack)
Definition: functions.inc:257
array_remove_entries(array $needles, array $haystack)
Definition: functions.inc:239
DEBUG($level, $line, $function, $file, $data, $info='')
Debug level action.
Definition: functions.inc:141
humanReadableSize($bytes, $precision=2)
Convert a size in bytes to a human readable version.
Definition: functions.inc:780
class_available($name)
Checks if a class is available.
Definition: functions.inc:92