Code

Reverted changes from 3871 on. They did not work - even if I
[gosa.git] / include / class_plugin.inc
1 <?php
2 /*
3    This code is part of GOsa (https://gosa.gonicus.de)
4    Copyright (C) 2003  Cajus Pollmeier
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
21 /*! \brief   The plugin base class
22   \author  Cajus Pollmeier <pollmeier@gonicus.de>
23   \version 2.00
24   \date    24.07.2003
26   This is the base class for all plugins. It can be used standalone or
27   can be included by the tabs class. All management should be done 
28   within this class. Extend your plugins from this class.
29  */
31 class plugin
32 {
33   /*!
34     \brief Reference to parent object
36     This variable is used when the plugin is included in tabs
37     and keeps reference to the tab class. Communication to other
38     tabs is possible by 'name'. So the 'fax' plugin can ask the
39     'userinfo' plugin for the fax number.
41     \sa tab
42    */
43   var $parent= NULL;
45   /*!
46     \brief Configuration container
48     Access to global configuration
49    */
50   var $config= NULL;
52   /*!
53     \brief Mark plugin as account
55     Defines whether this plugin is defined as an account or not.
56     This has consequences for the plugin to be saved from tab
57     mode. If it is set to 'FALSE' the tab will call the delete
58     function, else the save function. Should be set to 'TRUE' if
59     the construtor detects a valid LDAP object.
61     \sa plugin::plugin()
62    */
63   var $is_account= FALSE;
64   var $initially_was_account= FALSE;
66   /*!
67     \brief Mark plugin as template
69     Defines whether we are creating a template or a normal object.
70     Has conseqences on the way execute() shows the formular and how
71     save() puts the data to LDAP.
73     \sa plugin::save() plugin::execute()
74    */
75   var $is_template= FALSE;
76   var $ignore_account= FALSE;
77   var $is_modified= FALSE;
79   /*!
80     \brief Represent temporary LDAP data
82     This is only used internally.
83    */
84   var $attrs= array();
86   /* Keep set of conflicting plugins */
87   var $conflicts= array();
89   /* Save unit tags */
90   var $gosaUnitTag= "";
92   /*!
93     \brief Used standard values
95     dn
96    */
97   var $dn= "";
98   var $uid= "";
99   var $sn= "";
100   var $givenName= "";
101   var $acl= "*none*";
102   var $dialog= FALSE;
103   var $snapDialog = NULL;
105   /* attribute list for save action */
106   var $attributes= array();
107   var $objectclasses= array();
108   var $new= TRUE;
109   var $saved_attributes= array();
111   /*! \brief plugin constructor
113     If 'dn' is set, the node loads the given 'dn' from LDAP
115     \param dn Distinguished name to initialize plugin from
116     \sa plugin()
117    */
118   function plugin ($config, $dn= NULL)
119   {
120     /* Configuration is fine, allways */
121     $this->config= $config;     
122     $this->dn= $dn;
124     /* Handle new accounts, don't read information from LDAP */
125     if ($dn == "new"){
126       return;
127     }
129     /* Get LDAP descriptor */
130     $ldap= $this->config->get_ldap_link();
131     if ($dn != NULL){
133       /* Load data to 'attrs' and save 'dn' */
134       $ldap->cat ($dn);
135       $this->attrs= $ldap->fetch();
137       /* Copy needed attributes */
138       foreach ($this->attributes as $val){
139         $found= array_key_ics($val, $this->attrs);
140         if ($found != ""){
141           $this->$val= $this->attrs["$found"][0];
142         }
143       }
145       /* gosaUnitTag loading... */
146       if (isset($this->attrs['gosaUnitTag'][0])){
147         $this->gosaUnitTag= $this->attrs['gosaUnitTag'][0];
148       }
150       /* Set the template flag according to the existence of objectClass
151          gosaUserTemplate */
152       if (isset($this->attrs['objectClass'])){
153         if (in_array ("gosaUserTemplate", $this->attrs['objectClass'])){
154           $this->is_template= TRUE;
155           @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
156               "found", "Template check");
157         }
158       }
160       /* Is Account? */
161       error_reporting(0);
162       $found= TRUE;
163       foreach ($this->objectclasses as $obj){
164         if (preg_match('/top/i', $obj)){
165           continue;
166         }
167         if (!isset($this->attrs['objectClass']) || !in_array_ics ($obj, $this->attrs['objectClass'])){
168           $found= FALSE;
169           break;
170         }
171       }
172       error_reporting(E_ALL);
173       if ($found){
174         $this->is_account= TRUE;
175         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
176             "found", "Object check");
177       }
179       /* Prepare saved attributes */
180       $this->saved_attributes= $this->attrs;
181       foreach ($this->saved_attributes as $index => $value){
182         if (preg_match('/^[0-9]+$/', $index)){
183           unset($this->saved_attributes[$index]);
184           continue;
185         }
186         if (!in_array($index, $this->attributes) && $index != "objectClass"){
187           unset($this->saved_attributes[$index]);
188           continue;
189         }
190         if ($this->saved_attributes[$index]["count"] == 1){
191           $tmp= $this->saved_attributes[$index][0];
192           unset($this->saved_attributes[$index]);
193           $this->saved_attributes[$index]= $tmp;
194           continue;
195         }
197         unset($this->saved_attributes["$index"]["count"]);
198       }
199     }
201     /* Save initial account state */
202     $this->initially_was_account= $this->is_account;
203   }
205   /*! \brief execute plugin
207     Generates the html output for this node
208    */
209   function execute()
210   {
211     # This one is empty currently. Fabian - please fill in the docu code
212     $_SESSION['current_class_for_help'] = get_class($this);
213     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
214     $_SESSION['LOCK_VARS_TO_USE'] =array();
215   }
217   /*! \brief execute plugin
218      Removes object from parent
219    */
220   function remove_from_parent()
221   {
222     /* include global link_info */
223     $ldap= $this->config->get_ldap_link();
225     /* Get current objectClasses in order to add the required ones */
226     $ldap->cat($this->dn);
227     $tmp= $ldap->fetch ();
228     if (isset($tmp['objectClass'])){
229       $oc= $tmp['objectClass'];
230     } else {
231       $oc= array("count" => 0);
232     }
234     /* Remove objectClasses from entry */
235     $ldap->cd($this->dn);
236     $this->attrs= array();
237     $this->attrs['objectClass']= array();
238     for ($i= 0; $i<$oc["count"]; $i++){
239       if (!in_array_ics($oc[$i], $this->objectclasses)){
240         $this->attrs['objectClass'][]= $oc[$i];
241       }
242     }
244     /* Unset attributes from entry */
245     foreach ($this->attributes as $val){
246       $this->attrs["$val"]= array();
247     }
249     /* Unset account info */
250     $this->is_account= FALSE;
252     /* Do not write in plugin base class, this must be done by
253        children, since there are normally additional attribs,
254        lists, etc. */
255     /*
256        $ldap->modify($this->attrs);
257      */
258   }
261   /* Save data to object */
262   function save_object()
263   {
264     /* Save values to object */
265     foreach ($this->attributes as $val){
266       if (chkacl ($this->acl, "$val") == "" && isset ($_POST["$val"])){
267         /* Check for modifications */
268         if (get_magic_quotes_gpc()) {
269           $data= stripcslashes($_POST["$val"]);
270         } else {
271           $data= $this->$val = $_POST["$val"];
272         }
273         if ($this->$val != $data){
274           $this->is_modified= TRUE;
275         }
276     
277         /* Okay, how can I explain this fix ... 
278          * In firefox, disabled option fields aren't selectable ... but in IE you can select these fileds. 
279          * So IE posts these 'unselectable' option, with value = chr(194) 
280          * chr(194) seems to be the &nbsp; in between the ...option>&nbsp;</option.. because there is no value=".." specified in these option fields  
281          * This &nbsp; was added for W3c compliance, but now causes these ... ldap errors ... 
282          * So we set these Fields to ""; a normal empty string, and we can check these values in plugin::check() again ...
283          */
284         if(isset($data[0]) && $data[0] == chr(194)) {
285           $data = "";  
286         }
287         $this->$val= $data;
288       }
289     }
290   }
293   /* Save data to LDAP, depending on is_account we save or delete */
294   function save()
295   {
296     /* include global link_info */
297     $ldap= $this->config->get_ldap_link();
299     /* Start with empty array */
300     $this->attrs= array();
302     /* Get current objectClasses in order to add the required ones */
303     $ldap->cat($this->dn);
304     
305     $tmp= $ldap->fetch ();
306     
307     if (isset($tmp['objectClass'])){
308       $oc= $tmp["objectClass"];
309       $this->new= FALSE;
310     } else {
311       $oc= array("count" => 0);
312       $this->new= TRUE;
313     }
315     /* Load (minimum) attributes, add missing ones */
316     $this->attrs['objectClass']= $this->objectclasses;
317     for ($i= 0; $i<$oc["count"]; $i++){
318       if (!in_array_ics($oc[$i], $this->objectclasses)){
319         $this->attrs['objectClass'][]= $oc[$i];
320       }
321     }
323     /* Copy standard attributes */
324     foreach ($this->attributes as $val){
325       if ($this->$val != ""){
326         $this->attrs["$val"]= $this->$val;
327       } elseif (!$this->new) {
328         $this->attrs["$val"]= array();
329       }
330     }
332   }
335   function cleanup()
336   {
337     foreach ($this->attrs as $index => $value){
339       /* Convert arrays with one element to non arrays, if the saved
340          attributes are no array, too */
341       if (is_array($this->attrs[$index]) && 
342           count ($this->attrs[$index]) == 1 &&
343           isset($this->saved_attributes[$index]) &&
344           !is_array($this->saved_attributes[$index])){
345           
346         $tmp= $this->attrs[$index][0];
347         $this->attrs[$index]= $tmp;
348       }
350       /* Remove emtpy arrays if they do not differ */
351       if (is_array($this->attrs[$index]) &&
352           count($this->attrs[$index]) == 0 &&
353           !isset($this->saved_attributes[$index])){
354           
355         unset ($this->attrs[$index]);
356         continue;
357       }
359       /* Remove single attributes that do not differ */
360       if (!is_array($this->attrs[$index]) &&
361           isset($this->saved_attributes[$index]) &&
362           !is_array($this->saved_attributes[$index]) &&
363           $this->attrs[$index] == $this->saved_attributes[$index]){
365         unset ($this->attrs[$index]);
366         continue;
367       }
369       /* Remove arrays that do not differ */
370       if (is_array($this->attrs[$index]) && 
371           isset($this->saved_attributes[$index]) &&
372           is_array($this->saved_attributes[$index])){
373           
374         if (!array_differs($this->attrs[$index],$this->saved_attributes[$index])){
375           unset ($this->attrs[$index]);
376           continue;
377         }
378       }
379     }
380   }
382   /* Check formular input */
383   function check()
384   {
385     $message= array();
387     /* Skip if we've no config object */
388     if (!isset($this->config)){
389       return $message;
390     }
392     /* Find hooks entries for this class */
393     $command= search_config($this->config->data['MENU'], get_class($this), "CHECK");
394     if ($command == "" && isset($this->config->data['TABS'])){
395       $command= search_config($this->config->data['TABS'], get_class($this), "CHECK");
396     }
398     if ($command != ""){
400       if (!check_command($command)){
401         $message[]= sprintf(_("Command '%s', specified as CHECK hook for plugin '%s' doesn't seem to exist."), $command,
402                             get_class($this));
403       } else {
405         /* Generate "ldif" for check hook */
406         $ldif= "dn: $this->dn\n";
407         
408         /* ... objectClasses */
409         foreach ($this->objectclasses as $oc){
410           $ldif.= "objectClass: $oc\n";
411         }
412         
413         /* ... attributes */
414         foreach ($this->attributes as $attr){
415           if ($this->$attr == ""){
416             continue;
417           }
418           if (is_array($this->$attr)){
419             foreach ($this->$attr as $val){
420               $ldif.= "$attr: $val\n";
421             }
422           } else {
423               $ldif.= "$attr: ".$this->$attr."\n";
424           }
425         }
427         /* Append empty line */
428         $ldif.= "\n";
430         /* Feed "ldif" into hook and retrieve result*/
431         $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
432         $fh= proc_open($command, $descriptorspec, $pipes);
433         if (is_resource($fh)) {
434           fwrite ($pipes[0], $ldif);
435           fclose($pipes[0]);
436           
437           $result= stream_get_contents($pipes[1]);
438           if ($result != ""){
439             $message[]= $result;
440           }
441           
442           fclose($pipes[1]);
443           fclose($pipes[2]);
444           proc_close($fh);
445         }
446       }
448     }
450     return ($message);
451   }
453   /* Adapt from template, using 'dn' */
454   function adapt_from_template($dn)
455   {
456     /* Include global link_info */
457     $ldap= $this->config->get_ldap_link();
459     /* Load requested 'dn' to 'attrs' */
460     $ldap->cat ($dn);
461     $this->attrs= $ldap->fetch();
463     /* Walk through attributes */
464     foreach ($this->attributes as $val){
466       if (isset($this->attrs["$val"][0])){
468         /* If attribute is set, replace dynamic parts: 
469            %sn, %givenName and %uid. Fill these in our local variables. */
470         $value= $this->attrs["$val"][0];
472         foreach (array("sn", "givenName", "uid") as $repl){
473           if (preg_match("/%$repl/i", $value)){
474             $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
475           }
476         }
477         $this->$val= $value;
478       }
479     }
481     /* Is Account? */
482     $found= TRUE;
483     foreach ($this->objectclasses as $obj){
484       if (preg_match('/top/i', $obj)){
485         continue;
486       }
487       if (!in_array_ics ($obj, $this->attrs['objectClass'])){
488         $found= FALSE;
489         break;
490       }
491     }
492     if ($found){
493       $this->is_account= TRUE;
494     }
495   }
497   /* Indicate whether a password change is needed or not */
498   function password_change_needed()
499   {
500     return FALSE;
501   }
503   /* Show header message for tab dialogs */
504   function show_header($button_text, $text, $disabled= FALSE)
505   {
506     if ($disabled == TRUE){
507       $state= "disabled";
508     } else {
509       $state= "";
510     }
511     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
512     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".
513       chkacl($this->acl, "all")." ".$state.
514       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
516     return($display);
517   }
519   function postcreate($add_attrs= array())
520   {
521     /* Find postcreate entries for this class */
522     $command= search_config($this->config->data['MENU'], get_class($this), "POSTCREATE");
523     if ($command == "" && isset($this->config->data['TABS'])){
524       $command= search_config($this->config->data['TABS'], get_class($this), "POSTCREATE");
525     }
527     if ($command != ""){
528       /* Walk through attribute list */
529       foreach ($this->attributes as $attr){
530         if (!is_array($this->$attr)){
531           $command= preg_replace("/%$attr/", $this->$attr, $command);
532         }
533       }
534       $command= preg_replace("/%dn/", $this->dn, $command);
536       /* Additional attributes */
537       foreach ($add_attrs as $name => $value){
538         $command= preg_replace("/%$name/", $value, $command);
539       }
541       if (check_command($command)){
542         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
543             $command, "Execute");
545         exec($command);
546       } else {
547         $message= sprintf(_("Command '%s', specified as POSTCREATE for plugin '%s' doesn't seem to exist."), $command, get_class($this));
548         print_red ($message);
549       }
550     }
551   }
553   function postmodify($add_attrs= array())
554   {
555     /* Find postcreate entries for this class */
556     $command= search_config($this->config->data['MENU'], get_class($this), "POSTMODIFY");
557     if ($command == "" && isset($this->config->data['TABS'])){
558       $command= search_config($this->config->data['TABS'], get_class($this), "POSTMODIFY");
559     }
561     if ($command != ""){
562       /* Walk through attribute list */
563       foreach ($this->attributes as $attr){
564         if (!is_array($this->$attr)){
565           $command= preg_replace("/%$attr/", $this->$attr, $command);
566         }
567       }
568       $command= preg_replace("/%dn/", $this->dn, $command);
570       /* Additional attributes */
571       foreach ($add_attrs as $name => $value){
572         $command= preg_replace("/%$name/", $value, $command);
573       }
575       if (check_command($command)){
576         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
577             $command, "Execute");
579         exec($command);
580       } else {
581         $message= sprintf(_("Command '%s', specified as POSTMODIFY for plugin '%s' doesn't seem to exist."), $command, get_class($this));
582         print_red ($message);
583       }
584     }
585   }
587   function postremove($add_attrs= array())
588   {
589     /* Find postremove entries for this class */
590     $command= search_config($this->config->data['MENU'], get_class($this), "POSTREMOVE");
591     if ($command == "" && isset($this->config->data['TABS'])){
592       $command= search_config($this->config->data['TABS'], get_class($this), "POSTREMOVE");
593     }
595     if ($command != ""){
596       /* Walk through attribute list */
597       foreach ($this->attributes as $attr){
598         if (!is_array($this->$attr)){
599           $command= preg_replace("/%$attr/", $this->$attr, $command);
600         }
601       }
602       $command= preg_replace("/%dn/", $this->dn, $command);
604       /* Additional attributes */
605       foreach ($add_attrs as $name => $value){
606         $command= preg_replace("/%$name/", $value, $command);
607       }
609       if (check_command($command)){
610         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
611             $command, "Execute");
613         exec($command);
614       } else {
615         $message= sprintf(_("Command '%s', specified as POSTREMOVE for plugin '%s' doesn't seem to exist."), $command, get_class($this));
616         print_red ($message);
617       }
618     }
619   }
621   /* Create unique DN */
622   function create_unique_dn($attribute, $base)
623   {
624     $ldap= $this->config->get_ldap_link();
625     $base= preg_replace("/^,*/", "", $base);
627     /* Try to use plain entry first */
628     $dn= "$attribute=".$this->$attribute.",$base";
629     $ldap->cat ($dn, array('dn'));
630     if (!$ldap->fetch()){
631       return ($dn);
632     }
634     /* Look for additional attributes */
635     foreach ($this->attributes as $attr){
636       if ($attr == $attribute || $this->$attr == ""){
637         continue;
638       }
640       $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
641       $ldap->cat ($dn, array('dn'));
642       if (!$ldap->fetch()){
643         return ($dn);
644       }
645     }
647     /* None found */
648     return ("none");
649   }
651   function rebind($ldap, $referral)
652   {
653     $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
654     if (ldap_bind($ldap, $credentials['ADMIN'], $credentials['PASSWORD'])) {
655       $this->error = "Success";
656       $this->hascon=true;
657       $this->reconnect= true;
658       return (0);
659     } else {
660       $this->error = "Could not bind to " . $credentials['ADMIN'];
661       return NULL;
662     }
663   }
665   /* This is a workaround function. */
666   function copy($src_dn, $dst_dn)
667   {
668     /* Rename dn in possible object groups */
669     $ldap= $this->config->get_ldap_link();
670     $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.$src_dn.'))',
671         array('cn'));
672     while ($attrs= $ldap->fetch()){
673       $og= new ogroup($this->config, $ldap->getDN());
674       unset($og->member[$src_dn]);
675       $og->member[$dst_dn]= $dst_dn;
676       $og->save ();
677     }
679     $ldap->cat($dst_dn);
680     $attrs= $ldap->fetch();
681     if (count($attrs)){
682       trigger_error("Trying to overwrite $dst_dn, which already exists.",
683           E_USER_WARNING);
684       return (FALSE);
685     }
687     $ldap->cat($src_dn);
688     $attrs= $ldap->fetch();
689     if (!count($attrs)){
690       trigger_error("Trying to move $src_dn, which does not seem to exist.",
691           E_USER_WARNING);
692       return (FALSE);
693     }
695     /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
696     $ds= ldap_connect($this->config->current['SERVER']);
697     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
698     if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
699       ldap_set_rebind_proc($ds, array(&$this, "rebind"));
700     }
702     $r=ldap_bind($ds,$this->config->current['ADMIN'], $this->config->current['PASSWORD']);
703     error_reporting (0);
704     $sr=ldap_read($ds, $ldap->fix($src_dn), "objectClass=*");
706     /* Fill data from LDAP */
707     $new= array();
708     if ($sr) {
709       $ei=ldap_first_entry($ds, $sr);
710       if ($ei) {
711         foreach($attrs as $attr => $val){
712           if ($info = ldap_get_values_len($ds, $ei, $attr)){
713             for ($i= 0; $i<$info['count']; $i++){
714               if ($info['count'] == 1){
715                 $new[$attr]= $info[$i];
716               } else {
717                 $new[$attr][]= $info[$i];
718               }
719             }
720           }
721         }
722       }
723     }
725     /* close conncetion */
726     error_reporting (E_ALL);
727     ldap_unbind($ds);
729     /* Adapt naming attribute */
730     $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
731     $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
732     $new[$dst_name]= $dst_val;
734     /* Check if this is a department.
735      * If it is a dep. && there is a , override in his ou 
736      *  change \2C to , again, else this entry can't be saved ...
737      */
738     if((isset($new['ou'])) &&( preg_match("/\\\\2C/",$new['ou']))){
739       $new['ou'] = preg_replace("/\\\\2C/",",",$new['ou']);
740     }
742     /* Save copy */
743     $ldap->connect();
744     $ldap->cd($this->config->current['BASE']);
745     
746     $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
748     /* FAIvariable=.../..., cn=.. 
749         could not be saved, because the attribute FAIvariable was different to 
750         the dn FAIvariable=..., cn=... */
751     if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
752       $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
753     }
754     $ldap->cd($dst_dn);
755     $ldap->add($new);
757     if ($ldap->error != "Success"){
758       trigger_error("Trying to save $dst_dn failed.",
759           E_USER_WARNING);
760       return(FALSE);
761     }
763     return (TRUE);
764   }
767   function move($src_dn, $dst_dn)
768   {
769     /* Copy source to destination */
770     if (!$this->copy($src_dn, $dst_dn)){
771       return (FALSE);
772     }
774     /* Delete source */
775     $ldap= $this->config->get_ldap_link();
776     $ldap->rmdir($src_dn);
777     if ($ldap->error != "Success"){
778       trigger_error("Trying to delete $src_dn failed.",
779           E_USER_WARNING);
780       return (FALSE);
781     }
783     return (TRUE);
784   }
787   /* Move/Rename complete trees */
788   function recursive_move($src_dn, $dst_dn)
789   {
790     /* Check if the destination entry exists */
791     $ldap= $this->config->get_ldap_link();
793     /* Check if destination exists - abort */
794     $ldap->cat($dst_dn, array('dn'));
795     if ($ldap->fetch()){
796       trigger_error("recursive_move $dst_dn already exists.",
797           E_USER_WARNING);
798       return (FALSE);
799     }
801     /* Perform a search for all objects to be moved */
802     $objects= array();
803     $ldap->cd($src_dn);
804     $ldap->search("(objectClass=*)", array("dn"));
805     while($attrs= $ldap->fetch()){
806       $dn= $attrs['dn'];
807       $objects[$dn]= strlen($dn);
808     }
810     /* Sort objects by indent level */
811     asort($objects);
812     reset($objects);
814     /* Copy objects from small to big indent levels by replacing src_dn by dst_dn */
815     foreach ($objects as $object => $len){
816       $src= $object;
817       $dst= preg_replace("/$src_dn$/", "$dst_dn", $object);
818       if (!$this->copy($src, $dst)){
819         return (FALSE);
820       }
821     }
823     /* Remove src_dn */
824     $ldap->cd($src_dn);
825     $ldap->recursive_remove();
826     return (TRUE);
827   }
830   function handle_post_events($mode, $add_attrs= array())
831   {
832     switch ($mode){
833       case "add":
834         $this->postcreate($add_attrs);
835       break;
837       case "modify":
838         $this->postmodify($add_attrs);
839       break;
841       case "remove":
842         $this->postremove($add_attrs);
843       break;
844     }
845   }
848   function saveCopyDialog(){
849   }
852   function getCopyDialog(){
853     return(array("string"=>"","status"=>""));
854   }
857   function PrepareForCopyPaste($source){
858     $todo = $this->attributes;
859     if(isset($this->CopyPasteVars)){
860       $todo = array_merge($todo,$this->CopyPasteVars);
861     }
862     $todo[] = "is_account";
863     foreach($todo as $var){
864       $this->$var = $source->$var;
865     }
866   }
869   function handle_object_tagging($dn= "", $tag= "", $show= false)
870   {
871     //FIXME: How to optimize this? We have at least two
872     //       LDAP accesses per object. It would be a good
873     //       idea to have it integrated.
875     /* No dn? Self-operation... */
876     if ($dn == ""){
877       $dn= $this->dn;
879       /* No tag? Find it yourself... */
880       if ($tag == ""){
881         $len= strlen($dn);
883         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
884         $relevant= array();
885         foreach ($this->config->adepartments as $key => $ntag){
887           /* This one is bigger than our dn, its not relevant... */
888           if ($len <= strlen($key)){
889             continue;
890           }
892           /* This one matches with the latter part. Break and don't fix this entry */
893           if (preg_match('/(^|,)'.normalizePreg($key).'$/', $dn)){
894             @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
895             $relevant[strlen($key)]= $ntag;
896             continue;
897           }
899         }
901         /* If we've some relevant tags to set, just get the longest one */
902         if (count($relevant)){
903           ksort($relevant);
904           $tmp= array_keys($relevant);
905           $idx= end($tmp);
906           $tag= $relevant[$idx];
907           $this->gosaUnitTag= $tag;
908         }
909       }
910     }
913     /* Set tag? */
914     if ($tag != ""){
915       /* Set objectclass and attribute */
916       $ldap= $this->config->get_ldap_link();
917       $ldap->cat($dn, array('gosaUnitTag', 'objectClass'));
918       $attrs= $ldap->fetch();
919       if(isset($attrs['gosaUnitTag'][0]) && $attrs['gosaUnitTag'][0] == $tag){
920         if ($show) {
921           echo sprintf(_("Object '%s' is already tagged"), @LDAP::fix($dn))."<br>";
922           flush();
923         }
924         return;
925       }
926       if (count($attrs)){
927         if ($show){
928           echo sprintf(_("Adding tag (%s) to object '%s'"), $tag, @LDAP::fix($dn))."<br>";
929           flush();
930         }
931         $nattrs= array("gosaUnitTag" => $this->gosaUnitTag);
932         $nattrs['objectClass']= array();
933         for ($i= 0; $i<$attrs['objectClass']['count']; $i++){
934           $oc= $attrs['objectClass'][$i];
935           if ($oc != "gosaAdministrativeUnitTag"){
936             $nattrs['objectClass'][]= $oc;
937           }
938         }
939         $nattrs['objectClass'][]= "gosaAdministrativeUnitTag";
940         $ldap->cd($dn);
941         $ldap->modify($nattrs);
942         show_ldap_error($ldap->get_error(), _("Handle object tagging failed"));
943       } else {
944         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "Not tagging ($tag) $dn - seems to have moved away", "Tagging");
945       }
947     } else {
948       /* Remove objectclass and attribute */
949       $ldap= $this->config->get_ldap_link();
950       $ldap->cat($dn, array('gosaUnitTag', 'objectClass'));
951       $attrs= $ldap->fetch();
952       if (isset($attrs['objectClass']) && !in_array_ics("gosaAdministrativeUnitTag", $attrs['objectClass'])){
953         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "$dn is not tagged", "Tagging");
954         return;
955       }
956       if (count($attrs)){
957         if ($show){
958           echo sprintf(_("Removing tag from object '%s'"), @LDAP::fix($dn))."<br>";
959           flush();
960         }
961         $nattrs= array("gosaUnitTag" => array());
962         $nattrs['objectClass']= array();
963         for ($i= 0; $i<$attrs['objectClass']['count']; $i++){
964           $oc= $attrs['objectClass'][$i];
965           if ($oc != "gosaAdministrativeUnitTag"){
966             $nattrs['objectClass'][]= $oc;
967           }
968         }
969         $ldap->cd($dn);
970         $ldap->modify($nattrs);
971         show_ldap_error($ldap->get_error(), _("Handle object tagging failed"));
972       } else {
973         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "Not removing tag ($tag) $dn - seems to have moved away", "Tagging");
974       }
975     }
977   }
980   /* Add possibility to stop remove process */
981   function allow_remove()
982   {
983     $reason= "";
984     return $reason;
985   }
988   /* Create a snapshot of the current object */
989   function create_snapshot($type= "snapshot", $description= array())
990   {
992     /* Check if snapshot functionality is enabled */
993     if(!$this->snapshotEnabled()){
994       return;
995     }
997     /* Get configuration from gosa.conf */
998     $tmp = $this->config->current;
999     /* Check if the undo level is specified */
1000     if(isset($tmp['SNAPSHOT_UNDO_LEVEL'])){      
1001       $UndoLvl   = $tmp['SNAPSHOT_UNDO_LEVEL'];
1002     }else{
1003       $UndoLvl   = 5;
1004     }
1006     /* Create lokal ldap connection */
1007     $ldap= $this->config->get_ldap_link();
1008     $ldap->cd($this->config->current['BASE']);
1010     /* check if there are special server configurations for snapshots */
1011     if(!isset($tmp['SNAPSHOT_SERVER'])){
1013       /* Source and destination server are both the same, just copy source to dest obj */
1014       $ldap_to      = $ldap;
1015       $snapldapbase = $this->config->current['BASE'];
1017     }else{
1018       $server         = $tmp['SNAPSHOT_SERVER'];
1019       $user           = $tmp['SNAPSHOT_USER'];
1020       $password       = $tmp['SNAPSHOT_PASSWORD'];
1021       $snapldapbase   = $tmp['SNAPSHOT_LDAP_BASE'];
1023       $ldap_to        = new LDAP($user,$password, $server);
1024       $ldap_to -> cd($snapldapbase);
1025       show_ldap_error($ldap_to->get_error(), _("Snapshot failed."));
1026     }
1029     /* check if the dn exists */ 
1030     if ($ldap->dn_exists($this->dn)){
1032       /* Extract seconds & mysecs, they are used as entry index */
1033       list($usec, $sec)= explode(" ", microtime());
1035       /* Collect some infos */
1036       $base           = $this->config->current['BASE'];
1037       $snap_base      = $tmp['SNAPSHOT_BASE'];
1038       $base_of_object = preg_replace ('/^[^,]+,/i', '', $this->dn);
1039       $new_base       = preg_replace("/".normalizePreg($base)."$/","",$base_of_object).$snap_base;
1041       /* Create object */
1042 #$data             = preg_replace('/^dn:.*\n/', '', $ldap->gen_ldif($this->dn,"(!(objectClass=gosaDepartment))"));
1043       $data             = $ldap->gen_ldif($this->dn,"(&(!(objectClass=gosaDepartment))(!(objectClass=FAIclass)))");
1044       $newName          = preg_replace("/\./", "", $sec."-".$usec);
1045       $target= array();
1046       $target['objectClass']            = array("top", "gosaSnapshotObject");
1047       $target['gosaSnapshotData']       = gzcompress($data, 6);
1048       $target['gosaSnapshotType']       = $type;
1049       $target['gosaSnapshotDN']         = $this->dn;
1050       $target['description']            = $description;
1051       $target['gosaSnapshotTimestamp']  = $newName;
1053       /* Insert the new snapshot 
1054          But we have to check first, if the given gosaSnapshotTimestamp
1055          is already used, in this case we should increment this value till there is 
1056          an unused value. */ 
1057       $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1058       $ldap_to->cat($new_dn);
1059       while($ldap_to->count()){
1060         $ldap_to->cat($new_dn);
1061         $newName = preg_replace("/\./", "", $sec."-".($usec++));
1062         $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1063         $target['gosaSnapshotTimestamp']  = $newName;
1064       } 
1066       /* Inset this new snapshot */
1067       $ldap_to->cd($snapldapbase);
1068       $ldap_to->create_missing_trees($new_base);
1069       $ldap_to->cd($new_dn);
1070       $ldap_to->add($target);
1072       show_ldap_error($ldap_to->get_error(), _("Create snapshot failed."));
1073       show_ldap_error($ldap->get_error(), _("Create snapshot failed."));
1075       /* Check amount of used snapshots, and remove old ones if necessary */
1076       $test = $this->Available_SnapsShots($this->dn,true);
1077       if(count($test) > $UndoLvl){
1078         $toDel = array();
1079         foreach($test as $entry){
1080           $toDel[preg_replace("/-/","",$entry['gosaSnapshotTimestamp'][0])] = $entry['dn'];
1081         }
1082         krsort($toDel);
1083         $i = 0 ; 
1084         foreach($toDel as $entryID => $entry){
1085           $i ++ ; 
1086           if($i > $UndoLvl){
1087             $ldap_to->rmdir_recursive($entry);
1088           }
1089         }
1090       }
1091     }
1092   }
1095   /* returns true if snapshots are enabled, and false if it is disalbed
1096      There will also be some errors psoted, if the configuration failed */
1097   function snapshotEnabled()
1098   {
1099     $tmp = $this->config->current;
1100     if(isset($tmp['ENABLE_SNAPSHOT'])){
1101       if (preg_match("/^true$/i", $tmp['ENABLE_SNAPSHOT']) || preg_match("/yes/i", $tmp['ENABLE_SNAPSHOT'])){
1103         /* Check if the snapshot_base is defined */
1104         if(!isset($tmp['SNAPSHOT_BASE'])){
1105           print_red(sprintf(_("The snapshot functionality is enabled, but the required variable '%s' is not configured in your gosa.conf."),$missing));
1106           return(FALSE);
1107         }
1109         /* check if there are special server configurations for snapshots */
1110         if(isset($tmp['SNAPSHOT_SERVER'])){
1112           /* check if all required vars are available to create a new ldap connection */
1113           $missing = "";
1114           foreach(array("SNAPSHOT_SERVER","SNAPSHOT_USER","SNAPSHOT_PASSWORD","SNAPSHOT_LDAP_BASE") as $var){
1115             if(!isset($tmp[$var])){
1116               $missing .= $var." ";
1117               print_red(sprintf(_("The snapshot functionality is enabled, but the required variable(s) '%s' is not configured in your gosa.conf."),$missing));
1118               return(FALSE);
1119             }
1120           }
1121         }
1122         return(TRUE);
1123       }
1124     }
1125     return(FALSE);
1126   }
1129   /* Return available snapshots for the given base 
1130    */
1131   function Available_SnapsShots($dn,$raw = false)
1132   {
1133     if(!$this->snapshotEnabled()) return(array());
1135     /* Create an additional ldap object which
1136        points to our ldap snapshot server */
1137     $ldap= $this->config->get_ldap_link();
1138     $ldap->cd($this->config->current['BASE']);
1139     $tmp = $this->config->current;
1141     /* check if there are special server configurations for snapshots */
1142     if(isset($tmp['SNAPSHOT_SERVER'])){
1143       $server       = $tmp['SNAPSHOT_SERVER'];
1144       $user         = $tmp['SNAPSHOT_USER'];
1145       $password     = $tmp['SNAPSHOT_PASSWORD'];
1146       $snapldapbase = $tmp['SNAPSHOT_LDAP_BASE'];
1147       $ldap_to      = new LDAP($user,$password, $server);
1148       $ldap_to -> cd ($snapldapbase);
1149       show_ldap_error($ldap_to->get_error(), _("Snapshot failed."));
1150     }else{
1151       $ldap_to    = $ldap;
1152     }
1154     /* Prepare bases and some other infos */
1155     $base           = $this->config->current['BASE'];
1156     $snap_base      = $tmp['SNAPSHOT_BASE'];
1157     $base_of_object = preg_replace ('/^[^,]+,/i', '', $dn);
1158     $new_base       = preg_replace("/".normalizePreg($base)."$/","",$base_of_object).$snap_base;
1159     $tmp            = array(); 
1161     /* Fetch all objects with  gosaSnapshotDN=$dn */
1162     $ldap_to->cd($new_base);
1163     $ldap_to->ls("(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN=".$dn."))",$new_base,
1164         array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description")); 
1166     /* Put results into a list and add description if missing */
1167     while($entry = $ldap_to->fetch()){ 
1168       if(!isset($entry['description'][0])){
1169         $entry['description'][0]  = "";
1170       }
1171       $tmp[] = $entry; 
1172     }
1174     /* Return the raw array, or format the result */
1175     if($raw){
1176       return($tmp);
1177     }else{  
1178       $tmp2 = array();
1179       foreach($tmp as $entry){
1180         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1181       }
1182     }
1183     return($tmp2);
1184   }
1187   function getAllDeletedSnapshots($base_of_object,$raw = false)
1188   {
1189     if(!$this->snapshotEnabled()) return(array());
1191     /* Create an additional ldap object which
1192        points to our ldap snapshot server */
1193     $ldap= $this->config->get_ldap_link();
1194     $ldap->cd($this->config->current['BASE']);
1195     $tmp = $this->config->current;
1197     /* check if there are special server configurations for snapshots */
1198     if(isset($tmp['SNAPSHOT_SERVER'])){
1199       $server       = $tmp['SNAPSHOT_SERVER'];
1200       $user         = $tmp['SNAPSHOT_USER'];
1201       $password     = $tmp['SNAPSHOT_PASSWORD'];
1202       $snapldapbase = $tmp['SNAPSHOT_LDAP_BASE'];
1203       $ldap_to      = new LDAP($user,$password, $server);
1204       $ldap_to->cd ($snapldapbase);
1205       show_ldap_error($ldap_to->get_error(), _("Snapshot failed."));
1206     }else{
1207       $ldap_to    = $ldap;
1208     }
1210     /* Prepare bases */ 
1211     $base           = $this->config->current['BASE'];
1212     $snap_base      = $tmp['SNAPSHOT_BASE'];
1213     $new_base       = preg_replace("/".normalizePreg($base)."$/","",$base_of_object).$snap_base;
1215     /* Fetch all objects and check if they do not exist anymore */
1216     $ui = get_userinfo();
1217     $tmp = array();
1218     $ldap_to->cd($new_base);
1219     $ldap_to->ls("(objectClass=gosaSnapshotObject)",$new_base,array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description"));
1220     while($entry = $ldap_to->fetch()){
1222       $chk =  str_replace($new_base,"",$entry['dn']);
1223       if(preg_match("/,ou=/",$chk)) continue;
1225       if(!isset($entry['description'][0])){
1226         $entry['description'][0]  = "";
1227       }
1228       $tmp[] = $entry; 
1229     }
1231     /* Check if entry still exists */
1232     foreach($tmp as $key => $entry){
1233       $ldap->cat($entry['gosaSnapshotDN'][0]);
1234       if($ldap->count()){
1235         unset($tmp[$key]);
1236       }
1237     }
1239     /* Format result as requested */
1240     if($raw) {
1241       return($tmp);
1242     }else{
1243       $tmp2 = array();
1244       foreach($tmp as $key => $entry){
1245         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1246       }
1247     }
1248     return($tmp2);
1249   } 
1252   /* Restore selected snapshot */
1253   function restore_snapshot($dn)
1254   {
1255     if(!$this->snapshotEnabled()) return(array());
1257     $ldap= $this->config->get_ldap_link();
1258     $ldap->cd($this->config->current['BASE']);
1259     $tmp = $this->config->current;
1261     /* check if there are special server configurations for snapshots */
1262     if(isset($tmp['SNAPSHOT_SERVER'])){
1263       $server       = $tmp['SNAPSHOT_SERVER'];
1264       $user         = $tmp['SNAPSHOT_USER'];
1265       $password     = $tmp['SNAPSHOT_PASSWORD'];
1266       $snapldapbase = $tmp['SNAPSHOT_LDAP_BASE'];
1267       $ldap_to      = new LDAP($user,$password, $server);
1268       $ldap_to->cd ($snapldapbase);
1269       show_ldap_error($ldap_to->get_error(), _("Snapshot failed."));
1270     }else{
1271       $ldap_to    = $ldap;
1272     }
1274     /* Get the snapshot */ 
1275     $ldap_to->cat($dn);
1276     $restoreObject = $ldap_to->fetch();
1278     /* Prepare import string */
1279     $data  = gzuncompress($ldap_to->get_attribute($dn,'gosaSnapshotData'));
1281     /* Import the given data */
1282     $ldap->import_complete_ldif($data,$err,false,false);
1283     show_ldap_error($ldap_to->get_error().$err, _("Restore snapshot failed."));
1284   }
1287   function showSnapshotDialog($base,$baseSuffixe)
1288   {
1289     $once = true;
1290     foreach($_POST as $name => $value){
1292       /* Create a new snapshot, display a dialog */
1293       if(preg_match("/^CreateSnapShot_/",$name) && $once){
1294         $once = false;
1295         $entry = preg_replace("/^CreateSnapShot_/","",$name);
1296         $entry = base64_decode(preg_replace("/_[xy]$/","",$entry));
1297         $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1298       }
1300       /* Restore a snapshot, display a dialog with all snapshots of the current object */
1301       if(preg_match("/^RestoreSnapShot_/",$name) && $once){
1302         $once = false;
1303         $entry = preg_replace("/^RestoreSnapShot_/","",$name);
1304         $entry = base64_decode(preg_replace("/_[xy]$/","",$entry));
1305         $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1306         $this->snapDialog->Restore = true;
1307       }
1309       /* Restore one of the already deleted objects */
1310       if(preg_match("/^RestoreDeletedSnapShot_/",$name) && $once){
1311         $once = false;
1312         $this->snapDialog = new SnapShotDialog($this->config,$baseSuffixe,$this);
1313         $this->snapDialog->Restore      = true;
1314         $this->snapDialog->DeletedOnes  = true;
1315       }
1316     }
1318     /* Create a new snapshot requested, check
1319        the given attributes and create the snapshot*/
1320     if(isset($_POST['CreateSnapshot'])){
1321       $this->snapDialog->save_object();
1322       $msgs = $this->snapDialog->check();
1323       if(count($msgs)){
1324         foreach($msgs as $msg){
1325           print_red($msg);
1326         }
1327       }else{
1328         $this->dn =  $this->snapDialog->dn;
1329         $this->create_snapshot("snapshot",$this->snapDialog->CurrentDescription);
1330         $this->snapDialog = NULL;
1331       }
1332     }
1334     /* Restore is requested, restore the object with the posted dn .*/
1335     if((isset($_POST['RestoreSnapshot'])) && (isset($_POST['SnapShot']))){
1336       $entry =trim($_POST['SnapShot']);
1337       if(!empty($entry)){
1338         $entry = base64_decode($entry);
1339         $this->restore_snapshot($entry);
1340         $this->snapDialog = NULL;
1341       }
1342     }
1344     if(isset($_POST['CancelSnapshot'])){
1345       $this->snapDialog = NULL;
1346     }
1348     if($this->snapDialog){
1349       $this->snapDialog->save_object();
1350       return($this->snapDialog->execute());
1351     }
1352   }
1354 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1355 ?>