Code

6bef2744e5f3902ae0f940e7667516d17822b4f0
[gosa.git] / gosa-core / include / class_plugin.inc
1 <?php
2 /*
3  * This code is part of GOsa (http://www.gosa-project.org)
4  * Copyright (C) 2003-2008 GONICUS GmbH
5  *
6  * ID: $$Id$$
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
23 /*! \brief   The plugin base class
24   \author  Cajus Pollmeier <pollmeier@gonicus.de>
25   \version 2.00
26   \date    24.07.2003
28   This is the base class for all plugins. It can be used standalone or
29   can be included by the tabs class. All management should be done 
30   within this class. Extend your plugins from this class.
31  */
33 class plugin
34 {
35   /*!
36     \brief Reference to parent object
38     This variable is used when the plugin is included in tabs
39     and keeps reference to the tab class. Communication to other
40     tabs is possible by 'name'. So the 'fax' plugin can ask the
41     'userinfo' plugin for the fax number.
43     \sa tab
44    */
45   var $parent= NULL;
47   /*!
48     \brief Configuration container
50     Access to global configuration
51    */
52   var $config= NULL;
54   /*!
55     \brief Mark plugin as account
57     Defines whether this plugin is defined as an account or not.
58     This has consequences for the plugin to be saved from tab
59     mode. If it is set to 'FALSE' the tab will call the delete
60     function, else the save function. Should be set to 'TRUE' if
61     the construtor detects a valid LDAP object.
63     \sa plugin::plugin()
64    */
65   var $is_account= FALSE;
66   var $initially_was_account= FALSE;
68   /*!
69     \brief Mark plugin as template
71     Defines whether we are creating a template or a normal object.
72     Has conseqences on the way execute() shows the formular and how
73     save() puts the data to LDAP.
75     \sa plugin::save() plugin::execute()
76    */
77   var $is_template= FALSE;
78   var $ignore_account= FALSE;
79   var $is_modified= FALSE;
81   /*!
82     \brief Represent temporary LDAP data
84     This is only used internally.
85    */
86   var $attrs= array();
88   /* Keep set of conflicting plugins */
89   var $conflicts= array();
91   /* Save unit tags */
92   var $gosaUnitTag= "";
93   var $skipTagging= FALSE;
95   /*!
96     \brief Used standard values
98     dn
99    */
100   var $dn= "";
101   var $uid= "";
102   var $sn= "";
103   var $givenName= "";
104   var $acl= "*none*";
105   var $dialog= FALSE;
106   var $snapDialog = NULL;
108   /* attribute list for save action */
109   var $attributes= array();
110   var $objectclasses= array();
111   var $is_new= TRUE;
112   var $saved_attributes= array();
114   var $acl_base= "";
115   var $acl_category= "";
116   var $read_only = FALSE; // Used when the entry is opened as "readonly" due to locks.
118   /* This can be set to render the tabulators in another stylesheet */
119   var $pl_notify= FALSE;
121   /* Object entry CSN */
122   var $entryCSN         = "";
123   var $CSN_check_active = FALSE;
125   /* This variable indicates that this class can handle multiple dns at once. */
126   var $multiple_support = FALSE;
127   var $multi_attrs      = array();
128   var $multi_attrs_all  = array(); 
130   /* This aviable indicates, that we are currently in multiple edit handle */
131   var $multiple_support_active = FALSE; 
132   var $selected_edit_values = array();
133   var $multi_boxes = array();
135   /*! \brief plugin constructor
137     If 'dn' is set, the node loads the given 'dn' from LDAP
139     \param dn Distinguished name to initialize plugin from
140     \sa plugin()
141    */
142   function plugin (&$config, $dn= NULL, $object= NULL)
143   {
144     /* Configuration is fine, allways */
145     $this->config= &$config;    
146     $this->dn= $dn;
148     // Ensure that we've a valid acl_category set.
149     if(empty($this->acl_category)){
150       $tmp = $this->plInfo();
151       $c = key($tmp['plCategory']);
152       if(is_numeric($c)){
153         $c = $tmp['plCategory'][0];
154       }
155       $this->acl_category = $c."/";
156     }
158     /* Handle new accounts, don't read information from LDAP */
159     if ($dn == "new"){
160       return;
161     }
163     /* Check if this entry was opened in read only mode */
164     if(isset($_POST['open_readonly'])){
165       if(session::global_is_set("LOCK_CACHE")){
166         $cache = &session::get("LOCK_CACHE");
167         if(isset($cache['READ_ONLY'][$this->dn])){
168           $this->read_only = TRUE;
169         }
170       }
171     }
173     /* Save current dn as acl_base */
174     $this->acl_base= $dn;
176     /* Get LDAP descriptor */
177     if ($dn !== NULL){
179       /* Load data to 'attrs' and save 'dn' */
180       if ($object !== NULL){
181         $this->attrs= $object->attrs;
182       } else {
183         $ldap= $this->config->get_ldap_link();
184         $ldap->cat ($dn);
185         $this->attrs= $ldap->fetch();
186       }
188       /* Copy needed attributes */
189       foreach ($this->attributes as $val){
190         $found= array_key_ics($val, $this->attrs);
191         if ($found != ""){
192           $this->$val= $found[0];
193         }
194       }
196       /* gosaUnitTag loading... */
197       if (isset($this->attrs['gosaUnitTag'][0])){
198         $this->gosaUnitTag= $this->attrs['gosaUnitTag'][0];
199       }
201       /* Set the template flag according to the existence of objectClass
202          gosaUserTemplate */
203       if (isset($this->attrs['objectClass'])){
204         if (in_array_ics ("gosaUserTemplate", $this->attrs['objectClass'])){
205           $this->is_template= TRUE;
206           @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
207               "found", "Template check");
208         }
209       }
211       /* Is Account? */
212       $found= TRUE;
213       foreach ($this->objectclasses as $obj){
214         if (preg_match('/top/i', $obj)){
215           continue;
216         }
217         if (!isset($this->attrs['objectClass']) || !in_array_ics ($obj, $this->attrs['objectClass'])){
218           $found= FALSE;
219           break;
220         }
221       }
222       if ($found){
223         $this->is_account= TRUE;
224         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
225             "found", "Object check");
226       }
228       /* Prepare saved attributes */
229       $this->saved_attributes= $this->attrs;
230       foreach ($this->saved_attributes as $index => $value){
231         if (is_numeric($index)){
232           unset($this->saved_attributes[$index]);
233           continue;
234         }
236         if (!in_array_ics($index, $this->attributes) && strcasecmp('objectClass', $index)){
237           unset($this->saved_attributes[$index]);
238           continue;
239         }
241         if (isset($this->saved_attributes[$index][0])){
242           if(!isset($this->saved_attributes[$index]["count"])){
243             $this->saved_attributes[$index]["count"] = count($this->saved_attributes[$index]);
244           }
245           if($this->saved_attributes[$index]["count"] == 1){
246             $tmp= $this->saved_attributes[$index][0];
247             unset($this->saved_attributes[$index]);
248             $this->saved_attributes[$index]= $tmp;
249             continue;
250           }
251         }
252         unset($this->saved_attributes["$index"]["count"]);
253       }
255       if(isset($this->attrs['gosaUnitTag'])){
256         $this->saved_attributes['gosaUnitTag'] = $this->attrs['gosaUnitTag'][0];
257       }
258     }
260     /* Save initial account state */
261     $this->initially_was_account= $this->is_account;
262   }
265   /*! \brief Generates the html output for this node
266    */
267   function execute()
268   {
269     /* This one is empty currently. Fabian - please fill in the docu code */
270     session::global_set('current_class_for_help',get_class($this));
272     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
273     session::set('LOCK_VARS_TO_USE',array());
274     session::set('LOCK_VARS_USED_GET',array());
275     session::set('LOCK_VARS_USED_POST',array());
276     session::set('LOCK_VARS_USED_REQUEST',array());
277   }
279   /*! \brief Removes object from parent
280    */
281   function remove_from_parent()
282   {
283     /* include global link_info */
284     $ldap= $this->config->get_ldap_link();
286     /* Get current objectClasses in order to add the required ones */
287     $ldap->cat($this->dn);
288     $tmp= $ldap->fetch ();
289     $oc= array();
290     if (isset($tmp['objectClass'])){
291       $oc= $tmp['objectClass'];
292       unset($oc['count']);
293     }
295     /* Remove objectClasses from entry */
296     $ldap->cd($this->dn);
297     $this->attrs= array();
298     $this->attrs['objectClass']= array_remove_entries_ics($this->objectclasses,$oc);
300     /* Unset attributes from entry */
301     foreach ($this->attributes as $val){
302       $this->attrs["$val"]= array();
303     }
305     /* Unset account info */
306     $this->is_account= FALSE;
308     /* Do not write in plugin base class, this must be done by
309        children, since there are normally additional attribs,
310        lists, etc. */
311     /*
312        $ldap->modify($this->attrs);
313      */
314   }
317   /*! \brief Save HTML posted data to object 
318    */
319   function save_object()
320   {
321     /* Update entry CSN if it is empty. */
322     if(empty($this->entryCSN) && $this->CSN_check_active){
323       $this->entryCSN = getEntryCSN($this->dn);
324     }
326     /* Save values to object */
327     foreach ($this->attributes as $val){
328       if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
329         /* Check for modifications */
330         if (get_magic_quotes_gpc()) {
331           $data= stripcslashes($_POST["$val"]);
332         } else {
333           $data= $this->$val = $_POST["$val"];
334         }
335         if ($this->$val != $data){
336           $this->is_modified= TRUE;
337         }
338     
339         /* Okay, how can I explain this fix ... 
340          * In firefox, disabled option fields aren't selectable ... but in IE you can select these fileds. 
341          * So IE posts these 'unselectable' option, with value = chr(194) 
342          * chr(194) seems to be the &nbsp; in between the ...option>&nbsp;</option.. because there is no value=".." specified in these option fields  
343          * This &nbsp; was added for W3c compliance, but now causes these ... ldap errors ... 
344          * So we set these Fields to ""; a normal empty string, and we can check these values in plugin::check() again ...
345          */
346         if(isset($data[0]) && $data[0] == chr(194)) {
347           $data = "";  
348         }
349         $this->$val= $data;
350       }
351     }
352   }
355   /*! \brief Save data to LDAP, depending on is_account we save or delete */
356   function save()
357   {
358     /* include global link_info */
359     $ldap= $this->config->get_ldap_link();
361     /* Save all plugins */
362     $this->entryCSN = "";
364     /* Start with empty array */
365     $this->attrs= array();
367     /* Get current objectClasses in order to add the required ones */
368     $ldap->cat($this->dn);
369     
370     $tmp= $ldap->fetch ();
372     $oc= array();
373     if (isset($tmp['objectClass'])){
374       $oc= $tmp["objectClass"];
375       $this->is_new= FALSE;
376       unset($oc['count']);
377     } else {
378       $this->is_new= TRUE;
379     }
381     /* Load (minimum) attributes, add missing ones */
382     $this->attrs['objectClass']= gosa_array_merge($oc,$this->objectclasses);
384     /* Copy standard attributes */
385     foreach ($this->attributes as $val){
386       if ($this->$val != ""){
387         $this->attrs["$val"]= $this->$val;
388       } elseif (!$this->is_new) {
389         $this->attrs["$val"]= array();
390       }
391     }
393     /* Handle tagging */
394     $this->tag_attrs($this->attrs);
395   }
398   function cleanup()
399   {
400     foreach ($this->attrs as $index => $value){
401       
402       /* Convert arrays with one element to non arrays, if the saved
403          attributes are no array, too */
404       if (is_array($this->attrs[$index]) && 
405           count ($this->attrs[$index]) == 1 &&
406           isset($this->saved_attributes[$index]) &&
407           !is_array($this->saved_attributes[$index])){
408           
409         $tmp= $this->attrs[$index][0];
410         $this->attrs[$index]= $tmp;
411       }
413       /* Remove emtpy arrays if they do not differ */
414       if (is_array($this->attrs[$index]) &&
415           count($this->attrs[$index]) == 0 &&
416           !isset($this->saved_attributes[$index])){
417           
418         unset ($this->attrs[$index]);
419         continue;
420       }
422       /* Remove single attributes that do not differ */
423       if (!is_array($this->attrs[$index]) &&
424           isset($this->saved_attributes[$index]) &&
425           !is_array($this->saved_attributes[$index]) &&
426           $this->attrs[$index] == $this->saved_attributes[$index]){
428         unset ($this->attrs[$index]);
429         continue;
430       }
432       /* Remove arrays that do not differ */
433       if (is_array($this->attrs[$index]) && 
434           isset($this->saved_attributes[$index]) &&
435           is_array($this->saved_attributes[$index])){
436           
437         if (!array_differs($this->attrs[$index],$this->saved_attributes[$index])){
438           unset ($this->attrs[$index]);
439           continue;
440         }
441       }
442     }
444     /* Update saved attributes and ensure that next cleanups will be successful too */
445     foreach($this->attrs as $name => $value){
446       $this->saved_attributes[$name] = $value;
447     }
448   }
450   /*! \brief Check formular input */
451   function check()
452   {
453     $message= array();
455     /* Skip if we've no config object */
456     if (!isset($this->config) || !is_object($this->config)){
457       return $message;
458     }
460     /* Find hooks entries for this class */
461     $command= $this->config->search(get_class($this), "CHECK", array('menu', 'tabs'));
463     if ($command != ""){
465       if (!check_command($command)){
466         $message[]= msgPool::cmdnotfound("CHECK", get_class($this));
467       } else {
469         /* Generate "ldif" for check hook */
470         $ldif= "dn: $this->dn\n";
471         
472         /* ... objectClasses */
473         foreach ($this->objectclasses as $oc){
474           $ldif.= "objectClass: $oc\n";
475         }
476         
477         /* ... attributes */
478         foreach ($this->attributes as $attr){
479           if ($this->$attr == ""){
480             continue;
481           }
482           if (is_array($this->$attr)){
483             foreach ($this->$attr as $val){
484               $ldif.= "$attr: $val\n";
485             }
486           } else {
487               $ldif.= "$attr: ".$this->$attr."\n";
488           }
489         }
491         /* Append empty line */
492         $ldif.= "\n";
494         /* Feed "ldif" into hook and retrieve result*/
495         $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
496         $fh= proc_open($command, $descriptorspec, $pipes);
497         if (is_resource($fh)) {
498           fwrite ($pipes[0], $ldif);
499           fclose($pipes[0]);
500           
501           $result= stream_get_contents($pipes[1]);
502           if ($result != ""){
503             $message[]= $result;
504           }
505           
506           fclose($pipes[1]);
507           fclose($pipes[2]);
508           proc_close($fh);
509         }
510       }
512     }
514     /* Check entryCSN */
515     if($this->CSN_check_active){
516       $current_csn = getEntryCSN($this->dn);
517       if($current_csn != $this->entryCSN && !empty($this->entryCSN) && !empty($current_csn)){
518         $this->entryCSN = $current_csn;
519         $message[] = _("The object has changed since opened in GOsa. All changes that may be done by others get lost if you save this entry!");
520       }
521     }
522     return ($message);
523   }
525   /* Adapt from template, using 'dn' */
526   function adapt_from_template($dn, $skip= array())
527   {
528     /* Include global link_info */
529     $ldap= $this->config->get_ldap_link();
531     /* Load requested 'dn' to 'attrs' */
532     $ldap->cat ($dn);
533     $this->attrs= $ldap->fetch();
535     /* Walk through attributes */
536     foreach ($this->attributes as $val){
538       /* Skip the ones in skip list */
539       if (in_array($val, $skip)){
540         continue;
541       }
543       if (isset($this->attrs["$val"][0])){
545         /* If attribute is set, replace dynamic parts: 
546            %sn, %givenName and %uid. Fill these in our local variables. */
547         $value= $this->attrs["$val"][0];
549         foreach (array("sn", "givenName", "uid") as $repl){
550           if (preg_match("/%$repl/i", $value)){
551             $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
552           }
553         }
554         $this->$val= $value;
555       }
556     }
558     /* Is Account? */
559     $found= TRUE;
560     foreach ($this->objectclasses as $obj){
561       if (preg_match('/top/i', $obj)){
562         continue;
563       }
564       if (!in_array_ics ($obj, $this->attrs['objectClass'])){
565         $found= FALSE;
566         break;
567       }
568     }
569     if ($found){
570       $this->is_account= TRUE;
571     }
572   }
574   /* \brief Indicate whether a password change is needed or not */
575   function password_change_needed()
576   {
577     return FALSE;
578   }
581   /*! \brief Show header message for tab dialogs */
582   function show_enable_header($button_text, $text, $disabled= FALSE)
583   {
584     if (($disabled == TRUE) || (!$this->acl_is_createable())){
585       $state= "disabled";
586     } else {
587       $state= "";
588     }
589     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
590     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".$state.
591       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
593     return($display);
594   }
597   /*! \brief Show header message for tab dialogs */
598   function show_disable_header($button_text, $text, $disabled= FALSE)
599   {
600     if (($disabled == TRUE) || !$this->acl_is_removeable()){
601       $state= "disabled";
602     } else {
603       $state= "";
604     }
605     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
606     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".$state.
607       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
609     return($display);
610   }
613   /*! \brief Show header message for tab dialogs */
614   function show_header($button_text, $text, $disabled= FALSE)
615   {
616     echo "FIXME: show_header should be replaced by show_disable_header and show_enable_header<br>";
617     if ($disabled == TRUE){
618       $state= "disabled";
619     } else {
620       $state= "";
621     }
622     $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
623     $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".
624       ($this->acl_is_createable()?'':'disabled')." ".$state.
625       "><p class=\"seperator\">&nbsp;</p></td></tr></table>";
627     return($display);
628   }
630   /*! \brief Executes commands after an object has been created */
631   function postcreate($add_attrs= array())
632   {
633     /* Find postcreate entries for this class */
634     $command= $this->config->search(get_class($this), "POSTCREATE",array('menu', 'tabs'));
636     if ($command != ""){
638       /* Walk through attribute list */
639       foreach ($this->attributes as $attr){
640         if (!is_array($this->$attr)){
641           $add_attrs[$attr] = $this->$attr;
642         }
643       }
644       $add_attrs['dn']=$this->dn;
646       $tmp = array();
647       foreach($add_attrs as $name => $value){
648         $tmp[$name] =  strlen($name);
649       }
650       arsort($tmp);
651       
652       /* Additional attributes */
653       foreach ($tmp as $name => $len){
654         $value = $add_attrs[$name];
655         $command= str_replace("%$name", "$value", $command);
656       }
658       if (check_command($command)){
659         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
660             $command, "Execute");
661         exec($command,$arr);
662         foreach($arr as $str){
663           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
664             $command, "Result: ".$str);
665         }
666       } else {
667         $message= msgPool::cmdnotfound("POSTCREATE", get_class($this));
668         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
669       }
670     }
671   }
673   /*! \brief Execute commands after an object has been modified */
674   function postmodify($add_attrs= array())
675   {
676     /* Find postcreate entries for this class */
677     $command= $this->config->search(get_class($this), "POSTMODIFY",array('menu','tabs'));
679     if ($command != ""){
681       /* Walk through attribute list */
682       foreach ($this->attributes as $attr){
683         if (!is_array($this->$attr)){
684           $add_attrs[$attr] = $this->$attr;
685         }
686       }
687       $add_attrs['dn']=$this->dn;
689       $tmp = array();
690       foreach($add_attrs as $name => $value){
691         $tmp[$name] =  strlen($name);
692       }
693       arsort($tmp);
694       
695       /* Additional attributes */
696       foreach ($tmp as $name => $len){
697         $value = $add_attrs[$name];
698         $command= str_replace("%$name", "$value", $command);
699       }
701       if (check_command($command)){
702         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command, "Execute");
703         exec($command,$arr);
704         foreach($arr as $str){
705           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
706             $command, "Result: ".$str);
707         }
708       } else {
709         $message= msgPool::cmdnotfound("POSTMODIFY", get_class($this));
710         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
711       }
712     }
713   }
715   /*! \brief Executes a command after an object has been removed */
716   function postremove($add_attrs= array())
717   {
718     /* Find postremove entries for this class */
719     $command= $this->config->search(get_class($this), "POSTREMOVE",array('menu','tabs'));
720     if ($command != ""){
722       /* Walk through attribute list */
723       foreach ($this->attributes as $attr){
724         if (!is_array($this->$attr)){
725           $add_attrs[$attr] = $this->$attr;
726         }
727       }
728       $add_attrs['dn']=$this->dn;
730       $tmp = array();
731       foreach($add_attrs as $name => $value){
732         $tmp[$name] =  strlen($name);
733       }
734       arsort($tmp);
735       
736       /* Additional attributes */
737       foreach ($tmp as $name => $len){
738         $value = $add_attrs[$name];
739         $command= str_replace("%$name", "$value", $command);
740       }
742       if (check_command($command)){
743         @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
744             $command, "Execute");
746         exec($command,$arr);
747         foreach($arr as $str){
748           @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
749             $command, "Result: ".$str);
750         }
751       } else {
752         $message= msgPool::cmdnotfound("POSTREMOVE", get_class($this));
753         msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
754       }
755     }
756   }
759   /* Create unique DN */
760   function create_unique_dn2($data, $base)
761   {
762     $ldap= $this->config->get_ldap_link();
763     $base= preg_replace("/^,*/", "", $base);
765     /* Try to use plain entry first */
766     $dn= "$data,$base";
767     $attribute= preg_replace('/=.*$/', '', $data);
768     $ldap->cat ($dn, array('dn'));
769     if (!$ldap->fetch()){
770       return ($dn);
771     }
773     /* Look for additional attributes */
774     foreach ($this->attributes as $attr){
775       if ($attr == $attribute || $this->$attr == ""){
776         continue;
777       }
779       $dn= "$data+$attr=".$this->$attr.",$base";
780       $ldap->cat ($dn, array('dn'));
781       if (!$ldap->fetch()){
782         return ($dn);
783       }
784     }
786     /* None found */
787     return ("none");
788   }
791   /*! \brief Create unique DN */
792   function create_unique_dn($attribute, $base)
793   {
794     $ldap= $this->config->get_ldap_link();
795     $base= preg_replace("/^,*/", "", $base);
797     /* Try to use plain entry first */
798     $dn= "$attribute=".$this->$attribute.",$base";
799     $ldap->cat ($dn, array('dn'));
800     if (!$ldap->fetch()){
801       return ($dn);
802     }
804     /* Look for additional attributes */
805     foreach ($this->attributes as $attr){
806       if ($attr == $attribute || $this->$attr == ""){
807         continue;
808       }
810       $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
811       $ldap->cat ($dn, array('dn'));
812       if (!$ldap->fetch()){
813         return ($dn);
814       }
815     }
817     /* None found */
818     return ("none");
819   }
822   function rebind($ldap, $referral)
823   {
824     $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
825     if (ldap_bind($ldap, $credentials['ADMIN'], $this->config->get_credentials($credentials['PASSWORD']))) {
826       $this->error = "Success";
827       $this->hascon=true;
828       $this->reconnect= true;
829       return (0);
830     } else {
831       $this->error = "Could not bind to " . $credentials['ADMIN'];
832       return NULL;
833     }
834   }
837   /* Recursively copy ldap object */
838   function _copy($src_dn,$dst_dn)
839   {
840     $ldap=$this->config->get_ldap_link();
841     $ldap->cat($src_dn);
842     $attrs= $ldap->fetch();
844     /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
845     $ds= ldap_connect($this->config->current['SERVER']);
846     ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
847     if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
848       ldap_set_rebind_proc($ds, array(&$this, "rebind"));
849     }
851     $pwd = $this->config->get_credentials($this->config->current['ADMINPASSWORD']);
852     $r=ldap_bind($ds,$this->config->current['ADMINDN'], $pwd);
853     $sr=ldap_read($ds, LDAP::fix($src_dn), "objectClass=*");
855     /* Fill data from LDAP */
856     $new= array();
857     if ($sr) {
858       $ei=ldap_first_entry($ds, $sr);
859       if ($ei) {
860         foreach($attrs as $attr => $val){
861           if ($info = @ldap_get_values_len($ds, $ei, $attr)){
862             for ($i= 0; $i<$info['count']; $i++){
863               if ($info['count'] == 1){
864                 $new[$attr]= $info[$i];
865               } else {
866                 $new[$attr][]= $info[$i];
867               }
868             }
869           }
870         }
871       }
872     }
874     /* close conncetion */
875     ldap_unbind($ds);
877     /* Adapt naming attribute */
878     $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
879     $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
880     $new[$dst_name]= LDAP::fix($dst_val);
882     /* Check if this is a department.
883      * If it is a dep. && there is a , override in his ou 
884      *  change \2C to , again, else this entry can't be saved ...
885      */
886     if((isset($new['ou'])) &&( preg_match("/\\,/",$new['ou']))){
887       $new['ou'] = str_replace("\\\\,",",",$new['ou']);
888     }
890     /* Save copy */
891     $ldap->connect();
892     $ldap->cd($this->config->current['BASE']);
893     
894     $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
896     /* FAIvariable=.../..., cn=.. 
897         could not be saved, because the attribute FAIvariable was different to 
898         the dn FAIvariable=..., cn=... */
900     if(!is_array($new['objectClass'])) $new['objectClass'] = array($new['objectClass']);
902     if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
903       $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
904     }
905     $ldap->cd($dst_dn);
906     $ldap->add($new);
908     if (!$ldap->success()){
909       trigger_error("Trying to save $dst_dn failed.",
910           E_USER_WARNING);
911       return(FALSE);
912     }
913     return(TRUE);
914   }
917   /* This is a workaround function. */
918   function copy($src_dn, $dst_dn)
919   {
920     /* Rename dn in possible object groups */
921     $ldap= $this->config->get_ldap_link();
922     $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.@LDAP::prepare4filter($src_dn).'))',
923         array('cn'));
924     while ($attrs= $ldap->fetch()){
925       $og= new ogroup($this->config, $ldap->getDN());
926       unset($og->member[$src_dn]);
927       $og->member[$dst_dn]= $dst_dn;
928       $og->save ();
929     }
931     $ldap->cat($dst_dn);
932     $attrs= $ldap->fetch();
933     if (count($attrs)){
934       trigger_error("Trying to overwrite ".LDAP::fix($dst_dn).", which already exists.",
935           E_USER_WARNING);
936       return (FALSE);
937     }
939     $ldap->cat($src_dn);
940     $attrs= $ldap->fetch();
941     if (!count($attrs)){
942       trigger_error("Trying to move ".LDAP::fix($src_dn).", which does not seem to exist.",
943           E_USER_WARNING);
944       return (FALSE);
945     }
947     $ldap->cd($src_dn);
948     $ldap->search("objectClass=*",array("dn"));
949     while($attrs = $ldap->fetch()){
950       $src = $attrs['dn'];
951       $dst = preg_replace("/".preg_quote($src_dn, '/')."$/",$dst_dn,$attrs['dn']);
952       $this->_copy($src,$dst);
953     }
954     return (TRUE);
955   }
959   /*! \brief  Rename/Move a given src_dn to the given dest_dn
960    *
961    * Move a given ldap object indentified by $src_dn to the
962    * given destination $dst_dn
963    *
964    * - Ensure that all references are updated (ogroups)
965    * - Update ACLs   
966    * - Update accessTo
967    *
968    * \param  string  'src_dn' the source DN.
969    * \param  string  'dst_dn' the destination DN.
970    * \return boolean TRUE on success else FALSE.
971    */
972   function rename($src_dn, $dst_dn)
973   {
974     $start = microtime(1);
976     /* Try to move the source entry to the destination position */
977     $ldap = $this->config->get_ldap_link();
978     $ldap->cd($this->config->current['BASE']);
979     $ldap->create_missing_trees(preg_replace("/^[^,]+,/","",$dst_dn));
980     if (!$ldap->rename_dn($src_dn,$dst_dn)){
981 #      msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $src_dn, "", get_class()));
982       new log("debug","Ldap Protocol v3 implementation error, ldap_rename failed, falling back to manual copy.","FROM: $src_dn  -- TO: $dst_dn",array(),$ldap->get_error());
983       @DEBUG(DEBUG_LDAP,__LINE__,__FUNCTION__,__FILE__,"Rename failed FROM: $src_dn  -- TO:  $dst_dn", 
984           "Ldap Protocol v3 implementation error, falling back to maunal method.");
985       return(FALSE);
986     }
988     /* Get list of users,groups and roles within this tree,
989         maybe we have to update ACL references.
990      */
991     $leaf_objs = get_list("(|(objectClass=posixGroup)(objectClass=gosaAccount)(objectClass=gosaRole))",array("all"),$dst_dn,
992           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
993     foreach($leaf_objs as $obj){
994       $new_dn = $obj['dn'];
995       $old_dn = preg_replace("/".preg_quote(LDAP::convert($dst_dn), '/')."$/i",$src_dn,LDAP::convert($new_dn));
996       $this->update_acls($old_dn,$new_dn); 
997     }
999     // Migrate objectgroups if needed
1000     $ogroups = get_sub_list("(&(objectClass=gosaGroupOfNames)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))","ogroups", array(get_ou("ogroupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
1002     // Walk through all objectGroups
1003     foreach($ogroups as $ogroup){
1004       // Migrate old to new dn
1005       $o_ogroup= new ogroup($this->config,$ogroup['dn']);
1006       if (isset($o_group->member[$src_dn])) {
1007         unset($o_ogroup->member[$src_dn]);
1008       }
1009       $o_ogroup->member[$dst_dn]= $dst_dn;
1010       
1011       // Save object group
1012       $o_ogroup->save();
1013     }
1015     // Migrate rfc groups if needed
1016     $groups = get_sub_list("(&(objectClass=posixGroup)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))","groups", array(get_ou("groupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
1018     // Walk through all POSIX groups
1019     foreach($groups as $group){
1021       // Migrate old to new dn
1022       $o_group= new group($this->config,$group['dn']);
1023       $o_group->save();
1024     }
1026     /* Update roles to use the new entry dn */
1027     $roles = get_sub_list("(&(objectClass=organizationalRole)(roleOccupant=".LDAP::prepare4filter(LDAP::fix($src_dn))."))","roles", array(get_ou("roleRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
1029     // Walk through all roles
1030     foreach($roles as $role){
1031       $role = new roleGeneric($this->config,$role['dn']);
1032       $key= array_search($src_dn, $role->roleOccupant);      
1033       if($key !== FALSE){
1034         $role->roleOccupant[$key] = $dst_dn;
1035         $role->save();
1036       }
1037     }
1038  
1039     /* Check if there are gosa departments moved. 
1040        If there were deps moved, the force reload of config->deps.
1041      */
1042     $leaf_deps=  get_list("(objectClass=gosaDepartment)",array("all"),$dst_dn,
1043           array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
1044   
1045     if(count($leaf_deps)){
1046       $this->config->get_departments();
1047       $this->config->make_idepartments();
1048       session::global_set("config",$this->config);
1049       $ui =get_userinfo();
1050       $ui->reset_acl_cache();
1051     }
1053     return(TRUE); 
1054   }
1057  
1058   function move($src_dn, $dst_dn)
1059   {
1060     /* Do not copy if only upper- lowercase has changed */
1061     if(strtolower($src_dn) == strtolower($dst_dn)){
1062       return(TRUE);
1063     }
1065     
1066     /* Try to move the entry instead of copy & delete
1067      */
1068     if(TRUE){
1070       /* Try to move with ldap routines, if this was not successfull
1071           fall back to the old style copy & remove method 
1072        */
1073       if($this->rename($src_dn, $dst_dn)){
1074         return(TRUE);
1075       }else{
1076         // See code below.
1077       }
1078     }
1080     /* Copy source to destination */
1081     if (!$this->copy($src_dn, $dst_dn)){
1082       return (FALSE);
1083     }
1085     /* Delete source */
1086     $ldap= $this->config->get_ldap_link();
1087     $ldap->rmdir_recursive($src_dn);
1088     if (!$ldap->success()){
1089       trigger_error("Trying to delete $src_dn failed.",
1090           E_USER_WARNING);
1091       return (FALSE);
1092     }
1094     return (TRUE);
1095   }
1098   /* \brief Move/Rename complete trees */
1099   function recursive_move($src_dn, $dst_dn)
1100   {
1101     /* Check if the destination entry exists */
1102     $ldap= $this->config->get_ldap_link();
1104     /* Check if destination exists - abort */
1105     $ldap->cat($dst_dn, array('dn'));
1106     if ($ldap->fetch()){
1107       trigger_error("recursive_move $dst_dn already exists.",
1108           E_USER_WARNING);
1109       return (FALSE);
1110     }
1112     $this->copy($src_dn, $dst_dn);
1114     /* Remove src_dn */
1115     $ldap->cd($src_dn);
1116     $ldap->recursive_remove($src_dn);
1117     return (TRUE);
1118   }
1121   function handle_post_events($mode, $add_attrs= array())
1122   {
1123     switch ($mode){
1124       case "add":
1125         $this->postcreate($add_attrs);
1126       break;
1128       case "modify":
1129         $this->postmodify($add_attrs);
1130       break;
1132       case "remove":
1133         $this->postremove($add_attrs);
1134       break;
1135     }
1136   }
1139   function saveCopyDialog(){
1140   }
1143   function getCopyDialog(){
1144     return(array("string"=>"","status"=>""));
1145   }
1148   /*! \brief Prepare for Copy & Paste */
1149   function PrepareForCopyPaste($source)
1150   {
1151     $todo = $this->attributes;
1152     if(isset($this->CopyPasteVars)){
1153       $todo = array_merge($todo,$this->CopyPasteVars);
1154     }
1156     if(count($this->objectclasses)){
1157       $this->is_account = TRUE;
1158       foreach($this->objectclasses as $class){
1159         if(!in_array($class,$source['objectClass'])){
1160           $this->is_account = FALSE;
1161         }
1162       }
1163     }
1165     foreach($todo as $var){
1166       if (isset($source[$var])){
1167         if(isset($source[$var]['count'])){
1168           if($source[$var]['count'] > 1){
1169             $tmp= $source[$var];
1170             unset($tmp['count']);
1171             $this->$var = $tmp;
1172           }else{
1173             $this->$var = $source[$var][0];
1174           }
1175         }else{
1176           $this->$var= $source[$var];
1177         }
1178       }
1179     }
1180   }
1182   /*! \brief Get gosaUnitTag for the given DN
1183        If this is called from departmentGeneric, we have to skip this
1184         tagging procedure. 
1185     */
1186   function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1187   {
1188     /* Skip tagging? */
1189     if($this->skipTagging){
1190       return;
1191     }
1193     /* No dn? Self-operation... */
1194     if ($dn == ""){
1195       $dn= $this->dn;
1197       /* No tag? Find it yourself... */
1198       if ($tag == ""){
1199         $len= strlen($dn);
1201         @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
1202         $relevant= array();
1203         foreach ($this->config->adepartments as $key => $ntag){
1205           /* This one is bigger than our dn, its not relevant... */
1206           if ($len < strlen($key)){
1207             continue;
1208           }
1210           /* This one matches with the latter part. Break and don't fix this entry */
1211           if (preg_match('/(^|,)'.preg_quote($key, '/').'$/', $dn)){
1212             @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
1213             $relevant[strlen($key)]= $ntag;
1214             continue;
1215           }
1217         }
1219         /* If we've some relevant tags to set, just get the longest one */
1220         if (count($relevant)){
1221           ksort($relevant);
1222           $tmp= array_keys($relevant);
1223           $idx= end($tmp);
1224           $tag= $relevant[$idx];
1225           $this->gosaUnitTag= $tag;
1226         }
1227       }
1228     }
1230   /*! \brief Add unit tag */ 
1231     /* Remove tags that may already be here... */
1232     remove_objectClass("gosaAdministrativeUnitTag", $at);
1233     if (isset($at['gosaUnitTag'])){
1234         unset($at['gosaUnitTag']);
1235     }
1237     /* Set tag? */
1238     if ($tag != ""){
1239       add_objectClass("gosaAdministrativeUnitTag", $at);
1240       $at['gosaUnitTag']= $tag;
1241     }
1243     /* Initially this object was tagged. 
1244        - But now, it is no longer inside a tagged department. 
1245        So force the remove of the tag.
1246        (objectClass was already removed obove)
1247      */
1248     if($tag == "" && $this->gosaUnitTag){
1249       $at['gosaUnitTag'] = array();
1250     }
1251   }
1254   /*! \brief Test for removability of the object
1255    *
1256    * Allows testing of conditions for removal of object. If removal should be aborted
1257    * the function needs to remove an error message.
1258    * */
1259   function allow_remove()
1260   {
1261     $reason= "";
1262     return $reason;
1263   }
1266   /*! \brief Create a snapshot of the current object */
1267   function create_snapshot($type= "snapshot", $description= array())
1268   {
1270     /* Check if snapshot functionality is enabled */
1271     if(!$this->snapshotEnabled()){
1272       return;
1273     }
1275     /* Get configuration from gosa.conf */
1276     $config = $this->config;
1278     /* Create lokal ldap connection */
1279     $ldap= $this->config->get_ldap_link();
1280     $ldap->cd($this->config->current['BASE']);
1282     /* check if there are special server configurations for snapshots */
1283     if($config->get_cfg_value("snapshotURI") == ""){
1285       /* Source and destination server are both the same, just copy source to dest obj */
1286       $ldap_to      = $ldap;
1287       $snapldapbase = $this->config->current['BASE'];
1289     }else{
1290       $server         = $config->get_cfg_value("snapshotURI");
1291       $user           = $config->get_cfg_value("snapshotAdminDn");
1292       $password       = $this->config->get_credentials($config->get_cfg_value("snapshotAdminPassword"));
1293       $snapldapbase   = $config->get_cfg_value("snapshotBase");
1295       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1296       $ldap_to -> cd($snapldapbase);
1298       if (!$ldap_to->success()){
1299         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1300       }
1302     }
1304     /* check if the dn exists */ 
1305     if ($ldap->dn_exists($this->dn)){
1307       /* Extract seconds & mysecs, they are used as entry index */
1308       list($usec, $sec)= explode(" ", microtime());
1310       /* Collect some infos */
1311       $base           = $this->config->current['BASE'];
1312       $snap_base      = $config->get_cfg_value("snapshotBase");
1313       $base_of_object = preg_replace ('/^[^,]+,/i', '', $this->dn);
1314       $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1316       /* Create object */
1317 #$data             = preg_replace('/^dn:.*\n/', '', $ldap->gen_ldif($this->dn,"(!(objectClass=gosaDepartment))"));
1318       $data             = $ldap->gen_ldif($this->dn,"(&(!(objectClass=gosaDepartment))(!(objectClass=FAIclass)))");
1319       $newName          = str_replace(".", "", $sec."-".$usec);
1320       $target= array();
1321       $target['objectClass']            = array("top", "gosaSnapshotObject");
1322       $target['gosaSnapshotData']       = gzcompress($data, 6);
1323       $target['gosaSnapshotType']       = $type;
1324       $target['gosaSnapshotDN']         = $this->dn;
1325       $target['description']            = $description;
1326       $target['gosaSnapshotTimestamp']  = $newName;
1328       /* Insert the new snapshot 
1329          But we have to check first, if the given gosaSnapshotTimestamp
1330          is already used, in this case we should increment this value till there is 
1331          an unused value. */ 
1332       $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1333       $ldap_to->cat($new_dn);
1334       while($ldap_to->count()){
1335         $ldap_to->cat($new_dn);
1336         $newName = str_replace(".", "", $sec."-".($usec++));
1337         $new_dn                           = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1338         $target['gosaSnapshotTimestamp']  = $newName;
1339       } 
1341       /* Inset this new snapshot */
1342       $ldap_to->cd($snapldapbase);
1343       $ldap_to->create_missing_trees($snapldapbase);
1344       $ldap_to->create_missing_trees($new_base);
1345       $ldap_to->cd($new_dn);
1346       $ldap_to->add($target);
1347       if (!$ldap_to->success()){
1348         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $new_dn, LDAP_ADD, get_class()));
1349       }
1351       if (!$ldap->success()){
1352         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $new_base, "", get_class()));
1353       }
1355     }
1356   }
1358   /*! \brief Remove a snapshot */
1359   function remove_snapshot($dn)
1360   {
1361     $ui       = get_userinfo();
1362     $old_dn   = $this->dn; 
1363     $this->dn = $dn;
1364     $ldap = $this->config->get_ldap_link();
1365     $ldap->cd($this->config->current['BASE']);
1366     $ldap->rmdir_recursive($this->dn);
1367     if(!$ldap->success()){
1368       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn));
1369     }
1370     $this->dn = $old_dn;
1371   }
1374   /*! \brief Test if snapshotting is enabled
1375    *
1376    * Test weither snapshotting is enabled or not. There will also be some errors posted,
1377    * if the configuration failed 
1378    * \return TRUE if snapshots are enabled, and FALSE if it is disabled
1379    */
1380   function snapshotEnabled()
1381   {
1382     return $this->config->snapshotEnabled();
1383   }
1386   /* \brief Return available snapshots for the given base */
1387   function Available_SnapsShots($dn,$raw = false)
1388   {
1389     if(!$this->snapshotEnabled()) return(array());
1391     /* Create an additional ldap object which
1392        points to our ldap snapshot server */
1393     $ldap= $this->config->get_ldap_link();
1394     $ldap->cd($this->config->current['BASE']);
1395     $cfg= &$this->config->current;
1397     /* check if there are special server configurations for snapshots */
1398     if($this->config->get_cfg_value("snapshotURI") == ""){
1399       $ldap_to      = $ldap;
1400     }else{
1401       $server         = $this->config->get_cfg_value("snapshotURI");
1402       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1403       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1404       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1405       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1406       $ldap_to -> cd($snapldapbase);
1407       if (!$ldap_to->success()){
1408         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1409       }
1410     }
1412     /* Prepare bases and some other infos */
1413     $base           = $this->config->current['BASE'];
1414     $snap_base      = $this->config->get_cfg_value("snapshotBase");
1415     $base_of_object = preg_replace ('/^[^,]+,/i', '', $dn);
1416     $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1417     $tmp            = array(); 
1419     /* Fetch all objects with  gosaSnapshotDN=$dn */
1420     $ldap_to->cd($new_base);
1421     $ldap_to->ls("(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN=".$dn."))",$new_base,
1422         array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description")); 
1424     /* Put results into a list and add description if missing */
1425     while($entry = $ldap_to->fetch()){ 
1426       if(!isset($entry['description'][0])){
1427         $entry['description'][0]  = "";
1428       }
1429       $tmp[] = $entry; 
1430     }
1432     /* Return the raw array, or format the result */
1433     if($raw){
1434       return($tmp);
1435     }else{  
1436       $tmp2 = array();
1437       foreach($tmp as $entry){
1438         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1439       }
1440     }
1441     return($tmp2);
1442   }
1445   function getAllDeletedSnapshots($base_of_object,$raw = false)
1446   {
1447     if(!$this->snapshotEnabled()) return(array());
1449     /* Create an additional ldap object which
1450        points to our ldap snapshot server */
1451     $ldap= $this->config->get_ldap_link();
1452     $ldap->cd($this->config->current['BASE']);
1453     $cfg= &$this->config->current;
1455     /* check if there are special server configurations for snapshots */
1456     if($this->config->get_cfg_value("snapshotURI") == ""){
1457       $ldap_to      = $ldap;
1458     }else{
1459       $server         = $this->config->get_cfg_value("snapshotURI");
1460       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1461       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1462       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1463       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1464       $ldap_to -> cd($snapldapbase);
1465       if (!$ldap_to->success()){
1466         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1467       }
1468     }
1470     /* Prepare bases */ 
1471     $base           = $this->config->current['BASE'];
1472     $snap_base      = $this->config->get_cfg_value("snapshotBase");
1473     $new_base       = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1475     /* Fetch all objects and check if they do not exist anymore */
1476     $ui = get_userinfo();
1477     $tmp = array();
1478     $ldap_to->cd($new_base);
1479     $ldap_to->ls("(objectClass=gosaSnapshotObject)",$new_base,array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description"));
1480     while($entry = $ldap_to->fetch()){
1482       $chk =  str_replace($new_base,"",$entry['dn']);
1483       if(preg_match("/,ou=/",$chk)) continue;
1485       if(!isset($entry['description'][0])){
1486         $entry['description'][0]  = "";
1487       }
1488       $tmp[] = $entry; 
1489     }
1491     /* Check if entry still exists */
1492     foreach($tmp as $key => $entry){
1493       $ldap->cat($entry['gosaSnapshotDN'][0]);
1494       if($ldap->count()){
1495         unset($tmp[$key]);
1496       }
1497     }
1499     /* Format result as requested */
1500     if($raw) {
1501       return($tmp);
1502     }else{
1503       $tmp2 = array();
1504       foreach($tmp as $key => $entry){
1505         $tmp2[base64_encode($entry['dn'])] = $entry['description'][0]; 
1506       }
1507     }
1508     return($tmp2);
1509   } 
1512   /* \brief Restore selected snapshot */
1513   function restore_snapshot($dn)
1514   {
1515     if(!$this->snapshotEnabled()) return(array());
1517     $ldap= $this->config->get_ldap_link();
1518     $ldap->cd($this->config->current['BASE']);
1519     $cfg= &$this->config->current;
1521     /* check if there are special server configurations for snapshots */
1522     if($this->config->get_cfg_value("snapshotURI") == ""){
1523       $ldap_to      = $ldap;
1524     }else{
1525       $server         = $this->config->get_cfg_value("snapshotURI");
1526       $user           = $this->config->get_cfg_value("snapshotAdminDn");
1527       $password       = $this->config->get_credentials($this->config->get_cfg_value("snapshotAdminPassword"));
1528       $snapldapbase   = $this->config->get_cfg_value("snapshotBase");
1529       $ldap_to        = new ldapMultiplexer(new LDAP($user,$password, $server));
1530       $ldap_to -> cd($snapldapbase);
1531       if (!$ldap_to->success()){
1532         msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1533       }
1534     }
1536     /* Get the snapshot */ 
1537     $ldap_to->cat($dn);
1538     $restoreObject = $ldap_to->fetch();
1540     /* Prepare import string */
1541     $data  = gzuncompress($ldap_to->get_attribute($dn,'gosaSnapshotData'));
1543     /* Import the given data */
1544     $err = "";
1545     $ldap->import_complete_ldif($data,$err,false,false);
1546     if (!$ldap->success()){
1547       msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, "", get_class()));
1548     }
1549   }
1552   function showSnapshotDialog($base,$baseSuffixe,&$parent)
1553   {
1554     $once = true;
1555     $ui = get_userinfo();
1556     $this->parent = $parent;
1558     foreach($_POST as $name => $value){
1560       /* Create a new snapshot, display a dialog */
1561       if(preg_match("/^CreateSnapShotDialog_[^_]*_[xy]$/",$name) && $once){
1563                           $entry = base64_decode(preg_replace("/^CreateSnapShotDialog_([^_]*)_[xy]$/","\\1",$name));
1564         $once = false;
1565         $entry = preg_replace("/^CreateSnapShotDialog_/","",$entry);
1567         if(!empty($entry) && $ui->allow_snapshot_create($entry,$this->parent->acl_module)){
1568           $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1569         }else{
1570           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to create a snapshot for %s."),$entry),ERROR_DIALOG);
1571         }
1572       }  
1573   
1574       /* Restore a snapshot, display a dialog with all snapshots of the current object */
1575       if(preg_match("/^RestoreSnapShotDialog_/",$name) && $once){
1576         $once = false;
1577         $entry = base64_decode(preg_replace("/^RestoreSnapShotDialog_([^_]*)_[xy]$/i","\\1",$name));
1578         if(!empty($entry) && $ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1579           $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1580           $this->snapDialog->display_restore_dialog = true;
1581         }else{
1582           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1583         }
1584       }
1586       /* Restore one of the already deleted objects */
1587       if(((isset($_POST['menu_action']) && $_POST['menu_action'] == "RestoreDeletedSnapShot") 
1588           || preg_match("/^RestoreDeletedSnapShot_/",$name)) && $once){
1589         $once = false;
1591         if($ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1592           $this->snapDialog = new SnapShotDialog($this->config,"",$this);
1593           $this->snapDialog->set_snapshot_bases($baseSuffixe);
1594           $this->snapDialog->display_restore_dialog      = true;
1595           $this->snapDialog->display_all_removed_objects  = true;
1596         }else{
1597           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$base),ERROR_DIALOG);
1598         }
1599       }
1601       /* Restore selected snapshot */
1602       if(preg_match("/^RestoreSnapShot_/",$name) && $once){
1603         $once = false;
1604         $entry = base64_decode(preg_replace("/^RestoreSnapShot_([^_]*)_[xy]$/i","\\1",$name));
1606         if(!empty($entry) && $ui->allow_snapshot_restore($this->dn,$this->parent->acl_module)){
1607           $this->restore_snapshot($entry);
1608           $this->snapDialog = NULL;
1609         }else{
1610           msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1611         }
1612       }
1613     }
1615     /* Create a new snapshot requested, check
1616        the given attributes and create the snapshot*/
1617     if(isset($_POST['CreateSnapshot']) && is_object($this->snapDialog)){
1618       $this->snapDialog->save_object();
1619       $msgs = $this->snapDialog->check();
1620       if(count($msgs)){
1621         foreach($msgs as $msg){
1622           msg_dialog::display(_("Error"), $msg, ERROR_DIALOG);
1623         }
1624       }else{
1625         $this->dn =  $this->snapDialog->dn;
1626         $this->create_snapshot("snapshot",$this->snapDialog->CurrentDescription);
1627         $this->snapDialog = NULL;
1628       }
1629     }
1631     /* Restore is requested, restore the object with the posted dn .*/
1632     if((isset($_POST['RestoreSnapshot'])) && (isset($_POST['SnapShot']))){
1633     }
1635     if(isset($_POST['CancelSnapshot'])){
1636       $this->snapDialog = NULL;
1637     }
1639     if(is_object($this->snapDialog )){
1640       $this->snapDialog->save_object();
1641       return($this->snapDialog->execute());
1642     }
1643   }
1646   /*! \brief Return plugin informations for acl handling */
1647   static function plInfo()
1648   {
1649     return array();
1650   }
1653   function set_acl_base($base)
1654   {
1655     $this->acl_base= $base;
1656   }
1659   function set_acl_category($category)
1660   {
1661     $this->acl_category= "$category/";
1662   }
1665   function acl_is_writeable($attribute,$skip_write = FALSE)
1666   {
1667     if($this->read_only) return(FALSE);
1668     $ui= get_userinfo();
1669     return preg_match('/w/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute, $skip_write));
1670   }
1673   function acl_is_readable($attribute)
1674   {
1675     $ui= get_userinfo();
1676     return preg_match('/r/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute));
1677   }
1680   function acl_is_createable($base ="")
1681   {
1682     if($this->read_only) return(FALSE);
1683     $ui= get_userinfo();
1684     if($base == "") $base = $this->acl_base;
1685     return preg_match('/c/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1686   }
1689   function acl_is_removeable($base ="")
1690   {
1691     if($this->read_only) return(FALSE);
1692     $ui= get_userinfo();
1693     if($base == "") $base = $this->acl_base;
1694     return preg_match('/d/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1695   }
1698   function acl_is_moveable($base = "")
1699   {
1700     if($this->read_only) return(FALSE);
1701     $ui= get_userinfo();
1702     if($base == "") $base = $this->acl_base;
1703     return preg_match('/m/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1704   }
1707   function acl_have_any_permissions()
1708   {
1709   }
1712   function getacl($attribute,$skip_write= FALSE)
1713   {
1714     $ui= get_userinfo();
1715     $skip_write |= $this->read_only;
1716     return  $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute,$skip_write);
1717   }
1720   /*! \brief Returns a list of all available departments for this object.
1721    * 
1722    * If this object is new, all departments we are allowed to create a new user in
1723    * are returned. If this is an existing object, return all deps. 
1724    * We are allowed to move tis object too.
1725    * \return array [dn] => "..name"  // All deps. we are allowed to act on.
1726   */
1727   function get_allowed_bases()
1728   {
1729     $ui = get_userinfo();
1730     $deps = array();
1732     /* Is this a new object ? Or just an edited existing object */
1733     if(!$this->initially_was_account && $this->is_account){
1734       $new = true;
1735     }else{
1736       $new = false;
1737     }
1739     foreach($this->config->idepartments as $dn => $name){
1740       if($new && $this->acl_is_createable($dn)){
1741         $deps[$dn] = $name;
1742       }elseif(!$new && $this->acl_is_moveable($dn)){
1743         $deps[$dn] = $name;
1744       }
1745     }
1747     /* Add current base */      
1748     if(isset($this->base) && isset($this->config->idepartments[$this->base])){
1749       $deps[$this->base] = $this->config->idepartments[$this->base];
1750     }elseif(strtolower($this->dn) == strtolower($this->config->current['BASE'])){
1752     }else{
1753       trigger_error("Cannot return list of departments, no default base found in class ".get_class($this).". ".$this->base);
1754     }
1755     return($deps);
1756   }
1759   /* This function updates ACL settings if $old_dn was used.
1760    *  \param string 'old_dn' specifies the actually used dn
1761    *  \param string 'new_dn' specifies the destiantion dn
1762    */
1763   function update_acls($old_dn,$new_dn,$output_changes = FALSE)
1764   {
1765     /* Check if old_dn is empty. This should never happen */
1766     if(empty($old_dn) || empty($new_dn)){
1767       trigger_error("Failed to check acl dependencies, wrong dn given.");
1768       return;
1769     }
1771     /* Update userinfo if necessary */
1772     $ui = session::global_get('ui');
1773     if($ui->dn == $old_dn){
1774       $ui->dn = $new_dn;
1775       session::global_set('ui',$ui);
1776       new log("view","acl/".get_class($this),$this->dn,array(),"Updated current object dn from '".$old_dn."' to '".$new_dn."'");
1777     }
1779     /* Object was moved, ensure that all acls will be moved too */
1780     if($new_dn != $old_dn && $old_dn != "new"){
1782       /* get_ldap configuration */
1783       $update = array();
1784       $ldap = $this->config->get_ldap_link();
1785       $ldap->cd ($this->config->current['BASE']);
1786       $ldap->search("(&(objectClass=gosaAcl)(gosaAclEntry=*".base64_encode($old_dn)."*))",array("cn","gosaAclEntry"));
1787       while($attrs = $ldap->fetch()){
1788         $acls = array();
1789         $found = false;
1790         for($i = 0 ; $i <  $attrs['gosaAclEntry']['count'] ; $i ++ ){
1791           $acl_parts = explode(":",$attrs['gosaAclEntry'][$i]);
1793           /* Roles uses antoher data storage order, members are stored int the third part, 
1794              while the members in direct ACL assignments are stored in the second part.
1795            */
1796           $id = ($acl_parts[1] == "role") ? 3 : 2;
1798           /* Update member entries to use $new_dn instead of old_dn
1799            */
1800           $members = explode(",",$acl_parts[$id]);
1801           foreach($members as $key => $member){
1802             $member = base64_decode($member);
1803             if($member == $old_dn){
1804               $members[$key] = base64_encode($new_dn);
1805               $found = TRUE;
1806             }
1807           } 
1809           /* Check if the selected role has to updated
1810            */
1811           if($acl_parts[1] == "role" && $acl_parts[2] == base64_encode($old_dn)){
1812             $acl_parts[2] = base64_encode($new_dn);
1813             $found = TRUE;
1814           }
1816           /* Build new acl string */ 
1817           $acl_parts[$id] = implode($members,",");
1818           $acls[] = implode($acl_parts,":");
1819         }
1821         /* Acls for this object must be adjusted */
1822         if($found){
1824           $debug_info=  _("Changing ACL dn")."&nbsp;:&nbsp;<br>&nbsp;-"._("from")."&nbsp;<b>&nbsp;".
1825             $old_dn."</b><br>&nbsp;-"._("to")."&nbsp;<b>".$new_dn."</b><br>";
1826           @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,$debug_info,"ACL");
1828           $update[$attrs['dn']] =array();
1829           foreach($acls as $acl){
1830             $update[$attrs['dn']]['gosaAclEntry'][] = $acl;
1831           }
1832         }
1833       }
1835       /* Write updated acls */
1836       foreach($update as $dn => $attrs){
1837         $ldap->cd($dn);
1838         $ldap->modify($attrs);
1839       }
1840     }
1841   }
1843   
1845   /*! \brief Enable the Serial ID check
1846    *
1847    * This function enables the entry Serial ID check.  If an entry was edited while
1848    * we have edited the entry too, an error message will be shown. 
1849    * To configure this check correctly read the FAQ.
1850    */    
1851   function enable_CSN_check()
1852   {
1853     $this->CSN_check_active =TRUE;
1854     $this->entryCSN = getEntryCSN($this->dn);
1855   }
1858   /*! \brief  Prepares the plugin to be used for multiple edit
1859    *          Update plugin attributes with given array of attribtues.
1860    *  \param  array   Array with attributes that must be updated.
1861    */
1862   function init_multiple_support($attrs,$all)
1863   {
1864     $ldap= $this->config->get_ldap_link();
1865     $this->multi_attrs    = $attrs;
1866     $this->multi_attrs_all= $all;
1868     /* Copy needed attributes */
1869     foreach ($this->attributes as $val){
1870       $found= array_key_ics($val, $this->multi_attrs);
1871  
1872       if ($found != ""){
1873         if(isset($this->multi_attrs["$val"][0])){
1874           $this->$val= $this->multi_attrs["$val"][0];
1875         }
1876       }
1877     }
1878   }
1880  
1881   /*! \brief  Enables multiple support for this plugin
1882    */
1883   function enable_multiple_support()
1884   {
1885     $this->ignore_account = TRUE;
1886     $this->multiple_support_active = TRUE;
1887   }
1890   /*! \brief  Returns all values that have been modfied in multiple edit mode.
1891       \return array Cotaining all modified values. 
1892    */
1893   function get_multi_edit_values()
1894   {
1895     $ret = array();
1896     foreach($this->attributes as $attr){
1897       if(in_array($attr,$this->multi_boxes)){
1898         $ret[$attr] = $this->$attr;
1899       }
1900     }
1901     return($ret);
1902   }
1904   
1905   /*! \brief  Update class variables with values collected by multiple edit.
1906    */
1907   function set_multi_edit_values($attrs)
1908   {
1909     foreach($attrs as $name => $value){
1910       $this->$name = $value;
1911     }
1912   }
1915   /*! \brief Generates the html output for this node for multi edit*/
1916   function multiple_execute()
1917   {
1918     /* This one is empty currently. Fabian - please fill in the docu code */
1919     session::global_set('current_class_for_help',get_class($this));
1921     /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
1922     session::set('LOCK_VARS_TO_USE',array());
1923     session::set('LOCK_VARS_USED_GET',array());
1924     session::set('LOCK_VARS_USED_POST',array());
1925     session::set('LOCK_VARS_USED_REQUEST',array());
1926     
1927     return("Multiple edit is currently not implemented for this plugin.");
1928   }
1931   /*! \brief Save HTML posted data to object for multiple edit
1932    */
1933   function multiple_save_object()
1934   {
1935     if(empty($this->entryCSN) && $this->CSN_check_active){
1936       $this->entryCSN = getEntryCSN($this->dn);
1937     }
1939     /* Save values to object */
1940     $this->multi_boxes = array();
1941     foreach ($this->attributes as $val){
1942   
1943       /* Get selected checkboxes from multiple edit */
1944       if(isset($_POST["use_".$val])){
1945         $this->multi_boxes[] = $val;
1946       }
1948       if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
1950         /* Check for modifications */
1951         if (get_magic_quotes_gpc()) {
1952           $data= stripcslashes($_POST["$val"]);
1953         } else {
1954           $data= $this->$val = $_POST["$val"];
1955         }
1956         if ($this->$val != $data){
1957           $this->is_modified= TRUE;
1958         }
1959     
1960         /* IE post fix */
1961         if(isset($data[0]) && $data[0] == chr(194)) {
1962           $data = "";  
1963         }
1964         $this->$val= $data;
1965       }
1966     }
1967   }
1970   /*! \brief Returns all attributes of this plugin, 
1971                to be able to detect multiple used attributes 
1972                in multi_plugg::detect_multiple_used_attributes().
1973       @return array Attributes required for intialization of multi_plug
1974    */
1975   public function get_multi_init_values()
1976   {
1977     $attrs = $this->attrs;
1978     return($attrs);
1979   }
1982   /*! \brief  Check given values in multiple edit
1983       \return array Error messages
1984    */
1985   function multiple_check()
1986   {
1987     $message = plugin::check();
1988     return($message);
1989   }
1992   /*! \brief  Returns the snapshot header part for "Actions" menu in management dialogs 
1993       \param  $layer_menu  
1994    */   
1995   function get_snapshot_header($base,$category)
1996   {
1997     $str = "";
1998     $ui = get_userinfo();
1999     if($this->snapshotEnabled() && $ui->allow_snapshot_restore($base,$category)){
2001       $ok = false;
2002       foreach($this->get_used_snapshot_bases() as $base){
2003         $ok |= count($this->getAllDeletedSnapshots($base)) >= 1 ;
2004       }
2006       if($ok){
2007         $str = "..|<img class='center' src='images/lists/restore.png' ".
2008           "alt='"._("Restore")."'>&nbsp;"._("Restore").                       "|RestoreDeletedSnapShot|\n";
2009       }else{
2010         $str = "..|<img class='center' src='images/lists/restore_grey.png' alt=''>&nbsp;"._("Restore")."||\n";
2011       }
2012     }
2013     return($str);
2014   }
2017   function get_snapshot_action($base,$category)
2018   {
2019     $str= ""; 
2020     $ui = get_userinfo();
2021     if($this->snapshotEnabled()){
2022       if ($ui->allow_snapshot_restore($base,$category)){
2024         if(count($this->Available_SnapsShots($base))){
2025           $str.= "<input class='center' type='image' src='images/lists/restore.png'
2026             alt='"._("Restore snapshot")."' name='RestoreSnapShotDialog_".base64_encode($base)."' title='"._("Restore snapshot")."'>&nbsp;";
2027         } else {
2028           $str = "<img class='center' src='images/lists/restore_grey.png' alt=''>&nbsp;";
2029         }
2030       }
2031       if($ui->allow_snapshot_create($base,$category)){
2032         $str.= "<input class='center' type='image' src='images/snapshot.png'
2033           alt='"._("Create snapshot")."' name='CreateSnapShotDialog_".base64_encode($base)."' 
2034           title='"._("Create a new snapshot from this object")."'>&nbsp;";
2035       }else{
2036         $str = "<img class='center' src='images/empty.png' alt=' '>&nbsp;";
2037       }
2038     }
2040     return($str);
2041   }
2044   function get_copypaste_action($base,$category,$class,$copy = TRUE, $cut = TRUE)
2045   {
2046     $ui = get_userinfo();
2047     $action = "";
2048     if($this->CopyPasteHandler){
2049       if($cut){
2050         if($ui->is_cutable($base,$category,$class)){
2051           $action .= "<input class='center' type='image'
2052             src='images/lists/cut.png' alt='"._("cut")."' name='cut_%KEY%' title='"._("Cut this entry")."'>&nbsp;";
2053         }else{
2054           $action.="<img src='images/empty.png' alt=' ' class='center'>&nbsp;";
2055         }
2056       }
2057       if($copy){
2058         if($ui->is_copyable($base,$category,$class)){
2059           $action.= "<input class='center' type='image'
2060             src='images/lists/copy.png' alt='"._("copy")."' name='copy_%KEY%' title='"._("Copy this entry")."'>&nbsp;";
2061         }else{
2062           $action.="<img src='images/empty.png' alt=' ' class='center'>&nbsp;";
2063         }
2064       }
2065     }
2067     return($action); 
2068   }
2071   function get_copypaste_header($base,$category,$copy = TRUE, $cut = TRUE)
2072   {
2073     $s = "";
2074     $ui =get_userinfo();
2076     if(!is_array($category)){
2077       $category = array($category);
2078     }
2080     /* Check permissions for each category, if there is at least one category which 
2081         support read or paste permissions for the given base, then display the specific actions.
2082      */
2083     $readable = $pasteable = false;
2084     foreach($category as $cat){
2085       $readable= $readable || preg_match('/r/', $ui->get_category_permissions($base, $cat));
2086       $pasteable= $pasteable || $ui->is_pasteable($base, $cat) == 1;
2087     }
2088   
2089     if(($cut || $copy) && isset($this->CopyPasteHandler) && is_object($this->CopyPasteHandler)){
2090       if($readable){
2091         $s.= "..|---|\n";
2092         if($copy){
2093           $s.= "..|<img src='images/lists/copy.png' alt='' border='0' class='center'>".
2094             "&nbsp;"._("Copy")."|"."multiple_copy_systems|\n";
2095         }
2096         if($cut){
2097           $s.= "..|<img src='images/lists/cut.png' alt='' border='0' class='center'>".
2098             "&nbsp;"._("Cut")."|"."multiple_cut_systems|\n";
2099         }
2100       }
2102       if($pasteable){
2103         if($this->CopyPasteHandler->entries_queued()){
2104           $img = "<img border='0' class='center' src='images/lists/paste.png' alt=''>";
2105           $s.="..|".$img."&nbsp;"._("Paste")."|editPaste|\n";
2106         }else{
2107           $img = "<img border='0' class='center' src='images/lists/paste-grey.png' alt=''>";
2108           $s.="..|".$img."&nbsp;"._("Paste")."\n";
2109         }
2110       }
2111     }
2112     return($s);
2113   }
2116   function get_used_snapshot_bases()
2117   {
2118      return(array());
2119   }
2121   function is_modal_dialog()
2122   {
2123     return(isset($this->dialog) && $this->dialog);
2124   }
2127 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
2128 ?>