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 /*! \brief The title shown in path menu while this plugin is visible.
36 */
37 var $pathTitle = "";
39 /*!
40 \brief Reference to parent object
42 This variable is used when the plugin is included in tabs
43 and keeps reference to the tab class. Communication to other
44 tabs is possible by 'name'. So the 'fax' plugin can ask the
45 'userinfo' plugin for the fax number.
47 \sa tab
48 */
49 var $parent= NULL;
51 /*!
52 \brief Configuration container
54 Access to global configuration
55 */
56 var $config= NULL;
58 /*!
59 \brief Mark plugin as account
61 Defines whether this plugin is defined as an account or not.
62 This has consequences for the plugin to be saved from tab
63 mode. If it is set to 'FALSE' the tab will call the delete
64 function, else the save function. Should be set to 'TRUE' if
65 the construtor detects a valid LDAP object.
67 \sa plugin::plugin()
68 */
69 var $is_account= FALSE;
70 var $initially_was_account= FALSE;
72 /*!
73 \brief Mark plugin as template
75 Defines whether we are creating a template or a normal object.
76 Has conseqences on the way execute() shows the formular and how
77 save() puts the data to LDAP.
79 \sa plugin::save() plugin::execute()
80 */
81 var $is_template= FALSE;
82 var $ignore_account= FALSE;
83 var $is_modified= FALSE;
85 /*!
86 \brief Represent temporary LDAP data
88 This is only used internally.
89 */
90 var $attrs= array();
92 /* Keep set of conflicting plugins */
93 var $conflicts= array();
95 /* Save unit tags */
96 var $gosaUnitTag= "";
97 var $skipTagging= FALSE;
99 /*!
100 \brief Used standard values
102 dn
103 */
104 var $dn= "";
105 var $uid= "";
106 var $sn= "";
107 var $givenName= "";
108 var $acl= "*none*";
109 var $dialog= FALSE;
110 var $snapDialog = NULL;
112 /* attribute list for save action */
113 var $attributes= array();
114 var $objectclasses= array();
115 var $is_new= TRUE;
116 var $saved_attributes= array();
118 var $acl_base= "";
119 var $acl_category= "";
120 var $read_only = FALSE; // Used when the entry is opened as "readonly" due to locks.
122 /* This can be set to render the tabulators in another stylesheet */
123 var $pl_notify= FALSE;
125 /* Object entry CSN */
126 var $entryCSN = "";
127 var $CSN_check_active = FALSE;
129 /* This variable indicates that this class can handle multiple dns at once. */
130 var $multiple_support = FALSE;
131 var $multi_attrs = array();
132 var $multi_attrs_all = array();
134 /* This aviable indicates, that we are currently in multiple edit handle */
135 var $multiple_support_active = FALSE;
136 var $selected_edit_values = array();
137 var $multi_boxes = array();
139 /*! \brief plugin constructor
141 If 'dn' is set, the node loads the given 'dn' from LDAP
143 \param dn Distinguished name to initialize plugin from
144 \sa plugin()
145 */
146 function plugin (&$config, $dn= NULL, $object= NULL)
147 {
148 /* Configuration is fine, allways */
149 $this->config= &$config;
150 $this->dn= $dn;
152 // Ensure that we've a valid acl_category set.
153 if(empty($this->acl_category)){
154 $tmp = $this->plInfo();
155 if (isset($tmp['plCategory'])) {
156 $c = key($tmp['plCategory']);
157 if(is_numeric($c)){
158 $c = $tmp['plCategory'][0];
159 }
160 $this->acl_category = $c."/";
161 }
162 }
164 /* Handle new accounts, don't read information from LDAP */
165 if ($dn == "new"){
166 return;
167 }
169 /* Check if this entry was opened in read only mode */
170 if(isset($_POST['open_readonly'])){
171 if(session::global_is_set("LOCK_CACHE")){
172 $cache = &session::get("LOCK_CACHE");
173 if(isset($cache['READ_ONLY'][$this->dn])){
174 $this->read_only = TRUE;
175 }
176 }
177 }
179 /* Save current dn as acl_base */
180 $this->acl_base= $dn;
182 /* Get LDAP descriptor */
183 if ($dn !== NULL){
185 /* Load data to 'attrs' and save 'dn' */
186 if ($object !== NULL){
187 $this->attrs= $object->attrs;
188 } else {
189 $ldap= $this->config->get_ldap_link();
190 $ldap->cat ($dn);
191 $this->attrs= $ldap->fetch();
192 }
194 /* Copy needed attributes */
195 foreach ($this->attributes as $val){
196 $found= array_key_ics($val, $this->attrs);
197 if ($found != ""){
198 $this->$val= $found[0];
199 }
200 }
202 /* gosaUnitTag loading... */
203 if (isset($this->attrs['gosaUnitTag'][0])){
204 $this->gosaUnitTag= $this->attrs['gosaUnitTag'][0];
205 }
207 /* Set the template flag according to the existence of objectClass
208 gosaUserTemplate */
209 if (isset($this->attrs['objectClass'])){
210 if (in_array_ics ("gosaUserTemplate", $this->attrs['objectClass'])){
211 $this->is_template= TRUE;
212 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
213 "found", "Template check");
214 }
215 }
217 /* Is Account? */
218 $found= TRUE;
219 foreach ($this->objectclasses as $obj){
220 if (preg_match('/top/i', $obj)){
221 continue;
222 }
223 if (!isset($this->attrs['objectClass']) || !in_array_ics ($obj, $this->attrs['objectClass'])){
224 $found= FALSE;
225 break;
226 }
227 }
228 if ($found){
229 $this->is_account= TRUE;
230 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
231 "found", "Object check");
232 }
234 /* Prepare saved attributes */
235 $this->saved_attributes= $this->attrs;
236 foreach ($this->saved_attributes as $index => $value){
237 if (is_numeric($index)){
238 unset($this->saved_attributes[$index]);
239 continue;
240 }
242 if (!in_array_ics($index, $this->attributes) && strcasecmp('objectClass', $index)){
243 unset($this->saved_attributes[$index]);
244 continue;
245 }
247 if (isset($this->saved_attributes[$index][0])){
248 if(!isset($this->saved_attributes[$index]["count"])){
249 $this->saved_attributes[$index]["count"] = count($this->saved_attributes[$index]);
250 }
251 if($this->saved_attributes[$index]["count"] == 1){
252 $tmp= $this->saved_attributes[$index][0];
253 unset($this->saved_attributes[$index]);
254 $this->saved_attributes[$index]= $tmp;
255 continue;
256 }
257 }
258 unset($this->saved_attributes["$index"]["count"]);
259 }
261 if(isset($this->attrs['gosaUnitTag'])){
262 $this->saved_attributes['gosaUnitTag'] = $this->attrs['gosaUnitTag'][0];
263 }
264 }
266 /* Save initial account state */
267 $this->initially_was_account= $this->is_account;
268 }
271 /*! \brief Generates the html output for this node
272 */
273 function execute()
274 {
275 /* This one is empty currently. Fabian - please fill in the docu code */
276 session::global_set('current_class_for_help',get_class($this));
278 /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
279 session::set('LOCK_VARS_TO_USE',array());
280 session::set('LOCK_VARS_USED_GET',array());
281 session::set('LOCK_VARS_USED_POST',array());
282 session::set('LOCK_VARS_USED_REQUEST',array());
284 pathNavigator::registerPlugin($this);
285 }
287 /*! \brief Removes object from parent
288 */
289 function remove_from_parent()
290 {
291 /* include global link_info */
292 $ldap= $this->config->get_ldap_link();
294 /* Get current objectClasses in order to add the required ones */
295 $ldap->cat($this->dn);
296 $tmp= $ldap->fetch ();
297 $oc= array();
298 if (isset($tmp['objectClass'])){
299 $oc= $tmp['objectClass'];
300 unset($oc['count']);
301 }
303 /* Remove objectClasses from entry */
304 $ldap->cd($this->dn);
305 $this->attrs= array();
306 $this->attrs['objectClass']= array_remove_entries_ics($this->objectclasses,$oc);
308 /* Unset attributes from entry */
309 foreach ($this->attributes as $val){
310 $this->attrs["$val"]= array();
311 }
313 /* Unset account info */
314 $this->is_account= FALSE;
316 /* Do not write in plugin base class, this must be done by
317 children, since there are normally additional attribs,
318 lists, etc. */
319 /*
320 $ldap->modify($this->attrs);
321 */
322 if($this->initially_was_account){
323 $this->handle_pre_events('remove');
324 }
325 }
328 /*! \brief Save HTML posted data to object
329 */
330 function save_object()
331 {
332 /* Update entry CSN if it is empty. */
333 if(empty($this->entryCSN) && $this->CSN_check_active){
334 $this->entryCSN = getEntryCSN($this->dn);
335 }
337 /* Save values to object */
338 foreach ($this->attributes as $val){
339 if (isset ($_POST["$val"]) && $this->acl_is_writeable($val)){
341 /* Check for modifications */
342 $data= get_post($val);
343 if ($this->$val != $data){
344 $this->is_modified= TRUE;
345 }
346 $this->$val = $data;
348 /* Okay, how can I explain this fix ...
349 * In firefox, disabled option fields aren't selectable ... but in IE you can select these fileds.
350 * So IE posts these 'unselectable' option, with value = chr(194)
351 * chr(194) seems to be the in between the ...option> </option.. because there is no value=".." specified in these option fields
352 * This was added for W3c compliance, but now causes these ... ldap errors ...
353 * So we set these Fields to ""; a normal empty string, and we can check these values in plugin::check() again ...
354 */
355 if(isset($data[0]) && $data[0] == chr(194)) {
356 $data = "";
357 }
358 $this->$val= $data;
359 }
360 }
361 }
364 /*! \brief Save data to LDAP, depending on is_account we save or delete */
365 function save()
366 {
367 /* include global link_info */
368 $ldap= $this->config->get_ldap_link();
370 /* Save all plugins */
371 $this->entryCSN = "";
373 /* Start with empty array */
374 $this->attrs= array();
376 /* Get current objectClasses in order to add the required ones */
377 $ldap->cat($this->dn);
379 $tmp= $ldap->fetch ();
381 $oc= array();
382 if (isset($tmp['objectClass'])){
383 $oc= $tmp["objectClass"];
384 $this->is_new= FALSE;
385 unset($oc['count']);
386 } else {
387 $this->is_new= TRUE;
388 }
390 /* Load (minimum) attributes, add missing ones */
391 $this->attrs['objectClass']= gosa_array_merge($oc,$this->objectclasses);
393 /* Copy standard attributes */
394 foreach ($this->attributes as $val){
395 if ($this->$val != ""){
396 $this->attrs["$val"]= $this->$val;
397 } elseif (!$this->is_new) {
398 $this->attrs["$val"]= array();
399 }
400 }
402 /* Handle tagging */
403 $this->tag_attrs($this->attrs);
405 if($this->is_new){
406 $this->handle_pre_events('add');
407 }else{
408 $this->handle_pre_events('modify');
409 }
410 }
413 /*! \brief Forward command execution requests
414 * to the hook execution method.
415 */
416 function handle_pre_events($mode, $addAttrs= array())
417 {
418 if(!in_array($mode, array('add','remove','modify'))){
419 trigger_error(sprintf("Invalid pre event type given %s! Valid types are [add,modify,remove].", $mode));
420 return;
421 }
422 switch ($mode){
423 case "add":
424 plugin::callHook($this,"PRECREATE", $addAttrs);
425 break;
427 case "modify":
428 plugin::callHook($this,"PREMODIFY", $addAttrs);
429 break;
431 case "remove":
432 plugin::callHook($this,"PREREMOVE", $addAttrs);
433 break;
434 }
435 }
438 function cleanup()
439 {
440 foreach ($this->attrs as $index => $value){
442 /* Convert arrays with one element to non arrays, if the saved
443 attributes are no array, too */
444 if (is_array($this->attrs[$index]) &&
445 count ($this->attrs[$index]) == 1 &&
446 isset($this->saved_attributes[$index]) &&
447 !is_array($this->saved_attributes[$index])){
449 $tmp= $this->attrs[$index][0];
450 $this->attrs[$index]= $tmp;
451 }
453 /* Remove emtpy arrays if they do not differ */
454 if (is_array($this->attrs[$index]) &&
455 count($this->attrs[$index]) == 0 &&
456 !isset($this->saved_attributes[$index])){
458 unset ($this->attrs[$index]);
459 continue;
460 }
462 /* Remove single attributes that do not differ */
463 if (!is_array($this->attrs[$index]) &&
464 isset($this->saved_attributes[$index]) &&
465 !is_array($this->saved_attributes[$index]) &&
466 $this->attrs[$index] == $this->saved_attributes[$index]){
468 unset ($this->attrs[$index]);
469 continue;
470 }
472 /* Remove arrays that do not differ */
473 if (is_array($this->attrs[$index]) &&
474 isset($this->saved_attributes[$index]) &&
475 is_array($this->saved_attributes[$index])){
477 if (!array_differs($this->attrs[$index],$this->saved_attributes[$index])){
478 unset ($this->attrs[$index]);
479 continue;
480 }
481 }
482 }
484 /* Update saved attributes and ensure that next cleanups will be successful too */
485 foreach($this->attrs as $name => $value){
486 $this->saved_attributes[$name] = $value;
487 }
488 }
490 /*! \brief Check formular input */
491 function check()
492 {
493 $message= array();
495 /* Skip if we've no config object */
496 if (!isset($this->config) || !is_object($this->config)){
497 return $message;
498 }
500 /* Find hooks entries for this class */
501 $command = $this->config->configRegistry->getPropertyValue(get_class($this),"check");
502 if ($command != ""){
504 if (!check_command($command)){
505 $message[]= msgPool::cmdnotfound("CHECK", get_class($this));
506 } else {
508 /* Generate "ldif" for check hook */
509 $ldif= "dn: $this->dn\n";
511 /* ... objectClasses */
512 foreach ($this->objectclasses as $oc){
513 $ldif.= "objectClass: $oc\n";
514 }
516 /* ... attributes */
517 foreach ($this->attributes as $attr){
518 if ($this->$attr == ""){
519 continue;
520 }
521 if (is_array($this->$attr)){
522 foreach ($this->$attr as $val){
523 $ldif.= "$attr: $val\n";
524 }
525 } else {
526 $ldif.= "$attr: ".$this->$attr."\n";
527 }
528 }
530 /* Append empty line */
531 $ldif.= "\n";
533 /* Feed "ldif" into hook and retrieve result*/
534 $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
535 $fh= proc_open($command, $descriptorspec, $pipes);
536 if (is_resource($fh)) {
537 fwrite ($pipes[0], $ldif);
538 fclose($pipes[0]);
540 $result= stream_get_contents($pipes[1]);
541 if ($result != ""){
542 $message[]= $result;
543 }
545 fclose($pipes[1]);
546 fclose($pipes[2]);
547 proc_close($fh);
548 }
549 }
551 }
553 /* Check entryCSN */
554 if($this->CSN_check_active){
555 $current_csn = getEntryCSN($this->dn);
556 if($current_csn != $this->entryCSN && !empty($this->entryCSN) && !empty($current_csn)){
557 $this->entryCSN = $current_csn;
558 $message[] = _("The current object has been altered while beeing edited. If you save this entry, changes that have been made by others will be discarded!");
559 }
560 }
561 return ($message);
562 }
564 /* Adapt from template, using 'dn' */
565 function adapt_from_template($dn, $skip= array())
566 {
567 /* Include global link_info */
568 $ldap= $this->config->get_ldap_link();
570 /* Load requested 'dn' to 'attrs' */
571 $ldap->cat ($dn);
572 $this->attrs= $ldap->fetch();
574 /* Walk through attributes */
575 foreach ($this->attributes as $val){
577 /* Skip the ones in skip list */
578 if (in_array($val, $skip)){
579 continue;
580 }
582 if (isset($this->attrs["$val"][0])){
584 /* If attribute is set, replace dynamic parts:
585 %sn, %givenName and %uid. Fill these in our local variables. */
586 $value= $this->attrs["$val"][0];
588 foreach (array("sn", "givenName", "uid") as $repl){
589 if (preg_match("/%$repl/i", $value)){
590 $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
591 }
592 }
593 $this->$val= $value;
594 }
595 }
597 /* Is Account? */
598 $found= TRUE;
599 foreach ($this->objectclasses as $obj){
600 if (preg_match('/top/i', $obj)){
601 continue;
602 }
603 if (!in_array_ics ($obj, $this->attrs['objectClass'])){
604 $found= FALSE;
605 break;
606 }
607 }
608 if ($found){
609 $this->is_account= TRUE;
610 }
611 }
613 /* \brief Indicate whether a password change is needed or not */
614 function password_change_needed()
615 {
616 return FALSE;
617 }
620 /*! \brief Show header message for tab dialogs */
621 function show_enable_header($button_text, $text, $disabled= FALSE)
622 {
623 if (($disabled == TRUE) || (!$this->acl_is_createable())){
624 $state= "disabled";
625 } else {
626 $state= "";
627 }
628 $display = "<div class='plugin-enable-header'>\n";
629 $display.= "<p>$text</p>\n";
630 $display.= "<button type='submit' name=\"modify_state\" ".$state.">$button_text</button>\n";
631 $display.= "</div>\n";
633 return($display);
634 }
637 /*! \brief Show header message for tab dialogs */
638 function show_disable_header($button_text, $text, $disabled= FALSE)
639 {
640 if (($disabled == TRUE) || !$this->acl_is_removeable()){
641 $state= "disabled";
642 } else {
643 $state= "";
644 }
645 $display = "<div class='plugin-disable-header'>\n";
646 $display.= "<p>$text</p>\n";
647 $display.= "<button type='submit' name=\"modify_state\" ".$state.">$button_text</button>\n";
648 $display.= "</div>\n";
649 return($display);
650 }
654 /* Create unique DN */
655 function create_unique_dn2($data, $base)
656 {
657 $ldap= $this->config->get_ldap_link();
658 $base= preg_replace("/^,*/", "", $base);
660 /* Try to use plain entry first */
661 $dn= "$data,$base";
662 $attribute= preg_replace('/=.*$/', '', $data);
663 $ldap->cat ($dn, array('dn'));
664 if (!$ldap->fetch()){
665 return ($dn);
666 }
668 /* Look for additional attributes */
669 foreach ($this->attributes as $attr){
670 if ($attr == $attribute || $this->$attr == ""){
671 continue;
672 }
674 $dn= "$data+$attr=".$this->$attr.",$base";
675 $ldap->cat ($dn, array('dn'));
676 if (!$ldap->fetch()){
677 return ($dn);
678 }
679 }
681 /* None found */
682 return ("none");
683 }
686 /*! \brief Create unique DN */
687 function create_unique_dn($attribute, $base)
688 {
689 $ldap= $this->config->get_ldap_link();
690 $base= preg_replace("/^,*/", "", $base);
692 /* Try to use plain entry first */
693 $dn= "$attribute=".$this->$attribute.",$base";
694 $ldap->cat ($dn, array('dn'));
695 if (!$ldap->fetch()){
696 return ($dn);
697 }
699 /* Look for additional attributes */
700 foreach ($this->attributes as $attr){
701 if ($attr == $attribute || $this->$attr == ""){
702 continue;
703 }
705 $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
706 $ldap->cat ($dn, array('dn'));
707 if (!$ldap->fetch()){
708 return ($dn);
709 }
710 }
712 /* None found */
713 return ("none");
714 }
717 function rebind($ldap, $referral)
718 {
719 $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
720 if (ldap_bind($ldap, $credentials['ADMIN'], $this->config->get_credentials($credentials['PASSWORD']))) {
721 $this->error = "Success";
722 $this->hascon=true;
723 $this->reconnect= true;
724 return (0);
725 } else {
726 $this->error = "Could not bind to " . $credentials['ADMIN'];
727 return NULL;
728 }
729 }
732 /* Recursively copy ldap object */
733 function _copy($src_dn,$dst_dn)
734 {
735 $ldap=$this->config->get_ldap_link();
736 $ldap->cat($src_dn);
737 $attrs= $ldap->fetch();
739 /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
740 $ds= ldap_connect($this->config->current['SERVER']);
741 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
742 if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
743 ldap_set_rebind_proc($ds, array(&$this, "rebind"));
744 }
746 $pwd = $this->config->get_credentials($this->config->current['ADMINPASSWORD']);
747 $r=ldap_bind($ds,$this->config->current['ADMINDN'], $pwd);
748 $sr=ldap_read($ds, LDAP::fix($src_dn), "objectClass=*");
750 /* Fill data from LDAP */
751 $new= array();
752 if ($sr) {
753 $ei=ldap_first_entry($ds, $sr);
754 if ($ei) {
755 foreach($attrs as $attr => $val){
756 if ($info = @ldap_get_values_len($ds, $ei, $attr)){
757 for ($i= 0; $i<$info['count']; $i++){
758 if ($info['count'] == 1){
759 $new[$attr]= $info[$i];
760 } else {
761 $new[$attr][]= $info[$i];
762 }
763 }
764 }
765 }
766 }
767 }
769 /* close conncetion */
770 ldap_unbind($ds);
772 /* Adapt naming attribute */
773 $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
774 $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
775 $new[$dst_name]= LDAP::fix($dst_val);
777 /* Check if this is a department.
778 * If it is a dep. && there is a , override in his ou
779 * change \2C to , again, else this entry can't be saved ...
780 */
781 if((isset($new['ou'])) &&( preg_match("/\\,/",$new['ou']))){
782 $new['ou'] = str_replace("\\\\,",",",$new['ou']);
783 }
785 /* Save copy */
786 $ldap->connect();
787 $ldap->cd($this->config->current['BASE']);
789 $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
791 /* FAIvariable=.../..., cn=..
792 could not be saved, because the attribute FAIvariable was different to
793 the dn FAIvariable=..., cn=... */
795 if(!is_array($new['objectClass'])) $new['objectClass'] = array($new['objectClass']);
797 if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
798 $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
799 }
800 $ldap->cd($dst_dn);
801 $ldap->add($new);
803 if (!$ldap->success()){
804 trigger_error("Trying to save $dst_dn failed.",
805 E_USER_WARNING);
806 return(FALSE);
807 }
808 return(TRUE);
809 }
812 /* This is a workaround function. */
813 function copy($src_dn, $dst_dn)
814 {
815 /* Rename dn in possible object groups */
816 $ldap= $this->config->get_ldap_link();
817 $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.@LDAP::prepare4filter($src_dn).'))',
818 array('cn'));
819 while ($attrs= $ldap->fetch()){
820 $og= new ogroup($this->config, $ldap->getDN());
821 unset($og->member[$src_dn]);
822 $og->member[$dst_dn]= $dst_dn;
823 $og->save ();
824 }
826 $ldap->cat($dst_dn);
827 $attrs= $ldap->fetch();
828 if (count($attrs)){
829 trigger_error("Trying to overwrite ".LDAP::fix($dst_dn).", which already exists.",
830 E_USER_WARNING);
831 return (FALSE);
832 }
834 $ldap->cat($src_dn);
835 $attrs= $ldap->fetch();
836 if (!count($attrs)){
837 trigger_error("Trying to move ".LDAP::fix($src_dn).", which does not seem to exist.",
838 E_USER_WARNING);
839 return (FALSE);
840 }
842 $ldap->cd($src_dn);
843 $ldap->search("objectClass=*",array("dn"));
844 while($attrs = $ldap->fetch()){
845 $src = $attrs['dn'];
846 $dst = preg_replace("/".preg_quote($src_dn, '/')."$/",$dst_dn,$attrs['dn']);
847 $this->_copy($src,$dst);
848 }
849 return (TRUE);
850 }
854 /*! \brief Rename/Move a given src_dn to the given dest_dn
855 *
856 * Move a given ldap object indentified by $src_dn to the
857 * given destination $dst_dn
858 *
859 * - Ensure that all references are updated (ogroups)
860 * - Update ACLs
861 * - Update accessTo
862 *
863 * \param string 'src_dn' the source DN.
864 * \param string 'dst_dn' the destination DN.
865 * \return boolean TRUE on success else FALSE.
866 */
867 function rename($src_dn, $dst_dn)
868 {
869 $start = microtime(1);
871 /* Try to move the source entry to the destination position */
872 $ldap = $this->config->get_ldap_link();
873 $ldap->cd($this->config->current['BASE']);
874 $ldap->create_missing_trees(preg_replace("/^[^,]+,/","",$dst_dn));
875 if (!$ldap->rename_dn($src_dn,$dst_dn)){
876 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());
877 @DEBUG(DEBUG_LDAP,__LINE__,__FUNCTION__,__FILE__,"Rename failed FROM: $src_dn -- TO: $dst_dn",
878 "Ldap Protocol v3 implementation error, falling back to maunal method.");
879 return(FALSE);
880 }
882 /* Get list of users,groups and roles within this tree,
883 maybe we have to update ACL references.
884 */
885 $leaf_objs = get_list("(|(objectClass=posixGroup)(objectClass=gosaAccount)(objectClass=gosaRole))",array("all"),$dst_dn,
886 array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
887 foreach($leaf_objs as $obj){
888 $new_dn = $obj['dn'];
889 $old_dn = preg_replace("/".preg_quote(LDAP::convert($dst_dn), '/')."$/i",$src_dn,LDAP::convert($new_dn));
890 $this->update_acls($old_dn,$new_dn);
891 }
893 // Migrate objectgroups if needed
894 $ogroups = get_sub_list("(&(objectClass=gosaGroupOfNames)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))",
895 "ogroups", array(get_ou("group", "ogroupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
897 // Walk through all objectGroups
898 foreach($ogroups as $ogroup){
899 // Migrate old to new dn
900 $o_ogroup= new ogroup($this->config,$ogroup['dn']);
901 if (isset($o_ogroup->member[$src_dn])) {
902 unset($o_ogroup->member[$src_dn]);
903 }
904 $o_ogroup->member[$dst_dn]= $dst_dn;
906 // Save object group
907 $o_ogroup->save();
908 }
910 // Migrate rfc groups if needed
911 $groups = get_sub_list("(&(objectClass=posixGroup)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))","groups", array(get_ou("core", "groupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
913 // Walk through all POSIX groups
914 foreach($groups as $group){
916 // Migrate old to new dn
917 $o_group= new group($this->config,$group['dn']);
918 $o_group->save();
919 }
921 /* Update roles to use the new entry dn */
922 $roles = get_sub_list("(&(objectClass=organizationalRole)(roleOccupant=".LDAP::prepare4filter(LDAP::fix($src_dn))."))","roles", array(get_ou("roleGeneric", "roleRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
924 // Walk through all roles
925 foreach($roles as $role){
926 $role = new roleGeneric($this->config,$role['dn']);
927 $key= array_search($src_dn, $role->roleOccupant);
928 if($key !== FALSE){
929 $role->roleOccupant[$key] = $dst_dn;
930 $role->save();
931 }
932 }
934 // Update 'manager' attributes from gosaDepartment and inetOrgPerson
935 $filter = "(&(objectClass=inetOrgPerson)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn))."))";
936 $ocs = $ldap->get_objectclasses();
937 if(isset($ocs['gosaDepartment']['MAY']) && in_array('manager', $ocs['gosaDepartment']['MAY'])){
938 $filter = "(|".$filter."(&(objectClass=gosaDepartment)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn)).")))";
939 }
940 $leaf_deps= get_list($filter,array("all"),$this->config->current['BASE'],
941 array("manager","dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
942 foreach($leaf_deps as $entry){
943 $update = array('manager' => $dst_dn);
944 $ldap->cd($entry['dn']);
945 $ldap->modify($update);
946 if(!$ldap->success()){
947 trigger_error(sprintf("Failed to update manager for %s: %s", bold($entry['dn']), $ldap->get_error()));
948 }
949 }
951 // Migrate 'dyn-groups' here. labeledURIObject
952 if(class_available('DynamicLdapGroup')) {
953 DynamicLdapGroup::moveDynGroup($this->config,$src_dn,$dst_dn);
954 }
956 /* Check if there are gosa departments moved.
957 If there were deps moved, the force reload of config->deps.
958 */
959 $leaf_deps= get_list("(objectClass=gosaDepartment)",array("all"),$dst_dn,
960 array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
962 if(count($leaf_deps)){
963 $this->config->get_departments();
964 $this->config->make_idepartments();
965 session::global_set("config",$this->config);
966 $ui =get_userinfo();
967 $ui->reset_acl_cache();
968 }
970 return(TRUE);
971 }
975 function move($src_dn, $dst_dn)
976 {
977 /* Do not copy if only upper- lowercase has changed */
978 if(strtolower($src_dn) == strtolower($dst_dn)){
979 return(TRUE);
980 }
983 /* Try to move the entry instead of copy & delete
984 */
985 if(TRUE){
987 /* Try to move with ldap routines, if this was not successfull
988 fall back to the old style copy & remove method
989 */
990 if($this->rename($src_dn, $dst_dn)){
991 return(TRUE);
992 }else{
993 // See code below.
994 }
995 }
997 /* Copy source to destination */
998 if (!$this->copy($src_dn, $dst_dn)){
999 return (FALSE);
1000 }
1002 /* Delete source */
1003 $ldap= $this->config->get_ldap_link();
1004 $ldap->rmdir_recursive($src_dn);
1005 if (!$ldap->success()){
1006 trigger_error("Trying to delete $src_dn failed.",
1007 E_USER_WARNING);
1008 return (FALSE);
1009 }
1011 return (TRUE);
1012 }
1015 /* \brief Move/Rename complete trees */
1016 function recursive_move($src_dn, $dst_dn)
1017 {
1018 /* Check if the destination entry exists */
1019 $ldap= $this->config->get_ldap_link();
1021 /* Check if destination exists - abort */
1022 $ldap->cat($dst_dn, array('dn'));
1023 if ($ldap->fetch()){
1024 trigger_error("recursive_move $dst_dn already exists.",
1025 E_USER_WARNING);
1026 return (FALSE);
1027 }
1029 $this->copy($src_dn, $dst_dn);
1031 /* Remove src_dn */
1032 $ldap->cd($src_dn);
1033 $ldap->recursive_remove($src_dn);
1034 return (TRUE);
1035 }
1038 function saveCopyDialog(){
1039 }
1042 function getCopyDialog(){
1043 return(array("string"=>"","status"=>""));
1044 }
1047 /*! \brief Prepare for Copy & Paste */
1048 function PrepareForCopyPaste($source)
1049 {
1050 $todo = $this->attributes;
1051 if(isset($this->CopyPasteVars)){
1052 $todo = array_merge($todo,$this->CopyPasteVars);
1053 }
1055 if(count($this->objectclasses)){
1056 $this->is_account = TRUE;
1057 foreach($this->objectclasses as $class){
1058 if(!in_array($class,$source['objectClass'])){
1059 $this->is_account = FALSE;
1060 }
1061 }
1062 }
1064 foreach($todo as $var){
1065 if (isset($source[$var])){
1066 if(isset($source[$var]['count'])){
1067 if($source[$var]['count'] > 1){
1068 $tmp= $source[$var];
1069 unset($tmp['count']);
1070 $this->$var = $tmp;
1071 }else{
1072 $this->$var = $source[$var][0];
1073 }
1074 }else{
1075 $this->$var= $source[$var];
1076 }
1077 }
1078 }
1079 }
1081 /*! \brief Get gosaUnitTag for the given DN
1082 If this is called from departmentGeneric, we have to skip this
1083 tagging procedure.
1084 */
1085 function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1086 {
1087 /* Skip tagging? */
1088 if($this->skipTagging){
1089 return;
1090 }
1092 /* No dn? Self-operation... */
1093 if ($dn == ""){
1094 $dn= $this->dn;
1096 /* No tag? Find it yourself... */
1097 if ($tag == ""){
1098 $len= strlen($dn);
1100 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
1101 $relevant= array();
1102 foreach ($this->config->adepartments as $key => $ntag){
1104 /* This one is bigger than our dn, its not relevant... */
1105 if ($len < strlen($key)){
1106 continue;
1107 }
1109 /* This one matches with the latter part. Break and don't fix this entry */
1110 if (preg_match('/(^|,)'.preg_quote($key, '/').'$/', $dn)){
1111 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
1112 $relevant[strlen($key)]= $ntag;
1113 continue;
1114 }
1116 }
1118 /* If we've some relevant tags to set, just get the longest one */
1119 if (count($relevant)){
1120 ksort($relevant);
1121 $tmp= array_keys($relevant);
1122 $idx= end($tmp);
1123 $tag= $relevant[$idx];
1124 $this->gosaUnitTag= $tag;
1125 }
1126 }
1127 }
1129 /*! \brief Add unit tag */
1130 /* Remove tags that may already be here... */
1131 remove_objectClass("gosaAdministrativeUnitTag", $at);
1132 if (isset($at['gosaUnitTag'])){
1133 unset($at['gosaUnitTag']);
1134 }
1136 /* Set tag? */
1137 if ($tag != ""){
1138 add_objectClass("gosaAdministrativeUnitTag", $at);
1139 $at['gosaUnitTag']= $tag;
1140 }
1142 /* Initially this object was tagged.
1143 - But now, it is no longer inside a tagged department.
1144 So force the remove of the tag.
1145 (objectClass was already removed obove)
1146 */
1147 if($tag == "" && $this->gosaUnitTag){
1148 $at['gosaUnitTag'] = array();
1149 }
1150 }
1153 /*! \brief Test for removability of the object
1154 *
1155 * Allows testing of conditions for removal of object. If removal should be aborted
1156 * the function needs to remove an error message.
1157 * */
1158 function allow_remove()
1159 {
1160 $reason= "";
1161 return $reason;
1162 }
1165 /*! \brief Test if snapshotting is enabled
1166 *
1167 * Test weither snapshotting is enabled or not. There will also be some errors posted,
1168 * if the configuration failed
1169 * \return TRUE if snapshots are enabled, and FALSE if it is disabled
1170 */
1171 function snapshotEnabled()
1172 {
1173 return $this->config->snapshotEnabled();
1174 }
1177 /*! \brief Return plugin informations for acl handling
1178 * See class_core.inc for examples.
1179 */
1180 static function plInfo()
1181 {
1182 return array();
1183 }
1186 function set_acl_base($base)
1187 {
1188 @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,"<b>".$base."</b>","<b>ACL-Base:</b> ");
1189 $this->acl_base= $base;
1190 }
1193 function set_acl_category($category)
1194 {
1195 @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,"<b>".$category."</b>(/".get_class($this).")","<b>ACL-Category:</b> ");
1196 $this->acl_category= "$category/";
1197 }
1200 function acl_is_writeable($attribute,$skip_write = FALSE)
1201 {
1202 if($this->read_only) return(FALSE);
1203 $ui= get_userinfo();
1204 return preg_match('/w/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute, $skip_write));
1205 }
1208 function acl_is_readable($attribute)
1209 {
1210 $ui= get_userinfo();
1211 return preg_match('/r/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute));
1212 }
1215 function acl_is_createable($base ="")
1216 {
1217 if($this->read_only) return(FALSE);
1218 $ui= get_userinfo();
1219 if($base == "") $base = $this->acl_base;
1220 return preg_match('/c/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1221 }
1224 function acl_is_removeable($base ="")
1225 {
1226 if($this->read_only) return(FALSE);
1227 $ui= get_userinfo();
1228 if($base == "") $base = $this->acl_base;
1229 return preg_match('/d/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1230 }
1233 function acl_is_moveable($base = "")
1234 {
1235 if($this->read_only) return(FALSE);
1236 $ui= get_userinfo();
1237 if($base == "") $base = $this->acl_base;
1238 return preg_match('/m/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1239 }
1242 function acl_have_any_permissions()
1243 {
1244 }
1247 function getacl($attribute,$skip_write= FALSE)
1248 {
1249 $ui= get_userinfo();
1250 $skip_write |= $this->read_only;
1251 return $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute,$skip_write);
1252 }
1255 /*! \brief Returns a list of all available departments for this object.
1256 *
1257 * If this object is new, all departments we are allowed to create a new user in
1258 * are returned. If this is an existing object, return all deps.
1259 * We are allowed to move tis object too.
1260 * \return array [dn] => "..name" // All deps. we are allowed to act on.
1261 */
1262 function get_allowed_bases()
1263 {
1264 $ui = get_userinfo();
1265 $deps = array();
1267 /* Is this a new object ? Or just an edited existing object */
1268 if(!$this->initially_was_account && $this->is_account){
1269 $new = true;
1270 }else{
1271 $new = false;
1272 }
1274 foreach($this->config->idepartments as $dn => $name){
1275 if($new && $this->acl_is_createable($dn)){
1276 $deps[$dn] = $name;
1277 }elseif(!$new && $this->acl_is_moveable($dn)){
1278 $deps[$dn] = $name;
1279 }
1280 }
1282 /* Add current base */
1283 if(isset($this->base) && isset($this->config->idepartments[$this->base])){
1284 $deps[$this->base] = $this->config->idepartments[$this->base];
1285 }elseif(strtolower($this->dn) == strtolower($this->config->current['BASE'])){
1287 }else{
1288 trigger_error("Cannot return list of departments, no default base found in class ".get_class($this).". ".$this->base);
1289 }
1290 return($deps);
1291 }
1294 /* This function updates ACL settings if $old_dn was used.
1295 * \param string 'old_dn' specifies the actually used dn
1296 * \param string 'new_dn' specifies the destiantion dn
1297 */
1298 function update_acls($old_dn,$new_dn,$output_changes = FALSE)
1299 {
1300 /* Check if old_dn is empty. This should never happen */
1301 if(empty($old_dn) || empty($new_dn)){
1302 trigger_error("Failed to check acl dependencies, wrong dn given.");
1303 return;
1304 }
1306 /* Update userinfo if necessary */
1307 $ui = session::global_get('ui');
1308 if($ui->dn == $old_dn){
1309 $ui->dn = $new_dn;
1310 session::global_set('ui',$ui);
1311 new log("view","acl/".get_class($this),$this->dn,array(),"Updated current object dn from '".$old_dn."' to '".$new_dn."'");
1312 }
1314 /* Object was moved, ensure that all acls will be moved too */
1315 if($new_dn != $old_dn && $old_dn != "new"){
1317 /* get_ldap configuration */
1318 $update = array();
1319 $ldap = $this->config->get_ldap_link();
1320 $ldap->cd ($this->config->current['BASE']);
1321 $ldap->search("(&(objectClass=gosaAcl)(gosaAclEntry=*".base64_encode($old_dn)."*))",array("cn","gosaAclEntry"));
1322 while($attrs = $ldap->fetch()){
1323 $acls = array();
1324 $found = false;
1325 for($i = 0 ; $i < $attrs['gosaAclEntry']['count'] ; $i ++ ){
1326 $acl_parts = explode(":",$attrs['gosaAclEntry'][$i]);
1328 /* Roles uses antoher data storage order, members are stored int the third part,
1329 while the members in direct ACL assignments are stored in the second part.
1330 */
1331 $id = ($acl_parts[1] == "role") ? 3 : 2;
1333 /* Update member entries to use $new_dn instead of old_dn
1334 */
1335 $members = explode(",",$acl_parts[$id]);
1336 foreach($members as $key => $member){
1337 $member = base64_decode($member);
1338 if($member == $old_dn){
1339 $members[$key] = base64_encode($new_dn);
1340 $found = TRUE;
1341 }
1342 }
1344 /* Check if the selected role has to updated
1345 */
1346 if($acl_parts[1] == "role" && $acl_parts[2] == base64_encode($old_dn)){
1347 $acl_parts[2] = base64_encode($new_dn);
1348 $found = TRUE;
1349 }
1351 /* Build new acl string */
1352 $acl_parts[$id] = implode($members,",");
1353 $acls[] = implode($acl_parts,":");
1354 }
1356 /* Acls for this object must be adjusted */
1357 if($found){
1359 $debug_info= sprintf(_("Changing ACL DN from %s to %s"), bold($old_dn), bold($new_dn));
1360 @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,$debug_info,"ACL");
1362 $update[$attrs['dn']] =array();
1363 foreach($acls as $acl){
1364 $update[$attrs['dn']]['gosaAclEntry'][] = $acl;
1365 }
1366 }
1367 }
1369 /* Write updated acls */
1370 foreach($update as $dn => $attrs){
1371 $ldap->cd($dn);
1372 $ldap->modify($attrs);
1373 }
1374 }
1375 }
1379 /*! \brief Enable the Serial ID check
1380 *
1381 * This function enables the entry Serial ID check. If an entry was edited while
1382 * we have edited the entry too, an error message will be shown.
1383 * To configure this check correctly read the FAQ.
1384 */
1385 function enable_CSN_check()
1386 {
1387 $this->CSN_check_active =TRUE;
1388 $this->entryCSN = getEntryCSN($this->dn);
1389 }
1392 /*! \brief Prepares the plugin to be used for multiple edit
1393 * Update plugin attributes with given array of attribtues.
1394 * \param array Array with attributes that must be updated.
1395 */
1396 function init_multiple_support($attrs,$all)
1397 {
1398 $ldap= $this->config->get_ldap_link();
1399 $this->multi_attrs = $attrs;
1400 $this->multi_attrs_all= $all;
1402 /* Copy needed attributes */
1403 foreach ($this->attributes as $val){
1404 $found= array_key_ics($val, $this->multi_attrs);
1406 if ($found != ""){
1407 if(isset($this->multi_attrs["$val"][0])){
1408 $this->$val= $this->multi_attrs["$val"][0];
1409 }
1410 }
1411 }
1412 }
1415 /*! \brief Enables multiple support for this plugin
1416 */
1417 function enable_multiple_support()
1418 {
1419 $this->ignore_account = TRUE;
1420 $this->multiple_support_active = TRUE;
1421 }
1424 /*! \brief Returns all values that have been modfied in multiple edit mode.
1425 \return array Cotaining all modified values.
1426 */
1427 function get_multi_edit_values()
1428 {
1429 $ret = array();
1430 foreach($this->attributes as $attr){
1431 if(in_array($attr,$this->multi_boxes)){
1432 $ret[$attr] = $this->$attr;
1433 }
1434 }
1435 return($ret);
1436 }
1439 /*! \brief Update class variables with values collected by multiple edit.
1440 */
1441 function set_multi_edit_values($attrs)
1442 {
1443 foreach($attrs as $name => $value){
1444 $this->$name = $value;
1445 }
1446 }
1449 /*! \brief Generates the html output for this node for multi edit*/
1450 function multiple_execute()
1451 {
1452 /* This one is empty currently. Fabian - please fill in the docu code */
1453 session::global_set('current_class_for_help',get_class($this));
1455 /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
1456 session::set('LOCK_VARS_TO_USE',array());
1457 session::set('LOCK_VARS_USED_GET',array());
1458 session::set('LOCK_VARS_USED_POST',array());
1459 session::set('LOCK_VARS_USED_REQUEST',array());
1461 return("Multiple edit is currently not implemented for this plugin.");
1462 }
1465 /*! \brief Save HTML posted data to object for multiple edit
1466 */
1467 function multiple_save_object()
1468 {
1469 if(empty($this->entryCSN) && $this->CSN_check_active){
1470 $this->entryCSN = getEntryCSN($this->dn);
1471 }
1473 /* Save values to object */
1474 $this->multi_boxes = array();
1475 foreach ($this->attributes as $val){
1477 /* Get selected checkboxes from multiple edit */
1478 if(isset($_POST["use_".$val])){
1479 $this->multi_boxes[] = $val;
1480 }
1482 if (isset ($_POST["$val"]) && $this->acl_is_writeable($val)){
1484 $data= $this->$val = get_post($val);
1485 if ($this->$val != $data){
1486 $this->is_modified= TRUE;
1487 }
1489 /* IE post fix */
1490 if(isset($data[0]) && $data[0] == chr(194)) {
1491 $data = "";
1492 }
1493 $this->$val= $data;
1494 }
1495 }
1496 }
1499 /*! \brief Returns all attributes of this plugin,
1500 to be able to detect multiple used attributes
1501 in multi_plugg::detect_multiple_used_attributes().
1502 @return array Attributes required for intialization of multi_plug
1503 */
1504 public function get_multi_init_values()
1505 {
1506 $attrs = $this->attrs;
1507 return($attrs);
1508 }
1511 /*! \brief Check given values in multiple edit
1512 \return array Error messages
1513 */
1514 function multiple_check()
1515 {
1516 $message = plugin::check();
1517 return($message);
1518 }
1520 function get_used_snapshot_bases()
1521 {
1522 return(array());
1523 }
1525 function is_modal_dialog()
1526 {
1527 return(isset($this->dialog) && $this->dialog);
1528 }
1531 /*! \brief Forward command execution requests
1532 * to the hook execution method.
1533 */
1534 function handle_post_events($mode, $addAttrs= array())
1535 {
1536 if(!in_array($mode, array('add','remove','modify'))){
1537 trigger_error(sprintf("Invalid post event type given %s! Valid types are [add,modify,remove].", bold($mode)));
1538 return;
1539 }
1540 switch ($mode){
1541 case "add":
1542 plugin::callHook($this,"POSTCREATE", $addAttrs);
1543 break;
1545 case "modify":
1546 plugin::callHook($this,"POSTMODIFY", $addAttrs);
1547 break;
1549 case "remove":
1550 plugin::callHook($this,"POSTREMOVE", $addAttrs);
1551 break;
1552 }
1553 }
1556 /*! \brief Calls external hooks which are defined for this plugin (gosa.conf)
1557 * Replaces placeholder by class values of this plugin instance.
1558 * @param Allows to a add special replacements.
1559 */
1560 static function callHook($plugin, $cmd, $addAttrs= array(), &$returnOutput = array(), &$returnCode = NULL)
1561 {
1562 global $config;
1563 $command = $config->configRegistry->getPropertyValue(get_class($plugin),$cmd);
1565 if ($command != ""){
1567 // Walk trough attributes list and add the plugins attributes.
1568 foreach ($plugin->attributes as $attr){
1569 if (!is_array($plugin->$attr)){
1570 $addAttrs[$attr] = $plugin->$attr;
1571 }
1572 }
1573 $ui = get_userinfo();
1574 $addAttrs['callerDN']=$ui->dn;
1575 $addAttrs['dn']=$plugin->dn;
1576 $addAttrs['location']=$config->current['NAME'];
1578 // Sort attributes by length, ensures correct replacement
1579 $tmp = array();
1580 foreach($addAttrs as $name => $value){
1581 $tmp[$name] = strlen($name);
1582 }
1583 arsort($tmp);
1585 // Now replace the placeholder
1586 foreach ($tmp as $name => $len){
1587 $value = $addAttrs[$name];
1588 $command= str_replace("%$name", "$value", $command);
1589 }
1591 // If there are still some %.. in our command, try to fill these with some other class vars
1592 if(preg_match("/%/",$command)){
1593 $attrs = get_object_vars($plugin);
1594 foreach($attrs as $name => $value){
1595 if(is_array($value)){
1596 $s = "";
1597 foreach($value as $val){
1598 if(is_string($val) || is_int($val) || is_float($val) || is_bool($val)){
1599 $s .= '"'.$val.'",';
1600 }
1601 }
1602 $value = '['.trim($s,',').']';
1603 }
1604 if(!is_string($value) && !is_int($value) && !is_float($value) && !is_bool($value)){
1605 continue;
1606 }
1607 $command= preg_replace("/%$name/", $value, $command);
1608 }
1609 }
1611 if (check_command($command)){
1613 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command,"Execute");
1614 exec($command, $arr, $returnCode);
1615 $returnOutput = $arr;
1617 if($returnCode != 0){
1618 $str = implode("\n",$arr);
1619 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execution failed code: ".$returnCode);
1620 $message= msgPool::cmdexecfailed($cmd,$command, get_class($plugin));
1621 msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
1622 }elseif(is_array($arr)){
1623 $str = implode("\n",$arr);
1624 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$str);
1625 }
1626 } else {
1627 $message= msgPool::cmdinvalid($cmd,$command, get_class($plugin));
1628 msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
1629 }
1630 }
1631 }
1632 }
1634 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1635 ?>