88fccb4a09552577a803433ada5220f8e21fa9a3
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= "";
117 /* This can be set to render the tabulators in another stylesheet */
118 var $pl_notify= FALSE;
120 /* Object entry CSN */
121 var $entryCSN = "";
122 var $CSN_check_active = FALSE;
124 /* This variable indicates that this class can handle multiple dns at once. */
125 var $multiple_support = FALSE;
126 var $multi_attrs = array();
127 var $multi_attrs_all = array();
129 /* This aviable indicates, that we are currently in multiple edit handle */
130 var $multiple_support_active = FALSE;
131 var $selected_edit_values = array();
132 var $multi_boxes = array();
134 /*! \brief plugin constructor
136 If 'dn' is set, the node loads the given 'dn' from LDAP
138 \param dn Distinguished name to initialize plugin from
139 \sa plugin()
140 */
141 function plugin (&$config, $dn= NULL, $parent= NULL)
142 {
143 /* Configuration is fine, allways */
144 $this->config= &$config;
145 $this->dn= $dn;
147 /* Handle new accounts, don't read information from LDAP */
148 if ($dn == "new"){
149 return;
150 }
152 /* Save current dn as acl_base */
153 $this->acl_base= $dn;
155 /* Get LDAP descriptor */
156 if ($dn !== NULL){
158 /* Load data to 'attrs' and save 'dn' */
159 if ($parent !== NULL){
160 $this->attrs= $parent->attrs;
161 } else {
162 $ldap= $this->config->get_ldap_link();
163 $ldap->cat ($dn);
164 $this->attrs= $ldap->fetch();
165 }
167 /* Copy needed attributes */
168 foreach ($this->attributes as $val){
169 $found= array_key_ics($val, $this->attrs);
170 if ($found != ""){
171 $this->$val= $found[0];
172 }
173 }
175 /* gosaUnitTag loading... */
176 if (isset($this->attrs['gosaUnitTag'][0])){
177 $this->gosaUnitTag= $this->attrs['gosaUnitTag'][0];
178 }
180 /* Set the template flag according to the existence of objectClass
181 gosaUserTemplate */
182 if (isset($this->attrs['objectClass'])){
183 if (in_array_ics ("gosaUserTemplate", $this->attrs['objectClass'])){
184 $this->is_template= TRUE;
185 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
186 "found", "Template check");
187 }
188 }
190 /* Is Account? */
191 $found= TRUE;
192 foreach ($this->objectclasses as $obj){
193 if (preg_match('/top/i', $obj)){
194 continue;
195 }
196 if (!in_array_ics ($obj, $this->attrs['objectClass'])){
197 $found= FALSE;
198 break;
199 }
200 }
201 if ($found){
202 $this->is_account= TRUE;
203 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
204 "found", "Object check");
205 }
207 /* Prepare saved attributes */
208 $this->saved_attributes= $this->attrs;
209 foreach ($this->saved_attributes as $index => $value){
210 if (is_numeric($index)){
211 unset($this->saved_attributes[$index]);
212 continue;
213 }
215 if (!in_array_ics($index, $this->attributes) && strcasecmp('objectClass', $index)){
216 unset($this->saved_attributes[$index]);
217 continue;
218 }
220 if (isset($this->saved_attributes[$index][0])){
221 if(!isset($this->saved_attributes[$index]["count"])){
222 $this->saved_attributes[$index]["count"] = count($this->saved_attributes[$index]);
223 }
224 if($this->saved_attributes[$index]["count"] == 1){
225 $tmp= $this->saved_attributes[$index][0];
226 unset($this->saved_attributes[$index]);
227 $this->saved_attributes[$index]= $tmp;
228 continue;
229 }
230 }
231 unset($this->saved_attributes["$index"]["count"]);
232 }
234 if(isset($this->attrs['gosaUnitTag'])){
235 $this->saved_attributes['gosaUnitTag'] = $this->attrs['gosaUnitTag'][0];
236 }
237 }
239 /* Save initial account state */
240 $this->initially_was_account= $this->is_account;
241 }
244 /*! \brief execute plugin
246 Generates the html output for this node
247 */
248 function execute()
249 {
250 /* This one is empty currently. Fabian - please fill in the docu code */
251 session::set('current_class_for_help',get_class($this));
253 /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
254 session::set('LOCK_VARS_TO_USE',array());
255 session::set('LOCK_VARS_USED',array());
256 }
258 /*! \brief execute plugin
259 Removes object from parent
260 */
261 function remove_from_parent()
262 {
263 /* include global link_info */
264 $ldap= $this->config->get_ldap_link();
266 /* Get current objectClasses in order to add the required ones */
267 $ldap->cat($this->dn);
268 $tmp= $ldap->fetch ();
269 $oc= array();
270 if (isset($tmp['objectClass'])){
271 $oc= $tmp['objectClass'];
272 unset($oc['count']);
273 }
275 /* Remove objectClasses from entry */
276 $ldap->cd($this->dn);
277 $this->attrs= array();
278 $this->attrs['objectClass']= array_remove_entries_ics($this->objectclasses,$oc);
280 /* Unset attributes from entry */
281 foreach ($this->attributes as $val){
282 $this->attrs["$val"]= array();
283 }
285 /* Unset account info */
286 $this->is_account= FALSE;
288 /* Do not write in plugin base class, this must be done by
289 children, since there are normally additional attribs,
290 lists, etc. */
291 /*
292 $ldap->modify($this->attrs);
293 */
294 }
297 /*! \brief Save HTML posted data to object
298 */
299 function save_object()
300 {
301 /* Update entry CSN if it is empty. */
302 if(empty($this->entryCSN) && $this->CSN_check_active){
303 $this->entryCSN = getEntryCSN($this->dn);
304 }
306 /* Save values to object */
307 foreach ($this->attributes as $val){
308 if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
309 /* Check for modifications */
310 if (get_magic_quotes_gpc()) {
311 $data= stripcslashes($_POST["$val"]);
312 } else {
313 $data= $this->$val = $_POST["$val"];
314 }
315 if ($this->$val != $data){
316 $this->is_modified= TRUE;
317 }
319 /* Okay, how can I explain this fix ...
320 * In firefox, disabled option fields aren't selectable ... but in IE you can select these fileds.
321 * So IE posts these 'unselectable' option, with value = chr(194)
322 * chr(194) seems to be the in between the ...option> </option.. because there is no value=".." specified in these option fields
323 * This was added for W3c compliance, but now causes these ... ldap errors ...
324 * So we set these Fields to ""; a normal empty string, and we can check these values in plugin::check() again ...
325 */
326 if(isset($data[0]) && $data[0] == chr(194)) {
327 $data = "";
328 }
329 $this->$val= $data;
330 }
331 }
332 }
335 /* Save data to LDAP, depending on is_account we save or delete */
336 function save()
337 {
338 /* include global link_info */
339 $ldap= $this->config->get_ldap_link();
341 /* Save all plugins */
342 $this->entryCSN = "";
344 /* Start with empty array */
345 $this->attrs= array();
347 /* Get current objectClasses in order to add the required ones */
348 $ldap->cat($this->dn);
350 $tmp= $ldap->fetch ();
352 $oc= array();
353 if (isset($tmp['objectClass'])){
354 $oc= $tmp["objectClass"];
355 $this->is_new= FALSE;
356 unset($oc['count']);
357 } else {
358 $this->is_new= TRUE;
359 }
361 /* Load (minimum) attributes, add missing ones */
362 $this->attrs['objectClass']= gosa_array_merge($oc,$this->objectclasses);
364 /* Copy standard attributes */
365 foreach ($this->attributes as $val){
366 if ($this->$val != ""){
367 $this->attrs["$val"]= $this->$val;
368 } elseif (!$this->is_new) {
369 $this->attrs["$val"]= array();
370 }
371 }
373 /* Handle tagging */
374 $this->tag_attrs($this->attrs);
375 }
378 function cleanup()
379 {
380 foreach ($this->attrs as $index => $value){
382 /* Convert arrays with one element to non arrays, if the saved
383 attributes are no array, too */
384 if (is_array($this->attrs[$index]) &&
385 count ($this->attrs[$index]) == 1 &&
386 isset($this->saved_attributes[$index]) &&
387 !is_array($this->saved_attributes[$index])){
389 $tmp= $this->attrs[$index][0];
390 $this->attrs[$index]= $tmp;
391 }
393 /* Remove emtpy arrays if they do not differ */
394 if (is_array($this->attrs[$index]) &&
395 count($this->attrs[$index]) == 0 &&
396 !isset($this->saved_attributes[$index])){
398 unset ($this->attrs[$index]);
399 continue;
400 }
402 /* Remove single attributes that do not differ */
403 if (!is_array($this->attrs[$index]) &&
404 isset($this->saved_attributes[$index]) &&
405 !is_array($this->saved_attributes[$index]) &&
406 $this->attrs[$index] == $this->saved_attributes[$index]){
408 unset ($this->attrs[$index]);
409 continue;
410 }
412 /* Remove arrays that do not differ */
413 if (is_array($this->attrs[$index]) &&
414 isset($this->saved_attributes[$index]) &&
415 is_array($this->saved_attributes[$index])){
417 if (!array_differs($this->attrs[$index],$this->saved_attributes[$index])){
418 unset ($this->attrs[$index]);
419 continue;
420 }
421 }
422 }
424 /* Update saved attributes and ensure that next cleanups will be successful too */
425 foreach($this->attrs as $name => $value){
426 $this->saved_attributes[$name] = $value;
427 }
428 }
430 /* Check formular input */
431 function check()
432 {
433 $message= array();
435 /* Skip if we've no config object */
436 if (!isset($this->config) || !is_object($this->config)){
437 return $message;
438 }
440 /* Find hooks entries for this class */
441 $command= $this->config->search(get_class($this), "CHECK", array('menu', 'tabs'));
443 if ($command != ""){
445 if (!check_command($command)){
446 $message[]= msgPool::cmdnotfound("CHECK", get_class($this));
447 } else {
449 /* Generate "ldif" for check hook */
450 $ldif= "dn: $this->dn\n";
452 /* ... objectClasses */
453 foreach ($this->objectclasses as $oc){
454 $ldif.= "objectClass: $oc\n";
455 }
457 /* ... attributes */
458 foreach ($this->attributes as $attr){
459 if ($this->$attr == ""){
460 continue;
461 }
462 if (is_array($this->$attr)){
463 foreach ($this->$attr as $val){
464 $ldif.= "$attr: $val\n";
465 }
466 } else {
467 $ldif.= "$attr: ".$this->$attr."\n";
468 }
469 }
471 /* Append empty line */
472 $ldif.= "\n";
474 /* Feed "ldif" into hook and retrieve result*/
475 $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
476 $fh= proc_open($command, $descriptorspec, $pipes);
477 if (is_resource($fh)) {
478 fwrite ($pipes[0], $ldif);
479 fclose($pipes[0]);
481 $result= stream_get_contents($pipes[1]);
482 if ($result != ""){
483 $message[]= $result;
484 }
486 fclose($pipes[1]);
487 fclose($pipes[2]);
488 proc_close($fh);
489 }
490 }
492 }
494 /* Check entryCSN */
495 if($this->CSN_check_active){
496 $current_csn = getEntryCSN($this->dn);
497 if($current_csn != $this->entryCSN && !empty($this->entryCSN) && !empty($current_csn)){
498 $this->entryCSN = $current_csn;
499 $message[] = _("The object has changed since opened in GOsa. All changes that may be done by others get lost if you save this entry!");
500 }
501 }
502 return ($message);
503 }
505 /* Adapt from template, using 'dn' */
506 function adapt_from_template($dn, $skip= array())
507 {
508 /* Include global link_info */
509 $ldap= $this->config->get_ldap_link();
511 /* Load requested 'dn' to 'attrs' */
512 $ldap->cat ($dn);
513 $this->attrs= $ldap->fetch();
515 /* Walk through attributes */
516 foreach ($this->attributes as $val){
518 /* Skip the ones in skip list */
519 if (in_array($val, $skip)){
520 continue;
521 }
523 if (isset($this->attrs["$val"][0])){
525 /* If attribute is set, replace dynamic parts:
526 %sn, %givenName and %uid. Fill these in our local variables. */
527 $value= $this->attrs["$val"][0];
529 foreach (array("sn", "givenName", "uid") as $repl){
530 if (preg_match("/%$repl/i", $value)){
531 $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
532 }
533 }
534 $this->$val= $value;
535 }
536 }
538 /* Is Account? */
539 $found= TRUE;
540 foreach ($this->objectclasses as $obj){
541 if (preg_match('/top/i', $obj)){
542 continue;
543 }
544 if (!in_array_ics ($obj, $this->attrs['objectClass'])){
545 $found= FALSE;
546 break;
547 }
548 }
549 if ($found){
550 $this->is_account= TRUE;
551 }
552 }
554 /* Indicate whether a password change is needed or not */
555 function password_change_needed()
556 {
557 return FALSE;
558 }
561 /* Show header message for tab dialogs */
562 function show_enable_header($button_text, $text, $disabled= FALSE)
563 {
564 if (($disabled == TRUE) || (!$this->acl_is_createable())){
565 $state= "disabled";
566 } else {
567 $state= "";
568 }
569 $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
570 $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".$state.
571 "><p class=\"seperator\"> </p></td></tr></table>";
573 return($display);
574 }
577 /* Show header message for tab dialogs */
578 function show_disable_header($button_text, $text, $disabled= FALSE)
579 {
580 if (($disabled == TRUE) || !$this->acl_is_removeable()){
581 $state= "disabled";
582 } else {
583 $state= "";
584 }
585 $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
586 $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".$state.
587 "><p class=\"seperator\"> </p></td></tr></table>";
589 return($display);
590 }
593 /* Show header message for tab dialogs */
594 function show_header($button_text, $text, $disabled= FALSE)
595 {
596 echo "FIXME: show_header should be replaced by show_disable_header and show_enable_header<br>";
597 if ($disabled == TRUE){
598 $state= "disabled";
599 } else {
600 $state= "";
601 }
602 $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
603 $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".
604 ($this->acl_is_createable()?'':'disabled')." ".$state.
605 "><p class=\"seperator\"> </p></td></tr></table>";
607 return($display);
608 }
611 function postcreate($add_attrs= array())
612 {
613 /* Find postcreate entries for this class */
614 $command= $this->config->search(get_class($this), "POSTCREATE",array('menu', 'tabs'));
616 if ($command != ""){
618 /* Walk through attribute list */
619 foreach ($this->attributes as $attr){
620 if (!is_array($this->$attr)){
621 $add_attrs[$attr] = $this->$attr;
622 }
623 }
624 $add_attrs['dn']=$this->dn;
626 $tmp = array();
627 foreach($add_attrs as $name => $value){
628 $tmp[$name] = strlen($name);
629 }
630 arsort($tmp);
632 /* Additional attributes */
633 foreach ($tmp as $name => $len){
634 $value = $add_attrs[$name];
635 $command= preg_replace("/%$name/", "$value", $command);
636 }
638 if (check_command($command)){
639 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
640 $command, "Execute");
641 exec($command,$arr);
642 foreach($arr as $str){
643 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
644 $command, "Result: ".$str);
645 }
646 } else {
647 $message= msgPool::cmdnotfound("POSTCREATE", get_class($this));
648 msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
649 }
650 }
651 }
653 function postmodify($add_attrs= array())
654 {
655 /* Find postcreate entries for this class */
656 $command= $this->config->search(get_class($this), "POSTMODIFY",array('menu','tabs'));
658 if ($command != ""){
660 /* Walk through attribute list */
661 foreach ($this->attributes as $attr){
662 if (!is_array($this->$attr)){
663 $add_attrs[$attr] = $this->$attr;
664 }
665 }
666 $add_attrs['dn']=$this->dn;
668 $tmp = array();
669 foreach($add_attrs as $name => $value){
670 $tmp[$name] = strlen($name);
671 }
672 arsort($tmp);
674 /* Additional attributes */
675 foreach ($tmp as $name => $len){
676 $value = $add_attrs[$name];
677 $command= preg_replace("/%$name/", "$value", $command);
678 }
680 if (check_command($command)){
681 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command, "Execute");
682 exec($command,$arr);
683 foreach($arr as $str){
684 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
685 $command, "Result: ".$str);
686 }
687 } else {
688 $message= msgPool::cmdnotfound("POSTMODIFY", get_class($this));
689 msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
690 }
691 }
692 }
694 function postremove($add_attrs= array())
695 {
696 /* Find postremove entries for this class */
697 $command= $this->config->search(get_class($this), "POSTREMOVE",array('menu','tabs'));
698 if ($command != ""){
700 /* Walk through attribute list */
701 foreach ($this->attributes as $attr){
702 if (!is_array($this->$attr)){
703 $add_attrs[$attr] = $this->$attr;
704 }
705 }
706 $add_attrs['dn']=$this->dn;
708 $tmp = array();
709 foreach($add_attrs as $name => $value){
710 $tmp[$name] = strlen($name);
711 }
712 arsort($tmp);
714 /* Additional attributes */
715 foreach ($tmp as $name => $len){
716 $value = $add_attrs[$name];
717 $command= preg_replace("/%$name/", "$value", $command);
718 }
720 if (check_command($command)){
721 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
722 $command, "Execute");
724 exec($command,$arr);
725 foreach($arr as $str){
726 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
727 $command, "Result: ".$str);
728 }
729 } else {
730 $message= msgPool::cmdnotfound("POSTREMOVE", get_class($this));
731 msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
732 }
733 }
734 }
736 /* Create unique DN */
737 function create_unique_dn($attribute, $base)
738 {
739 $ldap= $this->config->get_ldap_link();
740 $base= preg_replace("/^,*/", "", $base);
742 /* Try to use plain entry first */
743 $dn= "$attribute=".$this->$attribute.",$base";
744 $ldap->cat ($dn, array('dn'));
745 if (!$ldap->fetch()){
746 return ($dn);
747 }
749 /* Look for additional attributes */
750 foreach ($this->attributes as $attr){
751 if ($attr == $attribute || $this->$attr == ""){
752 continue;
753 }
755 $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
756 $ldap->cat ($dn, array('dn'));
757 if (!$ldap->fetch()){
758 return ($dn);
759 }
760 }
762 /* None found */
763 return ("none");
764 }
766 function rebind($ldap, $referral)
767 {
768 $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
769 if (ldap_bind($ldap, $credentials['ADMIN'], $this->config->get_credentials($credentials['PASSWORD']))) {
770 $this->error = "Success";
771 $this->hascon=true;
772 $this->reconnect= true;
773 return (0);
774 } else {
775 $this->error = "Could not bind to " . $credentials['ADMIN'];
776 return NULL;
777 }
778 }
781 /* Recursively copy ldap object */
782 function _copy($src_dn,$dst_dn)
783 {
784 $ldap=$this->config->get_ldap_link();
785 $ldap->cat($src_dn);
786 $attrs= $ldap->fetch();
788 /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
789 $ds= ldap_connect($this->config->current['SERVER']);
790 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
791 if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
792 ldap_set_rebind_proc($ds, array(&$this, "rebind"));
793 }
795 $r=ldap_bind($ds,$this->config->current['ADMINDN'], $this->config->current['ADMINPASSWORD']);
796 $sr=ldap_read($ds, LDAP::fix($src_dn), "objectClass=*");
798 /* Fill data from LDAP */
799 $new= array();
800 if ($sr) {
801 $ei=ldap_first_entry($ds, $sr);
802 if ($ei) {
803 foreach($attrs as $attr => $val){
804 if ($info = @ldap_get_values_len($ds, $ei, $attr)){
805 for ($i= 0; $i<$info['count']; $i++){
806 if ($info['count'] == 1){
807 $new[$attr]= $info[$i];
808 } else {
809 $new[$attr][]= $info[$i];
810 }
811 }
812 }
813 }
814 }
815 }
817 /* close conncetion */
818 ldap_unbind($ds);
820 /* Adapt naming attribute */
821 $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
822 $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
823 $new[$dst_name]= LDAP::fix($dst_val);
825 /* Check if this is a department.
826 * If it is a dep. && there is a , override in his ou
827 * change \2C to , again, else this entry can't be saved ...
828 */
829 if((isset($new['ou'])) &&( preg_match("/\\,/",$new['ou']))){
830 $new['ou'] = preg_replace("/\\\\,/",",",$new['ou']);
831 }
833 /* Save copy */
834 $ldap->connect();
835 $ldap->cd($this->config->current['BASE']);
837 $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
839 /* FAIvariable=.../..., cn=..
840 could not be saved, because the attribute FAIvariable was different to
841 the dn FAIvariable=..., cn=... */
842 if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
843 $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
844 }
845 $ldap->cd($dst_dn);
846 $ldap->add($new);
848 if (!$ldap->success()){
849 trigger_error("Trying to save $dst_dn failed.",
850 E_USER_WARNING);
851 return(FALSE);
852 }
853 return(TRUE);
854 }
857 /* This is a workaround function. */
858 function copy($src_dn, $dst_dn)
859 {
860 /* Rename dn in possible object groups */
861 $ldap= $this->config->get_ldap_link();
862 $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.@LDAP::prepare4filter($src_dn).'))',
863 array('cn'));
864 while ($attrs= $ldap->fetch()){
865 $og= new ogroup($this->config, $ldap->getDN());
866 unset($og->member[$src_dn]);
867 $og->member[$dst_dn]= $dst_dn;
868 $og->save ();
869 }
871 $ldap->cat($dst_dn);
872 $attrs= $ldap->fetch();
873 if (count($attrs)){
874 trigger_error("Trying to overwrite ".LDAP::fix($dst_dn).", which already exists.",
875 E_USER_WARNING);
876 return (FALSE);
877 }
879 $ldap->cat($src_dn);
880 $attrs= $ldap->fetch();
881 if (!count($attrs)){
882 trigger_error("Trying to move ".LDAP::fix($src_dn).", which does not seem to exist.",
883 E_USER_WARNING);
884 return (FALSE);
885 }
887 $ldap->cd($src_dn);
888 $ldap->search("objectClass=*",array("dn"));
889 while($attrs = $ldap->fetch()){
890 $src = $attrs['dn'];
891 $dst = preg_replace("/".preg_quote($src_dn, '/')."$/",$dst_dn,$attrs['dn']);
892 $this->_copy($src,$dst);
893 }
894 return (TRUE);
895 }
899 /*! \brief Move a given ldap object indentified by $src_dn \
900 to the given destination $dst_dn \
901 * Ensure that all references are updated (ogroups) \
902 * Update ACLs \
903 * Update accessTo \
904 @param String The source dn.
905 @param String The destination dn.
906 @return Boolean TRUE on success else FALSE.
907 */
908 function rename($src_dn, $dst_dn)
909 {
910 $start = microtime(1);
912 /* Try to move the source entry to the destination position */
913 $ldap = $this->config->get_ldap_link();
914 $ldap->cd($this->config->current['BASE']);
915 $ldap->create_missing_trees(preg_replace("/^[^,]+,/","",$dst_dn));
917 if (!$ldap->rename_dn($src_dn,$dst_dn)){
918 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $src_dn, "", get_class()));
919 return(FALSE);
920 }
922 /* Get list of groups within this tree,
923 maybe we have to update ACL references.
924 */
925 $leaf_groups = get_list("(objectClass=posixGroup)",array("all"),$dst_dn,
926 array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
928 /* Get list of users within this tree,
929 maybe we have to update ACL references.
930 */
931 $leaf_users= get_list("(objectClass=gosaAccount)",array("all"),$dst_dn,
932 array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
935 /* Updated acls set for this groups */
936 foreach($leaf_groups as $group){
937 $new_dn = $group['dn'];
938 $old_dn = preg_replace("/".preg_quote($dst_dn, '/')."$/i",$src_dn,$new_dn);
939 $this->update_acls($old_dn,$new_dn);
940 }
942 /* Updated acls set for this users */
943 foreach($leaf_users as $user){
944 $new_dn = $user['dn'];
945 $old_dn = preg_replace("/".preg_quote($dst_dn, '/')."$/i",$src_dn,$new_dn);
946 $this->update_acls($old_dn,$new_dn);
947 }
949 /* Get all objectGroups defined in this database.
950 and check if there is an entry matching the source dn,
951 if this is the case, then update this objectgroup to use the new dn.
952 */
953 $ogroups = get_sub_list("(&(objectClass=gosaGroupOfNames)(member=*))","ogroups",
954 array(get_ou("ogroupRDN")),$this->config->current['BASE'],array("member"),
955 GL_SUBSEARCH | GL_NO_ACL_CHECK) ;
957 /* Walk through all objectGroups and check if there are
958 members matching the source dn
959 */
960 foreach($ogroups as $ogroup){
961 if(isset($ogroup['member'])){
963 /* Reset class object, this will be initialized with class_ogroup on demand
964 */
965 $o_ogroup = NULL;
966 for($i = 0 ; $i < $ogroup['member']['count'] ; $i ++){
968 $c_mem = $ogroup['member'][$i];
970 if(preg_match("/".preg_quote($src_dn, '/')."$/i",$c_mem)){
972 $d_mem = preg_replace("/".preg_quote($src_dn, '/')."$/i",$dst_dn,$ogroup['member'][$i]);
974 if($o_ogroup == NULL){
975 $o_ogroup = new ogroup($this->config,$ogroup['dn']);
976 }
978 unset($o_ogroup->member[$c_mem]);
979 $o_ogroup->member[$d_mem]= $d_mem;
980 }
981 }
983 /* Save object group if there were changes made on the membership */
984 if($o_ogroup != NULL){
985 $o_ogroup->save();
986 }
987 }
988 }
990 /* Check if there are gosa departments moved.
991 If there were deps moved, the force reload of config->deps.
992 */
993 $leaf_deps= get_list("(objectClass=gosaDepartment)",array("all"),$dst_dn,
994 array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
996 if(count($leaf_deps)){
997 $this->config->get_departments();
998 $this->config->make_idepartments();
999 session::set("config",$this->config);
1000 $ui =get_userinfo();
1001 $ui->reset_acl_cache();
1002 }
1004 return(TRUE);
1005 }
1009 function move($src_dn, $dst_dn)
1010 {
1011 /* Do not copy if only upper- lowercase has changed */
1012 if(strtolower($src_dn) == strtolower($dst_dn)){
1013 return(TRUE);
1014 }
1017 /* Try to move the entry instead of copy & delete
1018 */
1019 if(TRUE){
1021 /* Try to move with ldap routines, if this was not successfull
1022 fall back to the old style copy & remove method
1023 */
1024 if($this->rename($src_dn, $dst_dn)){
1025 return(TRUE);
1026 }else{
1027 // See code below.
1028 }
1029 }
1031 /* Copy source to destination */
1032 if (!$this->copy($src_dn, $dst_dn)){
1033 return (FALSE);
1034 }
1036 /* Delete source */
1037 $ldap= $this->config->get_ldap_link();
1038 $ldap->rmdir_recursive($src_dn);
1039 if (!$ldap->success()){
1040 trigger_error("Trying to delete $src_dn failed.",
1041 E_USER_WARNING);
1042 return (FALSE);
1043 }
1045 return (TRUE);
1046 }
1049 /* Move/Rename complete trees */
1050 function recursive_move($src_dn, $dst_dn)
1051 {
1052 /* Check if the destination entry exists */
1053 $ldap= $this->config->get_ldap_link();
1055 /* Check if destination exists - abort */
1056 $ldap->cat($dst_dn, array('dn'));
1057 if ($ldap->fetch()){
1058 trigger_error("recursive_move $dst_dn already exists.",
1059 E_USER_WARNING);
1060 return (FALSE);
1061 }
1063 $this->copy($src_dn, $dst_dn);
1065 /* Remove src_dn */
1066 $ldap->cd($src_dn);
1067 $ldap->recursive_remove($src_dn);
1068 return (TRUE);
1069 }
1072 function handle_post_events($mode, $add_attrs= array())
1073 {
1074 switch ($mode){
1075 case "add":
1076 $this->postcreate($add_attrs);
1077 break;
1079 case "modify":
1080 $this->postmodify($add_attrs);
1081 break;
1083 case "remove":
1084 $this->postremove($add_attrs);
1085 break;
1086 }
1087 }
1090 function saveCopyDialog(){
1091 }
1094 function getCopyDialog(){
1095 return(array("string"=>"","status"=>""));
1096 }
1099 function PrepareForCopyPaste($source)
1100 {
1101 $todo = $this->attributes;
1102 if(isset($this->CopyPasteVars)){
1103 $todo = array_merge($todo,$this->CopyPasteVars);
1104 }
1106 if(count($this->objectclasses)){
1107 $this->is_account = TRUE;
1108 foreach($this->objectclasses as $class){
1109 if(!in_array($class,$source['objectClass'])){
1110 $this->is_account = FALSE;
1111 }
1112 }
1113 }
1115 foreach($todo as $var){
1116 if (isset($source[$var])){
1117 if(isset($source[$var]['count'])){
1118 if($source[$var]['count'] > 1){
1119 $this->$var = array();
1120 $tmp = array();
1121 for($i = 0 ; $i < $source[$var]['count']; $i++){
1122 $tmp = $source[$var][$i];
1123 }
1124 $this->$var = $tmp;
1125 }else{
1126 $this->$var = $source[$var][0];
1127 }
1128 }else{
1129 $this->$var= $source[$var];
1130 }
1131 }
1132 }
1133 }
1135 function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1136 {
1137 /* Skip tagging?
1138 If this is called from departmentGeneric, we have to skip this
1139 tagging procedure.
1140 */
1141 if($this->skipTagging){
1142 return;
1143 }
1145 /* No dn? Self-operation... */
1146 if ($dn == ""){
1147 $dn= $this->dn;
1149 /* No tag? Find it yourself... */
1150 if ($tag == ""){
1151 $len= strlen($dn);
1153 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
1154 $relevant= array();
1155 foreach ($this->config->adepartments as $key => $ntag){
1157 /* This one is bigger than our dn, its not relevant... */
1158 if ($len < strlen($key)){
1159 continue;
1160 }
1162 /* This one matches with the latter part. Break and don't fix this entry */
1163 if (preg_match('/(^|,)'.preg_quote($key, '/').'$/', $dn)){
1164 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
1165 $relevant[strlen($key)]= $ntag;
1166 continue;
1167 }
1169 }
1171 /* If we've some relevant tags to set, just get the longest one */
1172 if (count($relevant)){
1173 ksort($relevant);
1174 $tmp= array_keys($relevant);
1175 $idx= end($tmp);
1176 $tag= $relevant[$idx];
1177 $this->gosaUnitTag= $tag;
1178 }
1179 }
1180 }
1182 /* Remove tags that may already be here... */
1183 remove_objectClass("gosaAdministrativeUnitTag", $at);
1184 if (isset($at['gosaUnitTag'])){
1185 unset($at['gosaUnitTag']);
1186 }
1188 /* Set tag? */
1189 if ($tag != ""){
1190 add_objectClass("gosaAdministrativeUnitTag", $at);
1191 $at['gosaUnitTag']= $tag;
1192 }
1194 /* Initially this object was tagged.
1195 - But now, it is no longer inside a tagged department.
1196 So force the remove of the tag.
1197 (objectClass was already removed obove)
1198 */
1199 if($tag == "" && $this->gosaUnitTag){
1200 $at['gosaUnitTag'] = array();
1201 }
1202 }
1205 /* Add possibility to stop remove process */
1206 function allow_remove()
1207 {
1208 $reason= "";
1209 return $reason;
1210 }
1213 /* Create a snapshot of the current object */
1214 function create_snapshot($type= "snapshot", $description= array())
1215 {
1217 /* Check if snapshot functionality is enabled */
1218 if(!$this->snapshotEnabled()){
1219 return;
1220 }
1222 /* Get configuration from gosa.conf */
1223 $config = $this->config;
1225 /* Create lokal ldap connection */
1226 $ldap= $this->config->get_ldap_link();
1227 $ldap->cd($this->config->current['BASE']);
1229 /* check if there are special server configurations for snapshots */
1230 if($config->get_cfg_value("snapshotURI") == ""){
1232 /* Source and destination server are both the same, just copy source to dest obj */
1233 $ldap_to = $ldap;
1234 $snapldapbase = $this->config->current['BASE'];
1236 }else{
1237 $server = $config->get_cfg_value("snapshotURI");
1238 $user = $config->get_cfg_value("snapshotAdminDn");
1239 $password = $config->get_cfg_value("snapshotAdminPassword");
1240 $snapldapbase = $config->get_cfg_value("snapshotBase");
1242 $ldap_to = new ldapMultiplexer(new LDAP($user,$password, $server));
1243 $ldap_to -> cd($snapldapbase);
1245 if (!$ldap_to->success()){
1246 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1247 }
1249 }
1251 /* check if the dn exists */
1252 if ($ldap->dn_exists($this->dn)){
1254 /* Extract seconds & mysecs, they are used as entry index */
1255 list($usec, $sec)= explode(" ", microtime());
1257 /* Collect some infos */
1258 $base = $this->config->current['BASE'];
1259 $snap_base = $config->get_cfg_value("snapshotBase");
1260 $base_of_object = preg_replace ('/^[^,]+,/i', '', $this->dn);
1261 $new_base = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1263 /* Create object */
1264 #$data = preg_replace('/^dn:.*\n/', '', $ldap->gen_ldif($this->dn,"(!(objectClass=gosaDepartment))"));
1265 $data = $ldap->gen_ldif($this->dn,"(&(!(objectClass=gosaDepartment))(!(objectClass=FAIclass)))");
1266 $newName = preg_replace("/\./", "", $sec."-".$usec);
1267 $target= array();
1268 $target['objectClass'] = array("top", "gosaSnapshotObject");
1269 $target['gosaSnapshotData'] = gzcompress($data, 6);
1270 $target['gosaSnapshotType'] = $type;
1271 $target['gosaSnapshotDN'] = $this->dn;
1272 $target['description'] = $description;
1273 $target['gosaSnapshotTimestamp'] = $newName;
1275 /* Insert the new snapshot
1276 But we have to check first, if the given gosaSnapshotTimestamp
1277 is already used, in this case we should increment this value till there is
1278 an unused value. */
1279 $new_dn = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1280 $ldap_to->cat($new_dn);
1281 while($ldap_to->count()){
1282 $ldap_to->cat($new_dn);
1283 $newName = preg_replace("/\./", "", $sec."-".($usec++));
1284 $new_dn = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1285 $target['gosaSnapshotTimestamp'] = $newName;
1286 }
1288 /* Inset this new snapshot */
1289 $ldap_to->cd($snapldapbase);
1290 $ldap_to->create_missing_trees($snapldapbase);
1291 $ldap_to->create_missing_trees($new_base);
1292 $ldap_to->cd($new_dn);
1293 $ldap_to->add($target);
1294 if (!$ldap_to->success()){
1295 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $new_dn, LDAP_ADD, get_class()));
1296 }
1298 if (!$ldap->success()){
1299 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $new_base, "", get_class()));
1300 }
1302 }
1303 }
1305 function remove_snapshot($dn)
1306 {
1307 $ui = get_userinfo();
1308 $old_dn = $this->dn;
1309 $this->dn = $dn;
1310 $ldap = $this->config->get_ldap_link();
1311 $ldap->cd($this->config->current['BASE']);
1312 $ldap->rmdir_recursive($dn);
1313 $this->dn = $old_dn;
1314 }
1317 /* returns true if snapshots are enabled, and false if it is disalbed
1318 There will also be some errors psoted, if the configuration failed */
1319 function snapshotEnabled()
1320 {
1321 $config = $this->config;
1322 if($config->get_cfg_value("enableSnapshots") == "true"){
1323 /* Check if the snapshot_base is defined */
1324 if ($config->get_cfg_value("snapshotBase") == ""){
1325 msg_dialog::display(_("Configuration error"), sprintf(_("The snapshot functionality is enabled, but the required variable '%s' is not set."),"snapshotBase"), ERROR_DIALOG);
1326 return(FALSE);
1327 }
1329 /* check if there are special server configurations for snapshots */
1330 if ($config->get_cfg_value("snapshotURI") != ""){
1332 /* check if all required vars are available to create a new ldap connection */
1333 $missing = "";
1334 foreach(array("snapshotURI","snapshotAdminDn","snapshotAdminPassword","snapshotBase") as $var){
1335 if($config->get_cfg_value($var) == ""){
1336 $missing .= $var." ";
1337 msg_dialog::display(_("Configuration error"), sprintf(_("The snapshot functionality is enabled, but the required variable '%s' is not set."), $missing), ERROR_DIALOG);
1338 return(FALSE);
1339 }
1340 }
1341 }
1342 return(TRUE);
1343 }
1344 return(FALSE);
1345 }
1348 /* Return available snapshots for the given base
1349 */
1350 function Available_SnapsShots($dn,$raw = false)
1351 {
1352 if(!$this->snapshotEnabled()) return(array());
1354 /* Create an additional ldap object which
1355 points to our ldap snapshot server */
1356 $ldap= $this->config->get_ldap_link();
1357 $ldap->cd($this->config->current['BASE']);
1358 $cfg= &$this->config->current;
1360 /* check if there are special server configurations for snapshots */
1361 if($this->config->get_cfg_value("snapshotURI") == ""){
1362 $ldap_to = $ldap;
1363 }else{
1364 $server = $this->config->get_cfg_value("snapshotURI");
1365 $user = $this->config->get_cfg_value("snapshotAdminDn");
1366 $password = $this->config->get_cfg_value("snapshotAdminPassword");
1367 $snapldapbase = $this->config->get_cfg_value("snapshotBase");
1368 $ldap_to = new ldapMultiplexer(new LDAP($user,$password, $server));
1369 $ldap_to -> cd($snapldapbase);
1370 if (!$ldap_to->success()){
1371 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1372 }
1373 }
1375 /* Prepare bases and some other infos */
1376 $base = $this->config->current['BASE'];
1377 $snap_base = $this->config->get_cfg_value("snapshotBase");
1378 $base_of_object = preg_replace ('/^[^,]+,/i', '', $dn);
1379 $new_base = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1380 $tmp = array();
1382 /* Fetch all objects with gosaSnapshotDN=$dn */
1383 $ldap_to->cd($new_base);
1384 $ldap_to->ls("(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN=".$dn."))",$new_base,
1385 array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description"));
1387 /* Put results into a list and add description if missing */
1388 while($entry = $ldap_to->fetch()){
1389 if(!isset($entry['description'][0])){
1390 $entry['description'][0] = "";
1391 }
1392 $tmp[] = $entry;
1393 }
1395 /* Return the raw array, or format the result */
1396 if($raw){
1397 return($tmp);
1398 }else{
1399 $tmp2 = array();
1400 foreach($tmp as $entry){
1401 $tmp2[base64_encode($entry['dn'])] = $entry['description'][0];
1402 }
1403 }
1404 return($tmp2);
1405 }
1408 function getAllDeletedSnapshots($base_of_object,$raw = false)
1409 {
1410 if(!$this->snapshotEnabled()) return(array());
1412 /* Create an additional ldap object which
1413 points to our ldap snapshot server */
1414 $ldap= $this->config->get_ldap_link();
1415 $ldap->cd($this->config->current['BASE']);
1416 $cfg= &$this->config->current;
1418 /* check if there are special server configurations for snapshots */
1419 if($this->config->get_cfg_value("snapshotURI") == ""){
1420 $ldap_to = $ldap;
1421 }else{
1422 $server = $this->config->get_cfg_value("snapshotURI");
1423 $user = $this->config->get_cfg_value("snapshotAdminDn");
1424 $password = $this->config->get_cfg_value("snapshotAdminPassword");
1425 $snapldapbase = $this->config->get_cfg_value("snapshotBase");
1426 $ldap_to = new ldapMultiplexer(new LDAP($user,$password, $server));
1427 $ldap_to -> cd($snapldapbase);
1428 if (!$ldap_to->success()){
1429 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1430 }
1431 }
1433 /* Prepare bases */
1434 $base = $this->config->current['BASE'];
1435 $snap_base = $this->config->get_cfg_value("snapshotBase");
1436 $new_base = preg_replace("/".preg_quote($base, '/')."$/","",$base_of_object).$snap_base;
1438 /* Fetch all objects and check if they do not exist anymore */
1439 $ui = get_userinfo();
1440 $tmp = array();
1441 $ldap_to->cd($new_base);
1442 $ldap_to->ls("(objectClass=gosaSnapshotObject)",$new_base,array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description"));
1443 while($entry = $ldap_to->fetch()){
1445 $chk = str_replace($new_base,"",$entry['dn']);
1446 if(preg_match("/,ou=/",$chk)) continue;
1448 if(!isset($entry['description'][0])){
1449 $entry['description'][0] = "";
1450 }
1451 $tmp[] = $entry;
1452 }
1454 /* Check if entry still exists */
1455 foreach($tmp as $key => $entry){
1456 $ldap->cat($entry['gosaSnapshotDN'][0]);
1457 if($ldap->count()){
1458 unset($tmp[$key]);
1459 }
1460 }
1462 /* Format result as requested */
1463 if($raw) {
1464 return($tmp);
1465 }else{
1466 $tmp2 = array();
1467 foreach($tmp as $key => $entry){
1468 $tmp2[base64_encode($entry['dn'])] = $entry['description'][0];
1469 }
1470 }
1471 return($tmp2);
1472 }
1475 /* Restore selected snapshot */
1476 function restore_snapshot($dn)
1477 {
1478 if(!$this->snapshotEnabled()) return(array());
1480 $ldap= $this->config->get_ldap_link();
1481 $ldap->cd($this->config->current['BASE']);
1482 $cfg= &$this->config->current;
1484 /* check if there are special server configurations for snapshots */
1485 if($this->config->get_cfg_value("snapshotURI") == ""){
1486 $ldap_to = $ldap;
1487 }else{
1488 $server = $this->config->get_cfg_value("snapshotURI");
1489 $user = $this->config->get_cfg_value("snapshotAdminDn");
1490 $password = $this->config->get_cfg_value("snapshotAdminPassword");
1491 $snapldapbase = $this->config->get_cfg_value("snapshotBase");
1492 $ldap_to = new ldapMultiplexer(new LDAP($user,$password, $server));
1493 $ldap_to -> cd($snapldapbase);
1494 if (!$ldap_to->success()){
1495 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1496 }
1497 }
1499 /* Get the snapshot */
1500 $ldap_to->cat($dn);
1501 $restoreObject = $ldap_to->fetch();
1503 /* Prepare import string */
1504 $data = gzuncompress($ldap_to->get_attribute($dn,'gosaSnapshotData'));
1506 /* Import the given data */
1507 $err = "";
1508 $ldap->import_complete_ldif($data,$err,false,false);
1509 if (!$ldap->success()){
1510 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, "", get_class()));
1511 }
1512 }
1515 function showSnapshotDialog($base,$baseSuffixe,&$parent)
1516 {
1517 $once = true;
1518 $ui = get_userinfo();
1519 $this->parent = $parent;
1521 foreach($_POST as $name => $value){
1523 /* Create a new snapshot, display a dialog */
1524 if(preg_match("/^CreateSnapShotDialog_/",$name) && $once){
1525 $once = false;
1526 $entry = preg_replace("/^CreateSnapShotDialog_/","",$name);
1527 $entry = base64_decode(preg_replace("/_[xy]$/","",$entry));
1529 if(!empty($entry) && $ui->allow_snapshot_create($entry,$this->parent->acl_module)){
1530 $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1531 }else{
1532 msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to create a snapshot for %s."),$entry),ERROR_DIALOG);
1533 }
1534 }
1536 /* Restore a snapshot, display a dialog with all snapshots of the current object */
1537 if(preg_match("/^RestoreSnapShotDialog_/",$name) && $once){
1538 $once = false;
1539 $entry = preg_replace("/^RestoreSnapShotDialog_/","",$name);
1540 $entry = base64_decode(preg_replace("/_[xy]$/","",$entry));
1541 if(!empty($entry) && $ui->allow_snapshot_restore($entry,$this->parent->acl_module)){
1542 $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1543 $this->snapDialog->display_restore_dialog = true;
1544 }else{
1545 msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1546 }
1547 }
1549 /* Restore one of the already deleted objects */
1550 if(((isset($_POST['menu_action']) && $_POST['menu_action'] == "RestoreDeletedSnapShot")
1551 || preg_match("/^RestoreDeletedSnapShot_/",$name)) && $once){
1552 $once = false;
1554 if($ui->allow_snapshot_restore($base,$this->parent->acl_module)){
1555 $this->snapDialog = new SnapShotDialog($this->config,"",$this);
1556 $this->snapDialog->set_snapshot_bases($baseSuffixe);
1557 $this->snapDialog->display_restore_dialog = true;
1558 $this->snapDialog->display_all_removed_objects = true;
1559 }else{
1560 msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$base),ERROR_DIALOG);
1561 }
1562 }
1564 /* Restore selected snapshot */
1565 if(preg_match("/^RestoreSnapShot_/",$name) && $once){
1566 $once = false;
1567 $entry = preg_replace("/^RestoreSnapShot_/","",$name);
1568 $entry = base64_decode(trim(preg_replace("/_[xy]$/","",$entry)));
1569 if(!empty($entry) && $ui->allow_snapshot_restore($entry,$this->parent->acl_module)){
1570 $this->restore_snapshot($entry);
1571 $this->snapDialog = NULL;
1572 }else{
1573 msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1574 }
1575 }
1576 }
1578 /* Create a new snapshot requested, check
1579 the given attributes and create the snapshot*/
1580 if(isset($_POST['CreateSnapshot']) && is_object($this->snapDialog)){
1581 $this->snapDialog->save_object();
1582 $msgs = $this->snapDialog->check();
1583 if(count($msgs)){
1584 foreach($msgs as $msg){
1585 msg_dialog::display(_("Error"), $msg, ERROR_DIALOG);
1586 }
1587 }else{
1588 $this->dn = $this->snapDialog->dn;
1589 $this->create_snapshot("snapshot",$this->snapDialog->CurrentDescription);
1590 $this->snapDialog = NULL;
1591 }
1592 }
1594 /* Restore is requested, restore the object with the posted dn .*/
1595 if((isset($_POST['RestoreSnapshot'])) && (isset($_POST['SnapShot']))){
1596 }
1598 if(isset($_POST['CancelSnapshot'])){
1599 $this->snapDialog = NULL;
1600 }
1602 if(is_object($this->snapDialog )){
1603 $this->snapDialog->save_object();
1604 return($this->snapDialog->execute());
1605 }
1606 }
1609 static function plInfo()
1610 {
1611 return array();
1612 }
1615 function set_acl_base($base)
1616 {
1617 $this->acl_base= $base;
1618 }
1621 function set_acl_category($category)
1622 {
1623 $this->acl_category= "$category/";
1624 }
1627 function acl_is_writeable($attribute,$skip_write = FALSE)
1628 {
1629 $ui= get_userinfo();
1630 return preg_match('/w/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute, $skip_write));
1631 }
1634 function acl_is_readable($attribute)
1635 {
1636 $ui= get_userinfo();
1637 return preg_match('/r/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute));
1638 }
1641 function acl_is_createable($base ="")
1642 {
1643 $ui= get_userinfo();
1644 if($base == "") $base = $this->acl_base;
1645 return preg_match('/c/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1646 }
1649 function acl_is_removeable($base ="")
1650 {
1651 $ui= get_userinfo();
1652 if($base == "") $base = $this->acl_base;
1653 return preg_match('/d/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1654 }
1657 function acl_is_moveable($base = "")
1658 {
1659 $ui= get_userinfo();
1660 if($base == "") $base = $this->acl_base;
1661 return preg_match('/m/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1662 }
1665 function acl_have_any_permissions()
1666 {
1667 }
1670 function getacl($attribute,$skip_write= FALSE)
1671 {
1672 $ui= get_userinfo();
1673 return $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute,$skip_write);
1674 }
1677 /*! \brief Returns a list of all available departments for this object.
1678 If this object is new, all departments we are allowed to create a new user in are returned.
1679 If this is an existing object, return all deps. we are allowed to move tis object too.
1681 @return Array [dn] => "..name" // All deps. we are allowed to act on.
1682 */
1683 function get_allowed_bases()
1684 {
1685 $ui = get_userinfo();
1686 $deps = array();
1688 /* Is this a new object ? Or just an edited existing object */
1689 if(!$this->initially_was_account && $this->is_account){
1690 $new = true;
1691 }else{
1692 $new = false;
1693 }
1695 foreach($this->config->idepartments as $dn => $name){
1696 if($new && $this->acl_is_createable($dn)){
1697 $deps[$dn] = $name;
1698 }elseif(!$new && $this->acl_is_moveable($dn)){
1699 $deps[$dn] = $name;
1700 }
1701 }
1703 /* Add current base */
1704 if(isset($this->base) && isset($this->config->idepartments[$this->base])){
1705 $deps[$this->base] = $this->config->idepartments[$this->base];
1706 }elseif(strtolower($this->dn) == strtolower($this->config->current['BASE'])){
1708 }else{
1709 trigger_error("Cannot return list of departments, no default base found in class ".get_class($this).". ".$this->base);
1710 }
1711 return($deps);
1712 }
1715 /* This function modifies object acls too, if an object is moved.
1716 * $old_dn specifies the actually used dn
1717 * $new_dn specifies the destiantion dn
1718 */
1719 function update_acls($old_dn,$new_dn,$output_changes = FALSE)
1720 {
1721 /* Check if old_dn is empty. This should never happen */
1722 if(empty($old_dn) || empty($new_dn)){
1723 trigger_error("Failed to check acl dependencies, wrong dn given.");
1724 return;
1725 }
1727 /* Update userinfo if necessary */
1728 $ui = session::get('ui');
1729 if($ui->dn == $old_dn){
1730 $ui->dn = $new_dn;
1731 session::set('ui',$ui);
1732 new log("view","acl/".get_class($this),$this->dn,array(),"Updated current user dn from '".$old_dn."' to '".$new_dn."'");
1733 }
1735 /* Object was moved, ensure that all acls will be moved too */
1736 if($new_dn != $old_dn && $old_dn != "new"){
1738 /* get_ldap configuration */
1739 $update = array();
1740 $ldap = $this->config->get_ldap_link();
1741 $ldap->cd ($this->config->current['BASE']);
1742 $ldap->search("(&(objectClass=gosaAcl)(gosaAclEntry=*))",array("cn","gosaAclEntry"));
1743 while($attrs = $ldap->fetch()){
1745 $acls = array();
1747 /* Reset vars */
1748 $found = false;
1750 /* Walk through acls */
1751 for($i = 0 ; $i < $attrs['gosaAclEntry']['count'] ; $i ++ ){
1753 /* Get Acl parts */
1754 $acl_parts = split(":",$attrs['gosaAclEntry'][$i]);
1756 /* Get every single member for this acl */
1757 $members = array();
1758 if(preg_match("/,/",$acl_parts[2])){
1759 $members = split(",",$acl_parts[2]);
1760 }else{
1761 $members = array($acl_parts[2]);
1762 }
1764 /* Check if member match current dn */
1765 foreach($members as $key => $member){
1766 $member = base64_decode($member);
1767 if($member == $old_dn){
1768 $found = true;
1769 $members[$key] = base64_encode($new_dn);
1770 }
1771 }
1773 /* Create new member string */
1774 $new_members = "";
1775 foreach($members as $member){
1776 $new_members .= $member.",";
1777 }
1778 $new_members = preg_replace("/,$/","",$new_members);
1779 $acl_parts[2] = $new_members;
1781 /* Reconstruckt acl entry */
1782 $acl_str ="";
1783 foreach($acl_parts as $t){
1784 $acl_str .= $t.":";
1785 }
1786 $acl_str = preg_replace("/:$/","",$acl_str);
1787 $acls[] = $acl_str;
1788 }
1790 /* Acls for this object must be adjusted */
1791 if($found){
1793 $debug_info= _("Changing ACL dn")." : <br> -"._("from")." <b> ".
1794 $old_dn."</b><br> -"._("to")." <b>".$new_dn."</b><br>";
1795 @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,$debug_info,"ACL");
1797 $update[$attrs['dn']] =array();
1798 foreach($acls as $acl){
1799 $update[$attrs['dn']]['gosaAclEntry'][] = $acl;
1800 }
1801 }
1802 }
1804 /* Write updated acls */
1805 foreach($update as $dn => $attrs){
1806 $ldap->cd($dn);
1807 $ldap->modify($attrs);
1808 }
1809 }
1810 }
1814 /* This function enables the entry Serial ID check.
1815 * If an entry was edited while we have edited the entry too,
1816 * an error message will be shown.
1817 * To configure this check correctly read the FAQ.
1818 */
1819 function enable_CSN_check()
1820 {
1821 $this->CSN_check_active =TRUE;
1822 $this->entryCSN = getEntryCSN($this->dn);
1823 }
1826 /*! \brief Prepares the plugin to be used for multiple edit
1827 * Update plugin attributes with given array of attribtues.
1828 * @param array Array with attributes that must be updated.
1829 */
1830 function init_multiple_support($attrs,$all)
1831 {
1832 $ldap= $this->config->get_ldap_link();
1833 $this->multi_attrs = $attrs;
1834 $this->multi_attrs_all= $all;
1836 /* Copy needed attributes */
1837 foreach ($this->attributes as $val){
1838 $found= array_key_ics($val, $this->multi_attrs);
1839 if ($found != ""){
1840 if(isset($this->multi_attrs["$found"][0])){
1841 $this->$val= $this->multi_attrs["$found"][0];
1842 }
1843 }
1844 }
1845 }
1848 /*! \brief Enables multiple support for this plugin
1849 */
1850 function enable_multiple_support()
1851 {
1852 $this->ignore_account = TRUE;
1853 $this->multiple_support_active = TRUE;
1854 }
1857 /*! \brief Returns all values that have been modfied in multiple edit mode.
1858 @return array Cotaining all mdofied values.
1859 */
1860 function get_multi_edit_values()
1861 {
1862 $ret = array();
1863 foreach($this->attributes as $attr){
1864 if(in_array($attr,$this->multi_boxes)){
1865 $ret[$attr] = $this->$attr;
1866 }
1867 }
1868 return($ret);
1869 }
1872 /*! \brief Update class variables with values collected by multiple edit.
1873 */
1874 function set_multi_edit_values($attrs)
1875 {
1876 foreach($attrs as $name => $value){
1877 $this->$name = $value;
1878 }
1879 }
1882 /*! \brief execute plugin
1884 Generates the html output for this node
1885 */
1886 function multiple_execute()
1887 {
1888 /* This one is empty currently. Fabian - please fill in the docu code */
1889 session::set('current_class_for_help',get_class($this));
1891 /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
1892 session::set('LOCK_VARS_TO_USE',array());
1893 session::set('LOCK_VARS_USED',array());
1895 return("Multiple edit is currently not implemented for this plugin.");
1896 }
1899 /*! \brief Save HTML posted data to object for multiple edit
1900 */
1901 function multiple_save_object()
1902 {
1903 if(empty($this->entryCSN) && $this->CSN_check_active){
1904 $this->entryCSN = getEntryCSN($this->dn);
1905 }
1907 /* Save values to object */
1908 $this->multi_boxes = array();
1909 foreach ($this->attributes as $val){
1911 /* Get selected checkboxes from multiple edit */
1912 if(isset($_POST["use_".$val])){
1913 $this->multi_boxes[] = $val;
1914 }
1916 if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
1918 /* Check for modifications */
1919 if (get_magic_quotes_gpc()) {
1920 $data= stripcslashes($_POST["$val"]);
1921 } else {
1922 $data= $this->$val = $_POST["$val"];
1923 }
1924 if ($this->$val != $data){
1925 $this->is_modified= TRUE;
1926 }
1928 /* IE post fix */
1929 if(isset($data[0]) && $data[0] == chr(194)) {
1930 $data = "";
1931 }
1932 $this->$val= $data;
1933 }
1934 }
1935 }
1938 /*! \brief Returns all attributes of this plugin,
1939 to be able to detect multiple used attributes
1940 in multi_plugg::detect_multiple_used_attributes().
1941 @return array Attributes required for intialization of multi_plug
1942 */
1943 public function get_multi_init_values()
1944 {
1945 $attrs = $this->attrs;
1946 return($attrs);
1947 }
1950 /*! \brief Check given values in multiple edit
1951 @return array Error messages
1952 */
1953 function multiple_check()
1954 {
1955 $message = plugin::check();
1956 return($message);
1957 }
1960 /*! \brief Returns the snapshot header part for "Actions" menu in management dialogs
1961 @param $layer_menu
1962 */
1963 function get_snapshot_header($base,$category)
1964 {
1965 $str = "";
1966 $ui = get_userinfo();
1967 if($this->snapshotEnabled() && $ui->allow_snapshot_restore($base,$category)){
1969 $ok = false;
1970 foreach($this->get_used_snapshot_bases() as $base){
1971 $ok |= count($this->getAllDeletedSnapshots($base)) >= 1 ;
1972 }
1974 if($ok){
1975 $str = "..|<img class='center' src='images/lists/restore.png' ".
1976 "alt='"._("Restore")."'> "._("Restore"). "|RestoreDeletedSnapShot|\n";
1977 }else{
1978 $str = "..|<img class='center' src='images/lists/restore_grey.png' alt=''> "._("Restore")."||\n";
1979 }
1980 }
1981 return($str);
1982 }
1985 function get_snapshot_action($base,$category)
1986 {
1987 $str= "";
1988 $ui = get_userinfo();
1989 if($this->snapshotEnabled()){
1990 if ($ui->allow_snapshot_restore($base,$category)){
1992 if(count($this->Available_SnapsShots($base))){
1993 $str.= "<input class='center' type='image' src='images/lists/restore.png'
1994 alt='"._("Restore snapshot")."' name='RestoreSnapShotDialog_".base64_encode($base)."' title='"._("Restore snapshot")."'> ";
1995 } else {
1996 $str = "<img class='center' src='images/lists/restore_grey.png' alt=''> ";
1997 }
1998 }
1999 if($ui->allow_snapshot_create($base,$category)){
2000 $str.= "<input class='center' type='image' src='images/snapshot.png'
2001 alt='"._("Create snapshot")."' name='CreateSnapShotDialog_".base64_encode($base)."'
2002 title='"._("Create a new snapshot from this object")."'> ";
2003 }else{
2004 $str = "<img class='center' src='images/empty.png' alt=' '> ";
2005 }
2006 }
2008 return($str);
2009 }
2012 function get_copypaste_action($base,$category,$class,$copy = TRUE, $cut = TRUE)
2013 {
2014 $ui = get_userinfo();
2015 $action = "";
2016 if($this->CopyPasteHandler){
2017 if($cut){
2018 if($ui->is_cutable($base,$category,$class)){
2019 $action .= "<input class='center' type='image'
2020 src='images/lists/cut.png' alt='"._("cut")."' name='cut_%KEY%' title='"._("Cut this entry")."'> ";
2021 }else{
2022 $action.="<img src='images/empty.png' alt=' ' class='center'> ";
2023 }
2024 }
2025 if($copy){
2026 if($ui->is_copyable($base,$category,$class)){
2027 $action.= "<input class='center' type='image'
2028 src='images/lists/copy.png' alt='"._("copy")."' name='copy_%KEY%' title='"._("Copy this entry")."'> ";
2029 }else{
2030 $action.="<img src='images/empty.png' alt=' ' class='center'> ";
2031 }
2032 }
2033 }
2035 return($action);
2036 }
2039 function get_copypaste_header($base,$category,$copy = TRUE, $cut = TRUE)
2040 {
2041 $s = "";
2042 $ui =get_userinfo();
2044 if(!is_array($category)){
2045 $category = array($category);
2046 }
2048 /* Check permissions for each category, if there is at least one category which
2049 support read or paste permissions for the given base, then display the specific actions.
2050 */
2051 $readable = $pasteable = TRUE;
2052 foreach($category as $cat){
2053 $readable |= $ui->get_category_permissions($base,$cat);
2054 $pasteable|= $ui->is_pasteable($base,$cat);
2055 }
2057 if(($cut || $copy) && isset($this->CopyPasteHandler) && is_object($this->CopyPasteHandler)){
2058 if($readable){
2059 $s.= "..|---|\n";
2060 if($copy){
2061 $s.= "..|<img src='images/lists/copy.png' alt='' border='0' class='center'>".
2062 " "._("Copy")."|"."multiple_copy_systems|\n";
2063 }
2064 if($cut){
2065 $s.= "..|<img src='images/lists/cut.png' alt='' border='0' class='center'>".
2066 " "._("Cut")."|"."multiple_cut_systems|\n";
2067 }
2068 }
2070 if($pasteable){
2071 if($this->CopyPasteHandler->entries_queued()){
2072 $img = "<img border='0' class='center' src='images/lists/paste.png' alt=''>";
2073 $s.="..|".$img." "._("Paste")."|editPaste|\n";
2074 }else{
2075 $img = "<img border='0' class='center' src='images/lists/paste-grey.png' alt=''>";
2076 $s.="..|".$img." "._("Paste")."\n";
2077 }
2078 }
2079 }
2080 return($s);
2081 }
2084 function get_used_snapshot_bases()
2085 {
2086 return(array());
2087 }
2088 }
2090 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
2091 ?>