Code

updated templates
[gosa.git] / gosa-si / server / events / gosaTriggered.pm
1 ## @file
2 # @details A GOsa-SI event module containing all functions common used by GOsa
3 # @brief Implementation of a GOsa-SI event module. 
5 package gosaTriggered;
6 use Exporter;
7 @ISA = qw(Exporter);
8 my @events = (
9     "get_events", 
10     "get_login_usr_for_client",
11     "get_client_for_login_usr",
12     "gen_smb_hash",
13     "trigger_reload_syslog_config",
14     "trigger_reload_ntp_config",
15     "trigger_reload_ldap_config",
16     "network_completition",
17     "set_activated_for_installation",
18     "new_key_for_client",
19     "detect_hardware",
20     "trigger_action_localboot",
21     "trigger_action_faireboot",
22     "trigger_action_reboot",
23     "trigger_action_activate",
24     "trigger_action_lock",
25     "trigger_action_halt",
26     "trigger_action_update", 
27     "trigger_action_reinstall",
28     "trigger_action_sysinfo",
29     "trigger_action_instant_update",
30     "trigger_action_rescan",
31     "trigger_action_wake",
32     "recreate_fai_server_db",
33     "recreate_fai_release_db",
34     "recreate_packages_list_db",
35     "send_user_msg", 
36     "get_available_kernel",
37         "trigger_activate_new",
38     "get_hosts_with_module",    
39     );
40 @EXPORT = @events;
42 use strict;
43 use warnings;
44 use GOSA::GosaSupportDaemon;
45 use Crypt::SmbHash;
46 use Net::ARP;
47 use Net::Ping;
48 use Socket;
49 use Time::HiRes qw( usleep);
50 use MIME::Base64;
51 use Data::Dumper;
53 BEGIN {}
55 END {}
57 ### Start ######################################################################
59 ## @method get_events()
60 # A brief function returning a list of functions which are exported by importing the module.
61 # @return List of all provided functions
62 sub get_events {
63     return \@events;
64 }
66 ## @method send_usr_msg($msg, $msg_hash, $session_id)
67 # This function accepts usr messages from GOsa, split mulitple target messages to mulitiple single target messages and put all messages into messaging_db
68 # @param msg - STRING - xml message
69 # @param msg_hash - HASHREF - message information parsed into a hash
70 # @param session_id - INTEGER - POE session id of the processing of this message
71 # @return (out_msg)  - ARRAY - Array containing the answer message from client
72 sub send_user_msg {
73     my ($msg, $msg_hash, $session_id) = @_ ;
74     my $header = @{$msg_hash->{'header'}}[0];
75     my $source = @{$msg_hash->{'source'}}[0];
76     my $target = @{$msg_hash->{'target'}}[0];
78     my $subject = @{$msg_hash->{'subject'}}[0];
79     my $from = @{$msg_hash->{'from'}}[0];
80     my @users = exists $msg_hash->{'user'} ? @{$msg_hash->{'user'}} : () ;
81         my @groups = exists $msg_hash->{'group'} ? @{$msg_hash->{'group'}} : ();
82     my $delivery_time = @{$msg_hash->{'delivery_time'}}[0];
83     my $message = @{$msg_hash->{'message'}}[0];
84     
85 #    # keep job queue uptodate if necessary 
86 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
87 #    if( defined $jobdb_id) {
88 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
89 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
90 #        my $res = $main::job_db->exec_statement($sql_statement);
91 #    }
93     # error handling
94     if (not $delivery_time =~ /^\d{14}$/) {
95         my $error_string = "delivery_time '$delivery_time' is not a valid timestamp, please use format 'yyyymmddhhmmss'";
96         &main::daemon_log("$session_id ERROR: $error_string", 1);
97         return &create_xml_string(&create_xml_hash($header, $target, $source, $error_string));
98     }
100     # determine new message id
101     my $new_msg_id = 1;
102         my $new_msg_id_sql = "SELECT MAX(id) FROM $main::messaging_tn";
103     my $new_msg_id_res = $main::messaging_db->exec_statement($new_msg_id_sql);
104     if (defined @{@{$new_msg_id_res}[0]}[0] ) {
105         $new_msg_id = int(@{@{$new_msg_id_res}[0]}[0]);
106         $new_msg_id += 1;
107     }
109         # highlight user name and group name
110         my @receiver_l;
111         @users = map(push(@receiver_l, "u_$_"), @users);
112         @groups = map(push(@receiver_l, "g_$_"), @groups);
114     # Sanitiy check of receivers list
115     if (@receiver_l == 0) {
116         &main::daemon_log("$session_id ERROR: 'send_usr_msg'-message contains neither a 'usr' nor a 'group' tag. No receiver specified.", 1); 
117         return;
118     }
120     # add incoming message to messaging_db
121     my $func_dic = {table=>$main::messaging_tn,
122         primkey=>[],
123         id=>$new_msg_id,
124         subject=>$subject,
125         message_from=>$from,
126         message_to=>join(",", @receiver_l),
127         flag=>"n",
128         direction=>"in",
129         delivery_time=>$delivery_time,
130         message=>$message,
131         timestamp=>&get_time(),
132     };
133     my $res = $main::messaging_db->add_dbentry($func_dic);
134     if (not $res == 0) {
135         &main::daemon_log("$session_id ERROR: gosaTriggered.pm: cannot add message to message_db: $res", 1);
136     } else {
137         &main::daemon_log("$session_id INFO: gosaTriggered.pm: message with subject '".&decode_base64($subject)."' successfully added to message_db", 5);
138     }
140     return;
144 sub recreate_fai_server_db {
145     my ($msg, $msg_hash, $session_id) = @_ ;
146     my $out_msg;
148 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
149 #    if( defined $jobdb_id) {
150 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
151 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
152 #        my $res = $main::job_db->exec_statement($sql_statement);
153 #    }
155     $main::fai_server_db->create_table("new_fai_server", \@main::fai_server_col_names);
156     &main::create_fai_server_db("new_fai_server",undef,"dont", $session_id);
157     $main::fai_server_db->move_table("new_fai_server", $main::fai_server_tn);
158     
159     my @out_msg_l = ( $out_msg );
160     return @out_msg_l;
164 sub recreate_fai_release_db {
165     my ($msg, $msg_hash, $session_id) = @_ ;
166     my $out_msg;
168 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
169 #    if( defined $jobdb_id) {
170 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
171 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7);
172 #        my $res = $main::job_db->exec_statement($sql_statement);
173 #    }
175     $main::fai_release_db->create_table("new_fai_release", \@main::fai_release_col_names);
176     &main::create_fai_release_db("new_fai_release", $session_id);
177     $main::fai_release_db->move_table("new_fai_release", $main::fai_release_tn);
179     my @out_msg_l = ( $out_msg );
180     return @out_msg_l;
184 sub recreate_packages_list_db {
185         my ($msg, $msg_hash, $session_id) = @_ ;
186         my $out_msg;
188 #       my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
189 #       if( defined $jobdb_id) {
190 #               my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
191 #               &main::daemon_log("$session_id DEBUG: $sql_statement", 7);
192 #               my $res = $main::job_db->exec_statement($sql_statement);
193 #       }
195         &main::create_packages_list_db;
197         my @out_msg_l = ( $out_msg );
198         return @out_msg_l;
202 sub get_login_usr_for_client {
203     my ($msg, $msg_hash, $session_id) = @_ ;
204     my $header = @{$msg_hash->{'header'}}[0];
205     $header =~ s/^gosa_//;
206     my $source = @{$msg_hash->{'source'}}[0];
207     my $target = @{$msg_hash->{'target'}}[0];
208     my $client = @{$msg_hash->{'client'}}[0];
210 #    # Set job status
211 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
212 #    if( defined $jobdb_id) {
213 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
214 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7);
215 #        my $res = $main::job_db->exec_statement($sql_statement);
216 #    }
218     # If $client is a mac address
219     if ($client =~ /^\w\w:\w\w:\w\w:\w\w:\w\w:\w\w$/i)
220     {
221         # Search for hostname of $client within known_clients_db
222         my $sql = "SELECT * FROM $main::known_clients_tn WHERE macaddress LIKE '$client'";
223         my $res = $main::known_clients_db->select_dbentry($sql);
224         my $found = 0;
225         if (keys(%$res) == 1)
226         {
227             $found = 1;
228             $client = $res->{1}->{hostname};
229         }
230         if (not $found)  # Do only if the first search results in nothing
231         {
232             # Search for hostname of $client within known_foreign_db
233             $sql = "SELECT * FROM $main::foreign_clients_tn WHERE macaddress LIKE '$client'";
234             $res = $main::foreign_clients_db->select_dbentry($sql);
235             if (keys(%$res) == 1)
236             {
237                 $client = $res->{1}->{hostname};
238             }
239         }
240     }
242     # Search for logged in users at hostname
243     my $sql_statement = "SELECT * FROM $main::login_users_tn WHERE client LIKE '$client'";
244     my $res = $main::login_users_db->select_dbentry($sql_statement);
246     # Create answer message for GOsa
247     my $out_msg;
248     if (keys(%$res) == 0)
249     {
250         my $info = "INFO: No hits found in login_users_db for client '$client'";
251         $out_msg = &create_xml_string(&create_xml_hash($header, $target, $source, $info));
252         &main::daemon_log("$session_id ".$info, 5);
253     }
254     else
255     {
256         $out_msg = "<xml><header>$header</header><source>$target</source><target>$source</target>";
257         $out_msg .= &db_res2xml($res);
258         $out_msg .= "</xml>";
259     }
261     return ($out_msg);
265 sub get_client_for_login_usr {
266     my ($msg, $msg_hash, $session_id) = @_ ;
267     my $header = @{$msg_hash->{'header'}}[0];
268     $header =~ s/^gosa_//;
269     my $source = @{$msg_hash->{'source'}}[0];
270     my $target = @{$msg_hash->{'target'}}[0];
271     my $usr = @{$msg_hash->{'usr'}}[0];
273 #    # Set job status
274 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
275 #    if( defined $jobdb_id) {
276 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
277 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7);
278 #        my $res = $main::job_db->exec_statement($sql_statement);
279 #    }
281     # Search for clients where $usr is logged in
282     my $sql_statement = "SELECT * FROM $main::login_users_tn WHERE user LIKE '%$usr%'";
283     my $res = $main::login_users_db->select_dbentry($sql_statement);
285     # Create answer message for GOsa
286     my $out_msg = "<xml><header>$header</header><source>$target</source><target>$source</target>";
287     $out_msg .= &db_res2xml($res);
288     $out_msg .= "</xml>";
290     return ( $out_msg );
294 sub gen_smb_hash {
295      my ($msg, $msg_hash, $session_id) = @_ ;
296      my $source = @{$msg_hash->{source}}[0];
297      my $target = @{$msg_hash->{target}}[0];
298      my $password = @{$msg_hash->{password}}[0];
300      my %data= ('hash' => join(q[:], ntlmgen $password));
301      my $out_msg = &build_msg("gen_smb_hash", $target, $source, \%data );
302      my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
303      if (defined $forward_to_gosa) {
304          $out_msg =~s/<\/xml>/<forward_to_gosa>$forward_to_gosa<\/forward_to_gosa><\/xml>/;
305      }
307      return ( $out_msg );
311 sub network_completition {
312      my ($msg, $msg_hash, $session_id) = @_ ;
313      my $source = @{$msg_hash->{source}}[0];
314      my $target = @{$msg_hash->{target}}[0];
315      my $name = @{$msg_hash->{hostname}}[0];
317      # Can we resolv the name?
318      my %data;
319      if (inet_aton($name)){
320          my $tmp = (inet_aton($name));
321              my $address = inet_ntoa($tmp);
322              my $p = Net::Ping->new('tcp');
323              my $mac= "";
324              if ($p->ping($address, 1)){
325                $mac = Net::ARP::arp_lookup("", $address);
326              }
328              %data= ('ip' => $address, 'mac' => $mac);
329      } else {
330              %data= ('ip' => '', 'mac' => '');
331      }
333      my $out_msg = &build_msg("network_completition", $target, $source, \%data );
334      my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
335      if (defined $forward_to_gosa) {
336          $out_msg =~s/<\/xml>/<forward_to_gosa>$forward_to_gosa<\/forward_to_gosa><\/xml>/;
337      }
339      return ( $out_msg );
343 sub detect_hardware {
344     my ($msg, $msg_hash, $session_id) = @_ ;
345     # just forward msg to client, but dont forget to split off 'gosa_' in header
346     my $source = @{$msg_hash->{source}}[0];
347     my $mac = @{$msg_hash->{macaddress}}[0];
348     my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
349     if( defined $jobdb_id) {
350         my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
351         &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
352         my $res = $main::job_db->exec_statement($sql_statement);
353     }
355     my $out_hash = &create_xml_hash("detect_hardware", $source, $mac);
356     if( defined $jobdb_id ) { 
357         &add_content2xml_hash($out_hash, 'jobdb_id', $jobdb_id); 
358     }
359     my $out_msg = &create_xml_string($out_hash);
361     my @out_msg_l = ( $out_msg );
362     return @out_msg_l;
366 sub trigger_reload_syslog_config {
367     my ($msg, $msg_hash, $session_id) = @_ ;
369     # Sanity check of macaddress
370     # TODO
372     my $macaddress = @{$msg_hash->{macaddress}}[0];
374 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
375 #    if( defined $jobdb_id) {
376 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
377 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
378 #        my $res = $main::job_db->exec_statement($sql_statement);
379 #    }
381         my $out_msg = &ClientPackages::new_syslog_config($macaddress, $session_id);
382         my @out_msg_l = ( $out_msg );
384     return @out_msg_l;
386    
389 sub trigger_reload_ntp_config {
390     my ($msg, $msg_hash, $session_id) = @_ ;
392     # Sanity check of macaddress
393     # TODO
395     my $macaddress = @{$msg_hash->{macaddress}}[0];
397 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
398 #    if( defined $jobdb_id) {
399 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
400 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
401 #        my $res = $main::job_db->exec_statement($sql_statement);
402 #    }
404         my $out_msg = &ClientPackages::new_ntp_config($macaddress, $session_id);
405         my @out_msg_l = ( $out_msg );
407     return @out_msg_l;
411 sub trigger_reload_ldap_config {
412     my ($msg, $msg_hash, $session_id) = @_ ;
413     my $mac = @{$msg_hash->{macaddress}}[0];
415 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
416 #    if( defined $jobdb_id) {
417 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
418 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
419 #        my $res = $main::job_db->exec_statement($sql_statement);
420 #    }
422         my $out_msg = &ClientPackages::new_ldap_config($mac, $session_id);
423         my @out_msg_l = ( $out_msg );
425     return @out_msg_l;
429 sub set_activated_for_installation {
430     my ($msg, $msg_hash, $session_id) = @_;
431     my $header = @{$msg_hash->{header}}[0];
432     my $source = @{$msg_hash->{source}}[0];
433         my $mac= (defined($msg_hash->{'macaddress'}))?@{$msg_hash->{'macaddress'}}[0]:undef;
434     my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
435         my @out_msg_l;
437     # TODO Sanity check macAddress defined
439 #       # update status of job 
440 #    if( defined $jobdb_id) {
441 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
442 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
443 #        my $res = $main::job_db->exec_statement($sql_statement);
444 #    }
446     # If a client gets a 'set_activated_for_installation' msg, always deliver a fresh 'new_ldap_config'
447     # just for backup and robustness purposes
448     my $ldap_out_msg = &ClientPackages::new_ldap_config($mac, $session_id);
449     push(@out_msg_l, $ldap_out_msg);
451         # create set_activated_for_installation message for delivery
452     my $out_hash = &create_xml_hash("set_activated_for_installation", $source, $mac);
453     if( defined $jobdb_id ) { 
454         &add_content2xml_hash($out_hash, 'jobdb_id', $jobdb_id); 
455     }
456     my $out_msg = &create_xml_string($out_hash);
457         push(@out_msg_l, $out_msg); 
459     return @out_msg_l;
463 sub trigger_action_faireboot {
464     my ($msg, $msg_hash, $session_id) = @_;
465     my $mac = @{$msg_hash->{macaddress}}[0];
466     my $source = @{$msg_hash->{source}}[0];
468     # Create message for client
469     my $out_msg = &main::create_xml_string(&main::create_xml_hash("trigger_action_faireboot", $source, $mac));
471     # Set LDAP states
472     &main::change_goto_state('locked', \@{$msg_hash->{macaddress}}, $session_id);
473         &main::change_fai_state('install', \@{$msg_hash->{macaddress}}, $session_id); 
475 #    # Set job to status 'done', job will be deleted automatically
476 #    my $sql_statement = "UPDATE $main::job_queue_tn ".
477 #        "SET status='done', modified='1'".
478 #        "WHERE (macaddress LIKE '$mac' AND status='processing')";
479 #    &main::daemon_log("$session_id DEBUG: $sql_statement", 7);
480 #    my $res = $main::job_db->update_dbentry( $sql_statement );
482     return ( $out_msg ); 
486 sub trigger_action_lock {
487     my ($msg, $msg_hash, $session_id) = @_;
489     # Set LDAP state
490     &main::change_goto_state('locked', \@{$msg_hash->{macaddress}}, $session_id);
492 #    # Set job status
493 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
494 #    if( defined $jobdb_id) {
495 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
496 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
497 #        my $res = $main::job_db->exec_statement($sql_statement);
498 #    }
499                                              
500     return;
504 sub trigger_action_activate {
505     my ($msg, $msg_hash, $session_id) = @_;
506     my $mac = @{$msg_hash->{macaddress}}[0];
507     my $source = @{$msg_hash->{source}}[0];
509     # Set LDAP state
510     &main::change_goto_state('active', \@{$msg_hash->{macaddress}}, $session_id);
512 #    # Set job status
513 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
514 #    if( defined $jobdb_id) {
515 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
516 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
517 #        my $res = $main::job_db->exec_statement($sql_statement);
518 #    }
519      
520     # Create message for client
521     my $out_hash = &create_xml_hash("set_activated_for_installation", $source, $mac);
522     if( exists $msg_hash->{'jobdb_id'} ) { 
523         &add_content2xml_hash($out_hash, 'jobdb_id', @{$msg_hash->{'jobdb_id'}}[0]); 
524     }
525     my $out_msg = &create_xml_string($out_hash);
527     return ( $out_msg );
532 sub trigger_action_localboot {
533     my ($msg, $msg_hash, $session_id) = @_;
534     my $source = $msg_hash->{source}[0];
535     my $mac = $msg_hash->{macaddress}[0];
536     my $target = $msg_hash->{target}[0];
537     my @out_msg_l;
539     # Create message for client
540     my $out_msg = &main::create_xml_string(&main::create_xml_hash("trigger_action_localboot", $source, $mac));
541     push(@out_msg_l, $out_msg);  
543     # Check for running jobs. In that case return a message to GOsa that running jobs have to be deleted/aborted
544     # befor trigger_action_localboot could be effective. Running jobs usually sets FAIstate and GOtomode to
545     # what they need again and again and overwrite the 'trigger_action_localboot' setting
546     my $job_sql= "SELECT * FROM $main::job_queue_tn WHERE macaddress LIKE '$mac'";
547     my $job_res = $main::job_db->select_dbentry($job_sql);
548     my $job_res_count = keys(%$job_res);
549     if ($job_res_count) {
550         push(@out_msg_l, "<xml><header>answer</header><source>$target</source><target>GOSA</target><answer1>existing_job_in_queue</answer1></xml>");
551     }
553     # Set LDAP state
554     &main::change_fai_state('localboot', \@{$msg_hash->{macaddress}}, $session_id);
556 #    # Set job status
557 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
558 #    if( defined $jobdb_id) {
559 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
560 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
561 #        my $res = $main::job_db->exec_statement($sql_statement);
562 #    }
564     return @out_msg_l;
568 sub trigger_action_halt {
569     my ($msg, $msg_hash, $session_id) = @_;
570     my $source = $msg_hash->{source}[0];
571     my $mac = $msg_hash->{macaddress}[0];
573     # Create message for client
574     my $out_msg = &main::create_xml_string(&main::create_xml_hash("trigger_action_halt", $source, $mac));
575  
576 #    # Set job status
577 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
578 #    if( defined $jobdb_id) {
579 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
580 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
581 #        my $res = $main::job_db->exec_statement($sql_statement);
582 #    }
584     return ($out_msg);
588 sub trigger_action_reboot {
589     my ($msg, $msg_hash, $session_id) = @_;
590     my $source = $msg_hash->{source}[0];
591     my $mac = $msg_hash->{macaddress}[0];
593     # Create message for client
594     my $out_msg = &main::create_xml_string(&main::create_xml_hash("trigger_action_reboot", $source, $mac));
596     # Set LDAP state
597     &main::change_fai_state('reboot', \@{$msg_hash->{macaddress}}, $session_id);
599     return ($out_msg);
603 sub trigger_action_reinstall {
604     my ($msg, $msg_hash, $session_id) = @_;
605     my $source = $msg_hash->{source}[0];
606     my $mac = $msg_hash->{macaddress}[0];
608     # Create message for client
609     my $out_msg = &main::create_xml_string(&main::create_xml_hash("trigger_action_reinstall", $source, $mac));
611     # Set LDAP state
612     &main::change_fai_state('reinstall', \@{$msg_hash->{macaddress}}, $session_id);
614     # Create wakeup message for all foreign server
615     my %data = ( 'macaddress'  => \@{$msg_hash->{macaddress}} );
616     my $wake_msg = &build_msg("trigger_wake", "GOSA", "KNOWN_SERVER", \%data);
618     # Invoke trigger wake for this gosa-si-server
619     &main::server_server_com::trigger_wake($msg, $msg_hash, $session_id);
621     return ($wake_msg, $msg);  
625 sub trigger_action_update {
626     my ($msg, $msg_hash, $session_id) = @_;
627     my $source = $msg_hash->{source}[0];
628     my $mac = $msg_hash->{macaddress}[0];
630     # Create message for client
631     my $out_msg = &main::create_xml_string(&main::create_xml_hash("trigger_action_update", $source, $mac));
633     # Set LDAP state
634     &main::change_fai_state('update', \@{$msg_hash->{macaddress}}, $session_id);
636     # Create wakeup message for all foreign server
637     my %data = ( 'macaddress'  => \@{$msg_hash->{macaddress}} );
638     my $wake_msg = &build_msg("trigger_wake", "GOSA", "KNOWN_SERVER", \%data);
640     # Invoke trigger wake for this gosa-si-server
641     &main::server_server_com::trigger_wake($msg, $msg_hash, $session_id);
643     return ($wake_msg, $msg);  
647 sub trigger_action_instant_update {
648     my ($msg, $msg_hash, $session_id) = @_;
649     my $source = $msg_hash->{source}[0];
650     my $mac = $msg_hash->{macaddress}[0];
652     # Create message for client
653     my $out_msg = &main::create_xml_string(&main::create_xml_hash("trigger_action_instant_update", $source, $mac));
655     # Set LDAP state
656     &main::change_fai_state('update', \@{$msg_hash->{macaddress}}, $session_id);
658 #    # Set job status
659 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
660 #    if( defined $jobdb_id) {
661 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
662 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
663 #        my $res = $main::job_db->exec_statement($sql_statement);
664 #    }
666     # Create wakeup message for all foreign server
667     my %data = ( 'macaddress'  => \@{$msg_hash->{macaddress}} );
668     my $wake_msg = &build_msg("trigger_wake", "GOSA", "KNOWN_SERVER", \%data);
670     # Invoke trigger wake for this gosa-si-server
671     &main::server_server_com::trigger_wake($msg, $msg_hash, $session_id);
673     return ($wake_msg, $msg);  
677 sub new_key_for_client {
678     my ($msg, $msg_hash, $session_id) = @_;
679     my $source = $msg_hash->{source}[0];
680     my $mac = $msg_hash->{macaddress}[0];
682     # Create message for client
683     my $out_msg = &main::create_xml_string(&main::create_xml_hash("new_key", $source, $mac));
685 #    # Set job status
686 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
687 #    if( defined $jobdb_id) {
688 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
689 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
690 #        my $res = $main::job_db->exec_statement($sql_statement);
691 #    }
693     return ($out_msg);
697 sub trigger_action_rescan {
698     my ($msg, $msg_hash, $session_id) = @_;
699     my $source = $msg_hash->{source}[0];
700     my $mac = $msg_hash->{macaddress}[0];
702     # Create message for client
703     my $out_msg = &main::create_xml_string(&main::create_xml_hash("detect_hardware", $source, $mac));
705 #    # Set job status
706 #    my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
707 #    if( defined $jobdb_id) {
708 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
709 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
710 #        my $res = $main::job_db->exec_statement($sql_statement);
711 #    }
713     return ( $out_msg );
717 sub trigger_action_wake {
718     my ($msg, $msg_hash, $session_id) = @_;
719     my $jobdb_id = @{$msg_hash->{'jobdb_id'}}[0];
720  
721 #    # Set job status
722 #    if( defined $jobdb_id) {
723 #        my $sql_statement = "UPDATE $main::job_queue_tn SET status='processed' WHERE ((id=$jobdb_id) AND (NOT status='done'))";
724 #        &main::daemon_log("$session_id DEBUG: $sql_statement", 7); 
725 #        my $res = $main::job_db->exec_statement($sql_statement);
726 #    }
728     # Create wakeup message for all foreign server
729     my $out_hash = &create_xml_hash("trigger_wake", "GOSA", "KNOWN_SERVER");
730     foreach (@{$msg_hash->{'macaddress'}}) {
731         &add_content2xml_hash($out_hash, 'macaddress', $_);
732     }
733     if (defined $jobdb_id){
734         &add_content2xml_hash($out_hash, 'jobdb_id', $jobdb_id);
735     }
736     my $out_msg = &create_xml_string($out_hash);
737     
738     # Invoke trigger wake for this gosa-si-server
739     &main::server_server_com::trigger_wake($out_msg, $out_hash, $session_id);
741     return ( $out_msg );
745 sub get_available_kernel {
746   my ($msg, $msg_hash, $session_id) = @_;
747   my $source = @{$msg_hash->{'source'}}[0];
748   my $target = @{$msg_hash->{'target'}}[0];
749   my $fai_release= @{$msg_hash->{'fai_release'}}[0];
750   my @kernel;
752   # Get Kernel packages for release
753   my $sql_statement = "SELECT * FROM $main::packages_list_tn WHERE distribution='$fai_release' AND package LIKE 'linux\-image\-%'";
754   my $res_hash = $main::packages_list_db->select_dbentry($sql_statement);
755   my %data;
756   my $i=1;
757   foreach my $package (keys %{$res_hash}) {
758     $data{"answer".$i++}= $data{"answer".$i++}= ${$res_hash}{$package}->{'package'};
759   }
760   $data{"answer".$i++}= "default";
762   # Create answer for GOsa  
763   my $out_msg = &build_msg("get_available_kernel", $target, $source, \%data);
764   my $forward_to_gosa = @{$msg_hash->{'forward_to_gosa'}}[0];
765   if (defined $forward_to_gosa) {
766     $out_msg =~s/<\/xml>/<forward_to_gosa>$forward_to_gosa<\/forward_to_gosa><\/xml>/;
767   }
769   return ( $out_msg );
772 sub trigger_activate_new {
773         my ($msg, $msg_hash, $session_id) = @_;
774         my $source = @{$msg_hash->{'source'}}[0];
775         my $target = @{$msg_hash->{'target'}}[0];
776         my $header= @{$msg_hash->{'header'}}[0];
777         my $mac= (defined($msg_hash->{'mac'}))?@{$msg_hash->{'mac'}}[0]:undef;
778         my $ogroup= (defined($msg_hash->{'ogroup'}))?@{$msg_hash->{'ogroup'}}[0]:undef;
779         my $timestamp= (defined($msg_hash->{'timestamp'}))?@{$msg_hash->{'timestamp'}}[0]:undef;
780         my $base= (defined($msg_hash->{'base'}))?@{$msg_hash->{'base'}}[0]:undef;
781         my $hostname= (defined($msg_hash->{'fqdn'}))?@{$msg_hash->{'fqdn'}}[0]:undef;
782         my $ip_address= (defined($msg_hash->{'ip'}))?@{$msg_hash->{'ip'}}[0]:undef;
783         my $dhcp_statement= (defined($msg_hash->{'dhcp'}))?@{$msg_hash->{'dhcp'}}[0]:undef;
784         my $jobdb_id= (defined($msg_hash->{'jobdb_id'}))?@{$msg_hash->{'jobdb_id'}}[0]:undef;
786     # Sanity check for base
787     if (ref($base) eq "HASH") {
788         # Incoming msg has a xml tag 'base' but no content
789         $base = undef;
790     }
792     # In case that the client is sleeping, wake it up
793     my %data = ( 'macaddress'  => $mac );
794     my $wake_msg = &build_msg("trigger_wake", "GOSA", "KNOWN_SERVER", \%data);
795     &main::server_server_com::trigger_wake($msg, $msg_hash, $session_id);
796     my $sql_statement= "SELECT * FROM $main::known_server_tn";
797     my $query_res = $main::known_server_db->select_dbentry( $sql_statement ); 
798     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
799         my $host_name = $hit->{hostname};
800         my $host_key = $hit->{hostkey};
801         $wake_msg =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
802         my $error = &main::send_msg_to_target($wake_msg, $host_name, $host_key, $header, $session_id);
803     }
805         my $ldap_entry;
806         my $ogroup_entry;
807         my $changed_attributes_counter = 0;
809     my $activate_client = 0;
810     my $ldap_handle=&main::get_ldap_handle();
811         
812     if(defined($ogroup)) {
813       my $ldap_mesg= $ldap_handle->search(
814         base => $main::ldap_base,
815         scope => 'sub',
816         filter => "(&(objectClass=gosaGroupOfnames)(cn=$ogroup))",
817       );
818       if($ldap_mesg->count == 1) {
819         $ogroup_entry= $ldap_mesg->pop_entry();
820         &main::daemon_log("$session_id DEBUG: A GosaGroupOfNames with cn '$ogroup' was found in base '".$main::ldap_base."'!", 5);
821       } elsif ($ldap_mesg->count == 0) {
822         &main::daemon_log("$session_id ERROR: A GosaGroupOfNames with cn '$ogroup' was not found in base '".$main::ldap_base."'!", 1);
823         $main::job_db->exec_statement("UPDATE ".$main::job_queue_tn." SET status = 'waiting', timestamp = '".(&calc_timestamp(&get_time(), 'plus', 60))."' WHERE id = $jobdb_id");
824         &main::release_ldap_handle($ldap_handle);
825         return undef;
826       } else {
827         &main::daemon_log("$session_id ERROR: More than one ObjectGroups with cn '$ogroup' was found in base '".$main::ldap_base."'!", 1);
828         $main::job_db->exec_statement("UPDATE ".$main::job_queue_tn." SET status = 'waiting', timestamp = '".(&calc_timestamp(&get_time(), 'plus', 60))."' WHERE id = $jobdb_id");
829         &main::release_ldap_handle($ldap_handle);
830         return undef;
831       }
833       # build the base, use optional base parameter or take it from ogroup
834       if(!(defined($base) && (length($base) > 0))) {
835         # Subtract the ObjectGroup cn
836         $base = $1 if $ogroup_entry->dn =~ /cn=$ogroup,ou=groups,(.*)$/;
837         &main::daemon_log("$session_id DEBUG: New base for system with mac address '$mac' is '$base'", 5);
838       }
839     }
841     # prepend ou=systems (configurable through config)
842     $base = $main::new_systems_ou.",".$base;
844     # Search for an existing entry (should be in ou=incoming)
845     my $ldap_mesg= $ldap_handle->search(
846       base => $main::ldap_base,
847       scope => 'sub',
848       filter => "(&(objectClass=GOhard)(|(macAddress=$mac)(dhcpHWaddress=$mac)))",
849     );
851     # TODO: Find a way to guess an ip address for hosts with no ldap entry (MAC->ARP->IP)
852     if($ldap_mesg->count == 1) {
853       &main::daemon_log("$session_id DEBUG: One system with mac address '$mac' was found in base '".$main::ldap_base."'!", 5);
854       # Get the entry from LDAP
855       $ldap_entry= $ldap_mesg->pop_entry();
857       if(!($ldap_entry->dn() eq "cn=".$ldap_entry->get_value('cn').",$base")) {
858         # Move the entry to the new ou
859         $ldap_entry->changetype('moddn');
860         $ldap_entry->add(
861           newrdn => "cn=".$ldap_entry->get_value('cn'),
862           deleteoldrdn => 1,
863           newsuperior => $base,
864         );
865         # To prevent replication problems just re-queue the job with 10 seconds in the future
866         my $moddn_result = $ldap_entry->update($ldap_handle);
867         if ($moddn_result->code() != 0) {
868           my $error_string = "Moving the system with mac address '$mac' to new base '$base' failed (code '".$moddn_result->code()."') with '".$moddn_result->{'errorMessage'}."'!";
869           &main::daemon_log("$session_id ERROR: $error_string", 1);
870           my $sql = "UPDATE $main::job_queue_tn SET status='error', result='$error_string' WHERE id=$jobdb_id";
871           &main::release_ldap_handle($ldap_handle);
872           return undef;
873         } else {
874           &main::daemon_log("$session_id INFO: System with mac address '$mac' was moved to base '".$main::ldap_base."'! Re-queuing job.", 4);
875           $main::job_db->exec_statement("UPDATE ".$main::job_queue_tn." SET status = 'waiting', timestamp = '".(&calc_timestamp(&get_time(), 'plus', 10))."' WHERE id = $jobdb_id");
876           &main::release_ldap_handle($ldap_handle);
877           return undef;
878         }
879       }
881     } elsif ($ldap_mesg->count == 0) {
882       &main::daemon_log("$session_id WARNING: No System with mac address '$mac' was found in base '".$main::ldap_base."'! Re-queuing job.", 4);
883       my $sql_statement = "UPDATE ".$main::job_queue_tn.
884               " SET status='waiting', timestamp = '".(&calc_timestamp(&get_time(), 'plus', 60))."' ".
885               " WHERE id = $jobdb_id";
886       $main::job_db->exec_statement($sql_statement);
887           &main::release_ldap_handle($ldap_handle);
888       return undef;
889     }
891     $ldap_mesg= $ldap_handle->search(
892       base => $main::ldap_base,
893       scope => 'sub',
894       filter => "(&(objectClass=GOhard)(|(macAddress=$mac)(dhcpHWaddress=$mac)))",
895     );
897     # TODO: Find a way to guess an ip address for hosts with no ldap entry (MAC->ARP->IP)
898     if($ldap_mesg->count == 1) {
899       $ldap_entry= $ldap_mesg->pop_entry();
900       # Check for needed objectClasses
901       my $oclasses = $ldap_entry->get_value('objectClass', asref => 1);
902       foreach my $oclass ("FAIobject", "GOhard", "gotoWorkstation") {
903         if(!(scalar grep $_ eq $oclass, map {$_ => 1} @$oclasses)) {
904           &main::daemon_log("$session_id INFO: Adding objectClass '$oclass' to system entry with mac adress '$mac'", 1);
905           $ldap_entry->add(
906             objectClass => $oclass,
907           );
908           my $oclass_result = $ldap_entry->update($ldap_handle);
909           if ($oclass_result->code() != 0) {
910             &main::daemon_log("$session_id ERROR: Adding the ObjectClass '$oclass' failed (code '".$oclass_result->code()."') with '".$oclass_result->{'errorMessage'}."'!", 1);
911           } else {
912             &main::daemon_log("$session_id DEBUG: Adding the ObjectClass '$oclass' to '".($ldap_entry->dn())."' succeeded!", 5);
913           }
914         }
915       }
917       # Set FAIstate
918       if(defined($ldap_entry->get_value('FAIstate'))) {
919         if(!($ldap_entry->get_value('FAIstate') eq 'install')) {
920           $ldap_entry->replace(
921             'FAIstate' => 'install'
922           );
923           my $replace_result = $ldap_entry->update($ldap_handle);
924           if ($replace_result->code() != 0) {
925             &main::daemon_log("$session_id ERROR: Setting the FAIstate to install failed with code '".$replace_result->code()."') and message '".$replace_result->{'errorMessage'}."'!", 1);
926           } else {
927             &main::daemon_log("$session_id DEBUG: Setting the FAIstate to install for '".($ldap_entry->dn())."' succeeded!", 5);
928           }
929         }
930       } else {
931         $ldap_entry->add(
932           'FAIstate' => 'install'
933         );
934         my $add_result = $ldap_entry->update($ldap_handle);
935         if ($add_result->code() != 0) {
936           &main::daemon_log("$session_id ERROR: Setting the FAIstate to install failed with code '".$add_result->code()."') and message '".$add_result->{'errorMessage'}."'!", 1);
937         } else {
938           &main::daemon_log("$session_id DEBUG: Setting the FAIstate to install for '".($ldap_entry->dn())."' succeeded!", 5);
939         }
940       }
943     } elsif ($ldap_mesg->count == 0) {
944       # TODO: Create a new entry
945       # $ldap_entry = Net::LDAP::Entry->new();
946       # $ldap_entry->dn("cn=$mac,$base");
947       &main::daemon_log("$session_id WARNING: No System with mac address '$mac' was found in base '".$main::ldap_base."'! Re-queuing job.", 4);
948       $main::job_db->exec_statement("UPDATE ".$main::job_queue_tn." SET status = 'waiting', timestamp = '".(&calc_timestamp(&get_time(), 'plus', 60))."' WHERE id = $jobdb_id");
949       &main::release_ldap_handle($ldap_handle);
950       return undef;
951     } else {
952       &main::daemon_log("$session_id ERROR: More than one system with mac address '$mac' was found in base '".$main::ldap_base."'!", 1);
953     }
955     # Add to ObjectGroup
956     my $ogroup_member = $ogroup_entry->get_value('member', asref => 1);
957     if( (!defined($ogroup_member)) ||
958         (!defined($ldap_entry)) ||
959         (!defined($ldap_entry->dn)) ||
960         (!(scalar grep $_ eq $ldap_entry->dn, @{$ogroup_member}))) {
961       $ogroup_entry->add (
962         'member' => $ldap_entry->dn(),
963       );
964       my $ogroup_result = $ogroup_entry->update($ldap_handle);
965       if ($ogroup_result->code() != 0) {
966         &main::daemon_log("$session_id ERROR: Updating the ObjectGroup '$ogroup' failed (code '".$ogroup_result->code()."') with '".$ogroup_result->{'errorMessage'}."'!", 1);
967       } else {
968         &main::daemon_log("$session_id DEBUG: Updating the ObjectGroup '$ogroup' for member '".($ldap_entry->dn())."' succeeded!", 5);
969       }
970     } else {
971       &main::daemon_log("$session_id DEBUG: System with mac address '$mac' is already a member of ObjectGroup '$ogroup'.", 5);
972     }
974     # Finally set gotoMode to active
975     if(defined($ldap_entry->get_value('gotoMode'))) {
976       if(!($ldap_entry->get_value('gotoMode') eq 'active')) {
977         $ldap_entry->replace(
978           'gotoMode' => 'active'
979         );
980         my $activate_result = $ldap_entry->update($ldap_handle);
981         if ($activate_result->code() != 0) {
982           &main::daemon_log("$session_id ERROR: Activating system '".$ldap_entry->dn()."' failed (code '".$activate_result->code()."') with '".$activate_result->{'errorMessage'}."'!", 1);
983         } else {
984           &main::daemon_log("$session_id DEBUG: Activating system '".$ldap_entry->dn()."' succeeded!", 5);
985           $activate_client = 1;
986         }
987       } else {
988           $activate_client = 1;
989       }
990     } else {
991       $ldap_entry->add(
992         'gotoMode' => 'active'
993       );
994       my $activate_result = $ldap_entry->update($ldap_handle);
995       if ($activate_result->code() != 0) {
996         &main::daemon_log("$session_id ERROR: Activating system '".$ldap_entry->dn()."' failed (code '".$activate_result->code()."') with '".$activate_result->{'errorMessage'}."'!", 1);
997       } else {
998         &main::daemon_log("$session_id DEBUG: Activating system '".$ldap_entry->dn()."' succeeded!", 5);
999         $activate_client = 1;
1000       }
1001     }
1003     if($activate_client == 1) {
1004         &main::daemon_log("$session_id DEBUG: Activating system with mac address '$mac'!", 5);
1006         # Create delivery list
1007         my @out_msg_l;
1009         # Set job to done
1010         $main::job_db->exec_statement("UPDATE jobs SET status = 'done' WHERE id = $jobdb_id");
1012         # create set_activated_for_installation message for delivery
1013         my $out_hash = &create_xml_hash("set_activated_for_installation", $source, $mac);
1014         my $out_msg = &create_xml_string($out_hash);
1015         push(@out_msg_l, $out_msg);
1017         # Return delivery list of messages
1018         &main::release_ldap_handle($ldap_handle);
1019         return @out_msg_l;
1021     } else {
1022       &main::daemon_log("$session_id WARNING: Activating system with mac address '$mac' failed! Re-queuing job.", 4);
1023       $main::job_db->exec_statement("UPDATE ".$main::job_queue_tn." SET status = 'waiting',  timestamp = '".(&calc_timestamp(&get_time(), 'plus', 60))."' WHERE id = $jobdb_id");
1024     }
1025     &main::release_ldap_handle($ldap_handle);
1026     return undef;
1030 ## @method get_hosts_with_module
1031 # Reports all GOsa-si-server providing the given module. 
1032 # @param msg - STRING - xml message with tag get_hosts_with_module
1033 # @param msg_hash - HASHREF - message information parsed into a hash
1034 # @param session_id - INTEGER - POE session id of the processing of this message
1035 # @return out_msg - STRING - feedback to GOsa in success and error case
1036 sub get_hosts_with_module {
1037     my ($msg, $msg_hash, $session_id) = @_;
1038     my $source = @{$msg_hash->{'source'}}[0];
1039     my $target = @{$msg_hash->{'target'}}[0];
1040     my $header= @{$msg_hash->{'header'}}[0];
1041     my $module_name = @{$msg_hash->{'module_name'}}[0];
1042     my $out_hash = &create_xml_hash($header, $target, $source);
1044     # Sanity check of module_name
1045     if ((not exists $msg_hash->{'module_name'}) || (@{$msg_hash->{'module_name'}} != 1))  {
1046         &add_content2xml_hash($out_hash, "error_string", "no module_name specified or module_name tag invalid");
1047         &add_content2xml_hash($out_hash, "error", "module_name");
1048         &main::daemon_log("$session_id ERROR: no module_name specified or module_name tag invalid: $msg", 1); 
1049         return (&create_xml_string($out_hash));
1050     }
1052     my $out_msg = &create_xml_string($out_hash);
1054     # Check localhost for module_name
1055     if (exists @{$main::known_modules->{'GosaPackages'}}[2]->{$module_name}) {
1056         my ($local_ip, $local_port) = split(/:/, $target);
1057         my $network_interface= &get_interface_for_ip($local_ip);
1058         my $local_mac = &get_mac_for_interface($network_interface);
1059         $out_msg =~ s/<\/xml>/<result>host0<\/result> <\/xml>/;
1060         my $host_infos = "<ip>$local_ip</ip>";
1061         $host_infos .= " <mac>$local_mac</mac>"; 
1062         $out_msg =~  s/<\/xml>/\n<answer0> $host_infos <\/answer0> \n <\/xml>/;
1063     }
1065     # Search for opsi hosts in server_db
1066     my $sql = "SELECT * FROM $main::known_server_tn WHERE loaded_modules LIKE '%$module_name%'"; 
1067     my $res = $main::known_server_db->select_dbentry($sql);
1068     while (my ($hit_id, $hit_hash) = each %$res) {
1069         $out_msg =~ s/<\/xml>/<result>host$hit_id<\/result> <\/xml>/;
1070         my $host_infos = "<ip>".$hit_hash->{'hostname'}."</ip>";
1071         $host_infos .= " <mac>".$hit_hash->{'macaddress'}."</mac>"; 
1072         $out_msg =~  s/<\/xml>/\n<answer$hit_id> $host_infos <\/answer$hit_id> \n <\/xml>/;
1073     }
1075     return $out_msg;
1078 # vim:ts=4:shiftwidth:expandtab
1079 1;