Code

Updated refs
[gosa.git] / gosa-si / server / events / opsi_com.pm
1 ## @file
2 # @details A GOsa-SI-server event module containing all functions for message handling.
3 # @brief Implementation of an event module for GOsa-SI-server. 
6 package opsi_com;
7 use Exporter;
8 @ISA = qw(Exporter);
9 my @events = (
10     "get_events",
11     "opsi_install_client",
12     "opsi_get_netboot_products",  
13     "opsi_get_local_products",
14     "opsi_get_client_hardware",
15     "opsi_get_client_software",
16     "opsi_get_product_properties",
17     "opsi_set_product_properties",
18     "opsi_list_clients",
19     "opsi_del_client",
20     "opsi_add_client",
21     "opsi_modify_client",
22     "opsi_add_product_to_client",
23     "opsi_del_product_from_client",
24         "opsi_createLicensePool",
25         "opsi_deleteLicensePool",
26         "opsi_createLicense",
27         "opsi_assignSoftwareLicenseToHost",
28         "opsi_unassignSoftwareLicenseFromHost",
29         "opsi_unassignAllSoftwareLicensesFromHost",
30         "opsi_getSoftwareLicense_hash",
31         "opsi_getLicensePool_hash",
32         "opsi_getSoftwareLicenseUsages",
33         "opsi_getSoftwareLicenseUsagesForProductId",
34         "opsi_getLicensePools_listOfHashes",
35         "opsi_getLicenseInformationForProduct",
36         "opsi_getPool",
37         "opsi_getAllSoftwareLicenses",
38         "opsi_removeLicense",
39         "opsi_getReservedLicenses",
40         "opsi_boundHostToLicense",
41         "opsi_unboundHostFromLicense",
42         "opsi_test",
43    );
44 @EXPORT = @events;
46 use strict;
47 use warnings;
48 use GOSA::GosaSupportDaemon;
49 use Data::Dumper;
50 use XML::Quote qw(:all);
52 BEGIN {}
54 END {}
56 # ----------------------------------------------------------------------------
57 #                          D E C L A R A T I O N S
58 # ----------------------------------------------------------------------------
60 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
64 # ----------------------------------------------------------------------------
65 #                            S U B R O U T I N E S
66 # ----------------------------------------------------------------------------
69 ################################
70 #
71 # @brief A function returning a list of functions which are exported by importing the module.
72 # @return List of all provided functions
73 #
74 sub get_events {
75     return \@events;
76 }
78 ################################
79 #
80 # @brief Checks if there is a specified tag and if the the tag has a content.
81 # @return 0|1
82 #
83 sub _check_xml_tag_is_ok {
84         my ($msg_hash,$tag) = @_;
85         if (not defined $msg_hash->{$tag}) {
86                 $_ = "message contains no tag '$tag'";
87                 return 0;
88         }
89         if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
90                 $_ = "message tag '$tag' has no content";
91                 return  0;
92         }
93         return 1;
94 }
96 ################################
97 #
98 # @brief Writes the log line and returns the error message for GOsa.
99 #
100 sub _giveErrorFeedback {
101         my ($msg_hash, $err_string, $session_id) = @_;
102         &main::daemon_log("$session_id ERROR: $err_string", 1);
103         my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
104     if (exists $msg_hash->{forward_to_gosa}) {
105         &add_content2xml_hash($out_hash, "forward_to_gosa", @{$msg_hash->{'forward_to_gosa'}}[0]);
106     }
107         return ( &create_xml_string($out_hash) );
111 ## @method opsi_add_product_to_client
112 # Adds an Opsi product to an Opsi client.
113 # @param msg - STRING - xml message with tags hostId and productId
114 # @param msg_hash - HASHREF - message information parsed into a hash
115 # @param session_id - INTEGER - POE session id of the processing of this message
116 # @return out_msg - STRING - feedback to GOsa in success and error case
117 sub opsi_add_product_to_client {
118     my ($msg, $msg_hash, $session_id) = @_;
119     my $header = @{$msg_hash->{'header'}}[0];
120     my $source = @{$msg_hash->{'source'}}[0];
121     my $target = @{$msg_hash->{'target'}}[0];
122     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
124     # Build return message
125     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
126     if (defined $forward_to_gosa) {
127         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
128     }
130     # Sanity check of needed parameter
131     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
132                 return &_giveErrorFeedback($msg_hash, "no hostId specified or hostId tag invalid", $session_id);
133     }
134     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
135                 return &_giveErrorFeedback($msg_hash, "no productId specified or productId tag invalid", $session_id);
136     }
138         # Get hostId
139         my $hostId = @{$msg_hash->{'hostId'}}[0];
140         &add_content2xml_hash($out_hash, "hostId", $hostId);
142         # Get productID
143         my $productId = @{$msg_hash->{'productId'}}[0];
144         &add_content2xml_hash($out_hash, "productId", $productId);
146         # Do an action request for all these -> "setup".
147         my $callobj = {
148                 method  => 'setProductActionRequest',
149                 params  => [ $productId, $hostId, "setup" ],
150                 id  => 1, }; 
152         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
153         if (&check_opsi_res($res)) { return ( (caller(0))[3]." : ".$_, 1 ); };
155     # return message
156     return ( &create_xml_string($out_hash) );
159 ## @method opsi_del_product_from_client
160 # Deletes an Opsi-product from an Opsi-client. 
161 # @param msg - STRING - xml message with tags hostId and productId
162 # @param msg_hash - HASHREF - message information parsed into a hash
163 # @param session_id - INTEGER - POE session id of the processing of this message
164 # @return out_msg - STRING - feedback to GOsa in success and error case
165 sub opsi_del_product_from_client {
166     my ($msg, $msg_hash, $session_id) = @_;
167     my $header = @{$msg_hash->{'header'}}[0];
168     my $source = @{$msg_hash->{'source'}}[0];
169     my $target = @{$msg_hash->{'target'}}[0];
170     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
171     my ($hostId, $productId);
172     my $error = 0;
173     my ($sres, $sres_err, $sres_err_string);
175     # Build return message
176     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
177     if (defined $forward_to_gosa) {
178         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
179     }
181     # Sanity check of needed parameter
182     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
183         $error++;
184         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
185         &add_content2xml_hash($out_hash, "error", "hostId");
186         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
188     }
189     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
190         $error++;
191         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
192         &add_content2xml_hash($out_hash, "error", "productId");
193         &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1); 
194     }
196     # All parameter available
197     if (not $error) {
198         # Get hostId
199         $hostId = @{$msg_hash->{'hostId'}}[0];
200         &add_content2xml_hash($out_hash, "hostId", $hostId);
202         # Get productID
203         $productId = @{$msg_hash->{'productId'}}[0];
204         &add_content2xml_hash($out_hash, "productId", $productId);
207 # : check the results for more than one entry which is currently installed
208         #$callobj = {
209         #    method  => 'getProductDependencies_listOfHashes',
210         #    params  => [ $productId ],
211         #    id  => 1, };
212         #
213         #my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
214         #my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
215         #if ($sres_err){
216         #  &main::daemon_log("ERROR: cannot perform dependency check: ".$sres_err_string, 1);
217         #  &add_content2xml_hash($out_hash, "error", $sres_err_string);
218         #  return ( &create_xml_string($out_hash) );
219         #}
222         # Check to get product action list 
223         my $callobj = {
224             method  => 'getPossibleProductActions_list',
225             params  => [ $productId ],
226             id  => 1, };
227         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
228         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
229         if ($sres_err){
230             &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
231             &add_content2xml_hash($out_hash, "error", $sres_err_string);
232             $error++;
233         }
234     }
236     # Check action uninstall of product
237     if (not $error) {
238         my $uninst_possible= 0;
239         foreach my $r (@{$sres->result}) {
240             if ($r eq 'uninstall') {
241                 $uninst_possible= 1;
242             }
243         }
244         if (!$uninst_possible){
245             &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
246             &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
247             $error++;
248         }
249     }
251     # Set product state to "none"
252     # Do an action request for all these -> "setup".
253     if (not $error) {
254         my $callobj = {
255             method  => 'setProductActionRequest',
256             params  => [ $productId, $hostId, "none" ],
257             id  => 1, 
258         }; 
259         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
260         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
261         if ($sres_err){
262             &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
263             &add_content2xml_hash($out_hash, "error", $sres_err_string);
264         }
265     }
267     # Return message
268     return ( &create_xml_string($out_hash) );
271 ## @method opsi_add_client
272 # Adds an Opsi client to Opsi.
273 # @param msg - STRING - xml message with tags hostId and macaddress
274 # @param msg_hash - HASHREF - message information parsed into a hash
275 # @param session_id - INTEGER - POE session id of the processing of this message
276 # @return out_msg - STRING - feedback to GOsa in success and error case
277 sub opsi_add_client {
278     my ($msg, $msg_hash, $session_id) = @_;
279     my $header = @{$msg_hash->{'header'}}[0];
280     my $source = @{$msg_hash->{'source'}}[0];
281     my $target = @{$msg_hash->{'target'}}[0];
282     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
283     my ($hostId, $mac);
284     my $error = 0;
285     my ($sres, $sres_err, $sres_err_string);
287     # Build return message with twisted target and source
288     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
289     if (defined $forward_to_gosa) {
290         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
291     }
293     # Sanity check of needed parameter
294     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
295         $error++;
296         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
297         &add_content2xml_hash($out_hash, "error", "hostId");
298         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
299     }
300     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH'))  {
301         $error++;
302         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
303         &add_content2xml_hash($out_hash, "error", "macaddress");
304         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
305     }
307     if (not $error) {
308         # Get hostId
309         $hostId = @{$msg_hash->{'hostId'}}[0];
310         &add_content2xml_hash($out_hash, "hostId", $hostId);
312         # Get macaddress
313         $mac = @{$msg_hash->{'macaddress'}}[0];
314         &add_content2xml_hash($out_hash, "macaddress", $mac);
316         my $name= $hostId;
317         $name=~ s/^([^.]+).*$/$1/;
318         my $domain= $hostId;
319         $domain=~ s/^[^.]+\.(.*)$/$1/;
320         my ($description, $notes, $ip);
322         if (defined @{$msg_hash->{'description'}}[0]){
323             $description = @{$msg_hash->{'description'}}[0];
324         }
325         if (defined @{$msg_hash->{'notes'}}[0]){
326             $notes = @{$msg_hash->{'notes'}}[0];
327         }
328         if (defined @{$msg_hash->{'ip'}}[0]){
329             $ip = @{$msg_hash->{'ip'}}[0];
330         }
332         my $callobj;
333         $callobj = {
334             method  => 'createClient',
335             params  => [ $name, $domain, $description, $notes, $ip, $mac ],
336             id  => 1,
337         };
339         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
340         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
341         if ($sres_err){
342             &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
343             &add_content2xml_hash($out_hash, "error", $sres_err_string);
344         } else {
345             &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5); 
346         }
347     }
349     # Return message
350     return ( &create_xml_string($out_hash) );
353 ## @method opsi_modify_client
354 # Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
355 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
356 # @param msg_hash - HASHREF - message information parsed into a hash
357 # @param session_id - INTEGER - POE session id of the processing of this message    
358 # @return out_msg - STRING - feedback to GOsa in success and error case
359 sub opsi_modify_client {
360     my ($msg, $msg_hash, $session_id) = @_;
361     my $header = @{$msg_hash->{'header'}}[0];
362     my $source = @{$msg_hash->{'source'}}[0];
363     my $target = @{$msg_hash->{'target'}}[0];
364     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
365     my $hostId;
366     my $error = 0;
367     my ($sres, $sres_err, $sres_err_string);
369     # Build return message with twisted target and source
370     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
371     if (defined $forward_to_gosa) {
372         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
373     }
375     # Sanity check of needed parameter
376     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
377         $error++;
378         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
379         &add_content2xml_hash($out_hash, "error", "hostId");
380         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
381     }
383     if (not $error) {
384         # Get hostId
385         $hostId = @{$msg_hash->{'hostId'}}[0];
386         &add_content2xml_hash($out_hash, "hostId", $hostId);
387         my $name= $hostId;
388         $name=~ s/^([^.]+).*$/$1/;
389         my $domain= $hostId;
390         $domain=~ s/^[^.]+(.*)$/$1/;
392         # Modify description, notes or mac if defined
393         my ($description, $notes, $mac);
394         my $callobj;
395         if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
396             $description = @{$msg_hash->{'description'}}[0];
397             $callobj = {
398                 method  => 'setHostDescription',
399                 params  => [ $hostId, $description ],
400                 id  => 1,
401             };
402             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
403             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
404             if ($sres_err){
405                 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
406                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
407             }
408         }
409         if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
410             $notes = @{$msg_hash->{'notes'}}[0];
411             $callobj = {
412                 method  => 'setHostNotes',
413                 params  => [ $hostId, $notes ],
414                 id  => 1,
415             };
416             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
417             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
418             if ($sres_err){
419                 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
420                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
421             }
422         }
423         if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
424             $mac = @{$msg_hash->{'mac'}}[0];
425             $callobj = {
426                 method  => 'setMacAddress',
427                 params  => [ $hostId, $mac ],
428                 id  => 1,
429             };
430             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
431             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
432             if ($sres_err){
433                 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
434                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
435             }
436         }
437     }
439     # Return message
440     return ( &create_xml_string($out_hash) );
443     
444 ## @method opsi_get_netboot_products
445 # Get netboot products for specific host.
446 # @param msg - STRING - xml message with tag hostId
447 # @param msg_hash - HASHREF - message information parsed into a hash
448 # @param session_id - INTEGER - POE session id of the processing of this message
449 # @return out_msg - STRING - feedback to GOsa in success and error case
450 sub opsi_get_netboot_products {
451     my ($msg, $msg_hash, $session_id) = @_;
452     my $header = @{$msg_hash->{'header'}}[0];
453     my $source = @{$msg_hash->{'source'}}[0];
454     my $target = @{$msg_hash->{'target'}}[0];
455     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
456     my $hostId;
457     my $xml_msg;
459     # Build return message with twisted target and source
460     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
461     if (defined $forward_to_gosa) {
462         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
463     }
465     # Get hostId if defined
466     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
467         $hostId = @{$msg_hash->{'hostId'}}[0];
468         &add_content2xml_hash($out_hash, "hostId", $hostId);
469     }
471     &add_content2xml_hash($out_hash, "xxx", "");
472     $xml_msg = &create_xml_string($out_hash);
473     # For hosts, only return the products that are or get installed
474     my $callobj;
475     $callobj = {
476         method  => 'getNetBootProductIds_list',
477         params  => [ ],
478         id  => 1,
479     };
480     &main::daemon_log("$session_id DEBUG: send callobj to opsi_client: ".&opsi_callobj2string($callobj), 7);
481     &main::daemon_log("$session_id DEBUG: opsi_url $main::opsi_url", 7);
482     &main::daemon_log("$session_id DEBUG: waiting for answer from opsi_client!", 7);
483     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
484     &main::daemon_log("$session_id DEBUG: get answer from opsi_client", 7);
485     my %r = ();
486     for (@{$res->result}) { $r{$_} = 1 }
488       if (not &check_opsi_res($res)){
490         if (defined $hostId){
492             $callobj = {
493                 method  => 'getProductStates_hash',
494                 params  => [ $hostId ],
495                 id  => 1,
496             };
498             my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
499             if (not &check_opsi_res($hres)){
500                 my $htmp= $hres->result->{$hostId};
502                 # check state != not_installed or action == setup -> load and add
503                 foreach my $product (@{$htmp}){
505                     if (!defined ($r{$product->{'productId'}})){
506                         next;
507                     }
509                     # Now we've a couple of hashes...
510                     if ($product->{'installationStatus'} ne "not_installed" or
511                             $product->{'actionRequest'} eq "setup"){
512                         my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
514                         $callobj = {
515                             method  => 'getProduct_hash',
516                             params  => [ $product->{'productId'} ],
517                             id  => 1,
518                         };
520                         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
521                         if (not &check_opsi_res($sres)){
522                             my $tres= $sres->result;
524                             my $name= xml_quote($tres->{'name'});
525                             my $r= $product->{'productId'};
526                             my $description= xml_quote($tres->{'description'});
527                             $name=~ s/\//\\\//;
528                             $description=~ s/\//\\\//;
529                             $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
530                         }
531                     }
532                 }
534             }
536         } else {
537             foreach my $r (@{$res->result}) {
538                 $callobj = {
539                     method  => 'getProduct_hash',
540                     params  => [ $r ],
541                     id  => 1,
542                 };
544                 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
545                 if (not &check_opsi_res($sres)){
546                     my $tres= $sres->result;
548                     my $name= xml_quote($tres->{'name'});
549                     my $description= xml_quote($tres->{'description'});
550                     $name=~ s/\//\\\//;
551                     $description=~ s/\//\\\//;
552                     $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
553                 }
554             }
556         }
557     }
558     $xml_msg=~ s/<xxx><\/xxx>//;
560     # Return message
561     return ( $xml_msg );
565 ## @method opsi_get_product_properties
566 # Get product properties for a product and a specific host or gobally for a product.
567 # @param msg - STRING - xml message with tags productId and optional hostId
568 # @param msg_hash - HASHREF - message information parsed into a hash
569 # @param session_id - INTEGER - POE session id of the processing of this message
570 # @return out_msg - STRING - feedback to GOsa in success and error case
571 sub opsi_get_product_properties {
572     my ($msg, $msg_hash, $session_id) = @_;
573     my $header = @{$msg_hash->{'header'}}[0];
574     my $source = @{$msg_hash->{'source'}}[0];
575     my $target = @{$msg_hash->{'target'}}[0];
576     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
577     my ($hostId, $productId);
578     my $xml_msg;
580     # Build return message with twisted target and source
581     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
582     if (defined $forward_to_gosa) {
583         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
584     }
586     # Sanity check of needed parameter
587     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
588         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
589         &add_content2xml_hash($out_hash, "error", "productId");
590         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
592         # Return message
593         return ( &create_xml_string($out_hash) );
594     }
596     # Get productid
597     $productId = @{$msg_hash->{'productId'}}[0];
598     &add_content2xml_hash($out_hash, "producId", "$productId");
600     # Get hostId if defined
601     if (defined @{$msg_hash->{'hostId'}}[0]){
602       $hostId = @{$msg_hash->{'hostId'}}[0];
603       &add_content2xml_hash($out_hash, "hostId", $hostId);
604     }
606     # Load actions
607     my $callobj = {
608       method  => 'getPossibleProductActions_list',
609       params  => [ $productId ],
610       id  => 1,
611     };
612     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
613     if (not &check_opsi_res($res)){
614       foreach my $action (@{$res->result}){
615         &add_content2xml_hash($out_hash, "action", $action);
616       }
617     }
619     # Add place holder
620     &add_content2xml_hash($out_hash, "xxx", "");
622     # Move to XML string
623     $xml_msg= &create_xml_string($out_hash);
625     # JSON Query
626     if (defined $hostId){
627       $callobj = {
628           method  => 'getProductProperties_hash',
629           params  => [ $productId, $hostId ],
630           id  => 1,
631       };
632     } else {
633       $callobj = {
634           method  => 'getProductProperties_hash',
635           params  => [ $productId ],
636           id  => 1,
637       };
638     }
639     $res = $main::opsi_client->call($main::opsi_url, $callobj);
641     # JSON Query 2
642     $callobj = {
643       method  => 'getProductPropertyDefinitions_listOfHashes',
644       params  => [ $productId ],
645       id  => 1,
646     };
648     # Assemble options
649     my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
650     my $values = {};
651     my $descriptions = {};
652     if (not &check_opsi_res($res2)){
653         my $r= $res2->result;
655           foreach my $entr (@$r){
656             # Unroll values
657             my $cnv;
658             if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
659               foreach my $v (@{$entr->{'values'}}){
660                 $cnv.= "<value>$v</value>";
661               }
662             } else {
663               $cnv= $entr->{'values'};
664             }
665             $values->{$entr->{'name'}}= $cnv;
666             $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
667           }
668     }
670     if (not &check_opsi_res($res)){
671         my $r= $res->result;
672         foreach my $key (keys %{$r}) {
673             my $item= "\n<item>";
674             my $value= $r->{$key};
675             my $dsc= "";
676             my $vals= "";
677             if (defined $descriptions->{$key}){
678               $dsc= $descriptions->{$key};
679             }
680             if (defined $values->{$key}){
681               $vals= $values->{$key};
682             }
683             $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
684             $item.= "</item>";
685             $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
686         }
687     }
689     $xml_msg=~ s/<xxx><\/xxx>//;
691     # Return message
692     return ( $xml_msg );
696 ## @method opsi_set_product_properties
697 # 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.
698 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
699 # @param msg_hash - HASHREF - message information parsed into a hash
700 # @param session_id - INTEGER - POE session id of the processing of this message
701 # @return out_msg - STRING - feedback to GOsa in success and error case
702 sub opsi_set_product_properties {
703     my ($msg, $msg_hash, $session_id) = @_;
704     my $header = @{$msg_hash->{'header'}}[0];
705     my $source = @{$msg_hash->{'source'}}[0];
706     my $target = @{$msg_hash->{'target'}}[0];
707     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
708     my ($productId, $hostId);
710     # Build return message with twisted target and source
711     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
712     if (defined $forward_to_gosa) {
713         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
714     }
716     # Sanity check of needed parameter
717     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
718         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
719         &add_content2xml_hash($out_hash, "error", "productId");
720         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
721         return ( &create_xml_string($out_hash) );
722     }
723     if (not exists $msg_hash->{'item'}) {
724         &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
725         &add_content2xml_hash($out_hash, "error", "item");
726         &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1); 
727         return ( &create_xml_string($out_hash) );
728     } else {
729         if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
730             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
731             &add_content2xml_hash($out_hash, "error", "name");
732             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1); 
733             return ( &create_xml_string($out_hash) );
734         }
735         if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
736             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
737             &add_content2xml_hash($out_hash, "error", "value");
738             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1); 
739             return ( &create_xml_string($out_hash) );
740         }
741     }
742     # if no hostId is given, set_product_properties will act on globally
743     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1))  {
744         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
745         &add_content2xml_hash($out_hash, "error", "hostId");
746         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
747         return ( &create_xml_string($out_hash) );
748     }
750         
751     # Get productId
752     $productId =  @{$msg_hash->{'productId'}}[0];
753     &add_content2xml_hash($out_hash, "productId", $productId);
755     # Get hostId if defined
756     if (exists $msg_hash->{'hostId'}){
757         $hostId = @{$msg_hash->{'hostId'}}[0];
758         &add_content2xml_hash($out_hash, "hostId", $hostId);
759     }
761     # Set product states if requested
762     if (defined @{$msg_hash->{'action'}}[0]){
763         &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
764     }
765     if (defined @{$msg_hash->{'state'}}[0]){
766         &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
767     }
769     # Find properties
770     foreach my $item (@{$msg_hash->{'item'}}){
771         # JSON Query
772         my $callobj;
774         if (defined $hostId){
775             $callobj = {
776                 method  => 'setProductProperty',
777                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
778                 id  => 1,
779             };
780         } else {
781             $callobj = {
782                 method  => 'setProductProperty',
783                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
784                 id  => 1,
785             };
786         }
788         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
789         my ($res_err, $res_err_string) = &check_opsi_res($res);
791         if ($res_err){
792             &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
793             &add_content2xml_hash($out_hash, "error", $res_err_string);
794         }
795     }
798     # Return message
799     return ( &create_xml_string($out_hash) );
803 ## @method opsi_get_client_hardware
804 # Reports client hardware inventory.
805 # @param msg - STRING - xml message with tag hostId
806 # @param msg_hash - HASHREF - message information parsed into a hash
807 # @param session_id - INTEGER - POE session id of the processing of this message
808 # @return out_msg - STRING - feedback to GOsa in success and error case
809 sub opsi_get_client_hardware {
810     my ($msg, $msg_hash, $session_id) = @_;
811     my $header = @{$msg_hash->{'header'}}[0];
812     my $source = @{$msg_hash->{'source'}}[0];
813     my $target = @{$msg_hash->{'target'}}[0];
814     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
815     my $hostId;
816     my $error = 0;
817     my $xml_msg;
819     # Build return message with twisted target and source
820     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
821     if (defined $forward_to_gosa) {
822       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
823     }
825     # Sanity check of needed parameter
826     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
827         $error++;
828         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
829         &add_content2xml_hash($out_hash, "error", "hostId");
830         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
831     }
833     if (not $error) {
835     # Get hostId
836         $hostId = @{$msg_hash->{'hostId'}}[0];
837         &add_content2xml_hash($out_hash, "hostId", "$hostId");
838         &add_content2xml_hash($out_hash, "xxx", "");
839     }    
841     # Move to XML string
842     $xml_msg= &create_xml_string($out_hash);
843     
844     if (not $error) {
846     # JSON Query
847         my $callobj = {
848             method  => 'getHardwareInformation_hash',
849             params  => [ $hostId ],
850             id  => 1,
851         };
853         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
854         if (not &check_opsi_res($res)){
855             my $result= $res->result;
856             if (ref $result eq "HASH") {
857                 foreach my $r (keys %{$result}){
858                     my $item= "\n<item><id>".xml_quote($r)."</id>";
859                     my $value= $result->{$r};
860                     foreach my $sres (@{$value}){
862                         foreach my $dres (keys %{$sres}){
863                             if (defined $sres->{$dres}){
864                                 $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
865                             }
866                         }
868                     }
869                     $item.= "</item>";
870                     $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
872                 }
873             }
874         }
876         $xml_msg=~ s/<xxx><\/xxx>//;
878     }
880     # Return message
881     return ( $xml_msg );
885 ## @method opsi_list_clients
886 # Reports all Opsi clients. 
887 # @param msg - STRING - xml message 
888 # @param msg_hash - HASHREF - message information parsed into a hash
889 # @param session_id - INTEGER - POE session id of the processing of this message
890 # @return out_msg - STRING - feedback to GOsa in success and error case
891 sub opsi_list_clients {
892     my ($msg, $msg_hash, $session_id) = @_;
893     my $header = @{$msg_hash->{'header'}}[0];
894     my $source = @{$msg_hash->{'source'}}[0];
895     my $target = @{$msg_hash->{'target'}}[0];
896     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
898     # Build return message with twisted target and source
899     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
900     if (defined $forward_to_gosa) {
901       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
902     }
903     &add_content2xml_hash($out_hash, "xxx", "");
905     # Move to XML string
906     my $xml_msg= &create_xml_string($out_hash);
908     # JSON Query
909     my $callobj = {
910         method  => 'getClients_listOfHashes',
911         params  => [ ],
912         id  => 1,
913     };
914     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
915     if (not &check_opsi_res($res)){
916         foreach my $host (@{$res->result}){
917             my $item= "\n<item><name>".$host->{'hostId'}."</name>";
918             if (defined($host->{'description'})){
919                 $item.= "<description>".xml_quote($host->{'description'})."</description>";
920             }
921             if (defined($host->{'notes'})){
922                 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
923             }
924             if (defined($host->{'lastSeen'})){
925                 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
926             }
928             $callobj = {
929               method  => 'getIpAddress',
930               params  => [ $host->{'hostId'} ],
931               id  => 1,
932             };
933             my $sres= $main::opsi_client->call($main::opsi_url, $callobj);
934             if ( not &check_opsi_res($sres)){
935               $item.= "<ip>".xml_quote($sres->result)."</ip>";
936             }
938             $callobj = {
939               method  => 'getMacAddress',
940               params  => [ $host->{'hostId'} ],
941               id  => 1,
942             };
943             $sres= $main::opsi_client->call($main::opsi_url, $callobj);
944             if ( not &check_opsi_res($sres)){
945                 $item.= "<mac>".xml_quote($sres->result)."</mac>";
946             }
947             $item.= "</item>";
948             $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
949         }
950     }
952     $xml_msg=~ s/<xxx><\/xxx>//;
953     return ( $xml_msg );
958 ## @method opsi_get_client_software
959 # Reports client software inventory.
960 # @param msg - STRING - xml message with tag hostId
961 # @param msg_hash - HASHREF - message information parsed into a hash
962 # @param session_id - INTEGER - POE session id of the processing of this message
963 # @return out_msg - STRING - feedback to GOsa in success and error case
964 sub opsi_get_client_software {
965     my ($msg, $msg_hash, $session_id) = @_;
966     my $header = @{$msg_hash->{'header'}}[0];
967     my $source = @{$msg_hash->{'source'}}[0];
968     my $target = @{$msg_hash->{'target'}}[0];
969     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
970     my $error = 0;
971     my $hostId;
972     my $xml_msg;
974     # Build return message with twisted target and source
975     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
976     if (defined $forward_to_gosa) {
977       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
978     }
980     # Sanity check of needed parameter
981     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
982         $error++;
983         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
984         &add_content2xml_hash($out_hash, "error", "hostId");
985         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
986     }
988     if (not $error) {
990     # Get hostId
991         $hostId = @{$msg_hash->{'hostId'}}[0];
992         &add_content2xml_hash($out_hash, "hostId", "$hostId");
993         &add_content2xml_hash($out_hash, "xxx", "");
994     }
996     $xml_msg= &create_xml_string($out_hash);
998     if (not $error) {
1000     # JSON Query
1001         my $callobj = {
1002             method  => 'getSoftwareInformation_hash',
1003             params  => [ $hostId ],
1004             id  => 1,
1005         };
1007         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1008         if (not &check_opsi_res($res)){
1009             my $result= $res->result;
1010         }
1012         $xml_msg=~ s/<xxx><\/xxx>//;
1014     }
1016     # Return message
1017     return ( $xml_msg );
1021 ## @method opsi_get_local_products
1022 # Reports product for given hostId or globally.
1023 # @param msg - STRING - xml message with optional tag hostId
1024 # @param msg_hash - HASHREF - message information parsed into a hash
1025 # @param session_id - INTEGER - POE session id of the processing of this message
1026 # @return out_msg - STRING - feedback to GOsa in success and error case
1027 sub opsi_get_local_products {
1028     my ($msg, $msg_hash, $session_id) = @_;
1029     my $header = @{$msg_hash->{'header'}}[0];
1030     my $source = @{$msg_hash->{'source'}}[0];
1031     my $target = @{$msg_hash->{'target'}}[0];
1032     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1033     my $hostId;
1035     # Build return message with twisted target and source
1036     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1037     if (defined $forward_to_gosa) {
1038         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1039     }
1040     &add_content2xml_hash($out_hash, "xxx", "");
1042     # Get hostId if defined
1043     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
1044         $hostId = @{$msg_hash->{'hostId'}}[0];
1045         &add_content2xml_hash($out_hash, "hostId", $hostId);
1046     }
1048     # Move to XML string
1049     my $xml_msg= &create_xml_string($out_hash);
1051     # For hosts, only return the products that are or get installed
1052     my $callobj;
1053     $callobj = {
1054         method  => 'getLocalBootProductIds_list',
1055         params  => [ ],
1056         id  => 1,
1057     };
1059     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1060     my %r = ();
1061     for (@{$res->result}) { $r{$_} = 1 }
1063     if (not &check_opsi_res($res)){
1065         if (defined $hostId){
1066             $callobj = {
1067                 method  => 'getProductStates_hash',
1068                 params  => [ $hostId ],
1069                 id  => 1,
1070             };
1072             my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1073             if (not &check_opsi_res($hres)){
1074                 my $htmp= $hres->result->{$hostId};
1076                 # Check state != not_installed or action == setup -> load and add
1077                 foreach my $product (@{$htmp}){
1079                     if (!defined ($r{$product->{'productId'}})){
1080                         next;
1081                     }
1083                     # Now we've a couple of hashes...
1084                     if ($product->{'installationStatus'} ne "not_installed" or
1085                             $product->{'actionRequest'} eq "setup"){
1086                         my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
1088                         $callobj = {
1089                             method  => 'getProduct_hash',
1090                             params  => [ $product->{'productId'} ],
1091                             id  => 1,
1092                         };
1094                         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1095                         if (not &check_opsi_res($sres)){
1096                             my $tres= $sres->result;
1098                             my $name= xml_quote($tres->{'name'});
1099                             my $r= $product->{'productId'};
1100                             my $description= xml_quote($tres->{'description'});
1101                             $name=~ s/\//\\\//;
1102                             $description=~ s/\//\\\//;
1103                             $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
1104                         }
1106                     }
1107                 }
1109             }
1111         } else {
1112             foreach my $r (@{$res->result}) {
1113                 $callobj = {
1114                     method  => 'getProduct_hash',
1115                     params  => [ $r ],
1116                     id  => 1,
1117                 };
1119                 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1120                 if (not &check_opsi_res($sres)){
1121                     my $tres= $sres->result;
1123                     my $name= xml_quote($tres->{'name'});
1124                     my $description= xml_quote($tres->{'description'});
1125                     $name=~ s/\//\\\//;
1126                     $description=~ s/\//\\\//;
1127                     $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
1128                 }
1130             }
1132         }
1133     }
1135     $xml_msg=~ s/<xxx><\/xxx>//;
1137     # Retrun Message
1138     return ( $xml_msg );
1142 ## @method opsi_del_client
1143 # Deletes a client from Opsi.
1144 # @param msg - STRING - xml message with tag hostId
1145 # @param msg_hash - HASHREF - message information parsed into a hash
1146 # @param session_id - INTEGER - POE session id of the processing of this message
1147 # @return out_msg - STRING - feedback to GOsa in success and error case
1148 sub opsi_del_client {
1149     my ($msg, $msg_hash, $session_id) = @_;
1150     my $header = @{$msg_hash->{'header'}}[0];
1151     my $source = @{$msg_hash->{'source'}}[0];
1152     my $target = @{$msg_hash->{'target'}}[0];
1153     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1154     my $hostId;
1155     my $error = 0;
1157     # Build return message with twisted target and source
1158     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1159     if (defined $forward_to_gosa) {
1160       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1161     }
1163     # Sanity check of needed parameter
1164     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1165         $error++;
1166         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1167         &add_content2xml_hash($out_hash, "error", "hostId");
1168         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
1169     }
1171     if (not $error) {
1173     # Get hostId
1174         $hostId = @{$msg_hash->{'hostId'}}[0];
1175         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1177     # JSON Query
1178         my $callobj = {
1179             method  => 'deleteClient',
1180             params  => [ $hostId ],
1181             id  => 1,
1182         };
1183         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1184     }
1186     # Move to XML string
1187     my $xml_msg= &create_xml_string($out_hash);
1189     # Return message
1190     return ( $xml_msg );
1194 ## @method opsi_install_client
1195 # Set a client in Opsi to install and trigger a wake on lan message (WOL).  
1196 # @param msg - STRING - xml message with tags hostId, macaddress
1197 # @param msg_hash - HASHREF - message information parsed into a hash
1198 # @param session_id - INTEGER - POE session id of the processing of this message
1199 # @return out_msg - STRING - feedback to GOsa in success and error case
1200 sub opsi_install_client {
1201     my ($msg, $msg_hash, $session_id) = @_;
1202     my $header = @{$msg_hash->{'header'}}[0];
1203     my $source = @{$msg_hash->{'source'}}[0];
1204     my $target = @{$msg_hash->{'target'}}[0];
1205     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1208     my ($hostId, $macaddress);
1210     my $error = 0;
1211     my @out_msg_l;
1213     # Build return message with twisted target and source
1214     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1215     if (defined $forward_to_gosa) {
1216         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1217     }
1219     # Sanity check of needed parameter
1220     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1221         $error++;
1222         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1223         &add_content2xml_hash($out_hash, "error", "hostId");
1224         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
1225     }
1226     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') )  {
1227         $error++;
1228         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1229         &add_content2xml_hash($out_hash, "error", "macaddress");
1230         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
1231     } else {
1232         if ((exists $msg_hash->{'macaddress'}) && 
1233                 ($msg_hash->{'macaddress'}[0] =~ /^([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})$/i)) {  
1234             $macaddress = $1; 
1235         } else { 
1236             $error ++; 
1237             &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1238             &add_content2xml_hash($out_hash, "error", "macaddress");
1239             &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1); 
1240         }
1241     }
1243     if (not $error) {
1245     # Get hostId
1246         $hostId = @{$msg_hash->{'hostId'}}[0];
1247         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1249         # Load all products for this host with status != "not_installed" or actionRequest != "none"
1250         my $callobj = {
1251             method  => 'getProductStates_hash',
1252             params  => [ $hostId ],
1253             id  => 1,
1254         };
1256         my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1257         if (not &check_opsi_res($hres)){
1258             my $htmp= $hres->result->{$hostId};
1260             # check state != not_installed or action == setup -> load and add
1261             foreach my $product (@{$htmp}){
1262                 # Now we've a couple of hashes...
1263                 if ($product->{'installationStatus'} ne "not_installed" or
1264                         $product->{'actionRequest'} ne "none"){
1266                     # Do an action request for all these -> "setup".
1267                     $callobj = {
1268                         method  => 'setProductActionRequest',
1269                         params  => [ $product->{'productId'}, $hostId, "setup" ],
1270                         id  => 1,
1271                     };
1272                     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1273                     my ($res_err, $res_err_string) = &check_opsi_res($res);
1274                     if ($res_err){
1275                         &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1276                     } else {
1277                         &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1278                     }
1279                 }
1280             }
1281         }
1282         push(@out_msg_l, &create_xml_string($out_hash));
1283     
1285     # Build wakeup message for client
1286         if (not $error) {
1287             my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1288             &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1289             my $wakeup_msg = &create_xml_string($wakeup_hash);
1290             push(@out_msg_l, $wakeup_msg);
1292             # invoke trigger wake for this gosa-si-server
1293             &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1294         }
1295     }
1296     
1297     # Return messages
1298     return @out_msg_l;
1302 ## @method _set_action
1303 # Set action for an Opsi client
1304 # @param product - STRING - Opsi product
1305 # @param action - STRING - action
1306 # @param hostId - STRING - Opsi hostId
1307 sub _set_action {
1308   my $product= shift;
1309   my $action = shift;
1310   my $hostId = shift;
1311   my $callobj;
1313   $callobj = {
1314     method  => 'setProductActionRequest',
1315     params  => [ $product, $hostId, $action],
1316     id  => 1,
1317   };
1319   $main::opsi_client->call($main::opsi_url, $callobj);
1322 ## @method _set_state
1323 # Set state for an Opsi client
1324 # @param product - STRING - Opsi product
1325 # @param action - STRING - state
1326 # @param hostId - STRING - Opsi hostId
1327 sub _set_state {
1328   my $product = shift;
1329   my $state = shift;
1330   my $hostId = shift;
1331   my $callobj;
1333   $callobj = {
1334     method  => 'setProductState',
1335     params  => [ $product, $hostId, $state ],
1336     id  => 1,
1337   };
1339   $main::opsi_client->call($main::opsi_url, $callobj);
1342 ################################
1344 # @brief Create a license pool at Opsi server.
1345 # @param licensePoolId The name of the pool (optional). 
1346 # @param description The description of the pool (optional).
1347 # @param productIds A list of assigned porducts of the pool (optional). 
1348 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional). 
1350 sub opsi_createLicensePool {
1351     my ($msg, $msg_hash, $session_id) = @_;
1352     my $header = @{$msg_hash->{'header'}}[0];
1353     my $source = @{$msg_hash->{'source'}}[0];
1354     my $target = @{$msg_hash->{'target'}}[0];
1355         my $out_hash;
1356         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1357         my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1358         my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1359         my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1361         # Create license Pool
1362     my $callobj = {
1363         method  => 'createLicensePool',
1364         params  => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1365         id  => 1,
1366     };
1367     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1369         # Check Opsi error
1370         my ($res_error, $res_error_str) = &check_opsi_res($res);
1371         if ($res_error){
1372                 # Create error message
1373                 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1374                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1375                 return ( &create_xml_string($out_hash) );
1376         }
1378         # Create function result message
1379         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1381         return ( &create_xml_string($out_hash) );
1384 ################################
1386 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
1388 sub opsi_getLicensePools_listOfHashes {
1389     my ($msg, $msg_hash, $session_id) = @_;
1390     my $header = @{$msg_hash->{'header'}}[0];
1391     my $source = @{$msg_hash->{'source'}}[0];
1392         my $out_hash;
1394         # Fetch infos from Opsi server
1395     my $callobj = {
1396         method  => 'getLicensePools_listOfHashes',
1397         params  => [ ],
1398         id  => 1,
1399     };
1400     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1402         # Check Opsi error
1403         my ($res_error, $res_error_str) = &check_opsi_res($res);
1404         if ($res_error){
1405                 # Create error message
1406                 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1407                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1408                 return ( &create_xml_string($out_hash) );
1409         }
1411         # Create function result message
1412         my $res_hash = { 'hit'=> [] };
1413         foreach my $licensePool ( @{$res->result}) {
1414                 my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
1415                         'description' => [$licensePool->{'description'}],
1416                         'productIds' => $licensePool->{'productIds'},
1417                         'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
1418                         };
1419                 push( @{$res_hash->{hit}}, $licensePool_hash );
1420         }
1422         # Create function result message
1423         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1424         $out_hash->{result} = [$res_hash];
1426         return ( &create_xml_string($out_hash) );
1429 ################################
1431 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
1432 # @param licensePoolId The name of the pool. 
1434 sub opsi_getLicensePool_hash {
1435     my ($msg, $msg_hash, $session_id) = @_;
1436     my $header = @{$msg_hash->{'header'}}[0];
1437     my $source = @{$msg_hash->{'source'}}[0];
1438     my $target = @{$msg_hash->{'target'}}[0];
1439     my $licensePoolId;
1440         my $out_hash;
1442         # Check input sanity
1443         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1444                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1445         } else {
1446                 return &_giveErrorFeedback($msg_hash, "", $session_id, $_);
1447         }
1449         # Fetch infos from Opsi server
1450     my $callobj = {
1451         method  => 'getLicensePool_hash',
1452         params  => [ $licensePoolId ],
1453         id  => 1,
1454     };
1455     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1457         # Check Opsi error
1458         my ($res_error, $res_error_str) = &check_opsi_res($res);
1459         if ($res_error){
1460                 # Create error message
1461                 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1462                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1463                 &add_content2xml_hash($out_hash, "error", $res_error_str);
1464                 return ( &create_xml_string($out_hash) );
1465         }
1467         # Create function result message
1468         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1469         &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1470         &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1471         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1472         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1474         return ( &create_xml_string($out_hash) );
1477 sub _parse_getSoftwareLicenseUsages {
1478         my $res = shift;
1480         # Parse Opsi result
1481         my $tmp_licensePool_cache = {};
1482         my $res_hash = { 'hit'=> [] };
1483         foreach my $license ( @{$res}) {
1484                 my $tmp_licensePool = $license->{'licensePoolId'};
1485                 if (not exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1486                         # Fetch missing informations from Opsi and cache the results for a possible later usage
1487                         my ($res, $err) = &_getLicensePool_hash('licensePoolId'=>$tmp_licensePool);
1488                         if (not $err) {
1489                                 $tmp_licensePool_cache->{$tmp_licensePool} = $res;
1490                         }
1491                 }
1492                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1493                         'notes' => [$license->{'notes'}],
1494                         'licenseKey' => [$license->{'licenseKey'}],
1495                         'hostId' => [$license->{'hostId'}],
1496                         'licensePoolId' => [$tmp_licensePool],
1497                         };
1498                 if (exists $tmp_licensePool_cache->{$tmp_licensePool}) {
1499                         $license_hash->{$tmp_licensePool} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
1500                         map (push (@{$license_hash->{$tmp_licensePool}->{productIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{productIds}});
1501                         map (push (@{$license_hash->{$tmp_licensePool}->{windowsSoftwareIds}}, $_), @{$tmp_licensePool_cache->{$tmp_licensePool}->{windowsSoftwareIds}});
1502                 }
1503                 push( @{$res_hash->{hit}}, $license_hash );
1504         }
1506         return $res_hash;
1509 ################################
1511 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
1512 # @param hostid Something like client_1.intranet.mydomain.de (optional).
1513 # @param licensePoolId The name of the pool (optional). 
1514
1515 sub opsi_getSoftwareLicenseUsages {
1516         my ($msg, $msg_hash, $session_id) = @_;
1517         my $header = @{$msg_hash->{'header'}}[0];
1518         my $source = @{$msg_hash->{'source'}}[0];
1519         my $target = @{$msg_hash->{'target'}}[0];
1520         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1521         my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
1522         my $out_hash;
1524         my ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId, 'hostId'=>$hostId);
1525         if ($err){
1526                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1527         }
1529         # Parse Opsi result
1530         my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1532         # Create function result message
1533         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1534         $out_hash->{result} = [$res_hash];
1536         return ( &create_xml_string($out_hash) );
1539 ################################
1541 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId. Function return is identical to opsi_getSoftwareLicenseUsages
1542 # @param productId Something like 'firefox', 'python' or anything else .
1543
1544 sub opsi_getSoftwareLicenseUsagesForProductId {
1545         my ($msg, $msg_hash, $session_id) = @_;
1546         my $header = @{$msg_hash->{'header'}}[0];
1547         my $source = @{$msg_hash->{'source'}}[0];
1549         # Check input sanity
1550         my $productId;
1551         if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1552                 $productId= @{$msg_hash->{'productId'}}[0];
1553         } else {
1554                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1555         }
1557         # Fetch licensePoolId for productId
1558         my ($res, $err) = &_getLicensePoolId('productId'=>$productId);
1559         if ($err){
1560                 return &_giveErrorFeedback($msg_hash, "cannot fetch licensePoolId for given productId : ".$res, $session_id);
1561         }
1563         my $licensePoolId;
1565         # Fetch softwareLiceceUsages for licensePoolId
1566         ($res, $err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
1567         if ($err){
1568                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from license pool : ".$res, $session_id);
1569         }
1571         # Parse Opsi result
1572         my $res_hash = &_parse_getSoftwareLicenseUsages($res);
1574         # Create function result message
1575         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1576         $out_hash->{result} = [$res_hash];
1578         return ( &create_xml_string($out_hash) );
1581 ################################
1583 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
1584 # @param softwareLicenseId Identificator of a license.
1586 sub opsi_getSoftwareLicense_hash {
1587         my ($msg, $msg_hash, $session_id) = @_;
1588         my $header = @{$msg_hash->{'header'}}[0];
1589         my $source = @{$msg_hash->{'source'}}[0];
1590         my $target = @{$msg_hash->{'target'}}[0];
1591         my $softwareLicenseId;
1592         my $out_hash;
1594         # Check input sanity
1595         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
1596                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
1597         } else {
1598                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1599         }
1601         my $callobj = {
1602                 method  => 'getSoftwareLicense_hash',
1603                 params  => [ $softwareLicenseId ],
1604                 id  => 1,
1605         };
1606         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1608         # Check Opsi error
1609         my ($res_error, $res_error_str) = &check_opsi_res($res);
1610         if ($res_error){
1611                 # Create error message
1612                 &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
1613                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1614                 return ( &create_xml_string($out_hash) );
1615         }
1616         
1617         # Create function result message
1618         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1619         &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
1620         &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
1621         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
1622         &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
1623         foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
1624                 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1625                 &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
1626         }
1628         return ( &create_xml_string($out_hash) );
1631 ################################
1633 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool. 
1634 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted. 
1635 # @param licensePoolId The name of the pool. 
1637 sub opsi_deleteLicensePool {
1638         my ($msg, $msg_hash, $session_id) = @_;
1639     my $header = @{$msg_hash->{'header'}}[0];
1640     my $source = @{$msg_hash->{'source'}}[0];
1641     my $target = @{$msg_hash->{'target'}}[0];
1642     my $licensePoolId;
1643         my $out_hash;
1645         # Check input sanity
1646         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1647                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1648         } else {
1649                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1650         }
1652         # Fetch softwareLicenseIds used in license pool
1653         # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
1654         # but not the license contracts of the software licenses. In our case each software license has exactly one license contract. 
1655         my $callobj = {
1656                 method  => 'getSoftwareLicenses_listOfHashes',
1657                 params  => [ ],
1658                 id  => 1,
1659         };
1660         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1662         # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
1663         my @lCI_toBeDeleted;
1664         foreach my $softwareLicenseHash ( @{$res->result} ) {
1665                 if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) { 
1666                         next; 
1667                 }  
1668                 push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
1669         }
1671         # Delete license pool at Opsi server
1672     $callobj = {
1673         method  => 'deleteLicensePool',
1674         params  => [ $licensePoolId, 'deleteLicenses=True'  ],
1675         id  => 1,
1676     };
1677     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1678         my ($res_error, $res_error_str) = &check_opsi_res($res);
1679         if ($res_error){
1680                 # Create error message
1681                 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1682                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1683                 return ( &create_xml_string($out_hash) );
1684         } 
1686         # Delete each license contract connected with the license pool
1687         foreach my $licenseContractId ( @lCI_toBeDeleted ) {
1688                 my $callobj = {
1689                         method  => 'deleteLicenseContract',
1690                         params  => [ $licenseContractId ],
1691                         id  => 1,
1692                 };
1693                 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1694                 my ($res_error, $res_error_str) = &check_opsi_res($res);
1695                 if ($res_error){
1696                         # Create error message
1697                         &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
1698                         $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1699                         return ( &create_xml_string($out_hash) );
1700                 }
1701         }
1703         # Create function result message
1704         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1705         
1706         return ( &create_xml_string($out_hash) );
1709 ################################
1711 # @brief Create a license contract, create a software license and add the software license to the license pool
1712 # @param licensePoolId The name of the pool the license should be assigned.
1713 # @param licenseKey The license key.
1714 # @param partner Name of the license partner (optional).
1715 # @param conclusionDate Date of conclusion of license contract (optional)
1716 # @param notificationDate Date of notification that license is running out soon (optional).
1717 # @param notes This is the place for some notes (optional)
1718 # @param softwareLicenseId Identificator of a license (optional).
1719 # @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
1720 # @param maxInstallations The number of clients use this license (optional). 
1721 # @param boundToHost The name of the client the license is bound to (optional).
1722 # @param expirationDate The date when the license is running down (optional). 
1724 sub opsi_createLicense {
1725         my ($msg, $msg_hash, $session_id) = @_;
1726     my $header = @{$msg_hash->{'header'}}[0];
1727     my $source = @{$msg_hash->{'source'}}[0];
1728     my $target = @{$msg_hash->{'target'}}[0];
1729         my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1730         my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1731         my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1732         my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1733         my $licenseContractId = undef;
1734         my $softwareLicenseId = defined $msg_hash->{'softwareLicenseId'} ? @{$msg_hash->{'softwareLicenseId'}}[0] : undef;
1735         my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1736         my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1737         my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1738         my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1739         my $licensePoolId;
1740         my $licenseKey;
1741         my $out_hash;
1743         # Check input sanity
1744         if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
1745                 $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1746         } else {
1747                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1748         }
1749         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1750                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1751         } else {
1752                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1753         }
1754         if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
1755                 return &_giveErrorFeedback($msg_hash, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'.", $session_id);
1756         }
1757         
1758         # Automatically define licenseContractId if ID is not given
1759         if (defined $softwareLicenseId) { 
1760                 $licenseContractId = "c_".$softwareLicenseId;
1761         }
1763         # Create license contract at Opsi server
1764     my $callobj = {
1765         method  => 'createLicenseContract',
1766         params  => [ $licenseContractId, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1767         id  => 1,
1768     };
1769     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1771         # Check Opsi error
1772         my ($res_error, $res_error_str) = &check_opsi_res($res);
1773         if ($res_error){
1774                 # Create error message
1775                 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1776                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1777                 return ( &create_xml_string($out_hash) );
1778         }
1779         
1780         $licenseContractId = $res->result;
1782         # Create software license at Opsi server
1783     $callobj = {
1784         method  => 'createSoftwareLicense',
1785         params  => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1786         id  => 1,
1787     };
1788     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1790         # Check Opsi error
1791         ($res_error, $res_error_str) = &check_opsi_res($res);
1792         if ($res_error){
1793                 # Create error message
1794                 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1795                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1796                 return ( &create_xml_string($out_hash) );
1797         }
1799         $softwareLicenseId = $res->result;
1801         # Add software license to license pool
1802         $callobj = {
1803         method  => 'addSoftwareLicenseToLicensePool',
1804         params  => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1805         id  => 1,
1806     };
1807     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1809         # Check Opsi error
1810         ($res_error, $res_error_str) = &check_opsi_res($res);
1811         if ($res_error){
1812                 # Create error message
1813                 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1814                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1815                 return ( &create_xml_string($out_hash) );
1816         }
1818         # Create function result message
1819         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1820         
1821         return ( &create_xml_string($out_hash) );
1824 ################################
1826 # @brief Assign a software license to a host
1827 # @param hostid Something like client_1.intranet.mydomain.de
1828 # @param licensePoolId The name of the pool.
1830 sub opsi_assignSoftwareLicenseToHost {
1831         my ($msg, $msg_hash, $session_id) = @_;
1832     my $header = @{$msg_hash->{'header'}}[0];
1833     my $source = @{$msg_hash->{'source'}}[0];
1834     my $target = @{$msg_hash->{'target'}}[0];
1835         my $hostId;
1836         my $licensePoolId;
1838         # Check input sanity
1839         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1840                 $hostId = @{$msg_hash->{'hostId'}}[0];
1841         } else {
1842                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1843         }
1844         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1845                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1846         } else {
1847                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1848         }
1850         # Assign a software license to a host
1851         my $callobj = {
1852         method  => 'getAndAssignSoftwareLicenseKey',
1853         params  => [ $hostId, $licensePoolId ],
1854         id  => 1,
1855     };
1856     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1858         # Check Opsi error
1859         my ($res_error, $res_error_str) = &check_opsi_res($res);
1860         if ($res_error){
1861                 # Create error message
1862                 &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
1863                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1864                 return ( &create_xml_string($out_hash) );
1865         }
1867         # Create function result message
1868         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1869         
1870         return ( &create_xml_string($out_hash) );
1873 ################################
1875 # @brief Unassign a software license from a host.
1876 # @param hostid Something like client_1.intranet.mydomain.de
1877 # @param licensePoolId The name of the pool.
1879 sub opsi_unassignSoftwareLicenseFromHost {
1880         my ($msg, $msg_hash, $session_id) = @_;
1881     my $header = @{$msg_hash->{'header'}}[0];
1882     my $source = @{$msg_hash->{'source'}}[0];
1883     my $target = @{$msg_hash->{'target'}}[0];
1884         my $hostId;
1885         my $licensePoolId;
1887         # Check input sanity
1888         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1889                 $hostId = @{$msg_hash->{'hostId'}}[0];
1890         } else {
1891                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1892         }
1893         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1894                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1895         } else {
1896                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1897         }
1899         # Unassign a software license from a host
1900         my $callobj = {
1901         method  => 'deleteSoftwareLicenseUsage',
1902         params  => [ $hostId, '', $licensePoolId ],
1903         id  => 1,
1904     };
1905     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1907         # Check Opsi error
1908         my ($res_error, $res_error_str) = &check_opsi_res($res);
1909         if ($res_error){
1910                 # Create error message
1911                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1912                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1913                 return ( &create_xml_string($out_hash) );
1914         }
1916         # Create function result message
1917         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1918         
1919         return ( &create_xml_string($out_hash) );
1922 ################################
1924 # @brief Unassign all software licenses from a host
1925 # @param hostid Something like client_1.intranet.mydomain.de
1927 sub opsi_unassignAllSoftwareLicensesFromHost {
1928         my ($msg, $msg_hash, $session_id) = @_;
1929     my $header = @{$msg_hash->{'header'}}[0];
1930     my $source = @{$msg_hash->{'source'}}[0];
1931     my $target = @{$msg_hash->{'target'}}[0];
1932         my $hostId;
1934         # Check input sanity
1935         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1936                 $hostId = @{$msg_hash->{'hostId'}}[0];
1937         } else {
1938                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1939         }
1941         # Unassign all software licenses from a host
1942         my $callobj = {
1943         method  => 'deleteAllSoftwareLicenseUsages',
1944         params  => [ $hostId ],
1945         id  => 1,
1946     };
1947     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1949         # Check Opsi error
1950         my ($res_error, $res_error_str) = &check_opsi_res($res);
1951         if ($res_error){
1952                 # Create error message
1953                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1954                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1955                 return ( &create_xml_string($out_hash) );
1956         }
1958         # Create function result message
1959         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1960         
1961         return ( &create_xml_string($out_hash) );
1965 ################################
1967 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
1968 # and the number of max and remaining installations for a given OPSI product.
1969 # @param productId Identificator of an OPSI product.
1970 #       
1971 sub opsi_getLicenseInformationForProduct {
1972     my ($msg, $msg_hash, $session_id) = @_;
1973     my $header = @{$msg_hash->{'header'}}[0];
1974     my $source = @{$msg_hash->{'source'}}[0];
1975         my $productId;
1976         my $out_hash;
1978         # Check input sanity
1979         if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1980                 $productId = @{$msg_hash->{'productId'}}[0];
1981         } else {
1982                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
1983         }
1985         # Fetch infos from Opsi server
1986     my $callobj = {
1987         method  => 'getLicensePoolId',
1988         params  => [ $productId ],
1989         id  => 1,
1990     };
1991     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1993         # Check Opsi error
1994         my ($res_error, $res_error_str) = &check_opsi_res($res);
1995         if ($res_error){
1996                 return &_giveErrorFeedback($msg_hash, "cannot get license pool for product '$productId' : ".$res_error_str, $session_id);
1997         } 
1998         
1999         my $licensePoolId = $res->result;
2001         # Fetch statistic information for given pool ID
2002         $callobj = {
2003                 method  => 'getLicenseStatistics_hash',
2004                 params  => [ ],
2005                 id  => 1,
2006         };
2007         $res = $main::opsi_client->call($main::opsi_url, $callobj);
2009         # Check Opsi error
2010         ($res_error, $res_error_str) = &check_opsi_res($res);
2011         if ($res_error){
2012                 # Create error message
2013                 &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
2014                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
2015                 return ( &create_xml_string($out_hash) );
2016         }
2018         # Create function result message
2019         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2020         &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
2021         &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
2022         &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
2023         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
2024         &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
2025         map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
2027         return ( &create_xml_string($out_hash) );
2031 ################################
2033 # @brief
2034 # @param 
2035 #       
2036 sub opsi_getPool {
2037     my ($msg, $msg_hash, $session_id) = @_;
2038     my $header = @{$msg_hash->{'header'}}[0];
2039     my $source = @{$msg_hash->{'source'}}[0];
2041         # Check input sanity
2042         my $licensePoolId;
2043         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2044                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2045         } else {
2046                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2047         }
2049         # Create hash for the answer
2050         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2052         # Call Opsi
2053         my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
2054         if ($err){
2055                 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
2056         }
2057         # Add data to outgoing hash
2058         &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
2059         &add_content2xml_hash($out_hash, "description", $res->{'description'});
2060         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
2061         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
2064         # Call Opsi two times
2065         my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
2066         if ($usages_err){
2067                 return &_giveErrorFeedback($msg_hash, "cannot get software license usage information from Opsi server: ".$usages_res, $session_id);
2068         }
2069         my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
2070         if ($licenses_err){
2071                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
2072         }
2074         # Add data to outgoing hash
2075         # Parse through all software licenses and select those associated to the pool
2076         my $res_hash = { 'hit'=> [] };
2077         foreach my $license ( @$licenses_res) {
2078                 # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
2079                 my $found = 0;
2080                 my @licensePoolIds_list = @{$license->{licensePoolIds}};
2081                 foreach my $lPI ( @licensePoolIds_list) {
2082                         if ($lPI eq $licensePoolId) { $found++ }
2083                 }
2084                 if (not $found ) { next; };
2085                 # Found matching licensePoolId
2086                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2087                         'licenseKeys' => {},
2088                         'expirationDate' => [$license->{'expirationDate'}],
2089                         'boundToHost' => [$license->{'boundToHost'}],
2090                         'maxInstallations' => [$license->{'maxInstallations'}],
2091                         'licenseType' => [$license->{'licenseType'}],
2092                         'licenseContractId' => [$license->{'licenseContractId'}],
2093                         'licensePoolIds' => [],
2094                         'hostIds' => [],
2095                         };
2096                 foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
2097                         push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2098                         $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
2099                 }
2100                 foreach my $usage (@$usages_res) {
2101                         # Search for hostIds with matching softwareLicenseId
2102                         if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
2103                                 push( @{ $license_hash->{hostIds}}, $usage->{hostId});
2104                         }
2105                 }
2107                 # Each softwareLicenseId has one licenseContractId, fetch contract details for each licenseContractId
2108                 my ($lContract_res, $lContract_err) = &_getLicenseContract_hash('licenseContractId'=>$license->{licenseContractId});
2109                 if ($lContract_err){
2110                         return &_giveErrorFeedback($msg_hash, "cannot get software license contract information from Opsi server: ".$licenses_res, $session_id);
2111                 }
2112                 $license_hash->{$license->{'licenseContractId'}} = [];
2113                 my $licenseContract_hash = { 'conclusionDate' => [$lContract_res->{conclusionDate}],
2114                         'notificationDate' => [$lContract_res->{notificationDate}],
2115                         'notes' => [$lContract_res->{notes}],
2116                         'exirationDate' => [$lContract_res->{expirationDate}],
2117                         'partner' => [$lContract_res->{partner}],
2118                 };
2119                 push( @{$license_hash->{licenseContractData}}, $licenseContract_hash );
2121                 push( @{$res_hash->{hit}}, $license_hash );
2122         }
2123         $out_hash->{licenses} = [$res_hash];
2125     return ( &create_xml_string($out_hash) );
2129 ################################
2131 # @brief Removes at first the software license from license pool and than deletes the software license. 
2132 # Attention, the software license has to exists otherwise it will lead to an Opsi internal server error.
2133 # @param softwareLicenseId 
2134 # @param licensePoolId
2136 sub opsi_removeLicense {
2137     my ($msg, $msg_hash, $session_id) = @_;
2138     my $header = @{$msg_hash->{'header'}}[0];
2139     my $source = @{$msg_hash->{'source'}}[0];
2141         # Check input sanity
2142         my $softwareLicenseId;
2143         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2144                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2145         } else {
2146                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2147         }
2148         my $licensePoolId;
2149         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2150                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2151         } else {
2152                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2153         }
2154         
2155         # Call Opsi
2156         my ($res, $err) = &_removeSoftwareLicenseFromLicensePool( 'licensePoolId' => $licensePoolId, 'softwareLicenseId' => $softwareLicenseId );
2157         if ($err){
2158                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from pool: ".$res, $session_id);
2159         }
2161         # Call Opsi
2162         ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId'=>$softwareLicenseId );
2163         if ($err){
2164                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from Opsi server: ".$res, $session_id);
2165         }
2167         # Create hash for the answer
2168         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2169         return ( &create_xml_string($out_hash) );
2173 ################################
2175 # @brief
2176 # @param 
2178 sub opsi_getReservedLicenses {
2179         my ($msg, $msg_hash, $session_id) = @_;
2180         my $header = @{$msg_hash->{'header'}}[0];
2181         my $source = @{$msg_hash->{'source'}}[0];
2183         # Check input sanity
2184         my $hostId;
2185         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2186                 $hostId = @{$msg_hash->{'hostId'}}[0];
2187         } else {
2188                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2189         }
2191         # Fetch informations from Opsi server
2192         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2193         if ($license_err){
2194                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2195         }
2198         # Parse result
2199         my $res_hash = { 'hit'=> [] };
2200         foreach my $license ( @$license_res) {
2201                 if ($license->{boundToHost} ne $hostId) { next; }
2203                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2204                         'maxInstallations' => [$license->{'maxInstallations'}],
2205                         'boundToHost' => [$license->{'boundToHost'}],
2206                         'expirationDate' => [$license->{'expirationDate'}],
2207                         'licenseContractId' => [$license->{'licenseContractId'}],
2208                         'licenseType' => [$license->{'licenseType'}],
2209                         'licensePoolIds' => [],
2210                         };
2211                 
2212                 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2213                         # Fetch information for license pools containing a software license which is bound to given host
2214                         my ($pool_res, $pool_err) = &_getLicensePool_hash( 'licensePoolId'=>$licensePoolId );
2215                         if ($pool_err){
2216                                 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$pool_res, $session_id);
2217                         }
2219                         # Add licensePool information to result hash
2220                         push (@{$license_hash->{licensePoolIds}}, $licensePoolId);
2221                         $license_hash->{$licensePoolId} = {'productIds'=>[], 'windowsSoftwareIds'=>[]};
2222                         map (push (@{$license_hash->{$licensePoolId}->{productIds}}, $_), @{$pool_res->{productIds}});
2223                         map (push (@{$license_hash->{$licensePoolId}->{windowsSoftwareIds}}, $_), @{$pool_res->{windowsSoftwareIds}});
2224                 }
2225                 push( @{$res_hash->{hit}}, $license_hash );
2226         }
2227         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2228         $out_hash->{licenses} = [$res_hash];
2229     return ( &create_xml_string($out_hash) );
2231         return;
2234 ################################
2236 # @brief
2237 # @param 
2239 sub opsi_boundHostToLicense {
2240         my ($msg, $msg_hash, $session_id) = @_;
2241         my $header = @{$msg_hash->{'header'}}[0];
2242         my $source = @{$msg_hash->{'source'}}[0];
2244         # Check input sanity
2245         my $hostId;
2246         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
2247                 $hostId = @{$msg_hash->{'hostId'}}[0];
2248         } else {
2249                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2250         }
2251         my $softwareLicenseId;
2252         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2253                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2254         } else {
2255                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2256         }
2258         # Fetch informations from Opsi server
2259         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2260         if ($license_err){
2261                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$license_res, $session_id);
2262         }
2264         # Memorize parameter for given softwareLicenseId
2265         my $licenseContractId;
2266         my $licenseType;
2267         my $maxInstallations;
2268         my $boundToHost;
2269         my $expirationDate = "";
2270         my $found;
2271         foreach my $license (@$license_res) {
2272                 if ($license->{softwareLicenseId} ne $softwareLicenseId) { next; }
2273                 $licenseContractId = $license->{licenseContractId};
2274                 $licenseType = $license->{licenseType};
2275                 $maxInstallations = $license->{maxInstallations};
2276                 $expirationDate = $license->{expirationDate};
2277                 $found++;
2278         }
2280         if (not $found) {
2281                 return &_giveErrorFeedback($msg_hash, "no softwarelicenseId found with name '".$softwareLicenseId."'", $session_id);
2282         }
2284         # Set boundToHost option for a given software license
2285         my ($bound_res, $bound_err) = &_createSoftwareLicense('softwareLicenseId'=>$softwareLicenseId, 
2286                         'licenseContractId' => $licenseContractId, 
2287                         'licenseType' => $licenseType, 
2288                         'maxInstallations' => $maxInstallations, 
2289                         'boundToHost' => $hostId, 
2290                         'expirationDate' => $expirationDate);
2291         if ($bound_err) {
2292                 return &_giveErrorFeedback($msg_hash, "cannot set boundToHost for given softwareLicenseId and hostId: ".$bound_res, $session_id);
2293         }
2295         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2296     return ( &create_xml_string($out_hash) );
2299 ################################
2301 # @brief
2302 # @param 
2304 sub opsi_unboundHostFromLicense {
2305         # This is really mad! Opsi is not able to unbound a lincense from a host. To provide the functionality for GOsa
2306         # 4 rpc calls to Opsi are necessary. First, fetch all data for the given softwareLicenseId, then all details for the associated
2307         # licenseContractId, then delete the softwareLicense and finally recreate the softwareLicense without the boundToHost option. NASTY!
2308         my ($msg, $msg_hash, $session_id) = @_;
2309         my $header = @{$msg_hash->{'header'}}[0];
2310         my $source = @{$msg_hash->{'source'}}[0];
2312         # Check input sanity
2313         my $softwareLicenseId;
2314         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2315                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2316         } else {
2317                 return &_giveErrorFeedback($msg_hash, $_, $session_id);
2318         }
2319         
2320         # Memorize parameter witch are required for this procedure
2321         my $licenseContractId;
2322         my $licenseType;
2323         my $maxInstallations;
2324         my $expirationDate;
2325         my $partner;
2326         my $conclusionDate;
2327         my $notificationDate;
2328         my $notes;
2329         my $licensePoolId;
2330         my $licenseKey;
2332         # Fetch license informations from Opsi server
2333         my ($license_res, $license_err) = &_getSoftwareLicenses_listOfHashes();
2334         if ($license_err){
2335                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2336         }
2337         my $found = 0;
2338         foreach my $license (@$license_res) {
2339                 if (($found > 0) || ($license->{softwareLicenseId} ne $softwareLicenseId)) { next; }
2340                 $licenseContractId = $license->{licenseContractId};
2341                 $licenseType = $license->{licenseType};
2342                 $maxInstallations = $license->{maxInstallations};
2343                 $expirationDate = $license->{expirationDate};
2344                 $licensePoolId = @{$license->{licensePoolIds}}[0];
2345                 $licenseKey = $license->{licenseKeys}->{$licensePoolId};
2346                 $found++;
2347         }
2348         
2349         # Fetch contract informations from Opsi server
2350         my ($contract_res, $contract_err) = &_getLicenseContract_hash('licenseContractId'=>$licenseContractId);
2351         if ($contract_err){
2352                 return &_giveErrorFeedback($msg_hash, "cannot get contract license information from Opsi server, required to unbound license from host: ".$license_res, $session_id);
2353         }
2354         $partner = $contract_res->{partner};
2355         $conclusionDate = $contract_res->{conclusionDate};
2356         $notificationDate = $contract_res->{notificationDate};
2357         $expirationDate = $contract_res->{expirationDate};
2358         $notes = $contract_res->{notes};
2360         # Delete software license
2361         my ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'removeFromPools'=> "true" );
2362         if ($err) {
2363                 return &_giveErrorFeedback($msg_hash, "cannot delet license from Opsi server, required to unbound license from host : ".$res, $session_id);
2364         }
2366         # Recreate software license without boundToHost
2367         ($res, $err) = &_createLicenseContract( 'licenseContractId' => $licenseContractId, 'partner' => $partner, 'conclusionDate' => $conclusionDate, 
2368                         'notificationDate' => $notificationDate, 'expirationDate' => $expirationDate, 'notes' => $notes );
2369         if ($err) {
2370                 return &_giveErrorFeedback($msg_hash, "cannot create license contract at Opsi server, required to unbound license from host : ".$res, $session_id);
2371         }
2372         ($res, $err) = &_createSoftwareLicense( 'softwareLicenseId' => $softwareLicenseId, 'licenseContractId' => $licenseContractId, 'licenseType' => $licenseType, 
2373                         'maxInstallations' => $maxInstallations, 'boundToHost' => "", 'expirationDate' => $expirationDate       );
2374         if ($err) {
2375                 return &_giveErrorFeedback($msg_hash, "cannot create software license at Opsi server, required to unbound license from host : ".$res, $session_id);
2376         }
2377         ($res, $err) = &_addSoftwareLicenseToLicensePool( 'softwareLicenseId' => $softwareLicenseId, 'licensePoolId' => $licensePoolId, 'licenseKey' => $licenseKey );
2378         if ($err) {
2379                 return &_giveErrorFeedback($msg_hash, "cannot add software license to license pool at Opsi server, required to unbound license from host : ".$res, $session_id);
2380         }
2382         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2383     return ( &create_xml_string($out_hash) );
2386 ################################
2388 # @brief
2389 # @param 
2391 sub opsi_getAllSoftwareLicenses {
2392         my ($msg, $msg_hash, $session_id) = @_;
2393         my $header = @{$msg_hash->{'header'}}[0];
2394         my $source = @{$msg_hash->{'source'}}[0];
2396         my ($res, $err) = &_getSoftwareLicenses_listOfHashes();
2397         if ($err) {
2398                 return &_giveErrorFeedback($msg_hash, "cannot fetch software licenses from Opsi server : ".$res, $session_id);
2399         }
2401         # Parse result
2402         my $res_hash = { 'hit'=> [] };
2403         foreach my $license ( @$res) {
2404                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2405                         'maxInstallations' => [$license->{'maxInstallations'}],
2406                         'boundToHost' => [$license->{'boundToHost'}],
2407                         'expirationDate' => [$license->{'expirationDate'}],
2408                         'licenseContractId' => [$license->{'licenseContractId'}],
2409                         'licenseType' => [$license->{'licenseType'}],
2410                         'licensePoolIds' => [],
2411                         'licenseKeys'=> {}
2412                         };
2413                 foreach my $licensePoolId (@{$license->{'licensePoolIds'}}) {
2414                         push( @{$license_hash->{'licensePoolIds'}}, $licensePoolId);
2415                         $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
2416                 }
2417                 push( @{$res_hash->{hit}}, $license_hash );
2418         }
2419         
2420         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2421         $out_hash->{licenses} = [$res_hash];
2422     return ( &create_xml_string($out_hash) );
2425 sub opsi_test {
2426     my ($msg, $msg_hash, $session_id) = @_;
2427     my $header = @{$msg_hash->{'header'}}[0];
2428     my $source = @{$msg_hash->{'source'}}[0];
2429         my $pram1 = @{$msg_hash->{'productId'}}[0];
2431 print STDERR Dumper $pram1;
2433         # Fetch infos from Opsi server
2434     my $callobj = {
2435         method  => 'getLicensePoolId',
2436         params  => [ $pram1 ],
2437         id  => 1,
2438     };
2439     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2441         print STDERR Dumper $res;
2442         return ();
2448 sub _getLicensePool_hash {
2449         my %arg = (
2450                 'licensePoolId' => undef,
2451                 @_,
2452         );
2454         if (not defined $arg{licensePoolId} ) { 
2455                 return ("function requires licensePoolId as parameter", 1);
2456         }
2458         # Fetch pool infos from Opsi server
2459     my $callobj = {
2460         method  => 'getLicensePool_hash',
2461         params  => [ $arg{licensePoolId} ],
2462         id  => 1,
2463     };
2464     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2466         # Check Opsi error
2467         my ($res_error, $res_error_str) = &check_opsi_res($res);
2468         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2470         return ($res->result, 0);
2473 sub _getSoftwareLicenses_listOfHashes {
2474         # Fetch licenses associated to the given pool
2475         my $callobj = {
2476                 method  => 'getSoftwareLicenses_listOfHashes',
2477                 params  => [ ],
2478                 id  => 1,
2479         };
2480         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2482         # Check Opsi error
2483         my ($res_error, $res_error_str) = &check_opsi_res($res);
2484         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2486         return ($res->result, 0);
2489 sub _getSoftwareLicenseUsages_listOfHashes {
2490         my %arg = (
2491                         'hostId' => "",
2492                         'licensePoolId' => "",
2493                         @_,
2494                         );
2496         # Fetch pool infos from Opsi server
2497         my $callobj = {
2498                 method  => 'getSoftwareLicenseUsages_listOfHashes',
2499                 params  => [ $arg{hostId}, $arg{licensePoolId} ],
2500                 id  => 1,
2501         };
2502         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2504         # Check Opsi error
2505         my ($res_error, $res_error_str) = &check_opsi_res($res);
2506         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2508         return ($res->result, 0);
2511 sub _removeSoftwareLicenseFromLicensePool {
2512         my %arg = (
2513                 'softwareLicenseId' => undef,
2514                 'licensePoolId' => undef,
2515                 @_,
2516                 );
2518         if (not defined $arg{softwareLicenseId} ) { 
2519                 return ("function requires softwareLicenseId as parameter", 1);
2520                 }
2521                 if (not defined $arg{licensePoolId} ) { 
2522                 return ("function requires licensePoolId as parameter", 1);
2523         }
2525         # Remove software license from license pool
2526         my $callobj = {
2527                 method  => 'removeSoftwareLicenseFromLicensePool',
2528                 params  => [ $arg{softwareLicenseId}, $arg{licensePoolId} ],
2529                 id  => 1,
2530         };
2531         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2533         # Check Opsi error
2534         my ($res_error, $res_error_str) = &check_opsi_res($res);
2535         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2537         return ($res->result, 0);
2540 sub _deleteSoftwareLicense {
2541         my %arg = (
2542                 'softwareLicenseId' => undef,
2543                 'removeFromPools' => "false",
2544                 @_,
2545                 );
2547         if (not defined $arg{softwareLicenseId} ) { 
2548                 return ("function requires softwareLicenseId as parameter", 1);
2549         }
2550         my $removeFromPools = "";
2551         if ((defined $arg{removeFromPools}) && ($arg{removeFromPools} eq "true")) { 
2552                 $removeFromPools = "removeFromPools";
2553         }
2555         # Fetch
2556         my $callobj = {
2557                 method  => 'deleteSoftwareLicense',
2558                 params  => [ $arg{softwareLicenseId}, $removeFromPools ],
2559                 id  => 1,
2560         };
2561         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2563         # Check Opsi error
2564         my ($res_error, $res_error_str) = &check_opsi_res($res);
2565         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2567         return ($res->result, 0);
2570 sub _getLicensePoolId {
2571         my %arg = (
2572                         'productId' => undef,
2573                         @_,
2574                         );
2575         
2576         if (not defined $arg{productId} ) {
2577                 return ("function requires productId as parameter", 1);
2578         }
2580     my $callobj = {
2581         method  => 'getLicensePoolId',
2582         params  => [ $arg{productId} ],
2583         id  => 1,
2584     };
2585     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2587         # Check Opsi error
2588         my ($res_error, $res_error_str) = &check_opsi_res($res);
2589         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2591         return ($res->result, 0);
2594 sub _getLicenseContract_hash {
2595         my %arg = (
2596                         'licenseContractId' => undef,
2597                         @_,
2598                         );
2599         
2600         if (not defined $arg{licenseContractId} ) {
2601                 return ("function requires licenseContractId as parameter", 1);
2602         }
2604     my $callobj = {
2605         method  => 'getLicenseContract_hash',
2606         params  => [ $arg{licenseContractId} ],
2607         id  => 1,
2608     };
2609     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2611         # Check Opsi error
2612         my ($res_error, $res_error_str) = &check_opsi_res($res);
2613         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2615         return ($res->result, 0);
2618 sub _createLicenseContract {
2619         my %arg = (
2620                         'licenseContractId' => undef,
2621                         'partner' => undef,
2622                         'conclusionDate' => undef,
2623                         'notificationDate' => undef,
2624                         'expirationDate' => undef,
2625                         'notes' => undef,
2626                         @_,
2627                         );
2629         # Create license contract at Opsi server
2630     my $callobj = {
2631         method  => 'createLicenseContract',
2632         params  => [ $arg{licenseContractId}, $arg{partner}, $arg{conclusionDate}, $arg{notificationDate}, $arg{expirationDate}, $arg{notes} ],
2633         id  => 1,
2634     };
2635     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2637         # Check Opsi error
2638         my ($res_error, $res_error_str) = &check_opsi_res($res);
2639         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2641         return ($res->result, 0);
2644 sub _createSoftwareLicense {
2645         my %arg = (
2646                         'softwareLicenseId' => undef,
2647                         'licenseContractId' => undef,
2648                         'licenseType' => undef,
2649                         'maxInstallations' => undef,
2650                         'boundToHost' => undef,
2651                         'expirationDate' => undef,
2652                         @_,
2653                         );
2655     my $callobj = {
2656         method  => 'createSoftwareLicense',
2657         params  => [ $arg{softwareLicenseId}, $arg{licenseContractId}, $arg{licenseType}, $arg{maxInstallations}, $arg{boundToHost}, $arg{expirationDate} ],
2658         id  => 1,
2659     };
2660     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2662         # Check Opsi error
2663         my ($res_error, $res_error_str) = &check_opsi_res($res);
2664         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2666         return ($res->result, 0);
2669 sub _addSoftwareLicenseToLicensePool {
2670         my %arg = (
2671             'softwareLicenseId' => undef,
2672             'licensePoolId' => undef,
2673             'licenseKey' => undef,
2674             @_,
2675             );
2677         if (not defined $arg{softwareLicenseId} ) {
2678                 return ("function requires softwareLicenseId as parameter", 1);
2679         }
2680         if (not defined $arg{licensePoolId} ) {
2681                 return ("function requires licensePoolId as parameter", 1);
2682         }
2684         # Add software license to license pool
2685         my $callobj = {
2686         method  => 'addSoftwareLicenseToLicensePool',
2687         params  => [ $arg{softwareLicenseId}, $arg{licensePoolId}, $arg{licenseKey} ],
2688         id  => 1,
2689     };
2690     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2692         # Check Opsi error
2693         my ($res_error, $res_error_str) = &check_opsi_res($res);
2694         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2696         return ($res->result, 0);
2699 1;