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