6bef2744e5f3902ae0f940e7667516d17822b4f0
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 }
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 in between the ...option> </option.. because there is no value=".." specified in these option fields
343 * This 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);
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){
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])){
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])){
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])){
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";
472 /* ... objectClasses */
473 foreach ($this->objectclasses as $oc){
474 $ldif.= "objectClass: $oc\n";
475 }
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]);
501 $result= stream_get_contents($pipes[1]);
502 if ($result != ""){
503 $message[]= $result;
504 }
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\"> </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\"> </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\"> </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);
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);
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);
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']);
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;
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 }
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);
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 }
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 }
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 }
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")." : <br> -"._("from")." <b> ".
1825 $old_dn."</b><br> -"._("to")." <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 }
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);
1872 if ($found != ""){
1873 if(isset($this->multi_attrs["$val"][0])){
1874 $this->$val= $this->multi_attrs["$val"][0];
1875 }
1876 }
1877 }
1878 }
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 }
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());
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){
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 }
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")."'> "._("Restore"). "|RestoreDeletedSnapShot|\n";
2009 }else{
2010 $str = "..|<img class='center' src='images/lists/restore_grey.png' alt=''> "._("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")."'> ";
2027 } else {
2028 $str = "<img class='center' src='images/lists/restore_grey.png' alt=''> ";
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")."'> ";
2035 }else{
2036 $str = "<img class='center' src='images/empty.png' alt=' '> ";
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")."'> ";
2053 }else{
2054 $action.="<img src='images/empty.png' alt=' ' class='center'> ";
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")."'> ";
2061 }else{
2062 $action.="<img src='images/empty.png' alt=' ' class='center'> ";
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 }
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 " "._("Copy")."|"."multiple_copy_systems|\n";
2095 }
2096 if($cut){
2097 $s.= "..|<img src='images/lists/cut.png' alt='' border='0' class='center'>".
2098 " "._("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." "._("Paste")."|editPaste|\n";
2106 }else{
2107 $img = "<img border='0' class='center' src='images/lists/paste-grey.png' alt=''>";
2108 $s.="..|".$img." "._("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 }
2125 }
2127 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
2128 ?>