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();
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 }
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 = "";
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 }
969 $ldap->cd($sub_rel.$dn) ;
970 $ldap->add($r_data);
971 echo " <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 }
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 " <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.
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 }
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 " <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 " <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 " <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'> </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 }
1429 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1430 ?>