Code

Updated some sieve templates
[gosa.git] / gosa-si / server / events / opsi_com.pm
index 48ab4e2ffae5cc24b3a6d088be6e5bc5f84a3daa..c6fee306ac2a0b02d626b75da569738457fd5b31 100644 (file)
@@ -4,8 +4,18 @@
 
 
 package opsi_com;
+
+use strict;
+use warnings;
+
 use Exporter;
-@ISA = qw(Exporter);
+use UNIVERSAL 'isa';
+use GOSA::GosaSupportDaemon;
+use Data::Dumper;
+use XML::Quote qw(:all);
+
+our @ISA = qw(Exporter);
+
 my @events = (
     "get_events",
     "opsi_install_client",
@@ -14,6 +24,7 @@ my @events = (
     "opsi_get_client_hardware",
     "opsi_get_client_software",
     "opsi_get_product_properties",
+    "opsi_get_full_product_host_information",
     "opsi_set_product_properties",
     "opsi_list_clients",
     "opsi_del_client",
@@ -21,33 +32,29 @@ my @events = (
     "opsi_modify_client",
     "opsi_add_product_to_client",
     "opsi_del_product_from_client",
-       "opsi_createLicensePool",
-       "opsi_deleteLicensePool",
-       "opsi_createLicense",
-       "opsi_assignSoftwareLicenseToHost",
-       "opsi_unassignSoftwareLicenseFromHost",
-       "opsi_unassignAllSoftwareLicensesFromHost",
-       "opsi_getSoftwareLicense_hash",
-       "opsi_getLicensePool_hash",
-       "opsi_getSoftwareLicenseUsages",
-       "opsi_getSoftwareLicenseUsagesForProductId",
-       "opsi_getLicensePools_listOfHashes",
-       "opsi_getLicenseInformationForProduct",
-       "opsi_getPool",
-       "opsi_getAllSoftwareLicenses",
-       "opsi_removeLicense",
-       "opsi_getReservedLicenses",
-       "opsi_boundHostToLicense",
-       "opsi_unboundHostFromLicense",
-       "opsi_test",
+    "opsi_createLicensePool",
+    "opsi_deleteLicensePool",
+    "opsi_createLicense",
+    "opsi_assignSoftwareLicenseToHost",
+    "opsi_unassignSoftwareLicenseFromHost",
+    "opsi_unassignAllSoftwareLicensesFromHost",
+    "opsi_getSoftwareLicense_hash",
+    "opsi_getLicensePool_hash",
+    "opsi_getSoftwareLicenseUsages",
+    "opsi_getSoftwareLicenseUsagesForProductId",
+    "opsi_getLicensePools_listOfHashes",
+    "opsi_getLicenseInformationForProduct",
+    "opsi_getPool",
+    "opsi_getAllSoftwareLicenses",
+    "opsi_removeLicense",
+    "opsi_getReservedLicenses",
+    "opsi_boundHostToLicense",
+    "opsi_unboundHostFromLicense",
+    "opsi_test",
    );
-@EXPORT = @events;
 
-use strict;
-use warnings;
-use GOSA::GosaSupportDaemon;
-use Data::Dumper;
-use XML::Quote qw(:all);
+our @EXPORT = @events;
+
 
 BEGIN {}
 
@@ -58,63 +65,49 @@ END {}
 # ----------------------------------------------------------------------------
 
 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
-
-
+my ($opsi_enabled, $opsi_server, $opsi_admin, $opsi_password, $opsi_url, $opsi_client);
+my %cfg_defaults = (
+               "Opsi" => {
+               "enabled"  => [\$opsi_enabled, "false"],
+               "server"   => [\$opsi_server, "localhost"],
+               "admin"    => [\$opsi_admin, "opsi-admin"],
+               "password" => [\$opsi_password, "secret"],
+               },
+);
+&read_configfile($main::cfg_file, %cfg_defaults);
+if ($opsi_enabled eq "true") {
+       use JSON::RPC::Client;
+       use XML::Quote qw(:all);
+       use Time::HiRes qw( time );
+       $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
+       $opsi_client = new JSON::RPC::Client;
+
+       # Check version dependencies
+       eval { &myXmlHashToString(); };
+       if ($@ ) {
+               die "\nThe version of the Opsi plugin you want to use requires a newer version of GosaSupportDaemon. Please update your GOsa-SI or deactivate the Opsi plugin.\n";
+       }
+}
 
 # ----------------------------------------------------------------------------
-#                            S U B R O U T I N E S
+#   external methods handling the comunication with GOsa/GOsa-si
 # ----------------------------------------------------------------------------
 
-
 ################################
-#
 # @brief A function returning a list of functions which are exported by importing the module.
 # @return List of all provided functions
-#
 sub get_events {
     return \@events;
 }
 
 ################################
-#
-# @brief Checks if there is a specified tag and if the the tag has a content.
-# @return 0|1
-#
-sub _check_xml_tag_is_ok {
-       my ($msg_hash,$tag) = @_;
-       if (not defined $msg_hash->{$tag}) {
-               $_ = "message contains no tag '$tag'";
-               return 0;
-       }
-       if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
-               $_ = "message tag '$tag' has no content";
-               return  0;
-       }
-       return 1;
-}
-
-################################
-#
-# @brief Writes the log line and returns the error message for GOsa.
-#
-sub _giveErrorFeedback {
-       my ($msg_hash, $err_string, $session_id) = @_;
-       &main::daemon_log("$session_id ERROR: $err_string", 1);
-       my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
-    if (exists $msg_hash->{forward_to_gosa}) {
-        &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
-    }
-       return ( &create_xml_string($out_hash) );
-}
-
-
-## @method opsi_add_product_to_client
-# Adds an Opsi product to an Opsi client.
+# @brief Adds an Opsi product to an Opsi client.
 # @param msg - STRING - xml message with tags hostId and productId
 # @param msg_hash - HASHREF - message information parsed into a hash
 # @param session_id - INTEGER - POE session id of the processing of this message
 # @return out_msg - STRING - feedback to GOsa in success and error case
 sub opsi_add_product_to_client {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -148,21 +141,22 @@ sub opsi_add_product_to_client {
                method  => 'setProductActionRequest',
                params  => [ $productId, $hostId, "setup" ],
                id  => 1, }; 
-
        my $res = $main::opsi_client->call($main::opsi_url, $callobj);
+
        if (&check_opsi_res($res)) { return ( (caller(0))[3]." : ".$_, 1 ); };
 
-    # return message
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return ( &create_xml_string($out_hash) );
 }
 
-## @method opsi_del_product_from_client
-# Deletes an Opsi-product from an Opsi-client. 
+################################
+# @brief Deletes an Opsi-product from an Opsi-client. 
 # @param msg - STRING - xml message with tags hostId and productId
 # @param msg_hash - HASHREF - message information parsed into a hash
 # @param session_id - INTEGER - POE session id of the processing of this message
 # @return out_msg - STRING - feedback to GOsa in success and error case
 sub opsi_del_product_from_client {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -203,22 +197,6 @@ sub opsi_del_product_from_client {
         $productId = @{$msg_hash->{'productId'}}[0];
         &add_content2xml_hash($out_hash, "productId", $productId);
 
-
-# : check the results for more than one entry which is currently installed
-        #$callobj = {
-        #    method  => 'getProductDependencies_listOfHashes',
-        #    params  => [ $productId ],
-        #    id  => 1, };
-        #
-        #my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
-        #my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
-        #if ($sres_err){
-        #  &main::daemon_log("ERROR: cannot perform dependency check: ".$sres_err_string, 1);
-        #  &add_content2xml_hash($out_hash, "error", $sres_err_string);
-        #  return ( &create_xml_string($out_hash) );
-        #}
-
-
         # Check to get product action list 
         my $callobj = {
             method  => 'getPossibleProductActions_list',
@@ -265,16 +243,18 @@ sub opsi_del_product_from_client {
     }
 
     # Return message
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return ( &create_xml_string($out_hash) );
 }
 
-## @method opsi_add_client
-# Adds an Opsi client to Opsi.
+################################
+# @brief Adds an Opsi client to Opsi.
 # @param msg - STRING - xml message with tags hostId and macaddress
 # @param msg_hash - HASHREF - message information parsed into a hash
 # @param session_id - INTEGER - POE session id of the processing of this message
 # @return out_msg - STRING - feedback to GOsa in success and error case
 sub opsi_add_client {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -347,16 +327,18 @@ sub opsi_add_client {
     }
 
     # Return message
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return ( &create_xml_string($out_hash) );
 }
 
-## @method opsi_modify_client
-# Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
+################################
+# @brief Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
 # @param msg_hash - HASHREF - message information parsed into a hash
 # @param session_id - INTEGER - POE session id of the processing of this message    
 # @return out_msg - STRING - feedback to GOsa in success and error case
 sub opsi_modify_client {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -437,30 +419,31 @@ sub opsi_modify_client {
     }
 
     # Return message
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return ( &create_xml_string($out_hash) );
 }
-
-    
-## @method opsi_get_netboot_products
-# Get netboot products for specific host.
+################################
+# @brief Get netboot products for specific host.
 # @param msg - STRING - xml message with tag hostId
 # @param msg_hash - HASHREF - message information parsed into a hash
 # @param session_id - INTEGER - POE session id of the processing of this message
 # @return out_msg - STRING - feedback to GOsa in success and error case
 sub opsi_get_netboot_products {
+    my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
     my $target = @{$msg_hash->{'target'}}[0];
     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
     my $hostId;
-    my $xml_msg;
 
     # Build return message with twisted target and source
     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
     if (defined $forward_to_gosa) {
         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
     }
+    &add_content2xml_hash($out_hash, "xxx", "");
 
     # Get hostId if defined
     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
@@ -468,107 +451,59 @@ sub opsi_get_netboot_products {
         &add_content2xml_hash($out_hash, "hostId", $hostId);
     }
 
-    &add_content2xml_hash($out_hash, "xxx", "");
-    $xml_msg = &create_xml_string($out_hash);
-    # For hosts, only return the products that are or get installed
+    # Move to XML string
+    my $xml_msg= &create_xml_string($out_hash);
+
     my $callobj;
-    $callobj = {
-        method  => 'getNetBootProductIds_list',
-        params  => [ ],
-        id  => 1,
-    };
-    &main::daemon_log("$session_id DEBUG: send callobj to opsi_client: ".&opsi_callobj2string($callobj), 7);
-    &main::daemon_log("$session_id DEBUG: opsi_url $main::opsi_url", 7);
-    &main::daemon_log("$session_id DEBUG: waiting for answer from opsi_client!", 7);
-    my $res = $main::opsi_client->call($main::opsi_url, $callobj);
-    &main::daemon_log("$session_id DEBUG: get answer from opsi_client", 7);
-    my %r = ();
-    for (@{$res->result}) { $r{$_} = 1 }
+    # Check if we need to get host or global information
+    if (defined $hostId){
+      $callobj = {
+          method  => 'getProductHostInformation_list',
+          params  => [ $hostId, undef, 'netboot'],
+          id  => 1,
+      };
 
+      my $res = $main::opsi_client->call($main::opsi_url, $callobj);
       if (not &check_opsi_res($res)){
+          foreach my $product (@{$res->result}){
+               my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><state>".xml_quote($product->{'installationStatus'})."</state><action>".xml_quote($product->{'actionRequest'})."</action><\/item><xxx><\/xxx>";
+               $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
+          }
+      }
 
-        if (defined $hostId){
-
-            $callobj = {
-                method  => 'getProductStates_hash',
-                params  => [ $hostId ],
-                id  => 1,
-            };
-
-            my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
-            if (not &check_opsi_res($hres)){
-                my $htmp= $hres->result->{$hostId};
-
-                # check state != not_installed or action == setup -> load and add
-                foreach my $product (@{$htmp}){
-
-                    if (!defined ($r{$product->{'productId'}})){
-                        next;
-                    }
-
-                    # Now we've a couple of hashes...
-                    if ($product->{'installationStatus'} ne "not_installed" or
-                            $product->{'actionRequest'} eq "setup"){
-                        my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
-
-                        $callobj = {
-                            method  => 'getProduct_hash',
-                            params  => [ $product->{'productId'} ],
-                            id  => 1,
-                        };
-
-                        my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
-                        if (not &check_opsi_res($sres)){
-                            my $tres= $sres->result;
-
-                            my $name= xml_quote($tres->{'name'});
-                            my $r= $product->{'productId'};
-                            my $description= xml_quote($tres->{'description'});
-                            $name=~ s/\//\\\//;
-                            $description=~ s/\//\\\//;
-                            $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
-                        }
-                    }
-                }
-
-            }
+    } else {
 
-        } else {
-            foreach my $r (@{$res->result}) {
-                $callobj = {
-                    method  => 'getProduct_hash',
-                    params  => [ $r ],
-                    id  => 1,
-                };
-
-                my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
-                if (not &check_opsi_res($sres)){
-                    my $tres= $sres->result;
-
-                    my $name= xml_quote($tres->{'name'});
-                    my $description= xml_quote($tres->{'description'});
-                    $name=~ s/\//\\\//;
-                    $description=~ s/\//\\\//;
-                    $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
-                }
-            }
+      # For hosts, only return the products that are or get installed
+      $callobj = {
+          method  => 'getProductInformation_list',
+          params  => [ undef, 'netboot' ],
+          id  => 1,
+      };
 
-        }
+      my $res = $main::opsi_client->call($main::opsi_url, $callobj);
+      if (not &check_opsi_res($res)){
+          foreach my $product (@{$res->result}) {
+               my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><\/item><xxx><\/xxx>";
+               $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
+          }
+      }
     }
+
     $xml_msg=~ s/<xxx><\/xxx>//;
 
-    # Return message
+    # Retrun Message
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return ( $xml_msg );
 }
 
-
-## @method opsi_get_product_properties
-# Get product properties for a product and a specific host or gobally for a product.
+################################   
+# @brief Get product properties for a product and a specific host or gobally for a product.
 # @param msg - STRING - xml message with tags productId and optional hostId
 # @param msg_hash - HASHREF - message information parsed into a hash
 # @param session_id - INTEGER - POE session id of the processing of this message
 # @return out_msg - STRING - feedback to GOsa in success and error case
 sub opsi_get_product_properties {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -680,7 +615,7 @@ sub opsi_get_product_properties {
             if (defined $values->{$key}){
               $vals= $values->{$key};
             }
-            $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
+            $item.= "<$key>$dsc<current>".xml_quote($value)."</current>$vals</$key>";
             $item.= "</item>";
             $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
         }
@@ -689,17 +624,18 @@ sub opsi_get_product_properties {
     $xml_msg=~ s/<xxx><\/xxx>//;
 
     # Return message
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return ( $xml_msg );
 }
 
-
-## @method opsi_set_product_properties
-# Set product properities for a specific host or globaly. Message needs one xml tag 'item' and within one xml tag 'name' and 'value'. The xml tags action and state are optional.
+################################   
+# @brief Set product properities for a specific host or globaly. Message needs one xml tag 'item' and within one xml tag 'name' and 'value'. The xml tags action and state are optional.
 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
 # @param msg_hash - HASHREF - message information parsed into a hash
 # @param session_id - INTEGER - POE session id of the processing of this message
 # @return out_msg - STRING - feedback to GOsa in success and error case
 sub opsi_set_product_properties {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -796,17 +732,18 @@ sub opsi_set_product_properties {
 
 
     # Return message
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return ( &create_xml_string($out_hash) );
 }
 
-
-## @method opsi_get_client_hardware
-# Reports client hardware inventory.
+################################   
+# @brief Reports client hardware inventory.
 # @param msg - STRING - xml message with tag hostId
 # @param msg_hash - HASHREF - message information parsed into a hash
 # @param session_id - INTEGER - POE session id of the processing of this message
 # @return out_msg - STRING - feedback to GOsa in success and error case
 sub opsi_get_client_hardware {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -816,79 +753,65 @@ sub opsi_get_client_hardware {
     my $error = 0;
     my $xml_msg;
 
+    # Sanity check of needed parameter
+       if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
+        $hostId = @{$msg_hash->{'hostId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+
+
     # Build return message with twisted target and source
     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
     if (defined $forward_to_gosa) {
       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
     }
-
-    # Sanity check of needed parameter
-    if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
-        $error++;
-        &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
-        &add_content2xml_hash($out_hash, "error", "hostId");
-        &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
-    }
-
-    if (not $error) {
-
-    # Get hostId
-        $hostId = @{$msg_hash->{'hostId'}}[0];
-        &add_content2xml_hash($out_hash, "hostId", "$hostId");
-        &add_content2xml_hash($out_hash, "xxx", "");
-    }    
+       &add_content2xml_hash($out_hash, "hostId", "$hostId");
+       &add_content2xml_hash($out_hash, "xxx", "");
 
     # Move to XML string
     $xml_msg= &create_xml_string($out_hash);
     
-    if (not $error) {
+       my $res = &_callOpsi(method=>'getHardwareInformation_hash', params=>[ $hostId ]);
+       if (not &check_opsi_res($res)){
+               my $result= $res->result;
+               if (ref $result eq "HASH") {
+                       foreach my $r (keys %{$result}){
+                               my $item= "\n<item><id>".xml_quote($r)."</id>";
+                               my $value= $result->{$r};
+                               foreach my $sres (@{$value}){
+
+                                       foreach my $dres (keys %{$sres}){
+                                               if (defined $sres->{$dres}){
+                                                       $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
+                                               }
+                                       }
+
+                               }
+                               $item.= "</item>";
+                               $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
 
-    # JSON Query
-        my $callobj = {
-            method  => 'getHardwareInformation_hash',
-            params  => [ $hostId ],
-            id  => 1,
-        };
-
-        my $res = $main::opsi_client->call($main::opsi_url, $callobj);
-        if (not &check_opsi_res($res)){
-            my $result= $res->result;
-            if (ref $result eq "HASH") {
-                foreach my $r (keys %{$result}){
-                    my $item= "\n<item><id>".xml_quote($r)."</id>";
-                    my $value= $result->{$r};
-                    foreach my $sres (@{$value}){
-
-                        foreach my $dres (keys %{$sres}){
-                            if (defined $sres->{$dres}){
-                                $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
-                            }
-                        }
-
-                    }
-                    $item.= "</item>";
-                    $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
-
-                }
-            }
-        }
-
-        $xml_msg=~ s/<xxx><\/xxx>//;
+                       }
+               }
+       }
 
-    }
+       $xml_msg=~ s/<xxx><\/xxx>//;
 
     # Return message
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
     return ( $xml_msg );
 }
 
-
-## @method opsi_list_clients
-# Reports all Opsi clients. 
+################################   
+# @brief Reports all Opsi clients. 
 # @param msg - STRING - xml message 
 # @param msg_hash - HASHREF - message information parsed into a hash
 # @param session_id - INTEGER - POE session id of the processing of this message
 # @return out_msg - STRING - feedback to GOsa in success and error case
 sub opsi_list_clients {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -907,14 +830,16 @@ sub opsi_list_clients {
 
     # JSON Query
     my $callobj = {
-        method  => 'getClients_listOfHashes',
+        method  => 'getClientsInformation_listOfHashes',
         params  => [ ],
         id  => 1,
     };
+
     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
     if (not &check_opsi_res($res)){
         foreach my $host (@{$res->result}){
             my $item= "\n<item><name>".$host->{'hostId'}."</name>";
+            $item.= "<mac>".xml_quote($host->{'macAddress'})."</mac>";
             if (defined($host->{'description'})){
                 $item.= "<description>".xml_quote($host->{'description'})."</description>";
             }
@@ -925,43 +850,24 @@ sub opsi_list_clients {
                 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
             }
 
-            $callobj = {
-              method  => 'getIpAddress',
-              params  => [ $host->{'hostId'} ],
-              id  => 1,
-            };
-            my $sres= $main::opsi_client->call($main::opsi_url, $callobj);
-            if ( not &check_opsi_res($sres)){
-              $item.= "<ip>".xml_quote($sres->result)."</ip>";
-            }
-
-            $callobj = {
-              method  => 'getMacAddress',
-              params  => [ $host->{'hostId'} ],
-              id  => 1,
-            };
-            $sres= $main::opsi_client->call($main::opsi_url, $callobj);
-            if ( not &check_opsi_res($sres)){
-                $item.= "<mac>".xml_quote($sres->result)."</mac>";
-            }
             $item.= "</item>";
             $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
         }
     }
-
     $xml_msg=~ s/<xxx><\/xxx>//;
+
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return ( $xml_msg );
 }
 
-
-
-## @method opsi_get_client_software
-# Reports client software inventory.
+################################   
+# @brief Reports client software inventory.
 # @param msg - STRING - xml message with tag hostId
 # @param msg_hash - HASHREF - message information parsed into a hash
 # @param session_id - INTEGER - POE session id of the processing of this message
 # @return out_msg - STRING - feedback to GOsa in success and error case
 sub opsi_get_client_software {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -1014,17 +920,18 @@ sub opsi_get_client_software {
     }
 
     # Return message
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return ( $xml_msg );
 }
 
-
-## @method opsi_get_local_products
-# Reports product for given hostId or globally.
+################################   
+# @brief Reports product for given hostId or globally.
 # @param msg - STRING - xml message with optional tag hostId
 # @param msg_hash - HASHREF - message information parsed into a hash
 # @param session_id - INTEGER - POE session id of the processing of this message
 # @return out_msg - STRING - feedback to GOsa in success and error case
 sub opsi_get_local_products {
+    my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -1045,107 +952,60 @@ sub opsi_get_local_products {
         &add_content2xml_hash($out_hash, "hostId", $hostId);
     }
 
-    # Move to XML string
-    my $xml_msg= &create_xml_string($out_hash);
-
-    # For hosts, only return the products that are or get installed
     my $callobj;
-    $callobj = {
-        method  => 'getLocalBootProductIds_list',
-        params  => [ ],
-        id  => 1,
-    };
-
-    my $res = $main::opsi_client->call($main::opsi_url, $callobj);
-    my %r = ();
-    for (@{$res->result}) { $r{$_} = 1 }
-
-    if (not &check_opsi_res($res)){
 
-        if (defined $hostId){
-            $callobj = {
-                method  => 'getProductStates_hash',
-                params  => [ $hostId ],
-                id  => 1,
-            };
-
-            my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
-            if (not &check_opsi_res($hres)){
-                my $htmp= $hres->result->{$hostId};
-
-                # Check state != not_installed or action == setup -> load and add
-                foreach my $product (@{$htmp}){
-
-                    if (!defined ($r{$product->{'productId'}})){
-                        next;
-                    }
-
-                    # Now we've a couple of hashes...
-                    if ($product->{'installationStatus'} ne "not_installed" or
-                            $product->{'actionRequest'} eq "setup"){
-                        my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
-
-                        $callobj = {
-                            method  => 'getProduct_hash',
-                            params  => [ $product->{'productId'} ],
-                            id  => 1,
-                        };
-
-                        my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
-                        if (not &check_opsi_res($sres)){
-                            my $tres= $sres->result;
-
-                            my $name= xml_quote($tres->{'name'});
-                            my $r= $product->{'productId'};
-                            my $description= xml_quote($tres->{'description'});
-                            $name=~ s/\//\\\//;
-                            $description=~ s/\//\\\//;
-                            $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
-                        }
+    # Move to XML string
+    my $xml_msg= &create_xml_string($out_hash);
 
-                    }
-                }
+    # Check if we need to get host or global information
+    if (defined $hostId){
+      $callobj = {
+          method  => 'getProductHostInformation_list',
+          params  => [ $hostId ],
+          id  => 1,
+      };
 
-            }
+      my $res = $main::opsi_client->call($main::opsi_url, $callobj);
+      if (not &check_opsi_res($res)){
+          foreach my $product (@{$res->result}){
+               my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><state>".xml_quote($product->{'installationStatus'})."</state><action>".xml_quote($product->{'actionRequest'})."</action><\/item><xxx><\/xxx>";
+               $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
+          }
+      }
 
-        } else {
-            foreach my $r (@{$res->result}) {
-                $callobj = {
-                    method  => 'getProduct_hash',
-                    params  => [ $r ],
-                    id  => 1,
-                };
-
-                my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
-                if (not &check_opsi_res($sres)){
-                    my $tres= $sres->result;
-
-                    my $name= xml_quote($tres->{'name'});
-                    my $description= xml_quote($tres->{'description'});
-                    $name=~ s/\//\\\//;
-                    $description=~ s/\//\\\//;
-                    $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
-                }
+    } else {
 
-            }
+      # For hosts, only return the products that are or get installed
+      $callobj = {
+          method  => 'getProductInformation_list',
+          params  => [ undef, 'localboot' ],
+          id  => 1,
+      };
 
-        }
+      my $res = $main::opsi_client->call($main::opsi_url, $callobj);
+      if (not &check_opsi_res($res)){
+          foreach my $product (@{$res->result}) {
+               my $replace= "<item><productId>".xml_quote($product->{'productId'})."<\/productId><name>".xml_quote($product->{'name'})."<\/name><description>".xml_quote($product->{'description'})."<\/description><\/item><xxx><\/xxx>";
+               $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
+          }
+      }
     }
 
     $xml_msg=~ s/<xxx><\/xxx>//;
 
     # Retrun Message
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return ( $xml_msg );
 }
 
-
-## @method opsi_del_client
-# Deletes a client from Opsi.
+################################   
+# @brief Deletes a client from Opsi.
 # @param msg - STRING - xml message with tag hostId
 # @param msg_hash - HASHREF - message information parsed into a hash
 # @param session_id - INTEGER - POE session id of the processing of this message
 # @return out_msg - STRING - feedback to GOsa in success and error case
 sub opsi_del_client {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -1187,26 +1047,24 @@ sub opsi_del_client {
     my $xml_msg= &create_xml_string($out_hash);
 
     # Return message
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return ( $xml_msg );
 }
 
-
-## @method opsi_install_client
-# Set a client in Opsi to install and trigger a wake on lan message (WOL).  
+################################   
+# @brief Set a client in Opsi to install and trigger a wake on lan message (WOL).  
 # @param msg - STRING - xml message with tags hostId, macaddress
 # @param msg_hash - HASHREF - message information parsed into a hash
 # @param session_id - INTEGER - POE session id of the processing of this message
 # @return out_msg - STRING - feedback to GOsa in success and error case
 sub opsi_install_client {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
     my $target = @{$msg_hash->{'target'}}[0];
     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
-
-
     my ($hostId, $macaddress);
-
     my $error = 0;
     my @out_msg_l;
 
@@ -1295,12 +1153,12 @@ sub opsi_install_client {
     }
     
     # Return messages
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return @out_msg_l;
 }
 
-
-## @method _set_action
-# Set action for an Opsi client
+################################
+# @brief Set action for an Opsi client
 # @param product - STRING - Opsi product
 # @param action - STRING - action
 # @param hostId - STRING - Opsi hostId
@@ -1319,8 +1177,8 @@ sub _set_action {
   $main::opsi_client->call($main::opsi_url, $callobj);
 }
 
-## @method _set_state
-# Set state for an Opsi client
+################################
+# @brief Set state for an Opsi client
 # @param product - STRING - Opsi product
 # @param action - STRING - state
 # @param hostId - STRING - Opsi hostId
@@ -1340,14 +1198,13 @@ sub _set_state {
 }
 
 ################################
-#
 # @brief Create a license pool at Opsi server.
 # @param licensePoolId The name of the pool (optional). 
 # @param description The description of the pool (optional).
 # @param productIds A list of assigned porducts of the pool (optional). 
 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional). 
-#
 sub opsi_createLicensePool {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -1377,15 +1234,18 @@ sub opsi_createLicensePool {
 
        # Create function result message
        $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
 
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
        return ( &create_xml_string($out_hash) );
 }
 
 ################################
-#
 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
-#
 sub opsi_getLicensePools_listOfHashes {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -1421,17 +1281,20 @@ sub opsi_getLicensePools_listOfHashes {
 
        # Create function result message
        $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
        $out_hash->{result} = [$res_hash];
 
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
        return ( &create_xml_string($out_hash) );
 }
 
 ################################
-#
 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
 # @param licensePoolId The name of the pool. 
-#
 sub opsi_getLicensePool_hash {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -1466,11 +1329,15 @@ sub opsi_getLicensePool_hash {
 
        # Create function result message
        $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
        &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
        &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
        map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
        map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
 
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
        return ( &create_xml_string($out_hash) );
 }
 
@@ -1507,12 +1374,11 @@ sub _parse_getSoftwareLicenseUsages {
 }
 
 ################################
-#
 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
 # @param hostid Something like client_1.intranet.mydomain.de (optional).
 # @param licensePoolId The name of the pool (optional). 
-# 
 sub opsi_getSoftwareLicenseUsages {
+       my $startTime = Time::HiRes::time;
        my ($msg, $msg_hash, $session_id) = @_;
        my $header = @{$msg_hash->{'header'}}[0];
        my $source = @{$msg_hash->{'source'}}[0];
@@ -1531,17 +1397,20 @@ sub opsi_getSoftwareLicenseUsages {
 
        # Create function result message
        $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
        $out_hash->{result} = [$res_hash];
 
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
        return ( &create_xml_string($out_hash) );
 }
 
 ################################
-#
 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId. Function return is identical to opsi_getSoftwareLicenseUsages
 # @param productId Something like 'firefox', 'python' or anything else .
-# 
 sub opsi_getSoftwareLicenseUsagesForProductId {
+       my $startTime = Time::HiRes::time;
        my ($msg, $msg_hash, $session_id) = @_;
        my $header = @{$msg_hash->{'header'}}[0];
        my $source = @{$msg_hash->{'source'}}[0];
@@ -1557,33 +1426,34 @@ sub opsi_getSoftwareLicenseUsagesForProductId {
        # Fetch licensePoolId for productId
        my ($res, $err) = &_getLicensePoolId('productId'=>$productId);
        if ($err){
-               return &_giveErrorFeedback($msg_hash, "cannot fetch licensePoolId for given productId : ".$res, $session_id);
+                my $out_hash = &create_xml_hash("answer_$header", $main::server_address, $source);
+                $out_hash->{result} = [];
+               return ( &create_xml_string($out_hash) );
        }
-
-       my $licensePoolId;
+       my $licensePoolId = $res;   # We assume that there is only one pool for each productID!!!
 
        # Fetch softwareLiceceUsages for licensePoolId
        ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
        if ($err){
                return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
        }
-
        # Parse Opsi result
        my $res_hash = &_parse_getSoftwareLicenseUsages($res);
 
        # Create function result message
-       my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       my $out_hash = &create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
        $out_hash->{result} = [$res_hash];
 
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
        return ( &create_xml_string($out_hash) );
 }
 
 ################################
-#
 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
 # @param softwareLicenseId Identificator of a license.
-#
 sub opsi_getSoftwareLicense_hash {
+       my $startTime = Time::HiRes::time;
        my ($msg, $msg_hash, $session_id) = @_;
        my $header = @{$msg_hash->{'header'}}[0];
        my $source = @{$msg_hash->{'source'}}[0];
@@ -1616,6 +1486,7 @@ sub opsi_getSoftwareLicense_hash {
        
        # Create function result message
        $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
        &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
        &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
        &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
@@ -1625,16 +1496,18 @@ sub opsi_getSoftwareLicense_hash {
                &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
        }
 
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
        return ( &create_xml_string($out_hash) );
 }
 
 ################################
-#
 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool. 
 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted. 
 # @param licensePoolId The name of the pool. 
-#
 sub opsi_deleteLicensePool {
+       my $startTime = Time::HiRes::time;
        my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -1702,12 +1575,15 @@ sub opsi_deleteLicensePool {
 
        # Create function result message
        $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
-       
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
+
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
        return ( &create_xml_string($out_hash) );
 }
 
 ################################
-#
 # @brief Create a license contract, create a software license and add the software license to the license pool
 # @param licensePoolId The name of the pool the license should be assigned.
 # @param licenseKey The license key.
@@ -1720,8 +1596,8 @@ sub opsi_deleteLicensePool {
 # @param maxInstallations The number of clients use this license (optional). 
 # @param boundToHost The name of the client the license is bound to (optional).
 # @param expirationDate The date when the license is running down (optional). 
-#
 sub opsi_createLicense {
+       my $startTime = Time::HiRes::time;
        my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -1817,17 +1693,20 @@ sub opsi_createLicense {
 
        # Create function result message
        $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
        
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
        return ( &create_xml_string($out_hash) );
 }
 
 ################################
-#
 # @brief Assign a software license to a host
 # @param hostid Something like client_1.intranet.mydomain.de
 # @param licensePoolId The name of the pool.
-#
 sub opsi_assignSoftwareLicenseToHost {
+       my $startTime = Time::HiRes::time;
        my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -1866,17 +1745,20 @@ sub opsi_assignSoftwareLicenseToHost {
 
        # Create function result message
        my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
        
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
        return ( &create_xml_string($out_hash) );
 }
 
 ################################
-#
 # @brief Unassign a software license from a host.
 # @param hostid Something like client_1.intranet.mydomain.de
 # @param licensePoolId The name of the pool.
-#
 sub opsi_unassignSoftwareLicenseFromHost {
+       my $startTime = Time::HiRes::time;
        my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -1915,16 +1797,19 @@ sub opsi_unassignSoftwareLicenseFromHost {
 
        # Create function result message
        my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
        
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
        return ( &create_xml_string($out_hash) );
 }
 
 ################################
-#
 # @brief Unassign all software licenses from a host
 # @param hostid Something like client_1.intranet.mydomain.de
-#
 sub opsi_unassignAllSoftwareLicensesFromHost {
+       my $startTime = Time::HiRes::time;
        my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -1957,18 +1842,21 @@ sub opsi_unassignAllSoftwareLicensesFromHost {
 
        # Create function result message
        my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
        
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
        return ( &create_xml_string($out_hash) );
 }
 
 
 ################################
-#
 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
 # and the number of max and remaining installations for a given OPSI product.
 # @param productId Identificator of an OPSI product.
-#      
 sub opsi_getLicenseInformationForProduct {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -1988,7 +1876,8 @@ sub opsi_getLicenseInformationForProduct {
         params  => [ $productId ],
         id  => 1,
     };
-    my $res = $main::opsi_client->call($main::opsi_url, $callobj);
+    #my $res = $main::opsi_client->call($main::opsi_url, $callobj);
+    my $res = $opsi_client->call($opsi_url, $callobj);
 
        # Check Opsi error
        my ($res_error, $res_error_str) = &check_opsi_res($res);
@@ -2004,7 +1893,7 @@ sub opsi_getLicenseInformationForProduct {
                params  => [ ],
                id  => 1,
        };
-       $res = $main::opsi_client->call($main::opsi_url, $callobj);
+       $res = $opsi_client->call($opsi_url, $callobj);
 
        # Check Opsi error
        ($res_error, $res_error_str) = &check_opsi_res($res);
@@ -2017,6 +1906,7 @@ sub opsi_getLicenseInformationForProduct {
 
        # Create function result message
        $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
        &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
        &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
        &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
@@ -2024,16 +1914,18 @@ sub opsi_getLicenseInformationForProduct {
        &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
        map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
 
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
        return ( &create_xml_string($out_hash) );
 }
 
 
 ################################
-#
-# @brief
-# @param 
-#      
+# @brief Returns licensePoolId, description, a list of productIds, al list of windowsSoftwareIds and a list of licenses for a given licensePoolId. 
+# Each license contains softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseKeys, hostIds, expirationDate, boundToHost and licenseContractId.
+# The licenseContract contains conclusionDate, expirationDate, notes, notificationDate and partner. 
+# @param licensePoolId The name of the pool.
 sub opsi_getPool {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -2048,6 +1940,7 @@ sub opsi_getPool {
 
        # Create hash for the answer
        my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
 
        # Call Opsi
        my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
@@ -2122,18 +2015,20 @@ sub opsi_getPool {
        }
        $out_hash->{licenses} = [$res_hash];
 
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
     return ( &create_xml_string($out_hash) );
 }
 
 
 ################################
-#
 # @brief Removes at first the software license from license pool and than deletes the software license. 
 # Attention, the software license has to exists otherwise it will lead to an Opsi internal server error.
-# @param softwareLicenseId 
-# @param licensePoolId
-#
+# @param softwareLicenseId Identificator of a license.
+# @param licensePoolId The name of the pool.
 sub opsi_removeLicense {
+       my $startTime = Time::HiRes::time;
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
@@ -2166,16 +2061,19 @@ sub opsi_removeLicense {
 
        # Create hash for the answer
        my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
        return ( &create_xml_string($out_hash) );
 }
 
 
 ################################
-#
-# @brief
-# @param 
-#
+# @brief Return softwareLicenseId, maxInstallations, licenseType, licensePoolIds, licenseContractId, expirationDate, boundToHost and a list of productIds.
+# @param hostId Something like client_1.intranet.mydomain.de
 sub opsi_getReservedLicenses {
+       my $startTime = Time::HiRes::time;
        my ($msg, $msg_hash, $session_id) = @_;
        my $header = @{$msg_hash->{'header'}}[0];
        my $source = @{$msg_hash->{'source'}}[0];
@@ -2194,7 +2092,6 @@ sub opsi_getReservedLicenses {
                return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
        }
 
-
        # Parse result
        my $res_hash = { 'hit'=> [] };
        foreach my $license ( @$license_res) {
@@ -2225,18 +2122,21 @@ sub opsi_getReservedLicenses {
                push( @{$res_hash->{hit}}, $license_hash );
        }
        my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
        $out_hash->{licenses} = [$res_hash];
-    return ( &create_xml_string($out_hash) );
 
-       return;
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
+    return ( &create_xml_string($out_hash) );
 }
 
 ################################
-#
-# @brief
-# @param 
-#
+# @brief Bound the given softwareLicenseId to the given host.
+# @param hostId Opsi hostId
+# @param softwareLicenseId Identificator of a license (optional).
 sub opsi_boundHostToLicense {
+       my $startTime = Time::HiRes::time;
        my ($msg, $msg_hash, $session_id) = @_;
        my $header = @{$msg_hash->{'header'}}[0];
        my $source = @{$msg_hash->{'source'}}[0];
@@ -2293,18 +2193,22 @@ sub opsi_boundHostToLicense {
        }
 
        my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
+
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : $elapsedTime seconds", 1034);
     return ( &create_xml_string($out_hash) );
 }
 
 ################################
-#
-# @brief
-# @param 
-#
+# @brief Release a software license formerly bound to a host.
+# @param softwareLicenseId Identificator of a license.
 sub opsi_unboundHostFromLicense {
        # This is really mad! Opsi is not able to unbound a lincense from a host. To provide the functionality for GOsa
        # 4 rpc calls to Opsi are necessary. First, fetch all data for the given softwareLicenseId, then all details for the associated
        # licenseContractId, then delete the softwareLicense and finally recreate the softwareLicense without the boundToHost option. NASTY!
+       my $startTime = Time::HiRes::time;
        my ($msg, $msg_hash, $session_id) = @_;
        my $header = @{$msg_hash->{'header'}}[0];
        my $source = @{$msg_hash->{'source'}}[0];
@@ -2380,15 +2284,16 @@ sub opsi_unboundHostFromLicense {
        }
 
        my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
+
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return ( &create_xml_string($out_hash) );
 }
 
 ################################
-#
-# @brief
-# @param 
-#
+# @brief Returns a list of licenses with softwaerLicenseId, maxInstallations, boundToHost, expirationDate, licenseContractId, licenseType, a list of licensePoolIds with associated licenseKeys
 sub opsi_getAllSoftwareLicenses {
+       my $startTime = Time::HiRes::time;
        my ($msg, $msg_hash, $session_id) = @_;
        my $header = @{$msg_hash->{'header'}}[0];
        my $source = @{$msg_hash->{'source'}}[0];
@@ -2419,16 +2324,112 @@ sub opsi_getAllSoftwareLicenses {
        
        my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
        $out_hash->{licenses} = [$res_hash];
+       if (exists $msg_hash->{forward_to_gosa}) { &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]); }
+
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
     return ( &create_xml_string($out_hash) );
 }
 
+
+################################
+# @brief Returns a list of values for a given host. Values: priority, onceScript, licenseRequired, packageVersion, productVersion, advice, setupScript, windowsSoftwareIds, installationStatus, pxeConfigTemplate, name, creationTimestamp, alwaysScript, productId, description, properties, actionRequest, uninstallScript, action, updateScript and productClassNames 
+# @param hostId Opsi hostId
+sub opsi_get_full_product_host_information {
+       my $startTime = Time::HiRes::time;
+       my ($msg, $msg_hash, $session_id) = @_;
+       my $header = @{$msg_hash->{'header'}}[0];
+       my $source = @{$msg_hash->{'source'}}[0];
+        my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
+        my $hostId;
+
+       my ($res, $err) = &_get_full_product_host_information( hostId=>@{$msg_hash->{'hostId'}}[0]);
+       if ($err) {
+               return &_giveErrorFeedback($msg_hash, "cannot fetch full_product_host_information from Opsi server : ".$res, $session_id);
+       }
+
+        # Build return message with twisted target and source
+        my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
+        if (defined $forward_to_gosa) {
+            &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
+        }
+        &add_content2xml_hash($out_hash, "xxx", "");
+
+        # Get hostId if defined
+        if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
+            $hostId = @{$msg_hash->{'hostId'}}[0];
+            &add_content2xml_hash($out_hash, "hostId", $hostId);
+        }
+
+        # Move to XML string
+        my $xml_msg= &create_xml_string($out_hash);
+        
+        # Convert result in something usable
+        my $replace= "";
+       foreach my $product ( @$res) {
+
+          # Open item
+          $replace.= "<item>";
+
+          # Add flat hash information
+          my @entries= ( "priority", "onceScript", "licenseRequired", "packageVersion", "productVersion", "advice",
+                              "setupScript", "windowsSoftwareIds", "installationStatus", "pxeConfigTemplate", "name", "type",
+                              "creationTimestamp", "alwaysScript", "productId", "description", "actionRequest", "uninstallScript",
+                              "action", "updateScript", "productClassNames");
+          foreach my $entry (@entries) {
+            if (defined $product->{$entry}) {
+              my $value= $product->{$entry};
+
+              if(ref($value) eq 'ARRAY'){
+                my $tmp= "";
+                foreach my $element (@$value) {
+                  $tmp.= "<element>$element</element>";
+                }
+                $replace.= "<$entry>$tmp</$entry>";
+              } else {
+                $replace.= "<$entry>$value</$entry>";
+              }
+            }
+          }
+
+          # Add property information
+          if (defined $product->{'properties'}) {
+            $replace.= "<properties>";
+            while ((my $key, my $value) = each(%{$product->{'properties'}})){
+              $replace.= "<$key>";
+
+              while ((my $pkey, my $pvalue) = each(%$value)){
+                if(ref($pvalue) eq 'ARRAY'){
+                  my $tmp= "";
+                  foreach my $element (@$pvalue) {
+                    $tmp.= "<element>$element</element>";
+                  }
+                  $replace.= "<$pkey>$tmp</$pkey>";
+                } else {
+                  $replace.= "<$pkey>$pvalue</$pkey>";
+                }
+              }
+              $replace.= "</$key>";
+            }
+            $replace.= "</properties>";
+          }
+
+          # Close item
+          $replace.= "</item>";
+        }
+
+        $xml_msg=~ s/<xxx><\/xxx>/\n$replace/;
+
+       &main::daemon_log("0 DEBUG: time to process gosa-si message '$header' : ".sprintf("%.4f", (Time::HiRes::time - $startTime))." seconds", 1034);
+    return ( $xml_msg );
+}
+
+
 sub opsi_test {
     my ($msg, $msg_hash, $session_id) = @_;
     my $header = @{$msg_hash->{'header'}}[0];
     my $source = @{$msg_hash->{'source'}}[0];
        my $pram1 = @{$msg_hash->{'productId'}}[0];
 
-print STDERR Dumper $pram1;
 
        # Fetch infos from Opsi server
     my $callobj = {
@@ -2438,32 +2439,71 @@ print STDERR Dumper $pram1;
     };
     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
 
-       print STDERR Dumper $res;
        return ();
 }
 
 
+# ----------------------------------------------------------------------------
+#  internal methods handling the comunication with Opsi
+# ----------------------------------------------------------------------------
+
+################################
+# @brief Checks if there is a specified tag and if the the tag has a content.
+sub _check_xml_tag_is_ok {
+       my ($msg_hash,$tag) = @_;
+       if (not defined $msg_hash->{$tag}) {
+               $_ = "message contains no tag '$tag'";
+               return 0;
+       }
+       if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
+               $_ = "message tag '$tag' has no content";
+               return  0;
+       }
+       return 1;
+}
+
+################################
+# @brief Writes the log line and returns the error message for GOsa.
+sub _giveErrorFeedback {
+       my ($msg_hash, $err_string, $session_id) = @_;
+       &main::daemon_log("$session_id ERROR: $err_string", 1);
+       my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
+    if (exists $msg_hash->{forward_to_gosa}) {
+        &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
+    }
+       return ( &create_xml_string($out_hash) );
+}
+
+
+################################
+# @brief Perform the call to the Opsi server and measure the time for the call
+sub _callOpsi {
+       my %arg = ('method'=>undef, 'params'=>[], 'id'=>1, @_);
+
+       my $callObject = {
+               method => $arg{method},
+               params => $arg{params},
+               id => $arg{id},
+       };
+
+       my $startTime = Time::HiRes::time;
+       my $opsiResult = $opsi_client->call($opsi_url, $callObject);
+       my $endTime = Time::HiRes::time;
+       my $elapsedTime = sprintf("%.4f", ($endTime - $startTime));
 
+       &main::daemon_log("0 DEBUG: time to process opsi call '$arg{method}' : $elapsedTime seconds", 1034); 
+
+       return $opsiResult;
+}
 
 sub _getLicensePool_hash {
-       my %arg = (
-               'licensePoolId' => undef,
-               @_,
-       );
+       my %arg = ( 'licensePoolId' => undef, @_ );
 
        if (not defined $arg{licensePoolId} ) { 
                return ("function requires licensePoolId as parameter", 1);
        }
 
-       # Fetch pool infos from Opsi server
-    my $callobj = {
-        method  => 'getLicensePool_hash',
-        params  => [ $arg{licensePoolId} ],
-        id  => 1,
-    };
-    my $res = $main::opsi_client->call($main::opsi_url, $callobj);
-
-       # Check Opsi error
+       my $res = &_callOpsi( method  => 'getLicensePool_hash', params =>[$arg{licensePoolId}], id  => 1 );
        my ($res_error, $res_error_str) = &check_opsi_res($res);
        if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
 
@@ -2471,15 +2511,8 @@ sub _getLicensePool_hash {
 }
 
 sub _getSoftwareLicenses_listOfHashes {
-       # Fetch licenses associated to the given pool
-       my $callobj = {
-               method  => 'getSoftwareLicenses_listOfHashes',
-               params  => [ ],
-               id  => 1,
-       };
-       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
-
-       # Check Opsi error
+       
+       my $res = &_callOpsi( method  => 'getSoftwareLicenses_listOfHashes' );
        my ($res_error, $res_error_str) = &check_opsi_res($res);
        if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
 
@@ -2487,21 +2520,9 @@ sub _getSoftwareLicenses_listOfHashes {
 }
 
 sub _getSoftwareLicenseUsages_listOfHashes {
-       my %arg = (
-                       'hostId' => "",
-                       'licensePoolId' => "",
-                       @_,
-                       );
-
-       # Fetch pool infos from Opsi server
-       my $callobj = {
-               method  => 'getSoftwareLicenseUsages_listOfHashes',
-               params  => [ $arg{hostId}, $arg{licensePoolId} ],
-               id  => 1,
-       };
-       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
+       my %arg = ( 'hostId' => "", 'licensePoolId' => "", @_ );
 
-       # Check Opsi error
+       my $res = &_callOpsi( method=>'getSoftwareLicenseUsages_listOfHashes', params=>[ $arg{hostId}, $arg{licensePoolId} ] );
        my ($res_error, $res_error_str) = &check_opsi_res($res);
        if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
 
@@ -2509,11 +2530,7 @@ sub _getSoftwareLicenseUsages_listOfHashes {
 }
 
 sub _removeSoftwareLicenseFromLicensePool {
-       my %arg = (
-               'softwareLicenseId' => undef,
-               'licensePoolId' => undef,
-               @_,
-               );
+       my %arg = ( 'softwareLicenseId' => undef, 'licensePoolId' => undef, @_ );
 
        if (not defined $arg{softwareLicenseId} ) { 
                return ("function requires softwareLicenseId as parameter", 1);
@@ -2522,15 +2539,7 @@ sub _removeSoftwareLicenseFromLicensePool {
                return ("function requires licensePoolId as parameter", 1);
        }
 
-       # Remove software license from license pool
-       my $callobj = {
-               method  => 'removeSoftwareLicenseFromLicensePool',
-               params  => [ $arg{softwareLicenseId}, $arg{licensePoolId} ],
-               id  => 1,
-       };
-       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
-
-       # Check Opsi error
+       my $res = &_callOpsi( method=>'removeSoftwareLicenseFromLicensePool', params=>[ $arg{softwareLicenseId}, $arg{licensePoolId} ] );
        my ($res_error, $res_error_str) = &check_opsi_res($res);
        if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
 
@@ -2538,11 +2547,7 @@ sub _removeSoftwareLicenseFromLicensePool {
 }
 
 sub _deleteSoftwareLicense {
-       my %arg = (
-               'softwareLicenseId' => undef,
-               'removeFromPools' => "false",
-               @_,
-               );
+       my %arg = ( 'softwareLicenseId' => undef, 'removeFromPools' => "false", @_ );
 
        if (not defined $arg{softwareLicenseId} ) { 
                return ("function requires softwareLicenseId as parameter", 1);
@@ -2552,15 +2557,7 @@ sub _deleteSoftwareLicense {
                $removeFromPools = "removeFromPools";
        }
 
-       # Fetch
-       my $callobj = {
-               method  => 'deleteSoftwareLicense',
-               params  => [ $arg{softwareLicenseId}, $removeFromPools ],
-               id  => 1,
-       };
-       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
-
-       # Check Opsi error
+       my $res = &_callOpsi( method=>'deleteSoftwareLicense', params=>[ $arg{softwareLicenseId}, $removeFromPools ] );
        my ($res_error, $res_error_str) = &check_opsi_res($res);
        if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
 
@@ -2568,23 +2565,13 @@ sub _deleteSoftwareLicense {
 }
 
 sub _getLicensePoolId {
-       my %arg = (
-                       'productId' => undef,
-                       @_,
-                       );
+       my %arg = ( 'productId' => undef, @_ );
        
        if (not defined $arg{productId} ) {
                return ("function requires productId as parameter", 1);
        }
 
-    my $callobj = {
-        method  => 'getLicensePoolId',
-        params  => [ $arg{productId} ],
-        id  => 1,
-    };
-    my $res = $main::opsi_client->call($main::opsi_url, $callobj);
-
-       # Check Opsi error
+    my $res = &_callOpsi( method  => 'getLicensePoolId', params  => [ $arg{productId} ] );
        my ($res_error, $res_error_str) = &check_opsi_res($res);
        if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
 
@@ -2592,23 +2579,13 @@ sub _getLicensePoolId {
 }
 
 sub _getLicenseContract_hash {
-       my %arg = (
-                       'licenseContractId' => undef,
-                       @_,
-                       );
+       my %arg = ( 'licenseContractId' => undef, @_ );
        
        if (not defined $arg{licenseContractId} ) {
                return ("function requires licenseContractId as parameter", 1);
        }
 
-    my $callobj = {
-        method  => 'getLicenseContract_hash',
-        params  => [ $arg{licenseContractId} ],
-        id  => 1,
-    };
-    my $res = $main::opsi_client->call($main::opsi_url, $callobj);
-
-       # Check Opsi error
+    my $res = &_callOpsi( method  => 'getLicenseContract_hash', params  => [ $arg{licenseContractId} ] );
        my ($res_error, $res_error_str) = &check_opsi_res($res);
        if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
 
@@ -2623,18 +2600,11 @@ sub _createLicenseContract {
                        'notificationDate' => undef,
                        'expirationDate' => undef,
                        'notes' => undef,
-                       @_,
-                       );
+                       @_ );
 
-       # Create license contract at Opsi server
-    my $callobj = {
-        method  => 'createLicenseContract',
-        params  => [ $arg{licenseContractId}, $arg{partner}, $arg{conclusionDate}, $arg{notificationDate}, $arg{expirationDate}, $arg{notes} ],
-        id  => 1,
-    };
-    my $res = $main::opsi_client->call($main::opsi_url, $callobj);
-
-       # Check Opsi error
+       my $res = &_callOpsi( method  => 'createLicenseContract', 
+                       params  => [ $arg{licenseContractId}, $arg{partner}, $arg{conclusionDate}, $arg{notificationDate}, $arg{expirationDate}, $arg{notes} ],
+                       );
        my ($res_error, $res_error_str) = &check_opsi_res($res);
        if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
 
@@ -2649,17 +2619,11 @@ sub _createSoftwareLicense {
                        'maxInstallations' => undef,
                        'boundToHost' => undef,
                        'expirationDate' => undef,
-                       @_,
-                       );
+                       @_ );
 
-    my $callobj = {
-        method  => 'createSoftwareLicense',
+    my $res = &_callOpsi( method  => 'createSoftwareLicense',
         params  => [ $arg{softwareLicenseId}, $arg{licenseContractId}, $arg{licenseType}, $arg{maxInstallations}, $arg{boundToHost}, $arg{expirationDate} ],
-        id  => 1,
-    };
-    my $res = $main::opsi_client->call($main::opsi_url, $callobj);
-
-       # Check Opsi error
+               );
        my ($res_error, $res_error_str) = &check_opsi_res($res);
        if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
 
@@ -2671,8 +2635,7 @@ sub _addSoftwareLicenseToLicensePool {
             'softwareLicenseId' => undef,
             'licensePoolId' => undef,
             'licenseKey' => undef,
-            @_,
-            );
+            @_ );
 
        if (not defined $arg{softwareLicenseId} ) {
                return ("function requires softwareLicenseId as parameter", 1);
@@ -2681,19 +2644,35 @@ sub _addSoftwareLicenseToLicensePool {
                return ("function requires licensePoolId as parameter", 1);
        }
 
-       # Add software license to license pool
-       my $callobj = {
-        method  => 'addSoftwareLicenseToLicensePool',
-        params  => [ $arg{softwareLicenseId}, $arg{licensePoolId}, $arg{licenseKey} ],
-        id  => 1,
-    };
-    my $res = $main::opsi_client->call($main::opsi_url, $callobj);
+       my $res = &_callOpsi( method  => 'addSoftwareLicenseToLicensePool', params  => [ $arg{softwareLicenseId}, $arg{licensePoolId}, $arg{licenseKey} ] );
+       my ($res_error, $res_error_str) = &check_opsi_res($res);
+       if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
 
-       # Check Opsi error
+       return ($res->result, 0);
+}
+
+sub _getProductStates_hash {
+       my %arg = (     'hostId' => undef, @_ );
+
+       if (not defined $arg{hostId} ) {
+               return ("function requires hostId as parameter", 1);
+       }
+
+       my $res = &_callOpsi( method => 'getProductStates_hash', params => [$arg{hostId}]);
        my ($res_error, $res_error_str) = &check_opsi_res($res);
        if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
 
        return ($res->result, 0);
 }
 
+sub _get_full_product_host_information {
+       my %arg = ( 'hostId' => undef, @_ );
+
+       my $res = &_callOpsi( method => 'getFullProductHostInformation_list',  params => [$arg{hostId}]);
+       my ($res_error, $res_error_str) = &check_opsi_res($res);
+       if ($res_error){ return ((caller(0))[3]." : ".$res_error_str, 1); }
+
+       return ($res->result, 0);
+}
+
 1;