An error occurred while loading the file. Please try again.
-
Côme Chilliet authored
This avoids calling array_merge on what could be a generator. It also changes a bit the logic behind modifier system issue #5882
fcfcea49
<?php
/*
This code is part of FusionDirectory (http://www.fusiondirectory.org/)
Copyright (C) 2011-2016 FusionDirectory
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*!
* \file class_templateHandling.inc
* Source code for the class templateHandling
*/
/*! \brief this class stores static methods used to parse templates LDAP data
*/
class templateHandling
{
/*! \brief Fetch a template from LDAP and returns its attributes and dependencies information */
public static function fetch($dn)
{
global $config;
$ldap = $config->get_ldap_link();
$ldap->cat($dn);
$attrs = $ldap->fetch();
$attrs = static::fieldsFromLDAP($attrs);
list($depends, $errors) = static::attributesDependencies($attrs);
msg_dialog::displayChecks($errors);
$attrs = static::sortAttributes($attrs, $depends);
return array($attrs, $depends);
}
/*! \brief Translate template attrs into $attrs as if taken from LDAP */
public static function fieldsFromLDAP (array $template_attrs)
{
$attrs = array();
if (isset($template_attrs['fdTemplateField'])) {
unset($template_attrs['fdTemplateField']['count']);
sort($template_attrs['fdTemplateField']);
foreach ($template_attrs['fdTemplateField'] as $field) {
preg_match('/^([^:]+):(.*)$/s', $field, $m);
if (isset($attrs[$m[1]])) {
$attrs[$m[1]][] = $m[2];
$attrs[$m[1]]['count']++;
} else {
$attrs[$m[1]] = array($m[2]);
$attrs[$m[1]]['count'] = 1;
}
}
}
return $attrs;
}
/*! \brief Translate $attrs into template attrs */
public static function fieldsToLDAP (array $template_attrs, array $attrs)
{
/* First a bit of cleanup */
unset($template_attrs['dn']);
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
unset($template_attrs['fdTemplateField']['count']);
unset($template_attrs['objectClass']['count']);
unset($template_attrs['cn']['count']);
if (isset($template_attrs['count'])) {
for ($i = 0; $i < $template_attrs['count']; ++$i) {
/* Remove numeric keys */
unset($template_attrs[$i]);
}
}
unset($template_attrs['count']);
/* Remove all concerned values */
foreach ($template_attrs['fdTemplateField'] as $key => $value) {
preg_match('/^([^:]+):(.*)$/s', $value, $m);
if (isset($attrs[$m[1]])) {
unset($template_attrs['fdTemplateField'][$key]);
}
}
/* Then insert non-empty values */
foreach ($attrs as $key => $value) {
if (is_array($value)) {
foreach ($value as $v) {
if ($value == "") {
continue;
}
$template_attrs['fdTemplateField'][] = $key.':'.$v;
}
} else {
if ($value == "") {
continue;
}
$template_attrs['fdTemplateField'][] = $key.':'.$value;
}
}
sort($template_attrs['fdTemplateField']);
return $template_attrs;
}
/*! \brief Check template fields
*
* Returns errors if there are recursive dependencies.
* Might check more things later
*/
public static function checkFields ($attrs)
{
list(, $errors) = static::attributesDependencies($attrs);
return $errors;
}
/*! \brief Parse a mask (without surrounding %) using $attrs attributes, apply modifiers and returns an array containing possible results */
public static function parseMask($mask, array $attrs)
{
if ($mask == '|') {
return array('%');
}
$modifiers = '';
if (preg_match('/^([^|]+)\|/', $mask, $m)) {
$modifiers = $m[1];
$mask = substr($mask, strlen($m[0]));
}
$result = array();
if (isset($attrs[$mask])) {
$result = $attrs[$mask];
if (is_array($result)) {
unset($result['count']);
}
} elseif (($mask != '') && !preg_match('/c/', $modifiers)) {
trigger_error("'$mask' was not found in attributes");
}
$len = strlen($modifiers);
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
for ($i = 0; $i < $len; ++$i) {
$args = array();
$modifier = $modifiers[$i];
if (preg_match('/^\[([^\]]+)\].*$/', substr($modifiers, $i + 1), $m)) {
/* get modifier args */
$args = explode(',', $m[1]);
$i += strlen($m[1]) + 2;
}
$result = static::applyModifier($modifier, $args, $result);
}
return $result;
}
/*! \brief Return attrs needed before applying template
*
* \return array An array of attributes which are needed by the template
*/
public static function neededAttrs(array &$attrs, array $flatdepends)
{
$needed = array();
foreach ($flatdepends as $attr => $depends) {
if ((isset($depends[0])) && ($depends[0] == 'askme')) {
$needed[] = $attr;
unset($flatdepends[$attr]);
unset($attrs[$attr]);
}
}
$dependencies = array_unique(array_merge(...array_values($flatdepends)));
foreach ($dependencies as $attr) {
if (empty($flatdepends[$attr])) {
$needed[] = $attr;
}
}
return array_unique($needed);
}
/*! \brief Parse template masks in an array
*
* \param array $attrs The attributes in LDAP format
* \param array $specialAttrs Some additional fake attributes which can be used in masks
* \param string $target Dn of an object to ignore in the unicity check
*
* \return array An array with the final values of attributes
*/
public static function parseArray(array $attrs, array $specialAttrs, $target = NULL)
{
foreach ($attrs as $name => &$attr) {
if (is_array($attr)) {
foreach ($attr as $key => &$string) {
if (!is_numeric($key)) {
continue;
}
$string = static::parseString($string, array_merge($attrs, $specialAttrs), NULL, $name, $target);
}
unset($string);
}
}
unset($attr);
return $attrs;
}
/*! \brief Parse template masks in a single string
*
* \param string $string The mask
* \param array $attrs The attributes in LDAP format
* \param callable $escapeMethod Method to call to escape mask result
* \param string $unique Name of the LDAP attribute to check unicity on, if any
* \param string $target Dn of an object to ignore in the unicity check
*
* \return string the string with patterns replaced by their values
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
*/
public static function parseString($string, array $attrs, $escapeMethod = NULL, $unique = NULL, $target = NULL)
{
global $config;
if (preg_match('/^%%/', $string)) {
/* Special case: %% at beginning of string means do not touch it. Used by binary attributes. */
return preg_replace('/^%%/', '', $string);
}
$offset = 0;
$vars = array();
while (preg_match('/%([^%]+)%/', $string, $m, PREG_OFFSET_CAPTURE, $offset)) {
$replace = static::parseMask($m[1][0], $attrs);
$vars[] = array($m[0][1], strlen($m[0][0]), $replace);
$offset = $m[0][1] + strlen($m[0][0]);
}
$generator = static::iteratePossibleValues($string, $vars, $escapeMethod);
$string = $generator->current();
if (($unique !== NULL) && !empty($vars)) {
$ldap = $config->get_ldap_link();
$ldap->cd($config->current['BASE']);
/* Return the first found unique value */
foreach ($generator as $value) {
$filter = '('.ldap_escape_f($unique).'='.ldap_escape_f($value).')';
$ldap->search($filter, array('dn'));
if (
($ldap->count() == 0) ||
(($target !== NULL) && ($ldap->count() == 1) && ($ldap->getDN() == $target))
) {
return $value;
}
}
}
return $string;
}
/*! \brief Generator that yields possible template mask values
*
* \param string $rule The mask
* \param array $variables The possible values for each mask with its position and length
* \param callable $escapeMethod Method to call to escape mask result
*/
protected static function iteratePossibleValues($rule, array $variables, $escapeMethod = NULL)
{
if (!count($variables)) {
yield $rule;
return;
}
/* Start from the end to avoid messing the positions, and to avoid ids at the end if not needed (common usecase) */
list($pos, $length, $val) = array_pop($variables);
/* $val may be an iterator or an array */
foreach ($val as $possibility) {
if ($escapeMethod !== NULL) {
$possibility = $escapeMethod($possibility);
}
$nrule = mb_substr_replace($rule, $possibility, $pos, $length);
foreach (static::iteratePossibleValues($nrule, $variables, $escapeMethod) as $result) {
yield $result;
}
}
}
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
/*! \brief Parse template masks in a single string and list the fields it needs
*
* \return array An array with the names of the fields used in the string pattern
*/
public static function listFields($string)
{
$fields = array();
$offset = 0;
while (preg_match('/%([^%]+)%/', $string, $m, PREG_OFFSET_CAPTURE, $offset)) {
$mask = $m[1][0];
$offset = $m[0][1] + strlen($m[0][0]);
if ($mask == '|') {
continue;
}
if (preg_match('/^([^|]+)\|/', $mask, $m)) {
$mask = substr($mask, strlen($m[0]));
}
$fields[] = $mask;
}
return $fields;
}
private static function modifierRemoveAccents($str)
{
$str = htmlentities($str, ENT_NOQUOTES, 'UTF-8');
$str = preg_replace('#&([A-za-z])(?:acute|cedil|circ|grave|orn|ring|slash|th|tilde|uml);#', '\1', $str);
// handle ligatures
$str = preg_replace('#&([A-za-z]{2})(?:lig);#', '\1', $str);
// delete unhandled characters
return array(preg_replace('#&[^;]+;#', '', $str));
}
private static function modifierTranslit(array $args, $str)
{
$localesaved = setlocale(LC_CTYPE, 0);
$ret = array();
foreach ($args as $arg) {
setlocale(LC_CTYPE, array($arg,"$arg.UTF8"));
$ret[] = iconv('UTF8', 'ASCII//TRANSLIT', $str);
}
setlocale(LC_CTYPE, $localesaved);
return array_unique($ret);
}
private static function modifierPregReplace(array $args, $str)
{
$pattern = '/\s/';
$replace = '';
if (count($args) >= 1) {
$pattern = $args[0];
if (count($args) >= 2) {
$replace = $args[1];
}
}
return array(preg_replace($pattern.'u', $replace, $str));
}
private static function modifierSubString(array $args, $str)
{
if (count($args) < 1) {
trigger_error("Missing 's' substr modifier parameter");
}
if (count($args) < 2) {
array_unshift($args, 0);
}
if (preg_match('/^(\d+)-(\d+)$/', $args[1], $m)) {
$res = array();
for ($i = $m[1];$i < $m[2]; ++$i) {
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
$res[] = mb_substr($str, $args[0], $i);
}
return array_unique($res);
} else {
return array(mb_substr($str, $args[0], $args[1]));
}
}
private static function modifierRandomString(array $args)
{
$length = 8;
$chars = 'b';
if (count($args) >= 2) {
$length = random_int($args[0], $args[1]);
if (count($args) >= 3) {
$chars = $args[2];
}
} elseif (count($args) >= 1) {
$length = $args[0];
}
$res = '';
for ($i = 0; $i < $length; ++$i) {
switch ($chars) {
case 'd':
/* digits */
$res .= (string)random_int(0, 9);
break;
case 'l':
/* letters */
$nb = random_int(65, 116);
if ($nb > 90) {
/* lowercase */
$nb += 6;
}
$res .= chr($nb);
break;
case 'b':
/* both */
default:
$nb = random_int(65, 126);
if ($nb > 116) {
/* digit */
$nb = (string)($nb - 117);
} else {
if ($nb > 90) {
/* lowercase */
$nb += 6;
}
$nb = chr($nb);
}
$res .= $nb;
break;
}
}
return $res;
}
private static function modifierDate(array $args)
{
if (count($args) < 1) {
$args[] = 'now';
}
if (count($args) < 2) {
$args[] = 'd.m.Y';
}
$dateObject = new DateTime($args[0], new DateTimeZone('UTC'));
if ($args[1] == 'epoch') {
/* Special handling for shadowExpire: days since epoch */
return array(floor($dateObject->format('U') / 86400));
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
}
return array($dateObject->format($args[1]));
}
private static function modifierNumber(array $args)
{
if (count($args) < 1) {
$args[] = FALSE;
}
if (count($args) < 2) {
$args[] = 1;
}
if (count($args) < 3) {
$args[] = 1;
}
$numberGenerator = function ($mandatory, $start, $step) {
$i = $start;
while (TRUE) {
yield $i;
$i += $step;
}
};
return $numberGenerator($args[0], $args[1], $args[2]);
}
/*! \brief Apply a modifier
*
* \param string $m the modifier
* \param array $args the parameters
* \param mixed $str the string or array to apply the modifier on
*
* \return array an array of possible values
* */
protected static function applyModifier($m, array $args, $str)
{
mb_internal_encoding('UTF-8');
mb_regex_encoding('UTF-8');
if (is_array($str) && (!is_numeric($m)) && (strtolower($m) == $m)) {
/* $str is an array and $m is lowercase, so it's a string modifier */
if (count($str) == 0) {
$str = '';
} else {
$str = reset($str);
}
}
switch ($m) {
case 'F':
// First
$result = array(reset($str));
break;
case 'L':
// Last
$result = array(end($str));
break;
case 'J':
// Join
if (isset($args[0])) {
$result = array(join($args[0], $str));
} else {
$result = array(join($str));
}
break;
case 'C':
// Count
$result = array(count($str));
break;
case 'M':
// Match
if (count($args) < 1) {