Code

fixing a tiny bug for 'removeLicense'
[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_listOfHashes",
33         "opsi_getLicensePools_listOfHashes",
34         "opsi_getLicenseInformationForProduct",
35         "opsi_getPool",
36         "opsi_removeLicense",
37         "opsi_test",
38    );
39 @EXPORT = @events;
41 use strict;
42 use warnings;
43 use GOSA::GosaSupportDaemon;
44 use Data::Dumper;
45 use XML::Quote qw(:all);
47 BEGIN {}
49 END {}
51 # ----------------------------------------------------------------------------
52 #                          D E C L A R A T I O N S
53 # ----------------------------------------------------------------------------
55 my $licenseTyp_hash = { 'OEM'=>'', 'VOLUME'=>'', 'RETAIL'=>''};
59 # ----------------------------------------------------------------------------
60 #                            S U B R O U T I N E S
61 # ----------------------------------------------------------------------------
64 ################################
65 #
66 # @brief A function returning a list of functions which are exported by importing the module.
67 # @return List of all provided functions
68 #
69 sub get_events {
70     return \@events;
71 }
73 ################################
74 #
75 # @brief Checks if there is a specified tag and if the the tag has a content.
76 # @return 0|1
77 #
78 sub _check_xml_tag_is_ok {
79         my ($msg_hash,$tag) = @_;
80         if (not defined $msg_hash->{$tag}) {
81                 $_ = "message contains no tag '$tag'";
82                 return 0;
83         }
84         if (ref @{$msg_hash->{$tag}}[0] eq 'HASH') {
85                 $_ = "message tag '$tag' has no content";
86                 return  0;
87         }
88         return 1;
89 }
91 ################################
92 #
93 # @brief Writes the log line and returns the error message for GOsa.
94 #
95 sub _give_feedback {
96         my ($msg, $msg_hash, $session_id, $error) = @_;
97         &main::daemon_log("$session_id ERROR: $error: ".$msg, 1);
98         my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{'source'}}[0], $error);
99         return &create_xml_string($out_hash);
102 ## @method opsi_add_product_to_client
103 # Adds an Opsi product to an Opsi client.
104 # @param msg - STRING - xml message with tags hostId and productId
105 # @param msg_hash - HASHREF - message information parsed into a hash
106 # @param session_id - INTEGER - POE session id of the processing of this message
107 # @return out_msg - STRING - feedback to GOsa in success and error case
108 sub opsi_add_product_to_client {
109     my ($msg, $msg_hash, $session_id) = @_;
110     my $header = @{$msg_hash->{'header'}}[0];
111     my $source = @{$msg_hash->{'source'}}[0];
112     my $target = @{$msg_hash->{'target'}}[0];
113     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
114     my ($hostId, $productId);
115     my $error = 0;
117     # Build return message
118     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
119     if (defined $forward_to_gosa) {
120         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
121     }
123     # Sanity check of needed parameter
124     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
125         $error++;
126         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
127         &add_content2xml_hash($out_hash, "error", "hostId");
128         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
130     }
131     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
132         $error++;
133         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
134         &add_content2xml_hash($out_hash, "error", "productId");
135         &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1); 
136     }
138     if (not $error) {
139         # Get hostId
140         $hostId = @{$msg_hash->{'hostId'}}[0];
141         &add_content2xml_hash($out_hash, "hostId", $hostId);
143         # Get productID
144         $productId = @{$msg_hash->{'productId'}}[0];
145         &add_content2xml_hash($out_hash, "productId", $productId);
147         # Do an action request for all these -> "setup".
148         my $callobj = {
149             method  => 'setProductActionRequest',
150             params  => [ $productId, $hostId, "setup" ],
151             id  => 1, }; 
153         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
154         my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
155         if ($sres_err){
156             &main::daemon_log("$session_id ERROR: cannot add product: ".$sres_err_string, 1);
157             &add_content2xml_hash($out_hash, "error", $sres_err_string);
158         }
159     } 
161     # return message
162     return ( &create_xml_string($out_hash) );
165 ## @method opsi_del_product_from_client
166 # Deletes an Opsi-product from an Opsi-client. 
167 # @param msg - STRING - xml message with tags hostId and productId
168 # @param msg_hash - HASHREF - message information parsed into a hash
169 # @param session_id - INTEGER - POE session id of the processing of this message
170 # @return out_msg - STRING - feedback to GOsa in success and error case
171 sub opsi_del_product_from_client {
172     my ($msg, $msg_hash, $session_id) = @_;
173     my $header = @{$msg_hash->{'header'}}[0];
174     my $source = @{$msg_hash->{'source'}}[0];
175     my $target = @{$msg_hash->{'target'}}[0];
176     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
177     my ($hostId, $productId);
178     my $error = 0;
179     my ($sres, $sres_err, $sres_err_string);
181     # Build return message
182     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
183     if (defined $forward_to_gosa) {
184         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
185     }
187     # Sanity check of needed parameter
188     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
189         $error++;
190         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
191         &add_content2xml_hash($out_hash, "error", "hostId");
192         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
194     }
195     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH')) {
196         $error++;
197         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
198         &add_content2xml_hash($out_hash, "error", "productId");
199         &main::daemon_log("$session_id ERROR: no productId specified or procutId tag invalid: $msg", 1); 
200     }
202     # All parameter available
203     if (not $error) {
204         # Get hostId
205         $hostId = @{$msg_hash->{'hostId'}}[0];
206         &add_content2xml_hash($out_hash, "hostId", $hostId);
208         # Get productID
209         $productId = @{$msg_hash->{'productId'}}[0];
210         &add_content2xml_hash($out_hash, "productId", $productId);
213 # : check the results for more than one entry which is currently installed
214         #$callobj = {
215         #    method  => 'getProductDependencies_listOfHashes',
216         #    params  => [ $productId ],
217         #    id  => 1, };
218         #
219         #my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
220         #my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
221         #if ($sres_err){
222         #  &main::daemon_log("ERROR: cannot perform dependency check: ".$sres_err_string, 1);
223         #  &add_content2xml_hash($out_hash, "error", $sres_err_string);
224         #  return ( &create_xml_string($out_hash) );
225         #}
228         # Check to get product action list 
229         my $callobj = {
230             method  => 'getPossibleProductActions_list',
231             params  => [ $productId ],
232             id  => 1, };
233         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
234         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
235         if ($sres_err){
236             &main::daemon_log("$session_id ERROR: cannot get product action list: ".$sres_err_string, 1);
237             &add_content2xml_hash($out_hash, "error", $sres_err_string);
238             $error++;
239         }
240     }
242     # Check action uninstall of product
243     if (not $error) {
244         my $uninst_possible= 0;
245         foreach my $r (@{$sres->result}) {
246             if ($r eq 'uninstall') {
247                 $uninst_possible= 1;
248             }
249         }
250         if (!$uninst_possible){
251             &main::daemon_log("$session_id ERROR: cannot uninstall product '$productId', product do not has the action 'uninstall'", 1);
252             &add_content2xml_hash($out_hash, "error", "cannot uninstall product '$productId', product do not has the action 'uninstall'");
253             $error++;
254         }
255     }
257     # Set product state to "none"
258     # Do an action request for all these -> "setup".
259     if (not $error) {
260         my $callobj = {
261             method  => 'setProductActionRequest',
262             params  => [ $productId, $hostId, "none" ],
263             id  => 1, 
264         }; 
265         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
266         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
267         if ($sres_err){
268             &main::daemon_log("$session_id ERROR: cannot delete product: ".$sres_err_string, 1);
269             &add_content2xml_hash($out_hash, "error", $sres_err_string);
270         }
271     }
273     # Return message
274     return ( &create_xml_string($out_hash) );
277 ## @method opsi_add_client
278 # Adds an Opsi client to Opsi.
279 # @param msg - STRING - xml message with tags hostId and macaddress
280 # @param msg_hash - HASHREF - message information parsed into a hash
281 # @param session_id - INTEGER - POE session id of the processing of this message
282 # @return out_msg - STRING - feedback to GOsa in success and error case
283 sub opsi_add_client {
284     my ($msg, $msg_hash, $session_id) = @_;
285     my $header = @{$msg_hash->{'header'}}[0];
286     my $source = @{$msg_hash->{'source'}}[0];
287     my $target = @{$msg_hash->{'target'}}[0];
288     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
289     my ($hostId, $mac);
290     my $error = 0;
291     my ($sres, $sres_err, $sres_err_string);
293     # Build return message with twisted target and source
294     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
295     if (defined $forward_to_gosa) {
296         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
297     }
299     # Sanity check of needed parameter
300     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
301         $error++;
302         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
303         &add_content2xml_hash($out_hash, "error", "hostId");
304         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
305     }
306     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH'))  {
307         $error++;
308         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
309         &add_content2xml_hash($out_hash, "error", "macaddress");
310         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
311     }
313     if (not $error) {
314         # Get hostId
315         $hostId = @{$msg_hash->{'hostId'}}[0];
316         &add_content2xml_hash($out_hash, "hostId", $hostId);
318         # Get macaddress
319         $mac = @{$msg_hash->{'macaddress'}}[0];
320         &add_content2xml_hash($out_hash, "macaddress", $mac);
322         my $name= $hostId;
323         $name=~ s/^([^.]+).*$/$1/;
324         my $domain= $hostId;
325         $domain=~ s/^[^.]+\.(.*)$/$1/;
326         my ($description, $notes, $ip);
328         if (defined @{$msg_hash->{'description'}}[0]){
329             $description = @{$msg_hash->{'description'}}[0];
330         }
331         if (defined @{$msg_hash->{'notes'}}[0]){
332             $notes = @{$msg_hash->{'notes'}}[0];
333         }
334         if (defined @{$msg_hash->{'ip'}}[0]){
335             $ip = @{$msg_hash->{'ip'}}[0];
336         }
338         my $callobj;
339         $callobj = {
340             method  => 'createClient',
341             params  => [ $name, $domain, $description, $notes, $ip, $mac ],
342             id  => 1,
343         };
345         $sres = $main::opsi_client->call($main::opsi_url, $callobj);
346         ($sres_err, $sres_err_string) = &check_opsi_res($sres);
347         if ($sres_err){
348             &main::daemon_log("$session_id ERROR: cannot create client: ".$sres_err_string, 1);
349             &add_content2xml_hash($out_hash, "error", $sres_err_string);
350         } else {
351             &main::daemon_log("$session_id INFO: add opsi client '$hostId' with mac '$mac'", 5); 
352         }
353     }
355     # Return message
356     return ( &create_xml_string($out_hash) );
359 ## @method opsi_modify_client
360 # Modifies the parameters description, mac or notes for an Opsi client if the corresponding message tags are given.
361 # @param msg - STRING - xml message with tag hostId and optional description, mac or notes
362 # @param msg_hash - HASHREF - message information parsed into a hash
363 # @param session_id - INTEGER - POE session id of the processing of this message    
364 # @return out_msg - STRING - feedback to GOsa in success and error case
365 sub opsi_modify_client {
366     my ($msg, $msg_hash, $session_id) = @_;
367     my $header = @{$msg_hash->{'header'}}[0];
368     my $source = @{$msg_hash->{'source'}}[0];
369     my $target = @{$msg_hash->{'target'}}[0];
370     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
371     my $hostId;
372     my $error = 0;
373     my ($sres, $sres_err, $sres_err_string);
375     # Build return message with twisted target and source
376     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
377     if (defined $forward_to_gosa) {
378         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
379     }
381     # Sanity check of needed parameter
382     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
383         $error++;
384         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
385         &add_content2xml_hash($out_hash, "error", "hostId");
386         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
387     }
389     if (not $error) {
390         # Get hostId
391         $hostId = @{$msg_hash->{'hostId'}}[0];
392         &add_content2xml_hash($out_hash, "hostId", $hostId);
393         my $name= $hostId;
394         $name=~ s/^([^.]+).*$/$1/;
395         my $domain= $hostId;
396         $domain=~ s/^[^.]+(.*)$/$1/;
398         # Modify description, notes or mac if defined
399         my ($description, $notes, $mac);
400         my $callobj;
401         if ((exists $msg_hash->{'description'}) && (@{$msg_hash->{'description'}} == 1) ){
402             $description = @{$msg_hash->{'description'}}[0];
403             $callobj = {
404                 method  => 'setHostDescription',
405                 params  => [ $hostId, $description ],
406                 id  => 1,
407             };
408             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
409             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
410             if ($sres_err){
411                 &main::daemon_log("ERROR: cannot set description: ".$sres_err_string, 1);
412                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
413             }
414         }
415         if ((exists $msg_hash->{'notes'}) && (@{$msg_hash->{'notes'}} == 1)) {
416             $notes = @{$msg_hash->{'notes'}}[0];
417             $callobj = {
418                 method  => 'setHostNotes',
419                 params  => [ $hostId, $notes ],
420                 id  => 1,
421             };
422             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
423             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
424             if ($sres_err){
425                 &main::daemon_log("ERROR: cannot set notes: ".$sres_err_string, 1);
426                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
427             }
428         }
429         if ((exists $msg_hash->{'mac'}) && (@{$msg_hash->{'mac'}} == 1)){
430             $mac = @{$msg_hash->{'mac'}}[0];
431             $callobj = {
432                 method  => 'setMacAddress',
433                 params  => [ $hostId, $mac ],
434                 id  => 1,
435             };
436             my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
437             my ($sres_err, $sres_err_string) = &check_opsi_res($sres);
438             if ($sres_err){
439                 &main::daemon_log("ERROR: cannot set mac address: ".$sres_err_string, 1);
440                 &add_content2xml_hash($out_hash, "error", $sres_err_string);
441             }
442         }
443     }
445     # Return message
446     return ( &create_xml_string($out_hash) );
449     
450 ## @method opsi_get_netboot_products
451 # Get netboot products for specific host.
452 # @param msg - STRING - xml message with tag hostId
453 # @param msg_hash - HASHREF - message information parsed into a hash
454 # @param session_id - INTEGER - POE session id of the processing of this message
455 # @return out_msg - STRING - feedback to GOsa in success and error case
456 sub opsi_get_netboot_products {
457     my ($msg, $msg_hash, $session_id) = @_;
458     my $header = @{$msg_hash->{'header'}}[0];
459     my $source = @{$msg_hash->{'source'}}[0];
460     my $target = @{$msg_hash->{'target'}}[0];
461     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
462     my $hostId;
463     my $xml_msg;
465     # Build return message with twisted target and source
466     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
467     if (defined $forward_to_gosa) {
468         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
469     }
471     # Get hostId if defined
472     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
473         $hostId = @{$msg_hash->{'hostId'}}[0];
474         &add_content2xml_hash($out_hash, "hostId", $hostId);
475     }
477     &add_content2xml_hash($out_hash, "xxx", "");
478     $xml_msg = &create_xml_string($out_hash);
479     # For hosts, only return the products that are or get installed
480     my $callobj;
481     $callobj = {
482         method  => 'getNetBootProductIds_list',
483         params  => [ ],
484         id  => 1,
485     };
486     &main::daemon_log("$session_id DEBUG: send callobj to opsi_client: ".&opsi_callobj2string($callobj), 7);
487     &main::daemon_log("$session_id DEBUG: opsi_url $main::opsi_url", 7);
488     &main::daemon_log("$session_id DEBUG: waiting for answer from opsi_client!", 7);
489     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
490     &main::daemon_log("$session_id DEBUG: get answer from opsi_client", 7);
491     my %r = ();
492     for (@{$res->result}) { $r{$_} = 1 }
494     if (not &check_opsi_res($res)){
496         if (defined $hostId){
498             $callobj = {
499                 method  => 'getProductStates_hash',
500                 params  => [ $hostId ],
501                 id  => 1,
502             };
504             my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
505             if (not &check_opsi_res($hres)){
506                 my $htmp= $hres->result->{$hostId};
508                 # check state != not_installed or action == setup -> load and add
509                 foreach my $product (@{$htmp}){
511                     if (!defined ($r{$product->{'productId'}})){
512                         next;
513                     }
515                     # Now we've a couple of hashes...
516                     if ($product->{'installationStatus'} ne "not_installed" or
517                             $product->{'actionRequest'} eq "setup"){
518                         my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
520                         $callobj = {
521                             method  => 'getProduct_hash',
522                             params  => [ $product->{'productId'} ],
523                             id  => 1,
524                         };
526                         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
527                         if (not &check_opsi_res($sres)){
528                             my $tres= $sres->result;
530                             my $name= xml_quote($tres->{'name'});
531                             my $r= $product->{'productId'};
532                             my $description= xml_quote($tres->{'description'});
533                             $name=~ s/\//\\\//;
534                             $description=~ s/\//\\\//;
535                             $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
536                         }
537                     }
538                 }
540             }
542         } else {
543             foreach my $r (@{$res->result}) {
544                 $callobj = {
545                     method  => 'getProduct_hash',
546                     params  => [ $r ],
547                     id  => 1,
548                 };
550                 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
551                 if (not &check_opsi_res($sres)){
552                     my $tres= $sres->result;
554                     my $name= xml_quote($tres->{'name'});
555                     my $description= xml_quote($tres->{'description'});
556                     $name=~ s/\//\\\//;
557                     $description=~ s/\//\\\//;
558                     $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
559                 }
560             }
562         }
563     }
564     $xml_msg=~ s/<xxx><\/xxx>//;
566     # Return message
567     return ( $xml_msg );
571 ## @method opsi_get_product_properties
572 # Get product properties for a product and a specific host or gobally for a product.
573 # @param msg - STRING - xml message with tags productId and optional hostId
574 # @param msg_hash - HASHREF - message information parsed into a hash
575 # @param session_id - INTEGER - POE session id of the processing of this message
576 # @return out_msg - STRING - feedback to GOsa in success and error case
577 sub opsi_get_product_properties {
578     my ($msg, $msg_hash, $session_id) = @_;
579     my $header = @{$msg_hash->{'header'}}[0];
580     my $source = @{$msg_hash->{'source'}}[0];
581     my $target = @{$msg_hash->{'target'}}[0];
582     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
583     my ($hostId, $productId);
584     my $xml_msg;
586     # Build return message with twisted target and source
587     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
588     if (defined $forward_to_gosa) {
589         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
590     }
592     # Sanity check of needed parameter
593     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
594         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
595         &add_content2xml_hash($out_hash, "error", "productId");
596         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
598         # Return message
599         return ( &create_xml_string($out_hash) );
600     }
602     # Get productid
603     $productId = @{$msg_hash->{'productId'}}[0];
604     &add_content2xml_hash($out_hash, "producId", "$productId");
606     # Get hostId if defined
607     if (defined @{$msg_hash->{'hostId'}}[0]){
608       $hostId = @{$msg_hash->{'hostId'}}[0];
609       &add_content2xml_hash($out_hash, "hostId", $hostId);
610     }
612     # Load actions
613     my $callobj = {
614       method  => 'getPossibleProductActions_list',
615       params  => [ $productId ],
616       id  => 1,
617     };
618     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
619     if (not &check_opsi_res($res)){
620       foreach my $action (@{$res->result}){
621         &add_content2xml_hash($out_hash, "action", $action);
622       }
623     }
625     # Add place holder
626     &add_content2xml_hash($out_hash, "xxx", "");
628     # Move to XML string
629     $xml_msg= &create_xml_string($out_hash);
631     # JSON Query
632     if (defined $hostId){
633       $callobj = {
634           method  => 'getProductProperties_hash',
635           params  => [ $productId, $hostId ],
636           id  => 1,
637       };
638     } else {
639       $callobj = {
640           method  => 'getProductProperties_hash',
641           params  => [ $productId ],
642           id  => 1,
643       };
644     }
645     $res = $main::opsi_client->call($main::opsi_url, $callobj);
647     # JSON Query 2
648     $callobj = {
649       method  => 'getProductPropertyDefinitions_listOfHashes',
650       params  => [ $productId ],
651       id  => 1,
652     };
654     # Assemble options
655     my $res2 = $main::opsi_client->call($main::opsi_url, $callobj);
656     my $values = {};
657     my $descriptions = {};
658     if (not &check_opsi_res($res2)){
659         my $r= $res2->result;
661           foreach my $entr (@$r){
662             # Unroll values
663             my $cnv;
664             if (UNIVERSAL::isa( $entr->{'values'}, "ARRAY" )){
665               foreach my $v (@{$entr->{'values'}}){
666                 $cnv.= "<value>$v</value>";
667               }
668             } else {
669               $cnv= $entr->{'values'};
670             }
671             $values->{$entr->{'name'}}= $cnv;
672             $descriptions->{$entr->{'name'}}= "<description>".$entr->{'description'}."</description>";
673           }
674     }
676     if (not &check_opsi_res($res)){
677         my $r= $res->result;
678         foreach my $key (keys %{$r}) {
679             my $item= "\n<item>";
680             my $value= $r->{$key};
681             my $dsc= "";
682             my $vals= "";
683             if (defined $descriptions->{$key}){
684               $dsc= $descriptions->{$key};
685             }
686             if (defined $values->{$key}){
687               $vals= $values->{$key};
688             }
689             $item.= "<$key>$dsc<default>".xml_quote($value)."</default>$vals</$key>";
690             $item.= "</item>";
691             $xml_msg=~ s/<xxx><\/xxx>/$item<xxx><\/xxx>/;
692         }
693     }
695     $xml_msg=~ s/<xxx><\/xxx>//;
697     # Return message
698     return ( $xml_msg );
702 ## @method opsi_set_product_properties
703 # 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.
704 # @param msg - STRING - xml message with tags productId, action, state and optional hostId, action and state
705 # @param msg_hash - HASHREF - message information parsed into a hash
706 # @param session_id - INTEGER - POE session id of the processing of this message
707 # @return out_msg - STRING - feedback to GOsa in success and error case
708 sub opsi_set_product_properties {
709     my ($msg, $msg_hash, $session_id) = @_;
710     my $header = @{$msg_hash->{'header'}}[0];
711     my $source = @{$msg_hash->{'source'}}[0];
712     my $target = @{$msg_hash->{'target'}}[0];
713     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
714     my ($productId, $hostId);
716     # Build return message with twisted target and source
717     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
718     if (defined $forward_to_gosa) {
719         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
720     }
722     # Sanity check of needed parameter
723     if ((not exists $msg_hash->{'productId'}) || (@{$msg_hash->{'productId'}} != 1) || (@{$msg_hash->{'productId'}}[0] eq ref 'HASH'))  {
724         &add_content2xml_hash($out_hash, "error_string", "no productId specified or productId tag invalid");
725         &add_content2xml_hash($out_hash, "error", "productId");
726         &main::daemon_log("$session_id ERROR: no productId specified or productId tag invalid: $msg", 1); 
727         return ( &create_xml_string($out_hash) );
728     }
729     if (not exists $msg_hash->{'item'}) {
730         &add_content2xml_hash($out_hash, "error_string", "message needs one xml-tag 'item' and within the xml-tags 'name' and 'value'");
731         &add_content2xml_hash($out_hash, "error", "item");
732         &main::daemon_log("$session_id ERROR: message needs one xml-tag 'item' and within the xml-tags 'name' and 'value': $msg", 1); 
733         return ( &create_xml_string($out_hash) );
734     } else {
735         if ((not exists @{$msg_hash->{'item'}}[0]->{'name'}) || (@{@{$msg_hash->{'item'}}[0]->{'name'}} != 1 )) {
736             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'name'");
737             &add_content2xml_hash($out_hash, "error", "name");
738             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'name': $msg", 1); 
739             return ( &create_xml_string($out_hash) );
740         }
741         if ((not exists @{$msg_hash->{'item'}}[0]->{'value'}) || (@{@{$msg_hash->{'item'}}[0]->{'value'}} != 1 )) {
742             &add_content2xml_hash($out_hash, "error_string", "message needs within the xml-tag 'item' one xml-tags 'value'");
743             &add_content2xml_hash($out_hash, "error", "value");
744             &main::daemon_log("$session_id ERROR: message needs within the xml-tag 'item' one xml-tags 'value': $msg", 1); 
745             return ( &create_xml_string($out_hash) );
746         }
747     }
748     # if no hostId is given, set_product_properties will act on globally
749     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} > 1))  {
750         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
751         &add_content2xml_hash($out_hash, "error", "hostId");
752         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
753         return ( &create_xml_string($out_hash) );
754     }
756         
757     # Get productId
758     $productId =  @{$msg_hash->{'productId'}}[0];
759     &add_content2xml_hash($out_hash, "productId", $productId);
761     # Get hostId if defined
762     if (exists $msg_hash->{'hostId'}){
763         $hostId = @{$msg_hash->{'hostId'}}[0];
764         &add_content2xml_hash($out_hash, "hostId", $hostId);
765     }
767     # Set product states if requested
768     if (defined @{$msg_hash->{'action'}}[0]){
769         &_set_action($productId, @{$msg_hash->{'action'}}[0], $hostId);
770     }
771     if (defined @{$msg_hash->{'state'}}[0]){
772         &_set_state($productId, @{$msg_hash->{'state'}}[0], $hostId);
773     }
775     # Find properties
776     foreach my $item (@{$msg_hash->{'item'}}){
777         # JSON Query
778         my $callobj;
780         if (defined $hostId){
781             $callobj = {
782                 method  => 'setProductProperty',
783                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0], $hostId ],
784                 id  => 1,
785             };
786         } else {
787             $callobj = {
788                 method  => 'setProductProperty',
789                 params  => [ $productId, $item->{'name'}[0], $item->{'value'}[0] ],
790                 id  => 1,
791             };
792         }
794         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
795         my ($res_err, $res_err_string) = &check_opsi_res($res);
797         if ($res_err){
798             &main::daemon_log("$session_id ERROR: communication failed while setting '".$item->{'name'}[0]."': ".$res_err_string, 1);
799             &add_content2xml_hash($out_hash, "error", $res_err_string);
800         }
801     }
804     # Return message
805     return ( &create_xml_string($out_hash) );
809 ## @method opsi_get_client_hardware
810 # Reports client hardware inventory.
811 # @param msg - STRING - xml message with tag hostId
812 # @param msg_hash - HASHREF - message information parsed into a hash
813 # @param session_id - INTEGER - POE session id of the processing of this message
814 # @return out_msg - STRING - feedback to GOsa in success and error case
815 sub opsi_get_client_hardware {
816     my ($msg, $msg_hash, $session_id) = @_;
817     my $header = @{$msg_hash->{'header'}}[0];
818     my $source = @{$msg_hash->{'source'}}[0];
819     my $target = @{$msg_hash->{'target'}}[0];
820     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
821     my $hostId;
822     my $error = 0;
823     my $xml_msg;
825     # Build return message with twisted target and source
826     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
827     if (defined $forward_to_gosa) {
828       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
829     }
831     # Sanity check of needed parameter
832     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
833         $error++;
834         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
835         &add_content2xml_hash($out_hash, "error", "hostId");
836         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
837     }
839     if (not $error) {
841     # Get hostId
842         $hostId = @{$msg_hash->{'hostId'}}[0];
843         &add_content2xml_hash($out_hash, "hostId", "$hostId");
844         &add_content2xml_hash($out_hash, "xxx", "");
845     }    
847     # Move to XML string
848     $xml_msg= &create_xml_string($out_hash);
849     
850     if (not $error) {
852     # JSON Query
853         my $callobj = {
854             method  => 'getHardwareInformation_hash',
855             params  => [ $hostId ],
856             id  => 1,
857         };
859         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
860         if (not &check_opsi_res($res)){
861             my $result= $res->result;
862             if (ref $result eq "HASH") {
863                 foreach my $r (keys %{$result}){
864                     my $item= "\n<item><id>".xml_quote($r)."</id>";
865                     my $value= $result->{$r};
866                     foreach my $sres (@{$value}){
868                         foreach my $dres (keys %{$sres}){
869                             if (defined $sres->{$dres}){
870                                 $item.= "<$dres>".xml_quote($sres->{$dres})."</$dres>";
871                             }
872                         }
874                     }
875                     $item.= "</item>";
876                     $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
878                 }
879             }
880         }
882         $xml_msg=~ s/<xxx><\/xxx>//;
884     }
886     # Return message
887     return ( $xml_msg );
891 ## @method opsi_list_clients
892 # Reports all Opsi clients. 
893 # @param msg - STRING - xml message 
894 # @param msg_hash - HASHREF - message information parsed into a hash
895 # @param session_id - INTEGER - POE session id of the processing of this message
896 # @return out_msg - STRING - feedback to GOsa in success and error case
897 sub opsi_list_clients {
898     my ($msg, $msg_hash, $session_id) = @_;
899     my $header = @{$msg_hash->{'header'}}[0];
900     my $source = @{$msg_hash->{'source'}}[0];
901     my $target = @{$msg_hash->{'target'}}[0];
902     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
904     # Build return message with twisted target and source
905     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
906     if (defined $forward_to_gosa) {
907       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
908     }
909     &add_content2xml_hash($out_hash, "xxx", "");
911     # Move to XML string
912     my $xml_msg= &create_xml_string($out_hash);
914     # JSON Query
915     my $callobj = {
916         method  => 'getClients_listOfHashes',
917         params  => [ ],
918         id  => 1,
919     };
920     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
921     if (not &check_opsi_res($res)){
922         foreach my $host (@{$res->result}){
923             my $item= "\n<item><name>".$host->{'hostId'}."</name>";
924             if (defined($host->{'description'})){
925                 $item.= "<description>".xml_quote($host->{'description'})."</description>";
926             }
927             if (defined($host->{'notes'})){
928                 $item.= "<notes>".xml_quote($host->{'notes'})."</notes>";
929             }
930             if (defined($host->{'lastSeen'})){
931                 $item.= "<lastSeen>".xml_quote($host->{'lastSeen'})."</lastSeen>";
932             }
934             $callobj = {
935               method  => 'getIpAddress',
936               params  => [ $host->{'hostId'} ],
937               id  => 1,
938             };
939             my $sres= $main::opsi_client->call($main::opsi_url, $callobj);
940             if ( not &check_opsi_res($sres)){
941               $item.= "<ip>".xml_quote($sres->result)."</ip>";
942             }
944             $callobj = {
945               method  => 'getMacAddress',
946               params  => [ $host->{'hostId'} ],
947               id  => 1,
948             };
949             $sres= $main::opsi_client->call($main::opsi_url, $callobj);
950             if ( not &check_opsi_res($sres)){
951                 $item.= "<mac>".xml_quote($sres->result)."</mac>";
952             }
953             $item.= "</item>";
954             $xml_msg=~ s%<xxx></xxx>%$item<xxx></xxx>%;
955         }
956     }
958     $xml_msg=~ s/<xxx><\/xxx>//;
959     return ( $xml_msg );
964 ## @method opsi_get_client_software
965 # Reports client software inventory.
966 # @param msg - STRING - xml message with tag hostId
967 # @param msg_hash - HASHREF - message information parsed into a hash
968 # @param session_id - INTEGER - POE session id of the processing of this message
969 # @return out_msg - STRING - feedback to GOsa in success and error case
970 sub opsi_get_client_software {
971     my ($msg, $msg_hash, $session_id) = @_;
972     my $header = @{$msg_hash->{'header'}}[0];
973     my $source = @{$msg_hash->{'source'}}[0];
974     my $target = @{$msg_hash->{'target'}}[0];
975     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
976     my $error = 0;
977     my $hostId;
978     my $xml_msg;
980     # Build return message with twisted target and source
981     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
982     if (defined $forward_to_gosa) {
983       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
984     }
986     # Sanity check of needed parameter
987     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
988         $error++;
989         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
990         &add_content2xml_hash($out_hash, "error", "hostId");
991         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
992     }
994     if (not $error) {
996     # Get hostId
997         $hostId = @{$msg_hash->{'hostId'}}[0];
998         &add_content2xml_hash($out_hash, "hostId", "$hostId");
999         &add_content2xml_hash($out_hash, "xxx", "");
1000     }
1002     $xml_msg= &create_xml_string($out_hash);
1004     if (not $error) {
1006     # JSON Query
1007         my $callobj = {
1008             method  => 'getSoftwareInformation_hash',
1009             params  => [ $hostId ],
1010             id  => 1,
1011         };
1013         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1014         if (not &check_opsi_res($res)){
1015             my $result= $res->result;
1016         }
1018         $xml_msg=~ s/<xxx><\/xxx>//;
1020     }
1022     # Return message
1023     return ( $xml_msg );
1027 ## @method opsi_get_local_products
1028 # Reports product for given hostId or globally.
1029 # @param msg - STRING - xml message with optional tag hostId
1030 # @param msg_hash - HASHREF - message information parsed into a hash
1031 # @param session_id - INTEGER - POE session id of the processing of this message
1032 # @return out_msg - STRING - feedback to GOsa in success and error case
1033 sub opsi_get_local_products {
1034     my ($msg, $msg_hash, $session_id) = @_;
1035     my $header = @{$msg_hash->{'header'}}[0];
1036     my $source = @{$msg_hash->{'source'}}[0];
1037     my $target = @{$msg_hash->{'target'}}[0];
1038     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1039     my $hostId;
1041     # Build return message with twisted target and source
1042     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1043     if (defined $forward_to_gosa) {
1044         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1045     }
1046     &add_content2xml_hash($out_hash, "xxx", "");
1048     # Get hostId if defined
1049     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} == 1))  {
1050         $hostId = @{$msg_hash->{'hostId'}}[0];
1051         &add_content2xml_hash($out_hash, "hostId", $hostId);
1052     }
1054     # Move to XML string
1055     my $xml_msg= &create_xml_string($out_hash);
1057     # For hosts, only return the products that are or get installed
1058     my $callobj;
1059     $callobj = {
1060         method  => 'getLocalBootProductIds_list',
1061         params  => [ ],
1062         id  => 1,
1063     };
1065     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1066     my %r = ();
1067     for (@{$res->result}) { $r{$_} = 1 }
1069     if (not &check_opsi_res($res)){
1071         if (defined $hostId){
1072             $callobj = {
1073                 method  => 'getProductStates_hash',
1074                 params  => [ $hostId ],
1075                 id  => 1,
1076             };
1078             my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1079             if (not &check_opsi_res($hres)){
1080                 my $htmp= $hres->result->{$hostId};
1082                 # Check state != not_installed or action == setup -> load and add
1083                 foreach my $product (@{$htmp}){
1085                     if (!defined ($r{$product->{'productId'}})){
1086                         next;
1087                     }
1089                     # Now we've a couple of hashes...
1090                     if ($product->{'installationStatus'} ne "not_installed" or
1091                             $product->{'actionRequest'} eq "setup"){
1092                         my $state= "<state>".$product->{'installationStatus'}."</state><action>".$product->{'actionRequest'}."</action>";
1094                         $callobj = {
1095                             method  => 'getProduct_hash',
1096                             params  => [ $product->{'productId'} ],
1097                             id  => 1,
1098                         };
1100                         my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1101                         if (not &check_opsi_res($sres)){
1102                             my $tres= $sres->result;
1104                             my $name= xml_quote($tres->{'name'});
1105                             my $r= $product->{'productId'};
1106                             my $description= xml_quote($tres->{'description'});
1107                             $name=~ s/\//\\\//;
1108                             $description=~ s/\//\\\//;
1109                             $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item>$state<xxx><\/xxx>/;
1110                         }
1112                     }
1113                 }
1115             }
1117         } else {
1118             foreach my $r (@{$res->result}) {
1119                 $callobj = {
1120                     method  => 'getProduct_hash',
1121                     params  => [ $r ],
1122                     id  => 1,
1123                 };
1125                 my $sres = $main::opsi_client->call($main::opsi_url, $callobj);
1126                 if (not &check_opsi_res($sres)){
1127                     my $tres= $sres->result;
1129                     my $name= xml_quote($tres->{'name'});
1130                     my $description= xml_quote($tres->{'description'});
1131                     $name=~ s/\//\\\//;
1132                     $description=~ s/\//\\\//;
1133                     $xml_msg=~ s/<xxx><\/xxx>/\n<item><productId>$r<\/productId><name>$name<\/name><description>$description<\/description><\/item><xxx><\/xxx>/;
1134                 }
1136             }
1138         }
1139     }
1141     $xml_msg=~ s/<xxx><\/xxx>//;
1143     # Retrun Message
1144     return ( $xml_msg );
1148 ## @method opsi_del_client
1149 # Deletes a client from Opsi.
1150 # @param msg - STRING - xml message with tag hostId
1151 # @param msg_hash - HASHREF - message information parsed into a hash
1152 # @param session_id - INTEGER - POE session id of the processing of this message
1153 # @return out_msg - STRING - feedback to GOsa in success and error case
1154 sub opsi_del_client {
1155     my ($msg, $msg_hash, $session_id) = @_;
1156     my $header = @{$msg_hash->{'header'}}[0];
1157     my $source = @{$msg_hash->{'source'}}[0];
1158     my $target = @{$msg_hash->{'target'}}[0];
1159     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1160     my $hostId;
1161     my $error = 0;
1163     # Build return message with twisted target and source
1164     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1165     if (defined $forward_to_gosa) {
1166       &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1167     }
1169     # Sanity check of needed parameter
1170     if ((exists $msg_hash->{'hostId'}) && (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1171         $error++;
1172         &add_content2xml_hash($out_hash, "error_string", "hostId contains no or more than one values");
1173         &add_content2xml_hash($out_hash, "error", "hostId");
1174         &main::daemon_log("$session_id ERROR: hostId contains no or more than one values: $msg", 1); 
1175     }
1177     if (not $error) {
1179     # Get hostId
1180         $hostId = @{$msg_hash->{'hostId'}}[0];
1181         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1183     # JSON Query
1184         my $callobj = {
1185             method  => 'deleteClient',
1186             params  => [ $hostId ],
1187             id  => 1,
1188         };
1189         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1190     }
1192     # Move to XML string
1193     my $xml_msg= &create_xml_string($out_hash);
1195     # Return message
1196     return ( $xml_msg );
1200 ## @method opsi_install_client
1201 # Set a client in Opsi to install and trigger a wake on lan message (WOL).  
1202 # @param msg - STRING - xml message with tags hostId, macaddress
1203 # @param msg_hash - HASHREF - message information parsed into a hash
1204 # @param session_id - INTEGER - POE session id of the processing of this message
1205 # @return out_msg - STRING - feedback to GOsa in success and error case
1206 sub opsi_install_client {
1207     my ($msg, $msg_hash, $session_id) = @_;
1208     my $header = @{$msg_hash->{'header'}}[0];
1209     my $source = @{$msg_hash->{'source'}}[0];
1210     my $target = @{$msg_hash->{'target'}}[0];
1211     my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
1214     my ($hostId, $macaddress);
1216     my $error = 0;
1217     my @out_msg_l;
1219     # Build return message with twisted target and source
1220     my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1221     if (defined $forward_to_gosa) {
1222         &add_content2xml_hash($out_hash, "forward_to_gosa", $forward_to_gosa);
1223     }
1225     # Sanity check of needed parameter
1226     if ((not exists $msg_hash->{'hostId'}) || (@{$msg_hash->{'hostId'}} != 1) || (@{$msg_hash->{'hostId'}}[0] eq ref 'HASH'))  {
1227         $error++;
1228         &add_content2xml_hash($out_hash, "error_string", "no hostId specified or hostId tag invalid");
1229         &add_content2xml_hash($out_hash, "error", "hostId");
1230         &main::daemon_log("$session_id ERROR: no hostId specified or hostId tag invalid: $msg", 1); 
1231     }
1232     if ((not exists $msg_hash->{'macaddress'}) || (@{$msg_hash->{'macaddress'}} != 1) || (@{$msg_hash->{'macaddress'}}[0] eq ref 'HASH') )  {
1233         $error++;
1234         &add_content2xml_hash($out_hash, "error_string", "no macaddress specified or macaddress tag invalid");
1235         &add_content2xml_hash($out_hash, "error", "macaddress");
1236         &main::daemon_log("$session_id ERROR: no macaddress specified or macaddress tag invalid: $msg", 1); 
1237     } else {
1238         if ((exists $msg_hash->{'macaddress'}) && 
1239                 ($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)) {  
1240             $macaddress = $1; 
1241         } else { 
1242             $error ++; 
1243             &add_content2xml_hash($out_hash, "error_string", "given mac address is not correct");
1244             &add_content2xml_hash($out_hash, "error", "macaddress");
1245             &main::daemon_log("$session_id ERROR: given mac address is not correct: $msg", 1); 
1246         }
1247     }
1249     if (not $error) {
1251     # Get hostId
1252         $hostId = @{$msg_hash->{'hostId'}}[0];
1253         &add_content2xml_hash($out_hash, "hostId", "$hostId");
1255         # Load all products for this host with status != "not_installed" or actionRequest != "none"
1256         my $callobj = {
1257             method  => 'getProductStates_hash',
1258             params  => [ $hostId ],
1259             id  => 1,
1260         };
1262         my $hres = $main::opsi_client->call($main::opsi_url, $callobj);
1263         if (not &check_opsi_res($hres)){
1264             my $htmp= $hres->result->{$hostId};
1266             # check state != not_installed or action == setup -> load and add
1267             foreach my $product (@{$htmp}){
1268                 # Now we've a couple of hashes...
1269                 if ($product->{'installationStatus'} ne "not_installed" or
1270                         $product->{'actionRequest'} ne "none"){
1272                     # Do an action request for all these -> "setup".
1273                     $callobj = {
1274                         method  => 'setProductActionRequest',
1275                         params  => [ $product->{'productId'}, $hostId, "setup" ],
1276                         id  => 1,
1277                     };
1278                     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1279                     my ($res_err, $res_err_string) = &check_opsi_res($res);
1280                     if ($res_err){
1281                         &main::daemon_log("$session_id ERROR: cannot set product action request for '$hostId': ".$product->{'productId'}, 1);
1282                     } else {
1283                         &main::daemon_log("$session_id INFO: requesting 'setup' for '".$product->{'productId'}."' on $hostId", 1);
1284                     }
1285                 }
1286             }
1287         }
1288         push(@out_msg_l, &create_xml_string($out_hash));
1289     
1291     # Build wakeup message for client
1292         if (not $error) {
1293             my $wakeup_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
1294             &add_content2xml_hash($wakeup_hash, 'macaddress', $macaddress);
1295             my $wakeup_msg = &create_xml_string($wakeup_hash);
1296             push(@out_msg_l, $wakeup_msg);
1298             # invoke trigger wake for this gosa-si-server
1299             &main::server_server_com::trigger_wake($wakeup_msg, $wakeup_hash, $session_id);
1300         }
1301     }
1302     
1303     # Return messages
1304     return @out_msg_l;
1308 ## @method _set_action
1309 # Set action for an Opsi client
1310 # @param product - STRING - Opsi product
1311 # @param action - STRING - action
1312 # @param hostId - STRING - Opsi hostId
1313 sub _set_action {
1314   my $product= shift;
1315   my $action = shift;
1316   my $hostId = shift;
1317   my $callobj;
1319   $callobj = {
1320     method  => 'setProductActionRequest',
1321     params  => [ $product, $hostId, $action],
1322     id  => 1,
1323   };
1325   $main::opsi_client->call($main::opsi_url, $callobj);
1328 ## @method _set_state
1329 # Set state for an Opsi client
1330 # @param product - STRING - Opsi product
1331 # @param action - STRING - state
1332 # @param hostId - STRING - Opsi hostId
1333 sub _set_state {
1334   my $product = shift;
1335   my $state = shift;
1336   my $hostId = shift;
1337   my $callobj;
1339   $callobj = {
1340     method  => 'setProductState',
1341     params  => [ $product, $hostId, $state ],
1342     id  => 1,
1343   };
1345   $main::opsi_client->call($main::opsi_url, $callobj);
1348 ################################
1350 # @brief Create a license pool at Opsi server.
1351 # @param licensePoolId The name of the pool (optional). 
1352 # @param description The description of the pool (optional).
1353 # @param productIds A list of assigned porducts of the pool (optional). 
1354 # @param windowsSoftwareIds A list of windows software IDs associated to the pool (optional). 
1356 sub opsi_createLicensePool {
1357     my ($msg, $msg_hash, $session_id) = @_;
1358     my $header = @{$msg_hash->{'header'}}[0];
1359     my $source = @{$msg_hash->{'source'}}[0];
1360     my $target = @{$msg_hash->{'target'}}[0];
1361         my $out_hash;
1362         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1363         my $description = defined $msg_hash->{'description'} ? @{$msg_hash->{'description'}}[0] : undef;
1364         my @productIds = defined $msg_hash->{'productIds'} ? $msg_hash->{'productIds'} : undef;
1365         my @windowsSoftwareIds = defined $msg_hash->{'windowsSoftwareIds'} ? $msg_hash->{'windowsSoftwareIds'} : undef;
1367         # Create license Pool
1368     my $callobj = {
1369         method  => 'createLicensePool',
1370         params  => [ $licensePoolId, $description, @productIds, @windowsSoftwareIds],
1371         id  => 1,
1372     };
1373     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1375         # Check Opsi error
1376         my ($res_error, $res_error_str) = &check_opsi_res($res);
1377         if ($res_error){
1378                 # Create error message
1379                 &main::daemon_log("$session_id ERROR: cannot create license pool at Opsi server: ".$res_error_str, 1);
1380                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1381                 return ( &create_xml_string($out_hash) );
1382         }
1384         # Create function result message
1385         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source, $res->result);
1387         return ( &create_xml_string($out_hash) );
1390 ################################
1392 # @brief Return licensePoolId, description, productIds and windowsSoftwareIds for all found license pools.
1394 sub opsi_getLicensePools_listOfHashes {
1395     my ($msg, $msg_hash, $session_id) = @_;
1396     my $header = @{$msg_hash->{'header'}}[0];
1397     my $source = @{$msg_hash->{'source'}}[0];
1398         my $out_hash;
1400         # Fetch infos from Opsi server
1401     my $callobj = {
1402         method  => 'getLicensePools_listOfHashes',
1403         params  => [ ],
1404         id  => 1,
1405     };
1406     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1408         # Check Opsi error
1409         my ($res_error, $res_error_str) = &check_opsi_res($res);
1410         if ($res_error){
1411                 # Create error message
1412                 &main::daemon_log("$session_id ERROR: cannot get license pool ID list from Opsi server: ".$res_error_str, 1);
1413                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1414                 return ( &create_xml_string($out_hash) );
1415         }
1417         # Create function result message
1418         my $res_hash = { 'hit'=> [] };
1419         foreach my $licensePool ( @{$res->result}) {
1420                 my $licensePool_hash = { 'licensePoolId' => [$licensePool->{'licensePoolId'}],
1421                         'description' => [$licensePool->{'description'}],
1422                         'productIds' => $licensePool->{'productIds'},
1423                         'windowsSoftwareIds' => $licensePool->{'windowsSoftwareIds'},
1424                         };
1425                 push( @{$res_hash->{hit}}, $licensePool_hash );
1426         }
1428         # Create function result message
1429         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1430         $out_hash->{result} = [$res_hash];
1432         return ( &create_xml_string($out_hash) );
1435 ################################
1437 # @brief Return productIds, windowsSoftwareIds and description for a given licensePoolId
1438 # @param licensePoolId The name of the pool. 
1440 sub opsi_getLicensePool_hash {
1441     my ($msg, $msg_hash, $session_id) = @_;
1442     my $header = @{$msg_hash->{'header'}}[0];
1443     my $source = @{$msg_hash->{'source'}}[0];
1444     my $target = @{$msg_hash->{'target'}}[0];
1445     my $licensePoolId;
1446         my $out_hash;
1448         # Check input sanity
1449         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1450                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1451         } else {
1452                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1453         }
1455         # Fetch infos from Opsi server
1456     my $callobj = {
1457         method  => 'getLicensePool_hash',
1458         params  => [ $licensePoolId ],
1459         id  => 1,
1460     };
1461     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1463         # Check Opsi error
1464         my ($res_error, $res_error_str) = &check_opsi_res($res);
1465         if ($res_error){
1466                 # Create error message
1467                 &main::daemon_log("$session_id ERROR: cannot get license pool from Opsi server: ".$res_error_str, 1);
1468                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source);
1469                 &add_content2xml_hash($out_hash, "error", $res_error_str);
1470                 return ( &create_xml_string($out_hash) );
1471         }
1473         # Create function result message
1474         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1475         &add_content2xml_hash($out_hash, "licensePoolId", $res->result->{'licensePoolId'});
1476         &add_content2xml_hash($out_hash, "description", $res->result->{'description'});
1477         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->result->{'productIds'} });
1478         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->result->{'windowsSoftwareIds'} });
1480         return ( &create_xml_string($out_hash) );
1483 ################################
1485 # @brief Returns softwareLicenseId, notes, licenseKey, hostId and licensePoolId for optional given licensePoolId and hostId
1486 # @param hostid Something like client_1.intranet.mydomain.de (optional).
1487 # @param licensePoolId The name of the pool (optional). 
1488
1489 sub opsi_getSoftwareLicenseUsages_listOfHashes {
1490         my ($msg, $msg_hash, $session_id) = @_;
1491         my $header = @{$msg_hash->{'header'}}[0];
1492         my $source = @{$msg_hash->{'source'}}[0];
1493         my $target = @{$msg_hash->{'target'}}[0];
1494         my $licensePoolId = defined $msg_hash->{'licensePoolId'} ? @{$msg_hash->{'licensePoolId'}}[0] : undef;
1495         my $hostId = defined $msg_hash->{'hostId'} ? @{$msg_hash->{'hostId'}}[0] : undef;
1496         my $out_hash;
1498         # Fetch information from Opsi server
1499         my $callobj = {
1500                 method  => 'getSoftwareLicenseUsages_listOfHashes',
1501                 params  => [  $hostId, $licensePoolId ],
1502                 id  => 1,
1503         };
1504         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1506         # Check Opsi error
1507         my ($res_error, $res_error_str) = &check_opsi_res($res);
1508         if ($res_error){
1509                 # Create error message
1510                 &main::daemon_log("$session_id ERROR: cannot fetch software licenses from license pool '$licensePoolId': ".$res_error_str, 1);
1511                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1512                 return ( &create_xml_string($out_hash) );
1513         }
1515         # Parse Opsi result
1516         my $res_hash = { 'hit'=> [] };
1517         foreach my $license ( @{$res->result}) {
1518                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
1519                         'notes' => [$license->{'notes'}],
1520                         'licenseKey' => [$license->{'licenseKey'}],
1521                         'hostId' => [$license->{'hostId'}],
1522                         'licensePoolId' => [$license->{'licensePoolId'}],
1523                         };
1524                 push( @{$res_hash->{hit}}, $license_hash );
1525         }
1527         # Create function result message
1528         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1529         $out_hash->{result} = [$res_hash];
1531         return ( &create_xml_string($out_hash) );
1534 ################################
1536 # @brief Returns expirationDate, boundToHost, maxInstallation, licenseTyp, licensePoolIds and licenseKeys for a given softwareLicense ID.
1537 # @param softwareLicenseId Identificator of a license.
1539 sub opsi_getSoftwareLicense_hash {
1540         my ($msg, $msg_hash, $session_id) = @_;
1541         my $header = @{$msg_hash->{'header'}}[0];
1542         my $source = @{$msg_hash->{'source'}}[0];
1543         my $target = @{$msg_hash->{'target'}}[0];
1544         my $softwareLicenseId;
1545         my $out_hash;
1547         # Check input sanity
1548         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
1549                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
1550         } else {
1551                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1552         }
1554         my $callobj = {
1555                 method  => 'getSoftwareLicense_hash',
1556                 params  => [ $softwareLicenseId ],
1557                 id  => 1,
1558         };
1559         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1561         # Check Opsi error
1562         my ($res_error, $res_error_str) = &check_opsi_res($res);
1563         if ($res_error){
1564                 # Create error message
1565                 &main::daemon_log("$session_id ERROR: cannot fetch information for license '$softwareLicenseId': ".$res_error_str, 1);
1566                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1567                 return ( &create_xml_string($out_hash) );
1568         }
1569         
1570         # Create function result message
1571         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1572         &add_content2xml_hash($out_hash, "expirationDate", $res->result->{'expirationDate'});
1573         &add_content2xml_hash($out_hash, "boundToHost", $res->result->{'boundToHost'});
1574         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{'maxInstallations'});
1575         &add_content2xml_hash($out_hash, "licenseTyp", $res->result->{'licenseTyp'});
1576         foreach my $licensePoolId ( @{$res->result->{'licensePoolIds'}}) {
1577                 &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1578                 &add_content2xml_hash($out_hash, $licensePoolId, $res->result->{'licenseKeys'}->{$licensePoolId});
1579         }
1581         return ( &create_xml_string($out_hash) );
1584 ################################
1586 # @brief Delete licnese pool by license pool ID. A pool can only be deleted if there are no software licenses bound to the pool. 
1587 # The fixed parameter deleteLicenses=True specifies that all software licenses bound to the pool are being deleted. 
1588 # @param licensePoolId The name of the pool. 
1590 sub opsi_deleteLicensePool {
1591         my ($msg, $msg_hash, $session_id) = @_;
1592     my $header = @{$msg_hash->{'header'}}[0];
1593     my $source = @{$msg_hash->{'source'}}[0];
1594     my $target = @{$msg_hash->{'target'}}[0];
1595     my $licensePoolId;
1596         my $out_hash;
1598         # Check input sanity
1599         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1600                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1601         } else {
1602                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1603         }
1605         # Fetch softwareLicenseIds used in license pool
1606         # This has to be done because function deleteLicensePool deletes the pool and the corresponding software licenses
1607         # but not the license contracts of the software licenses. In our case each software license has exactly one license contract. 
1608         my $callobj = {
1609                 method  => 'getSoftwareLicenses_listOfHashes',
1610                 params  => [ ],
1611                 id  => 1,
1612         };
1613         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1615         # Keep list of licenseContractIds in mind to delete it after the deletion of the software licenses
1616         my @lCI_toBeDeleted;
1617         foreach my $softwareLicenseHash ( @{$res->result} ) {
1618                 if ((@{$softwareLicenseHash->{'licensePoolIds'}} == 0) || (@{$softwareLicenseHash->{'licensePoolIds'}}[0] ne $licensePoolId)) { 
1619                         next; 
1620                 }  
1621                 push (@lCI_toBeDeleted, $softwareLicenseHash->{'licenseContractId'});
1622         }
1624         # Delete license pool at Opsi server
1625     $callobj = {
1626         method  => 'deleteLicensePool',
1627         params  => [ $licensePoolId, 'deleteLicenses=True'  ],
1628         id  => 1,
1629     };
1630     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1631         my ($res_error, $res_error_str) = &check_opsi_res($res);
1632         if ($res_error){
1633                 # Create error message
1634                 &main::daemon_log("$session_id ERROR: cannot delete license pool at Opsi server: ".$res_error_str, 1);
1635                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1636                 return ( &create_xml_string($out_hash) );
1637         } 
1639         # Delete each license contract connected with the license pool
1640         foreach my $licenseContractId ( @lCI_toBeDeleted ) {
1641                 my $callobj = {
1642                         method  => 'deleteLicenseContract',
1643                         params  => [ $licenseContractId ],
1644                         id  => 1,
1645                 };
1646                 my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1647                 my ($res_error, $res_error_str) = &check_opsi_res($res);
1648                 if ($res_error){
1649                         # Create error message
1650                         &main::daemon_log("$session_id ERROR: cannot delete license contract '$licenseContractId' connected with license pool '$licensePoolId' at Opsi server: ".$res_error_str, 1);
1651                         $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1652                         return ( &create_xml_string($out_hash) );
1653                 }
1654         }
1656         # Create function result message
1657         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1658         
1659         return ( &create_xml_string($out_hash) );
1662 ################################
1664 # @brief Create a license contract, create a software license and add the software license to the license pool
1665 # @param licensePoolId The name of the pool the license should be assigned.
1666 # @param licenseKey The license key.
1667 # @param partner Name of the license partner (optional).
1668 # @param conclusionDate Date of conclusion of license contract (optional)
1669 # @param notificationDate Date of notification that license is running out soon (optional).
1670 # @param notes This is the place for some notes (optional)
1671 # @param softwareLicenseId Identificator of a license (optional).
1672 # @param licenseTyp Typ of a licnese, either "OEM", "VOLUME" or "RETAIL" (optional).
1673 # @param maxInstallations The number of clients use this license (optional). 
1674 # @param boundToHost The name of the client the license is bound to (optional).
1675 # @param expirationDate The date when the license is running down (optional). 
1677 sub opsi_createLicense {
1678         my ($msg, $msg_hash, $session_id) = @_;
1679     my $header = @{$msg_hash->{'header'}}[0];
1680     my $source = @{$msg_hash->{'source'}}[0];
1681     my $target = @{$msg_hash->{'target'}}[0];
1682         my $partner = defined $msg_hash->{'partner'} ? @{$msg_hash->{'partner'}}[0] : undef;
1683         my $conclusionDate = defined $msg_hash->{'conclusionDate'} ? @{$msg_hash->{'conclusionDate'}}[0] : undef;
1684         my $notificationDate = defined $msg_hash->{'notificationDate'} ? @{$msg_hash->{'notificationDate'}}[0] : undef;
1685         my $notes = defined $msg_hash->{'notes'} ? @{$msg_hash->{'notes'}}[0] : undef;
1686         my $licenseContractId;
1687         my $softwareLicenseId = defined $msg_hash->{'licenseId'} ? @{$msg_hash->{'licenseId'}}[0] : undef;
1688         my $licenseType = defined $msg_hash->{'licenseType'} ? @{$msg_hash->{'licenseType'}}[0] : undef;
1689         my $maxInstallations = defined $msg_hash->{'maxInstallations'} ? @{$msg_hash->{'maxInstallations'}}[0] : undef;
1690         my $boundToHost = defined $msg_hash->{'boundToHost'} ? @{$msg_hash->{'boundToHost'}}[0] : undef;
1691         my $expirationDate = defined $msg_hash->{'expirationDate'} ? @{$msg_hash->{'expirationDate'}}[0] : undef;
1692         my $licensePoolId;
1693         my $licenseKey;
1694         my $out_hash;
1696         # Check input sanity
1697         if (&_check_xml_tag_is_ok ($msg_hash, 'licenseKey')) {
1698                 $licenseKey = @{$msg_hash->{'licenseKey'}}[0];
1699         } else {
1700                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1701         }
1702         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1703                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1704         } else {
1705                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1706         }
1707         if ((defined $licenseType) && (not exists $licenseTyp_hash->{$licenseType})) {
1708                 return ( &_give_feedback($msg, $msg_hash, $session_id, "The typ of a license can be either 'OEM', 'VOLUME' or 'RETAIL'."));
1709         }
1711         # Create license contract at Opsi server
1712     my $callobj = {
1713         method  => 'createLicenseContract',
1714         params  => [ undef, $partner, $conclusionDate, $notificationDate, undef, $notes ],
1715         id  => 1,
1716     };
1717     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1719         # Check Opsi error
1720         my ($res_error, $res_error_str) = &check_opsi_res($res);
1721         if ($res_error){
1722                 # Create error message
1723                 &main::daemon_log("$session_id ERROR: cannot create license contract at Opsi server: ".$res_error_str, 1);
1724                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1725                 return ( &create_xml_string($out_hash) );
1726         }
1727         
1728         $licenseContractId = $res->result;
1730         # Create software license at Opsi server
1731     $callobj = {
1732         method  => 'createSoftwareLicense',
1733         params  => [ $softwareLicenseId, $licenseContractId, $licenseType, $maxInstallations, $boundToHost, $expirationDate ],
1734         id  => 1,
1735     };
1736     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1738         # Check Opsi error
1739         ($res_error, $res_error_str) = &check_opsi_res($res);
1740         if ($res_error){
1741                 # Create error message
1742                 &main::daemon_log("$session_id ERROR: cannot create software license at Opsi server: ".$res_error_str, 1);
1743                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1744                 return ( &create_xml_string($out_hash) );
1745         }
1747         $softwareLicenseId = $res->result;
1749         # Add software license to license pool
1750         $callobj = {
1751         method  => 'addSoftwareLicenseToLicensePool',
1752         params  => [ $softwareLicenseId, $licensePoolId, $licenseKey ],
1753         id  => 1,
1754     };
1755     $res = $main::opsi_client->call($main::opsi_url, $callobj);
1757         # Check Opsi error
1758         ($res_error, $res_error_str) = &check_opsi_res($res);
1759         if ($res_error){
1760                 # Create error message
1761                 &main::daemon_log("$session_id ERROR: cannot add software license to license pool at Opsi server: ".$res_error_str, 1);
1762                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1763                 return ( &create_xml_string($out_hash) );
1764         }
1766         # Create function result message
1767         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1768         
1769         return ( &create_xml_string($out_hash) );
1772 ################################
1774 # @brief Assign a software license to a host
1775 # @param hostid Something like client_1.intranet.mydomain.de
1776 # @param licensePoolId The name of the pool.
1778 sub opsi_assignSoftwareLicenseToHost {
1779         my ($msg, $msg_hash, $session_id) = @_;
1780     my $header = @{$msg_hash->{'header'}}[0];
1781     my $source = @{$msg_hash->{'source'}}[0];
1782     my $target = @{$msg_hash->{'target'}}[0];
1783         my $hostId;
1784         my $licensePoolId;
1786         # Check input sanity
1787         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1788                 $hostId = @{$msg_hash->{'hostId'}}[0];
1789         } else {
1790                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1791         }
1792         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1793                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1794         } else {
1795                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1796         }
1798         # Assign a software license to a host
1799         my $callobj = {
1800         method  => 'getAndAssignSoftwareLicenseKey',
1801         params  => [ $hostId, $licensePoolId ],
1802         id  => 1,
1803     };
1804     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1806         # Check Opsi error
1807         my ($res_error, $res_error_str) = &check_opsi_res($res);
1808         if ($res_error){
1809                 # Create error message
1810                 &main::daemon_log("$session_id ERROR: cannot assign a software license to a host at Opsi server: ".$res_error_str, 1);
1811                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1812                 return ( &create_xml_string($out_hash) );
1813         }
1815         # Create function result message
1816         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1817         
1818         return ( &create_xml_string($out_hash) );
1821 ################################
1823 # @brief Unassign a software license from a host.
1824 # @param hostid Something like client_1.intranet.mydomain.de
1825 # @param licensePoolId The name of the pool.
1827 sub opsi_unassignSoftwareLicenseFromHost {
1828         my ($msg, $msg_hash, $session_id) = @_;
1829     my $header = @{$msg_hash->{'header'}}[0];
1830     my $source = @{$msg_hash->{'source'}}[0];
1831     my $target = @{$msg_hash->{'target'}}[0];
1832         my $hostId;
1833         my $licensePoolId;
1835         # Check input sanity
1836         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1837                 $hostId = @{$msg_hash->{'hostId'}}[0];
1838         } else {
1839                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1840         }
1841         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1842                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1843         } else {
1844                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1845         }
1847         # Unassign a software license from a host
1848         my $callobj = {
1849         method  => 'deleteSoftwareLicenseUsage',
1850         params  => [ $hostId, '', $licensePoolId ],
1851         id  => 1,
1852     };
1853     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1855         # Check Opsi error
1856         my ($res_error, $res_error_str) = &check_opsi_res($res);
1857         if ($res_error){
1858                 # Create error message
1859                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1860                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1861                 return ( &create_xml_string($out_hash) );
1862         }
1864         # Create function result message
1865         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1866         
1867         return ( &create_xml_string($out_hash) );
1870 ################################
1872 # @brief Unassign all software licenses from a host
1873 # @param hostid Something like client_1.intranet.mydomain.de
1875 sub opsi_unassignAllSoftwareLicensesFromHost {
1876         my ($msg, $msg_hash, $session_id) = @_;
1877     my $header = @{$msg_hash->{'header'}}[0];
1878     my $source = @{$msg_hash->{'source'}}[0];
1879     my $target = @{$msg_hash->{'target'}}[0];
1880         my $hostId;
1882         # Check input sanity
1883         if (&_check_xml_tag_is_ok ($msg_hash, 'hostId')) {
1884                 $hostId = @{$msg_hash->{'hostId'}}[0];
1885         } else {
1886                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1887         }
1889         # Unassign all software licenses from a host
1890         my $callobj = {
1891         method  => 'deleteAllSoftwareLicenseUsages',
1892         params  => [ $hostId ],
1893         id  => 1,
1894     };
1895     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1897         # Check Opsi error
1898         my ($res_error, $res_error_str) = &check_opsi_res($res);
1899         if ($res_error){
1900                 # Create error message
1901                 &main::daemon_log("$session_id ERROR: cannot unassign a software license from a host at Opsi server: ".$res_error_str, 1);
1902                 my $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1903                 return ( &create_xml_string($out_hash) );
1904         }
1906         # Create function result message
1907         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1908         
1909         return ( &create_xml_string($out_hash) );
1913 ################################
1915 # @brief Returns the assigned licensePoolId and licenses, how often the product is installed and at which host
1916 # and the number of max and remaining installations for a given OPSI product.
1917 # @param productId Identificator of an OPSI product.
1918 #       
1919 sub opsi_getLicenseInformationForProduct {
1920     my ($msg, $msg_hash, $session_id) = @_;
1921     my $header = @{$msg_hash->{'header'}}[0];
1922     my $source = @{$msg_hash->{'source'}}[0];
1923         my $productId;
1924         my $out_hash;
1926         # Check input sanity
1927         if (&_check_xml_tag_is_ok ($msg_hash, 'productId')) {
1928                 $productId = @{$msg_hash->{'productId'}}[0];
1929         } else {
1930                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1931         }
1933         # Fetch infos from Opsi server
1934     my $callobj = {
1935         method  => 'getLicensePoolId',
1936         params  => [ $productId ],
1937         id  => 1,
1938     };
1939     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
1941         # Check Opsi error
1942         my ($res_error, $res_error_str) = &check_opsi_res($res);
1943         if ($res_error){
1944                 # Create error message
1945                 &main::daemon_log("$session_id ERROR: cannot get license pool for product '$productId' : ".$res_error_str, 1);
1946                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1947                 return ( &create_xml_string($out_hash) );
1948         } 
1949         
1950         my $licensePoolId = $res->result;
1952         # Fetch statistic information for given pool ID
1953         $callobj = {
1954                 method  => 'getLicenseStatistics_hash',
1955                 params  => [ ],
1956                 id  => 1,
1957         };
1958         $res = $main::opsi_client->call($main::opsi_url, $callobj);
1960         # Check Opsi error
1961         ($res_error, $res_error_str) = &check_opsi_res($res);
1962         if ($res_error){
1963                 # Create error message
1964                 &main::daemon_log("$session_id ERROR: cannot get statistic informations for license pools : ".$res_error_str, 1);
1965                 $out_hash = &main::create_xml_hash("error_$header", $main::server_address, $source, $res_error_str);
1966                 return ( &create_xml_string($out_hash) );
1967         }
1969         # Create function result message
1970         $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
1971         &add_content2xml_hash($out_hash, "licensePoolId", $licensePoolId);
1972         &add_content2xml_hash($out_hash, "licenses", $res->result->{$licensePoolId}->{'licenses'});
1973         &add_content2xml_hash($out_hash, "usageCount", $res->result->{$licensePoolId}->{'usageCount'});
1974         &add_content2xml_hash($out_hash, "maxInstallations", $res->result->{$licensePoolId}->{'maxInstallations'});
1975         &add_content2xml_hash($out_hash, "remainingInstallations", $res->result->{$licensePoolId}->{'remainingInstallations'});
1976         map(&add_content2xml_hash($out_hash, "usedBy", "$_"), @{ $res->result->{$licensePoolId}->{'usedBy'}});
1978         return ( &create_xml_string($out_hash) );
1982 ################################
1984 # @brief
1985 # @param 
1986 #       
1987 sub opsi_getPool {
1988     my ($msg, $msg_hash, $session_id) = @_;
1989     my $header = @{$msg_hash->{'header'}}[0];
1990     my $source = @{$msg_hash->{'source'}}[0];
1992         # Check input sanity
1993         my $licensePoolId;
1994         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
1995                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
1996         } else {
1997                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
1998         }
2000         # Create hash for the answer
2001         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2003         # Call Opsi
2004         my ($res, $err) = &_getLicensePool_hash( 'licensePoolId'=> $licensePoolId );
2005         if ($err){
2006                 return &_giveErrorFeedback($msg_hash, "cannot get license pool from Opsi server: ".$res, $session_id);
2007         }
2008         # Add data to outgoing hash
2009         &add_content2xml_hash($out_hash, "licensePoolId", $res->{'licensePoolId'});
2010         &add_content2xml_hash($out_hash, "description", $res->{'description'});
2011         map(&add_content2xml_hash($out_hash, "productIds", "$_"), @{ $res->{'productIds'} });
2012         map(&add_content2xml_hash($out_hash, "windowsSoftwareIds", "$_"), @{ $res->{'windowsSoftwareIds'} });
2015         # Call Opsi two times
2016         my ($usages_res, $usages_err) = &_getSoftwareLicenseUsages_listOfHashes('licensePoolId'=>$licensePoolId);
2017         if ($usages_err){
2018                 return &_giveErrorFeedback($msg_hash, "cannot get software license usage information from Opsi server: ".$usages_res, $session_id);
2019         }
2020         my ($licenses_res, $licenses_err) = &_getSoftwareLicenses_listOfHashes();
2021         if ($licenses_err){
2022                 return &_giveErrorFeedback($msg_hash, "cannot get software license information from Opsi server: ".$licenses_res, $session_id);
2023         }
2025         # Add data to outgoing hash
2026         # Parse through all software licenses and select those associated to the pool
2027         my $res_hash = { 'hit'=> [] };
2028         foreach my $license ( @$licenses_res) {
2029                 # Each license hash has a list of licensePoolIds so go through this list and search for matching licensePoolIds
2030                 my $found = 0;
2031                 my @licensePoolIds_list = @{$license->{licensePoolIds}};
2032                 foreach my $lPI ( @licensePoolIds_list) {
2033                         if ($lPI eq $licensePoolId) { $found++ }
2034                 }
2035                 if (not $found ) { next; };
2036                 # Found matching licensePoolId
2037                 my $license_hash = { 'softwareLicenseId' => [$license->{'softwareLicenseId'}],
2038                         'licenseKeys' => {},
2039                         'expirationDate' => [$license->{'expirationDate'}],
2040                         'boundToHost' => [$license->{'boundToHost'}],
2041                         'maxInstallations' => [$license->{'maxInstallations'}],
2042                         'licenseType' => [$license->{'licenseType'}],
2043                         'licenseContractId' => [$license->{'licenseContractId'}],
2044                         'licensePoolIds' => [],
2045                         'hostIds' => [],
2046                         };
2047                 foreach my $licensePoolId (@{ $license->{'licensePoolIds'}}) {
2048                         push( @{$license_hash->{'licensePooIds'}}, $licensePoolId);
2049                         $license_hash->{licenseKeys}->{$licensePoolId} =  [ $license->{'licenseKeys'}->{$licensePoolId} ];
2050                 }
2051                 foreach my $usage (@$usages_res) {
2052                         # Search for hostIds with matching softwareLicenseId
2053                         if ($license->{'softwareLicenseId'} eq $usage->{'softwareLicenseId'}) {
2054                                 push( @{ $license_hash->{hostIds}}, $usage->{hostId});
2055                         }
2056                 }
2058                 push( @{$res_hash->{hit}}, $license_hash );
2059         }
2060         $out_hash->{licenses} = [$res_hash];
2062     return ( &create_xml_string($out_hash) );
2066 ################################
2068 # @brief Removes at first the software license from license pool and than deletes the software license. 
2069 # Attention, the software license has to exists otherwise it will lead to an Opsi internal server error.
2070 # @param softwareLicenseId 
2071 # @param licensePoolId
2073 sub opsi_removeLicense {
2074     my ($msg, $msg_hash, $session_id) = @_;
2075     my $header = @{$msg_hash->{'header'}}[0];
2076     my $source = @{$msg_hash->{'source'}}[0];
2078         # Check input sanity
2079         my $softwareLicenseId;
2080         if (&_check_xml_tag_is_ok ($msg_hash, 'softwareLicenseId')) {
2081                 $softwareLicenseId = @{$msg_hash->{'softwareLicenseId'}}[0];
2082         } else {
2083                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2084         }
2085         my $licensePoolId;
2086         if (&_check_xml_tag_is_ok ($msg_hash, 'licensePoolId')) {
2087                 $licensePoolId = @{$msg_hash->{'licensePoolId'}}[0];
2088         } else {
2089                 return ( &_give_feedback($msg, $msg_hash, $session_id, $_) );
2090         }
2091         
2092         # Call Opsi
2093         my ($res, $err) = &_removeSoftwareLicenseFromLicensePool( 'licensePoolId' => $licensePoolId, 'softwareLicenseId' => $softwareLicenseId );
2094         if ($err){
2095                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from pool: ".$res, $session_id);
2096         }
2098         # Call Opsi
2099         ($res, $err) = &_deleteSoftwareLicense( 'softwareLicenseId'=>$softwareLicenseId );
2100         if ($err){
2101                 return &_giveErrorFeedback($msg_hash, "cannot delete software license from Opsi server: ".$res, $session_id);
2102         }
2104         # Create hash for the answer
2105         my $out_hash = &main::create_xml_hash("answer_$header", $main::server_address, $source);
2106         return ( &create_xml_string($out_hash) );
2109 sub opsi_test {
2110     my ($msg, $msg_hash, $session_id) = @_;
2111     my $header = @{$msg_hash->{'header'}}[0];
2112     my $source = @{$msg_hash->{'source'}}[0];
2113         my $pram1 = @{$msg_hash->{'productId'}}[0];
2115 print STDERR Dumper $pram1;
2117         # Fetch infos from Opsi server
2118     my $callobj = {
2119         method  => 'getLicensePoolId',
2120         params  => [ $pram1 ],
2121         id  => 1,
2122     };
2123     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2125         print STDERR Dumper $res;
2126         return ();
2129 sub _giveErrorFeedback {
2130         my ($msg_hash, $err_string, $session_id) = @_;
2131         &main::daemon_log("$session_id ERROR: $err_string", 1);
2132         my $out_hash = &main::create_xml_hash("error", $main::server_address, @{$msg_hash->{source}}[0], $err_string);
2133         return ( &create_xml_string($out_hash) );
2137 sub _getLicensePool_hash {
2138         my %arg = (
2139                 'licensePoolId' => undef,
2140                 @_,
2141         );
2143         if (not defined $arg{licensePoolId} ) { 
2144                 return ("function requires licensePoolId as parameter", 1);
2145         }
2147         # Fetch pool infos from Opsi server
2148     my $callobj = {
2149         method  => 'getLicensePool_hash',
2150         params  => [ $arg{licensePoolId} ],
2151         id  => 1,
2152     };
2153     my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2155         # Check Opsi error
2156         my ($res_error, $res_error_str) = &check_opsi_res($res);
2157         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2159         return ($res->result, 0);
2162 sub _getSoftwareLicenses_listOfHashes {
2163         # Fetch licenses associated to the given pool
2164         my $callobj = {
2165                 method  => 'getSoftwareLicenses_listOfHashes',
2166                 params  => [ ],
2167                 id  => 1,
2168         };
2169         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2171         # Check Opsi error
2172         my ($res_error, $res_error_str) = &check_opsi_res($res);
2173         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2175         return ($res->result, 0);
2178 sub _getSoftwareLicenseUsages_listOfHashes {
2179         my %arg = (
2180                         'hostId' => "",
2181                         'licensePoolId' => "",
2182                         @_,
2183                         );
2185         # Fetch pool infos from Opsi server
2186         my $callobj = {
2187                 method  => 'getSoftwareLicenseUsages_listOfHashes',
2188                 params  => [ $arg{hostId}, $arg{licensePoolId} ],
2189                 id  => 1,
2190         };
2191         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2193         # Check Opsi error
2194         my ($res_error, $res_error_str) = &check_opsi_res($res);
2195         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2197         return ($res->result, 0);
2200 sub _removeSoftwareLicenseFromLicensePool {
2201         my %arg = (
2202                 'softwareLicenseId' => undef,
2203                 'licensePoolId' => undef,
2204                 @_,
2205                 );
2207         if (not defined $arg{softwareLicenseId} ) { 
2208                 return ("function requires softwareLicenseId as parameter", 1);
2209                 }
2210                 if (not defined $arg{licensePoolId} ) { 
2211                 return ("function requires licensePoolId as parameter", 1);
2212         }
2214         # Remove software license from license pool
2215         my $callobj = {
2216                 method  => 'removeSoftwareLicenseFromLicensePool',
2217                 params  => [ $arg{softwareLicenseId}, $arg{licensePoolId} ],
2218                 id  => 1,
2219         };
2220         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2222         # Check Opsi error
2223         my ($res_error, $res_error_str) = &check_opsi_res($res);
2224         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2226         return ($res->result, 0);
2229 sub _deleteSoftwareLicense {
2230         my %arg = (
2231                 'softwareLicenseId' => undef,
2232                 'removeFromPools' => "",
2233                 @_,
2234                 );
2236         if (not defined $arg{softwareLicenseId} ) { 
2237                 return ("function requires softwareLicenseId as parameter", 1);
2238         }
2240         # Fetch
2241         my $callobj = {
2242                 method  => 'deleteSoftwareLicense',
2243                 params  => [ $arg{softwareLicenseId}, $arg{removeFromPools} ],
2244                 id  => 1,
2245         };
2246         my $res = $main::opsi_client->call($main::opsi_url, $callobj);
2248         # Check Opsi error
2249         my ($res_error, $res_error_str) = &check_opsi_res($res);
2250         if ($res_error){ return ( (caller(0))[3]." : ".$res_error_str, 1 ); }
2252         return ($res->result, 0);
2255 1;