Code

Updated some sieve templates
[gosa.git] / gosa-si / server / events / opsi_com.pm
index 105c9b450c2b2885e6cef37c8ab7f09d66f80731..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,41 +32,87 @@ 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",
    );
-@EXPORT = @events;
 
-use strict;
-use warnings;
-use GOSA::GosaSupportDaemon;
-use Data::Dumper;
-use XML::Quote qw(:all);
+our @EXPORT = @events;
 
 
 BEGIN {}
 
 END {}
 
-## @method get_events()
-# A brief function returning a list of functions which are exported by importing the module.
+# ----------------------------------------------------------------------------
+#                          D E C L A R A T I O N S
+# ----------------------------------------------------------------------------
+
+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";
+       }
+}
+
+# ----------------------------------------------------------------------------
+#   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;
 }
 
-## @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];
     my $target = @{$msg_hash->{'target'}}[0];
     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
-    my ($hostId, $productId);
-    my $error = 0;
 
     # Build return message
     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
@@ -64,54 +121,42 @@ sub opsi_add_product_to_client {
     }
 
     # Sanity check of needed parameter
-    if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1))  {
-        $error++;
-        &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
-        &add_content2xml_hash($out_hash, "error", "hostId");
-        &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
-
+    if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
+               return &_giveErrorFeedback($msg_hash, "no hostId specified or hostId tag invalid", $session_id);
     }
-    if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1)) {
-        $error++;
-        &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
-        &add_content2xml_hash($out_hash, "error", "productId");
-        &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1); 
+    if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
+               return &_giveErrorFeedback($msg_hash, "no productId specified or productId tag invalid", $session_id);
     }
 
-    if (not $error) {
-        # Get hostID
-        $hostId = @{$msg_hash->{'hostId'}}[0];
-        &add_content2xml_hash($out_hash, "hostId", $hostId);
+       # Get hostId
+       my $hostId = @{$msg_hash->{'hostId'}}[0];
+       &add_content2xml_hash($out_hash, "hostId", $hostId);
 
-        # Get productID
-        $productId = @{$msg_hash->{'productId'}}[0];
-        &add_content2xml_hash($out_hash, "productId", $productId);
+       # Get productID
+       my $productId = @{$msg_hash->{'productId'}}[0];
+       &add_content2xml_hash($out_hash, "productId", $productId);
 
-        # Do an action request for all these -> "setup".
-        my $callobj = {
-            method  => 'setProductActionRequest',
-            params  => [ $productId, $hostId, "setup" ],
-            id  => 1, }; 
+       # Do an action request for all these -> "setup".
+       my $callobj = {
+               method  => 'setProductActionRequest',
+               params  => [ $productId, $hostId, "setup" ],
+               id  => 1, }; 
+       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
 
-        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("$session_id ERROR: cannot add product: ".$sres_err_string, 1);
-            &add_content2xml_hash($out_hash, "error", $sres_err_string);
-        }
-    } 
+       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];
@@ -128,14 +173,14 @@ sub opsi_del_product_from_client {
     }
 
     # Sanity check of needed parameter
-    if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1))  {
+    if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
         $error++;
         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
         &add_content2xml_hash($out_hash, "error", "hostId");
         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
 
     }
-    if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1)) {
+    if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
         $error++;
         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
         &add_content2xml_hash($out_hash, "error", "productId");
@@ -144,7 +189,7 @@ sub opsi_del_product_from_client {
 
     # All parameter available
     if (not $error) {
-        # Get hostID
+        # Get hostId
         $hostId = @{$msg_hash->{'hostId'}}[0];
         &add_content2xml_hash($out_hash, "hostId", $hostId);
 
@@ -152,23 +197,7 @@ sub opsi_del_product_from_client {
         $productId = @{$msg_hash->{'productId'}}[0];
         &add_content2xml_hash($out_hash, "productId", $productId);
 
-
-        #TODO: 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 
+        # Check to get product action list 
         my $callobj = {
             method  => 'getPossibleProductActions_list',
             params  => [ $productId ],
@@ -214,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];
@@ -240,13 +271,13 @@ sub opsi_add_client {
     }
 
     # Sanity check of needed parameter
-    if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1))  {
+    if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
         $error++;
         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
         &add_content2xml_hash($out_hash, "error", "hostId");
         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
     }
-    if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1))  {
+    if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH'))  {
         $error++;
         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
         &add_content2xml_hash($out_hash, "error", "macaddress");
@@ -254,7 +285,7 @@ sub opsi_add_client {
     }
 
     if (not $error) {
-        # Get hostID
+        # Get hostId
         $hostId = @{$msg_hash->{'hostId'}}[0];
         &add_content2xml_hash($out_hash, "hostId", $hostId);
 
@@ -296,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];
@@ -322,7 +355,7 @@ sub opsi_modify_client {
     }
 
     # Sanity check of needed parameter
-    if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1))  {
+    if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
         $error++;
         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
         &add_content2xml_hash($out_hash, "error", "hostId");
@@ -330,7 +363,7 @@ sub opsi_modify_client {
     }
 
     if (not $error) {
-        # Get hostID
+        # Get hostId
         $hostId = @{$msg_hash->{'hostId'}}[0];
         &add_content2xml_hash($out_hash, "hostId", $hostId);
         my $name= $hostId;
@@ -386,135 +419,91 @@ 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
+    # 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);
     }
 
-    &add_content2xml_hash($out_hash, "xxx", "");
-    $xml_msg= &create_xml_string($out_hash);
+    # 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  => 'getNetBootProductIds_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;
-                    }
+    # Check if we need to get host or global information
+    if (defined $hostId){
+      $callobj = {
+          method  => 'getProductHostInformation_list',
+          params  => [ $hostId, undef, 'netboot'],
+          id  => 1,
+      };
 
-                    # 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><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
-                        }
-                    }
-                }
+      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 {
 
-        } 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><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];
@@ -530,7 +519,7 @@ sub opsi_get_product_properties {
     }
 
     # Sanity check of needed parameter
