>From b09b39736366706ac957511bbfc6fd0eccbbddac Mon Sep 17 00:00:00 2001
From: Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
Date: Tue, 27 Jul 2021 08:50:41 +0200
Subject: [PATCH] include/functions.inc: Resolve UID generation for cases where
 many UIDs have already been taken.

 While mass creating users these days for a school, I noticed that an
 idGenerator expression such as "{%sn[6-8]}{%givenName[2-4]}" did not
 produce expected results if many UIDs have already been taken.

 In my case (making up names here), I had

   sn: Fresher
   givenName: Jim

 Note that both name parts, sn and givenName, have less then the
 max number of characters as specified in the idGenerator expression.
 (i.e. Fresher -> 7 chars, Jim -> 3 chars).

 In my use case excample, the following login name was already in use,
 so it got excluded by gen_uids() from the list of possible login names::

   fresheji

 So, the expected results for gen_uids() should have been:

  fresherji
  freshejim
  fresherjim

 But gen_uids() returned an empty result array().

 This change fixes UID generation and also includes UIDs that have max
 number of allowed / possible characters as specified by the idGenerator
 expression.

 Without this change, only UIDs up to max-1 characters (or strlen-1
 characters) are offered.

Signed-off-by: Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
---
 include/functions.inc | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/include/functions.inc b/include/functions.inc
index 046bbed75..2e686c243 100644
--- a/include/functions.inc
+++ b/include/functions.inc
@@ -3866,10 +3866,10 @@ function gen_uids($rule, $attributes)
     
     @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $attributes, "Prepare");
 
-    // Search for '{%...[n-m]}
+    // Search for '{%...[min-max]}'
     // Get all matching parts of the given string and sort them by
     //  length, to avoid replacing strings like '%uidNumber' with 'uid'
-    //  instead of 'uidNumber'; The longest tring at first.
+    //  instead of 'uidNumber'; The longest string at first.
     preg_match_all('/(\{?%([a-z0-9]+)(\[(([0-9]+)(\-([0-9]+))?)\])?\}?)/i', $rule ,$matches, PREG_SET_ORDER);
     $replacements = array(); 
     foreach($matches as $match){
@@ -3877,13 +3877,23 @@ function gen_uids($rule, $attributes)
       # initialize the match's replacement array...
       $replacements[$match[0]] = array();
 
+      # parameter 'min': don't allow '0' as start position (if set), rewrite to '1'
+      if (isset ($match[5]) && ($match[5] == 0)) {
+          $match[5] = 1;
+      }
+
+      # parameter 'max':  don't allow '0' as end position (if set), rewrite to '1'
+      if (isset ($match[7]) && ($match[7] == 0)) {
+          $match[7] = 1;
+      }
+
       // No start position given, then add the complete value
       if(!isset($match[7])){
         $replacements[$match[0]][] = $attributes[$match[2]];
 
         // Start given but no end, so just add a single character
       }elseif(!isset($match[7])){
-        if(isset($attributes[$match[2]][$match[5]])){
+        if(isset($attributes[$match[2]][$match[5]-1])){
           $tmp = " ".$attributes[$match[2]];
           $replacements[$match[0]][] = trim($tmp[$match[5]]);
         }
@@ -3892,7 +3902,7 @@ function gen_uids($rule, $attributes)
       }else{
         $str = "";
         for($i=$match[5]; $i<= $match[7]; $i++){
-          if(isset($attributes[$match[2]][$i])){
+          if(isset($attributes[$match[2]][$i-1])){
             $tmp = " ".$attributes[$match[2]];
             $str = substr ($tmp, 1, $i);
             $replacements[$match[0]][] = trim($str);
-- 
2.30.2

