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 $classlist = split(" ", $attrs['FAIclass']);
253 $new_classlist = "";
254 foreach($classlist as $class){
255 if($class != $cn){
256 $new_classlist = $new_classlist." ".$class;
257 }
258 }
259 $attrs['FAIclass'] = $new_classlist;
260 if(empty($attrs['FAIclass'])){
261 $attrs['FAIclass'] = array();
262 }
263 $ldap->cd($dn['dn']);
264 $ldap->modify($attrs);
266 if (!$ldap->success()){
267 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, LDAP_MOD, get_class()));
268 }
269 }
270 }
271 }
272 }
273 }
277 $FAI_objects_to_save = session::get('FAI_objects_to_save') ;
278 $FAI_objects_to_save[$Current_DN] = $addObj;
279 session::set('FAI_objects_to_save',$FAI_objects_to_save);
280 }
283 /* Detect differences in attribute arrays */
284 static function array_diff_FAI($ar1,$ar2)
285 {
287 if((!isset($ar1['description'])) || (isset($ar1['description']) && (count($ar1['description']) == 0))){
288 $ar1['description'] = "";
289 }
290 if((!isset($ar2['description'])) || (isset($ar2['description']) && (count($ar2['description']) == 0))){
291 $ar2['description'] = "";
292 }
294 if(count($ar1) != count($ar2)) {
295 return (true);
296 }
298 foreach($ar1 as $key1 => $val1){
300 if((is_array($val1)) && (count($val1)==1)){
301 $ar1[$key1] = $val1[0];
302 }
304 if(isset($ar2[$key1])&& (is_array($ar2[$key1])) && (count($ar2[$key1])==1)){
305 $val1 = $val1[0];
306 $ar2[$key1] = $ar2[$key1][0];
307 }
308 }
309 ksort($ar1);
310 ksort($ar2);
311 if(count( array_diff($ar1,$ar2)) || FAI::arr_diff($ar1,$ar2)){
312 return(true);
313 }else{
314 return(false);
315 }
316 }
319 static function arr_diff($ar1,$ar2)
320 {
321 foreach($ar1 as $ak1 => $av1){
322 if(!isset($ar2[$ak1]) || (!($av1 === $ar2[$ak1]))){
323 return(TRUE);
324 }elseif(is_array($av1)){
325 $ret = (FAI::arr_diff($av1,$ar2[$ak1]));
326 if($ret) {
327 return(TRUE);
328 }
329 }
330 }
331 return(FALSE);
332 }
337 /* check which objects must be saved, and save them */
338 static function save_release_changes_now()
339 {
340 global $config;
341 /* Variable init*/
342 $to_save = array();
344 $reload_fai_classes = FALSE;
346 /* check which objects must be saved */
347 if(!session::is_set('FAI_objects_to_save')){
348 return;
349 }
350 $FAI_objects_to_save = session::get('FAI_objects_to_save');
351 if(!is_array($FAI_objects_to_save)) {
352 print_a(array(session::get('FAI_objects_to_save')));
353 trigger_error("Can't save FAI objects, no array given.");
354 return;
355 }
357 foreach($FAI_objects_to_save as $Current_DN => $object){
358 if($object['diff']){
359 $sub_name = $Current_DN;
360 while(isset($FAI_objects_to_save[$sub_name])){
361 $to_save[strlen($sub_name)][$sub_name] = $FAI_objects_to_save[$sub_name];
362 unset($FAI_objects_to_save[$sub_name]);
363 $sub_name = preg_replace('/^[^,]+,/', '', $sub_name);
364 }
365 }
366 }
367 session::set('FAI_objects_to_save',$FAI_objects_to_save);
369 /* Sort list of objects that must be saved, and ensure that
370 container objects are safed, before their childs are saved */
371 ksort($to_save);
372 $tmp = array();
373 foreach($to_save as $SubObjects){
374 foreach($SubObjects as $object){
375 $tmp[] = $object;
376 }
377 }
378 $to_save = $tmp;
381 /* Save objects and manage the correct release behavior*/
382 foreach($to_save as $save){
384 $Current_DN = $save['Current_DN'];
385 $removed = $save['removed'];
386 $objectAttrs= $save['objectAttrs'];
388 /* Get ldap object */
389 $ldap = $config->get_ldap_link();
390 $ldap->cd($config->current['BASE']);
392 /* Get some basic informations */
393 $base_release = FAI::get_release_dn($Current_DN);
394 $sub_releases = FAI::get_sub_releases_of_this_release($base_release,true);
395 $parent_obj = FAI::get_parent_release_object($Current_DN);
396 $following_releases = $sub_releases;
398 /* Check if given dn exists or if is a new entry */
399 $ldap->cat($Current_DN);
400 if(!$ldap->count()){
401 $is_new = true;
402 }else{
403 $is_new = false;
404 }
406 /* if parameter removed is true, we have to add FAIstate to the current attrs
407 FAIstate should end with ...|removed after this operation */
408 if($removed ){
409 $ldap->cat($Current_DN);
411 /* Get current object, because we must add the FAIstate ...|removed */
412 if((!$ldap->count()) && !empty($parent_obj)){
413 $ldap->cat($parent_obj);
414 }
416 /* Check if we have found a suiteable object */
417 if(!$ldap->count()){
418 echo "Error can't remove this object ".$Current_DN;
419 return;
420 }else{
422 /* Set FAIstate to current objectAttrs */
423 $objectAttrs = FAI:: prepare_ldap_fetch_to_be_saved($ldap->fetch());
424 if(isset($objectAttrs['FAIstate'][0])){
425 if(!preg_match("/removed$/",$objectAttrs['FAIstate'][0])){
426 $objectAttrs['FAIstate'][0] .= "|removed";
427 }
428 }else{
429 $objectAttrs['FAIstate'][0] = "|removed";
430 }
432 /* Force reload of FAI classes */
433 $classes = array("FAIprofile","FAIscript","FAIpackageList","FAIpartitionTable","FAIHook","FAIvariable","FAItemplate");
434 if(count(array_intersect($classes,$objectAttrs['objectClass']))){
435 $reload_fai_classes = TRUE;
436 }
437 }
438 }
440 /* Check if this a leaf release or not */
441 if(count($following_releases) == 0 ){
443 /* This is a leaf object. It isn't inherited by any other object */
444 if(DEBUG_FAI_FUNC) {
445 echo "<b>Saving directly, is a leaf object</b><br> ".$Current_DN;
446 print_a($objectAttrs);
447 }
448 FAI::save_FAI_object($Current_DN,$objectAttrs);
450 /* Force reload of FAI classes */
451 $classes = array("FAIprofile","FAIscript","FAIpackageList","FAIpartitionTable","FAIHook","FAIvariable","FAItemplate");
452 if(count(array_intersect($classes,$objectAttrs['objectClass']))){
453 $reload_fai_classes = TRUE;
454 }
456 }else{
458 /* This object is inherited by some sub releases */
460 /* Get all releases, that inherit this object */
461 $r = FAI::get_following_releases_that_inherit_this_object($Current_DN);
463 /* Get parent object */
464 $ldap->cat($parent_obj);
465 $parent_attrs = FAI::prepare_ldap_fetch_to_be_saved($ldap->fetch());
467 /* New objects require special handling */
468 if($is_new){
470 /* Force reload of FAI classes */
471 $classes = array("FAIprofile","FAIscript","FAIpackageList","FAIpartitionTable","FAIHook","FAIvariable","FAItemplate");
472 if(count(array_intersect($classes,$objectAttrs['objectClass']))){
473 $reload_fai_classes = TRUE;
474 }
476 /* check if there is already an entry named like this,
477 in one of our parent releases */
478 if(!empty($parent_obj)){
479 if(DEBUG_FAI_FUNC) {
480 echo "There is already an entry named like this.</b><br>";
482 echo "<b>Saving main object</b>".$Current_DN;
483 print_a($objectAttrs);
484 }
485 FAI::save_FAI_object($Current_DN,$objectAttrs);
487 foreach($r as $key){
488 if(DEBUG_FAI_FUNC) {
489 echo "<b>Saving parent to following release</b> ".$key;
490 print_a($parent_attrs);
491 }
492 FAI::save_FAI_object($key,$parent_attrs);
493 }
494 }else{
496 if(DEBUG_FAI_FUNC) {
497 echo "<b>Saving main object</b>".$Current_DN;
498 print_a($objectAttrs);
499 }
500 FAI::save_FAI_object($Current_DN,$objectAttrs);
502 if(isset($objectAttrs['FAIstate'])){
503 $objectAttrs['FAIstate'] .= "|removed";
504 }else{
505 $objectAttrs['FAIstate'] = "|removed";
506 }
508 foreach($r as $key ){
509 /* Only save removed parent objects, not their children, unless
510 they are a child of a copy-on-write parent in a subrelease */
511 if (FAI::is_parent_object($Current_DN) || FAI::is_child_of_cow_parent($key)){
512 if(DEBUG_FAI_FUNC) {
513 echo "<b>Create an empty placeholder in follwing release</b> ".$key;
514 print_a($objectAttrs);
515 }
516 FAI::save_FAI_object($key,$objectAttrs);
517 }
518 }
519 }
520 }else{
522 /* check if we must patch the follwing release */
523 if(!empty($r)){
525 foreach($r as $key ){
526 /* Append FAIstate tag to ensure that freezed objects stay freezed
527 */
528 $rTag = FAI::get_release_tag(FAI::get_release_dn($key));
529 $parent_attrs['FAIstate'] = $rTag;
530 /* Don't copy over subobjects in subreleases if their parent is in "removed" state */
531 if(!FAI::parent_is_removed($key)){
532 /* FAItemplateFile can be binary, therefore it needs to be fetched with
533 * $ldap->get_attribute */
534 if (isset($parent_attrs['FAItemplateFile'])) {
535 $parent_attrs['FAItemplateFile'] = $ldap->get_attribute($parent_obj, 'FAItemplateFile');
536 }
537 if(DEBUG_FAI_FUNC) {
538 echo "<b>Copy current objects original attributes to next release</b> ".$key;
539 print_a($parent_attrs);
540 }
541 FAI::save_FAI_object($key,$parent_attrs);
542 }
543 }
544 }
546 if(DEBUG_FAI_FUNC) {
547 echo "<b>Saving current object</b>".$parent_obj;
548 print_a($objectAttrs);
549 }
550 FAI::save_FAI_object($parent_obj,$objectAttrs);
552 if(($parent_obj != $Current_DN)){
553 msg_dialog::display(_("Error"), sprintf(_("Error, following objects should be equal '%s' and '%s'"),$parent_obj,$Current_DN), ERROR_DIALOG);
554 }
555 }
556 }
557 }
559 /* Reload GOsa si FAI DB/cache
560 */
561 if($reload_fai_classes){
562 if( class_available("DaemonEvent") && class_available("gosaSupportDaemon")){
563 $events = DaemonEvent::get_event_types(SYSTEM_EVENT | HIDDEN_EVENT);
564 if(isset($events['TRIGGERED']['DaemonEvent_recreate_fai_release_db'])){
565 $evt = $events['TRIGGERED']['DaemonEvent_recreate_fai_release_db'];
566 $tmp = new $evt['CLASS_NAME']($config);
567 $tmp->set_type(TRIGGERED_EVENT);
568 $tmp->add_targets(array("GOSA"));
569 $o_queue = new gosaSupportDaemon();
570 if(!$o_queue->append($tmp)){
571 msg_dialog::display(_("Service infrastructure"),msgPool::siError($o_queue->get_error()),ERROR_DIALOG);
572 }
573 }
574 }
575 }
577 session::set('FAI_objects_to_save',array());
578 }
581 /* this function will remove all unused (deleted) objects,
582 that have no parent object. If $recursive is set to true,
583 also check sub releases. */
584 static function clean_up_releases($Current_DN, $recursive=true)
585 {
586 global $config;
587 $ldap = $config->get_ldap_link();
588 $ldap->cd($config->current['BASE']);
590 /* Collect some basic informations and initialize some variables */
591 $base_release = FAI::get_release_dn($Current_DN);
592 $previous_releases = array_reverse(FAI:: get_previous_releases_of_this_release($base_release,true));
593 $sub_releases = array_keys(FAI::get_sub_releases_of_this_release($base_release,false));
594 $Kill = array();
595 $Skip = array();
597 /* We must also include the given release dn */
598 $previous_releases[] = $base_release;
600 $all_releases = $previous_releases;
602 if ($recursive) {
603 /* Merge parent, current and child releases into one big release to
604 iterate over */
605 foreach($sub_releases as $sub_release){
606 $all_releases[] = $sub_release;
607 }
608 }
610 /* Walk through all releases */
611 foreach($all_releases as $release){
613 /* Get fai departments */
614 $deps_to_search = FAI::get_FAI_departments($release);
616 /* For every single department (ou=hoos,ou ..) */
617 foreach($deps_to_search as $fai_base){
619 /* Ldap search for fai classes specified in this release */
620 $ldap->cd($fai_base);
621 $ldap->search("(|(objectClass=FAIclass)(objectClass=FAIdebconfInfo))",array("dn","objectClass","FAIstate"));
623 /* check the returned objects, and add/replace them in our return variable */
624 while($attr = $ldap->fetch()){
626 $buffer = array();
627 # $name = str_ireplace($release,"",$attr['dn']);
628 $name = preg_replace("/".preg_quote($release, '/')."/i","",$attr['dn']);
630 if(isset($attr['FAIstate'][0])&&(preg_match("/removed$/",$attr['FAIstate'][0]))){
632 /* Check if this object is required somehow */
633 if(!isset($Skip[$name])){
634 $Kill[$attr['dn']] = $attr['dn'];
635 }
636 }else{
638 /* This object is required (not removed), so do not
639 delete any following sub releases of this object */
640 $Skip[$name] = $attr['dn'];
641 }
642 }
643 }
644 }
645 return($Kill);
646 }
649 /* Remove numeric index and 'count' from ldap->fetch result */
650 static function prepare_ldap_fetch_to_be_saved($attrs)
651 {
652 foreach($attrs as $key => $value){
653 if(is_numeric($key) || ($key == "count") || ($key == "dn")){
654 unset($attrs[$key]);
655 }
656 if(is_array($value) && isset($value['count'])){
657 unset($attrs[$key]['count']);
658 }
659 }
660 return($attrs);
661 }
664 /* Save given attrs to specified dn*/
665 static function save_FAI_object($dn,$attrs)
666 {
667 global $config;
668 $ldap = $config->get_ldap_link();
669 $ldap->cd($config->current['BASE']);
670 $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dn));
671 $ldap->cd($dn);
673 $ui= get_userinfo();
674 FAI::tag_attrs($attrs, $dn, $ui->gosaUnitTag);
676 $ldap->cat($dn,array("dn"));
677 if($ldap->count()){
679 /* Remove FAIstate*/
680 if(!isset($attrs['FAIstate'])){
681 $attrs['FAIstate'] = array();
682 }
684 $ldap->modify($attrs);
685 }else{
687 /* Unset description if empty */
688 if(empty($attrs['description'])){
689 unset($attrs['description']);
690 }
692 $ldap->add($attrs);
693 }
694 if (!$ldap->success()){
695 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, 0, get_class()));
696 }
697 }
700 /* Return FAIstate freeze branch or "" for specified release department */
701 static function get_release_tag($dn)
702 {
703 global $config;
704 $ldap = $config->get_ldap_link();
705 $ldap->cd($dn);
706 $ldap->cat($dn,array("FAIstate"));
708 if($ldap->count()){
710 $attr = $ldap->fetch();
711 if(isset($attr['FAIstate'][0])){
712 if(preg_match("/freeze/",$attr['FAIstate'][0])){
713 return("freeze");
714 }elseif(preg_match("/branch/",$attr['FAIstate'][0])){
715 return("branch");
716 }
717 }
718 }
719 return("");
720 }
723 static function get_following_releases_that_inherit_this_object($dn)
724 {
725 global $config;
726 $ldap = $config->get_ldap_link();
727 $ldap->cd($config->current['BASE']);
729 $ret = array();
731 /* Get base release */
732 $base_release = FAI::get_release_dn($dn);
734 /* Get previous release dns */
735 $sub_releases = FAI:: get_sub_releases_of_this_release($base_release);
737 /* Get dn suffix. Example "FAIvairableEntry=keksdose,FAIvariable=Keksregal," */
738 # $dn_suffix = str_ireplace($base_release,"",$dn);
739 $dn_suffix = preg_replace("/".preg_quote($base_release, '/')."/i","",$dn);
741 /* Check if given object also exists whitin one of these releases */
742 foreach($sub_releases as $p_release => $name){
744 $check_dn = $dn_suffix.$p_release;
746 $ldap->cat($check_dn,array("dn","objectClass"));
748 if($ldap->count()){
749 //return($ret);
750 }else{
751 $ret[$check_dn]=$check_dn;
752 }
753 }
754 return($ret);
755 }
758 /* Get previous version of the object dn */
759 static function get_parent_release_object($dn,$include_myself=true)
760 {
761 global $config;
762 $ldap = $config->get_ldap_link();
763 $ldap->cd($config->current['BASE']);
764 $previous_releases= array();
766 /* Get base release */
767 $base_release = FAI::get_release_dn($dn);
768 if($include_myself){
769 $previous_releases[] = $base_release;
770 }
772 /* Get previous release dns */
773 $tmp = FAI:: get_previous_releases_of_this_release($base_release,true);
774 foreach($tmp as $release){
775 $previous_releases[] = $release;
776 }
778 /* Get dn suffix. Example "FAIvairableEntry=keksdose,FAIvariable=Keksregal," */
779 # $dn_suffix = str_ireplace($base_release,"",$dn);
780 $dn_suffix = preg_replace("/".preg_quote($base_release, '/')."/i","",$dn);
782 /* Check if given object also exists whitin one of these releases */
783 foreach($previous_releases as $p_release){
784 $check_dn = $dn_suffix.$p_release;
785 $ldap->cat($check_dn,array("dn","objectClass"));
787 if($ldap->count()){
788 return($check_dn);
789 }
790 }
791 return("");
792 }
795 /* return release names of all parent releases */
796 static function get_previous_releases_of_this_release($dn,$flat)
797 {
798 global $config;
799 $ldap = $config->get_ldap_link();
800 $ldap->cd($config->current['BASE']);
801 $ret = array();
803 /* Explode dns into pieces, to be able to build parent dns */
804 $dns_to_check = gosa_ldap_explode_dn(preg_replace("/".preg_quote(",".$config->current['BASE'], '/')."/i","",$dn));
806 if(!is_array($dns_to_check)){
807 return;
808 }
810 /* Unset first entry which represents the given dn */
811 unset($dns_to_check['count']);
812 unset($dns_to_check[key($dns_to_check)]);
814 /* Create dns addresses and check if this dn is a release dn */
815 $id = 0;
816 while(count($dns_to_check)){
818 /* build parent dn */
819 $new_dn = "";
820 foreach($dns_to_check as $part){
821 $new_dn .= $part.",";
822 }
823 $new_dn .= $config->current['BASE'];
825 /* check if this dn is a release */
826 if(FAI::is_release_department($new_dn)){
827 if($flat){
828 $ret[$id] = $new_dn;
829 }else{
830 $ret = array($new_dn=>$ret);
831 }
832 $id ++;
833 }else{
834 return($ret);
835 }
836 reset($dns_to_check);
837 unset($dns_to_check[key($dns_to_check)]);
838 }
839 return($ret);
840 }
843 /* This function returns all sub release names, recursivly */
844 static function get_sub_releases_of_this_release($dn,$flat = false)
845 {
846 global $config;
847 $res = array();
848 $ldap = $config->get_ldap_link();
849 $ldap->cd($config->current['BASE']);
850 $ldap->ls("(objectClass=FAIbranch)",$dn,array("objectClass","dn","ou"));
851 while($attr = $ldap->fetch()){
853 /* Append department name */
854 if($flat){
855 $res[$attr['dn']] = $attr['ou'][0];
856 }else{
857 $res[$attr['dn']] = array();
858 }
860 /* Get sub release departments of this department */
861 if(in_array("FAIbranch",$attr['objectClass'])) {
862 if($flat){
863 $tmp = FAI:: get_sub_releases_of_this_release($attr['dn'],$flat);
864 foreach($tmp as $dn => $value){
865 $res[$dn]=$value;
866 }
867 }else{
868 $res[$attr['dn']] = FAI:: get_sub_releases_of_this_release($attr['dn']);
869 }
870 }
871 }
872 return($res);
873 }
876 /* Check if the given department is a release department */
877 static function is_release_department($dn)
878 {
879 global $config;
880 $ldap = $config->get_ldap_link();
881 $ldap->cd($config->current['BASE']);
882 $ldap->cat($dn,array("objectClass","ou"));
884 /* Check objectClasses and name to check if this is a release department */
885 if($ldap->count()){
886 $attrs = $ldap->fetch();
888 $ou = "";
889 if(isset($attrs['ou'][0])){
890 $ou = $attrs['ou'][0];
891 }
893 if((in_array("FAIbranch",$attrs['objectClass'])) || ($ou == "fai")){
894 return($attrs['dn']);
895 }
896 }
897 return(false);
898 }
901 static function copy_FAI_group_releases($source_release , $destination_name, $type ="" )
902 {
903 global $config;
904 $start = microtime(TRUE);
905 $source_release = trim($source_release,"/");
907 echo "<h2>".sprintf(_("Creating group application release for %s"),$destination_name)."</h2>";
909 $sub_releases = array();
910 $source_dn = "";
912 $tmp = split("\/",$source_release);
913 foreach($tmp as $part){
914 if(empty($part)){
915 continue;
916 }
917 $source_dn = "ou=".$part.",".$source_dn;
918 $sub_releases[$part] = $source_dn;
919 }
921 /* Get all groups */
922 $ldap =$config->get_ldap_link();
923 $ldap->cd($config->current['BASE']);
924 $ldap->search("(objectClass=posixGroup)",array("dn"));
925 $groups = array();
926 while($attrs = $ldap->fetch()){
927 $groups[$attrs['dn']] = $attrs;
928 }
930 /* Get all FAI releases, to be able to create missing group application releases
931 with the correct type of release (FAIstate=freeze/branch).
932 */
933 $f_releases = array();
934 $ldap->cd ($config->current['BASE']);
935 $ldap->search("(objectClass=FAIbranch)",array("ou","FAIstate"));
936 while($attrs = $ldap->fetch()){
937 foreach($sub_releases as $sub_rel){
938 if(preg_match("/^".preg_quote($sub_rel.get_ou('faiBaseRDN'), '/')."/",$attrs['dn'])){
939 $f_releases[$sub_rel.get_ou('faiBaseRDN')] = $attrs;
940 }
941 }
942 }
944 /* Get all group releases */
945 $g_releases = array();
946 foreach($groups as $dn => $data){
947 $ldap->cd($dn);
948 $ldap->search("(objectClass=FAIbranch)",array("ou","FAIstate"));
949 while($attrs = $ldap->fetch()){
950 $g_releases[$attrs['dn']] = $attrs;
951 }
952 }
954 /* Check if base releases exists.
955 If they do not exist, create them and adapt FAIstate attribute from FAI releases.
956 */
957 foreach($sub_releases as $name => $sub_rel){
959 $FAIstate = "";
960 if(isset($f_releases[$sub_rel.get_ou('faiBaseRDN')]) && isset($f_releases[$sub_rel.get_ou('faiBaseRDN')]['FAIstate'])){
961 $FAIstate = $f_releases[$sub_rel.get_ou('faiBaseRDN')]['FAIstate'][0];
962 }
964 foreach($groups as $dn => $data){
965 if(!isset($g_releases[$sub_rel.$dn])){
966 $ldap->cd($dn);
967 $r_data = array();
968 $r_data['ou'] = $name;
969 $r_data['objectClass'] = array("top","organizationalUnit","FAIbranch");
970 if(!empty($FAIstate)) {
971 $r_data['FAIstate'] = $FAIstate;
972 }
974 $ldap->cd($sub_rel.$dn) ;
975 $ldap->add($r_data);
976 echo " <b>"._("Object").":</b> ";
977 echo sprintf(_("Adding missing group application release container %s."),substr(LDAP::fix($sub_rel.$dn),0,96))."<br>";
978 flush();
979 }
980 }
981 }
983 /* Create new release container in each group.
984 */
985 $n_data = array();
986 $n_data = array();
987 $n_data['ou'] = $destination_name;
988 $n_data['objectClass'] = array("top","organizationalUnit","FAIbranch");
989 if(!empty($type)){
990 $n_data['FAIstate'] = $type."/cow";
991 }
993 foreach($groups as $dn => $att){
994 $n_dn = "ou=".$destination_name.",".$source_dn.$dn;
995 if(!isset($g_releases[$n_dn])){
996 $ldap->cd ($n_dn);
997 $ldap->add($n_data);
998 echo " <b>"._("Object").":</b> ";
999 echo sprintf(_("Adding group application release container %s."),substr(LDAP::fix($n_dn),0,96))."<br>";
1000 flush();
1001 }
1002 }
1004 /* If the source release is empty, then create a new release by copying
1005 all group application menus into a new ou=$destination_name release container.
1007 If the source release is not empty.
1008 We detect all releases which match the source release dn and copy the contents.
1009 */
1010 if(empty($source_release)){
1011 $source_dns = $groups;
1012 }else{
1013 $source_dns = array();
1014 foreach($g_releases as $dn => $data){
1015 if(preg_match("/^".preg_quote($source_dn, '/')."/",$dn)){
1016 $source_dns[$dn] = $data;
1017 }
1018 }
1019 }
1021 /* Detect all menu object we have to copy
1022 */
1023 $to_copy = array();
1024 foreach($source_dns as $dn => $attrs){
1025 $ldap->cd($dn);
1026 $ldap->ls("(|(objectClass=gotoSubmenuEntry)(objectClass=gotoMenuEntry))",$dn,array("dn"));
1027 while($attrs = $ldap->fetch()){
1028 $destination = preg_replace("/".preg_quote($dn, '/')."$/","ou=".$destination_name.",".$dn,$attrs['dn']);
1029 $to_copy[$attrs['dn']] = $destination;
1030 }
1031 }
1033 /* At least create the menu objects object */
1034 $plug = new plugin($config);
1035 foreach($to_copy as $source => $destination){
1036 $ldap->cat($destination);
1037 if($ldap->count()){
1038 echo " <b>"._("Object").":</b> ";
1039 echo sprintf(_("Could not create menu entry %s. (Already exists)."),substr(LDAP::fix($destination),0,96))."<br>";
1040 flush();
1041 }else{
1042 $plug->copy($source,$destination);
1043 echo " <b>"._("Object").":</b> ";
1044 echo sprintf(_("Created group application menu entry for %s."),substr(LDAP::fix($destination),0,96))."<br>";
1045 flush();
1046 }
1047 }
1048 }
1051 /*! \brief Create a new FAI branch.
1052 * @param $sourcedn String The source release dn
1053 * @param $destinationdn String The destination dn
1054 * @param $destinationName String The name of the new release
1055 * @param $type String The release type (freeze/branch)
1056 * @param $is_first Boolean Use to identify the first func. call when recursivly called.
1057 * @param $depth Integer Current depth of recursion.
1058 */
1059 static function copy_FAI_resource_recursive($sourcedn,$destinationdn,$destinationName,$type="branch",$is_first = true,$depth=0)
1060 {
1061 global $config;
1062 error_reporting(E_ALL | E_STRICT);
1063 $ldap = $config->get_ldap_link();
1064 $basedn = $config->current['BASE'];
1065 $delarray = array();
1067 /* The following code will output a status string
1068 * for each handled object, in a seperate iframe.
1069 */
1072 /* Display current action information.
1073 */
1074 if($is_first){
1075 echo "<h2>".sprintf(_("Creating copy of %s"),"<i>".LDAP::fix($sourcedn)."</i>")."</h2>";
1076 }else{
1077 if(preg_match("/^ou=/",$sourcedn)){
1078 echo "<h3>"._("Processing")." <i>".LDAP::fix($destinationdn)."</i></h3>";
1079 }else{
1080 $tmp = split(",",$sourcedn);
1081 echo " <b>"._("Object").":</b> ";
1082 $deststr = LDAP::fix($destinationdn);
1083 if(strlen($deststr) > 96){
1084 $deststr = substr($deststr,0,96)."...";
1085 }
1086 echo $deststr."<br>";
1087 }
1088 }
1089 /* .. immediately display infos */
1090 flush();
1092 /* Check if destination entry already exists
1093 */
1094 $ldap->cat($destinationdn);
1095 if($ldap->count()){
1096 echo _("Could not create new release, the destination dn is already in use.");
1097 return;
1098 }else{
1100 $ldap->clearResult();
1102 /* Get source entry
1103 * if it does not exist, abort here.
1104 */
1105 $ldap->cd($basedn);
1106 $ldap->cat($sourcedn);
1107 $attr = $ldap->fetch();
1108 if((!$attr) || (count($attr)) ==0) {
1109 echo _("Error while fetching source dn - aborted!");
1110 return;
1111 }
1113 /* The current object we want to create is an department.
1114 * Create the department and add the FAIbranch tag.
1115 */
1116 if(in_array("organizationalUnit",$attr['objectClass'])){
1117 $attr['dn'] = LDAP::convert($destinationdn);
1118 $ldap->cd($basedn);
1119 $ldap->create_missing_trees($destinationdn);
1120 $ldap->cd($destinationdn);
1122 /* If is first entry, append FAIbranch to department entry */
1123 if($is_first){
1124 $ldap->cat($destinationdn);
1125 $attr= $ldap->fetch();
1126 /* Filter unneeded informations */
1127 foreach($attr as $key => $value){
1128 if(is_numeric($key)) unset($attr[$key]);
1129 if(isset($attr[$key]['count'])){
1130 if(is_array($attr[$key])){
1131 unset($attr[$key]['count']);
1132 }
1133 }
1134 }
1136 unset($attr['count']);
1137 unset($attr['dn']);
1139 /* Add marking attribute */
1140 $attr['objectClass'][] = "FAIbranch";
1141 $attr['FAIstate'] = $type;
1143 /* Add this entry */
1144 $ldap->modify($attr);
1145 }
1146 }else{
1148 /* Replicate all relevant FAI objects here.
1149 * FAI objects, Apps and Mimetypes.
1150 * Get all attributes as binary value, to ensure that Icon, File template aso
1151 * are created correctly.
1152 */
1153 foreach($attr as $key => $value){
1155 if(in_array($key ,array("gotoLogonScript", "gosaApplicationIcon","gotoMimeIcon"))){
1156 $sr= ldap_read($ldap->cid, LDAP::fix($sourcedn), "$key=*", array($key));
1157 $ei= ldap_first_entry($ldap->cid, $sr);
1158 if ($tmp= @ldap_get_values_len($ldap->cid, $ei,$key)){
1159 $attr[$key] = $tmp;
1160 }
1161 }
1163 if(is_numeric($key)) unset($attr[$key]);
1164 if(isset($attr[$key]['count'])){
1165 if(is_array($attr[$key])){
1166 unset($attr[$key]['count']);
1167 }
1168 }
1169 }
1170 unset($attr['count']);
1171 unset($attr['dn']);
1172 if(!in_array("FAIobject",$attr['objectClass'])){
1173 $attr['objectClass'][] = "FAIobject";
1174 }
1175 $attr['FAIstate'] = $type;
1177 /* Add entry
1178 */
1179 $ldap->cd($destinationdn);
1180 $ldap->cat($destinationdn);
1182 $a = $ldap->fetch();
1183 if(!count($a)){
1184 $ldap->add($attr);
1185 }
1187 if(!$ldap->success()){
1189 /* Some error occurred */
1190 msg_dialog::display(_("Fatal error"),
1191 sprintf(_("Release creation failed due to ldap errors. Additional informations '%s'."),
1192 $ldap->get_error()."<br>".$sourcedn."<br>".$destinationdn."<br>"),FATAL_ERROR_DIALOG);
1193 exit();
1194 }
1195 }
1196 }
1198 echo "<script language=\"javascript\" type=\"text/javascript\">scrollDown2();</script>" ;
1200 /* Prepare for recursive copy.
1201 * Get all object within the source dn and
1202 * call the recursive copy for each.
1203 */
1204 $ldap->ls ("(objectClass=*)",$sourcedn);
1205 while ($ldap->fetch()){
1206 $deldn= $ldap->getDN();
1207 $delarray[$deldn]= strlen($deldn);
1208 }
1209 asort ($delarray);
1210 reset ($delarray);
1211 $depth ++;
1212 foreach($delarray as $dn => $bla){
1213 if($dn != $destinationdn){
1214 $ldap->cd($basedn);
1215 $item = $ldap->fetch($ldap->cat($dn));
1216 if(!in_array("FAIbranch",$item['objectClass'])){
1217 FAI::copy_FAI_resource_recursive($dn,str_replace($sourcedn,$destinationdn,$dn),$destinationName,$type,false,$depth);
1218 }
1219 }
1220 }
1221 if($is_first){
1222 echo "<p class='seperator'> </p>";
1223 }
1224 }
1228 /* This function returns the dn of the object release */
1229 static function get_release_dn($Current_DN)
1230 {
1231 global $config;
1232 $ldap = $config->get_ldap_link();
1233 $ldap->cd($config->current['BASE']);
1235 /* Split dn into pices */
1236 $dns_to_check = gosa_ldap_explode_dn(preg_replace("/".preg_quote(",".$config->current['BASE'], '/')."/i","",$Current_DN));
1238 if(!is_array($dns_to_check)){
1239 return;
1240 }
1242 /* Use dn pieces, to create sub dns like
1243 ou=test,ou=1,ou=0...
1244 ou=1,ou=0...
1245 ou=0...
1246 To check which dn is our release container.
1247 */
1248 unset($dns_to_check['count']);
1249 while(count($dns_to_check)){
1251 /* Create dn */
1252 $new_dn = "";
1253 foreach($dns_to_check as $part){
1254 $new_dn .= $part.",";
1255 }
1256 $new_dn .= $config->current['BASE'];
1258 /* Check if this dn is a release dn */
1259 if(FAI::is_release_department($new_dn)){
1260 return($new_dn);
1261 }
1263 /* Remove first element of dn pieces */
1264 reset($dns_to_check);
1265 unset($dns_to_check[key($dns_to_check)]);
1266 }
1267 return("");
1268 }
1271 static function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1272 {
1273 /* Remove tags that may already be here... */
1274 remove_objectClass("gosaAdministrativeUnitTag", $at);
1275 if (isset($at['gosaUnitTag'])){
1276 unset($at['gosaUnitTag']);
1277 }
1279 /* Set tag? */
1280 if ($tag != ""){
1281 add_objectClass("gosaAdministrativeUnitTag", $at);
1282 $at['gosaUnitTag']= $tag;
1283 }
1285 /* Initially this object was tagged.
1286 - But now, it is no longer inside a tagged department.
1287 So force the remove of the tag.
1288 (objectClass was already removed obove)
1289 */
1290 if($tag == ""){
1291 $at['gosaUnitTag'] = array();
1292 }
1293 }
1295 /* Returns true if this is a parent object, e.g. FAIpartitionTable, not FAIpartitionDisk */
1296 static function is_parent_object($dn)
1297 {
1298 global $config;
1299 $Current_DN = $dn;
1301 /* special case partitions and debconf variables, as they don't start with cn= in their DN */
1302 if (preg_match('/^(FAIpartitionNr|FAIvariable)=/', $Current_DN)){
1303 return false;
1304 }
1306 $parent_dn = preg_replace('/^cn=[^,.]*,/', '', $Current_DN);
1307 if (preg_match('/^cn=/', $parent_dn)) {
1308 $ldap = $config->get_ldap_link();
1309 $ldap->cd($config->current['BASE']);
1310 $ldap->cat($parent_dn);
1311 if($ldap->count()){
1312 return false;
1313 }
1314 }else{
1315 return true;
1316 }
1317 }
1319 /* Return child objects, if there are any */
1320 static function get_child_objects($dn)
1321 {
1322 global $config;
1323 $Current_DN = $dn;
1324 $children= array();
1326 if (FAI::is_parent_object($Current_DN)){
1327 $ldap = $config->get_ldap_link();
1328 $ldap->cd($dn);
1329 $ldap->search("(objectClass=FAIclass)",array("dn"));
1330 while($attrs = $ldap->fetch()){
1331 if(preg_match("/".preg_quote($Current_DN, '/')."/",$attrs['dn']) && $attrs['dn'] != $Current_DN){
1332 $children[] = $attrs['dn'];
1333 }
1334 }
1335 }
1336 return $children;
1337 }
1339 /* Get the DN of the parent object; e.g. the FAIpartitionTable of a FAIpartitionDisk.
1340 If $check is false, do not check whether the parent actually exists in LDAP */
1341 static function get_parent_object($dn, $check=true)
1342 {
1343 global $config;
1344 $Current_DN = $dn;
1345 $i = 0;
1347 $tmp_dn = $Current_DN;
1348 $parent_dn = array();
1350 /* special case partitions and debconf variables, as they don't start with cn= in their DN */
1351 $tmp_dn = preg_replace('/^(FAIpartitionNr|FAIvariable)=/', 'cn=', $tmp_dn);
1353 $tmp_dn = gosa_ldap_explode_dn($tmp_dn);
1354 while(preg_match('/^cn=/', $tmp_dn[$i])) {
1355 $i++;
1356 }
1357 /* DN part does not start with cn= anymore, remove one from the counter to get the
1358 last CN element */
1359 $i--;
1361 for ($i;$i<$tmp_dn['count'];$i++) {
1362 $parent_dn[] = trim($tmp_dn[$i]);
1363 }
1364 $parent_dn = join($parent_dn, ',');
1366 if ($parent_dn != $Current_DN){
1367 if($check){
1368 $ldap = $config->get_ldap_link();
1369 $ldap->cd($config->current['BASE']);
1370 $ldap->cat($parent_dn);
1371 if(!$ldap->count()){
1372 return "";
1373 }
1374 }
1375 return $parent_dn;
1376 }
1377 return "";
1378 }
1380 /* Check whether parent object is removed */
1381 static function parent_is_removed($dn)
1382 {
1383 global $config;
1384 $Current_DN = $dn;
1386 $ldap = $config->get_ldap_link();
1387 $parent_dn = FAI::get_parent_object($Current_DN);
1388 if ($parent_dn) {
1389 $ldap->cd($config->current['BASE']);
1390 $ldap->cat($parent_dn);
1391 $parentObjectAttrs = FAI::prepare_ldap_fetch_to_be_saved($ldap->fetch());
1392 if(isset($parentObjectAttrs['FAIstate'][0])){
1393 if(preg_match("/removed$/",$parentObjectAttrs['FAIstate'][0])){
1394 return true;
1395 }
1396 }
1397 }
1398 return false;
1399 }
1401 /* Check whether this is a child object of a Copy-on-Write parent */
1402 static function is_child_of_cow_parent($dn)
1403 {
1404 global $config;
1405 $Current_DN = $dn;
1406 $parent_dn = "";
1408 if (!FAI::is_parent_object($Current_DN)){
1410 /* This is a child object; check that the parent object is not
1411 marked as removed */
1412 $cow_parent_dn = FAI::get_parent_object($Current_DN, false);
1413 if ($cow_parent_dn && !FAI::parent_is_removed($Current_DN)){
1415 /* This is a child object, check whether the parent object in
1416 the main release exists */
1417 $main_parent_dn = FAI::get_parent_release_object($cow_parent_dn);
1418 $ldap = $config->get_ldap_link();
1419 $ldap->cd($config->current['BASE']);
1420 $ldap->cat($main_parent_dn);
1421 if($ldap->count()){
1422 return true;
1423 }
1424 }
1425 }
1426 return false;
1427 }
1429 static function get_leaf_objects($dn, $cn, $subclass, $rdn) {
1430 $valid_releases = FAI::get_previous_releases_of_this_release(FAI::get_release_dn($dn), true);
1431 /* Remove the last release DN */
1432 array_pop($valid_releases);
1433 $valid_releases[] = FAI::get_release_dn($dn);
1435 $objects = FAI::get_all_objects_for_given_base($dn,"(&(objectClass=FAIclass)(objectClass=".$subclass."))");
1436 $res = array();
1437 /* Strip elements which are not a leaf object of the current dn */
1438 foreach($objects as $obj){
1439 $keep = FALSE;
1440 foreach($valid_releases as $valid_release) {
1441 if (preg_match("/cn=".$cn.",".$rdn.$valid_release."$/", $obj['dn'])) {
1442 $keep = TRUE;
1443 break;
1444 }
1445 }
1447 if ($keep) {
1448 $res[] = $obj;
1449 }
1450 }
1452 return $res;
1453 }
1456 }
1461 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1462 ?>