-    if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1))  {
+    if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
         &add_content2xml_hash($out_hash, "error", "productId");
         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
@@ -543,7 +532,7 @@ sub opsi_get_product_properties {
     $productId = @{$msg_hash->{'productId'}}[0];
     &add_content2xml_hash($out_hash, "producId", "$productId");
 
-    # Get hostID if defined
+    # Get hostId if defined
     if (defined @{$msg_hash->{'hostId'}}[0]){
       $hostId = @{$msg_hash->{'hostId'}}[0];
       &add_content2xml_hash($out_hash, "hostId", $hostId);
@@ -582,20 +571,51 @@ sub opsi_get_product_properties {
           id  => 1,
       };
     }
-
     $res = $main::opsi_client->call($main::opsi_url, $callobj);
+
+    # JSON Query 2
+    $callobj = {
+      method  => 'getProductPropertyDefinitions_listOfHashes',
+      params  => [ $productId ],
+      id  => 1,
+    };
+
+    # Assemble options
+    my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
+    my $values = {};
+    my $descriptions = {};
+    if (not &check_opsi_res($res2)){
+        my $r= $res2->result;
+
+          foreach my $entr (@$r){
+            # Unroll values
+            my $cnv;
+            if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
+              foreach my $v (@{$entr->{'values'}}){
+                $cnv.= "<value>$v</value>";
+              }
+            } else {
+              $cnv= $entr->{'values'};
+            }
+            $values->{$entr->{'name'}}= $cnv;
+            $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
+          }
+    }
+
     if (not &check_opsi_res($res)){
         my $r= $res->result;
         foreach my $key (keys %{$r}) {
             my $item= "\n<item>";
             my $value= $r->{$key};
-            if (UNIVERSAL::isa( $value, "ARRAY" )){
-                foreach my $subval (@{$value}){
-                    $item.= "<$key>".xml_quote($subval)."</$key>";
-                }
-            } else {
-                $item.= "<$key>".xml_quote($value)."</$key>";
+            my $dsc= "";
+            my $vals= "";
+            if (defined $descriptions->{$key}){
+              $dsc= $descriptions->{$key};
+            }
+            if (defined $values->{$key}){
+              $vals= $values->{$key};
             }
+            $item.= "<$key>$dsc<current>".xml_quote($value)."</current>$vals</$key>";
             $item.= "</item>";
             $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
         }
@@ -604,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];
@@ -629,7 +650,7 @@ sub opsi_set_product_properties {
     }
 
     # Sanity check of needed parameter
-    if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1))  {
+    if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
         &add_content2xml_hash($out_hash, "error", "productId");
         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
@@ -654,7 +675,8 @@ sub opsi_set_product_properties {
             return ( &create_xml_string($out_hash) );
         }
     }
-    if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1))  {
+    # if no hostId is given, set_product_properties will act on globally
+    if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1))  {
         &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); 
@@ -666,7 +688,7 @@ sub opsi_set_product_properties {
     $productId =  @{$msg_hash->{'productId'}}[0];
     &add_content2xml_hash($out_hash, "productId", $productId);
 
-    # Get hostID if defined
+    # Get hostId if defined
     if (exists $msg_hash->{'hostId'}){
         $hostId = @{$msg_hash->{'hostId'}}[0];
         &add_content2xml_hash($out_hash, "hostId", $hostId);
@@ -703,24 +725,25 @@ sub opsi_set_product_properties {
         my ($res_err, $res_err_string) = &check_opsi_res($res);
 
         if ($res_err){
-            &man::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
+            &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
             &add_content2xml_hash($out_hash, "error", $res_err_string);
         }
     }
 
 
     # 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];
@@ -730,77 +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))  {
-        $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) {
-
-    # 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;
-            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>//;
-
-    }
+       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>%;
+
+                       }
+               }
+       }
+
+       $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];
@@ -819,57 +830,44 @@ 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= "<item><name>".$host->{'hostId'}."</name>";
+            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>";
             }
             if (defined($host->{'notes'})){
                 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
             }
