Code

2678284e982b466ae7389af8a0b80b4c751badd8
[gosa.git] / gosa-si / gosa-si-server
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 #         FILE:  gosa-sd
5 #
6 #        USAGE:  ./gosa-sd
7 #
8 #  DESCRIPTION:
9 #
10 #      OPTIONS:  ---
11 # REQUIREMENTS:  libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl 
12 #                libdata-dumper-simple-perl libdbd-sqlite3-perl libnet-ldap-perl
13 #                libpoe-perl
14 #         BUGS:  ---
15 #        NOTES:
16 #       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
17 #      COMPANY:
18 #      VERSION:  1.0
19 #      CREATED:  12.09.2007 08:54:41 CEST
20 #     REVISION:  ---
21 #===============================================================================
24 # TODO
25 #
26 # max_children wird momentan nicht mehr verwendet, jede eingehende nachricht bekommt ein eigenes POE child
28 use strict;
29 use warnings;
30 use Getopt::Long;
31 use Config::IniFiles;
32 use POSIX;
34 use Fcntl;
35 use IO::Socket::INET;
36 use IO::Handle;
37 use IO::Select;
38 use Symbol qw(qualify_to_ref);
39 use Crypt::Rijndael;
40 use MIME::Base64;
41 use Digest::MD5  qw(md5 md5_hex md5_base64);
42 use XML::Simple;
43 use Data::Dumper;
44 use Sys::Syslog qw( :DEFAULT setlogsock);
45 use Cwd;
46 use File::Spec;
47 use File::Basename;
48 use File::Find;
49 use File::Copy;
50 use File::Path;
51 use GOSA::DBsqlite;
52 use GOSA::GosaSupportDaemon;
53 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
54 use Net::LDAP;
55 use Net::LDAP::Util qw(:escape);
56 use Time::HiRes qw( usleep);
57 use DateTime;
59 my $modules_path = "/usr/lib/gosa-si/modules";
60 use lib "/usr/lib/gosa-si/modules";
62 # revision number of server and program name
63 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev: 10826 $';
64 my $server_headURL;
65 my $server_revision;
66 my $server_status;
67 our $prg= basename($0);
69 our $global_kernel;
70 my ($foreground, $ping_timeout);
71 my ($server);
72 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
73 my ($messaging_db_loop_delay);
74 my ($known_modules);
75 my ($procid, $pid);
76 my ($arp_fifo);
77 my ($xml);
78 my $sources_list;
79 my $max_clients;
80 my %repo_files=();
81 my $repo_path;
82 my %repo_dirs=();
83 # variables declared in config file are always set to 'our'
84 our (%cfg_defaults, $log_file, $pid_file, 
85     $server_ip, $server_port, $ClientPackages_key, 
86     $arp_activ, $gosa_unit_tag,
87     $GosaPackages_key, $gosa_timeout,
88     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
89     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
90     $arp_enabled, $arp_interface,
91     $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
92 );
94 # additional variable which should be globaly accessable
95 our $server_address;
96 our $server_mac_address;
97 our $gosa_address;
98 our $no_arp;
99 our $verbose;
100 our $forground;
101 our $cfg_file;
102 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
104 # specifies the verbosity of the daemon_log
105 $verbose = 0 ;
107 # if foreground is not null, script will be not forked to background
108 $foreground = 0 ;
110 # specifies the timeout seconds while checking the online status of a registrating client
111 $ping_timeout = 5;
113 $no_arp = 0;
114 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
115 my @packages_list_statements;
116 my $watch_for_new_jobs_in_progress = 0;
118 # holds all incoming decrypted messages
119 our $incoming_db;
120 our $incoming_tn = 'incoming';
121 my $incoming_file_name;
122 my @incoming_col_names = ("id INTEGER PRIMARY KEY", 
123         "timestamp DEFAULT 'none'", 
124         "headertag DEFAULT 'none'",
125                 "targettag DEFAULT 'none'",
126         "xmlmessage DEFAULT 'none'",
127         "module DEFAULT 'none'",
128         "sessionid DEFAULT '0'",
129         );
131 # holds all gosa jobs
132 our $job_db;
133 our $job_queue_tn = 'jobs';
134 my $job_queue_file_name;
135 my @job_queue_col_names = ("id INTEGER PRIMARY KEY", 
136                 "timestamp DEFAULT 'none'", 
137                 "status DEFAULT 'none'", 
138                 "result DEFAULT 'none'", 
139                 "progress DEFAULT 'none'", 
140         "headertag DEFAULT 'none'", 
141                 "targettag DEFAULT 'none'", 
142                 "xmlmessage DEFAULT 'none'", 
143                 "macaddress DEFAULT 'none'",
144                 "plainname DEFAULT 'none'",
145         "siserver DEFAULT 'none'",
146         "modified DEFAULT '0'",
147                 );
149 # holds all other gosa-si-server
150 our $known_server_db;
151 our $known_server_tn = "known_server";
152 my $known_server_file_name;
153 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
155 # holds all registrated clients
156 our $known_clients_db;
157 our $known_clients_tn = "known_clients";
158 my $known_clients_file_name;
159 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events", "keylifetime");
161 # holds all registered clients at a foreign server
162 our $foreign_clients_db;
163 our $foreign_clients_tn = "foreign_clients"; 
164 my $foreign_clients_file_name;
165 my @foreign_clients_col_names = ("hostname", "macaddress", "regserver", "timestamp");
167 # holds all logged in user at each client 
168 our $login_users_db;
169 our $login_users_tn = "login_users";
170 my $login_users_file_name;
171 my @login_users_col_names = ("client", "user", "timestamp");
173 # holds all fai server, the debian release and tag
174 our $fai_server_db;
175 our $fai_server_tn = "fai_server"; 
176 my $fai_server_file_name;
177 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag"); 
179 our $fai_release_db;
180 our $fai_release_tn = "fai_release"; 
181 my $fai_release_file_name;
182 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state"); 
184 # holds all packages available from different repositories
185 our $packages_list_db;
186 our $packages_list_tn = "packages_list";
187 my $packages_list_file_name;
188 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
189 my $outdir = "/tmp/packages_list_db";
190 my $arch = "i386"; 
192 # holds all messages which should be delivered to a user
193 our $messaging_db;
194 our $messaging_tn = "messaging"; 
195 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to", 
196         "flag", "direction", "delivery_time", "message", "timestamp" );
197 my $messaging_file_name;
199 # path to directory to store client install log files
200 our $client_fai_log_dir = "/var/log/fai"; 
202 # queue which stores taskes until one of the $max_children children are ready to process the task
203 my @tasks = qw();
204 my @msgs_to_decrypt = qw();
205 my $max_children = 2;
208 # loop delay for job queue to look for opsi jobs
209 my $job_queue_opsi_delay = 10;
210 our $opsi_client;
211 our $opsi_url;
212  
215 %cfg_defaults = (
216 "general" => {
217     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
218     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
219     },
220 "server" => {
221     "ip" => [\$server_ip, "0.0.0.0"],
222     "port" => [\$server_port, "20081"],
223     "known-clients"        => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
224     "known-servers"        => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
225     "incoming"             => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
226     "login-users"          => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
227     "fai-server"           => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
228     "fai-release"          => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
229     "packages-list"        => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
230     "messaging"            => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
231     "foreign-clients"      => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
232     "source-list"          => [\$sources_list, '/etc/apt/sources.list'],
233     "repo-path"            => [\$repo_path, '/srv/www/repository'],
234     "ldap-uri"             => [\$ldap_uri, ""],
235     "ldap-base"            => [\$ldap_base, ""],
236     "ldap-admin-dn"        => [\$ldap_admin_dn, ""],
237     "ldap-admin-password"  => [\$ldap_admin_password, ""],
238     "gosa-unit-tag"        => [\$gosa_unit_tag, ""],
239     "max-clients"          => [\$max_clients, 10],
240     "wol-password"           => [\$wake_on_lan_passwd, ""],
241     },
242 "GOsaPackages" => {
243     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
244     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
245     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
246     "key" => [\$GosaPackages_key, "none"],
247     },
248 "ClientPackages" => {
249     "key" => [\$ClientPackages_key, "none"],
250     },
251 "ServerPackages"=> {
252     "address"      => [\$foreign_server_string, ""],
253     "domain"  => [\$server_domain, ""],
254     "key"     => [\$ServerPackages_key, "none"],
255     "key-lifetime" => [\$foreign_servers_register_delay, 120],
256     "job-synchronization-enabled" => [\$job_synchronization, "true"],
257     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
258     },
259 "ArpHandler" => {
260     "enabled"   => [\$arp_enabled, "true"],
261     "interface" => [\$arp_interface, "all"],
262         },
263 "Opsi" => {
264     "enabled"  => [\$opsi_enabled, "false"], 
265     "server"   => [\$opsi_server, "localhost"],
266     "admin"    => [\$opsi_admin, "opsi-admin"],
267     "password" => [\$opsi_password, "secret"],
268    },
270 );
273 #===  FUNCTION  ================================================================
274 #         NAME:  usage
275 #   PARAMETERS:  nothing
276 #      RETURNS:  nothing
277 #  DESCRIPTION:  print out usage text to STDERR
278 #===============================================================================
279 sub usage {
280     print STDERR << "EOF" ;
281 usage: $prg [-hvf] [-c config]
283            -h        : this (help) message
284            -c <file> : config file
285            -f        : foreground, process will not be forked to background
286            -v        : be verbose (multiple to increase verbosity)
287            -no-arp   : starts $prg without connection to arp module
288  
289 EOF
290     print "\n" ;
294 #===  FUNCTION  ================================================================
295 #         NAME:  logging
296 #   PARAMETERS:  level - string - default 'info'
297 #                msg - string -
298 #                facility - string - default 'LOG_DAEMON'
299 #      RETURNS:  nothing
300 #  DESCRIPTION:  function for logging
301 #===============================================================================
302 sub daemon_log {
303     # log into log_file
304     my( $msg, $level ) = @_;
305     if(not defined $msg) { return }
306     if(not defined $level) { $level = 1 }
307     if(defined $log_file){
308         open(LOG_HANDLE, ">>$log_file");
309         chmod 0600, $log_file;
310         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
311             print STDERR "cannot open $log_file: $!";
312             return 
313         }
314         chomp($msg);
315         #$msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
316         if($level <= $verbose){
317             my ($seconds, $minutes, $hours, $monthday, $month,
318                     $year, $weekday, $yearday, $sommertime) = localtime(time);
319             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
320             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
321             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
322             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
323             $month = $monthnames[$month];
324             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
325             $year+=1900;
326             my $name = $prg;
328             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
329             print LOG_HANDLE $log_msg;
330             if( $foreground ) { 
331                 print STDERR $log_msg;
332             }
333         }
334         close( LOG_HANDLE );
335     }
339 #===  FUNCTION  ================================================================
340 #         NAME:  check_cmdline_param
341 #   PARAMETERS:  nothing
342 #      RETURNS:  nothing
343 #  DESCRIPTION:  validates commandline parameter
344 #===============================================================================
345 sub check_cmdline_param () {
346     my $err_config;
347     my $err_counter = 0;
348         if(not defined($cfg_file)) {
349                 $cfg_file = "/etc/gosa-si/server.conf";
350                 if(! -r $cfg_file) {
351                         $err_config = "please specify a config file";
352                         $err_counter += 1;
353                 }
354     }
355     if( $err_counter > 0 ) {
356         &usage( "", 1 );
357         if( defined( $err_config)) { print STDERR "$err_config\n"}
358         print STDERR "\n";
359         exit( -1 );
360     }
364 #===  FUNCTION  ================================================================
365 #         NAME:  check_pid
366 #   PARAMETERS:  nothing
367 #      RETURNS:  nothing
368 #  DESCRIPTION:  handels pid processing
369 #===============================================================================
370 sub check_pid {
371     $pid = -1;
372     # Check, if we are already running
373     if( open(LOCK_FILE, "<$pid_file") ) {
374         $pid = <LOCK_FILE>;
375         if( defined $pid ) {
376             chomp( $pid );
377             if( -f "/proc/$pid/stat" ) {
378                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
379                 if( $stat ) {
380                                         daemon_log("ERROR: Already running",1);
381                     close( LOCK_FILE );
382                     exit -1;
383                 }
384             }
385         }
386         close( LOCK_FILE );
387         unlink( $pid_file );
388     }
390     # create a syslog msg if it is not to possible to open PID file
391     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
392         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
393         if (open(LOCK_FILE, '<', $pid_file)
394                 && ($pid = <LOCK_FILE>))
395         {
396             chomp($pid);
397             $msg .= "(PID $pid)\n";
398         } else {
399             $msg .= "(unable to read PID)\n";
400         }
401         if( ! ($foreground) ) {
402             openlog( $0, "cons,pid", "daemon" );
403             syslog( "warning", $msg );
404             closelog();
405         }
406         else {
407             print( STDERR " $msg " );
408         }
409         exit( -1 );
410     }
413 #===  FUNCTION  ================================================================
414 #         NAME:  import_modules
415 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
416 #                are stored
417 #      RETURNS:  nothing
418 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
419 #                state is on is imported by "require 'file';"
420 #===============================================================================
421 sub import_modules {
422     daemon_log(" ", 1);
424     if (not -e $modules_path) {
425         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
426     }
428     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
429     while (defined (my $file = readdir (DIR))) {
430         if (not $file =~ /(\S*?).pm$/) {
431             next;
432         }
433                 my $mod_name = $1;
435         # ArpHandler switch
436         if( $file =~ /ArpHandler.pm/ ) {
437             if( $arp_enabled eq "false" ) { next; }
438         }
439         
440         eval { require $file; };
441         if ($@) {
442             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
443             daemon_log("$@", 5);
444                 } else {
445                         my $info = eval($mod_name.'::get_module_info()');
446                         # Only load module if get_module_info() returns a non-null object
447                         if( $info ) {
448                                 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
449                                 $known_modules->{$mod_name} = $info;
450                                 daemon_log("0 INFO: module $mod_name loaded", 5);
451                         }
452                 }
453     }   
455     close (DIR);
458 #===  FUNCTION  ================================================================
459 #         NAME:  password_check
460 #   PARAMETERS:  nothing
461 #      RETURNS:  nothing
462 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
463 #                the same password
464 #===============================================================================
465 sub password_check {
466     my $passwd_hash = {};
467     while (my ($mod_name, $mod_info) = each %$known_modules) {
468         my $mod_passwd = @$mod_info[1];
469         if (not defined $mod_passwd) { next; }
470         if (not exists $passwd_hash->{$mod_passwd}) {
471             $passwd_hash->{$mod_passwd} = $mod_name;
473         # escalates critical error
474         } else {
475             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
476             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
477             exit( -1 );
478         }
479     }
484 #===  FUNCTION  ================================================================
485 #         NAME:  sig_int_handler
486 #   PARAMETERS:  signal - string - signal arose from system
487 #      RETURNS:  nothing
488 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
489 #===============================================================================
490 sub sig_int_handler {
491     my ($signal) = @_;
493 #       if (defined($ldap_handle)) {
494 #               $ldap_handle->disconnect;
495 #       }
496     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
497     
499     daemon_log("shutting down gosa-si-server", 1);
500     system("kill `ps -C gosa-si-server -o pid=`");
502 $SIG{INT} = \&sig_int_handler;
505 sub check_key_and_xml_validity {
506     my ($crypted_msg, $module_key, $session_id) = @_;
507     my $msg;
508     my $msg_hash;
509     my $error_string;
510     eval{
511         $msg = &decrypt_msg($crypted_msg, $module_key);
513         if ($msg =~ /<xml>/i){
514             $msg =~ s/\s+/ /g;  # just for better daemon_log
515             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
516             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
518             ##############
519             # check header
520             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
521             my $header_l = $msg_hash->{'header'};
522             if( 1 > @{$header_l} ) { die 'empty header tag'; }
523             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
524             my $header = @{$header_l}[0];
525             if( 0 == length $header) { die 'empty string in header tag'; }
527             ##############
528             # check source
529             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
530             my $source_l = $msg_hash->{'source'};
531             if( 1 > @{$source_l} ) { die 'empty source tag'; }
532             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
533             my $source = @{$source_l}[0];
534             if( 0 == length $source) { die 'source error'; }
536             ##############
537             # check target
538             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
539             my $target_l = $msg_hash->{'target'};
540             if( 1 > @{$target_l} ) { die 'empty target tag'; }
541         }
542     };
543     if($@) {
544         daemon_log("$session_id ERROR: do not understand the message: $@", 1);
545         $msg = undef;
546         $msg_hash = undef;
547     }
549     return ($msg, $msg_hash);
553 sub check_outgoing_xml_validity {
554     my ($msg, $session_id) = @_;
556     my $msg_hash;
557     eval{
558         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
560         ##############
561         # check header
562         my $header_l = $msg_hash->{'header'};
563         if( 1 != @{$header_l} ) {
564             die 'no or more than one headers specified';
565         }
566         my $header = @{$header_l}[0];
567         if( 0 == length $header) {
568             die 'header has length 0';
569         }
571         ##############
572         # check source
573         my $source_l = $msg_hash->{'source'};
574         if( 1 != @{$source_l} ) {
575             die 'no or more than 1 sources specified';
576         }
577         my $source = @{$source_l}[0];
578         if( 0 == length $source) {
579             die 'source has length 0';
580         }
581         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
582                 $source =~ /^GOSA$/i ) {
583             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
584         }
585         
586         ##############
587         # check target  
588         my $target_l = $msg_hash->{'target'};
589         if( 0 == @{$target_l} ) {
590             die "no targets specified";
591         }
592         foreach my $target (@$target_l) {
593             if( 0 == length $target) {
594                 die "target has length 0";
595             }
596             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
597                     $target =~ /^GOSA$/i ||
598                     $target =~ /^\*$/ ||
599                     $target =~ /KNOWN_SERVER/i ||
600                     $target =~ /JOBDB/i ||
601                     $target =~ /^([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 ){
602                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
603             }
604         }
605     };
606     if($@) {
607         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
608         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
609         $msg_hash = undef;
610     }
612     return ($msg_hash);
616 sub input_from_known_server {
617     my ($input, $remote_ip, $session_id) = @_ ;  
618     my ($msg, $msg_hash, $module);
620     my $sql_statement= "SELECT * FROM known_server";
621     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
623     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
624         my $host_name = $hit->{hostname};
625         if( not $host_name =~ "^$remote_ip") {
626             next;
627         }
628         my $host_key = $hit->{hostkey};
629         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
630         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
632         # check if module can open msg envelope with module key
633         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
634         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
635             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
636             daemon_log("$@", 8);
637             next;
638         }
639         else {
640             $msg = $tmp_msg;
641             $msg_hash = $tmp_msg_hash;
642             $module = "ServerPackages";
643             last;
644         }
645     }
647     if( (!$msg) || (!$msg_hash) || (!$module) ) {
648         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
649     }
650   
651     return ($msg, $msg_hash, $module);
655 sub input_from_known_client {
656     my ($input, $remote_ip, $session_id) = @_ ;  
657     my ($msg, $msg_hash, $module);
659     my $sql_statement= "SELECT * FROM known_clients";
660     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
661     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
662         my $host_name = $hit->{hostname};
663         if( not $host_name =~ /^$remote_ip:\d*$/) {
664                 next;
665                 }
666         my $host_key = $hit->{hostkey};
667         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
668         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
670         # check if module can open msg envelope with module key
671         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
673         if( (!$msg) || (!$msg_hash) ) {
674             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
675             &daemon_log("$@", 8);
676             next;
677         }
678         else {
679             $module = "ClientPackages";
680             last;
681         }
682     }
684     if( (!$msg) || (!$msg_hash) || (!$module) ) {
685         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
686     }
688     return ($msg, $msg_hash, $module);
692 sub input_from_unknown_host {
693     no strict "refs";
694     my ($input, $session_id) = @_ ;
695     my ($msg, $msg_hash, $module);
696     my $error_string;
697     
698         my %act_modules = %$known_modules;
699         
700     while( my ($mod, $info) = each(%act_modules)) {
702         # check a key exists for this module
703         my $module_key = ${$mod."_key"};
704         if( not defined $module_key ) {
705             if( $mod eq 'ArpHandler' ) {
706                 next;
707             }
708             daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
709             next;
710         }
711         daemon_log("$session_id DEBUG: $mod: $module_key", 7);
713         # check if module can open msg envelope with module key
714         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
715         if( (not defined $msg) || (not defined $msg_hash) ) {
716             next;
717         }
718         else {
719             $module = $mod;
720             last;
721         }
722     }
724     if( (!$msg) || (!$msg_hash) || (!$module)) {
725         daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
726     }
728     return ($msg, $msg_hash, $module);
732 sub create_ciphering {
733     my ($passwd) = @_;
734         if((!defined($passwd)) || length($passwd)==0) {
735                 $passwd = "";
736         }
737     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
738     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
739     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
740     $my_cipher->set_iv($iv);
741     return $my_cipher;
745 sub encrypt_msg {
746     my ($msg, $key) = @_;
747     my $my_cipher = &create_ciphering($key);
748     my $len;
749     {
750             use bytes;
751             $len= 16-length($msg)%16;
752     }
753     $msg = "\0"x($len).$msg;
754     $msg = $my_cipher->encrypt($msg);
755     chomp($msg = &encode_base64($msg));
756     # there are no newlines allowed inside msg
757     $msg=~ s/\n//g;
758     return $msg;
762 sub decrypt_msg {
764     my ($msg, $key) = @_ ;
765     $msg = &decode_base64($msg);
766     my $my_cipher = &create_ciphering($key);
767     $msg = $my_cipher->decrypt($msg); 
768     $msg =~ s/\0*//g;
769     return $msg;
773 sub get_encrypt_key {
774     my ($target) = @_ ;
775     my $encrypt_key;
776     my $error = 0;
778     # target can be in known_server
779     if( not defined $encrypt_key ) {
780         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
781         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
782         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
783             my $host_name = $hit->{hostname};
784             if( $host_name ne $target ) {
785                 next;
786             }
787             $encrypt_key = $hit->{hostkey};
788             last;
789         }
790     }
792     # target can be in known_client
793     if( not defined $encrypt_key ) {
794         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
795         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
796         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
797             my $host_name = $hit->{hostname};
798             if( $host_name ne $target ) {
799                 next;
800             }
801             $encrypt_key = $hit->{hostkey};
802             last;
803         }
804     }
806     return $encrypt_key;
810 #===  FUNCTION  ================================================================
811 #         NAME:  open_socket
812 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
813 #                [PeerPort] string necessary if port not appended by PeerAddr
814 #      RETURNS:  socket IO::Socket::INET
815 #  DESCRIPTION:  open a socket to PeerAddr
816 #===============================================================================
817 sub open_socket {
818     my ($PeerAddr, $PeerPort) = @_ ;
819     if(defined($PeerPort)){
820         $PeerAddr = $PeerAddr.":".$PeerPort;
821     }
822     my $socket;
823     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
824             Porto => "tcp",
825             Type => SOCK_STREAM,
826             Timeout => 5,
827             );
828     if(not defined $socket) {
829         return;
830     }
831 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
832     return $socket;
836 sub get_local_ip_for_remote_ip {
837         my $remote_ip= shift;
838         my $result="0.0.0.0";
840         if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
841                 if($remote_ip eq "127.0.0.1") {
842                         $result = "127.0.0.1";
843                 } else {
844                         my $PROC_NET_ROUTE= ('/proc/net/route');
846                         open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
847                                 or die "Could not open $PROC_NET_ROUTE";
849                         my @ifs = <PROC_NET_ROUTE>;
851                         close(PROC_NET_ROUTE);
853                         # Eat header line
854                         shift @ifs;
855                         chomp @ifs;
856                         foreach my $line(@ifs) {
857                                 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
858                                 my $destination;
859                                 my $mask;
860                                 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
861                                 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
862                                 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
863                                 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
864                                 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
865                                         # destination matches route, save mac and exit
866                                         $result= &get_ip($Iface);
867                                         last;
868                                 }
869                         }
870                 }
871         } else {
872                 daemon_log("0 WARNING: get_local_ip_for_remote_ip() was called with a non-ip parameter: '$remote_ip'", 1);
873         }
874         return $result;
878 sub send_msg_to_target {
879     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
880     my $error = 0;
881     my $header;
882     my $timestamp = &get_time();
883     my $new_status;
884     my $act_status;
885     my ($sql_statement, $res);
886   
887     if( $msg_header ) {
888         $header = "'$msg_header'-";
889     } else {
890         $header = "";
891     }
893         # Patch the source ip
894         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
895                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
896                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
897         }
899     # encrypt xml msg
900     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
902     # opensocket
903     my $socket = &open_socket($address);
904     if( !$socket ) {
905         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
906         $error++;
907     }
908     
909     if( $error == 0 ) {
910         # send xml msg
911         print $socket $crypted_msg."\n";
913         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
914         daemon_log("$session_id DEBUG: message:\n$msg", 9);
915         
916     }
918     # close socket in any case
919     if( $socket ) {
920         close $socket;
921     }
923     if( $error > 0 ) { $new_status = "down"; }
924     else { $new_status = $msg_header; }
927     # known_clients
928     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
929     $res = $known_clients_db->select_dbentry($sql_statement);
930     if( keys(%$res) == 1) {
931         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
932         if ($act_status eq "down" && $new_status eq "down") {
933             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
934             $res = $known_clients_db->del_dbentry($sql_statement);
935             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
936         } else { 
937             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
938             $res = $known_clients_db->update_dbentry($sql_statement);
939             if($new_status eq "down"){
940                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
941             } else {
942                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
943             }
944         }
945     }
947     # known_server
948     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
949     $res = $known_server_db->select_dbentry($sql_statement);
950     if( keys(%$res) == 1) {
951         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
952         if ($act_status eq "down" && $new_status eq "down") {
953             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
954             $res = $known_server_db->del_dbentry($sql_statement);
955             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
956         } 
957         else { 
958             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
959             $res = $known_server_db->update_dbentry($sql_statement);
960             if($new_status eq "down"){
961                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
962             } else {
963                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
964             }
965         }
966     }
967     return $error; 
971 sub update_jobdb_status_for_send_msgs {
972     my ($answer, $error) = @_;
973     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
974         my $jobdb_id = $1;
975             
976         # sending msg faild
977         if( $error ) {
978             if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
979                 my $sql_statement = "UPDATE $job_queue_tn ".
980                     "SET status='error', result='can not deliver msg, please consult log file' ".
981                     "WHERE id=$jobdb_id";
982                 my $res = $job_db->update_dbentry($sql_statement);
983             }
985         # sending msg was successful
986         } else {
987             my $sql_statement = "UPDATE $job_queue_tn ".
988                 "SET status='done' ".
989                 "WHERE id=$jobdb_id AND status='processed'";
990             my $res = $job_db->update_dbentry($sql_statement);
991         }
992     }
996 sub sig_handler {
997         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
998         daemon_log("0 INFO got signal '$signal'", 1); 
999         $kernel->sig_handled();
1000         return;
1004 sub msg_to_decrypt {
1005     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1006     my $session_id = $session->ID;
1007     my ($msg, $msg_hash, $module);
1008     my $error = 0;
1010     # hole neue msg aus @msgs_to_decrypt
1011     my $next_msg = shift @msgs_to_decrypt;
1012     
1013     # entschlüssle sie
1015     # msg is from a new client or gosa
1016     ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1017     # msg is from a gosa-si-server
1018     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1019         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1020     }
1021     # msg is from a gosa-si-client
1022     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1023         ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1024     }
1025     # an error occurred
1026     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1027         # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1028         # could not understand a msg from its server the client cause a re-registering process
1029         daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1030                 "' to cause a re-registering of the client if necessary", 3);
1031         my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1032         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1033         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1034             my $host_name = $hit->{'hostname'};
1035             my $host_key = $hit->{'hostkey'};
1036             my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1037             my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1038             &update_jobdb_status_for_send_msgs($ping_msg, $error);
1039         }
1040         $error++;
1041     }
1044     my $header;
1045     my $target;
1046     my $source;
1047     my $done = 0;
1048     my $sql;
1049     my $res;
1051     # check whether this message should be processed here
1052     if ($error == 0) {
1053         $header = @{$msg_hash->{'header'}}[0];
1054         $target = @{$msg_hash->{'target'}}[0];
1055         $source = @{$msg_hash->{'source'}}[0];
1056                 my $not_found_in_known_clients_db = 0;
1057                 my $not_found_in_known_server_db = 0;
1058                 my $not_found_in_foreign_clients_db = 0;
1059         my $local_address;
1060         my ($target_ip, $target_port) = split(':', $target);
1061                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1062                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1063                 } else {
1064             $local_address = $server_address;
1065         }
1067         # target and source is equal to GOSA -> process here
1068         if (not $done) {
1069             if ($target eq "GOSA" && $source eq "GOSA") {
1070                 $done = 1;                    
1071             }
1072         }
1074         # target is own address without forward_to_gosa-tag -> process here
1075         if (not $done) {
1076             if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1077                 $done = 1;
1078                 if ($source eq "GOSA") {
1079                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1080                 }
1081                 #print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
1082             }
1083         }
1085         # target is a client address in known_clients -> process here
1086                 if (not $done) {
1087                                 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1088                                 $res = $known_clients_db->select_dbentry($sql);
1089                                 if (keys(%$res) > 0) {
1090                                                 $done = 1; 
1091                                                 my $hostname = $res->{1}->{'hostname'};
1092                                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1093                                                 #print STDERR "target is a client address in known_clients -> process here\n";
1094                         my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1095                         if ($source eq "GOSA") {
1096                             $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1097                         }
1099                                 } else {
1100                                                 $not_found_in_known_clients_db = 1;
1101                                 }
1102                 }
1103         
1104         # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1105         if (not $done) {
1106             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1107             my $gosa_at;
1108             my $gosa_session_id;
1109             if (($target eq $local_address) && (defined $forward_to_gosa)){
1110                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1111                 if ($gosa_at ne $local_address) {
1112                     $done = 1;
1113                     #print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n"; 
1114                 }
1115             }
1116         }
1118         # if message should be processed here -> add message to incoming_db
1119                 if ($done) {
1120                                 # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1121                                 # so gosa-si-server knows how to process this kind of messages
1122                                 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1123                                                 $module = "GosaPackages";
1124                                 }
1126                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1127                                                                 primkey=>[],
1128                                                                 headertag=>$header,
1129                                                                 targettag=>$target,
1130                                                                 xmlmessage=>&encode_base64($msg),
1131                                                                 timestamp=>&get_time,
1132                                                                 module=>$module,
1133                                                                 sessionid=>$session_id,
1134                                                                 } );
1135                 }
1137         # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1138         if (not $done) {
1139             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1140             my $gosa_at;
1141             my $gosa_session_id;
1142             if (($target eq $local_address) && (defined $forward_to_gosa)){
1143                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1144                 if ($gosa_at eq $local_address) {
1145                     my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1146                     if( defined $session_reference ) {
1147                         $heap = $session_reference->get_heap();
1148                     }
1149                     if(exists $heap->{'client'}) {
1150                         $msg = &encrypt_msg($msg, $GosaPackages_key);
1151                         $heap->{'client'}->put($msg);
1152                         &daemon_log("$session_id INFO: incoming '$header' message forwarded to GOsa", 5); 
1153                     }
1154                     $done = 1;
1155                     #print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
1156                 }
1157             }
1159         }
1161         # target is a client address in foreign_clients -> forward to registration server
1162         if (not $done) {
1163             $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1164             $res = $foreign_clients_db->select_dbentry($sql);
1165             if (keys(%$res) > 0) {
1166                     my $hostname = $res->{1}->{'hostname'};
1167                     my ($host_ip, $host_port) = split(/:/, $hostname);
1168                     my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1169                 my $regserver = $res->{1}->{'regserver'};
1170                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1171                 my $res = $known_server_db->select_dbentry($sql);
1172                 if (keys(%$res) > 0) {
1173                     my $regserver_key = $res->{1}->{'hostkey'};
1174                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1175                     $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1176                     if ($source eq "GOSA") {
1177                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1178                     }
1179                     &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1180                 }
1181                 $done = 1;
1182                 #print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
1183             } else {
1184                                 $not_found_in_foreign_clients_db = 1;
1185                         }
1186         }
1188         # target is a server address -> forward to server
1189         if (not $done) {
1190             $sql = "SELECT * FROM $known_server_tn WHERE hostname='$target'";
1191             $res = $known_server_db->select_dbentry($sql);
1192             if (keys(%$res) > 0) {
1193                 my $hostkey = $res->{1}->{'hostkey'};
1195                 if ($source eq "GOSA") {
1196                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1197                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1199                 }
1201                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1202                 $done = 1;
1203                 #print STDERR "target is a server address -> forward to server\n";
1204             } else {
1205                                 $not_found_in_known_server_db = 1;
1206                         }
1207         }
1209                 
1210                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1211                 if ( $not_found_in_foreign_clients_db 
1212                                                 && $not_found_in_known_server_db
1213                                                 && $not_found_in_known_clients_db) {
1214                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1215                                                                 primkey=>[],
1216                                                                 headertag=>$header,
1217                                                                 targettag=>$target,
1218                                                                 xmlmessage=>&encode_base64($msg),
1219                                                                 timestamp=>&get_time,
1220                                                                 module=>$module,
1221                                                                 sessionid=>$session_id,
1222                                                                 } );
1223                                 $done = 1;
1224                 }
1227         if (not $done) {
1228             daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1229             if ($source eq "GOSA") {
1230                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1231                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1233                 my $session_reference = $kernel->ID_id_to_session($session_id);
1234                 if( defined $session_reference ) {
1235                     $heap = $session_reference->get_heap();
1236                 }
1237                 if(exists $heap->{'client'}) {
1238                     $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1239                     $heap->{'client'}->put($error_msg);
1240                 }
1241             }
1242         }
1244     }
1246     return;
1250 sub next_task {
1251     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1252     my $running_task = POE::Wheel::Run->new(
1253             Program => sub { process_task($session, $heap, $task) },
1254             StdioFilter => POE::Filter::Reference->new(),
1255             StdoutEvent  => "task_result",
1256             StderrEvent  => "task_debug",
1257             CloseEvent   => "task_done",
1258             );
1259     $heap->{task}->{ $running_task->ID } = $running_task;
1262 sub handle_task_result {
1263     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1264     my $client_answer = $result->{'answer'};
1265     if( $client_answer =~ s/session_id=(\d+)$// ) {
1266         my $session_id = $1;
1267         if( defined $session_id ) {
1268             my $session_reference = $kernel->ID_id_to_session($session_id);
1269             if( defined $session_reference ) {
1270                 $heap = $session_reference->get_heap();
1271             }
1272         }
1274         if(exists $heap->{'client'}) {
1275             $heap->{'client'}->put($client_answer);
1276         }
1277     }
1278     $kernel->sig(CHLD => "child_reap");
1281 sub handle_task_debug {
1282     my $result = $_[ARG0];
1283     print STDERR "$result\n";
1286 sub handle_task_done {
1287     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1288     delete $heap->{task}->{$task_id};
1291 sub process_task {
1292     no strict "refs";
1293     #CHECK: Not @_[...]?
1294     my ($session, $heap, $task) = @_;
1295     my $error = 0;
1296     my $answer_l;
1297     my ($answer_header, @answer_target_l, $answer_source);
1298     my $client_answer = "";
1300     # prepare all variables needed to process message
1301     #my $msg = $task->{'xmlmessage'};
1302     my $msg = &decode_base64($task->{'xmlmessage'});
1303     my $incoming_id = $task->{'id'};
1304     my $module = $task->{'module'};
1305     my $header =  $task->{'headertag'};
1306     my $session_id = $task->{'sessionid'};
1307     my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1308     my $source = @{$msg_hash->{'source'}}[0];
1309     
1310     # set timestamp of incoming client uptodate, so client will not 
1311     # be deleted from known_clients because of expiration
1312     my $act_time = &get_time();
1313     my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'"; 
1314     my $res = $known_clients_db->exec_statement($sql);
1316     ######################
1317     # process incoming msg
1318     if( $error == 0) {
1319         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1320         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1321         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1323         if ( 0 < @{$answer_l} ) {
1324             my $answer_str = join("\n", @{$answer_l});
1325             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1326                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1327             }
1328             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,8);
1329         } else {
1330             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,8);
1331         }
1333     }
1334     if( !$answer_l ) { $error++ };
1336     ########
1337     # answer
1338     if( $error == 0 ) {
1340         foreach my $answer ( @{$answer_l} ) {
1341             # check outgoing msg to xml validity
1342             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1343             if( not defined $answer_hash ) { next; }
1344             
1345             $answer_header = @{$answer_hash->{'header'}}[0];
1346             @answer_target_l = @{$answer_hash->{'target'}};
1347             $answer_source = @{$answer_hash->{'source'}}[0];
1349             # deliver msg to all targets 
1350             foreach my $answer_target ( @answer_target_l ) {
1352                 # targets of msg are all gosa-si-clients in known_clients_db
1353                 if( $answer_target eq "*" ) {
1354                     # answer is for all clients
1355                     my $sql_statement= "SELECT * FROM known_clients";
1356                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1357                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1358                         my $host_name = $hit->{hostname};
1359                         my $host_key = $hit->{hostkey};
1360                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1361                         &update_jobdb_status_for_send_msgs($answer, $error);
1362                     }
1363                 }
1365                 # targets of msg are all gosa-si-server in known_server_db
1366                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1367                     # answer is for all server in known_server
1368                     my $sql_statement= "SELECT * FROM $known_server_tn";
1369                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1370                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1371                         my $host_name = $hit->{hostname};
1372                         my $host_key = $hit->{hostkey};
1373                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1374                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1375                         &update_jobdb_status_for_send_msgs($answer, $error);
1376                     }
1377                 }
1379                 # target of msg is GOsa
1380                                 elsif( $answer_target eq "GOSA" ) {
1381                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1382                                         my $add_on = "";
1383                     if( defined $session_id ) {
1384                         $add_on = ".session_id=$session_id";
1385                     }
1386                     # answer is for GOSA and has to returned to connected client
1387                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1388                     $client_answer = $gosa_answer.$add_on;
1389                 }
1391                 # target of msg is job queue at this host
1392                 elsif( $answer_target eq "JOBDB") {
1393                     $answer =~ /<header>(\S+)<\/header>/;   
1394                     my $header;
1395                     if( defined $1 ) { $header = $1; }
1396                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1397                     &update_jobdb_status_for_send_msgs($answer, $error);
1398                 }
1400                 # target of msg is a mac address
1401                 elsif( $answer_target =~ /^([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 ) {
1402                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1403                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1404                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1405                     my $found_ip_flag = 0;
1406                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1407                         my $host_name = $hit->{hostname};
1408                         my $host_key = $hit->{hostkey};
1409                         $answer =~ s/$answer_target/$host_name/g;
1410                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1411                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1412                         &update_jobdb_status_for_send_msgs($answer, $error);
1413                         $found_ip_flag++ ;
1414                     }   
1415                     if( $found_ip_flag == 0) {
1416                         daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1417                     }
1419                 #  answer is for one specific host   
1420                 } else {
1421                     # get encrypt_key
1422                     my $encrypt_key = &get_encrypt_key($answer_target);
1423                     if( not defined $encrypt_key ) {
1424                         # unknown target
1425                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1426                         next;
1427                     }
1428                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1429                     &update_jobdb_status_for_send_msgs($answer, $error);
1430                 }
1431             }
1432         }
1433     }
1435     my $filter = POE::Filter::Reference->new();
1436     my %result = ( 
1437             status => "seems ok to me",
1438             answer => $client_answer,
1439             );
1441     my $output = $filter->put( [ \%result ] );
1442     print @$output;
1447 sub session_start {
1448     my ($kernel) = $_[KERNEL];
1449     $global_kernel = $kernel;
1450     $kernel->yield('register_at_foreign_servers');
1451         $kernel->yield('create_fai_server_db', $fai_server_tn );
1452         $kernel->yield('create_fai_release_db', $fai_release_tn );
1453     $kernel->yield('watch_for_next_tasks');
1454         $kernel->sig(USR1 => "sig_handler");
1455         $kernel->sig(USR2 => "recreate_packages_db");
1456         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1457         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1458     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1459         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1460     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1461         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1462     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1464     # Start opsi check
1465     if ($opsi_enabled eq "true") {
1466         $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay); 
1467     }
1472 sub watch_for_done_jobs {
1473     #CHECK: $heap for what?
1474     my ($kernel,$heap) = @_[KERNEL, HEAP];
1476     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1477         my $res = $job_db->select_dbentry( $sql_statement );
1479     while( my ($id, $hit) = each %{$res} ) {
1480         my $jobdb_id = $hit->{id};
1481         my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1482         my $res = $job_db->del_dbentry($sql_statement); 
1483     }
1485     $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1489 sub watch_for_opsi_jobs {
1490     my ($kernel) = $_[KERNEL];
1492     # This is not very nice to look for opsi install jobs, but headertag has to be trigger_action_reinstall. The only way to identify a 
1493     # opsi install job is to parse the xml message. There is still the correct header.
1494     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
1495         my $res = $job_db->select_dbentry( $sql_statement );
1497     # Ask OPSI for an update of the running jobs
1498     while (my ($id, $hit) = each %$res ) {
1499         # Determine current parameters of the job
1500         my $hostId = $hit->{'plainname'};
1501         my $macaddress = $hit->{'macaddress'};
1502         my $progress = $hit->{'progress'};
1504         my $result= {};
1505         
1506         # For hosts, only return the products that are or get installed
1507         my $callobj;
1508         $callobj = {
1509             method  => 'getProductStates_hash',
1510             params  => [ $hostId ],
1511             id  => 1,
1512         };
1513         
1514         my $hres = $opsi_client->call($opsi_url, $callobj);
1515         #my ($hres_err, $hres_err_string) = &check_opsi_res($hres);
1516         if (not &check_opsi_res($hres)) {
1517             my $htmp= $hres->result->{$hostId};
1518         
1519             # Check state != not_installed or action == setup -> load and add
1520             my $products= 0;
1521             my $installed= 0;
1522             my $installing = 0;
1523             my $error= 0;  
1524             my @installed_list;
1525             my @error_list;
1526             my $act_status = "none";
1527             foreach my $product (@{$htmp}){
1529                 if ($product->{'installationStatus'} ne "not_installed" or
1530                         $product->{'actionRequest'} eq "setup"){
1532                     # Increase number of products for this host
1533                     $products++;
1534         
1535                     if ($product->{'installationStatus'} eq "failed"){
1536                         $result->{$product->{'productId'}}= "error";
1537                         unshift(@error_list, $product->{'productId'});
1538                         $error++;
1539                     }
1540                     if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
1541                         $result->{$product->{'productId'}}= "installed";
1542                         unshift(@installed_list, $product->{'productId'});
1543                         $installed++;
1544                     }
1545                     if ($product->{'installationStatus'} eq "installing"){
1546                         $result->{$product->{'productId'}}= "installing";
1547                         $installing++;
1548                         $act_status = "installing - ".$product->{'productId'};
1549                     }
1550                 }
1551             }
1552         
1553             # Estimate "rough" progress
1554             $result->{'progress'}= int($installed * 100 / $products);
1556             # Set updates in job queue
1557             if ((not $error) && (not $installing) && ($installed)) {
1558                 $act_status = "installed - ".join(", ", @installed_list);
1559             }
1560             if ($error) {
1561                 $act_status = "error - ".join(", ", @error_list);
1562             }
1563             if ($progress ne $result->{'progress'} ) {
1564                 # Updating progress and result 
1565                 my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress='$macaddress' AND siserver='localhost'";
1566                 my $update_res = $job_db->update_dbentry($update_statement);
1567             }
1568             if ($progress eq 100) { 
1569                 # Updateing status
1570                 my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
1571                 if ($error) {
1572                     $done_statement .= "status='error'";
1573                 } else {
1574                     $done_statement .= "status='done'";
1575                 }
1576                 $done_statement .= " WHERE macaddress='$macaddress' AND siserver='localhost'";
1577                 my $done_res = $job_db->update_dbentry($done_statement);
1578             }
1581         }
1582     }
1584     $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
1588 # If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
1589 sub watch_for_modified_jobs {
1590     my ($kernel,$heap) = @_[KERNEL, HEAP];
1592     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE ((siserver='localhost') AND (modified='1'))"; 
1593     my $res = $job_db->select_dbentry( $sql_statement );
1594     
1595     # if db contains no jobs which should be update, do nothing
1596     if (keys %$res != 0) {
1598         if ($job_synchronization  eq "true") {
1599             # make out of the db result a gosa-si message   
1600             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1601  
1602             # update all other SI-server
1603             &inform_all_other_si_server($update_msg);
1604         }
1606         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1607         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1608         $res = $job_db->update_dbentry($sql_statement);
1609     }
1611     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1615 sub watch_for_new_jobs {
1616         if($watch_for_new_jobs_in_progress == 0) {
1617                 $watch_for_new_jobs_in_progress = 1;
1618                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1620                 # check gosa job quaeue for jobs with executable timestamp
1621                 my $timestamp = &get_time();
1622                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1623                 my $res = $job_db->exec_statement( $sql_statement );
1625                 # Merge all new jobs that would do the same actions
1626                 my @drops;
1627                 my $hits;
1628                 foreach my $hit (reverse @{$res} ) {
1629                         my $macaddress= lc @{$hit}[8];
1630                         my $headertag= @{$hit}[5];
1631                         if(
1632                                 defined($hits->{$macaddress}) &&
1633                                 defined($hits->{$macaddress}->{$headertag}) &&
1634                                 defined($hits->{$macaddress}->{$headertag}[0])
1635                         ) {
1636                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1637                         }
1638                         $hits->{$macaddress}->{$headertag}= $hit;
1639                 }
1641                 # Delete new jobs with a matching job in state 'processing'
1642                 foreach my $macaddress (keys %{$hits}) {
1643                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1644                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1645                                 if(defined($jobdb_id)) {
1646                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1647                                         my $res = $job_db->exec_statement( $sql_statement );
1648                                         foreach my $hit (@{$res}) {
1649                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1650                                         }
1651                                 } else {
1652                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1653                                 }
1654                         }
1655                 }
1657                 # Commit deletion
1658                 $job_db->exec_statementlist(\@drops);
1660                 # Look for new jobs that could be executed
1661                 foreach my $macaddress (keys %{$hits}) {
1663                         # Look if there is an executing job
1664                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1665                         my $res = $job_db->exec_statement( $sql_statement );
1667                         # Skip new jobs for host if there is a processing job
1668                         if(defined($res) and defined @{$res}[0]) {
1669                                 next;
1670                         }
1672                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1673                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1674                                 if(defined($jobdb_id)) {
1675                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1677                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1678                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1679                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1681                                         # expect macaddress is unique!!!!!!
1682                                         my $target = $res_hash->{1}->{hostname};
1684                                         # change header
1685                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1687                                         # add sqlite_id
1688                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1690                                         $job_msg =~ /<header>(\S+)<\/header>/;
1691                                         my $header = $1 ;
1692                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1694                                         # update status in job queue to 'processing'
1695                                         $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1696                                         my $res = $job_db->update_dbentry($sql_statement);
1697 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen                                        
1699                                         # We don't want parallel processing
1700                                         last;
1701                                 }
1702                         }
1703                 }
1705                 $watch_for_new_jobs_in_progress = 0;
1706                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1707         }
1711 sub watch_for_new_messages {
1712     my ($kernel,$heap) = @_[KERNEL, HEAP];
1713     my @coll_user_msg;   # collection list of outgoing messages
1714     
1715     # check messaging_db for new incoming messages with executable timestamp
1716     my $timestamp = &get_time();
1717     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1718     my $res = $messaging_db->exec_statement( $sql_statement );
1719         foreach my $hit (@{$res}) {
1721         # create outgoing messages
1722         my $message_to = @{$hit}[3];
1723         # translate message_to to plain login name
1724         my @message_to_l = split(/,/, $message_to);  
1725                 my %receiver_h; 
1726                 foreach my $receiver (@message_to_l) {
1727                         if ($receiver =~ /^u_([\s\S]*)$/) {
1728                                 $receiver_h{$1} = 0;
1729                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1730                                 my $group_name = $1;
1731                                 # fetch all group members from ldap and add them to receiver hash
1732                                 my $ldap_handle = &get_ldap_handle();
1733                                 if (defined $ldap_handle) {
1734                                                 my $mesg = $ldap_handle->search(
1735                                                                                 base => $ldap_base,
1736                                                                                 scope => 'sub',
1737                                                                                 attrs => ['memberUid'],
1738                                                                                 filter => "cn=$group_name",
1739                                                                                 );
1740                                                 if ($mesg->count) {
1741                                                                 my @entries = $mesg->entries;
1742                                                                 foreach my $entry (@entries) {
1743                                                                                 my @receivers= $entry->get_value("memberUid");
1744                                                                                 foreach my $receiver (@receivers) { 
1745                                                                                                 $receiver_h{$1} = 0;
1746                                                                                 }
1747                                                                 }
1748                                                 } 
1749                                                 # translating errors ?
1750                                                 if ($mesg->code) {
1751                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1752                                                 }
1753                                 # ldap handle error ?           
1754                                 } else {
1755                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1756                                 }
1757                         } else {
1758                                 my $sbjct = &encode_base64(@{$hit}[1]);
1759                                 my $msg = &encode_base64(@{$hit}[7]);
1760                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1761                         }
1762                 }
1763                 my @receiver_l = keys(%receiver_h);
1765         my $message_id = @{$hit}[0];
1767         #add each outgoing msg to messaging_db
1768         my $receiver;
1769         foreach $receiver (@receiver_l) {
1770             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1771                 "VALUES ('".
1772                 $message_id."', '".    # id
1773                 @{$hit}[1]."', '".     # subject
1774                 @{$hit}[2]."', '".     # message_from
1775                 $receiver."', '".      # message_to
1776                 "none"."', '".         # flag
1777                 "out"."', '".          # direction
1778                 @{$hit}[6]."', '".     # delivery_time
1779                 @{$hit}[7]."', '".     # message
1780                 $timestamp."'".     # timestamp
1781                 ")";
1782             &daemon_log("M DEBUG: $sql_statement", 1);
1783             my $res = $messaging_db->exec_statement($sql_statement);
1784             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1785         }
1787         # set incoming message to flag d=deliverd
1788         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1789         &daemon_log("M DEBUG: $sql_statement", 7);
1790         $res = $messaging_db->update_dbentry($sql_statement);
1791         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1792     }
1794     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1795     return;
1798 sub watch_for_delivery_messages {
1799     my ($kernel, $heap) = @_[KERNEL, HEAP];
1801     # select outgoing messages
1802     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1803     #&daemon_log("0 DEBUG: $sql", 7);
1804     my $res = $messaging_db->exec_statement( $sql_statement );
1805     
1806     # build out msg for each    usr
1807     foreach my $hit (@{$res}) {
1808         my $receiver = @{$hit}[3];
1809         my $msg_id = @{$hit}[0];
1810         my $subject = @{$hit}[1];
1811         my $message = @{$hit}[7];
1813         # resolve usr -> host where usr is logged in
1814         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1815         #&daemon_log("0 DEBUG: $sql", 7);
1816         my $res = $login_users_db->exec_statement($sql);
1818         # reciver is logged in nowhere
1819         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1821                 my $send_succeed = 0;
1822                 foreach my $hit (@$res) {
1823                                 my $receiver_host = @$hit[0];
1824                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1826                                 # fetch key to encrypt msg propperly for usr/host
1827                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1828                                 &daemon_log("0 DEBUG: $sql", 7);
1829                                 my $res = $known_clients_db->exec_statement($sql);
1831                                 # host is already down
1832                                 if (not ref(@$res[0]) eq "ARRAY") { next; }
1834                                 # host is on
1835                                 my $receiver_key = @{@{$res}[0]}[2];
1836                                 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1837                                 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1838                                 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1839                                 if ($error == 0 ) {
1840                                         $send_succeed++ ;
1841                                 }
1842                 }
1844                 if ($send_succeed) {
1845                                 # set outgoing msg at db to deliverd
1846                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
1847                                 &daemon_log("0 DEBUG: $sql", 7);
1848                                 my $res = $messaging_db->exec_statement($sql); 
1849                 }
1850         }
1852     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
1853     return;
1857 sub watch_for_done_messages {
1858     my ($kernel,$heap) = @_[KERNEL, HEAP];
1860     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
1861     #&daemon_log("0 DEBUG: $sql", 7);
1862     my $res = $messaging_db->exec_statement($sql); 
1864     foreach my $hit (@{$res}) {
1865         my $msg_id = @{$hit}[0];
1867         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
1868         #&daemon_log("0 DEBUG: $sql", 7); 
1869         my $res = $messaging_db->exec_statement($sql);
1871         # not all usr msgs have been seen till now
1872         if ( ref(@$res[0]) eq "ARRAY") { next; }
1873         
1874         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
1875         #&daemon_log("0 DEBUG: $sql", 7);
1876         $res = $messaging_db->exec_statement($sql);
1877     
1878     }
1880     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
1881     return;
1885 sub watch_for_old_known_clients {
1886     my ($kernel,$heap) = @_[KERNEL, HEAP];
1888     my $sql_statement = "SELECT * FROM $known_clients_tn";
1889     my $res = $known_clients_db->select_dbentry( $sql_statement );
1891     my $act_time = int(&get_time());
1893     while ( my ($hit_num, $hit) = each %$res) {
1894         my $expired_timestamp = int($hit->{'timestamp'});
1895         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
1896         my $dt = DateTime->new( year   => $1,
1897                 month  => $2,
1898                 day    => $3,
1899                 hour   => $4,
1900                 minute => $5,
1901                 second => $6,
1902                 );
1904         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
1905         $expired_timestamp = $dt->ymd('').$dt->hms('')."\n";
1906         if ($act_time > $expired_timestamp) {
1907             my $hostname = $hit->{'hostname'};
1908             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
1909             my $del_res = $known_clients_db->exec_statement($del_sql);
1911             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
1912         }
1914     }
1916     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1920 sub watch_for_next_tasks {
1921     my ($kernel,$heap) = @_[KERNEL, HEAP];
1923     my $sql = "SELECT * FROM $incoming_tn";
1924     my $res = $incoming_db->select_dbentry($sql);
1926     while ( my ($hit_num, $hit) = each %$res) {
1927         my $headertag = $hit->{'headertag'};
1928         if ($headertag =~ /^answer_(\d+)/) {
1929             # do not start processing, this message is for a still running POE::Wheel
1930             next;
1931         }
1932         my $message_id = $hit->{'id'};
1933         $kernel->yield('next_task', $hit);
1935         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
1936         my $res = $incoming_db->exec_statement($sql);
1937     }
1939     $kernel->delay_set('watch_for_next_tasks', 0.1); 
1943 sub get_ldap_handle {
1944         my ($session_id) = @_;
1945         my $heap;
1946         my $ldap_handle;
1948         if (not defined $session_id ) { $session_id = 0 };
1949         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1951         if ($session_id == 0) {
1952                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
1953                 $ldap_handle = Net::LDAP->new( $ldap_uri );
1954                 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!"); 
1956         } else {
1957                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1958                 if( defined $session_reference ) {
1959                         $heap = $session_reference->get_heap();
1960                 }
1962                 if (not defined $heap) {
1963                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
1964                         return;
1965                 }
1967                 # TODO: This "if" is nonsense, because it doesn't prove that the
1968                 #       used handle is still valid - or if we've to reconnect...
1969                 #if (not exists $heap->{ldap_handle}) {
1970                         $ldap_handle = Net::LDAP->new( $ldap_uri );
1971                         $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!"); 
1972                         $heap->{ldap_handle} = $ldap_handle;
1973                 #}
1974         }
1975         return $ldap_handle;
1979 sub change_fai_state {
1980     my ($st, $targets, $session_id) = @_;
1981     $session_id = 0 if not defined $session_id;
1982     # Set FAI state to localboot
1983     my %mapActions= (
1984         reboot    => '',
1985         update    => 'softupdate',
1986         localboot => 'localboot',
1987         reinstall => 'install',
1988         rescan    => '',
1989         wake      => '',
1990         memcheck  => 'memcheck',
1991         sysinfo   => 'sysinfo',
1992         install   => 'install',
1993     );
1995     # Return if this is unknown
1996     if (!exists $mapActions{ $st }){
1997         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
1998       return;
1999     }
2001     my $state= $mapActions{ $st };
2003     my $ldap_handle = &get_ldap_handle($session_id);
2004     if( defined($ldap_handle) ) {
2006       # Build search filter for hosts
2007         my $search= "(&(objectClass=GOhard)";
2008         foreach (@{$targets}){
2009             $search.= "(macAddress=$_)";
2010         }
2011         $search.= ")";
2013       # If there's any host inside of the search string, procress them
2014         if (!($search =~ /macAddress/)){
2015             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
2016             return;
2017         }
2019       # Perform search for Unit Tag
2020       my $mesg = $ldap_handle->search(
2021           base   => $ldap_base,
2022           scope  => 'sub',
2023           attrs  => ['dn', 'FAIstate', 'objectClass'],
2024           filter => "$search"
2025           );
2027           if ($mesg->count) {
2028                   my @entries = $mesg->entries;
2029                   if (0 == @entries) {
2030                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
2031                   }
2033                   foreach my $entry (@entries) {
2034                           # Only modify entry if it is not set to '$state'
2035                           if ($entry->get_value("FAIstate") ne "$state"){
2036                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
2037                                   my $result;
2038                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
2039                                   if (exists $tmp{'FAIobject'}){
2040                                           if ($state eq ''){
2041                                                   $result= $ldap_handle->modify($entry->dn, changes => [
2042                                                           delete => [ FAIstate => [] ] ]);
2043                                           } else {
2044                                                   $result= $ldap_handle->modify($entry->dn, changes => [
2045                                                           replace => [ FAIstate => $state ] ]);
2046                                           }
2047                                   } elsif ($state ne ''){
2048                                           $result= $ldap_handle->modify($entry->dn, changes => [
2049                                                   add     => [ objectClass => 'FAIobject' ],
2050                                                   add     => [ FAIstate => $state ] ]);
2051                                   }
2053                                   # Errors?
2054                                   if ($result->code){
2055                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2056                                   }
2057                           } else {
2058                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
2059                           }  
2060                   }
2061           } else {
2062                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
2063           }
2065     # if no ldap handle defined
2066     } else {
2067         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
2068     }
2070         return;
2074 sub change_goto_state {
2075     my ($st, $targets, $session_id) = @_;
2076     $session_id = 0  if not defined $session_id;
2078     # Switch on or off?
2079     my $state= $st eq 'active' ? 'active': 'locked';
2081     my $ldap_handle = &get_ldap_handle($session_id);
2082     if( defined($ldap_handle) ) {
2084       # Build search filter for hosts
2085       my $search= "(&(objectClass=GOhard)";
2086       foreach (@{$targets}){
2087         $search.= "(macAddress=$_)";
2088       }
2089       $search.= ")";
2091       # If there's any host inside of the search string, procress them
2092       if (!($search =~ /macAddress/)){
2093         return;
2094       }
2096       # Perform search for Unit Tag
2097       my $mesg = $ldap_handle->search(
2098           base   => $ldap_base,
2099           scope  => 'sub',
2100           attrs  => ['dn', 'gotoMode'],
2101           filter => "$search"
2102           );
2104       if ($mesg->count) {
2105         my @entries = $mesg->entries;
2106         foreach my $entry (@entries) {
2108           # Only modify entry if it is not set to '$state'
2109           if ($entry->get_value("gotoMode") ne $state){
2111             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2112             my $result;
2113             $result= $ldap_handle->modify($entry->dn, changes => [
2114                                                 replace => [ gotoMode => $state ] ]);
2116             # Errors?
2117             if ($result->code){
2118               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2119             }
2121           }
2122         }
2123       } else {
2124                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2125           }
2127     }
2131 sub run_recreate_packages_db {
2132     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2133     my $session_id = $session->ID;
2134         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2135         $kernel->yield('create_fai_release_db', $fai_release_tn);
2136         $kernel->yield('create_fai_server_db', $fai_server_tn);
2137         return;
2141 sub run_create_fai_server_db {
2142     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2143     my $session_id = $session->ID;
2144     my $task = POE::Wheel::Run->new(
2145             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2146             StdoutEvent  => "session_run_result",
2147             StderrEvent  => "session_run_debug",
2148             CloseEvent   => "session_run_done",
2149             );
2151     $heap->{task}->{ $task->ID } = $task;
2152     return;
2156 sub create_fai_server_db {
2157     my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2158         my $result;
2160         if (not defined $session_id) { $session_id = 0; }
2161     my $ldap_handle = &get_ldap_handle();
2162         if(defined($ldap_handle)) {
2163                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2164                 my $mesg= $ldap_handle->search(
2165                         base   => $ldap_base,
2166                         scope  => 'sub',
2167                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2168                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2169                 );
2170                 if($mesg->{'resultCode'} == 0 &&
2171                    $mesg->count != 0) {
2172                    foreach my $entry (@{$mesg->{entries}}) {
2173                            if($entry->exists('FAIrepository')) {
2174                                    # Add an entry for each Repository configured for server
2175                                    foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2176                                                    my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2177                                                    my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2178                                                    $result= $fai_server_db->add_dbentry( { 
2179                                                                    table => $table_name,
2180                                                                    primkey => ['server', 'release', 'tag'],
2181                                                                    server => $tmp_url,
2182                                                                    release => $tmp_release,
2183                                                                    sections => $tmp_sections,
2184                                                                    tag => (length($tmp_tag)>0)?$tmp_tag:"",
2185                                                            } );
2186                                            }
2187                                    }
2188                            }
2189                    }
2190                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2192                 # TODO: Find a way to post the 'create_packages_list_db' event
2193                 if(not defined($dont_create_packages_list)) {
2194                         &create_packages_list_db(undef, undef, $session_id);
2195                 }
2196         }       
2197     
2198     $ldap_handle->disconnect;
2199         return $result;
2203 sub run_create_fai_release_db {
2204     my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2205         my $session_id = $session->ID;
2206     my $task = POE::Wheel::Run->new(
2207             Program => sub { &create_fai_release_db($table_name, $session_id) },
2208             StdoutEvent  => "session_run_result",
2209             StderrEvent  => "session_run_debug",
2210             CloseEvent   => "session_run_done",
2211             );
2213     $heap->{task}->{ $task->ID } = $task;
2214     return;
2218 sub create_fai_release_db {
2219         my ($table_name, $session_id) = @_;
2220         my $result;
2222     # used for logging
2223     if (not defined $session_id) { $session_id = 0; }
2225     my $ldap_handle = &get_ldap_handle();
2226         if(defined($ldap_handle)) {
2227                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2228                 my $mesg= $ldap_handle->search(
2229                         base   => $ldap_base,
2230                         scope  => 'sub',
2231                         attrs  => [],
2232                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2233                 );
2234                 if($mesg->{'resultCode'} == 0 &&
2235                         $mesg->count != 0) {
2236                         # Walk through all possible FAI container ou's
2237                         my @sql_list;
2238                         my $timestamp= &get_time();
2239                         foreach my $ou (@{$mesg->{entries}}) {
2240                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2241                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2242                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2243                                         if(@tmp_array) {
2244                                                 foreach my $entry (@tmp_array) {
2245                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2246                                                                 my $sql= 
2247                                                                 "INSERT INTO $table_name "
2248                                                                 ."(timestamp, release, class, type, state) VALUES ("
2249                                                                 .$timestamp.","
2250                                                                 ."'".$entry->{'release'}."',"
2251                                                                 ."'".$entry->{'class'}."',"
2252                                                                 ."'".$entry->{'type'}."',"
2253                                                                 ."'".$entry->{'state'}."')";
2254                                                                 push @sql_list, $sql;
2255                                                         }
2256                                                 }
2257                                         }
2258                                 }
2259                         }
2261                         daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2262                         if(@sql_list) {
2263                                 unshift @sql_list, "VACUUM";
2264                                 unshift @sql_list, "DELETE FROM $table_name";
2265                                 $fai_release_db->exec_statementlist(\@sql_list);
2266                         }
2267                         daemon_log("$session_id DEBUG: Done with inserting",7);
2268                 }
2269                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2270         }
2271     $ldap_handle->disconnect;
2272         return $result;
2275 sub get_fai_types {
2276         my $tmp_classes = shift || return undef;
2277         my @result;
2279         foreach my $type(keys %{$tmp_classes}) {
2280                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2281                         my $entry = {
2282                                 type => $type,
2283                                 state => $tmp_classes->{$type}[0],
2284                         };
2285                         push @result, $entry;
2286                 }
2287         }
2289         return @result;
2292 sub get_fai_state {
2293         my $result = "";
2294         my $tmp_classes = shift || return $result;
2296         foreach my $type(keys %{$tmp_classes}) {
2297                 if(defined($tmp_classes->{$type}[0])) {
2298                         $result = $tmp_classes->{$type}[0];
2299                         
2300                 # State is equal for all types in class
2301                         last;
2302                 }
2303         }
2305         return $result;
2308 sub resolve_fai_classes {
2309         my ($fai_base, $ldap_handle, $session_id) = @_;
2310         if (not defined $session_id) { $session_id = 0; }
2311         my $result;
2312         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2313         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2314         my $fai_classes;
2316         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2317         my $mesg= $ldap_handle->search(
2318                 base   => $fai_base,
2319                 scope  => 'sub',
2320                 attrs  => ['cn','objectClass','FAIstate'],
2321                 filter => $fai_filter,
2322         );
2323         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2325         if($mesg->{'resultCode'} == 0 &&
2326                 $mesg->count != 0) {
2327                 foreach my $entry (@{$mesg->{entries}}) {
2328                         if($entry->exists('cn')) {
2329                                 my $tmp_dn= $entry->dn();
2331                                 # Skip classname and ou dn parts for class
2332                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2334                                 # Skip classes without releases
2335                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2336                                         next;
2337                                 }
2339                                 my $tmp_cn= $entry->get_value('cn');
2340                                 my $tmp_state= $entry->get_value('FAIstate');
2342                                 my $tmp_type;
2343                                 # Get FAI type
2344                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2345                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2346                                                 $tmp_type= $oclass;
2347                                                 last;
2348                                         }
2349                                 }
2351                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2352                                         # A Subrelease
2353                                         my @sub_releases = split(/,/, $tmp_release);
2355                                         # Walk through subreleases and build hash tree
2356                                         my $hash;
2357                                         while(my $tmp_sub_release = pop @sub_releases) {
2358                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2359                                         }
2360                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2361                                 } else {
2362                                         # A branch, no subrelease
2363                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2364                                 }
2365                         } elsif (!$entry->exists('cn')) {
2366                                 my $tmp_dn= $entry->dn();
2367                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2369                                 # Skip classes without releases
2370                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2371                                         next;
2372                                 }
2374                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2375                                         # A Subrelease
2376                                         my @sub_releases= split(/,/, $tmp_release);
2378                                         # Walk through subreleases and build hash tree
2379                                         my $hash;
2380                                         while(my $tmp_sub_release = pop @sub_releases) {
2381                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2382                                         }
2383                                         # Remove the last two characters
2384                                         chop($hash);
2385                                         chop($hash);
2387                                         eval('$fai_classes->'.$hash.'= {}');
2388                                 } else {
2389                                         # A branch, no subrelease
2390                                         if(!exists($fai_classes->{$tmp_release})) {
2391                                                 $fai_classes->{$tmp_release} = {};
2392                                         }
2393                                 }
2394                         }
2395                 }
2397                 # The hash is complete, now we can honor the copy-on-write based missing entries
2398                 foreach my $release (keys %$fai_classes) {
2399                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2400                 }
2401         }
2402         return $result;
2405 sub apply_fai_inheritance {
2406        my $fai_classes = shift || return {};
2407        my $tmp_classes;
2409        # Get the classes from the branch
2410        foreach my $class (keys %{$fai_classes}) {
2411                # Skip subreleases
2412                if($class =~ /^ou=.*$/) {
2413                        next;
2414                } else {
2415                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2416                }
2417        }
2419        # Apply to each subrelease
2420        foreach my $subrelease (keys %{$fai_classes}) {
2421                if($subrelease =~ /ou=/) {
2422                        foreach my $tmp_class (keys %{$tmp_classes}) {
2423                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2424                                        $fai_classes->{$subrelease}->{$tmp_class} =
2425                                        deep_copy($tmp_classes->{$tmp_class});
2426                                } else {
2427                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2428                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2429                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2430                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2431                                                }
2432                                        }
2433                                }
2434                        }
2435                }
2436        }
2438        # Find subreleases in deeper levels
2439        foreach my $subrelease (keys %{$fai_classes}) {
2440                if($subrelease =~ /ou=/) {
2441                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2442                                if($subsubrelease =~ /ou=/) {
2443                                        apply_fai_inheritance($fai_classes->{$subrelease});
2444                                }
2445                        }
2446                }
2447        }
2449        return $fai_classes;
2452 sub get_fai_release_entries {
2453         my $tmp_classes = shift || return;
2454         my $parent = shift || "";
2455         my @result = shift || ();
2457         foreach my $entry (keys %{$tmp_classes}) {
2458                 if(defined($entry)) {
2459                         if($entry =~ /^ou=.*$/) {
2460                                 my $release_name = $entry;
2461                                 $release_name =~ s/ou=//g;
2462                                 if(length($parent)>0) {
2463                                         $release_name = $parent."/".$release_name;
2464                                 }
2465                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2466                                 foreach my $bufentry(@bufentries) {
2467                                         push @result, $bufentry;
2468                                 }
2469                         } else {
2470                                 my @types = get_fai_types($tmp_classes->{$entry});
2471                                 foreach my $type (@types) {
2472                                         push @result, 
2473                                         {
2474                                                 'class' => $entry,
2475                                                 'type' => $type->{'type'},
2476                                                 'release' => $parent,
2477                                                 'state' => $type->{'state'},
2478                                         };
2479                                 }
2480                         }
2481                 }
2482         }
2484         return @result;
2487 sub deep_copy {
2488         my $this = shift;
2489         if (not ref $this) {
2490                 $this;
2491         } elsif (ref $this eq "ARRAY") {
2492                 [map deep_copy($_), @$this];
2493         } elsif (ref $this eq "HASH") {
2494                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2495         } else { die "what type is $_?" }
2499 sub session_run_result {
2500     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2501     $kernel->sig(CHLD => "child_reap");
2504 sub session_run_debug {
2505     my $result = $_[ARG0];
2506     print STDERR "$result\n";
2509 sub session_run_done {
2510     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2511     delete $heap->{task}->{$task_id};
2515 sub create_sources_list {
2516         my $session_id = shift;
2517         my $ldap_handle = &main::get_ldap_handle;
2518         my $result="/tmp/gosa_si_tmp_sources_list";
2520         # Remove old file
2521         if(stat($result)) {
2522                 unlink($result);
2523                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2524         }
2526         my $fh;
2527         open($fh, ">$result");
2528         if (not defined $fh) {
2529                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2530                 return undef;
2531         }
2532         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2533                 my $mesg=$ldap_handle->search(
2534                         base    => $main::ldap_server_dn,
2535                         scope   => 'base',
2536                         attrs   => 'FAIrepository',
2537                         filter  => 'objectClass=FAIrepositoryServer'
2538                 );
2539                 if($mesg->count) {
2540                         foreach my $entry(@{$mesg->{'entries'}}) {
2541                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2542                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2543                                         my $line = "deb $server $release";
2544                                         $sections =~ s/,/ /g;
2545                                         $line.= " $sections";
2546                                         print $fh $line."\n";
2547                                 }
2548                         }
2549                 }
2550         } else {
2551                 if (defined $main::ldap_server_dn){
2552                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2553                 } else {
2554                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2555                 }
2556         }
2557         close($fh);
2559         return $result;
2563 sub run_create_packages_list_db {
2564     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2565         my $session_id = $session->ID;
2567         my $task = POE::Wheel::Run->new(
2568                                         Priority => +20,
2569                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2570                                         StdoutEvent  => "session_run_result",
2571                                         StderrEvent  => "session_run_debug",
2572                                         CloseEvent   => "session_run_done",
2573                                         );
2574         $heap->{task}->{ $task->ID } = $task;
2578 sub create_packages_list_db {
2579         my ($ldap_handle, $sources_file, $session_id) = @_;
2580         
2581         # it should not be possible to trigger a recreation of packages_list_db
2582         # while packages_list_db is under construction, so set flag packages_list_under_construction
2583         # which is tested befor recreation can be started
2584         if (-r $packages_list_under_construction) {
2585                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2586                 return;
2587         } else {
2588                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2589                 # set packages_list_under_construction to true
2590                 system("touch $packages_list_under_construction");
2591                 @packages_list_statements=();
2592         }
2594         if (not defined $session_id) { $session_id = 0; }
2595         if (not defined $ldap_handle) { 
2596                 $ldap_handle= &get_ldap_handle();
2598                 if (not defined $ldap_handle) {
2599                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2600                         unlink($packages_list_under_construction);
2601                         return;
2602                 }
2603         }
2604         if (not defined $sources_file) { 
2605                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2606                 $sources_file = &create_sources_list($session_id);
2607         }
2609         if (not defined $sources_file) {
2610                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2611                 unlink($packages_list_under_construction);
2612                 return;
2613         }
2615         my $line;
2617         open(CONFIG, "<$sources_file") or do {
2618                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2619                 unlink($packages_list_under_construction);
2620                 return;
2621         };
2623         # Read lines
2624         while ($line = <CONFIG>){
2625                 # Unify
2626                 chop($line);
2627                 $line =~ s/^\s+//;
2628                 $line =~ s/^\s+/ /;
2630                 # Strip comments
2631                 $line =~ s/#.*$//g;
2633                 # Skip empty lines
2634                 if ($line =~ /^\s*$/){
2635                         next;
2636                 }
2638                 # Interpret deb line
2639                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2640                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2641                         my $section;
2642                         foreach $section (split(' ', $sections)){
2643                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2644                         }
2645                 }
2646         }
2648         close (CONFIG);
2651         find(\&cleanup_and_extract, keys( %repo_dirs ));
2652         &main::strip_packages_list_statements();
2653         unshift @packages_list_statements, "VACUUM";
2654         $packages_list_db->exec_statementlist(\@packages_list_statements);
2655         unlink($packages_list_under_construction);
2656         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2657         return;
2660 # This function should do some intensive task to minimize the db-traffic
2661 sub strip_packages_list_statements {
2662     my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2663         my @new_statement_list=();
2664         my $hash;
2665         my $insert_hash;
2666         my $update_hash;
2667         my $delete_hash;
2668         my $local_timestamp=get_time();
2670         foreach my $existing_entry (@existing_entries) {
2671                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2672         }
2674         foreach my $statement (@packages_list_statements) {
2675                 if($statement =~ /^INSERT/i) {
2676                         # Assign the values from the insert statement
2677                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2678                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2679                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2680                                 # If section or description has changed, update the DB
2681                                 if( 
2682                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2683                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2684                                 ) {
2685                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2686                                 }
2687                         } else {
2688                                 # Insert a non-existing entry to db
2689                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2690                         }
2691                 } elsif ($statement =~ /^UPDATE/i) {
2692                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2693                         /^update\s+?$main::packages_list_tn\s+?set\s+?template\s*?=\s*?'(.*?)'\s+?where\s+?package\s*?=\s*?'(.*?)'\s+?and\s+?version\s*?=\s*?'(.*?)'\s*?;$/si;
2694                         foreach my $distribution (keys %{$hash}) {
2695                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2696                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2697                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2698                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2699                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2700                                                 my $section;
2701                                                 my $description;
2702                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2703                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2704                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2705                                                 }
2706                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2707                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2708                                                 }
2709                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2710                                         }
2711                                 }
2712                         }
2713                 }
2714         }
2716         # TODO: Check for orphaned entries
2718         # unroll the insert_hash
2719         foreach my $distribution (keys %{$insert_hash}) {
2720                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2721                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2722                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2723                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2724                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2725                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2726                                 ."'$local_timestamp')";
2727                         }
2728                 }
2729         }
2731         # unroll the update hash
2732         foreach my $distribution (keys %{$update_hash}) {
2733                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2734                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2735                                 my $set = "";
2736                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2737                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2738                                 }
2739                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2740                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2741                                 }
2742                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2743                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2744                                 }
2745                                 if(defined($set) and length($set) > 0) {
2746                                         $set .= "timestamp = '$local_timestamp'";
2747                                 } else {
2748                                         next;
2749                                 }
2750                                 push @new_statement_list, 
2751                                         "UPDATE $main::packages_list_tn SET $set WHERE"
2752                                         ." distribution = '$distribution'"
2753                                         ." AND package = '$package'"
2754                                         ." AND version = '$version'";
2755                         }
2756                 }
2757         }
2759         @packages_list_statements = @new_statement_list;
2763 sub parse_package_info {
2764     my ($baseurl, $dist, $section, $session_id)= @_;
2765     my ($package);
2766     if (not defined $session_id) { $session_id = 0; }
2767     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2768     $repo_dirs{ "${repo_path}/pool" } = 1;
2770     foreach $package ("Packages.gz"){
2771         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2772         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2773         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2774     }
2775     
2779 sub get_package {
2780     my ($url, $dest, $session_id)= @_;
2781     if (not defined $session_id) { $session_id = 0; }
2783     my $tpath = dirname($dest);
2784     -d "$tpath" || mkpath "$tpath";
2786     # This is ugly, but I've no time to take a look at "how it works in perl"
2787     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2788         system("gunzip -cd '$dest' > '$dest.in'");
2789         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2790         unlink($dest);
2791         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
2792     } else {
2793         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2794     }
2795     return 0;
2799 sub parse_package {
2800     my ($path, $dist, $srv_path, $session_id)= @_;
2801     if (not defined $session_id) { $session_id = 0;}
2802     my ($package, $version, $section, $description);
2803     my $PACKAGES;
2804     my $timestamp = &get_time();
2806     if(not stat("$path.in")) {
2807         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2808         return;
2809     }
2811     open($PACKAGES, "<$path.in");
2812     if(not defined($PACKAGES)) {
2813         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
2814         return;
2815     }
2817     # Read lines
2818     while (<$PACKAGES>){
2819         my $line = $_;
2820         # Unify
2821         chop($line);
2823         # Use empty lines as a trigger
2824         if ($line =~ /^\s*$/){
2825             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2826             push(@packages_list_statements, $sql);
2827             $package = "none";
2828             $version = "none";
2829             $section = "none";
2830             $description = "none"; 
2831             next;
2832         }
2834         # Trigger for package name
2835         if ($line =~ /^Package:\s/){
2836             ($package)= ($line =~ /^Package: (.*)$/);
2837             next;
2838         }
2840         # Trigger for version
2841         if ($line =~ /^Version:\s/){
2842             ($version)= ($line =~ /^Version: (.*)$/);
2843             next;
2844         }
2846         # Trigger for description
2847         if ($line =~ /^Description:\s/){
2848             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2849             next;
2850         }
2852         # Trigger for section
2853         if ($line =~ /^Section:\s/){
2854             ($section)= ($line =~ /^Section: (.*)$/);
2855             next;
2856         }
2858         # Trigger for filename
2859         if ($line =~ /^Filename:\s/){
2860             my ($filename) = ($line =~ /^Filename: (.*)$/);
2861             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2862             next;
2863         }
2864     }
2866     close( $PACKAGES );
2867     unlink( "$path.in" );
2871 sub store_fileinfo {
2872     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2874     my %fileinfo = (
2875         'package' => $package,
2876         'dist' => $dist,
2877         'version' => $vers,
2878     );
2880     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2884 sub cleanup_and_extract {
2885     my $fileinfo = $repo_files{ $File::Find::name };
2887     if( defined $fileinfo ) {
2889         my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2890         my $sql;
2891         my $package = $fileinfo->{ 'package' };
2892         my $newver = $fileinfo->{ 'version' };
2894         mkpath($dir);
2895         system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2897                 if( -f "$dir/DEBIAN/templates" ) {
2899                         daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 7);
2901                         my $tmpl= "";
2902                         {
2903                                 local $/=undef;
2904                                 open FILE, "$dir/DEBIAN/templates";
2905                                 $tmpl = &encode_base64(<FILE>);
2906                                 close FILE;
2907                         }
2908                         rmtree("$dir/DEBIAN/templates");
2910                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2911                 push @packages_list_statements, $sql;
2912                 }
2913     }
2915     return;
2919 sub register_at_foreign_servers {   
2920     my ($kernel) = $_[KERNEL];
2922     # hole alle bekannten server aus known_server_db
2923     my $server_sql = "SELECT * FROM $known_server_tn";
2924     my $server_res = $known_server_db->exec_statement($server_sql);
2926     # no entries in known_server_db
2927     if (not ref(@$server_res[0]) eq "ARRAY") { 
2928         # TODO
2929     }
2931     # detect already connected clients
2932     my $client_sql = "SELECT * FROM $known_clients_tn"; 
2933     my $client_res = $known_clients_db->exec_statement($client_sql);
2935     # send my server details to all other gosa-si-server within the network
2936     foreach my $hit (@$server_res) {
2937         my $hostname = @$hit[0];
2938         my $hostkey = &create_passwd;
2940         # add already connected clients to registration message 
2941         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
2942         &add_content2xml_hash($myhash, 'key', $hostkey);
2943         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
2944         
2945         # build registration message and send it
2946         my $foreign_server_msg = &create_xml_string($myhash);
2947         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
2948     }
2949     
2950     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
2951     return;
2955 #==== MAIN = main ==============================================================
2956 #  parse commandline options
2957 Getopt::Long::Configure( "bundling" );
2958 GetOptions("h|help" => \&usage,
2959         "c|config=s" => \$cfg_file,
2960         "f|foreground" => \$foreground,
2961         "v|verbose+" => \$verbose,
2962         "no-arp+" => \$no_arp,
2963            );
2965 #  read and set config parameters
2966 &check_cmdline_param ;
2967 &read_configfile($cfg_file, %cfg_defaults);
2968 &check_pid;
2970 $SIG{CHLD} = 'IGNORE';
2972 # forward error messages to logfile
2973 if( ! $foreground ) {
2974   open( STDIN,  '+>/dev/null' );
2975   open( STDOUT, '+>&STDIN'    );
2976   open( STDERR, '+>&STDIN'    );
2979 # Just fork, if we are not in foreground mode
2980 if( ! $foreground ) { 
2981     chdir '/'                 or die "Can't chdir to /: $!";
2982     $pid = fork;
2983     setsid                    or die "Can't start a new session: $!";
2984     umask 0;
2985 } else { 
2986     $pid = $$; 
2989 # Do something useful - put our PID into the pid_file
2990 if( 0 != $pid ) {
2991     open( LOCK_FILE, ">$pid_file" );
2992     print LOCK_FILE "$pid\n";
2993     close( LOCK_FILE );
2994     if( !$foreground ) { 
2995         exit( 0 ) 
2996     };
2999 # parse head url and revision from svn
3000 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
3001 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
3002 $server_headURL = defined $1 ? $1 : 'unknown' ;
3003 $server_revision = defined $2 ? $2 : 'unknown' ;
3004 if ($server_headURL =~ /\/tag\// || 
3005         $server_headURL =~ /\/branches\// ) {
3006     $server_status = "stable"; 
3007 } else {
3008     $server_status = "developmental" ;
3012 daemon_log(" ", 1);
3013 daemon_log("$0 started!", 1);
3014 daemon_log("status: $server_status", 1);
3015 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
3017 # connect to incoming_db
3018 unlink($incoming_file_name);
3019 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
3020 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
3022 # connect to gosa-si job queue
3023 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
3024 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
3026 # connect to known_clients_db
3027 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
3028 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
3030 # connect to foreign_clients_db
3031 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
3032 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
3034 # connect to known_server_db
3035 unlink($known_server_file_name);
3036 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
3037 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
3039 # connect to login_usr_db
3040 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
3041 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
3043 # connect to fai_server_db and fai_release_db
3044 unlink($fai_server_file_name);
3045 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
3046 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
3048 unlink($fai_release_file_name);
3049 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
3050 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
3052 # connect to packages_list_db
3053 #unlink($packages_list_file_name);
3054 unlink($packages_list_under_construction);
3055 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
3056 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
3058 # connect to messaging_db
3059 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
3060 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
3063 # create xml object used for en/decrypting
3064 $xml = new XML::Simple();
3067 # foreign servers 
3068 my @foreign_server_list;
3070 # add foreign server from cfg file
3071 if ($foreign_server_string ne "") {
3072     my @cfg_foreign_server_list = split(",", $foreign_server_string);
3073     foreach my $foreign_server (@cfg_foreign_server_list) {
3074         push(@foreign_server_list, $foreign_server);
3075     }
3078 # add foreign server from dns
3079 my @tmp_servers;
3080 if ( !$server_domain) {
3081     # Try our DNS Searchlist
3082     for my $domain(get_dns_domains()) {
3083         chomp($domain);
3084         my @tmp_domains= &get_server_addresses($domain);
3085         if(@tmp_domains) {
3086             for my $tmp_server(@tmp_domains) {
3087                 push @tmp_servers, $tmp_server;
3088             }
3089         }
3090     }
3091     if(@tmp_servers && length(@tmp_servers)==0) {
3092         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3093     }
3094 } else {
3095     @tmp_servers = &get_server_addresses($server_domain);
3096     if( 0 == @tmp_servers ) {
3097         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3098     }
3100 foreach my $server (@tmp_servers) { 
3101     unshift(@foreign_server_list, $server); 
3103 # eliminate duplicate entries
3104 @foreign_server_list = &del_doubles(@foreign_server_list);
3105 my $all_foreign_server = join(", ", @foreign_server_list);
3106 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
3108 # add all found foreign servers to known_server
3109 my $act_timestamp = &get_time();
3110 foreach my $foreign_server (@foreign_server_list) {
3112         # do not add myself to known_server_db
3113         if (&is_local($foreign_server)) { next; }
3114         ######################################
3116     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3117             primkey=>['hostname'],
3118             hostname=>$foreign_server,
3119             status=>'not_jet_registered',
3120             hostkey=>"none",
3121             timestamp=>$act_timestamp,
3122             } );
3126 # Import all modules
3127 &import_modules;
3129 # Check wether all modules are gosa-si valid passwd check
3130 &password_check;
3132 # Prepare for using Opsi 
3133 if ($opsi_enabled eq "true") {
3134     use JSON::RPC::Client;
3135     use XML::Quote qw(:all);
3136     $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
3137     $opsi_client = new JSON::RPC::Client;
3141 POE::Component::Server::TCP->new(
3142     Alias => "TCP_SERVER",
3143         Port => $server_port,
3144         ClientInput => sub {
3145         my ($kernel, $input) = @_[KERNEL, ARG0];
3146         push(@tasks, $input);
3147         push(@msgs_to_decrypt, $input);
3148         $kernel->yield("msg_to_decrypt");
3149         },
3150     InlineStates => {
3151         msg_to_decrypt => \&msg_to_decrypt,
3152         next_task => \&next_task,
3153         task_result => \&handle_task_result,
3154         task_done   => \&handle_task_done,
3155         task_debug  => \&handle_task_debug,
3156         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3157     }
3158 );
3160 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
3162 # create session for repeatedly checking the job queue for jobs
3163 POE::Session->create(
3164         inline_states => {
3165                 _start => \&session_start,
3166         register_at_foreign_servers => \&register_at_foreign_servers,
3167         sig_handler => \&sig_handler,
3168         next_task => \&next_task,
3169         task_result => \&handle_task_result,
3170         task_done   => \&handle_task_done,
3171         task_debug  => \&handle_task_debug,
3172         watch_for_next_tasks => \&watch_for_next_tasks,
3173         watch_for_new_messages => \&watch_for_new_messages,
3174         watch_for_delivery_messages => \&watch_for_delivery_messages,
3175         watch_for_done_messages => \&watch_for_done_messages,
3176                 watch_for_new_jobs => \&watch_for_new_jobs,
3177         watch_for_modified_jobs => \&watch_for_modified_jobs,
3178         watch_for_done_jobs => \&watch_for_done_jobs,
3179         watch_for_opsi_jobs => \&watch_for_opsi_jobs,
3180         watch_for_old_known_clients => \&watch_for_old_known_clients,
3181         create_packages_list_db => \&run_create_packages_list_db,
3182         create_fai_server_db => \&run_create_fai_server_db,
3183         create_fai_release_db => \&run_create_fai_release_db,
3184                 recreate_packages_db => \&run_recreate_packages_db,
3185         session_run_result => \&session_run_result,
3186         session_run_debug => \&session_run_debug,
3187         session_run_done => \&session_run_done,
3188         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3189         }
3190 );
3193 POE::Kernel->run();
3194 exit;