Code

Updated Placeholder handling, fixed problems with UPPER/lowercase handling
[gosa.git] / gosa-core / include / class_plugin.inc
index 33f750a351abde4216839399a5784ca2d3519d83..0180a8529a50fc25e108417911f1fecfdd84d9ad 100644 (file)
@@ -145,6 +145,9 @@ class plugin
    */
   function plugin (&$config, $dn= NULL, $object= NULL)
   {
+
+    $this->initTime = microtime(TRUE);
+
     /* Configuration is fine, allways */
     $this->config= &$config;   
     $this->dn= $dn;
@@ -161,6 +164,10 @@ class plugin
       }
     }
 
+    // Create statistic table entry 
+    stats::log('plugin', $class = get_class($this), $category = array($this->acl_category),  $action = 'open', 
+        $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
+
     /* Handle new accounts, don't read information from LDAP */
     if ($dn == "new"){
       return;
@@ -282,6 +289,10 @@ class plugin
     session::set('LOCK_VARS_USED_REQUEST',array());
 
     pathNavigator::registerPlugin($this);
+
+    // Create statistic table entry 
+    stats::log('plugin', $class = get_class($this), $category = array($this->acl_category),  $action = 'view', 
+        $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
   }
 
   /*! \brief Removes object from parent
@@ -319,6 +330,13 @@ class plugin
     /*
        $ldap->modify($this->attrs);
      */
+    if($this->initially_was_account){
+        $this->handle_pre_events('remove');
+
+        // Create statistic table entry 
+        stats::log('plugin', $class = get_class($this), $category = array($this->acl_category),  $action = 'remove', 
+                $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
+    }
   }
 
 
@@ -334,15 +352,13 @@ class plugin
     /* Save values to object */
     foreach ($this->attributes as $val){
       if (isset ($_POST["$val"]) && $this->acl_is_writeable($val)){
+  
         /* Check for modifications */
-        if (get_magic_quotes_gpc()) {
-          $data= stripcslashes($_POST["$val"]);
-        } else {
-          $data= $this->$val = $_POST["$val"];
-        }
+        $data= get_post($val);
         if ($this->$val != $data){
           $this->is_modified= TRUE;
         }
+        $this->$val = $data;
     
         /* Okay, how can I explain this fix ... 
          * In firefox, disabled option fields aren't selectable ... but in IE you can select these fileds. 
@@ -400,6 +416,45 @@ class plugin
 
     /* Handle tagging */
     $this->tag_attrs($this->attrs);
+
+    if($this->is_new){
+        $this->handle_pre_events('add');
+
+        // Create statistic table entry 
+        stats::log('plugin', $class = get_class($this), $category = array($this->acl_category),  $action = 'create', 
+                $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
+    }else{
+        $this->handle_pre_events('modify');
+
+        // Create statistic table entry 
+        stats::log('plugin', $class = get_class($this), $category = array($this->acl_category),  $action = 'modify', 
+                $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
+    }
+  }
+
+
+  /*! \brief    Forward command execution requests
+   *             to the hook execution method.
+   */
+  function handle_pre_events($mode, $addAttrs= array())
+  {
+    if(!in_array($mode, array('add','remove','modify'))){
+      trigger_error(sprintf("Invalid pre event type given %s! Valid types are [add,modify,remove].", $mode));
+      return;
+    }
+    switch ($mode){
+      case "add":
+        plugin::callHook($this,"PRECREATE", $addAttrs);
+      break;
+
+      case "modify":
+        plugin::callHook($this,"PREMODIFY", $addAttrs);
+      break;
+
+      case "remove":
+        plugin::callHook($this,"PREREMOVE", $addAttrs);
+      break;
+    }
   }
 
 
@@ -466,7 +521,7 @@ class plugin
     }
 
     /* Find hooks entries for this class */
-    $command = $config->configRegistry->getPropertyValue(get_class($plugin),"check");
+    $command = $this->config->configRegistry->getPropertyValue(get_class($this),"check");
     if ($command != ""){
 
       if (!check_command($command)){
@@ -539,43 +594,45 @@ class plugin
     $ldap->cat ($dn);
     $this->attrs= $ldap->fetch();
 
-    /* Walk through attributes */
-    foreach ($this->attributes as $val){
+    $values = array();
+    foreach(array('uid','sn','givenName') as $name){
+        if(isset($this->parent->$name)){
+            $value = $this->parent->$name;
+            if(is_numeric($name)) continue;
+            if(is_string($value))  $values[$name] = $value;
+            if(is_array($value) && isset($value[0]))  $values[$name] = $value[0];
+        }
+    }
 
-      /* Skip the ones in skip list */
-      if (in_array($val, $skip)){
-        continue;
-      }
+    foreach($this->attributes as $name){
 
-      if (isset($this->attrs["$val"][0])){
+        // Skip the ones in skip list 
+        if (in_array($name, $skip)) continue;
+        if (!isset($this->attrs[$name]['count'])) continue;
 
-        /* If attribute is set, replace dynamic parts: 
-           %sn, %givenName and %uid. Fill these in our local variables. */
-        $value= $this->attrs["$val"][0];
+        $value= $this->attrs[$name][0];
 
-        foreach (array("sn", "givenName", "uid") as $repl){
-          if (preg_match("/%$repl/i", $value)){
-            $value= preg_replace ("/%$repl/i", $this->parent->$repl, $value);
-          }
+        if($this->attrs[$name]['count'] == 1){
+            $value = fillReplacements($this->attrs[$name][0], $values);
+        }else{
+            $value = array();
+            for($i=0;$i<$this->attrs[$name]['count'];$i++){
+                $value[] = fillReplacements($this->attrs[$name][$i], $values);
+            }
         }
-        $this->$val= $value;
-      }
+        $this->$name = $value; 
     }
 
     /* Is Account? */
     $found= TRUE;
     foreach ($this->objectclasses as $obj){
-      if (preg_match('/top/i', $obj)){
-        continue;
-      }
+      if (preg_match('/top/i', $obj)) continue;
       if (!in_array_ics ($obj, $this->attrs['objectClass'])){
         $found= FALSE;
         break;
       }
     }
-    if ($found){
-      $this->is_account= TRUE;
-    }
+    $this->is_account = $found;
   }
 
   /* \brief Indicate whether a password change is needed or not */
@@ -860,7 +917,7 @@ class plugin
 
     // Migrate objectgroups if needed
     $ogroups = get_sub_list("(&(objectClass=gosaGroupOfNames)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))",
-      "ogroups", array(get_ou("ogroupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
+      "ogroups", array(get_ou("group", "ogroupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
 
     // Walk through all objectGroups
     foreach($ogroups as $ogroup){
@@ -875,8 +932,29 @@ class plugin
       $o_ogroup->save();
     }
 
+    // Migrate objectgroups if needed
+    $objects = get_sub_list("(&(objectClass=gotoEnvironment)(gotoHotplugDeviceDN=".LDAP::prepare4filter(LDAP::fix($src_dn))."))",
+            "users",array(get_ou("core","userRDN"), get_ou("core","groupRDN")),
+            $this->config->current['BASE'],array("dn", "gotoHotplugDeviceDN"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
+    $ldap = $this->config->get_ldap_link();
+    foreach($objects as $obj){
+        $deviceDNS = array();
+        for($i=0; $i < $obj["gotoHotplugDeviceDN"]['count']; $i++){
+            $odn = $obj["gotoHotplugDeviceDN"][$i];
+            if($odn == $src_dn){
+                $odn = $dst_dn;
+            }
+            $deviceDNS[] = $odn;
+        }
+        $ldap->cd($obj['dn']);
+        $ldap->modify(array('gotoHotplugDeviceDN'=>$deviceDNS));
+        if(!$ldap->success()){
+            trigger_error(sprintf("Failed to update gotoHotplugDeviceDN for %s: %s", bold($obj['dn']), $ldap->get_error()));
+        }
+    }
+
     // Migrate rfc groups if needed
-    $groups = get_sub_list("(&(objectClass=posixGroup)(member=".LDAP::prepare4filter(LDAP::fix($src_dn))."))","groups", array(get_ou("groupRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
+    $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);
 
     // Walk through all POSIX groups
     foreach($groups as $group){
@@ -887,16 +965,18 @@ class plugin
     }
 
     /* Update roles to use the new entry dn */
-    $roles = get_sub_list("(&(objectClass=organizationalRole)(roleOccupant=".LDAP::prepare4filter(LDAP::fix($src_dn))."))","roles", array(get_ou("roleRDN")),$this->config->current['BASE'],array("dn"), GL_SUBSEARCH | GL_NO_ACL_CHECK);
-
-    // Walk through all roles
-    foreach($roles as $role){
-      $role = new roleGeneric($this->config,$role['dn']);
-      $key= array_search($src_dn, $role->roleOccupant);      
-      if($key !== FALSE){
-        $role->roleOccupant[$key] = $dst_dn;
-        $role->save();
-      }
+    if(class_available('roleGeneric')){
+        $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);
+
+        // Walk through all roles
+        foreach($roles as $role){
+            $role = new roleGeneric($this->config,$role['dn']);
+            $key= array_search($src_dn, $role->roleOccupant);      
+            if($key !== FALSE){
+                $role->roleOccupant[$key] = $dst_dn;
+                $role->save();
+            }
+        }
     }
 
     // Update 'manager' attributes from gosaDepartment and inetOrgPerson 
@@ -915,6 +995,11 @@ class plugin
         trigger_error(sprintf("Failed to update manager for %s: %s", bold($entry['dn']), $ldap->get_error()));
       }
     }
+
+    // Migrate 'dyn-groups' here. labeledURIObject
+    if(class_available('DynamicLdapGroup')) {
+        DynamicLdapGroup::moveDynGroup($this->config,$src_dn,$dst_dn);
+    }
  
     /* Check if there are gosa departments moved. 
        If there were deps moved, the force reload of config->deps.
@@ -942,7 +1027,10 @@ class plugin
       return(TRUE);
     }
 
-    
+    // Create statistic table entry 
+    stats::log('plugin', $class = get_class($this), $category = array($this->acl_category),  $action = 'move', 
+            $amount = 1, $duration = (microtime(TRUE) - $this->initTime));
+
     /* Try to move the entry instead of copy & delete
      */
     if(TRUE){
@@ -1137,7 +1225,9 @@ class plugin
   }
 
 
-  /*! \brief Return plugin informations for acl handling */
+  /*! \brief Return plugin informations for acl handling 
+   *         See class_core.inc for examples.
+   */
   static function plInfo()
   {
     return array();
@@ -1268,6 +1358,7 @@ class plugin
     $ui = session::global_get('ui');
     if($ui->dn == $old_dn){
       $ui->dn = $new_dn;
+      $ui->loadACL();
       session::global_set('ui',$ui);
       new log("view","acl/".get_class($this),$this->dn,array(),"Updated current object dn from '".$old_dn."' to '".$new_dn."'");
     }
@@ -1442,12 +1533,7 @@ class plugin
 
       if (isset ($_POST["$val"]) && $this->acl_is_writeable($val)){
 
-        /* Check for modifications */
-        if (get_magic_quotes_gpc()) {
-          $data= stripcslashes($_POST["$val"]);
-        } else {
-          $data= $this->$val = $_POST["$val"];
-        }
+        $data= $this->$val = get_post($val);
         if ($this->$val != $data){
           $this->is_modified= TRUE;
         }
@@ -1523,68 +1609,102 @@ class plugin
    *            Replaces placeholder by class values of this plugin instance.
    *  @param    Allows to a add special replacements.
    */
-  static function callHook($plugin, $cmd, $addAttrs= array())
+  static function callHook($plugin, $cmd, $addAttrs= array(), &$returnOutput = array(), 
+          &$returnCode = NULL, &$errorOutput = array(), $displayErrors = TRUE)
   {
-    global $config;
-    $command = $config->configRegistry->getPropertyValue(get_class($plugin),$cmd);
-    
-    if ($command != ""){
+      global $config;
+      $command = $config->configRegistry->getPropertyValue(get_class($plugin),$cmd);
 
-      // Walk trough attributes list and add the plugins attributes. 
-      foreach ($plugin->attributes as $attr){
-        if (!is_array($plugin->$attr)){
-          $addAttrs[$attr] = $plugin->$attr;
-        }
-      }
-      $ui = get_userinfo();
-      $addAttrs['callerDN']=$ui->dn;
-      $addAttrs['dn']=$plugin->dn;
-
-      // Sort attributes by length, ensures correct replacement
-      $tmp = array();
-      foreach($addAttrs as $name => $value){
-        $tmp[$name] =  strlen($name);
-      }
-      arsort($tmp);
+      $returnCode = 0; // Simulate a return code to tell the caller that everythin is fine.
+      $returnOutput = array();
+      $arr = array();
 
-      // Now replace the placeholder 
-      foreach ($tmp as $name => $len){
-        $value = $addAttrs[$name];
-        $command= str_replace("%$name", "$value", $command);
-      }
+      if (!empty($command)){
 
-      // If there are still some %.. in our command, try to fill these with some other class vars 
-      if(preg_match("/%/",$command)){
-        $attrs = get_object_vars($plugin);
-        foreach($attrs as $name => $value){
-          if(is_array($value)){
-            $s = "";
-            foreach($value as $val){
-              if(is_string($val) || is_int($val) || is_float($val) || is_bool($val)){
-                $s .= '"'.$val.'",'; 
+          // Walk trough attributes list and add the plugins attributes. 
+          foreach ($plugin->attributes as $attr){
+              if (!is_array($plugin->$attr)){
+                  $addAttrs[$attr] = $plugin->$attr;
               }
-            }
-            $value = '['.trim($s,',').']';
           }
-          if(!is_string($value) && !is_int($value) && !is_float($value) && !is_bool($value)){
-            continue;
+          $ui = get_userinfo();
+          $addAttrs['callerDN']=$ui->dn;
+          $addAttrs['dn']=$plugin->dn;
+          $addAttrs['location']=$config->current['NAME'];
+
+          // Sort attributes by length, ensures correct replacement
+          $tmp = array();
+          foreach($addAttrs as $name => $value){
+              $tmp[$name] =  strlen($name);
+          }
+          arsort($tmp);
+
+          // Now replace the placeholder 
+          $command = fillReplacements($command, $addAttrs, TRUE);
+
+          // If there are still some %.. in our command, try to fill these with some other class vars 
+          if(preg_match("/%/",$command)){
+              $attrs = get_object_vars($plugin);
+              foreach($attrs as $name => $value){
+                  if(is_array($value)){
+                      $s = "";
+                      foreach($value as $val){
+                          if(is_string($val) || is_int($val) || is_float($val) || is_bool($val)){
+                              $s .= '"'.$val.'",'; 
+                          }
+                      }
+                      $value = '['.trim($s,',').']';
+                  }
+                  if(!is_string($value) && !is_int($value) && !is_float($value) && !is_bool($value)){
+                      continue;
+                  }
+                  $command= preg_replace("/%$name/", escapeshellarg($value), $command);
+              }
           }
-          $command= preg_replace("/%$name/", $value, $command);
-        }
-      }
 
-      if (check_command($command)){
-        @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command,"Execute");
-        exec($command,$arr);
-        if(is_array($arr)){
-          $str = implode("\n",$arr);
-          @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$str);
-        }
-      } else {
-        $message= msgPool::cmdnotfound("POSTCREATE", get_class($plugin));
-        msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
+          if (check_command($command)){
+
+              // Create list of process pipes
+              $descriptorspec = array(
+                      0 => array("pipe", "r"),  // stdin
+                      1 => array("pipe", "w"),  // stdout
+                      2 => array("pipe", "w")); // stderr
+
+              // Try to open the process
+              @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__,$command,"Execute");
+              $process = proc_open($command, $descriptorspec, $pipes);
+              if (is_resource($process)) {
+
+                  // Write the password to stdin
+                  // fwrite($pipes[0], $pwd); 
+                  fclose($pipes[0]);
+
+                  // Get results from stdout and stderr
+                  $arr = stream_get_contents($pipes[1]);
+                  $err = stream_get_contents($pipes[2]);
+                  fclose($pipes[1]);
+
+                  // Close the process and check its return value
+                  $returnCode = proc_close($process);
+                  $returnOutput = preg_split("/\n/", $arr,0,PREG_SPLIT_NO_EMPTY);
+                  $errorOutput = preg_split("/\n/",$err,0,PREG_SPLIT_NO_EMPTY);
+              }
+
+              if($returnCode != 0){
+                  @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Execution failed code: ".$returnCode);
+                  @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$err);
+                  if($displayErrors){
+                      $message= msgPool::cmdexecfailed($cmd,$command, get_class($plugin));
+                      msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
+                  }
+              }elseif(is_array($arr)){
+                  @DEBUG (DEBUG_SHELL, __LINE__, __FUNCTION__, __FILE__, $command, "Result: ".$arr);
+              }
+          } elseif($displayErrors) {
+              $message= msgPool::cmdinvalid($cmd,$command, get_class($plugin));
+              msg_dialog::display(_("Error"), $message, ERROR_DIALOG);
+          }
       }
-    }
   }
 }