-
-            $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>";
+            if (defined($host->{'lastSeen'})){
+                $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
             }
 
-            $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];
@@ -886,7 +884,7 @@ sub opsi_get_client_software {
     }
 
     # Sanity check of needed parameter
-    if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1))  {
+    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");
@@ -895,7 +893,7 @@ sub opsi_get_client_software {
 
     if (not $error) {
 
-    # Get hostID
+    # Get hostId
         $hostId = @{$msg_hash->{'hostId'}}[0];
         &add_content2xml_hash($out_hash, "hostId", "$hostId");
         &add_content2xml_hash($out_hash, "xxx", "");
@@ -915,7 +913,6 @@ sub opsi_get_client_software {
         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
         if (not &check_opsi_res($res)){
             my $result= $res->result;
-# TODO : Ist das hier schon fertig???   
         }
 
         $xml_msg=~ s/<xxx><\/xxx>//;
@@ -923,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];
@@ -948,113 +946,66 @@ sub opsi_get_local_products {
     }
     &add_content2xml_hash($out_hash, "xxx", "");
 
-    # Get hostID if defined
+    # 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);
-
-    # 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><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><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];
@@ -1070,7 +1021,7 @@ sub opsi_del_client {
     }
 
     # Sanity check of needed parameter
-    if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1))  {
+    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");
@@ -1079,7 +1030,7 @@ sub opsi_del_client {
 
     if (not $error) {
 
-    # Get hostID
+    # Get hostId
         $hostId = @{$msg_hash->{'hostId'}}[0];
         &add_content2xml_hash($out_hash, "hostId", "$hostId");
 
@@ -1096,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;
 
@@ -1126,13 +1075,13 @@ sub opsi_install_client {
     }
 
     # Sanity check of needed parameter
-    if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1))  {
+    if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
         $error++;
         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
         &add_content2xml_hash($out_hash, "error", "hostId");
         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
     }
-    if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1))  {
+    if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') )  {
         $error++;
         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
         &add_content2xml_hash($out_hash, "error", "macaddress");
@@ -1194,7 +1143,7 @@ sub opsi_install_client {
     # Build wakeup message for client
         if (not $error) {
             my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
-            &add_content2xml_hash($wakeup_hash, 'macAddress', $macaddress);
+            &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
             my $wakeup_msg = &create_xml_string($wakeup_hash);
             push(@out_msg_l, $wakeup_msg);
 
@@ -1204,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
@@ -1228,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
@@ -1248,4 +1197,1482 @@ sub _set_state {
   $main::opsi_client->call($main::opsi_url, $callobj);
 }
 
+################################
+# @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];
+    my $target = @{$msg_hash->{'target'}}[0];
+       my $out_hash;
+       my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
+       my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
+       my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
+       my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
+
+       # Create license Pool
+    my $callobj = {
+        method  => 'createLicensePool',
+        params  => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
+        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){
+               # Create error message
+               &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
+               $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
+               return ( &create_xml_string($out_hash) );
+       }
+
+       # 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];
+       my $out_hash;
+
+       # Fetch infos from Opsi server
+    my $callobj = {
+        method  => 'getLicensePools_listOfHashes',
+        params  => [ ],
+        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){
+               # Create error message
+               &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
+               $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
+               return ( &create_xml_string($out_hash) );
+       }
+
+       # Create function result message
+       my $res_hash = { 'hit'=> [] };
+       foreach my $licensePool ( @{$res->result}) {
+               my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
+                       'description' => [$licensePool->{'description'}],
+                       'productIds' => $licensePool->{'productIds'},
+                       'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
+                       };
+               push( @{$res_hash->{hit}}, $licensePool_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]); }
+       $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];
+    my $target = @{$msg_hash->{'target'}}[0];
+    my $licensePoolId;
+       my $out_hash;
+
+       # Check input sanity
+       if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
+               $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, "", $session_id, $_);
+       }
+
+       # Fetch infos from Opsi server
+    my $callobj = {
+        method  => 'getLicensePool_hash',
+        params  => [ $licensePoolId ],
+        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){
+               # Create error message
+               &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
+               $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
+               &add_content2xml_hash($out_hash, "error", $res_error_str);
+               return ( &create_xml_string($out_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) );
+}
+
+sub _parse_getSoftwareLicenseUsages {
+       my $res = shift;
+
+       # Parse Opsi result
+       my $tmp_licensePool_cache = {};
+       my $res_hash = { 'hit'=> [] };
+       foreach my $license ( @{$res}) {
+               my $tmp_licensePool = $license->{'licensePoolId'};
+               if (not exists $tmp_licensePool_cache->{$tmp_licensePool}) {
+                       # Fetch missing informations from Opsi and cache the results for a possible later usage
+                       my ($res, $err) = &_getLicensePool_hash('licensePoolId'=>$tmp_licensePool);
+                       if (not $err) {
+                               $tmp_licensePool_cache->{$tmp_licensePool} = $res;
+                       }
+               }
+               my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
+                       'notes' => [$license->{'notes'}],
+                       'licenseKey' => [$license->{'licenseKey'}],
+                       'hostId' => [$license->{'hostId'}],
+                       'licensePoolId' => [$tmp_licensePool],
+                       };
+               if (exists $tmp_licensePool_cache->{$tmp_licensePool}) {
+                       $license_hash->{$tmp_licensePool} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
+                       map (push (@{$license_hash->{$tmp_licensePool}->{productIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{productIds}});
+                       map (push (@{$license_hash->{$tmp_licensePool}->{windowsSoftwareIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{windowsSoftwareIds}});
+               }
+               push( @{$res_hash->{hit}}, $license_hash );
+       }
+
+       return $res_hash;
+}
+
+################################
+# @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];
+       my $target = @{$msg_hash->{'target'}}[0];
+       my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
+       my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
+       my $out_hash;
+
+       my ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId, 'hostId'=>$hostId);
+       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
+       $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];
+
+       # Check input sanity
+       my $productId;
+       if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
+               $productId= @{$msg_hash->{'productId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+
+       # Fetch licensePoolId for productId
+       my ($res, $err) = &_getLicensePoolId('productId'=>$productId);
+       if ($err){
+                my $out_hash = &create_xml_hash("answer_$header", $main::server_address, $source);
+                $out_hash->{result} = [];
+               return ( &create_xml_string($out_hash) );
+       }
+       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 = &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];
+       my $target = @{$msg_hash->{'target'}}[0];
+       my $softwareLicenseId;
+       my $out_hash;
+
+       # Check input sanity
+       if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
+               $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+
+       my $callobj = {
+               method  => 'getSoftwareLicense_hash',
+               params  => [ $softwareLicenseId ],
+               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){
+               # Create error message
+               &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
+               $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
+               return ( &create_xml_string($out_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'});
+       &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
+       foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
+               &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
+               &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];
+    my $target = @{$msg_hash->{'target'}}[0];
+    my $licensePoolId;
+       my $out_hash;
+
+       # Check input sanity
+       if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
+               $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+
+       # Fetch softwareLicenseIds used in license pool
+       # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
+       # but not the license contracts of the software licenses. In our case each software license has exactly one license contract. 
+       my $callobj = {
+               method  => 'getSoftwareLicenses_listOfHashes',
+               params  => [ ],
+               id  => 1,
+       };
+       my $res = $main::opsi_client->call($main::opsi_url, $callobj);
+
+       # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
+       my @lCI_toBeDeleted;
+       foreach my $softwareLicenseHash ( @{$res->result} ) {
+               if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) { 
+                       next; 
+               }  
+               push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
+       }
+
+       # Delete license pool at Opsi server
+    $callobj = {
+        method  => 'deleteLicensePool',
+        params  => [ $licensePoolId, 'deleteLicenses=True'  ],
+        id  => 1,
+    };
+    $res = $main::opsi_client->call($main::opsi_url, $callobj);
+       my ($res_error, $res_error_str) = &check_opsi_res($res);
+       if ($res_error){
+               # Create error message
+               &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
+               $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
+               return ( &create_xml_string($out_hash) );
+       } 
+
+       # Delete each license contract connected with the license pool
+       foreach my $licenseContractId ( @lCI_toBeDeleted ) {
+               my $callobj = {
+                       method  => 'deleteLicenseContract',
+                       params  => [ $licenseContractId ],
+                       id  => 1,
+               };
+               my $res = $main::opsi_client->call($main::opsi_url, $callobj);
+               my ($res_error, $res_error_str) = &check_opsi_res($res);
+               if ($res_error){
+                       # Create error message
+                       &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
+                       $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
+                       return ( &create_xml_string($out_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]); }
+
+       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.
+# @param partner Name of the license partner (optional).
+# @param conclusionDate Date of conclusion of license contract (optional)
+# @param notificationDate Date of notification that license is running out soon (optional).
+# @param notes This is the place for some notes (optional)
+# @param softwareLicenseId Identificator of a license (optional).
+# @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
+# @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];
+    my $target = @{$msg_hash->{'target'}}[0];
+       my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
+       my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
+       my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
+       my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
+       my $licenseContractId = undef;
+       my $softwareLicenseId = defined $msg_hash->{'softwareLicenseId'} ? @{$msg_hash->{'softwareLicenseId'}}[0] : undef;
+       my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
+       my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
+       my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
+       my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
+       my $licensePoolId;
+       my $licenseKey;
+       my $out_hash;
+
+       # Check input sanity
+       if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
+               $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+       if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
+               $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+       if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
+               return &_giveErrorFeedback($msg_hash, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'.", $session_id);
+       }
+       
+       # Automatically define licenseContractId if ID is not given
+       if (defined $softwareLicenseId) { 
+               $licenseContractId = "c_".$softwareLicenseId;
+       }
+
+       # Create license contract at Opsi server
+    my $callobj = {
+        method  => 'createLicenseContract',
+        params  => [ $licenseContractId, $partner, $conclusionDate, $notificationDate, undef, $notes ],
+        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){
+               # Create error message
+               &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
+               $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
+               return ( &create_xml_string($out_hash) );
+       }
+       
+       $licenseContractId = $res->result;
+
+       # Create software license at Opsi server
+    $callobj = {
+        method  => 'createSoftwareLicense',
+        params  => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
+        id  => 1,
+    };
+    $res = $main::opsi_client->call($main::opsi_url, $callobj);
+
+       # Check Opsi error
+       ($res_error, $res_error_str) = &check_opsi_res($res);
+       if ($res_error){
+               # Create error message
+               &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
+               $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
+               return ( &create_xml_string($out_hash) );
+       }
+
+       $softwareLicenseId = $res->result;
+
+       # Add software license to license pool
+       $callobj = {
+        method  => 'addSoftwareLicenseToLicensePool',
+        params  => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
+        id  => 1,
+    };
+    $res = $main::opsi_client->call($main::opsi_url, $callobj);
+
+       # Check Opsi error
+       ($res_error, $res_error_str) = &check_opsi_res($res);
+       if ($res_error){
+               # Create error message
+               &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
+               $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
+               return ( &create_xml_string($out_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]); }
+       
+       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];
+    my $target = @{$msg_hash->{'target'}}[0];
+       my $hostId;
+       my $licensePoolId;
+
+       # Check input sanity
+       if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
+               $hostId = @{$msg_hash->{'hostId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+       if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
+               $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+
+       # Assign a software license to a host
+       my $callobj = {
+        method  => 'getAndAssignSoftwareLicenseKey',
+        params  => [ $hostId, $licensePoolId ],
+        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){
+               # Create error message
+               &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
+               my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
+               return ( &create_xml_string($out_hash) );
+       }
+
+       # 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];
+    my $target = @{$msg_hash->{'target'}}[0];
+       my $hostId;
+       my $licensePoolId;
+
+       # Check input sanity
+       if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
+               $hostId = @{$msg_hash->{'hostId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+       if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
+               $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+
+       # Unassign a software license from a host
+       my $callobj = {
+        method  => 'deleteSoftwareLicenseUsage',
+        params  => [ $hostId, '', $licensePoolId ],
+        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){
+               # Create error message
+               &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
+               my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
+               return ( &create_xml_string($out_hash) );
+       }
+
+       # 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];
+    my $target = @{$msg_hash->{'target'}}[0];
+       my $hostId;
+
+       # Check input sanity
+       if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
+               $hostId = @{$msg_hash->{'hostId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+
+       # Unassign all software licenses from a host
+       my $callobj = {
+        method  => 'deleteAllSoftwareLicenseUsages',
+        params  => [ $hostId ],
+        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){
+               # Create error message
+               &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
+               my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
+               return ( &create_xml_string($out_hash) );
+       }
+
+       # 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];
+       my $productId;
+       my $out_hash;
+
+       # Check input sanity
+       if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
+               $productId = @{$msg_hash->{'productId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+
+       # Fetch infos from Opsi server
+    my $callobj = {
+        method  => 'getLicensePoolId',
+        params  => [ $productId ],
+        id  => 1,
+    };
+    #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);
+       if ($res_error){
+               return &_giveErrorFeedback($msg_hash, "cannot get license pool for product '$productId' : ".$res_error_str, $session_id);
+       } 
+       
+       my $licensePoolId = $res->result;
+
+       # Fetch statistic information for given pool ID
+       $callobj = {
+               method  => 'getLicenseStatistics_hash',
+               params  => [ ],
+               id  => 1,
+       };
+       $res = $opsi_client->call($opsi_url, $callobj);
+
+       # Check Opsi error
+       ($res_error, $res_error_str) = &check_opsi_res($res);
+       if ($res_error){
+               # Create error message
+               &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
+               $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
+               return ( &create_xml_string($out_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", $licensePoolId);
+       &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
+       &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
+       &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
+       &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 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];
+
+       # Check input sanity
+       my $licensePoolId;
+       if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
+               $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+
+       # 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 );
+       if ($err){
+               return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
+       }
+       # Add data to outgoing hash
+       &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
+       &add_content2xml_hash($out_hash, "description", $res->{'description'});
+       map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
+       map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
+
+
+       # Call Opsi two times
+       my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
+       if ($usages_err){
+               return &_giveErrorFeedback($msg_hash, "cannot get software license usage information from Opsi server: ".$usages_res, $session_id);
+       }
+       my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
+       if ($licenses_err){
+               return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
+       }
+
+       # Add data to outgoing hash
+       # Parse through all software licenses and select those associated to the pool
+       my $res_hash = { 'hit'=> [] };
+       foreach my $license ( @$licenses_res) {
+               # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
+               my $found = 0;
+               my @licensePoolIds_list = @{$license->{licensePoolIds}};
+               foreach my $lPI ( @licensePoolIds_list) {
+                       if ($lPI eq $licensePoolId) { $found++ }
+               }
+               if (not $found ) { next; };
+               # Found matching licensePoolId
+               my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
+                       'licenseKeys' => {},
+                       'expirationDate' => [$license->{'expirationDate'}],
+                       'boundToHost' => [$license->{'boundToHost'}],
+                       'maxInstallations' => [$license->{'maxInstallations'}],
+                       'licenseType' => [$license->{'licenseType'}],
+                       'licenseContractId' => [$license->{'licenseContractId'}],
+                       'licensePoolIds' => [],
+                       'hostIds' => [],
+                       };
+               foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
+                       push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
+                       $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
+               }
+               foreach my $usage (@$usages_res) {
+                       # Search for hostIds with matching softwareLicenseId
+                       if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
+                               push( @{ $license_hash->{hostIds}}, $usage->{hostId});
+                       }
+               }
+
+               # Each softwareLicenseId has one licenseContractId, fetch contract details for each licenseContractId
+               my ($lContract_res, $lContract_err) = &_getLicenseContract_hash('licenseContractId'=>$license->{licenseContractId});
+               if ($lContract_err){
+                       return &_giveErrorFeedback($msg_hash, "cannot get software license contract information from Opsi server: ".$licenses_res, $session_id);
+               }
+               $license_hash->{$license->{'licenseContractId'}} = [];
+               my $licenseContract_hash = { 'conclusionDate' => [$lContract_res->{conclusionDate}],
+                       'notificationDate' => [$lContract_res->{notificationDate}],
+                       'notes' => [$lContract_res->{notes}],
+                       'exirationDate' => [$lContract_res->{expirationDate}],
+                       'partner' => [$lContract_res->{partner}],
+               };
+               push( @{$license_hash->{licenseContractData}}, $licenseContract_hash );
+
+               push( @{$res_hash->{hit}}, $license_hash );
+       }
+       $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 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];
+
+       # Check input sanity
+       my $softwareLicenseId;
+       if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
+               $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+       my $licensePoolId;
+       if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
+               $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+       
+       # Call Opsi
+       my ($res, $err) = &_removeSoftwareLicenseFromLicensePool( 'licensePoolId' => $licensePoolId, 'softwareLicenseId' => $softwareLicenseId );
+       if ($err){
+               return &_giveErrorFeedback($msg_hash, "cannot delete software license from pool: ".$res, $session_id);
+       }
+
+       # Call Opsi
+       ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId'=>$softwareLicenseId );
+       if ($err){
+               return &_giveErrorFeedback($msg_hash, "cannot delete software license from Opsi server: ".$res, $session_id);
+       }
+
+       # 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 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];
+
+       # Check input sanity
+       my $hostId;
+       if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
+               $hostId = @{$msg_hash->{'hostId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+
+       # Fetch informations from Opsi server
+       my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
+       if ($license_err){
+               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) {
+               if ($license->{boundToHost} ne $hostId) { next; }
+
+               my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
+                       'maxInstallations' => [$license->{'maxInstallations'}],
+                       'boundToHost' => [$license->{'boundToHost'}],
+                       'expirationDate' => [$license->{'expirationDate'}],
+                       'licenseContractId' => [$license->{'licenseContractId'}],
+                       'licenseType' => [$license->{'licenseType'}],
+                       'licensePoolIds' => [],
+                       };
+               
+               foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
+                       # Fetch information for license pools containing a software license which is bound to given host
+                       my ($pool_res, $pool_err) = &_getLicensePool_hash( 'licensePoolId'=>$licensePoolId );
+                       if ($pool_err){
+                               return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$pool_res, $session_id);
+                       }
+
+                       # Add licensePool information to result hash
+                       push (@{$license_hash->{licensePoolIds}}, $licensePoolId);
+                       $license_hash->{$licensePoolId} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
+                       map (push (@{$license_hash->{$licensePoolId}->{productIds}}, $_), @{$pool_res->{productIds}});
+                       map (push (@{$license_hash->{$licensePoolId}->{windowsSoftwareIds}}, $_), @{$pool_res->{windowsSoftwareIds}});
+               }
+               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];
+
+       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 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];
+
+       # Check input sanity
+       my $hostId;
+       if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
+               $hostId = @{$msg_hash->{'hostId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+       my $softwareLicenseId;
+       if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
+               $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+
+       # Fetch informations from Opsi server
+       my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
+       if ($license_err){
+               return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
+       }
+
+       # Memorize parameter for given softwareLicenseId
+       my $licenseContractId;
+       my $licenseType;
+       my $maxInstallations;
+       my $boundToHost;
+       my $expirationDate = "";
+       my $found;
+       foreach my $license (@$license_res) {
+               if ($license->{softwareLicenseId} ne $softwareLicenseId) { next; }
+               $licenseContractId = $license->{licenseContractId};
+               $licenseType = $license->{licenseType};
+               $maxInstallations = $license->{maxInstallations};
+               $expirationDate = $license->{expirationDate};
+               $found++;
+       }
+
+       if (not $found) {
+               return &_giveErrorFeedback($msg_hash, "no softwarelicenseId found with name '".$softwareLicenseId."'", $session_id);
+       }
+
+       # Set boundToHost option for a given software license
+       my ($bound_res, $bound_err) = &_createSoftwareLicense('softwareLicenseId'=>$softwareLicenseId, 
+                       'licenseContractId' => $licenseContractId, 
+                       'licenseType' => $licenseType, 
+                       'maxInstallations' => $maxInstallations, 
+                       'boundToHost' => $hostId, 
+                       'expirationDate' => $expirationDate);
+       if ($bound_err) {
+               return &_giveErrorFeedback($msg_hash, "cannot set boundToHost for given softwareLicenseId and hostId: ".$bound_res, $session_id);
+       }
+
+       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 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];
+
+       # Check input sanity
+       my $softwareLicenseId;
+       if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
+               $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
+       } else {
+               return &_giveErrorFeedback($msg_hash, $_, $session_id);
+       }
+       
+       # Memorize parameter witch are required for this procedure
+       my $licenseContractId;
+       my $licenseType;
+       my $maxInstallations;
+       my $expirationDate;
+       my $partner;
+       my $conclusionDate;
+       my $notificationDate;
+       my $notes;
+       my $licensePoolId;
+       my $licenseKey;
+
+       # Fetch license informations from Opsi server
+       my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
+       if ($license_err){
+               return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
+       }
+       my $found = 0;
+       foreach my $license (@$license_res) {
+               if (($found > 0) || ($license->{softwareLicenseId} ne $softwareLicenseId)) { next; }
+               $licenseContractId = $license->{licenseContractId};
+               $licenseType = $license->{licenseType};
+               $maxInstallations = $license->{maxInstallations};
+               $expirationDate = $license->{expirationDate};
+               $licensePoolId = @{$license->{licensePoolIds}}[0];
+               $licenseKey = $license->{licenseKeys}->{$licensePoolId};
+               $found++;
+       }
+       
+       # Fetch contract informations from Opsi server
+       my ($contract_res, $contract_err) = &_getLicenseContract_hash('licenseContractId'=>$licenseContractId);
+       if ($contract_err){
+               return &_giveErrorFeedback($msg_hash, "cannot get contract license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
+       }
+       $partner = $contract_res->{partner};
+       $conclusionDate = $contract_res->{conclusionDate};
+       $notificationDate = $contract_res->{notificationDate};
+       $expirationDate = $contract_res->{expirationDate};
+       $notes = $contract_res->{notes};
+
+       # Delete software license
+       my ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'removeFromPools'=> "true" );
+       if ($err) {
+               return &_giveErrorFeedback($msg_hash, "cannot delet license from Opsi server, required to unbound license from host : ".$res, $session_id);
+       }
+
+       # Recreate software license without boundToHost
+       ($res, $err) = &_createLicenseContract( 'licenseContractId' => $licenseContractId, 'partner' => $partner, 'conclusionDate' => $conclusionDate, 
+                       'notificationDate' => $notificationDate, 'expirationDate' => $expirationDate, 'notes' => $notes );
+       if ($err) {
+               return &_giveErrorFeedback($msg_hash, "cannot create license contract at Opsi server, required to unbound license from host : ".$res, $session_id);
+       }
+       ($res, $err) = &_createSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'licenseContractId' => $licenseContractId, 'licenseType' => $licenseType, 
+                       'maxInstallations' => $maxInstallations, 'boundToHost' => "", 'expirationDate' => $expirationDate       );
+       if ($err) {
+               return &_giveErrorFeedback($msg_hash, "cannot create software license at Opsi server, required to unbound license from host : ".$res, $session_id);
+       }
+       ($res, $err) = &_addSoftwareLicenseToLicensePool( 'softwareLicenseId' => $softwareLicenseId, 'licensePoolId' => $licensePoolId, 'licenseKey' => $licenseKey );
+       if ($err) {
+               return &_giveErrorFeedback($msg_hash, "cannot add software license to license pool at Opsi server, required to unbound license from host : ".$res, $session_id);
+       }
+
+       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 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];
+
+       my ($res, $err) = &_getSoftwareLicenses_listOfHashes();
+       if ($err) {
+               return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from Opsi server : ".$res, $session_id);
+       }
+
+       # Parse result
+       my $res_hash = { 'hit'=> [] };
+       foreach my $license ( @$res) {
+               my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
+                       'maxInstallations' => [$license->{'maxInstallations'}],
+                       'boundToHost' => [$license->{'boundToHost'}],
+                       'expirationDate' => [$license->{'expirationDate'}],
+                       'licenseContractId' => [$license->{'licenseContractId'}],
+                       'licenseType' => [$license->{'licenseType'}],
+                       'licensePoolIds' => [],
+                       'licenseKeys'=> {}
+                       };
+               foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
+                       push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
+                       $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
+               }
+               push( @{$res_hash->{hit}}, $license_hash );
+       }
+       
+       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];
+
+
+       # Fetch infos from Opsi server
+    my $callobj = {
+        method  => 'getLicensePoolId',
+        params  => [ $pram1 ],
+        id  => 1,
+    };
+    my $res = $main::opsi_client->call($main::opsi_url, $callobj);
+
+       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, @_ );
+
+       if (not defined $arg{licensePoolId} ) { 
+               return ("function requires licensePoolId as parameter", 1);
+       }
+
+       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 ); }
+
+       return ($res->result, 0);
+}
+
+sub _getSoftwareLicenses_listOfHashes {
+       
+       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 ); }
+
+       return ($res->result, 0);
+}
+
+sub _getSoftwareLicenseUsages_listOfHashes {
+       my %arg = ( 'hostId' => "", 'licensePoolId' => "", @_ );
+
+       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 ); }
+
+       return ($res->result, 0);
+}
+
+sub _removeSoftwareLicenseFromLicensePool {
+       my %arg = ( 'softwareLicenseId' => undef, 'licensePoolId' => undef, @_ );
+
+       if (not defined $arg{softwareLicenseId} ) { 
+               return ("function requires softwareLicenseId as parameter", 1);
+               }
+               if (not defined $arg{licensePoolId} ) { 
+               return ("function requires licensePoolId as parameter", 1);
+       }
+
+       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 ); }
+
+       return ($res->result, 0);
+}
+
+sub _deleteSoftwareLicense {
+       my %arg = ( 'softwareLicenseId' => undef, 'removeFromPools' => "false", @_ );
+
+       if (not defined $arg{softwareLicenseId} ) { 
+               return ("function requires softwareLicenseId as parameter", 1);
+       }
+       my $removeFromPools = "";
+       if ((defined $arg{removeFromPools}) && ($arg{removeFromPools} eq "true")) { 
+               $removeFromPools = "removeFromPools";
+       }
+
+       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 ); }
+
+       return ($res->result, 0);
+}
+
+sub _getLicensePoolId {
+       my %arg = ( 'productId' => undef, @_ );
+       
+       if (not defined $arg{productId} ) {
+               return ("function requires productId as parameter", 1);
+       }
+
+    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 ); }
+
+       return ($res->result, 0);
+}
+
+sub _getLicenseContract_hash {
+       my %arg = ( 'licenseContractId' => undef, @_ );
+       
+       if (not defined $arg{licenseContractId} ) {
+               return ("function requires licenseContractId as parameter", 1);
+       }
+
+    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 ); }
+
+       return ($res->result, 0);
+}
+
+sub _createLicenseContract {
+       my %arg = (
+                       'licenseContractId' => undef,
+                       'partner' => undef,
+                       'conclusionDate' => undef,
+                       'notificationDate' => undef,
+                       'expirationDate' => undef,
+                       'notes' => undef,
+                       @_ );
+
+       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 ); }
+
+       return ($res->result, 0);
+}
+
+sub _createSoftwareLicense {
+       my %arg = (
+                       'softwareLicenseId' => undef,
+                       'licenseContractId' => undef,
+                       'licenseType' => undef,
+                       'maxInstallations' => undef,
+                       'boundToHost' => undef,
+                       'expirationDate' => undef,
+                       @_ );
+
+    my $res = &_callOpsi( method  => 'createSoftwareLicense',
+        params  => [ $arg{softwareLicenseId}, $arg{licenseContractId}, $arg{licenseType}, $arg{maxInstallations}, $arg{boundToHost}, $arg{expirationDate} ],
+               );
+       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 _addSoftwareLicenseToLicensePool {
+       my %arg = (
+            'softwareLicenseId' => undef,
+            'licensePoolId' => undef,
+            'licenseKey' => undef,
+            @_ );
+
+       if (not defined $arg{softwareLicenseId} ) {
+               return ("function requires softwareLicenseId as parameter", 1);
+       }
+       if (not defined $arg{licensePoolId} ) {
+               return ("function requires licensePoolId as parameter", 1);
+       }
+
+       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 ); }
+
+       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;