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 {
149 $this->initTime = microtime(TRUE);
151 /* Configuration is fine, allways */
152 $this->config= &$config;
153 $this->dn= $dn;
155 // Ensure that we've a valid acl_category set.
156 if(empty($this->acl_category)){
157 $tmp = $this->plInfo();
158 if (isset($tmp['plCategory'])) {
159 $c = key($tmp['plCategory']);
160 if(is_numeric($c)){
161 $c = $tmp['plCategory'][0];
162 }
163 $this->acl_category = $c."/";
164 }
165 }
167 // Create statistic table entry
168 stats::log('plugin', $class = get_class($this), $category = array($this->acl_category), $action = 'open',
169 $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
171 /* Handle new accounts, don't read information from LDAP */
172 if ($dn == "new"){
173 return;
174 }
176 /* Check if this entry was opened in read only mode */
177 if(isset($_POST['open_readonly'])){
178 if(session::global_is_set("LOCK_CACHE")){
179 $cache = &session::get("LOCK_CACHE");
180 if(isset($cache['READ_ONLY'][$this->dn])){
181 $this->read_only = TRUE;
182 }
183 }
184 }
186 /* Save current dn as acl_base */
187 $this->acl_base= $dn;
189 /* Get LDAP descriptor */
190 if ($dn !== NULL){
192 /* Load data to 'attrs' and save 'dn' */
193 if ($object !== NULL){
194 $this->attrs= $object->attrs;
195 } else {
196 $ldap= $this->config->get_ldap_link();
197 $ldap->cat ($dn);
198 $this->attrs= $ldap->fetch();
199 }
201 /* Copy needed attributes */
202 foreach ($this->attributes as $val){
203 $found= array_key_ics($val, $this->attrs);
204 if ($found != ""){
205 $this->$val= $found[0];
206 }
207 }
209 /* gosaUnitTag loading... */
210 if (isset($this->attrs['gosaUnitTag'][0])){
211 $this->gosaUnitTag= $this->attrs['gosaUnitTag'][0];
212 }
214 /* Set the template flag according to the existence of objectClass
215 gosaUserTemplate */
216 if (isset($this->attrs['objectClass'])){
217 if (in_array_ics ("gosaUserTemplate", $this->attrs['objectClass'])){
218 $this->is_template= TRUE;
219 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
220 "found", "Template check");
221 }
222 }
224 /* Is Account? */
225 $found= TRUE;
226 foreach ($this->objectclasses as $obj){
227 if (preg_match('/top/i', $obj)){
228 continue;
229 }
230 if (!isset($this->attrs['objectClass']) || !in_array_ics ($obj, $this->attrs['objectClass'])){
231 $found= FALSE;
232 break;
233 }
234 }
235 if ($found){
236 $this->is_account= TRUE;
237 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__,
238 "found", "Object check");
239 }
241 /* Prepare saved attributes */
242 $this->saved_attributes= $this->attrs;
243 foreach ($this->saved_attributes as $index => $value){
244 if (is_numeric($index)){
245 unset($this->saved_attributes[$index]);
246 continue;
247 }
249 if (!in_array_ics($index, $this->attributes) && strcasecmp('objectClass', $index)){
250 unset($this->saved_attributes[$index]);
251 continue;
252 }
254 if (isset($this->saved_attributes[$index][0])){
255 if(!isset($this->saved_attributes[$index]["count"])){
256 $this->saved_attributes[$index]["count"] = count($this->saved_attributes[$index]);
257 }
258 if($this->saved_attributes[$index]["count"] == 1){
259 $tmp= $this->saved_attributes[$index][0];
260 unset($this->saved_attributes[$index]);
261 $this->saved_attributes[$index]= $tmp;
262 continue;
263 }
264 }
265 unset($this->saved_attributes["$index"]["count"]);
266 }
268 if(isset($this->attrs['gosaUnitTag'])){
269 $this->saved_attributes['gosaUnitTag'] = $this->attrs['gosaUnitTag'][0];
270 }
271 }
273 /* Save initial account state */
274 $this->initially_was_account= $this->is_account;
275 }
278 /*! \brief Generates the html output for this node
279 */
280 function execute()
281 {
282 /* This one is empty currently. Fabian - please fill in the docu code */
283 session::global_set('current_class_for_help',get_class($this));
285 /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
286 session::set('LOCK_VARS_TO_USE',array());
287 session::set('LOCK_VARS_USED_GET',array());
288 session::set('LOCK_VARS_USED_POST',array());
289 session::set('LOCK_VARS_USED_REQUEST',array());
291 pathNavigator::registerPlugin($this);
293 // Create statistic table entry
294 stats::log('plugin', $class = get_class($this), $category = array($this->acl_category), $action = 'view',
295 $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
296 }
298 /*! \brief Removes object from parent
299 */
300 function remove_from_parent()
301 {
302 /* include global link_info */
303 $ldap= $this->config->get_ldap_link();
305 /* Get current objectClasses in order to add the required ones */
306 $ldap->cat($this->dn);
307 $tmp= $ldap->fetch ();
308 $oc= array();
309 if (isset($tmp['objectClass'])){
310 $oc= $tmp['objectClass'];
311 unset($oc['count']);
312 }
314 /* Remove objectClasses from entry */
315 $ldap->cd($this->dn);
316 $this->attrs= array();
317 $this->attrs['objectClass']= array_remove_entries_ics($this->objectclasses,$oc);
319 /* Unset attributes from entry */
320 foreach ($this->attributes as $val){
321 $this->attrs["$val"]= array();
322 }
324 /* Unset account info */
325 $this->is_account= FALSE;
327 /* Do not write in plugin base class, this must be done by
328 children, since there are normally additional attribs,
329 lists, etc. */
330 /*
331 $ldap->modify($this->attrs);
332 */
333 if($this->initially_was_account){
334 $this->handle_pre_events('remove');
336 // Create statistic table entry
337 stats::log('plugin', $class = get_class($this), $category = array($this->acl_category), $action = 'remove',
338 $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
339 }
340 }
343 /*! \brief Save HTML posted data to object
344 */
345 function save_object()
346 {
347 /* Update entry CSN if it is empty. */
348 if(empty($this->entryCSN) && $this->CSN_check_active){
349 $this->entryCSN = getEntryCSN($this->dn);
350 }
352 /* Save values to object */
353 foreach ($this->attributes as $val){
354 if (isset ($_POST["$val"]) && $this->acl_is_writeable($val)){
356 /* Check for modifications */
357 $data= get_post($val);
358 if ($this->$val != $data){
359 $this->is_modified= TRUE;
360 }
361 $this->$val = $data;
363 /* Okay, how can I explain this fix ...
364 * In firefox, disabled option fields aren't selectable ... but in IE you can select these fileds.
365 * So IE posts these 'unselectable' option, with value = chr(194)
366 * chr(194) seems to be the in between the ...option> </option.. because there is no value=".." specified in these option fields
367 * This was added for W3c compliance, but now causes these ... ldap errors ...
368 * So we set these Fields to ""; a normal empty string, and we can check these values in plugin::check() again ...
369 */
370 if(isset($data[0]) && $data[0] == chr(194)) {
371 $data = "";
372 }
373 $this->$val= $data;
374 }
375 }
376 }
379 /*! \brief Save data to LDAP, depending on is_account we save or delete */
380 function save()
381 {
382 /* include global link_info */
383 $ldap= $this->config->get_ldap_link();
385 /* Save all plugins */
386 $this->entryCSN = "";
388 /* Start with empty array */
389 $this->attrs= array();
391 /* Get current objectClasses in order to add the required ones */
392 $ldap->cat($this->dn);
394 $tmp= $ldap->fetch ();
396 $oc= array();
397 if (isset($tmp['objectClass'])){
398 $oc= $tmp["objectClass"];
399 $this->is_new= FALSE;
400 unset($oc['count']);
401 } else {
402 $this->is_new= TRUE;
403 }
405 /* Load (minimum) attributes, add missing ones */
406 $this->attrs['objectClass']= gosa_array_merge($oc,$this->objectclasses);
408 /* Copy standard attributes */
409 foreach ($this->attributes as $val){
410 if ($this->$val != ""){
411 $this->attrs["$val"]= $this->$val;
412 } elseif (!$this->is_new) {
413 $this->attrs["$val"]= array();
414 }
415 }
417 /* Handle tagging */
418 $this->tag_attrs($this->attrs);
420 if($this->is_new){
421 $this->handle_pre_events('add');
423 // Create statistic table entry
424 stats::log('plugin', $class = get_class($this), $category = array($this->acl_category), $action = 'create',
425 $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
426 }else{
427 $this->handle_pre_events('modify');
429 // Create statistic table entry
430 stats::log('plugin', $class = get_class($this), $category = array($this->acl_category), $action = 'modify',
431 $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
432 }
433 }
436 /*! \brief Forward command execution requests
437 * to the hook execution method.
438 */
439 function handle_pre_events($mode, $addAttrs= array())
440 {
441 if(!in_array($mode, array('add','remove','modify'))){
442 trigger_error(sprintf("Invalid pre event type given %s! Valid types are [add,modify,remove].", $mode));
443 return;
444 }
445 switch ($mode){
446 case "add":
447 plugin::callHook($this,"PRECREATE", $addAttrs);
448 break;
450 case "modify":
451 plugin::callHook($this,"PREMODIFY", $addAttrs);
452 break;
454 case "remove":
455 plugin::callHook($this,"PREREMOVE", $addAttrs);
456 break;
457 }
458 }
461 function cleanup()
462 {
463 foreach ($this->attrs as $index => $value){
465 /* Convert arrays with one element to non arrays, if the saved
466 attributes are no array, too */
467 if (is_array($this->attrs[$index]) &&
468 count ($this->attrs[$index]) == 1 &&
469 isset($this->saved_attributes[$index]) &&
470 !is_array($this->saved_attributes[$index])){
472 $tmp= $this->attrs[$index][0];
473 $this->attrs[$index]= $tmp;
474 }
476 /* Remove emtpy arrays if they do not differ */
477 if (is_array($this->attrs[$index]) &&
478 count($this->attrs[$index]) == 0 &&
479 !isset($this->saved_attributes[$index])){
481 unset ($this->attrs[$index]);
482 continue;
483 }
485 /* Remove single attributes that do not differ */
486 if (!is_array($this->attrs[$index]) &&
487 isset($this->saved_attributes[$index]) &&
488 !is_array($this->saved_attributes[$index]) &&
489 $this->attrs[$index] == $this->saved_attributes[$index]){
491 unset ($this->attrs[$index]);
492 continue;
493 }
495 /* Remove arrays that do not differ */
496 if (is_array($this->attrs[$index]) &&
497 isset($this->saved_attributes[$index]) &&
498 is_array($this->saved_attributes[$index])){
500 if (!array_differs($this->attrs[$index],$this->saved_attributes[$index])){
501 unset ($this->attrs[$index]);
502 continue;
503 }
504 }
505 }
507 /* Update saved attributes and ensure that next cleanups will be successful too */
508 foreach($this->attrs as $name => $value){
509 $this->saved_attributes[$name] = $value;
510 }
511 }
513 /*! \brief Check formular input */
514 function check()
515 {
516 $message= array();
518 /* Skip if we've no config object */
519 if (!isset($this->config) || !is_object($this->config)){
520 return $message;
521 }
523 /* Find hooks entries for this class */
524 $command = $this->config->configRegistry->getPropertyValue(get_class($this),"check");
525 if ($command != ""){
527 if (!check_command($command)){
528 $message[]= msgPool::cmdnotfound("CHECK", get_class($this));
529 } else {
531 /* Generate "ldif" for check hook */
532 $ldif= "dn: $this->dn\n";
534 /* ... objectClasses */
535 foreach ($this->objectclasses as $oc){
536 $ldif.= "objectClass: $oc\n";
537 }
539 /* ... attributes */
540 foreach ($this->attributes as $attr){
541 if ($this->$attr == ""){
542 continue;
543 }
544 if (is_array($this->$attr)){
545 foreach ($this->$attr as $val){
546 $ldif.= "$attr: $val\n";
547 }
548 } else {
549 $ldif.= "$attr: ".$this->$attr."\n";
550 }
551 }
553 /* Append empty line */
554 $ldif.= "\n";
556 /* Feed "ldif" into hook and retrieve result*/
557 $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
558 $fh= proc_open($command, $descriptorspec, $pipes);
559 if (is_resource($fh)) {
560 fwrite ($pipes[0], $ldif);
561 fclose($pipes[0]);
563 $result= stream_get_contents($pipes[1]);
564 if ($result != ""){
565 $message[]= $result;
566 }
568 fclose($pipes[1]);
569 fclose($pipes[2]);
570 proc_close($fh);
571 }
572 }
574 }
576 /* Check entryCSN */
577 if($this->CSN_check_active){
578 $current_csn = getEntryCSN($this->dn);
579 if($current_csn != $this->entryCSN && !empty($this->entryCSN) && !empty($current_csn)){
580 $this->entryCSN = $current_csn;
581 $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!");
582 }
583 }
584 return ($message);
585 }
587 /* Adapt from template, using 'dn' */
588 function adapt_from_template($dn, $skip= array())
589 {
590 /* Include global link_info */
591 $ldap= $this->config->get_ldap_link();
593 /* Load requested 'dn' to 'attrs' */
594 $ldap->cat ($dn);
595 $this->attrs= $ldap->fetch();
597 /* Walk through attributes */
598 foreach ($this->attributes as $val){
600 /* Skip the ones in skip list */
601 if (in_array($val, $skip)){
602 continue;
603 }
605 if (isset($this->attrs["$val"][0])){
607 /* If attribute is set, replace dynamic parts:
608 %sn, %givenName and %uid. Fill these in our local variables. */
609 $value= $this->attrs["$val"][0];
611 foreach (array("sn", "givenName", "uid") as $repl){
612 if (preg_match("/%$repl/i", $value)){
613 $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
614 }
615 }
616 $this->$val= $value;
617 }
618 }
620 /* Is Account? */
621 $found= TRUE;
622 foreach ($this->objectclasses as $obj){
623 if (preg_match('/top/i', $obj)){
624 continue;
625 }
626 if (!in_array_ics ($obj, $this->attrs['objectClass'])){
627 $found= FALSE;
628 break;
629 }
630 }
631 if ($found){
632 $this->is_account= TRUE;
633 }
634 }
636 /* \brief Indicate whether a password change is needed or not */
637 function password_change_needed()
638 {
639 return FALSE;
640 }
643 /*! \brief Show header message for tab dialogs */
644 function show_enable_header($button_text, $text, $disabled= FALSE)
645 {
646 if (($disabled == TRUE) || (!$this->acl_is_createable())){
647 $state= "disabled";
648 } else {
649 $state= "";
650 }
651 $display = "<div class='plugin-enable-header'>\n";
652 $display.= "<p>$text</p>\n";
653 $display.= "<button type='submit' name=\"modify_state\" ".$state.">$button_text</button>\n";
654 $display.= "</div>\n";
656 return($display);
657 }
660 /*! \brief Show header message for tab dialogs */
661 function show_disable_header($button_text, $text, $disabled= FALSE)
662 {
663 if (($disabled == TRUE) || !$this->acl_is_removeable()){
664 $state= "disabled";
665 } else {
666 $state= "";
667 }
668 $display = "<div class='plugin-disable-header'>\n";
669 $display.= "<p>$text</p>\n";
670 $display.= "<button type='submit' name=\"modify_state\" ".$state.">$button_text</button>\n";
671 $display.= "</div>\n";
672 return($display);
673 }
677 /* Create unique DN */
678 function create_unique_dn2($data, $base)
679 {
680 $ldap= $this->config->get_ldap_link();
681 $base= preg_replace("/^,*/", "", $base);
683 /* Try to use plain entry first */
684 $dn= "$data,$base";
685 $attribute= preg_replace('/=.*$/', '', $data);
686 $ldap->cat ($dn, array('dn'));
687 if (!$ldap->fetch()){
688 return ($dn);
689 }
691 /* Look for additional attributes */
692 foreach ($this->attributes as $attr){
693 if ($attr == $attribute || $this->$attr == ""){
694 continue;
695 }
697 $dn= "$data+$attr=".$this->$attr.",$base";
698 $ldap->cat ($dn, array('dn'));
699 if (!$ldap->fetch()){
700 return ($dn);
701 }
702 }
704 /* None found */
705 return ("none");
706 }
709 /*! \brief Create unique DN */
710 function create_unique_dn($attribute, $base)
711 {
712 $ldap= $this->config->get_ldap_link();
713 $base= preg_replace("/^,*/", "", $base);
715 /* Try to use plain entry first */
716 $dn= "$attribute=".$this->$attribute.",$base";
717 $ldap->cat ($dn, array('dn'));
718 if (!$ldap->fetch()){
719 return ($dn);
720 }
722 /* Look for additional attributes */
723 foreach ($this->attributes as $attr){
724 if ($attr == $attribute || $this->$attr == ""){
725 continue;
726 }
728 $dn= "$attribute=".$this->$attribute."+$attr=".$this->$attr.",$base";
729 $ldap->cat ($dn, array('dn'));
730 if (!$ldap->fetch()){
731 return ($dn);
732 }
733 }
735 /* None found */
736 return ("none");
737 }
740 function rebind($ldap, $referral)
741 {
742 $credentials= LDAP::get_credentials($referral, $this->config->current['REFERRAL']);
743 if (ldap_bind($ldap, $credentials['ADMIN'], $this->config->get_credentials($credentials['PASSWORD']))) {
744 $this->error = "Success";
745 $this->hascon=true;
746 $this->reconnect= true;
747 return (0);
748 } else {
749 $this->error = "Could not bind to " . $credentials['ADMIN'];
750 return NULL;
751 }
752 }
755 /* Recursively copy ldap object */
756 function _copy($src_dn,$dst_dn)
757 {
758 $ldap=$this->config->get_ldap_link();
759 $ldap->cat($src_dn);
760 $attrs= $ldap->fetch();
762 /* Grummble. This really sucks. PHP ldap doesn't support rdn stuff. */
763 $ds= ldap_connect($this->config->current['SERVER']);
764 ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
765 if (function_exists("ldap_set_rebind_proc") && isset($this->config->current['REFERRAL'])) {
766 ldap_set_rebind_proc($ds, array(&$this, "rebind"));
767 }
769 $pwd = $this->config->get_credentials($this->config->current['ADMINPASSWORD']);
770 $r=ldap_bind($ds,$this->config->current['ADMINDN'], $pwd);
771 $sr=ldap_read($ds, LDAP::fix($src_dn), "objectClass=*");
773 /* Fill data from LDAP */
774 $new= array();
775 if ($sr) {
776 $ei=ldap_first_entry($ds, $sr);
777 if ($ei) {
778 foreach($attrs as $attr => $val){
779 if ($info = @ldap_get_values_len($ds, $ei, $attr)){
780 for ($i= 0; $i<$info['count']; $i++){
781 if ($info['count'] == 1){
782 $new[$attr]= $info[$i];
783 } else {
784 $new[$attr][]= $info[$i];
785 }
786 }
787 }
788 }
789 }
790 }
792 /* close conncetion */
793 ldap_unbind($ds);
795 /* Adapt naming attribute */
796 $dst_name= preg_replace("/^([^=]+)=.*$/", "\\1", $dst_dn);
797 $dst_val = preg_replace("/^[^=]+=([^,+]+).*,.*$/", "\\1", $dst_dn);
798 $new[$dst_name]= LDAP::fix($dst_val);
800 /* Check if this is a department.
801 * If it is a dep. && there is a , override in his ou
802 * change \2C to , again, else this entry can't be saved ...
803 */
804 if((isset($new['ou'])) &&( preg_match("/\\,/",$new['ou']))){
805 $new['ou'] = str_replace("\\\\,",",",$new['ou']);
806 }
808 /* Save copy */
809 $ldap->connect();
810 $ldap->cd($this->config->current['BASE']);
812 $ldap->create_missing_trees(preg_replace('/^[^,]+,/', '', $dst_dn));
814 /* FAIvariable=.../..., cn=..
815 could not be saved, because the attribute FAIvariable was different to
816 the dn FAIvariable=..., cn=... */
818 if(!is_array($new['objectClass'])) $new['objectClass'] = array($new['objectClass']);
820 if(in_array_ics("FAIdebconfInfo",$new['objectClass'])){
821 $new['FAIvariable'] = $ldap->fix($new['FAIvariable']);
822 }
823 $ldap->cd($dst_dn);
824 $ldap->add($new);
826 if (!$ldap->success()){
827 trigger_error("Trying to save $dst_dn failed.",
828 E_USER_WARNING);
829 return(FALSE);
830 }
831 return(TRUE);
832 }
835 /* This is a workaround function. */
836 function copy($src_dn, $dst_dn)
837 {
838 /* Rename dn in possible object groups */
839 $ldap= $this->config->get_ldap_link();
840 $ldap->search('(&(objectClass=gosaGroupOfNames)(member='.@LDAP::prepare4filter($src_dn).'))',
841 array('cn'));
842 while ($attrs= $ldap->fetch()){
843 $og= new ogroup($this->config, $ldap->getDN());
844 unset($og->member[$src_dn]);
845 $og->member[$dst_dn]= $dst_dn;
846 $og->save ();
847 }
849 $ldap->cat($dst_dn);
850 $attrs= $ldap->fetch();
851 if (count($attrs)){
852 trigger_error("Trying to overwrite ".LDAP::fix($dst_dn).", which already exists.",
853 E_USER_WARNING);
854 return (FALSE);
855 }
857 $ldap->cat($src_dn);
858 $attrs= $ldap->fetch();
859 if (!count($attrs)){
860 trigger_error("Trying to move ".LDAP::fix($src_dn).", which does not seem to exist.",
861 E_USER_WARNING);
862 return (FALSE);
863 }
865 $ldap->cd($src_dn);
866 $ldap->search("objectClass=*",array("dn"));
867 while($attrs = $ldap->fetch()){
868 $src = $attrs['dn'];
869 $dst = preg_replace("/".preg_quote($src_dn, '/')."$/",$dst_dn,$attrs['dn']);
870 $this->_copy($src,$dst);
871 }
872 return (TRUE);
873 }
877 /*! \brief Rename/Move a given src_dn to the given dest_dn
878 *
879 * Move a given ldap object indentified by $src_dn to the
880 * given destination $dst_dn
881 *
882 * - Ensure that all references are updated (ogroups)
883 * - Update ACLs
884 * - Update accessTo
885 *
886 * \param string 'src_dn' the source DN.
887 * \param string 'dst_dn' the destination DN.
888 * \return boolean TRUE on success else FALSE.
889 */
890 function rename($src_dn, $dst_dn)
891 {
892 $start = microtime(1);
894 /* Try to move the source entry to the destination position */
895 $ldap = $this->config->get_ldap_link();
896 $ldap->cd($this->config->current['BASE']);
897 $ldap->create_missing_trees(preg_replace("/^[^,]+,/","",$dst_dn));
898 if (!$ldap->rename_dn($src_dn,$dst_dn)){
899 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());
900 @DEBUG(DEBUG_LDAP,__LINE__,__FUNCTION__,__FILE__,"Rename failed FROM: $src_dn -- TO: $dst_dn",
901 "Ldap Protocol v3 implementation error, falling back to maunal method.");
902 return(FALSE);
903 }
905 /* Get list of users,groups and roles within this tree,
906 maybe we have to update ACL references.
907 */
908 $leaf_objs = get_list("(|(objectClass=posixGroup)(objectClass=gosaAccount)(objectClass=gosaRole))",array("all"),$dst_dn,
909 array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
910 foreach($leaf_objs as $obj){
911 $new_dn = $obj['dn'];
912 $old_dn = preg_replace("/".preg_quote(LDAP::convert($dst_dn), '/')."$/i",$src_dn,LDAP::convert($new_dn));
913 $this->update_acls($old_dn,$new_dn);
914 }
916 // Migrate objectgroups if needed
917 $ogroups = get_sub_list("(&(objectClass=gosaGroupOfNames)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))",
918 "ogroups", array(get_ou("group", "ogroupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
920 // Walk through all objectGroups
921 foreach($ogroups as $ogroup){
922 // Migrate old to new dn
923 $o_ogroup= new ogroup($this->config,$ogroup['dn']);
924 if (isset($o_ogroup->member[$src_dn])) {
925 unset($o_ogroup->member[$src_dn]);
926 }
927 $o_ogroup->member[$dst_dn]= $dst_dn;
929 // Save object group
930 $o_ogroup->save();
931 }
933 // Migrate objectgroups if needed
934 $objects = get_sub_list("(&(objectClass=gotoEnvironment)(gotoHotplugDeviceDN=".LDAP::prepare4filter(LDAP::fix($src_dn))."))",
935 "users",array(get_ou("core","userRDN"), get_ou("core","groupRDN")),
936 $this->config->current['BASE'],array("dn", "gotoHotplugDeviceDN"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
937 $ldap = $this->config->get_ldap_link();
938 foreach($objects as $obj){
939 $deviceDNS = array();
940 for($i=0; $i < $obj["gotoHotplugDeviceDN"]['count']; $i++){
941 $odn = $obj["gotoHotplugDeviceDN"][$i];
942 if($odn == $src_dn){
943 $odn = $dst_dn;
944 }
945 $deviceDNS[] = $odn;
946 }
947 $ldap->cd($obj['dn']);
948 $ldap->modify(array('gotoHotplugDeviceDN'=>$deviceDNS));
949 if(!$ldap->success()){
950 trigger_error(sprintf("Failed to update gotoHotplugDeviceDN for %s: %s", bold($obj['dn']), $ldap->get_error()));
951 }
952 }
954 // Migrate rfc groups if needed
955 $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);
957 // Walk through all POSIX groups
958 foreach($groups as $group){
960 // Migrate old to new dn
961 $o_group= new group($this->config,$group['dn']);
962 $o_group->save();
963 }
965 /* Update roles to use the new entry dn */
966 $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);
968 // Walk through all roles
969 foreach($roles as $role){
970 $role = new roleGeneric($this->config,$role['dn']);
971 $key= array_search($src_dn, $role->roleOccupant);
972 if($key !== FALSE){
973 $role->roleOccupant[$key] = $dst_dn;
974 $role->save();
975 }
976 }
978 // Update 'manager' attributes from gosaDepartment and inetOrgPerson
979 $filter = "(&(objectClass=inetOrgPerson)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn))."))";
980 $ocs = $ldap->get_objectclasses();
981 if(isset($ocs['gosaDepartment']['MAY']) && in_array('manager', $ocs['gosaDepartment']['MAY'])){
982 $filter = "(|".$filter."(&(objectClass=gosaDepartment)(manager=".LDAP::prepare4filter(LDAP::fix($src_dn)).")))";
983 }
984 $leaf_deps= get_list($filter,array("all"),$this->config->current['BASE'],
985 array("manager","dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
986 foreach($leaf_deps as $entry){
987 $update = array('manager' => $dst_dn);
988 $ldap->cd($entry['dn']);
989 $ldap->modify($update);
990 if(!$ldap->success()){
991 trigger_error(sprintf("Failed to update manager for %s: %s", bold($entry['dn']), $ldap->get_error()));
992 }
993 }
995 // Migrate 'dyn-groups' here. labeledURIObject
996 if(class_available('DynamicLdapGroup')) {
997 DynamicLdapGroup::moveDynGroup($this->config,$src_dn,$dst_dn);
998 }
1000 /* Check if there are gosa departments moved.
1001 If there were deps moved, the force reload of config->deps.
1002 */
1003 $leaf_deps= get_list("(objectClass=gosaDepartment)",array("all"),$dst_dn,
1004 array("dn","objectClass"),GL_SUBSEARCH | GL_NO_ACL_CHECK);
1006 if(count($leaf_deps)){
1007 $this->config->get_departments();
1008 $this->config->make_idepartments();
1009 session::global_set("config",$this->config);
1010 $ui =get_userinfo();
1011 $ui->reset_acl_cache();
1012 }
1014 return(TRUE);
1015 }
1019 function move($src_dn, $dst_dn)
1020 {
1021 /* Do not copy if only upper- lowercase has changed */
1022 if(strtolower($src_dn) == strtolower($dst_dn)){
1023 return(TRUE);
1024 }
1026 // Create statistic table entry
1027 stats::log('plugin', $class = get_class($this), $category = array($this->acl_category), $action = 'move',
1028 $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
1030 /* Try to move the entry instead of copy & delete
1031 */
1032 if(TRUE){
1034 /* Try to move with ldap routines, if this was not successfull
1035 fall back to the old style copy & remove method
1036 */
1037 if($this->rename($src_dn, $dst_dn)){
1038 return(TRUE);
1039 }else{
1040 // See code below.
1041 }
1042 }
1044 /* Copy source to destination */
1045 if (!$this->copy($src_dn, $dst_dn)){
1046 return (FALSE);
1047 }
1049 /* Delete source */
1050 $ldap= $this->config->get_ldap_link();
1051 $ldap->rmdir_recursive($src_dn);
1052 if (!$ldap->success()){
1053 trigger_error("Trying to delete $src_dn failed.",
1054 E_USER_WARNING);
1055 return (FALSE);
1056 }
1058 return (TRUE);
1059 }
1062 /* \brief Move/Rename complete trees */
1063 function recursive_move($src_dn, $dst_dn)
1064 {
1065 /* Check if the destination entry exists */
1066 $ldap= $this->config->get_ldap_link();
1068 /* Check if destination exists - abort */
1069 $ldap->cat($dst_dn, array('dn'));
1070 if ($ldap->fetch()){
1071 trigger_error("recursive_move $dst_dn already exists.",
1072 E_USER_WARNING);
1073 return (FALSE);
1074 }
1076 $this->copy($src_dn, $dst_dn);
1078 /* Remove src_dn */
1079 $ldap->cd($src_dn);
1080 $ldap->recursive_remove($src_dn);
1081 return (TRUE);
1082 }
1085 function saveCopyDialog(){
1086 }
1089 function getCopyDialog(){
1090 return(array("string"=>"","status"=>""));
1091 }
1094 /*! \brief Prepare for Copy & Paste */
1095 function PrepareForCopyPaste($source)
1096 {
1097 $todo = $this->attributes;
1098 if(isset($this->CopyPasteVars)){
1099 $todo = array_merge($todo,$this->CopyPasteVars);
1100 }
1102 if(count($this->objectclasses)){
1103 $this->is_account = TRUE;
1104 foreach($this->objectclasses as $class){
1105 if(!in_array($class,$source['objectClass'])){
1106 $this->is_account = FALSE;
1107 }
1108 }
1109 }
1111 foreach($todo as $var){
1112 if (isset($source[$var])){
1113 if(isset($source[$var]['count'])){
1114 if($source[$var]['count'] > 1){
1115 $tmp= $source[$var];
1116 unset($tmp['count']);
1117 $this->$var = $tmp;
1118 }else{
1119 $this->$var = $source[$var][0];
1120 }
1121 }else{
1122 $this->$var= $source[$var];
1123 }
1124 }
1125 }
1126 }
1128 /*! \brief Get gosaUnitTag for the given DN
1129 If this is called from departmentGeneric, we have to skip this
1130 tagging procedure.
1131 */
1132 function tag_attrs(&$at, $dn= "", $tag= "", $show= false)
1133 {
1134 /* Skip tagging? */
1135 if($this->skipTagging){
1136 return;
1137 }
1139 /* No dn? Self-operation... */
1140 if ($dn == ""){
1141 $dn= $this->dn;
1143 /* No tag? Find it yourself... */
1144 if ($tag == ""){
1145 $len= strlen($dn);
1147 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "No tag for $dn - looking for one...", "Tagging");
1148 $relevant= array();
1149 foreach ($this->config->adepartments as $key => $ntag){
1151 /* This one is bigger than our dn, its not relevant... */
1152 if ($len < strlen($key)){
1153 continue;
1154 }
1156 /* This one matches with the latter part. Break and don't fix this entry */
1157 if (preg_match('/(^|,)'.preg_quote($key, '/').'$/', $dn)){
1158 @DEBUG (DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, "DEBUG: Possibly relevant: $key", "Tagging");
1159 $relevant[strlen($key)]= $ntag;
1160 continue;
1161 }
1163 }
1165 /* If we've some relevant tags to set, just get the longest one */
1166 if (count($relevant)){
1167 ksort($relevant);
1168 $tmp= array_keys($relevant);
1169 $idx= end($tmp);
1170 $tag= $relevant[$idx];
1171 $this->gosaUnitTag= $tag;
1172 }
1173 }
1174 }
1176 /*! \brief Add unit tag */
1177 /* Remove tags that may already be here... */
1178 remove_objectClass("gosaAdministrativeUnitTag", $at);
1179 if (isset($at['gosaUnitTag'])){
1180 unset($at['gosaUnitTag']);
1181 }
1183 /* Set tag? */
1184 if ($tag != ""){
1185 add_objectClass("gosaAdministrativeUnitTag", $at);
1186 $at['gosaUnitTag']= $tag;
1187 }
1189 /* Initially this object was tagged.
1190 - But now, it is no longer inside a tagged department.
1191 So force the remove of the tag.
1192 (objectClass was already removed obove)
1193 */
1194 if($tag == "" && $this->gosaUnitTag){
1195 $at['gosaUnitTag'] = array();
1196 }
1197 }
1200 /*! \brief Test for removability of the object
1201 *
1202 * Allows testing of conditions for removal of object. If removal should be aborted
1203 * the function needs to remove an error message.
1204 * */
1205 function allow_remove()
1206 {
1207 $reason= "";
1208 return $reason;
1209 }
1212 /*! \brief Test if snapshotting is enabled
1213 *
1214 * Test weither snapshotting is enabled or not. There will also be some errors posted,
1215 * if the configuration failed
1216 * \return TRUE if snapshots are enabled, and FALSE if it is disabled
1217 */
1218 function snapshotEnabled()
1219 {
1220 return $this->config->snapshotEnabled();
1221 }
1224 /*! \brief Return plugin informations for acl handling
1225 * See class_core.inc for examples.
1226 */
1227 static function plInfo()
1228 {
1229 return array();
1230 }
1233 function set_acl_base($base)
1234 {
1235 @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,"<b>".$base."</b>","<b>ACL-Base:</b> ");
1236 $this->acl_base= $base;
1237 }
1240 function set_acl_category($category)
1241 {
1242 @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,"<b>".$category."</b>(/".get_class($this).")","<b>ACL-Category:</b> ");
1243 $this->acl_category= "$category/";
1244 }
1247 function acl_is_writeable($attribute,$skip_write = FALSE)
1248 {
1249 if($this->read_only) return(FALSE);
1250 $ui= get_userinfo();
1251 return preg_match('/w/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute, $skip_write));
1252 }
1255 function acl_is_readable($attribute)
1256 {
1257 $ui= get_userinfo();
1258 return preg_match('/r/', $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute));
1259 }
1262 function acl_is_createable($base ="")
1263 {
1264 if($this->read_only) return(FALSE);
1265 $ui= get_userinfo();
1266 if($base == "") $base = $this->acl_base;
1267 return preg_match('/c/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1268 }
1271 function acl_is_removeable($base ="")
1272 {
1273 if($this->read_only) return(FALSE);
1274 $ui= get_userinfo();
1275 if($base == "") $base = $this->acl_base;
1276 return preg_match('/d/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1277 }
1280 function acl_is_moveable($base = "")
1281 {
1282 if($this->read_only) return(FALSE);
1283 $ui= get_userinfo();
1284 if($base == "") $base = $this->acl_base;
1285 return preg_match('/m/', $ui->get_permissions($base, $this->acl_category.get_class($this), '0'));
1286 }
1289 function acl_have_any_permissions()
1290 {
1291 }
1294 function getacl($attribute,$skip_write= FALSE)
1295 {
1296 $ui= get_userinfo();
1297 $skip_write |= $this->read_only;
1298 return $ui->get_permissions($this->acl_base, $this->acl_category.get_class($this), $attribute,$skip_write);
1299 }
1302 /*! \brief Returns a list of all available departments for this object.
1303 *
1304 * If this object is new, all departments we are allowed to create a new user in
1305 * are returned. If this is an existing object, return all deps.
1306 * We are allowed to move tis object too.
1307 * \return array [dn] => "..name" // All deps. we are allowed to act on.
1308 */
1309 function get_allowed_bases()
1310 {
1311 $ui = get_userinfo();
1312 $deps = array();
1314 /* Is this a new object ? Or just an edited existing object */
1315 if(!$this->initially_was_account && $this->is_account){
1316 $new = true;
1317 }else{
1318 $new = false;
1319 }
1321 foreach($this->config->idepartments as $dn => $name){
1322 if($new && $this->acl_is_createable($dn)){
1323 $deps[$dn] = $name;
1324 }elseif(!$new && $this->acl_is_moveable($dn)){
1325 $deps[$dn] = $name;
1326 }
1327 }
1329 /* Add current base */
1330 if(isset($this->base) && isset($this->config->idepartments[$this->base])){
1331 $deps[$this->base] = $this->config->idepartments[$this->base];
1332 }elseif(strtolower($this->dn) == strtolower($this->config->current['BASE'])){
1334 }else{
1335 trigger_error("Cannot return list of departments, no default base found in class ".get_class($this).". ".$this->base);
1336 }
1337 return($deps);
1338 }
1341 /* This function updates ACL settings if $old_dn was used.
1342 * \param string 'old_dn' specifies the actually used dn
1343 * \param string 'new_dn' specifies the destiantion dn
1344 */
1345 function update_acls($old_dn,$new_dn,$output_changes = FALSE)
1346 {
1347 /* Check if old_dn is empty. This should never happen */
1348 if(empty($old_dn) || empty($new_dn)){
1349 trigger_error("Failed to check acl dependencies, wrong dn given.");
1350 return;
1351 }
1353 /* Update userinfo if necessary */
1354 $ui = session::global_get('ui');
1355 if($ui->dn == $old_dn){
1356 $ui->dn = $new_dn;
1357 $ui->loadACL();
1358 session::global_set('ui',$ui);
1359 new log("view","acl/".get_class($this),$this->dn,array(),"Updated current object dn from '".$old_dn."' to '".$new_dn."'");
1360 }
1362 /* Object was moved, ensure that all acls will be moved too */
1363 if($new_dn != $old_dn && $old_dn != "new"){
1365 /* get_ldap configuration */
1366 $update = array();
1367 $ldap = $this->config->get_ldap_link();
1368 $ldap->cd ($this->config->current['BASE']);
1369 $ldap->search("(&(objectClass=gosaAcl)(gosaAclEntry=*".base64_encode($old_dn)."*))",array("cn","gosaAclEntry"));
1370 while($attrs = $ldap->fetch()){
1371 $acls = array();
1372 $found = false;
1373 for($i = 0 ; $i < $attrs['gosaAclEntry']['count'] ; $i ++ ){
1374 $acl_parts = explode(":",$attrs['gosaAclEntry'][$i]);
1376 /* Roles uses antoher data storage order, members are stored int the third part,
1377 while the members in direct ACL assignments are stored in the second part.
1378 */
1379 $id = ($acl_parts[1] == "role") ? 3 : 2;
1381 /* Update member entries to use $new_dn instead of old_dn
1382 */
1383 $members = explode(",",$acl_parts[$id]);
1384 foreach($members as $key => $member){
1385 $member = base64_decode($member);
1386 if($member == $old_dn){
1387 $members[$key] = base64_encode($new_dn);
1388 $found = TRUE;
1389 }
1390 }
1392 /* Check if the selected role has to updated
1393 */
1394 if($acl_parts[1] == "role" && $acl_parts[2] == base64_encode($old_dn)){
1395 $acl_parts[2] = base64_encode($new_dn);
1396 $found = TRUE;
1397 }
1399 /* Build new acl string */
1400 $acl_parts[$id] = implode($members,",");
1401 $acls[] = implode($acl_parts,":");
1402 }
1404 /* Acls for this object must be adjusted */
1405 if($found){
1407 $debug_info= sprintf(_("Changing ACL DN from %s to %s"), bold($old_dn), bold($new_dn));
1408 @DEBUG (DEBUG_ACL, __LINE__, __FUNCTION__, __FILE__,$debug_info,"ACL");
1410 $update[$attrs['dn']] =array();
1411 foreach($acls as $acl){
1412 $update[$attrs['dn']]['gosaAclEntry'][] = $acl;
1413 }
1414 }
1415 }
1417 /* Write updated acls */
1418 foreach($update as $dn => $attrs){
1419 $ldap->cd($dn);
1420 $ldap->modify($attrs);
1421 }
1422 }
1423 }
1427 /*! \brief Enable the Serial ID check
1428 *
1429 * This function enables the entry Serial ID check. If an entry was edited while
1430 * we have edited the entry too, an error message will be shown.
1431 * To configure this check correctly read the FAQ.
1432 */
1433 function enable_CSN_check()
1434 {
1435 $this->CSN_check_active =TRUE;
1436 $this->entryCSN = getEntryCSN($this->dn);
1437 }
1440 /*! \brief Prepares the plugin to be used for multiple edit
1441 * Update plugin attributes with given array of attribtues.
1442 * \param array Array with attributes that must be updated.
1443 */
1444 function init_multiple_support($attrs,$all)
1445 {
1446 $ldap= $this->config->get_ldap_link();
1447 $this->multi_attrs = $attrs;
1448 $this->multi_attrs_all= $all;
1450 /* Copy needed attributes */
1451 foreach ($this->attributes as $val){
1452 $found= array_key_ics($val, $this->multi_attrs);
1454 if ($found != ""){
1455 if(isset($this->multi_attrs["$val"][0])){
1456 $this->$val= $this->multi_attrs["$val"][0];
1457 }
1458 }
1459 }
1460 }
1463 /*! \brief Enables multiple support for this plugin
1464 */
1465 function enable_multiple_support()
1466 {
1467 $this->ignore_account = TRUE;
1468 $this->multiple_support_active = TRUE;
1469 }
1472 /*! \brief Returns all values that have been modfied in multiple edit mode.
1473 \return array Cotaining all modified values.
1474 */
1475 function get_multi_edit_values()
1476 {
1477 $ret = array();
1478 foreach($this->attributes as $attr){
1479 if(in_array($attr,$this->multi_boxes)){
1480 $ret[$attr] = $this->$attr;
1481 }
1482 }
1483 return($ret);
1484 }
1487 /*! \brief Update class variables with values collected by multiple edit.
1488 */
1489 function set_multi_edit_values($attrs)
1490 {
1491 foreach($attrs as $name => $value){
1492 $this->$name = $value;
1493 }
1494 }
1497 /*! \brief Generates the html output for this node for multi edit*/
1498 function multiple_execute()
1499 {
1500 /* This one is empty currently. Fabian - please fill in the docu code */
1501 session::global_set('current_class_for_help',get_class($this));
1503 /* Reset Lock message POST/GET check array, to prevent perg_match errors*/
1504 session::set('LOCK_VARS_TO_USE',array());
1505 session::set('LOCK_VARS_USED_GET',array());
1506 session::set('LOCK_VARS_USED_POST',array());
1507 session::set('LOCK_VARS_USED_REQUEST',array());
1509 return("Multiple edit is currently not implemented for this plugin.");
1510 }
1513 /*! \brief Save HTML posted data to object for multiple edit
1514 */
1515 function multiple_save_object()
1516 {
1517 if(empty($this->entryCSN) && $this->CSN_check_active){
1518 $this->entryCSN = getEntryCSN($this->dn);
1519 }
1521 /* Save values to object */
1522 $this->multi_boxes = array();
1523 foreach ($this->attributes as $val){
1525 /* Get selected checkboxes from multiple edit */
1526 if(isset($_POST["use_".$val])){
1527 $this->multi_boxes[] = $val;
1528 }
1530 if (isset ($_POST["$val"]) && $this->acl_is_writeable($val)){
1532 $data= $this->$val = get_post($val);
1533 if ($this->$val != $data){
1534 $this->is_modified= TRUE;
1535 }
1537 /* IE post fix */
1538 if(isset($data[0]) && $data[0] == chr(194)) {
1539 $data = "";
1540 }
1541 $this->$val= $data;
1542 }
1543 }
1544 }
1547 /*! \brief Returns all attributes of this plugin,
1548 to be able to detect multiple used attributes
1549 in multi_plugg::detect_multiple_used_attributes().
1550 @return array Attributes required for intialization of multi_plug
1551 */
1552 public function get_multi_init_values()
1553 {
1554 $attrs = $this->attrs;
1555 return($attrs);
1556 }
1559 /*! \brief Check given values in multiple edit
1560 \return array Error messages
1561 */
1562 function multiple_check()
1563 {
1564 $message = plugin::check();
1565 return($message);
1566 }
1568 function get_used_snapshot_bases()
1569 {
1570 return(array());
1571 }
1573 function is_modal_dialog()
1574 {
1575 return(isset($this->dialog) && $this->dialog);
1576 }
1579 /*! \brief Forward command execution requests
1580 * to the hook execution method.
1581 */
1582 function handle_post_events($mode, $addAttrs= array())
1583 {
1584 if(!in_array($mode, array('add','remove','modify'))){
1585 trigger_error(sprintf("Invalid post event type given %s! Valid types are [add,modify,remove].", bold($mode)));
1586 return;
1587 }
1588 switch ($mode){
1589 case "add":
1590 plugin::callHook($this,"POSTCREATE", $addAttrs);
1591 break;
1593 case "modify":
1594 plugin::callHook($this,"POSTMODIFY", $addAttrs);
1595 break;
1597 case "remove":
1598 plugin::callHook($this,"POSTREMOVE", $addAttrs);
1599 break;
1600 }
1601 }
1604 /*! \brief Calls external hooks which are defined for this plugin (gosa.conf)
1605 * Replaces placeholder by class values of this plugin instance.
1606 * @param Allows to a add special replacements.
1607 */
1608 static function callHook($plugin, $cmd, $addAttrs= array(), &$returnOutput = array(),
1609 &$returnCode = NULL, &$errorOutput = array(), $displayErrors = TRUE)
1610 {
1611 global $config;
1612 $command = $config->configRegistry->getPropertyValue(get_class($plugin),$cmd);
1614 $returnCode = 0; // Simulate a return code to tell the caller that everythin is fine.
1615 $returnOutput = array();
1616 $arr = array();
1618 if (!empty($command)){
1620 // Walk trough attributes list and add the plugins attributes.
1621 foreach ($plugin->attributes as $attr){
1622 if (!is_array($plugin->$attr)){
1623 $addAttrs[$attr] = $plugin->$attr;
1624 }
1625 }
1626 $ui = get_userinfo();
1627 $addAttrs['callerDN']=$ui->dn;
1628 $addAttrs['dn']=$plugin->dn;
1629 $addAttrs['location']=$config->current['NAME'];
1631 // Sort attributes by length, ensures correct replacement
1632 $tmp = array();
1633 foreach($addAttrs as $name => $value){
1634 $tmp[$name] = strlen($name);
1635 }
1636 arsort($tmp);
1638 // Now replace the placeholder
1639 $command = fillReplacements($command, $addAttrs, TRUE);
1641 // If there are still some %.. in our command, try to fill these with some other class vars
1642 if(preg_match("/%/",$command)){
1643 $attrs = get_object_vars($plugin);
1644 foreach($attrs as $name => $value){
1645 if(is_array($value)){
1646 $s = "";
1647 foreach($value as $val){
1648 if(is_string($val) || is_int($val) || is_float($val) || is_bool($val)){
1649 $s .= '"'.$val.'",';
1650 }
1651 }
1652 $value = '['.trim($s,',').']';
1653 }
1654 if(!is_string($value) && !is_int($value) && !is_float($value) && !is_bool($value)){
1655 continue;
1656 }
1657 $command= preg_replace("/%$name/", escapeshellarg($value), $command);
1658 }
1659 }
1661 if (check_command($command)){
1663 // Create list of process pipes
1664 $descriptorspec = array(
1665 0 => array("pipe", "r"), // stdin
1666 1 => array("pipe", "w"), // stdout
1667 2 => array("pipe", "w")); // stderr
1669 // Try to open the process
1670 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command,"Execute");
1671 $process = proc_open($command, $descriptorspec, $pipes);
1672 if (is_resource($process)) {
1674 // Write the password to stdin
1675 // fwrite($pipes[0], $pwd);
1676 fclose($pipes[0]);
1678 // Get results from stdout and stderr
1679 $arr = stream_get_contents($pipes[1]);
1680 $err = stream_get_contents($pipes[2]);
1681 fclose($pipes[1]);
1683 // Close the process and check its return value
1684 $returnCode = proc_close($process);
1685 $returnOutput = preg_split("/\n/", $arr,0,PREG_SPLIT_NO_EMPTY);
1686 $errorOutput = preg_split("/\n/",$err,0,PREG_SPLIT_NO_EMPTY);
1687 }
1689 if($returnCode != 0){
1690 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execution failed code: ".$returnCode);
1691 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$err);
1692 if($displayErrors){
1693 $message= msgPool::cmdexecfailed($cmd,$command, get_class($plugin));
1694 msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
1695 }
1696 }elseif(is_array($arr)){
1697 @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$arr);
1698 }
1699 } elseif($displayErrors) {
1700 $message= msgPool::cmdinvalid($cmd,$command, get_class($plugin));
1701 msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
1702 }
1703 }
1704 }
1705 }
1707 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
1708 ?>