Code

1d2ead0c1e8c258ecfc7415f501b4f49c0ea2e69
[gosa.git] / trunk / gosa-plugins / fai / admin / fai / class_FAI.inc
1 <?php
3 define("DEBUG_FAI_FUNC",FALSE);
6 class FAI
7 {
9   /* TEST PHASE .... */
11   static function get_all_objects_for_given_object($dn,$filter,$detailed = false)
12   {
13     $res  = FAI::get_all_objects_for_given_base($dn,$filter,$detailed);
14     $name = preg_replace("/,ou=.*$/","",$dn);
15     $entries = array();
16     foreach($res as $entry_dn => $data){
17       if(!preg_match("/,".$name.",/",$entry_dn)) continue;
18       $entries[$entry_dn] = $data; 
19     }
20     return($entries);
21   }
26   /* Returns all object for the given release.
27      This function resolves the releases  
28      from base up to the given dn.
29    */
30   static function get_all_objects_for_given_base($Current_DN,$filter,$detailed = false)
31   {
32     global $config;
33     $ldap = $config->get_ldap_link();
34     $ldap->cd($config->current['BASE']);
35     $res = array();
36     $tmp = array();
38     if(!FAI::is_release_department($Current_DN)) {
39 #      return($res);
40     }
42     /* Collect some basic informations and initialize some variables */ 
43     $base_release       = FAI::get_release_dn($Current_DN);
44     $previous_releases  = array_reverse(FAI::get_previous_releases_of_this_release($base_release,true));
46     $ldap->cat($base_release);
47     $attrs = $ldap->fetch();
48     $FAIstate = "branch";
49     if(isset($attrs['FAIstate'][0])){
50       $FAIstate = $attrs['FAIstate'][0];
51     }
53     /* We must also include the given release dn */
54     $previous_releases[] = $base_release;
56     /* Walk through all releases */
57     foreach($previous_releases as $release){
59       /* Get fai departments */
60       $deps_to_search = FAI::get_FAI_departments($release); 
62       /* For every single department  (ou=hoos,ou ..) */
63       foreach($deps_to_search as $fai_base){
65         /* Ldap search for fai classes specified in this release */
66         $attributes  = array("dn","objectClass","FAIstate","cn");
67         $res_tmp = get_list($filter,"fai",$fai_base,$attributes,GL_SUBSEARCH | GL_SIZELIMIT);
69         /* check the returned objects, and add/replace them in our return variable */
70         foreach($res_tmp as $attr){
72           $buffer = array();
73           $name = preg_replace("/".preg_quote($release, '/')."/i","",$attr['dn']);
75           if(isset($attr['FAIstate'][0])){
76             if(preg_match("/removed$/",$attr['FAIstate'][0])){
77               if(isset($res[$name])){
78                 unset($res[$name]);
79               }
80               continue;
81             }
82           }
84           /* Seems to be an inherited class, apply current FAIstate to this classes 
85            */
86           if(!preg_match("/".preg_quote($base_release, '/')."$/i",$attr['dn'])){
87             $buffer['FAIstate'] = $FAIstate; 
88           }else{
90             /* Seems to be created within this release department.
91                This indicates - it can't be of state "freeze"
92              */             
93             if(isset($attr['FAIstate'])){
94               $buffer['FAIstate'] = $attr['FAIstate'][0];
95             }else{
96               $buffer['FAIstate'] = "branch"; 
97             }
98           }
100           /* In detailed mode are some additonal informations visible */
101           if($detailed){
103             /* Create list of parents */
104             if(isset($res[$name])){
105               $buffer = $res[$name];
106               $buffer['parents'][] = $res[$name]['dn'];
107             }else{
108               $buffer['parents'] = array();
109             }
111             /* Append objectClass to resulsts */
112             foreach($attributes as $val){
113               if(isset($attr[$val])){
114                 $buffer[$val] = $attr[$val];
115               }
116             }
117             unset($buffer['objectClass']['count']);
118           }
120           /* Add this object to our list */
121           $buffer['dn']           = $attr['dn'];
122           $res[$name] = $buffer;
123         }
124       }
125     }
126     return($res);
127   }
130   /* Return all relevant FAI departments */
131   static function get_FAI_departments($suffix = "")
132   {
133     $arr = array("hooks","scripts","disk","packages","profiles","templates","variables");
134     $tmp = array();
135     if(preg_match("/^,/",$suffix)){
136       $suffix = preg_replace("/^,/","",$suffix);
137     }
138     foreach($arr as $name){
139       if(empty($suffix)){
140         $tmp[$name] = "ou=".$name;
141       }else{
142         $tmp[$name] = "ou=".$name.",".$suffix;
143       }
144     }
145     return($tmp);
146   }
149   /* Return all releases within the given base */
150   static function get_all_releases_from_base($dn,$appendedName=false)
151   {
152     global $config;
154     if(!preg_match("/".preg_quote(get_ou('faiBaseRDN'), '/')."/",$dn)){
155       $base = get_ou('faiBaseRDN').$dn;
156     }else{
157       $base = $dn;
158     }
159     $res = array();  
161     $ldap = $config->get_ldap_link();
162     $ldap->cd($base);
163     $ldap->search("(objectClass=FAIbranch)",array("ou","dn"));
164     while($attrs = $ldap->fetch()){
165       if($appendedName){
166         $res[$attrs['dn']] = convert_department_dn(preg_replace("/,".preg_quote(get_ou('faiBaseRDN'), '/').".*$/","",$attrs['dn']));
167       }else{
168         $res[$attrs['dn']] = $attrs['ou'][0];
169       }
170     }
171     return($res);
172   }
175   /* Add this object to list of objects, that must be checked for release saving */
176   static function prepare_to_save_FAI_object($Current_DN,$objectAttrs,$removed = false)
177   {
178     /* Get ldap object */  
179     global $config;
180     $addObj['Current_DN'] = $Current_DN;
181     $addObj['objectAttrs']= $objectAttrs;
182     $addObj['removed']    = $removed;
183     $addObj['diff']       = TRUE;
185     if(!$removed){
186       $ldap = $config->get_ldap_link();
187       $ldap->cd($config->current['BASE']);
188       $parent_dn = FAI::get_parent_object($Current_DN);
190       /* Get some basic informations */
191       $parent_obj   = FAI::get_parent_release_object($Current_DN);
192       /* Check whether parent object is removed, do not create object in this case */
193       if(!empty($parent_obj) && !FAI::parent_is_removed($Current_DN)){
194         $ldap->cat($parent_obj,array("*"));
195         $attrs = FAI::                           prepare_ldap_fetch_to_be_saved($ldap->fetch());
197         if(!FAI::array_diff_FAI( $attrs,$objectAttrs)){
198           $addObj['diff'] = FALSE;
199         }
200       } 
201     }else{
203       /* If this is the last CLASS of a specific name (e.g. DEPOTSERVER)
204           we have to remove this name from all profiles in this release.
205       */
206       $ldap = $config->get_ldap_link();
207       $ldap->cd($config->current['BASE']);
208       $obj_dn = FAI::get_parent_release_object($Current_DN,TRUE);
210       /* Dont't try to modify non FAIclasses  
211        */
212       if(!preg_match("/[^,]+,".preg_quote(get_ou("faiBaseRDN"), '/')."/",$obj_dn)){
213         trigger_error("PLEASE check fai class handling in ".__LINE__." -> ".__FILE__);        
214         echo "<br>-->".$Current_DN."<br>";
215         echo "<br>-->".$obj_dn."<br>";
216       }else{
218         /* Get source object and check if it is a base FAIclass
219          */
220         $ldap->cat($obj_dn);
221         $attrs = $ldap->fetch();
222         $classes = array("FAIprofile","FAIscript","FAIpackageList","FAIpartitionTable","FAIHook","FAIvariable","FAItemplate");
223         if(count(array_intersect($classes,$attrs['objectClass']))){
224           $cn    = $attrs['cn'][0];
226           /* Check if this is the last with this name in the current release.
227               In this case we have to remove the package name 
228               from all profiles in this release.
229            */
230           $classes = FAI::get_all_objects_for_given_base(FAI::get_release_dn($Current_DN),
231               "(&(objectClass=FAIclass)(cn=".$cn."))"); 
233           /* Check if this is the last class with this name.
234            */
235           if(count($classes) == 1){
237             /* Get all FAI Profiles 
238              */
239             $profiles = FAI::get_all_objects_for_given_base(FAI::get_release_dn($Current_DN),
240                 "(&(objectClass=FAIprofile)(FAIclass=*))"); 
242             /* Walk though all profiles and remove the source class name
243              */
244             foreach($profiles as $dn){
245               $ldap->cat($dn['dn']);
246               $attrs = $ldap->fetch();
248               $attrs = array('FAIclass' => $attrs['FAIclass'][0]);
250               /* Check if this Profile uses the source class ($cn)
251                */
252               if(preg_match("/".preg_quote($cn, '/')."/",$attrs['FAIclass'])){
253                 $attrs['FAIclass'] = preg_replace("/[ ]*".preg_quote($cn, '/')."[ ]*/i"," ",$attrs['FAIclass']);
254                 if(empty($attrs['FAIclass'])){
255                   $attrs['FAIclass'] = array();
256                 }
257                 $ldap->cd($dn['dn']);
258                 $ldap->modify($attrs);
260                 if (!$ldap->success()){
261                   msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, LDAP_MOD, get_class()));
262                 }
263               }
264             }
265           }
266         }
267       }
268     }
272     $FAI_objects_to_save = session::get('FAI_objects_to_save') ;
273     $FAI_objects_to_save[$Current_DN] =  $addObj;
274     session::set('FAI_objects_to_save',$FAI_objects_to_save);
275   }
278   /* Detect differences in attribute arrays  */
279   static function array_diff_FAI($ar1,$ar2)
280   {
282     if((!isset($ar1['description'])) || (isset($ar1['description']) && (count($ar1['description']) == 0))){
283       $ar1['description'] = "";
284     }
285     if((!isset($ar2['description'])) || (isset($ar2['description']) && (count($ar2['description']) == 0))){
286       $ar2['description'] = "";
287     }
289     if(count($ar1) != count($ar2)) {
290       return (true);
291     }
293     foreach($ar1 as $key1 => $val1){
295       if((is_array($val1)) && (count($val1)==1)){
296         $ar1[$key1] = $val1[0];
297       }
299       if(isset($ar2[$key1])&&  (is_array($ar2[$key1])) && (count($ar2[$key1])==1)){
300         $val1 = $val1[0];
301         $ar2[$key1] = $ar2[$key1][0];
302       }
303     }
304     ksort($ar1);
305     ksort($ar2);
306     if(count( array_diff($ar1,$ar2)) || FAI::arr_diff($ar1,$ar2)){
307       return(true);
308     }else{
309       return(false);
310     }
311   }
314   static function arr_diff($ar1,$ar2)
315   {
316     foreach($ar1 as $ak1 => $av1){
317       if(!isset($ar2[$ak1]) || (!($av1 === $ar2[$ak1]))){
318         return(TRUE);    
319       }elseif(is_array($av1)){
320         $ret = (FAI::arr_diff($av1,$ar2[$ak1]));
321         if($ret) {
322           return(TRUE);
323         }
324       }
325     }
326     return(FALSE);
327   }
332   /* check which objects must be saved, and save them */
333   static function save_release_changes_now()
334   {
335     global $config;
336     /* Variable init*/
337     $to_save = array();
338     
339     $reload_fai_classes = FALSE;
341     /* check which objects must be saved */
342     if(!session::is_set('FAI_objects_to_save')){
343       return;
344     }
345     $FAI_objects_to_save = session::get('FAI_objects_to_save');
346     if(!is_array($FAI_objects_to_save)) {
347       print_a(array(session::get('FAI_objects_to_save')));
348       trigger_error("Can't save FAI objects, no array given.");
349       return;
350     }
351   
352     foreach($FAI_objects_to_save as $Current_DN => $object){
353       if($object['diff']){
354         $sub_name = $Current_DN;
355         while(isset($FAI_objects_to_save[$sub_name])){
356           $to_save[strlen($sub_name)][$sub_name] = $FAI_objects_to_save[$sub_name]; 
357           unset($FAI_objects_to_save[$sub_name]);
358           $sub_name = preg_replace('/^[^,]+,/', '', $sub_name);
359         }
360       }
361     }
362     session::set('FAI_objects_to_save',$FAI_objects_to_save);
364     /* Sort list of objects that must be saved, and ensure that 
365        container   objects are safed, before their childs are saved */
366     ksort($to_save);
367     $tmp = array();
368     foreach($to_save as $SubObjects){
369       foreach($SubObjects as $object){
370         $tmp[] = $object;
371       }
372     }
373     $to_save = $tmp;
376     /* Save objects and manage the correct release behavior*/
377     foreach($to_save as $save){
379       $Current_DN = $save['Current_DN'];
380       $removed    = $save['removed'];
381       $objectAttrs= $save['objectAttrs'];
383       /* Get ldap object */ 
384       $ldap = $config->get_ldap_link();
385       $ldap->cd($config->current['BASE']);
387       /* Get some basic informations */
388       $base_release       = FAI::get_release_dn($Current_DN);
389       $sub_releases       = FAI::get_sub_releases_of_this_release($base_release,true);
390       $parent_obj         = FAI::get_parent_release_object($Current_DN);
391       $following_releases = $sub_releases;
393       /* Check if given dn exists or if is a new entry */
394       $ldap->cat($Current_DN);
395       if(!$ldap->count()){
396         $is_new = true;
397       }else{
398         $is_new = false;
399       }
401       /* if parameter removed is true, we have to add FAIstate to the current attrs 
402          FAIstate should end with ...|removed after this operation */  
403       if($removed ){
404         $ldap->cat($Current_DN);
406         /* Get current object, because we must add the FAIstate ...|removed */
407         if((!$ldap->count()) && !empty($parent_obj)){
408           $ldap->cat($parent_obj);
409         }
411         /* Check if we have found a suiteable object */ 
412         if(!$ldap->count()){
413           echo "Error can't remove this object ".$Current_DN;
414           return;
415         }else{
417           /* Set FAIstate to current objectAttrs */
418           $objectAttrs = FAI::                           prepare_ldap_fetch_to_be_saved($ldap->fetch());
419           if(isset($objectAttrs['FAIstate'][0])){
420             if(!preg_match("/removed$/",$objectAttrs['FAIstate'][0])){
421               $objectAttrs['FAIstate'][0] .= "|removed";
422             }
423           }else{
424             $objectAttrs['FAIstate'][0] = "|removed";
425           }
427           /* Force reload of FAI classes */
428           $classes = array("FAIprofile","FAIscript","FAIpackageList","FAIpartitionTable","FAIHook","FAIvariable","FAItemplate");
429           if(count(array_intersect($classes,$objectAttrs['objectClass']))){
430             $reload_fai_classes = TRUE;
431           }
432         }
433       }
435       /* Check if this a leaf release or not */ 
436       if(count($following_releases) == 0 ){
438         /* This is a leaf object. It isn't inherited by any other object */    
439         if(DEBUG_FAI_FUNC) { 
440           echo "<b>Saving directly, is a leaf object</b><br> ".$Current_DN;
441           print_a($objectAttrs);
442         }
443         FAI::save_FAI_object($Current_DN,$objectAttrs);
445         /* Force reload of FAI classes */
446         $classes = array("FAIprofile","FAIscript","FAIpackageList","FAIpartitionTable","FAIHook","FAIvariable","FAItemplate");
447         if(count(array_intersect($classes,$objectAttrs['objectClass']))){
448           $reload_fai_classes = TRUE;
449         }
451       }else{
453         /* This object is inherited by some sub releases */  
455         /* Get all releases, that inherit this object */ 
456         $r = FAI::get_following_releases_that_inherit_this_object($Current_DN);
458         /* Get parent object */
459         $ldap->cat($parent_obj);
460         $parent_attrs = FAI::prepare_ldap_fetch_to_be_saved($ldap->fetch());
462         /* New objects require special handling */
463         if($is_new){
465           /* Force reload of FAI classes */
466           $classes = array("FAIprofile","FAIscript","FAIpackageList","FAIpartitionTable","FAIHook","FAIvariable","FAItemplate");
467           if(count(array_intersect($classes,$objectAttrs['objectClass']))){
468             $reload_fai_classes = TRUE;
469           }
471           /* check if there is already an entry named like this,
472              in one of our parent releases */
473           if(!empty($parent_obj)){
474             if(DEBUG_FAI_FUNC) { 
475               echo "There is already an entry named like this.</b><br>";
477               echo "<b>Saving main object</b>".$Current_DN;
478               print_a($objectAttrs);
479             }    
480             FAI::save_FAI_object($Current_DN,$objectAttrs);
482             foreach($r as $key){
483               if(DEBUG_FAI_FUNC) { 
484                 echo "<b>Saving parent to following release</b> ".$key;
485                 print_a($parent_attrs);
486               }
487               FAI::save_FAI_object($key,$parent_attrs);
488             }
489           }else{
491             if(DEBUG_FAI_FUNC) { 
492               echo "<b>Saving main object</b>".$Current_DN;
493               print_a($objectAttrs);
494             }
495             FAI::save_FAI_object($Current_DN,$objectAttrs);
497             if(isset($objectAttrs['FAIstate'])){
498               $objectAttrs['FAIstate'] .= "|removed"; 
499             }else{
500               $objectAttrs['FAIstate'] = "|removed";
501             }
503             foreach($r as $key ){
504               /* Only save removed parent objects, not their children, unless
505                  they are a child of a copy-on-write parent in a subrelease */
506               if (FAI::is_parent_object($Current_DN) || FAI::is_child_of_cow_parent($key)){
507                 if(DEBUG_FAI_FUNC) { 
508                   echo "<b>Create an empty placeholder in follwing release</b> ".$key; 
509                   print_a($objectAttrs);
510                 }
511                 FAI::save_FAI_object($key,$objectAttrs);
512               }
513             }
514           }
515         }else{
517           /* check if we must patch the follwing release */
518           if(!empty($r)){
520             foreach($r as $key ){
521               /* Append FAIstate tag to ensure that freezed objects stay freezed
522                */ 
523               $rTag = FAI::get_release_tag(FAI::get_release_dn($key));
524               $parent_attrs['FAIstate'] = $rTag;
525               /* Don't copy over subobjects in subreleases if their parent is in "removed" state */
526               if(!FAI::parent_is_removed($key)){
527                 /* FAItemplateFile can be binary, therefore it needs to be fetched with
528                  * $ldap->get_attribute */
529                 if (isset($parent_attrs['FAItemplateFile'])) {
530                   $parent_attrs['FAItemplateFile'] = $ldap->get_attribute($parent_obj, 'FAItemplateFile');
531                 }
532                 if(DEBUG_FAI_FUNC) { 
533                   echo "<b>Copy current objects original attributes to next release</b> ".$key;
534                   print_a($parent_attrs);
535                 }
536                 FAI::save_FAI_object($key,$parent_attrs);
537               }
538             }
539           }
541           if(DEBUG_FAI_FUNC) { 
542             echo "<b>Saving current object</b>".$parent_obj;
543             print_a($objectAttrs);
544           }
545           FAI::save_FAI_object($parent_obj,$objectAttrs);
547           if(($parent_obj != $Current_DN)){
548             msg_dialog::display(_("Error"), sprintf(_("Error, following objects should be equal '%s' and '%s'"),$parent_obj,$Current_DN), ERROR_DIALOG);
549           }
550         }
551       }
552     }
554     /* Reload GOsa si FAI DB/cache
555      */
556     if($reload_fai_classes){
557       if( class_available("DaemonEvent") && class_available("gosaSupportDaemon")){
558         $events = DaemonEvent::get_event_types(SYSTEM_EVENT | HIDDEN_EVENT);        
559         if(isset($events['TRIGGERED']['DaemonEvent_recreate_fai_release_db'])){
560           $evt = $events['TRIGGERED']['DaemonEvent_recreate_fai_release_db']; 
561           $tmp = new $evt['CLASS_NAME']($config);
562           $tmp->set_type(TRIGGERED_EVENT);
563           $tmp->add_targets(array("GOSA"));
564           $o_queue = new gosaSupportDaemon();
565           if(!$o_queue->append($tmp)){
566             msg_dialog::display(_("Service infrastructure"),msgPool::siError($o_queue->get_error()),ERROR_DIALOG);
567           }
568         }
569       }
570     }
572     session::set('FAI_objects_to_save',array());
573   }
576   /* this function will remove all unused (deleted) objects,
577      that have no parent object. If $recursive is set to true,
578      also check sub releases. */
579   static function clean_up_releases($Current_DN, $recursive=true)
580   {
581     global $config;
582     $ldap = $config->get_ldap_link();
583     $ldap->cd($config->current['BASE']);
585     /* Collect some basic informations and initialize some variables */ 
586     $base_release       = FAI::get_release_dn($Current_DN);
587     $previous_releases  = array_reverse(FAI::             get_previous_releases_of_this_release($base_release,true));
588     $sub_releases       = array_keys(FAI::get_sub_releases_of_this_release($base_release,false));
589     $Kill = array();
590     $Skip = array();
592     /* We must also include the given release dn */
593     $previous_releases[] = $base_release;
595     $all_releases = $previous_releases;
597     if ($recursive) {
598       /* Merge parent, current and child releases into one big release to 
599          iterate over */
600       foreach($sub_releases as $sub_release){
601         $all_releases[] = $sub_release;
602       }
603     }
605     /* Walk through all releases */
606     foreach($all_releases as $release){
608       /* Get fai departments */
609       $deps_to_search = FAI::get_FAI_departments($release); 
611       /* For every single department  (ou=hoos,ou ..) */
612       foreach($deps_to_search as $fai_base){
614         /* Ldap search for fai classes specified in this release */
615         $ldap->cd($fai_base);
616         $ldap->search("(objectClass=FAIclass)",array("dn","objectClass","FAIstate"));
618         /* check the returned objects, and add/replace them in our return variable */
619         while($attr = $ldap->fetch()){
621           $buffer = array();
622 #        $name = str_ireplace($release,"",$attr['dn']);
623           $name = preg_replace("/".preg_quote($release, '/')."/i","",$attr['dn']);
625           if(isset($attr['FAIstate'][0])&&(preg_match("/removed$/",$attr['FAIstate'][0]))){
627             /* Check if this object is required somehow */    
628             if(!isset($Skip[$name])){
629               $Kill[$attr['dn']] = $attr['dn'];
630             }
631           }else{
633             /* This object is required (not removed), so do not 
634                delete any following sub releases of this object */
635             $Skip[$name] = $attr['dn'];
636           }
637         }
638       }
639     }
640     return($Kill);
641   }
644   /* Remove numeric index and 'count' from ldap->fetch result */
645   static function prepare_ldap_fetch_to_be_saved($attrs)
646   {
647     foreach($attrs as $key => $value){
648       if(is_numeric($key) || ($key == "count") || ($key == "dn")){
649         unset($attrs[$key]);
650       }
651       if(is_array($value) && isset($value['count'])){
652         unset($attrs[$key]['count']);
653       }
654     }
655     return($attrs);
656   }
659   /* Save given attrs to specified dn*/
660   static function save_FAI_object($dn,$attrs)
661   {
662     global $config;
663     $ldap = $config->get_ldap_link();
664     $ldap->cd($config->current['BASE']);
665     $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dn));
666     $ldap->cd($dn);
668     $ui= get_userinfo();
669     FAI::tag_attrs($attrs, $dn, $ui->gosaUnitTag);
671     $ldap->cat($dn,array("dn"));
672     if($ldap->count()){
674       /* Remove FAIstate*/
675       if(!isset($attrs['FAIstate'])){
676         $attrs['FAIstate'] = array();
677       }
679       $ldap->modify($attrs);
680     }else{
682       /* Unset description if empty  */
683       if(empty($attrs['description'])){
684         unset($attrs['description']);
685       }    
687       $ldap->add($attrs);
688     }
689     if (!$ldap->success()){
690       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, 0, get_class()));
691     }
692   }
695   /* Return FAIstate freeze branch or "" for specified release department */
696   static function get_release_tag($dn)
697   {
698     global $config;
699     $ldap = $config->get_ldap_link();
700     $ldap->cd($dn);
701     $ldap->cat($dn,array("FAIstate"));
703     if($ldap->count()){
705       $attr = $ldap->fetch();
706       if(isset($attr['FAIstate'][0])){
707         if(preg_match("/freeze/",$attr['FAIstate'][0])){
708           return("freeze");
709         }elseif(preg_match("/branch/",$attr['FAIstate'][0])){
710           return("branch");
711         }
712       }
713     }
714     return("");
715   }
718   static function get_following_releases_that_inherit_this_object($dn)
719   {
720     global $config;
721     $ldap = $config->get_ldap_link();
722     $ldap->cd($config->current['BASE']);
724     $ret = array();
726     /* Get base release */
727     $base_release = FAI::get_release_dn($dn);
729     /* Get previous release dns */
730     $sub_releases = FAI::                       get_sub_releases_of_this_release($base_release);
732     /* Get dn suffix. Example  "FAIvairableEntry=keksdose,FAIvariable=Keksregal," */
733 #  $dn_suffix = str_ireplace($base_release,"",$dn);
734     $dn_suffix = preg_replace("/".preg_quote($base_release, '/')."/i","",$dn);
736     /* Check if given object also exists whitin one of these releases */
737     foreach($sub_releases as $p_release => $name){
739       $check_dn = $dn_suffix.$p_release;
741       $ldap->cat($check_dn,array("dn","objectClass"));
743       if($ldap->count()){
744         //return($ret);
745       }else{
746         $ret[$check_dn]=$check_dn;
747       }
748     }
749     return($ret);
750   }
753   /* Get previous version of the object dn */
754   static function get_parent_release_object($dn,$include_myself=true)
755   {
756     global $config;
757     $ldap = $config->get_ldap_link();
758     $ldap->cd($config->current['BASE']);
759     $previous_releases= array();
761     /* Get base release */
762     $base_release = FAI::get_release_dn($dn);
763     if($include_myself){
764       $previous_releases[] = $base_release;  
765     }
767     /* Get previous release dns */
768     $tmp = FAI::             get_previous_releases_of_this_release($base_release,true);
769     foreach($tmp as $release){
770       $previous_releases[] = $release;
771     }
773     /* Get dn suffix. Example  "FAIvairableEntry=keksdose,FAIvariable=Keksregal," */
774 #  $dn_suffix = str_ireplace($base_release,"",$dn);
775     $dn_suffix = preg_replace("/".preg_quote($base_release, '/')."/i","",$dn);
777     /* Check if given object also exists whitin one of these releases */
778     foreach($previous_releases as $p_release){
779       $check_dn = $dn_suffix.$p_release;
780       $ldap->cat($check_dn,array("dn","objectClass"));
782       if($ldap->count()){
783         return($check_dn);
784       }
785     }
786     return("");
787   }
790   /* return release names of all parent releases */
791   static function get_previous_releases_of_this_release($dn,$flat)
792   {
793     global $config;
794     $ldap = $config->get_ldap_link();
795     $ldap->cd($config->current['BASE']);
796     $ret = array();
798     /* Explode dns into pieces, to be able to build parent dns */
799     $dns_to_check = gosa_ldap_explode_dn(preg_replace("/".preg_quote(",".$config->current['BASE'], '/')."/i","",$dn));
801     if(!is_array($dns_to_check)){
802       return;  
803     }
805     /* Unset first entry which represents the given dn */
806     unset($dns_to_check['count']); 
807     unset($dns_to_check[key($dns_to_check)]);
809     /* Create dns addresses and check if this dn is a release dn */
810     $id = 0;
811     while(count($dns_to_check)){
813       /* build parent dn */
814       $new_dn = "";
815       foreach($dns_to_check as $part){
816         $new_dn .= $part.",";
817       }
818       $new_dn .= $config->current['BASE'];
820       /* check if this dn is a release */
821       if(FAI::is_release_department($new_dn)){
822         if($flat){
823           $ret[$id] = $new_dn; 
824         }else{
825           $ret = array($new_dn=>$ret); 
826         }
827         $id ++;
828       }else{
829         return($ret);
830       }
831       reset($dns_to_check);
832       unset($dns_to_check[key($dns_to_check)]);
833     }
834     return($ret);
835   } 
838   /* This function returns all sub release names, recursivly  */
839   static function get_sub_releases_of_this_release($dn,$flat = false)
840   {
841     global $config;
842     $res  = array();
843     $ldap = $config->get_ldap_link();
844     $ldap->cd($config->current['BASE']);
845     $ldap->ls("(objectClass=FAIbranch)",$dn,array("objectClass","dn","ou"));
846     while($attr = $ldap->fetch()){
848       /* Append department name */
849       if($flat){
850         $res[$attr['dn']] = $attr['ou'][0];
851       }else{
852         $res[$attr['dn']] = array();
853       }
855       /* Get sub release departments of this department */
856       if(in_array("FAIbranch",$attr['objectClass'])) {
857         if($flat){
858           $tmp = FAI::                       get_sub_releases_of_this_release($attr['dn'],$flat);
859           foreach($tmp as $dn => $value){
860             $res[$dn]=$value;
861           }
862         }else{
863           $res[$attr['dn']] = FAI::                       get_sub_releases_of_this_release($attr['dn']);
864         }
865       }
866     }
867     return($res);
868   }
871   /* Check if the given department is a release department */
872   static function is_release_department($dn)
873   {
874     global $config;
875     $ldap = $config->get_ldap_link();
876     $ldap->cd($config->current['BASE']);
877     $ldap->cat($dn,array("objectClass","ou"));
879     /* Check objectClasses and name to check if this is a release department */
880     if($ldap->count()){
881       $attrs = $ldap->fetch();
883       $ou = "";
884       if(isset($attrs['ou'][0])){
885         $ou = $attrs['ou'][0];  
886       }
888       if((in_array("FAIbranch",$attrs['objectClass'])) || ($ou == "fai")){
889         return($attrs['dn']);
890       }
891     }
892     return(false);
893   }
896   static function copy_FAI_group_releases($source_release , $destination_name, $type ="" )
897   {
898     global $config;
899     $start = microtime(TRUE);
900     $source_release = trim($source_release,"/");
902     echo "<h2>".sprintf(_("Creating group application release for %s"),$destination_name)."</h2>";
904     $sub_releases = array();
905     $source_dn = "";
906     
907     $tmp = split("\/",$source_release);
908     foreach($tmp as $part){
909       if(empty($part)){
910         continue;
911       }
912       $source_dn            = "ou=".$part.",".$source_dn;
913       $sub_releases[$part]  = $source_dn;
914     }
916     /* Get all groups */
917     $ldap =$config->get_ldap_link();
918     $ldap->cd($config->current['BASE']);
919     $ldap->search("(objectClass=posixGroup)",array("dn"));
920     $groups = array();
921     while($attrs = $ldap->fetch()){
922       $groups[$attrs['dn']] = $attrs;
923     }
925     /* Get all FAI releases, to be able to create missing group application releases 
926         with the correct type of release (FAIstate=freeze/branch).
927      */
928     $f_releases = array();
929     $ldap->cd ($config->current['BASE']);
930     $ldap->search("(objectClass=FAIbranch)",array("ou","FAIstate"));
931     while($attrs = $ldap->fetch()){
932       foreach($sub_releases as $sub_rel){
933         if(preg_match("/^".preg_quote($sub_rel.get_ou('faiBaseRDN'), '/')."/",$attrs['dn'])){
934           $f_releases[$sub_rel.get_ou('faiBaseRDN')] = $attrs;
935         }
936       }
937     }
939     /* Get all group releases */
940     $g_releases = array();
941     foreach($groups as $dn => $data){
942       $ldap->cd($dn);
943       $ldap->search("(objectClass=FAIbranch)",array("ou","FAIstate"));
944       while($attrs = $ldap->fetch()){
945         $g_releases[$attrs['dn']] = $attrs;
946       }
947     }
949     /* Check if base releases exists.
950        If they do not exist, create them and adapt FAIstate attribute from FAI releases. 
951      */
952     foreach($sub_releases as $name => $sub_rel){
954       $FAIstate = "";
955       if(isset($f_releases[$sub_rel.get_ou('faiBaseRDN')]) && isset($f_releases[$sub_rel.get_ou('faiBaseRDN')]['FAIstate'])){
956         $FAIstate = $f_releases[$sub_rel.get_ou('faiBaseRDN')]['FAIstate'][0];
957       }
959       foreach($groups as $dn => $data){
960         if(!isset($g_releases[$sub_rel.$dn])){
961           $ldap->cd($dn);
962           $r_data = array();
963           $r_data['ou'] = $name;
964           $r_data['objectClass'] = array("top","organizationalUnit","FAIbranch");
965           if(!empty($FAIstate)) {
966             $r_data['FAIstate'] = $FAIstate;
967           }
968  
969           $ldap->cd($sub_rel.$dn) ;
970           $ldap->add($r_data);
971           echo "&nbsp;<b>"._("Object").":</b> ";
972           echo sprintf(_("Adding missing group application release container %s."),substr(LDAP::fix($sub_rel.$dn),0,96))."<br>";
973           flush();
974         }
975       }
976     } 
977  
978     /* Create new release container in each group.
979      */
980     $n_data = array();
981     $n_data = array();
982     $n_data['ou'] = $destination_name;
983     $n_data['objectClass'] = array("top","organizationalUnit","FAIbranch");
984     if(!empty($type)){
985       $n_data['FAIstate'] = $type."/cow";
986     }
988     foreach($groups as $dn => $att){
989       $n_dn = "ou=".$destination_name.",".$source_dn.$dn;
990       if(!isset($g_releases[$n_dn])){
991         $ldap->cd ($n_dn);
992         $ldap->add($n_data);
993         echo "&nbsp;<b>"._("Object").":</b> ";
994         echo sprintf(_("Adding group application release container %s."),substr(LDAP::fix($n_dn),0,96))."<br>";
995         flush();
996       }
997     }
999     /* If the source release is empty, then create a new release by copying 
1000         all group application menus into a new ou=$destination_name release container.
1001       
1002        If the source release is not empty. 
1003          We detect all releases which match the source release dn and copy the contents.
1004      */
1005     if(empty($source_release)){
1006       $source_dns = $groups;
1007     }else{
1008       $source_dns = array();
1009       foreach($g_releases as $dn => $data){
1010         if(preg_match("/^".preg_quote($source_dn, '/')."/",$dn)){
1011           $source_dns[$dn] = $data; 
1012         }
1013       }
1014     }
1016     /* Detect all menu object we have to copy 
1017      */
1018     $to_copy = array();
1019     foreach($source_dns as $dn => $attrs){
1020       $ldap->cd($dn);
1021       $ldap->ls("(|(objectClass=gotoSubmenuEntry)(objectClass=gotoMenuEntry))",$dn,array("dn"));
1022       while($attrs = $ldap->fetch()){
1023         $destination = preg_replace("/".preg_quote($dn, '/')."$/","ou=".$destination_name.",".$dn,$attrs['dn']);
1024         $to_copy[$attrs['dn']] = $destination;
1025       }
1026     }
1027    
1028     /* At least create the menu objects object */
1029     $plug = new plugin($config);
1030     foreach($to_copy as $source => $destination){
1031       $ldap->cat($destination);
1032       if($ldap->count()){
1033         echo "&nbsp;<b>"._("Object").":</b> ";
1034         echo sprintf(_("Could not create menu entry %s. (Already exists)."),substr(LDAP::fix($destination),0,96))."<br>";
1035         flush();
1036       }else{
1037         $plug->copy($source,$destination);
1038         echo "&nbsp;<b>"._("Object").":</b> ";
1039         echo sprintf(_("Created group application menu entry for %s."),substr(LDAP::fix($destination),0,96))."<br>";
1040         flush();
1041       }
1042     }
1043   }
1046   /*! \brief Create a new FAI branch.
1047    *  @param $sourcedn          String  The source release dn
1048    *  @param $destinationdn     String  The destination dn
1049    *  @param $destinationName   String  The name of the new release
1050    *  @param $type              String  The release type (freeze/branch)
1051    *  @param $is_first          Boolean Use to identify the first func. call when recursivly called.
1052    *  @param $depth             Integer Current depth of recursion.
1053    */
1054   static function copy_FAI_resource_recursive($sourcedn,$destinationdn,$destinationName,$type="branch",$is_first = true,$depth=0)
1055   {
1056     global $config;
1057     error_reporting(E_ALL | E_STRICT);
1058     $ldap     = $config->get_ldap_link();
1059     $basedn   = $config->current['BASE'];
1060     $delarray = array();
1062     /* The following code will output a status string
1063      *  for each handled object, in a seperate iframe.
1064      */
1067     /* Display current action information.
1068      */
1069     if($is_first){
1070       echo "<h2>".sprintf(_("Creating copy of %s"),"<i>".LDAP::fix($sourcedn)."</i>")."</h2>";
1071     }else{
1072       if(preg_match("/^ou=/",$sourcedn)){
1073         echo "<h3>"._("Processing")." <i>".LDAP::fix($destinationdn)."</i></h3>";
1074       }else{
1075         $tmp = split(",",$sourcedn);
1076         echo "&nbsp;<b>"._("Object").":</b> ";
1077         $deststr = LDAP::fix($destinationdn);
1078         if(strlen($deststr) > 96){
1079           $deststr = substr($deststr,0,96)."...";
1080         }
1081         echo $deststr."<br>";
1082       }
1083     }
1084     /* .. immediately display infos */
1085     flush();
1087     /* Check if destination entry already exists
1088      */
1089     $ldap->cat($destinationdn);
1090     if($ldap->count()){
1091       echo _("Could not create new release, the destination dn is already in use.");
1092       return;
1093     }else{
1095       $ldap->clearResult();
1097       /* Get source entry
1098        *  if it does not exist, abort here.
1099        */
1100       $ldap->cd($basedn);
1101       $ldap->cat($sourcedn);
1102       $attr = $ldap->fetch();
1103       if((!$attr) || (count($attr)) ==0) {
1104         echo _("Error while fetching source dn - aborted!");
1105         return;
1106       }
1108       /* The current object we want to create is an department.
1109        * Create the department and add the FAIbranch tag.
1110        */
1111       if(in_array("organizationalUnit",$attr['objectClass'])){
1112         $attr['dn'] = LDAP::convert($destinationdn);
1113         $ldap->cd($basedn);
1114         $ldap->create_missing_trees($destinationdn);
1115         $ldap->cd($destinationdn);
1117         /* If is first entry, append FAIbranch to department entry */
1118         if($is_first){
1119           $ldap->cat($destinationdn);
1120           $attr= $ldap->fetch();
1121           /* Filter unneeded informations */
1122           foreach($attr as $key => $value){
1123             if(is_numeric($key)) unset($attr[$key]);
1124             if(isset($attr[$key]['count'])){
1125               if(is_array($attr[$key])){
1126                 unset($attr[$key]['count']);
1127               }
1128             }
1129           }
1131           unset($attr['count']);
1132           unset($attr['dn']);
1134           /* Add marking attribute */
1135           $attr['objectClass'][] = "FAIbranch";
1136           $attr['FAIstate'] = $type;
1138           /* Add this entry */
1139           $ldap->modify($attr);
1140         }
1141       }else{
1143         /* Replicate all relevant FAI objects here.
1144          * FAI objects, Apps and Mimetypes.
1145          * Get all attributes as binary value, to ensure that Icon, File template aso
1146          *  are created correctly.
1147          */
1148         foreach($attr as $key => $value){
1150           if(in_array($key ,array("gotoLogonScript", "gosaApplicationIcon","gotoMimeIcon"))){
1151             $sr= ldap_read($ldap->cid, LDAP::fix($sourcedn), "$key=*", array($key));
1152             $ei= ldap_first_entry($ldap->cid, $sr);
1153             if ($tmp= @ldap_get_values_len($ldap->cid, $ei,$key)){
1154               $attr[$key] = $tmp;
1155             }
1156           }
1158           if(is_numeric($key)) unset($attr[$key]);
1159           if(isset($attr[$key]['count'])){
1160             if(is_array($attr[$key])){
1161               unset($attr[$key]['count']);
1162             }
1163           }
1164         }
1165         unset($attr['count']);
1166         unset($attr['dn']);
1167         if(!in_array("FAIobject",$attr['objectClass'])){
1168           $attr['objectClass'][] = "FAIobject";
1169         }
1170         $attr['FAIstate'] = $type;
1172         /* Add entry
1173          */
1174         $ldap->cd($destinationdn);
1175         $ldap->cat($destinationdn);
1177         $a = $ldap->fetch();
1178         if(!count($a)){
1179           $ldap->add($attr);
1180         }
1182         if(!$ldap->success()){
1184           /* Some error occurred */
1185           msg_dialog::display(_("Fatal error"),
1186               sprintf(_("Release creation failed due to ldap errors. Additional informations '%s'."),
1187                 $ldap->get_error()."<br>".$sourcedn."<br>".$destinationdn."<br>"),FATAL_ERROR_DIALOG);
1188           exit();
1189         }
1190       }
1191     }
1193     echo "<script language=\"javascript\" type=\"text/javascript\">scrollDown2();</script>" ;
1195     /* Prepare for recursive copy.
1196      * Get all object within the source dn and
1197      *  call the recursive copy for each.
1198      */
1199     $ldap->ls ("(objectClass=*)",$sourcedn);
1200     while ($ldap->fetch()){
1201       $deldn= $ldap->getDN();
1202       $delarray[$deldn]= strlen($deldn);
1203     }
1204     asort ($delarray);
1205     reset ($delarray);
1206     $depth ++;
1207     foreach($delarray as $dn => $bla){
1208       if($dn != $destinationdn){
1209         $ldap->cd($basedn);
1210         $item = $ldap->fetch($ldap->cat($dn));
1211         if(!in_array("FAIbranch",$item['objectClass'])){
1212           FAI::copy_FAI_resource_recursive($dn,str_replace($sourcedn,$destinationdn,$dn),$destinationName,$type,false,$depth);
1213         }
1214       }
1215     }
1216     if($is_first){
1217       echo "<p class='seperator'>&nbsp;</p>";
1218     }
1219   }
1223   /* This function returns the dn of the object release */
1224   static function get_release_dn($Current_DN)
1225   {
1226     global $config;
1227     $ldap = $config->get_ldap_link();
1228     $ldap->cd($config->current['BASE']);
1230     /* Split dn into pices */ 
1231     $dns_to_check = gosa_ldap_explode_dn(preg_replace("/".preg_quote(",".$config->current['BASE'], '/')."/i","",$Current_DN));
1233     if(!is_array($dns_to_check)){
1234       return;  
1235     }
1237     /* Use dn pieces, to create sub dns like 
1238        ou=test,ou=1,ou=0...
1239        ou=1,ou=0...
1240        ou=0... 
1241        To check which dn is our release container.
1242      */
1243     unset($dns_to_check['count']); 
1244     while(count($dns_to_check)){
1246       /* Create dn */
1247       $new_dn = "";
1248       foreach($dns_to_check as $part){
1249         $new_dn .= $part.",";
1250       }
1251       $new_dn .= $config->current['BASE'];
1253       /* Check if this dn is a release dn */
1254       if(FAI::is_release_department($new_dn)){
1255         return($new_dn);
1256       }
1258       /* Remove first element of dn pieces */
1259       reset($dns_to_check);
1260       unset($dns_to_check[key($dns_to_check)]);
1261     }
1262     return("");
1263   }
1266   static function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1267   {                                                        
1268     /* Remove tags that may already be here... */                         
1269     remove_objectClass("gosaAdministrativeUnitTag", $at);                 
1270     if (isset($at['gosaUnitTag'])){                                       
1271         unset($at['gosaUnitTag']);                                        
1272     }                                                                     
1274     /* Set tag? */
1275     if ($tag != ""){
1276       add_objectClass("gosaAdministrativeUnitTag", $at);
1277       $at['gosaUnitTag']= $tag;                         
1278     }                                                   
1280     /* Initially this object was tagged. 
1281        - But now, it is no longer inside a tagged department. 
1282        So force the remove of the tag.                        
1283        (objectClass was already removed obove)                
1284      */                                                       
1285     if($tag == ""){                     
1286       $at['gosaUnitTag'] = array();                           
1287     }                                                         
1288   }                                                           
1290   /* Returns true if this is a parent object, e.g. FAIpartitionTable, not FAIpartitionDisk */
1291   static function is_parent_object($dn)
1292   {
1293     global $config;
1294     $Current_DN = $dn;
1296     /* special case partitions and debconf variables, as they don't start with cn= in their DN */
1297     if (preg_match('/^(FAIpartitionNr|FAIvariable)=/', $Current_DN)){
1298       return false;
1299     }
1301     $parent_dn = preg_replace('/^cn=[^,.]*,/', '', $Current_DN);
1302     if (preg_match('/^cn=/', $parent_dn)) {
1303       $ldap = $config->get_ldap_link();
1304       $ldap->cd($config->current['BASE']);
1305       $ldap->cat($parent_dn);
1306       if($ldap->count()){
1307         return false;
1308       }
1309     }else{
1310       return true;
1311     }
1312   }
1314   /* Return child objects, if there are any */
1315   static function get_child_objects($dn)
1316   {
1317     global $config;
1318     $Current_DN = $dn;
1319     $children= array();
1321     if (FAI::is_parent_object($Current_DN)){
1322       $ldap = $config->get_ldap_link();
1323       $ldap->cd($dn);
1324       $ldap->search("(objectClass=FAIclass)",array("dn"));
1325       while($attrs = $ldap->fetch()){
1326         if(preg_match("/".preg_quote($Current_DN, '/')."/",$attrs['dn']) && $attrs['dn'] != $Current_DN){
1327           $children[] = $attrs['dn'];
1328         }
1329       }
1330     }
1331     return $children;
1332   }
1334   /* Get the DN of the parent object; e.g. the FAIpartitionTable of a FAIpartitionDisk.
1335      If $check is false, do not check whether the parent actually exists in LDAP */
1336   static function get_parent_object($dn, $check=true)
1337   {
1338     global $config;
1339     $Current_DN = $dn;
1340     $i = 0;
1342     $tmp_dn = $Current_DN;
1343     $parent_dn = array();
1345     /* special case partitions and debconf variables, as they don't start with cn= in their DN */
1346     $tmp_dn = preg_replace('/^(FAIpartitionNr|FAIvariable)=/', 'cn=', $tmp_dn);
1348     $tmp_dn = gosa_ldap_explode_dn($tmp_dn);
1349     while(preg_match('/^cn=/', $tmp_dn[$i])) {
1350       $i++;
1351     }
1352     /* DN part does not start with cn= anymore, remove one from the counter to get the
1353        last CN element */
1354     $i--;
1356     for ($i;$i<$tmp_dn['count'];$i++) {
1357       $parent_dn[] = trim($tmp_dn[$i]);
1358     }
1359     $parent_dn = join($parent_dn, ',');
1361     if ($parent_dn != $Current_DN){
1362       if($check){
1363         $ldap = $config->get_ldap_link();
1364         $ldap->cd($config->current['BASE']);
1365         $ldap->cat($parent_dn);
1366         if(!$ldap->count()){
1367           return "";
1368         }
1369       }
1370       return $parent_dn;
1371     }
1372     return "";
1373   }
1375   /* Check whether parent object is removed */
1376   static function parent_is_removed($dn)
1377   {
1378     global $config;
1379     $Current_DN = $dn;
1381     $ldap = $config->get_ldap_link();
1382     $parent_dn = FAI::get_parent_object($Current_DN);
1383     if ($parent_dn) { 
1384       $ldap->cd($config->current['BASE']);
1385       $ldap->cat($parent_dn);
1386       $parentObjectAttrs = FAI::prepare_ldap_fetch_to_be_saved($ldap->fetch());
1387       if(isset($parentObjectAttrs['FAIstate'][0])){
1388         if(preg_match("/removed$/",$parentObjectAttrs['FAIstate'][0])){
1389           return true;
1390         }
1391       }
1392     } 
1393     return false;
1394   }
1396   /* Check whether this is a child object of a Copy-on-Write parent */
1397   static function is_child_of_cow_parent($dn)
1398   { 
1399     global $config;
1400     $Current_DN = $dn;
1401     $parent_dn = "";
1403     if (!FAI::is_parent_object($Current_DN)){
1405       /* This is a child object; check that the parent object is not 
1406          marked as removed */
1407       $cow_parent_dn = FAI::get_parent_object($Current_DN, false);
1408       if ($cow_parent_dn && !FAI::parent_is_removed($Current_DN)){
1410         /* This is a child object, check whether the parent object in 
1411            the main release exists */
1412         $main_parent_dn = FAI::get_parent_release_object($cow_parent_dn);
1413         $ldap = $config->get_ldap_link();
1414         $ldap->cd($config->current['BASE']);
1415         $ldap->cat($main_parent_dn);
1416         if($ldap->count()){
1417           return true;
1418         }
1419       }
1420     }
1421     return false;
1422   }
1424   static function get_leaf_objects($dn, $cn, $subclass, $rdn) {
1425     $valid_releases = FAI::get_previous_releases_of_this_release(FAI::get_release_dn($dn), true);
1426     /* Remove the last release DN */
1427     array_pop($valid_releases);
1428     $valid_releases[] = FAI::get_release_dn($dn);
1430     $objects = FAI::get_all_objects_for_given_base($dn,"(&(objectClass=FAIclass)(objectClass=".$subclass."))");
1431     $res = array();
1432     /* Strip elements which are not a leaf object of the current dn */
1433     foreach($objects as $obj){
1434       $keep = FALSE;
1435       foreach($valid_releases as $valid_release) {
1436         if (preg_match("/cn=".$cn.",".$rdn.$valid_release."$/", $obj['dn'])) {
1437           $keep = TRUE;
1438           break;
1439         }
1440       }
1442       if ($keep) {
1443         $res[] = $obj;
1444       }
1445     }
1447     return $res;
1448   }
1450     
1456 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1457 ?>