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= $this->attrs["$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 ("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 (!isset($this->attrs['objectClass']) || !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 (preg_match('/^[0-9]+$/', $index)){
211 unset($this->saved_attributes[$index]);
212 continue;
213 }
214 if (!in_array($index, $this->attributes) && $index != "objectClass"){
215 unset($this->saved_attributes[$index]);
216 continue;
217 }
219 if (isset($this->saved_attributes[$index][0])){
220 if(!isset($this->saved_attributes[$index]["count"])){
221 $this->saved_attributes[$index]["count"] = count($this->saved_attributes[$index]);
222 }
223 if($this->saved_attributes[$index]["count"] == 1){
224 $tmp= $this->saved_attributes[$index][0];
225 unset($this->saved_attributes[$index]);
226 $this->saved_attributes[$index]= $tmp;
227 continue;
228 }
229 }
230 unset($this->saved_attributes["$index"]["count"]);
231 }
232 if(isset($this->attrs['gosaUnitTag'])){
233 $this->saved_attributes['gosaUnitTag'] = $this->attrs['gosaUnitTag'][0];
234 }
235 }
237 /* Save initial account state */
238 $this->initially_was_account= $this->is_account;
239 }
242 /*! \brief execute plugin
244 Generates the html output for this node
245 */
246 function execute()
247 {
248 /* This one is empty currently. Fabian - please fill in the docu code */
249 session::set('current_class_for_help',get_class($this));
251 /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
252 session::set('LOCK_VARS_TO_USE',array());
253 session::set('LOCK_VARS_USED',array());
254 }
256 /*! \brief execute plugin
257 Removes object from parent
258 */
259 function remove_from_parent()
260 {
261 /* include global link_info */
262 $ldap= $this->config->get_ldap_link();
264 /* Get current objectClasses in order to add the required ones */
265 $ldap->cat($this->dn);
266 $tmp= $ldap->fetch ();
267 $oc= array();
268 if (isset($tmp['objectClass'])){
269 $oc= $tmp['objectClass'];
270 unset($oc['count']);
271 }
273 /* Remove objectClasses from entry */
274 $ldap->cd($this->dn);
275 $this->attrs= array();
276 $this->attrs['objectClass']= array_remove_entries($this->objectclasses,$oc);
278 /* Unset attributes from entry */
279 foreach ($this->attributes as $val){
280 $this->attrs["$val"]= array();
281 }
283 /* Unset account info */
284 $this->is_account= FALSE;
286 /* Do not write in plugin base class, this must be done by
287 children, since there are normally additional attribs,
288 lists, etc. */
289 /*
290 $ldap->modify($this->attrs);
291 */
292 }
295 /*! \brief Save HTML posted data to object
296 */
297 function save_object()
298 {
299 /* Update entry CSN if it is empty. */
300 if(empty($this->entryCSN) && $this->CSN_check_active){
301 $this->entryCSN = getEntryCSN($this->dn);
302 }
304 /* Save values to object */
305 foreach ($this->attributes as $val){
306 if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
307 /* Check for modifications */
308 if (get_magic_quotes_gpc()) {
309 $data= stripcslashes($_POST["$val"]);
310 } else {
311 $data= $this->$val = $_POST["$val"];
312 }
313 if ($this->$val != $data){
314 $this->is_modified= TRUE;
315 }
317 /* Okay, how can I explain this fix ...
318 * In firefox, disabled option fields aren't selectable ... but in IE you can select these fileds.
319 * So IE posts these 'unselectable' option, with value = chr(194)
320 * chr(194) seems to be the in between the ...option> </option.. because there is no value=".." specified in these option fields
321 * This was added for W3c compliance, but now causes these ... ldap errors ...
322 * So we set these Fields to ""; a normal empty string, and we can check these values in plugin::check() again ...
323 */
324 if(isset($data[0]) && $data[0] == chr(194)) {
325 $data = "";
326 }
327 $this->$val= $data;
328 }
329 }
330 }
333 /* Save data to LDAP, depending on is_account we save or delete */
334 function save()
335 {
336 /* include global link_info */
337 $ldap= $this->config->get_ldap_link();
339 /* Save all plugins */
340 $this->entryCSN = "";
342 /* Start with empty array */
343 $this->attrs= array();
345 /* Get current objectClasses in order to add the required ones */
346 $ldap->cat($this->dn);
348 $tmp= $ldap->fetch ();
350 $oc= array();
351 if (isset($tmp['objectClass'])){
352 $oc= $tmp["objectClass"];
353 $this->is_new= FALSE;
354 unset($oc['count']);
355 } else {
356 $this->is_new= TRUE;
357 }
359 /* Load (minimum) attributes, add missing ones */
360 $this->attrs['objectClass']= gosa_array_merge($oc,$this->objectclasses);
362 /* Copy standard attributes */
363 foreach ($this->attributes as $val){
364 if ($this->$val != ""){
365 $this->attrs["$val"]= $this->$val;
366 } elseif (!$this->is_new) {
367 $this->attrs["$val"]= array();
368 }
369 }
371 /* Handle tagging */
372 $this->tag_attrs($this->attrs);
373 }
376 function cleanup()
377 {
378 foreach ($this->attrs as $index => $value){
380 /* Convert arrays with one element to non arrays, if the saved
381 attributes are no array, too */
382 if (is_array($this->attrs[$index]) &&
383 count ($this->attrs[$index]) == 1 &&
384 isset($this->saved_attributes[$index]) &&
385 !is_array($this->saved_attributes[$index])){
387 $tmp= $this->attrs[$index][0];
388 $this->attrs[$index]= $tmp;
389 }
391 /* Remove emtpy arrays if they do not differ */
392 if (is_array($this->attrs[$index]) &&
393 count($this->attrs[$index]) == 0 &&
394 !isset($this->saved_attributes[$index])){
396 unset ($this->attrs[$index]);
397 continue;
398 }
400 /* Remove single attributes that do not differ */
401 if (!is_array($this->attrs[$index]) &&
402 isset($this->saved_attributes[$index]) &&
403 !is_array($this->saved_attributes[$index]) &&
404 $this->attrs[$index] == $this->saved_attributes[$index]){
406 unset ($this->attrs[$index]);
407 continue;
408 }
410 /* Remove arrays that do not differ */
411 if (is_array($this->attrs[$index]) &&
412 isset($this->saved_attributes[$index]) &&
413 is_array($this->saved_attributes[$index])){
415 if (!array_differs($this->attrs[$index],$this->saved_attributes[$index])){
416 unset ($this->attrs[$index]);
417 continue;
418 }
419 }
420 }
422 /* Update saved attributes and ensure that next cleanups will be successful too */
423 foreach($this->attrs as $name => $value){
424 $this->saved_attributes[$name] = $value;
425 }
426 }
428 /* Check formular input */
429 function check()
430 {
431 $message= array();
433 /* Skip if we've no config object */
434 if (!isset($this->config) || !is_object($this->config)){
435 return $message;
436 }
438 /* Find hooks entries for this class */
439 $command= $this->config->search(get_class($this), "CHECK", array('menu', 'tabs'));
441 if ($command != ""){
443 if (!check_command($command)){
444 $message[]= msgPool::cmdnotfound("CHECK", get_class($this));
445 } else {
447 /* Generate "ldif" for check hook */
448 $ldif= "dn: $this->dn\n";
450 /* ... objectClasses */
451 foreach ($this->objectclasses as $oc){
452 $ldif.= "objectClass: $oc\n";
453 }
455 /* ... attributes */
456 foreach ($this->attributes as $attr){
457 if ($this->$attr == ""){
458 continue;
459 }
460 if (is_array($this->$attr)){
461 foreach ($this->$attr as $val){
462 $ldif.= "$attr: $val\n";
463 }
464 } else {
465 $ldif.= "$attr: ".$this->$attr."\n";
466 }
467 }
469 /* Append empty line */
470 $ldif.= "\n";
472 /* Feed "ldif" into hook and retrieve result*/
473 $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
474 $fh= proc_open($command, $descriptorspec, $pipes);
475 if (is_resource($fh)) {
476 fwrite ($pipes[0], $ldif);
477 fclose($pipes[0]);
479 $result= stream_get_contents($pipes[1]);
480 if ($result != ""){
481 $message[]= $result;
482 }
484 fclose($pipes[1]);
485 fclose($pipes[2]);
486 proc_close($fh);
487 }
488 }
490 }
492 /* Check entryCSN */
493 if($this->CSN_check_active){
494 $current_csn = getEntryCSN($this->dn);
495 if($current_csn != $this->entryCSN && !empty($this->entryCSN) && !empty($current_csn)){
496 $this->entryCSN = $current_csn;
497 $message[] = _("The object has changed since opened in GOsa. All changes that may be done by others get lost if you save this entry!");
498 }
499 }
500 return ($message);
501 }
503 /* Adapt from template, using 'dn' */
504 function adapt_from_template($dn, $skip= array())
505 {
506 /* Include global link_info */
507 $ldap= $this->config->get_ldap_link();
509 /* Load requested 'dn' to 'attrs' */
510 $ldap->cat ($dn);
511 $this->attrs= $ldap->fetch();
513 /* Walk through attributes */
514 foreach ($this->attributes as $val){
516 /* Skip the ones in skip list */
517 if (in_array($val, $skip)){
518 continue;
519 }
521 if (isset($this->attrs["$val"][0])){
523 /* If attribute is set, replace dynamic parts:
524 %sn, %givenName and %uid. Fill these in our local variables. */
525 $value= $this->attrs["$val"][0];
527 foreach (array("sn", "givenName", "uid") as $repl){
528 if (preg_match("/%$repl/i", $value)){
529 $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
530 }
531 }
532 $this->$val= $value;
533 }
534 }
536 /* Is Account? */
537 $found= TRUE;
538 foreach ($this->objectclasses as $obj){
539 if (preg_match('/top/i', $obj)){
540 continue;
541 }
542 if (!in_array_ics ($obj, $this->attrs['objectClass'])){
543 $found= FALSE;
544 break;
545 }
546 }
547 if ($found){
548 $this->is_account= TRUE;
549 }
550 }
552 /* Indicate whether a password change is needed or not */
553 function password_change_needed()
554 {
555 return FALSE;
556 }
559 /* Show header message for tab dialogs */
560 function show_enable_header($button_text, $text, $disabled= FALSE)
561 {
562 if (($disabled == TRUE) || (!$this->acl_is_createable())){
563 $state= "disabled";
564 } else {
565 $state= "";
566 }
567 $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
568 $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".$state.
569 "><p class=\"seperator\"> </p></td></tr></table>";
571 return($display);
572 }
575 /* Show header message for tab dialogs */
576 function show_disable_header($button_text, $text, $disabled= FALSE)
577 {
578 if (($disabled == TRUE) || !$this->acl_is_removeable()){
579 $state= "disabled";
580 } else {
581 $state= "";
582 }
583 $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
584 $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".$state.
585 "><p class=\"seperator\"> </p></td></tr></table>";
587 return($display);
588 }
591 /* Show header message for tab dialogs */
592 function show_header($button_text, $text, $disabled= FALSE)
593 {
594 echo "FIXME: show_header should be replaced by show_disable_header and show_enable_header<br>";
595 if ($disabled == TRUE){
596 $state= "disabled";
597 } else {
598 $state= "";
599 }
600 $display= "<table summary=\"\" width=\"100%\"><tr>\n<td colspan=2><p><b>$text</b></p>\n";
601 $display.= "<input type=submit value=\"$button_text\" name=\"modify_state\" ".
602 ($this->acl_is_createable()?'':'disabled')." ".$state.
603 "><p class=\"seperator\"> </p></td></tr></table>";
605 return($display);
606 }
609 function postcreate($add_attrs= array())
610 {
611 /* Find postcreate entries for this class */
612 $command= $this->config->search(get_class($this), "POSTCREATE",array('menu', 'tabs'));
614 if ($command != ""){
616 /* Walk through attribute list */
617 foreach ($this->attributes as $attr){
618 if (!is_array($this->$attr)){
619 $add_attrs[$attr] = $this->$attr;
620 }
621 }
622 $add_attrs['dn']=$this->dn;
624 $tmp = array();
625 foreach($add_attrs as $name => $value){
626 $tmp[$name] = strlen($name);
627 }
628 arsort($tmp);
630 /* Additional attributes */
631 foreach ($tmp as $name => $len){
632 $value = $add_attrs[$name];
633 $command= preg_replace("/%$name/", "$value", $command);
634 }
636 if (check_command($command)){
637 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
638 $command, "Execute");
640 exec($command);
641 } else {
642 $message= msgPool::cmdnotfound("POSTCREATE", get_class($this));
643 msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
644 }
645 }
646 }
648 function postmodify($add_attrs= array())
649 {
650 /* Find postcreate entries for this class */
651 $command= $this->config->search(get_class($this), "POSTMODIFY",array('menu','tabs'));
653 if ($command != ""){
655 /* Walk through attribute list */
656 foreach ($this->attributes as $attr){
657 if (!is_array($this->$attr)){
658 $add_attrs[$attr] = $this->$attr;
659 }
660 }
661 $add_attrs['dn']=$this->dn;
663 $tmp = array();
664 foreach($add_attrs as $name => $value){
665 $tmp[$name] = strlen($name);
666 }
667 arsort($tmp);
669 /* Additional attributes */
670 foreach ($tmp as $name => $len){
671 $value = $add_attrs[$name];
672 $command= preg_replace("/%$name/", "$value", $command);
673 }
675 if (check_command($command)){
676 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command, "Execute");
677 exec($command);
678 } else {
679 $message= msgPool::cmdnotfound("POSTMODIFY", get_class($this));
680 msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
681 }
682 }
683 }
685 function postremove($add_attrs= array())
686 {
687 /* Find postremove entries for this class */
688 $command= $this->config->search(get_class($this), "POSTREMOVE",array('menu','tabs'));
689 if ($command != ""){
691 /* Walk through attribute list */
692 foreach ($this->attributes as $attr){
693 if (!is_array($this->$attr)){
694 $add_attrs[$attr] = $this->$attr;
695 }
696 }
697 $add_attrs['dn']=$this->dn;
699 $tmp = array();
700 foreach($add_attrs as $name => $value){
701 $tmp[$name] = strlen($name);
702 }
703 arsort($tmp);
705 /* Additional attributes */
706 foreach ($tmp as $name => $len){
707 $value = $add_attrs[$name];
708 $command= preg_replace("/%$name/", "$value", $command);
709 }
711 if (check_command($command)){
712 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,
713 $command, "Execute");
715 exec($command);
716 } else {
717 $message= msgPool::cmdnotfound("POSTREMOVE", get_class($this));
718 msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
719 }
720 }
721 }
723 /* Create unique DN */
724 function create_unique_dn($attribute, $base)
725 {
726 $ldap= $this->config->get_ldap_link();
727 $base= preg_replace("/^,*/", "", $base);
729 /* Try to use plain entry first */
730 $dn= "$attribute=".$this->$attribute.",$base";
731 $ldap->cat ($dn, array('dn'));
732 if (!$ldap->fetch()){
733 return ($dn);
734 }
736 /* Look for additional attributes */
737 foreach ($this->attributes as $attr){
738 if ($attr == $attribute || $this->$attr == ""){
739 continue;
740 }
742 $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
743 $ldap->cat ($dn, array('dn'));
744 if (!$ldap->fetch()){
745 return ($dn);
746 }
747 }
749 /* None found */
750 return ("none");
751 }
753 function rebind($ldap, $referral)
754 {
755 $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
756 if (ldap_bind($ldap, $credentials['ADMIN'], $this->config->get_credentials($credentials['PASSWORD']))) {
757 $this->error = "Success";
758 $this->hascon=true;
759 $this->reconnect= true;
760 return (0);
761 } else {
762 $this->error = "Could not bind to " . $credentials['ADMIN'];
763 return NULL;
764 }
765 }
768 /* Recursively copy ldap object */
769 function _copy($src_dn,$dst_dn)
770 {
771 $ldap=$this->config->get_ldap_link();
772 $ldap->cat($src_dn);
773 $attrs= $ldap->fetch();
775 /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
776 $ds= ldap_connect($this->config->current['SERVER']);
777 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
778 if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
779 ldap_set_rebind_proc($ds, array(&$this, "rebind"));
780 }
782 $r=ldap_bind($ds,$this->config->current['ADMIN'], $this->config->current['PASSWORD']);
783 $sr=ldap_read($ds, @LDAP::fix($src_dn), "objectClass=*");
785 /* Fill data from LDAP */
786 $new= array();
787 if ($sr) {
788 $ei=ldap_first_entry($ds, $sr);
789 if ($ei) {
790 foreach($attrs as $attr => $val){
791 if ($info = @ldap_get_values_len($ds, $ei, $attr)){
792 for ($i= 0; $i<$info['count']; $i++){
793 if ($info['count'] == 1){
794 $new[$attr]= $info[$i];
795 } else {
796 $new[$attr][]= $info[$i];
797 }
798 }
799 }
800 }
801 }
802 }
804 /* close conncetion */
805 ldap_unbind($ds);
807 /* Adapt naming attribute */
808 $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
809 $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
810 $new[$dst_name]= @LDAP::fix($dst_val);
812 /* Check if this is a department.
813 * If it is a dep. && there is a , override in his ou
814 * change \2C to , again, else this entry can't be saved ...
815 */
816 if((isset($new['ou'])) &&( preg_match("/\\,/",$new['ou']))){
817 $new['ou'] = preg_replace("/\\\\,/",",",$new['ou']);
818 }
820 /* Save copy */
821 $ldap->connect();
822 $ldap->cd($this->config->current['BASE']);
824 $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
826 /* FAIvariable=.../..., cn=..
827 could not be saved, because the attribute FAIvariable was different to
828 the dn FAIvariable=..., cn=... */
829 if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
830 $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
831 }
832 $ldap->cd($dst_dn);
833 $ldap->add($new);
835 if (!$ldap->success()){
836 trigger_error("Trying to save $dst_dn failed.",
837 E_USER_WARNING);
838 return(FALSE);
839 }
840 return(TRUE);
841 }
844 /* This is a workaround function. */
845 function copy($src_dn, $dst_dn)
846 {
847 /* Rename dn in possible object groups */
848 $ldap= $this->config->get_ldap_link();
849 $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.@LDAP::prepare4filter($src_dn).'))',
850 array('cn'));
851 while ($attrs= $ldap->fetch()){
852 $og= new ogroup($this->config, $ldap->getDN());
853 unset($og->member[$src_dn]);
854 $og->member[$dst_dn]= $dst_dn;
855 $og->save ();
856 }
858 $ldap->cat($dst_dn);
859 $attrs= $ldap->fetch();
860 if (count($attrs)){
861 trigger_error("Trying to overwrite ".@LDAP::fix($dst_dn).", which already exists.",
862 E_USER_WARNING);
863 return (FALSE);
864 }
866 $ldap->cat($src_dn);
867 $attrs= $ldap->fetch();
868 if (!count($attrs)){
869 trigger_error("Trying to move ".@LDAP::fix($src_dn).", which does not seem to exist.",
870 E_USER_WARNING);
871 return (FALSE);
872 }
874 $ldap->cd($src_dn);
875 $ldap->search("objectClass=*",array("dn"));
876 while($attrs = $ldap->fetch()){
877 $src = $attrs['dn'];
878 $dst = preg_replace("/".normalizePreg($src_dn)."$/",$dst_dn,$attrs['dn']);
879 $this->_copy($src,$dst);
880 }
881 return (TRUE);
882 }
886 /*! \brief Move a given ldap object indentified by $src_dn \
887 to the given destination $dst_dn \
888 * Ensure that all references are updated (ogroups) \
889 * Update ACLs \
890 * Update accessTo \
891 @param String The source dn.
892 @param String The destination dn.
893 @return Boolean TRUE on success else FALSE.
894 */
895 function rename($src_dn, $dst_dn)
896 {
897 $start = microtime(1);
899 /* Try to move the source entry to the destination position */
900 $ldap = $this->config->get_ldap_link();
901 $ldap->cd($this->config->current['BASE']);
902 $ldap->create_missing_trees(preg_replace("/^[^,]+,/","",$dst_dn));
904 if (!$ldap->rename_dn($src_dn,$dst_dn)){
905 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $src_dn, "", get_class()));
906 return(FALSE);
907 }
909 /* Get list of groups within this tree,
910 maybe we have to update ACL references.
911 */
912 $leaf_groups = get_list("(objectClass=posixGroup)",array("all"),$dst_dn,
913 array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
915 /* Get list of users within this tree,
916 maybe we have to update ACL references.
917 */
918 $leaf_users= get_list("(objectClass=gosaAccount)",array("all"),$dst_dn,
919 array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
922 /* Updated acls set for this groups */
923 foreach($leaf_groups as $group){
924 $new_dn = $group['dn'];
925 $old_dn = preg_replace("/".normalizePreg($dst_dn)."$/i",$src_dn,$new_dn);
926 $this->update_acls($old_dn,$new_dn);
927 }
929 /* Updated acls set for this users */
930 foreach($leaf_users as $user){
931 $new_dn = $user['dn'];
932 $old_dn = preg_replace("/".normalizePreg($dst_dn)."$/i",$src_dn,$new_dn);
933 $this->update_acls($old_dn,$new_dn);
934 }
936 /* Get all objectGroups defined in this database.
937 and check if there is an entry matching the source dn,
938 if this is the case, then update this objectgroup to use the new dn.
939 */
940 $ogroups = get_sub_list("(&(objectClass=gosaGroupOfNames)(member=*))","ogroups",
941 array(get_ou("ogroupou")),$this->config->current['BASE'],array("member"),
942 GL_SUBSEARCH | GL_NO_ACL_CHECK) ;
944 /* Walk through all objectGroups and check if there are
945 members matching the source dn
946 */
947 foreach($ogroups as $ogroup){
948 if(isset($ogroup['member'])){
950 /* Reset class object, this will be initialized with class_ogroup on demand
951 */
952 $o_ogroup = NULL;
953 for($i = 0 ; $i < $ogroup['member']['count'] ; $i ++){
955 $c_mem = $ogroup['member'][$i];
957 if(preg_match("/".normalizePreg($src_dn)."$/i",$c_mem)){
959 $d_mem = preg_replace("/".normalizePreg($src_dn)."$/i",$dst_dn,$ogroup['member'][$i]);
961 if($o_ogroup == NULL){
962 $o_ogroup = new ogroup($this->config,$ogroup['dn']);
963 }
965 unset($o_ogroup->member[$c_mem]);
966 $o_ogroup->member[$d_mem]= $d_mem;
967 }
968 }
970 /* Save object group if there were changes made on the membership */
971 if($o_ogroup != NULL){
972 $o_ogroup->save();
973 }
974 }
975 }
977 /* Check if there are gosa departments moved.
978 If there were deps moved, the force reload of config->deps.
979 */
980 $leaf_deps= get_list("(objectClass=gosaDepartment)",array("all"),$dst_dn,
981 array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
983 if(count($leaf_deps)){
984 $this->config->get_departments();
985 $this->config->make_idepartments();
986 session::set("config",$this->config);
987 $ui =get_userinfo();
988 $ui->reset_acl_cache();
989 }
991 return(TRUE);
992 }
996 function move($src_dn, $dst_dn)
997 {
998 /* Do not copy if only upper- lowercase has changed */
999 if(strtolower($src_dn) == strtolower($dst_dn)){
1000 return(TRUE);
1001 }
1004 /* Try to move the entry instead of copy & delete
1005 */
1006 if(TRUE){
1008 /* Try to move with ldap routines, if this was not successfull
1009 fall back to the old style copy & remove method
1010 */
1011 if($this->rename($src_dn, $dst_dn)){
1012 return(TRUE);
1013 }else{
1014 // See code below.
1015 }
1016 }
1018 /* Copy source to destination */
1019 if (!$this->copy($src_dn, $dst_dn)){
1020 return (FALSE);
1021 }
1023 /* Delete source */
1024 $ldap= $this->config->get_ldap_link();
1025 $ldap->rmdir_recursive($src_dn);
1026 if (!$ldap->success()){
1027 trigger_error("Trying to delete $src_dn failed.",
1028 E_USER_WARNING);
1029 return (FALSE);
1030 }
1032 return (TRUE);
1033 }
1036 /* Move/Rename complete trees */
1037 function recursive_move($src_dn, $dst_dn)
1038 {
1039 /* Check if the destination entry exists */
1040 $ldap= $this->config->get_ldap_link();
1042 /* Check if destination exists - abort */
1043 $ldap->cat($dst_dn, array('dn'));
1044 if ($ldap->fetch()){
1045 trigger_error("recursive_move $dst_dn already exists.",
1046 E_USER_WARNING);
1047 return (FALSE);
1048 }
1050 $this->copy($src_dn, $dst_dn);
1052 /* Remove src_dn */
1053 $ldap->cd($src_dn);
1054 $ldap->recursive_remove($src_dn);
1055 return (TRUE);
1056 }
1059 function handle_post_events($mode, $add_attrs= array())
1060 {
1061 switch ($mode){
1062 case "add":
1063 $this->postcreate($add_attrs);
1064 break;
1066 case "modify":
1067 $this->postmodify($add_attrs);
1068 break;
1070 case "remove":
1071 $this->postremove($add_attrs);
1072 break;
1073 }
1074 }
1077 function saveCopyDialog(){
1078 }
1081 function getCopyDialog(){
1082 return(array("string"=>"","status"=>""));
1083 }
1086 function PrepareForCopyPaste($source)
1087 {
1088 $todo = $this->attributes;
1089 if(isset($this->CopyPasteVars)){
1090 $todo = array_merge($todo,$this->CopyPasteVars);
1091 }
1093 if(count($this->objectclasses)){
1094 $this->is_account = TRUE;
1095 foreach($this->objectclasses as $class){
1096 if(!in_array($class,$source['objectClass'])){
1097 $this->is_account = FALSE;
1098 }
1099 }
1100 }
1102 foreach($todo as $var){
1103 if (isset($source[$var])){
1104 if(isset($source[$var]['count'])){
1105 if($source[$var]['count'] > 1){
1106 $this->$var = array();
1107 $tmp = array();
1108 for($i = 0 ; $i < $source[$var]['count']; $i++){
1109 $tmp = $source[$var][$i];
1110 }
1111 $this->$var = $tmp;
1112 }else{
1113 $this->$var = $source[$var][0];
1114 }
1115 }else{
1116 $this->$var= $source[$var];
1117 }
1118 }
1119 }
1120 }
1122 function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1123 {
1124 /* Skip tagging?
1125 If this is called from departmentGeneric, we have to skip this
1126 tagging procedure.
1127 */
1128 if($this->skipTagging){
1129 return;
1130 }
1132 /* No dn? Self-operation... */
1133 if ($dn == ""){
1134 $dn= $this->dn;
1136 /* No tag? Find it yourself... */
1137 if ($tag == ""){
1138 $len= strlen($dn);
1140 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
1141 $relevant= array();
1142 foreach ($this->config->adepartments as $key => $ntag){
1144 /* This one is bigger than our dn, its not relevant... */
1145 if ($len < strlen($key)){
1146 continue;
1147 }
1149 /* This one matches with the latter part. Break and don't fix this entry */
1150 if (preg_match('/(^|,)'.normalizePreg($key).'$/', $dn)){
1151 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
1152 $relevant[strlen($key)]= $ntag;
1153 continue;
1154 }
1156 }
1158 /* If we've some relevant tags to set, just get the longest one */
1159 if (count($relevant)){
1160 ksort($relevant);
1161 $tmp= array_keys($relevant);
1162 $idx= end($tmp);
1163 $tag= $relevant[$idx];
1164 $this->gosaUnitTag= $tag;
1165 }
1166 }
1167 }
1169 /* Remove tags that may already be here... */
1170 remove_objectClass("gosaAdministrativeUnitTag", $at);
1171 if (isset($at['gosaUnitTag'])){
1172 unset($at['gosaUnitTag']);
1173 }
1175 /* Set tag? */
1176 if ($tag != ""){
1177 add_objectClass("gosaAdministrativeUnitTag", $at);
1178 $at['gosaUnitTag']= $tag;
1179 }
1181 /* Initially this object was tagged.
1182 - But now, it is no longer inside a tagged department.
1183 So force the remove of the tag.
1184 (objectClass was already removed obove)
1185 */
1186 if($tag == "" && $this->gosaUnitTag){
1187 $at['gosaUnitTag'] = array();
1188 }
1189 }
1192 /* Add possibility to stop remove process */
1193 function allow_remove()
1194 {
1195 $reason= "";
1196 return $reason;
1197 }
1200 /* Create a snapshot of the current object */
1201 function create_snapshot($type= "snapshot", $description= array())
1202 {
1204 /* Check if snapshot functionality is enabled */
1205 if(!$this->snapshotEnabled()){
1206 return;
1207 }
1209 /* Get configuration from gosa.conf */
1210 $tmp = $this->config->current;
1212 /* Create lokal ldap connection */
1213 $ldap= $this->config->get_ldap_link();
1214 $ldap->cd($this->config->current['BASE']);
1216 /* check if there are special server configurations for snapshots */
1217 if(!isset($tmp['SNAPSHOT_SERVER'])){
1219 /* Source and destination server are both the same, just copy source to dest obj */
1220 $ldap_to = $ldap;
1221 $snapldapbase = $this->config->current['BASE'];
1223 }else{
1224 $server = $tmp['SNAPSHOT_SERVER'];
1225 $user = $tmp['SNAPSHOT_USER'];
1226 $password = $tmp['SNAPSHOT_PASSWORD'];
1227 $snapldapbase = $tmp['SNAPSHOT_BASE'];
1229 $ldap_to = new ldapMultiplexer(new LDAP($user,$password, $server));
1230 $ldap_to -> cd($snapldapbase);
1232 if (!$ldap_to->success()){
1233 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1234 }
1236 }
1238 /* check if the dn exists */
1239 if ($ldap->dn_exists($this->dn)){
1241 /* Extract seconds & mysecs, they are used as entry index */
1242 list($usec, $sec)= explode(" ", microtime());
1244 /* Collect some infos */
1245 $base = $this->config->current['BASE'];
1246 $snap_base = $tmp['SNAPSHOT_BASE'];
1247 $base_of_object = preg_replace ('/^[^,]+,/i', '', $this->dn);
1248 $new_base = preg_replace("/".normalizePreg($base)."$/","",$base_of_object).$snap_base;
1250 /* Create object */
1251 #$data = preg_replace('/^dn:.*\n/', '', $ldap->gen_ldif($this->dn,"(!(objectClass=gosaDepartment))"));
1252 $data = $ldap->gen_ldif($this->dn,"(&(!(objectClass=gosaDepartment))(!(objectClass=FAIclass)))");
1253 $newName = preg_replace("/\./", "", $sec."-".$usec);
1254 $target= array();
1255 $target['objectClass'] = array("top", "gosaSnapshotObject");
1256 $target['gosaSnapshotData'] = gzcompress($data, 6);
1257 $target['gosaSnapshotType'] = $type;
1258 $target['gosaSnapshotDN'] = $this->dn;
1259 $target['description'] = $description;
1260 $target['gosaSnapshotTimestamp'] = $newName;
1262 /* Insert the new snapshot
1263 But we have to check first, if the given gosaSnapshotTimestamp
1264 is already used, in this case we should increment this value till there is
1265 an unused value. */
1266 $new_dn = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1267 $ldap_to->cat($new_dn);
1268 while($ldap_to->count()){
1269 $ldap_to->cat($new_dn);
1270 $newName = preg_replace("/\./", "", $sec."-".($usec++));
1271 $new_dn = "gosaSnapshotTimestamp=".$newName.",".$new_base;
1272 $target['gosaSnapshotTimestamp'] = $newName;
1273 }
1275 /* Inset this new snapshot */
1276 $ldap_to->cd($snapldapbase);
1277 $ldap_to->create_missing_trees($snapldapbase);
1278 $ldap_to->create_missing_trees($new_base);
1279 $ldap_to->cd($new_dn);
1280 $ldap_to->add($target);
1281 if (!$ldap_to->success()){
1282 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $new_dn, LDAP_ADD, get_class()));
1283 }
1285 if (!$ldap->success()){
1286 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $new_base, "", get_class()));
1287 }
1289 }
1290 }
1292 function remove_snapshot($dn)
1293 {
1294 $ui = get_userinfo();
1295 $old_dn = $this->dn;
1296 $this->dn = $dn;
1297 $ldap = $this->config->get_ldap_link();
1298 $ldap->cd($this->config->current['BASE']);
1299 $ldap->rmdir_recursive($dn);
1300 $this->dn = $old_dn;
1301 }
1304 /* returns true if snapshots are enabled, and false if it is disalbed
1305 There will also be some errors psoted, if the configuration failed */
1306 function snapshotEnabled()
1307 {
1308 $tmp = $this->config->current;
1309 if(isset($tmp['ENABLE_SNAPSHOT'])){
1310 if (preg_match("/^true$/i", $tmp['ENABLE_SNAPSHOT']) || preg_match("/yes/i", $tmp['ENABLE_SNAPSHOT'])){
1312 /* Check if the snapshot_base is defined */
1313 if(!isset($tmp['SNAPSHOT_BASE'])){
1314 msg_dialog::display(_("Configuration error"), sprintf(_("The snapshot functionality is enabled, but the required variable '%s' is not set."),"SNAPSHOT_BASE"), ERROR_DIALOG);
1315 return(FALSE);
1316 }
1318 /* check if there are special server configurations for snapshots */
1319 if(isset($tmp['SNAPSHOT_SERVER'])){
1321 /* check if all required vars are available to create a new ldap connection */
1322 $missing = "";
1323 foreach(array("SNAPSHOT_SERVER","SNAPSHOT_USER","SNAPSHOT_PASSWORD","SNAPSHOT_BASE") as $var){
1324 if(!isset($tmp[$var])){
1325 $missing .= $var." ";
1326 msg_dialog::display(_("Configuration error"), sprintf(_("The snapshot functionality is enabled, but the required variable '%s' is not set."), $missing), ERROR_DIALOG);
1327 return(FALSE);
1328 }
1329 }
1330 }
1331 return(TRUE);
1332 }
1333 }
1334 return(FALSE);
1335 }
1338 /* Return available snapshots for the given base
1339 */
1340 function Available_SnapsShots($dn,$raw = false)
1341 {
1342 if(!$this->snapshotEnabled()) return(array());
1344 /* Create an additional ldap object which
1345 points to our ldap snapshot server */
1346 $ldap= $this->config->get_ldap_link();
1347 $ldap->cd($this->config->current['BASE']);
1348 $cfg= &$this->config->current;
1350 /* check if there are special server configurations for snapshots */
1352 if(isset($cfg['SERVER']) && isset($cfg['SNAPSHOT_SERVER']) && $cfg['SERVER'] == $cfg['SNAPSHOT_SERVER']){
1353 $ldap_to = $ldap;
1354 }elseif(isset($cfg['SNAPSHOT_SERVER'])){
1355 $server = $cfg['SNAPSHOT_SERVER'];
1356 $user = $cfg['SNAPSHOT_USER'];
1357 $password = $cfg['SNAPSHOT_PASSWORD'];
1358 $snapldapbase = $cfg['SNAPSHOT_BASE'];
1360 $ldap_to = new ldapMultiplexer(new LDAP($user,$password, $server));
1361 $ldap_to -> cd ($snapldapbase);
1362 if (!$ldap_to->success()){
1363 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1364 }
1365 }else{
1366 $ldap_to = $ldap;
1367 }
1369 /* Prepare bases and some other infos */
1370 $base = $this->config->current['BASE'];
1371 $snap_base = $cfg['SNAPSHOT_BASE'];
1372 $base_of_object = preg_replace ('/^[^,]+,/i', '', $dn);
1373 $new_base = preg_replace("/".normalizePreg($base)."$/","",$base_of_object).$snap_base;
1374 $tmp = array();
1376 /* Fetch all objects with gosaSnapshotDN=$dn */
1377 $ldap_to->cd($new_base);
1378 $ldap_to->ls("(&(objectClass=gosaSnapshotObject)(gosaSnapshotDN=".$dn."))",$new_base,
1379 array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description"));
1381 /* Put results into a list and add description if missing */
1382 while($entry = $ldap_to->fetch()){
1383 if(!isset($entry['description'][0])){
1384 $entry['description'][0] = "";
1385 }
1386 $tmp[] = $entry;
1387 }
1389 /* Return the raw array, or format the result */
1390 if($raw){
1391 return($tmp);
1392 }else{
1393 $tmp2 = array();
1394 foreach($tmp as $entry){
1395 $tmp2[base64_encode($entry['dn'])] = $entry['description'][0];
1396 }
1397 }
1398 return($tmp2);
1399 }
1402 function getAllDeletedSnapshots($base_of_object,$raw = false)
1403 {
1404 if(!$this->snapshotEnabled()) return(array());
1406 /* Create an additional ldap object which
1407 points to our ldap snapshot server */
1408 $ldap= $this->config->get_ldap_link();
1409 $ldap->cd($this->config->current['BASE']);
1410 $cfg= &$this->config->current;
1412 /* check if there are special server configurations for snapshots */
1413 if(isset($cfg['SNAPSHOT_SERVER'])){
1414 $server = $cfg['SNAPSHOT_SERVER'];
1415 $user = $cfg['SNAPSHOT_USER'];
1416 $password = $cfg['SNAPSHOT_PASSWORD'];
1417 $snapldapbase = $cfg['SNAPSHOT_BASE'];
1418 $ldap_to = new ldapMultiplexer(new LDAP($user,$password, $server));
1419 $ldap_to->cd ($snapldapbase);
1420 if (!$ldap_to->success()){
1421 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1422 }
1423 }else{
1424 $ldap_to = $ldap;
1425 }
1427 /* Prepare bases */
1428 $base = $this->config->current['BASE'];
1429 $snap_base = $cfg['SNAPSHOT_BASE'];
1430 $new_base = preg_replace("/".normalizePreg($base)."$/","",$base_of_object).$snap_base;
1432 /* Fetch all objects and check if they do not exist anymore */
1433 $ui = get_userinfo();
1434 $tmp = array();
1435 $ldap_to->cd($new_base);
1436 $ldap_to->ls("(objectClass=gosaSnapshotObject)",$new_base,array("gosaSnapshotType","gosaSnapshotTimestamp","gosaSnapshotDN","description"));
1437 while($entry = $ldap_to->fetch()){
1439 $chk = str_replace($new_base,"",$entry['dn']);
1440 if(preg_match("/,ou=/",$chk)) continue;
1442 if(!isset($entry['description'][0])){
1443 $entry['description'][0] = "";
1444 }
1445 $tmp[] = $entry;
1446 }
1448 /* Check if entry still exists */
1449 foreach($tmp as $key => $entry){
1450 $ldap->cat($entry['gosaSnapshotDN'][0]);
1451 if($ldap->count()){
1452 unset($tmp[$key]);
1453 }
1454 }
1456 /* Format result as requested */
1457 if($raw) {
1458 return($tmp);
1459 }else{
1460 $tmp2 = array();
1461 foreach($tmp as $key => $entry){
1462 $tmp2[base64_encode($entry['dn'])] = $entry['description'][0];
1463 }
1464 }
1465 return($tmp2);
1466 }
1469 /* Restore selected snapshot */
1470 function restore_snapshot($dn)
1471 {
1472 if(!$this->snapshotEnabled()) return(array());
1474 $ldap= $this->config->get_ldap_link();
1475 $ldap->cd($this->config->current['BASE']);
1476 $cfg= &$this->config->current;
1478 /* check if there are special server configurations for snapshots */
1479 if(isset($cfg['SNAPSHOT_SERVER'])){
1480 $server = $cfg['SNAPSHOT_SERVER'];
1481 $user = $cfg['SNAPSHOT_USER'];
1482 $password = $cfg['SNAPSHOT_PASSWORD'];
1483 $snapldapbase = $cfg['SNAPSHOT_BASE'];
1484 $ldap_to = new ldapMultiplexer(new LDAP($user,$password, $server));
1485 $ldap_to->cd ($snapldapbase);
1486 if (!$ldap_to->success()){
1487 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap_to->get_error(), $snapldapbase, "", get_class()));
1488 }
1489 }else{
1490 $ldap_to = $ldap;
1491 }
1493 /* Get the snapshot */
1494 $ldap_to->cat($dn);
1495 $restoreObject = $ldap_to->fetch();
1497 /* Prepare import string */
1498 $data = gzuncompress($ldap_to->get_attribute($dn,'gosaSnapshotData'));
1500 /* Import the given data */
1501 $err = "";
1502 $ldap->import_complete_ldif($data,$err,false,false);
1503 if (!$ldap->success()){
1504 msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $dn, "", get_class()));
1505 }
1506 }
1509 function showSnapshotDialog($base,$baseSuffixe,&$parent)
1510 {
1511 $once = true;
1512 $ui = get_userinfo();
1513 $this->parent = $parent;
1515 foreach($_POST as $name => $value){
1517 /* Create a new snapshot, display a dialog */
1518 if(preg_match("/^CreateSnapShotDialog_/",$name) && $once){
1519 $once = false;
1520 $entry = preg_replace("/^CreateSnapShotDialog_/","",$name);
1521 $entry = base64_decode(preg_replace("/_[xy]$/","",$entry));
1523 if(!empty($entry) && $ui->allow_snapshot_create($entry,$this->parent->acl_module)){
1524 $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1525 }else{
1526 msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to create a snapshot for %s."),$entry),ERROR_DIALOG);
1527 }
1528 }
1530 /* Restore a snapshot, display a dialog with all snapshots of the current object */
1531 if(preg_match("/^RestoreSnapShotDialog_/",$name) && $once){
1532 $once = false;
1533 $entry = preg_replace("/^RestoreSnapShotDialog_/","",$name);
1534 $entry = base64_decode(preg_replace("/_[xy]$/","",$entry));
1535 if(!empty($entry) && $ui->allow_snapshot_restore($entry,$this->parent->acl_module)){
1536 $this->snapDialog = new SnapShotDialog($this->config,$entry,$this);
1537 $this->snapDialog->display_restore_dialog = true;
1538 }else{
1539 msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1540 }
1541 }
1543 /* Restore one of the already deleted objects */
1544 if(((isset($_POST['menu_action']) && $_POST['menu_action'] == "RestoreDeletedSnapShot")
1545 || preg_match("/^RestoreDeletedSnapShot_/",$name)) && $once){
1546 $once = false;
1548 if($ui->allow_snapshot_restore($base,$this->parent->acl_module)){
1549 $this->snapDialog = new SnapShotDialog($this->config,"",$this);
1550 $this->snapDialog->set_snapshot_bases($baseSuffixe);
1551 $this->snapDialog->display_restore_dialog = true;
1552 $this->snapDialog->display_all_removed_objects = true;
1553 }else{
1554 msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$base),ERROR_DIALOG);
1555 }
1556 }
1558 /* Restore selected snapshot */
1559 if(preg_match("/^RestoreSnapShot_/",$name) && $once){
1560 $once = false;
1561 $entry = preg_replace("/^RestoreSnapShot_/","",$name);
1562 $entry = base64_decode(trim(preg_replace("/_[xy]$/","",$entry)));
1563 if(!empty($entry) && $ui->allow_snapshot_restore($entry,$this->parent->acl_module)){
1564 $this->restore_snapshot($entry);
1565 $this->snapDialog = NULL;
1566 }else{
1567 msg_dialog::display(_("Permission"),sprintf(_("You are not allowed to restore a snapshot for %s."),$entry),ERROR_DIALOG);
1568 }
1569 }
1570 }
1572 /* Create a new snapshot requested, check
1573 the given attributes and create the snapshot*/
1574 if(isset($_POST['CreateSnapshot']) && is_object($this->snapDialog)){
1575 $this->snapDialog->save_object();
1576 $msgs = $this->snapDialog->check();
1577 if(count($msgs)){
1578 foreach($msgs as $msg){
1579 msg_dialog::display(_("Error"), $msg, ERROR_DIALOG);
1580 }
1581 }else{
1582 $this->dn = $this->snapDialog->dn;
1583 $this->create_snapshot("snapshot",$this->snapDialog->CurrentDescription);
1584 $this->snapDialog = NULL;
1585 }
1586 }
1588 /* Restore is requested, restore the object with the posted dn .*/
1589 if((isset($_POST['RestoreSnapshot'])) && (isset($_POST['SnapShot']))){
1590 }
1592 if(isset($_POST['CancelSnapshot'])){
1593 $this->snapDialog = NULL;
1594 }
1596 if(is_object($this->snapDialog )){
1597 $this->snapDialog->save_object();
1598 return($this->snapDialog->execute());
1599 }
1600 }
1603 static function plInfo()
1604 {
1605 return array();
1606 }
1609 function set_acl_base($base)
1610 {
1611 $this->acl_base= $base;
1612 }
1615 function set_acl_category($category)
1616 {
1617 $this->acl_category= "$category/";
1618 }
1621 function acl_is_writeable($attribute,$skip_write = FALSE)
1622 {
1623 $ui= get_userinfo();
1624 return preg_match('/w/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute, $skip_write));
1625 }
1628 function acl_is_readable($attribute)
1629 {
1630 $ui= get_userinfo();
1631 return preg_match('/r/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute));
1632 }
1635 function acl_is_createable($base ="")
1636 {
1637 $ui= get_userinfo();
1638 if($base == "") $base = $this->acl_base;
1639 return preg_match('/c/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1640 }
1643 function acl_is_removeable($base ="")
1644 {
1645 $ui= get_userinfo();
1646 if($base == "") $base = $this->acl_base;
1647 return preg_match('/d/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1648 }
1651 function acl_is_moveable($base = "")
1652 {
1653 $ui= get_userinfo();
1654 if($base == "") $base = $this->acl_base;
1655 return preg_match('/m/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1656 }
1659 function acl_have_any_permissions()
1660 {
1661 }
1664 function getacl($attribute,$skip_write= FALSE)
1665 {
1666 $ui= get_userinfo();
1667 return $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute,$skip_write);
1668 }
1671 /*! \brief Returns a list of all available departments for this object.
1672 If this object is new, all departments we are allowed to create a new user in are returned.
1673 If this is an existing object, return all deps. we are allowed to move tis object too.
1675 @return Array [dn] => "..name" // All deps. we are allowed to act on.
1676 */
1677 function get_allowed_bases()
1678 {
1679 $ui = get_userinfo();
1680 $deps = array();
1682 /* Is this a new object ? Or just an edited existing object */
1683 if(!$this->initially_was_account && $this->is_account){
1684 $new = true;
1685 }else{
1686 $new = false;
1687 }
1689 foreach($this->config->idepartments as $dn => $name){
1690 if($new && $this->acl_is_createable($dn)){
1691 $deps[$dn] = $name;
1692 }elseif(!$new && $this->acl_is_moveable($dn)){
1693 $deps[$dn] = $name;
1694 }
1695 }
1697 /* Add current base */
1698 if(isset($this->base) && isset($this->config->idepartments[$this->base])){
1699 $deps[$this->base] = $this->config->idepartments[$this->base];
1700 }elseif(strtolower($this->dn) == strtolower($this->config->current['BASE'])){
1702 }else{
1703 trigger_error("Cannot return list of departments, no default base found in class ".get_class($this).". ".$this->base);
1704 }
1705 return($deps);
1706 }
1709 /* This function modifies object acls too, if an object is moved.
1710 * $old_dn specifies the actually used dn
1711 * $new_dn specifies the destiantion dn
1712 */
1713 function update_acls($old_dn,$new_dn,$output_changes = FALSE)
1714 {
1715 /* Check if old_dn is empty. This should never happen */
1716 if(empty($old_dn) || empty($new_dn)){
1717 trigger_error("Failed to check acl dependencies, wrong dn given.");
1718 return;
1719 }
1721 /* Update userinfo if necessary */
1722 $ui = session::get('ui');
1723 if($ui->dn == $old_dn){
1724 $ui->dn = $new_dn;
1725 session::set('ui',$ui);
1726 new log("view","acl/".get_class($this),$this->dn,array(),"Updated current user dn from '".$old_dn."' to '".$new_dn."'");
1727 }
1729 /* Object was moved, ensure that all acls will be moved too */
1730 if($new_dn != $old_dn && $old_dn != "new"){
1732 /* get_ldap configuration */
1733 $update = array();
1734 $ldap = $this->config->get_ldap_link();
1735 $ldap->cd ($this->config->current['BASE']);
1736 $ldap->search("(&(objectClass=gosaAcl)(gosaAclEntry=*))",array("cn","gosaAclEntry"));
1737 while($attrs = $ldap->fetch()){
1739 $acls = array();
1741 /* Reset vars */
1742 $found = false;
1744 /* Walk through acls */
1745 for($i = 0 ; $i < $attrs['gosaAclEntry']['count'] ; $i ++ ){
1747 /* Get Acl parts */
1748 $acl_parts = split(":",$attrs['gosaAclEntry'][$i]);
1750 /* Get every single member for this acl */
1751 $members = array();
1752 if(preg_match("/,/",$acl_parts[2])){
1753 $members = split(",",$acl_parts[2]);
1754 }else{
1755 $members = array($acl_parts[2]);
1756 }
1758 /* Check if member match current dn */
1759 foreach($members as $key => $member){
1760 $member = base64_decode($member);
1761 if($member == $old_dn){
1762 $found = true;
1763 $members[$key] = base64_encode($new_dn);
1764 }
1765 }
1767 /* Create new member string */
1768 $new_members = "";
1769 foreach($members as $member){
1770 $new_members .= $member.",";
1771 }
1772 $new_members = preg_replace("/,$/","",$new_members);
1773 $acl_parts[2] = $new_members;
1775 /* Reconstruckt acl entry */
1776 $acl_str ="";
1777 foreach($acl_parts as $t){
1778 $acl_str .= $t.":";
1779 }
1780 $acl_str = preg_replace("/:$/","",$acl_str);
1781 $acls[] = $acl_str;
1782 }
1784 /* Acls for this object must be adjusted */
1785 if($found){
1787 $debug_info= _("Changing ACL dn")." : <br> -"._("from")." <b> ".
1788 $old_dn."</b><br> -"._("to")." <b>".$new_dn."</b><br>";
1789 @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,$debug_info,"ACL");
1791 $update[$attrs['dn']] =array();
1792 foreach($acls as $acl){
1793 $update[$attrs['dn']]['gosaAclEntry'][] = $acl;
1794 }
1795 }
1796 }
1798 /* Write updated acls */
1799 foreach($update as $dn => $attrs){
1800 $ldap->cd($dn);
1801 $ldap->modify($attrs);
1802 }
1803 }
1804 }
1808 /* This function enables the entry Serial ID check.
1809 * If an entry was edited while we have edited the entry too,
1810 * an error message will be shown.
1811 * To configure this check correctly read the FAQ.
1812 */
1813 function enable_CSN_check()
1814 {
1815 $this->CSN_check_active =TRUE;
1816 $this->entryCSN = getEntryCSN($this->dn);
1817 }
1820 /*! \brief Prepares the plugin to be used for multiple edit
1821 * Update plugin attributes with given array of attribtues.
1822 * @param array Array with attributes that must be updated.
1823 */
1824 function init_multiple_support($attrs,$all)
1825 {
1826 $ldap= $this->config->get_ldap_link();
1827 $this->multi_attrs = $attrs;
1828 $this->multi_attrs_all= $all;
1830 /* Copy needed attributes */
1831 foreach ($this->attributes as $val){
1832 $found= array_key_ics($val, $this->multi_attrs);
1833 if ($found != ""){
1834 if(isset($this->multi_attrs["$found"][0])){
1835 $this->$val= $this->multi_attrs["$found"][0];
1836 }
1837 }
1838 }
1839 }
1842 /*! \brief Enables multiple support for this plugin
1843 */
1844 function enable_multiple_support()
1845 {
1846 $this->ignore_account = TRUE;
1847 $this->multiple_support_active = TRUE;
1848 }
1851 /*! \brief Returns all values that have been modfied in multiple edit mode.
1852 @return array Cotaining all mdofied values.
1853 */
1854 function get_multi_edit_values()
1855 {
1856 $ret = array();
1857 foreach($this->attributes as $attr){
1858 if(in_array($attr,$this->multi_boxes)){
1859 $ret[$attr] = $this->$attr;
1860 }
1861 }
1862 return($ret);
1863 }
1866 /*! \brief Update class variables with values collected by multiple edit.
1867 */
1868 function set_multi_edit_values($attrs)
1869 {
1870 foreach($attrs as $name => $value){
1871 $this->$name = $value;
1872 }
1873 }
1876 /*! \brief execute plugin
1878 Generates the html output for this node
1879 */
1880 function multiple_execute()
1881 {
1882 /* This one is empty currently. Fabian - please fill in the docu code */
1883 session::set('current_class_for_help',get_class($this));
1885 /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
1886 session::set('LOCK_VARS_TO_USE',array());
1887 session::set('LOCK_VARS_USED',array());
1889 return("Multiple edit is currently not implemented for this plugin.");
1890 }
1893 /*! \brief Save HTML posted data to object for multiple edit
1894 */
1895 function multiple_save_object()
1896 {
1897 if(empty($this->entryCSN) && $this->CSN_check_active){
1898 $this->entryCSN = getEntryCSN($this->dn);
1899 }
1901 /* Save values to object */
1902 $this->multi_boxes = array();
1903 foreach ($this->attributes as $val){
1905 /* Get selected checkboxes from multiple edit */
1906 if(isset($_POST["use_".$val])){
1907 $this->multi_boxes[] = $val;
1908 }
1910 if ($this->acl_is_writeable($val) && isset ($_POST["$val"])){
1912 /* Check for modifications */
1913 if (get_magic_quotes_gpc()) {
1914 $data= stripcslashes($_POST["$val"]);
1915 } else {
1916 $data= $this->$val = $_POST["$val"];
1917 }
1918 if ($this->$val != $data){
1919 $this->is_modified= TRUE;
1920 }
1922 /* IE post fix */
1923 if(isset($data[0]) && $data[0] == chr(194)) {
1924 $data = "";
1925 }
1926 $this->$val= $data;
1927 }
1928 }
1929 }
1932 /*! \brief Returns all attributes of this plugin,
1933 to be able to detect multiple used attributes
1934 in multi_plugg::detect_multiple_used_attributes().
1935 @return array Attributes required for intialization of multi_plug
1936 */
1937 public function get_multi_init_values()
1938 {
1939 $attrs = $this->attrs;
1940 return($attrs);
1941 }
1944 /*! \brief Check given values in multiple edit
1945 @return array Error messages
1946 */
1947 function multiple_check()
1948 {
1949 $message = plugin::check();
1950 return($message);
1951 }
1954 /*! \brief Returns the snapshot header part for "Actions" menu in management dialogs
1955 @param $layer_menu
1956 */
1957 function get_snapshot_header($base,$category)
1958 {
1959 $str = "";
1960 $ui = get_userinfo();
1961 if($this->snapshotEnabled() && $ui->allow_snapshot_restore($base,$category)){
1963 $ok = false;
1964 foreach($this->get_used_snapshot_bases() as $base){
1965 $ok |= count($this->getAllDeletedSnapshots($base)) >= 1 ;
1966 }
1968 if($ok){
1969 $str = "..|<img class='center' src='images/lists/restore.png' ".
1970 "alt='"._("Restore")."'> "._("Restore"). "|RestoreDeletedSnapShot|\n";
1971 }else{
1972 $str = "..|<img class='center' src='images/lists/restore_grey.png' alt=''> "._("Restore")."||\n";
1973 }
1974 }
1975 return($str);
1976 }
1979 function get_snapshot_action($base,$category)
1980 {
1981 $str= "";
1982 $ui = get_userinfo();
1983 if($this->snapshotEnabled()){
1984 if ($ui->allow_snapshot_restore($base,$category)){
1986 if(count($this->Available_SnapsShots($base))){
1987 $str.= "<input class='center' type='image' src='images/lists/restore.png'
1988 alt='"._("Restore snapshot")."' name='RestoreSnapShotDialog_".base64_encode($base)."' title='"._("Restore snapshot")."'> ";
1989 } else {
1990 $str = "<img class='center' src='images/lists/restore_grey.png' alt=''> ";
1991 }
1992 }
1993 if($ui->allow_snapshot_create($base,$category)){
1994 $str.= "<input class='center' type='image' src='images/snapshot.png'
1995 alt='"._("Create snapshot")."' name='CreateSnapShotDialog_".base64_encode($base)."'
1996 title='"._("Create a new snapshot from this object")."'> ";
1997 }else{
1998 $str = "<img class='center' src='images/empty.png' alt=' '> ";
1999 }
2000 }
2002 return($str);
2003 }
2006 function get_copypaste_action($base,$category,$class,$copy = TRUE, $cut = TRUE)
2007 {
2008 $ui = get_userinfo();
2009 $action = "";
2010 if($this->CopyPasteHandler){
2011 if($cut){
2012 if($ui->is_cutable($base,$category,$class)){
2013 $action .= "<input class='center' type='image'
2014 src='images/lists/cut.png' alt='"._("cut")."' name='cut_%KEY%' title='"._("Cut this entry")."'> ";
2015 }else{
2016 $action.="<img src='images/empty.png' alt=' ' class='center'> ";
2017 }
2018 }
2019 if($copy){
2020 if($ui->is_copyable($base,$category,$class)){
2021 $action.= "<input class='center' type='image'
2022 src='images/lists/copy.png' alt='"._("copy")."' name='copy_%KEY%' title='"._("Copy this entry")."'> ";
2023 }else{
2024 $action.="<img src='images/empty.png' alt=' ' class='center'> ";
2025 }
2026 }
2027 }
2029 return($action);
2030 }
2033 function get_copypaste_header($base,$category,$copy = TRUE, $cut = TRUE)
2034 {
2035 $s = "";
2036 $ui =get_userinfo();
2038 if(!is_array($category)){
2039 $category = array($category);
2040 }
2042 /* Check permissions for each category, if there is at least one category which
2043 support read or paste permissions for the given base, then display the specific actions.
2044 */
2045 $readable = $pasteable = TRUE;
2046 foreach($category as $cat){
2047 $readable |= $ui->get_category_permissions($base,$cat);
2048 $pasteable|= $ui->is_pasteable($base,$cat);
2049 }
2051 if(($cut || $copy) && isset($this->CopyPasteHandler) && is_object($this->CopyPasteHandler)){
2052 if($readable){
2053 $s.= "..|---|\n";
2054 if($copy){
2055 $s.= "..|<img src='images/lists/copy.png' alt='' border='0' class='center'>".
2056 " "._("Copy")."|"."multiple_copy_systems|\n";
2057 }
2058 if($cut){
2059 $s.= "..|<img src='images/lists/cut.png' alt='' border='0' class='center'>".
2060 " "._("Cut")."|"."multiple_cut_systems|\n";
2061 }
2062 }
2064 if($pasteable){
2065 if($this->CopyPasteHandler->entries_queued()){
2066 $img = "<img border='0' class='center' src='images/lists/paste.png' alt=''>";
2067 $s.="..|".$img." "._("Paste")."|editPaste|\n";
2068 }else{
2069 $img = "<img border='0' class='center' src='images/lists/paste-grey.png' alt=''>";
2070 $s.="..|".$img." "._("Paste")."\n";
2071 }
2072 }
2073 }
2074 return($s);
2075 }
2078 function get_used_snapshot_bases()
2079 {
2080 return(array());
2081 }
2082 }
2084 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
2085 ?>