Code

Updated ACL check for My Account plugins
[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_ip, $gosa_port, $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 );
92 # additional variable which should be globaly accessable
93 our $server_address;
94 our $server_mac_address;
95 our $gosa_address;
96 our $no_arp;
97 our $verbose;
98 our $forground;
99 our $cfg_file;
100 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
102 # dak variables
103 our $dak_base_directory;
104 our $dak_signing_keys_directory;
105 our $dak_queue_directory;
106 our $dak_user;
108 # specifies the verbosity of the daemon_log
109 $verbose = 0 ;
111 # if foreground is not null, script will be not forked to background
112 $foreground = 0 ;
114 # specifies the timeout seconds while checking the online status of a registrating client
115 $ping_timeout = 5;
117 $no_arp = 0;
118 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
119 my @packages_list_statements;
120 my $watch_for_new_jobs_in_progress = 0;
122 # holds all incoming decrypted messages
123 our $incoming_db;
124 our $incoming_tn = 'incoming';
125 my $incoming_file_name;
126 my @incoming_col_names = ("id INTEGER PRIMARY KEY", 
127         "timestamp DEFAULT 'none'", 
128         "headertag DEFAULT 'none'",
129                 "targettag DEFAULT 'none'",
130         "xmlmessage DEFAULT 'none'",
131         "module DEFAULT 'none'",
132         "sessionid DEFAULT '0'",
133         );
135 # holds all gosa jobs
136 our $job_db;
137 our $job_queue_tn = 'jobs';
138 my $job_queue_file_name;
139 my @job_queue_col_names = ("id INTEGER PRIMARY KEY", 
140                 "timestamp DEFAULT 'none'", 
141                 "status DEFAULT 'none'", 
142                 "result DEFAULT 'none'", 
143                 "progress DEFAULT 'none'", 
144         "headertag DEFAULT 'none'", 
145                 "targettag DEFAULT 'none'", 
146                 "xmlmessage DEFAULT 'none'", 
147                 "macaddress DEFAULT 'none'",
148                 "plainname DEFAULT 'none'",
149         "siserver DEFAULT 'none'",
150         "modified DEFAULT '0'",
151                 );
153 # holds all other gosa-si-server
154 our $known_server_db;
155 our $known_server_tn = "known_server";
156 my $known_server_file_name;
157 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
159 # holds all registrated clients
160 our $known_clients_db;
161 our $known_clients_tn = "known_clients";
162 my $known_clients_file_name;
163 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events", "keylifetime");
165 # holds all registered clients at a foreign server
166 our $foreign_clients_db;
167 our $foreign_clients_tn = "foreign_clients"; 
168 my $foreign_clients_file_name;
169 my @foreign_clients_col_names = ("hostname", "macaddress", "regserver", "timestamp");
171 # holds all logged in user at each client 
172 our $login_users_db;
173 our $login_users_tn = "login_users";
174 my $login_users_file_name;
175 my @login_users_col_names = ("client", "user", "timestamp");
177 # holds all fai server, the debian release and tag
178 our $fai_server_db;
179 our $fai_server_tn = "fai_server"; 
180 my $fai_server_file_name;
181 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag"); 
183 our $fai_release_db;
184 our $fai_release_tn = "fai_release"; 
185 my $fai_release_file_name;
186 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state"); 
188 # holds all packages available from different repositories
189 our $packages_list_db;
190 our $packages_list_tn = "packages_list";
191 my $packages_list_file_name;
192 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
193 my $outdir = "/tmp/packages_list_db";
194 my $arch = "i386"; 
196 # holds all messages which should be delivered to a user
197 our $messaging_db;
198 our $messaging_tn = "messaging"; 
199 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to", 
200         "flag", "direction", "delivery_time", "message", "timestamp" );
201 my $messaging_file_name;
203 # path to directory to store client install log files
204 our $client_fai_log_dir = "/var/log/fai"; 
206 # queue which stores taskes until one of the $max_children children are ready to process the task
207 my @tasks = qw();
208 my @msgs_to_decrypt = qw();
209 my $max_children = 2;
212 %cfg_defaults = (
213 "general" => {
214     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
215     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
216     },
217 "server" => {
218     "port" => [\$server_port, "20081"],
219     "known-clients"        => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
220     "known-servers"        => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
221     "incoming"             => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
222     "login-users"          => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
223     "fai-server"           => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
224     "fai-release"          => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
225     "packages-list"        => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
226     "messaging"            => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
227     "foreign-clients"      => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
228     "source-list"          => [\$sources_list, '/etc/apt/sources.list'],
229     "repo-path"            => [\$repo_path, '/srv/www/repository'],
230     "ldap-uri"             => [\$ldap_uri, ""],
231     "ldap-base"            => [\$ldap_base, ""],
232     "ldap-admin-dn"        => [\$ldap_admin_dn, ""],
233     "ldap-admin-password"  => [\$ldap_admin_password, ""],
234     "gosa-unit-tag"        => [\$gosa_unit_tag, ""],
235     "max-clients"          => [\$max_clients, 10],
236     "wol-password"           => [\$wake_on_lan_passwd, ""],
237     },
238 "GOsaPackages" => {
239     "ip" => [\$gosa_ip, "0.0.0.0"],
240     "port" => [\$gosa_port, "20082"],
241     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
242     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
243     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
244     "key" => [\$GosaPackages_key, "none"],
245         "dak-base" => [\$dak_base_directory, "/srv/archive"],
246         "dak-keyring" => [\$dak_signing_keys_directory, "/srv/archive/keyrings"],
247         "dak-queue" => [\$dak_queue_directory, "/srv/archive/queue"],
248         "dak-user" => [\$dak_user, "deb-dak"],
249     },
250 "ClientPackages" => {
251     "key" => [\$ClientPackages_key, "none"],
252     },
253 "ServerPackages"=> {
254     "address"      => [\$foreign_server_string, ""],
255     "domain"  => [\$server_domain, ""],
256     "key"     => [\$ServerPackages_key, "none"],
257     "key-lifetime" => [\$foreign_servers_register_delay, 120],
258     "job-synchronization-enabled" => [\$job_synchronization, "true"],
259     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
261 );
264 #===  FUNCTION  ================================================================
265 #         NAME:  usage
266 #   PARAMETERS:  nothing
267 #      RETURNS:  nothing
268 #  DESCRIPTION:  print out usage text to STDERR
269 #===============================================================================
270 sub usage {
271     print STDERR << "EOF" ;
272 usage: $prg [-hvf] [-c config]
274            -h        : this (help) message
275            -c <file> : config file
276            -f        : foreground, process will not be forked to background
277            -v        : be verbose (multiple to increase verbosity)
278            -no-arp   : starts $prg without connection to arp module
279  
280 EOF
281     print "\n" ;
285 #===  FUNCTION  ================================================================
286 #         NAME:  read_configfile
287 #   PARAMETERS:  cfg_file - string -
288 #      RETURNS:  nothing
289 #  DESCRIPTION:  read cfg_file and set variables
290 #===============================================================================
291 sub read_configfile {
292     my $cfg;
293     if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
294         if( -r $cfg_file ) {
295             $cfg = Config::IniFiles->new( -file => $cfg_file );
296         } else {
297             print STDERR "Couldn't read config file!\n";
298         }
299     } else {
300         $cfg = Config::IniFiles->new() ;
301     }
302     foreach my $section (keys %cfg_defaults) {
303         foreach my $param (keys %{$cfg_defaults{ $section }}) {
304             my $pinfo = $cfg_defaults{ $section }{ $param };
305             ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
306         }
307     }
311 #===  FUNCTION  ================================================================
312 #         NAME:  logging
313 #   PARAMETERS:  level - string - default 'info'
314 #                msg - string -
315 #                facility - string - default 'LOG_DAEMON'
316 #      RETURNS:  nothing
317 #  DESCRIPTION:  function for logging
318 #===============================================================================
319 sub daemon_log {
320     # log into log_file
321     my( $msg, $level ) = @_;
322     if(not defined $msg) { return }
323     if(not defined $level) { $level = 1 }
324     if(defined $log_file){
325         open(LOG_HANDLE, ">>$log_file");
326         chmod 0600, $log_file;
327         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
328             print STDERR "cannot open $log_file: $!";
329             return 
330         }
331         chomp($msg);
332         $msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
333         if($level <= $verbose){
334             my ($seconds, $minutes, $hours, $monthday, $month,
335                     $year, $weekday, $yearday, $sommertime) = localtime(time);
336             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
337             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
338             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
339             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
340             $month = $monthnames[$month];
341             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
342             $year+=1900;
343             my $name = $prg;
345             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
346             print LOG_HANDLE $log_msg;
347             if( $foreground ) { 
348                 print STDERR $log_msg;
349             }
350         }
351         close( LOG_HANDLE );
352     }
356 #===  FUNCTION  ================================================================
357 #         NAME:  check_cmdline_param
358 #   PARAMETERS:  nothing
359 #      RETURNS:  nothing
360 #  DESCRIPTION:  validates commandline parameter
361 #===============================================================================
362 sub check_cmdline_param () {
363     my $err_config;
364     my $err_counter = 0;
365         if(not defined($cfg_file)) {
366                 $cfg_file = "/etc/gosa-si/server.conf";
367                 if(! -r $cfg_file) {
368                         $err_config = "please specify a config file";
369                         $err_counter += 1;
370                 }
371     }
372     if( $err_counter > 0 ) {
373         &usage( "", 1 );
374         if( defined( $err_config)) { print STDERR "$err_config\n"}
375         print STDERR "\n";
376         exit( -1 );
377     }
381 #===  FUNCTION  ================================================================
382 #         NAME:  check_pid
383 #   PARAMETERS:  nothing
384 #      RETURNS:  nothing
385 #  DESCRIPTION:  handels pid processing
386 #===============================================================================
387 sub check_pid {
388     $pid = -1;
389     # Check, if we are already running
390     if( open(LOCK_FILE, "<$pid_file") ) {
391         $pid = <LOCK_FILE>;
392         if( defined $pid ) {
393             chomp( $pid );
394             if( -f "/proc/$pid/stat" ) {
395                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
396                 if( $stat ) {
397                                         daemon_log("ERROR: Already running",1);
398                     close( LOCK_FILE );
399                     exit -1;
400                 }
401             }
402         }
403         close( LOCK_FILE );
404         unlink( $pid_file );
405     }
407     # create a syslog msg if it is not to possible to open PID file
408     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
409         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
410         if (open(LOCK_FILE, '<', $pid_file)
411                 && ($pid = <LOCK_FILE>))
412         {
413             chomp($pid);
414             $msg .= "(PID $pid)\n";
415         } else {
416             $msg .= "(unable to read PID)\n";
417         }
418         if( ! ($foreground) ) {
419             openlog( $0, "cons,pid", "daemon" );
420             syslog( "warning", $msg );
421             closelog();
422         }
423         else {
424             print( STDERR " $msg " );
425         }
426         exit( -1 );
427     }
430 #===  FUNCTION  ================================================================
431 #         NAME:  import_modules
432 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
433 #                are stored
434 #      RETURNS:  nothing
435 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
436 #                state is on is imported by "require 'file';"
437 #===============================================================================
438 sub import_modules {
439     daemon_log(" ", 1);
441     if (not -e $modules_path) {
442         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
443     }
445     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
446     while (defined (my $file = readdir (DIR))) {
447         if (not $file =~ /(\S*?).pm$/) {
448             next;
449         }
450                 my $mod_name = $1;
452         if( $file =~ /ArpHandler.pm/ ) {
453             if( $no_arp > 0 ) {
454                 next;
455             }
456         }
457         
458         eval { require $file; };
459         if ($@) {
460             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
461             daemon_log("$@", 5);
462                 } else {
463                         my $info = eval($mod_name.'::get_module_info()');
464                         # Only load module if get_module_info() returns a non-null object
465                         if( $info ) {
466                                 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
467                                 $known_modules->{$mod_name} = $info;
468                                 daemon_log("0 INFO: module $mod_name loaded", 5);
469                         }
470                 }
471     }   
472     close (DIR);
476 #===  FUNCTION  ================================================================
477 #         NAME:  sig_int_handler
478 #   PARAMETERS:  signal - string - signal arose from system
479 #      RETURNS:  noting
480 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
481 #===============================================================================
482 sub sig_int_handler {
483     my ($signal) = @_;
485 #       if (defined($ldap_handle)) {
486 #               $ldap_handle->disconnect;
487 #       }
488     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
489     
491     daemon_log("shutting down gosa-si-server", 1);
492     system("kill `ps -C gosa-si-server -o pid=`");
494 $SIG{INT} = \&sig_int_handler;
497 sub check_key_and_xml_validity {
498     my ($crypted_msg, $module_key, $session_id) = @_;
499     my $msg;
500     my $msg_hash;
501     my $error_string;
502     eval{
503         $msg = &decrypt_msg($crypted_msg, $module_key);
505         if ($msg =~ /<xml>/i){
506             $msg =~ s/\s+/ /g;  # just for better daemon_log
507             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
508             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
510             ##############
511             # check header
512             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
513             my $header_l = $msg_hash->{'header'};
514             if( 1 > @{$header_l} ) { die 'empty header tag'; }
515             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
516             my $header = @{$header_l}[0];
517             if( 0 == length $header) { die 'empty string in header tag'; }
519             ##############
520             # check source
521             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
522             my $source_l = $msg_hash->{'source'};
523             if( 1 > @{$source_l} ) { die 'empty source tag'; }
524             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
525             my $source = @{$source_l}[0];
526             if( 0 == length $source) { die 'source error'; }
528             ##############
529             # check target
530             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
531             my $target_l = $msg_hash->{'target'};
532             if( 1 > @{$target_l} ) { die 'empty target tag'; }
533         }
534     };
535     if($@) {
536         daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
537         $msg = undef;
538         $msg_hash = undef;
539     }
541     return ($msg, $msg_hash);
545 sub check_outgoing_xml_validity {
546     my ($msg, $session_id) = @_;
548     my $msg_hash;
549     eval{
550         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
552         ##############
553         # check header
554         my $header_l = $msg_hash->{'header'};
555         if( 1 != @{$header_l} ) {
556             die 'no or more than one headers specified';
557         }
558         my $header = @{$header_l}[0];
559         if( 0 == length $header) {
560             die 'header has length 0';
561         }
563         ##############
564         # check source
565         my $source_l = $msg_hash->{'source'};
566         if( 1 != @{$source_l} ) {
567             die 'no or more than 1 sources specified';
568         }
569         my $source = @{$source_l}[0];
570         if( 0 == length $source) {
571             die 'source has length 0';
572         }
573         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
574                 $source =~ /^GOSA$/i ) {
575             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
576         }
577         
578         ##############
579         # check target  
580         my $target_l = $msg_hash->{'target'};
581         if( 0 == @{$target_l} ) {
582             die "no targets specified";
583         }
584         foreach my $target (@$target_l) {
585             if( 0 == length $target) {
586                 die "target has length 0";
587             }
588             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
589                     $target =~ /^GOSA$/i ||
590                     $target =~ /^\*$/ ||
591                     $target =~ /KNOWN_SERVER/i ||
592                     $target =~ /JOBDB/i ||
593                     $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 ){
594                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
595             }
596         }
597     };
598     if($@) {
599         daemon_log("$session_id WARNING: outgoing msg is not gosa-si envelope conform: ", 5);
600         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 5);
601         $msg_hash = undef;
602     }
604     return ($msg_hash);
608 sub input_from_known_server {
609     my ($input, $remote_ip, $session_id) = @_ ;  
610     my ($msg, $msg_hash, $module);
612     my $sql_statement= "SELECT * FROM known_server";
613     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
615     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
616         my $host_name = $hit->{hostname};
617         if( not $host_name =~ "^$remote_ip") {
618             next;
619         }
620         my $host_key = $hit->{hostkey};
621         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
622         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
624         # check if module can open msg envelope with module key
625         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
626         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
627             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
628             daemon_log("$@", 8);
629             next;
630         }
631         else {
632             $msg = $tmp_msg;
633             $msg_hash = $tmp_msg_hash;
634             $module = "ServerPackages";
635             last;
636         }
637     }
639     if( (!$msg) || (!$msg_hash) || (!$module) ) {
640         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
641     }
642   
643     return ($msg, $msg_hash, $module);
647 sub input_from_known_client {
648     my ($input, $remote_ip, $session_id) = @_ ;  
649     my ($msg, $msg_hash, $module);
651     my $sql_statement= "SELECT * FROM known_clients";
652     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
653     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
654         my $host_name = $hit->{hostname};
655         if( not $host_name =~ /^$remote_ip:\d*$/) {
656                 next;
657                 }
658         my $host_key = $hit->{hostkey};
659         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
660         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
662         # check if module can open msg envelope with module key
663         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
665         if( (!$msg) || (!$msg_hash) ) {
666             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
667             &daemon_log("$@", 8);
668             next;
669         }
670         else {
671             $module = "ClientPackages";
672             last;
673         }
674     }
676     if( (!$msg) || (!$msg_hash) || (!$module) ) {
677         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
678     }
680     return ($msg, $msg_hash, $module);
684 sub input_from_unknown_host {
685     no strict "refs";
686     my ($input, $session_id) = @_ ;
687     my ($msg, $msg_hash, $module);
688     my $error_string;
689     
690         my %act_modules = %$known_modules;
691         
692     while( my ($mod, $info) = each(%act_modules)) {
694         # check a key exists for this module
695         my $module_key = ${$mod."_key"};
696         if( not defined $module_key ) {
697             if( $mod eq 'ArpHandler' ) {
698                 next;
699             }
700             daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
701             next;
702         }
703         daemon_log("$session_id DEBUG: $mod: $module_key", 7);
705         # check if module can open msg envelope with module key
706         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
707         if( (not defined $msg) || (not defined $msg_hash) ) {
708             next;
709         }
710         else {
711             $module = $mod;
712             last;
713         }
714     }
716     if( (!$msg) || (!$msg_hash) || (!$module)) {
717         daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
718     }
720     return ($msg, $msg_hash, $module);
724 sub create_ciphering {
725     my ($passwd) = @_;
726         if((!defined($passwd)) || length($passwd)==0) {
727                 $passwd = "";
728         }
729     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
730     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
731     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
732     $my_cipher->set_iv($iv);
733     return $my_cipher;
737 sub encrypt_msg {
738     my ($msg, $key) = @_;
739     my $my_cipher = &create_ciphering($key);
740     my $len;
741     {
742             use bytes;
743             $len= 16-length($msg)%16;
744     }
745     $msg = "\0"x($len).$msg;
746     $msg = $my_cipher->encrypt($msg);
747     chomp($msg = &encode_base64($msg));
748     # there are no newlines allowed inside msg
749     $msg=~ s/\n//g;
750     return $msg;
754 sub decrypt_msg {
756     my ($msg, $key) = @_ ;
757     $msg = &decode_base64($msg);
758     my $my_cipher = &create_ciphering($key);
759     $msg = $my_cipher->decrypt($msg); 
760     $msg =~ s/\0*//g;
761     return $msg;
765 sub get_encrypt_key {
766     my ($target) = @_ ;
767     my $encrypt_key;
768     my $error = 0;
770     # target can be in known_server
771     if( not defined $encrypt_key ) {
772         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
773         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
774         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
775             my $host_name = $hit->{hostname};
776             if( $host_name ne $target ) {
777                 next;
778             }
779             $encrypt_key = $hit->{hostkey};
780             last;
781         }
782     }
784     # target can be in known_client
785     if( not defined $encrypt_key ) {
786         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
787         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
788         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
789             my $host_name = $hit->{hostname};
790             if( $host_name ne $target ) {
791                 next;
792             }
793             $encrypt_key = $hit->{hostkey};
794             last;
795         }
796     }
798     return $encrypt_key;
802 #===  FUNCTION  ================================================================
803 #         NAME:  open_socket
804 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
805 #                [PeerPort] string necessary if port not appended by PeerAddr
806 #      RETURNS:  socket IO::Socket::INET
807 #  DESCRIPTION:  open a socket to PeerAddr
808 #===============================================================================
809 sub open_socket {
810     my ($PeerAddr, $PeerPort) = @_ ;
811     if(defined($PeerPort)){
812         $PeerAddr = $PeerAddr.":".$PeerPort;
813     }
814     my $socket;
815     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
816             Porto => "tcp",
817             Type => SOCK_STREAM,
818             Timeout => 5,
819             );
820     if(not defined $socket) {
821         return;
822     }
823 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
824     return $socket;
828 sub get_local_ip_for_remote_ip {
829         my $remote_ip= shift;
830         my $result="0.0.0.0";
832         if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
833                 if($remote_ip eq "127.0.0.1") {
834                         $result = "127.0.0.1";
835                 } else {
836                         my $PROC_NET_ROUTE= ('/proc/net/route');
838                         open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
839                                 or die "Could not open $PROC_NET_ROUTE";
841                         my @ifs = <PROC_NET_ROUTE>;
843                         close(PROC_NET_ROUTE);
845                         # Eat header line
846                         shift @ifs;
847                         chomp @ifs;
848                         foreach my $line(@ifs) {
849                                 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
850                                 my $destination;
851                                 my $mask;
852                                 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
853                                 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
854                                 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
855                                 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
856                                 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
857                                         # destination matches route, save mac and exit
858                                         $result= &get_ip($Iface);
859                                         last;
860                                 }
861                         }
862                 }
863         } else {
864                 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
865         }
866         return $result;
870 sub send_msg_to_target {
871     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
872     my $error = 0;
873     my $header;
874     my $timestamp = &get_time();
875     my $new_status;
876     my $act_status;
877     my ($sql_statement, $res);
878   
879     if( $msg_header ) {
880         $header = "'$msg_header'-";
881     } else {
882         $header = "";
883     }
885         # Patch the source ip
886         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
887                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
888                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
889         }
891     # encrypt xml msg
892     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
894     # opensocket
895     my $socket = &open_socket($address);
896     if( !$socket ) {
897         daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
898         $error++;
899     }
900     
901     if( $error == 0 ) {
902         # send xml msg
903         print $socket $crypted_msg."\n";
905         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
906         daemon_log("$session_id DEBUG: message:\n$msg", 9);
907         
908     }
910     # close socket in any case
911     if( $socket ) {
912         close $socket;
913     }
915     if( $error > 0 ) { $new_status = "down"; }
916     else { $new_status = $msg_header; }
919     # known_clients
920     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
921     $res = $known_clients_db->select_dbentry($sql_statement);
922     if( keys(%$res) == 1) {
923         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
924         if ($act_status eq "down" && $new_status eq "down") {
925             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
926             $res = $known_clients_db->del_dbentry($sql_statement);
927             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
928         } else { 
929             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
930             $res = $known_clients_db->update_dbentry($sql_statement);
931             if($new_status eq "down"){
932                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
933             } else {
934                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
935             }
936         }
937     }
939     # known_server
940     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
941     $res = $known_server_db->select_dbentry($sql_statement);
942     if( keys(%$res) == 1) {
943         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
944         if ($act_status eq "down" && $new_status eq "down") {
945             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
946             $res = $known_server_db->del_dbentry($sql_statement);
947             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
948         } 
949         else { 
950             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
951             $res = $known_server_db->update_dbentry($sql_statement);
952             if($new_status eq "down"){
953                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
954             } else {
955                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
956             }
957         }
958     }
959     return $error; 
963 sub update_jobdb_status_for_send_msgs {
964     my ($answer, $error) = @_;
965     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
966         my $jobdb_id = $1;
967             
968         # sending msg faild
969         if( $error ) {
970             if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
971                 my $sql_statement = "UPDATE $job_queue_tn ".
972                     "SET status='error', result='can not deliver msg, please consult log file' ".
973                     "WHERE id=$jobdb_id";
974                 my $res = $job_db->update_dbentry($sql_statement);
975             }
977         # sending msg was successful
978         } else {
979             my $sql_statement = "UPDATE $job_queue_tn ".
980                 "SET status='done' ".
981                 "WHERE id=$jobdb_id AND status='processed'";
982             my $res = $job_db->update_dbentry($sql_statement);
983         }
984     }
988 sub sig_handler {
989         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
990         daemon_log("0 INFO got signal '$signal'", 1); 
991         $kernel->sig_handled();
992         return;
996 sub msg_to_decrypt {
997     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
998     my $session_id = $session->ID;
999     my ($msg, $msg_hash, $module);
1000     my $error = 0;
1002     # hole neue msg aus @msgs_to_decrypt
1003     my $next_msg = shift @msgs_to_decrypt;
1004     
1005     # entschlüssle sie
1007     # msg is from a new client or gosa
1008     ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1009     # msg is from a gosa-si-server
1010     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1011         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1012     }
1013     # msg is from a gosa-si-client
1014     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1015         ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1016     }
1017     # an error occurred
1018     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1019         # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1020         # could not understand a msg from its server the client cause a re-registering process
1021         daemon_log("$session_id INFO cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1022                 "' to cause a re-registering of the client if necessary", 5);
1023         my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1024         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1025         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1026             my $host_name = $hit->{'hostname'};
1027             my $host_key = $hit->{'hostkey'};
1028             my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1029             my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1030             &update_jobdb_status_for_send_msgs($ping_msg, $error);
1031         }
1032         $error++;
1033     }
1036     my $header;
1037     my $target;
1038     my $source;
1039     my $done = 0;
1040     my $sql;
1041     my $res;
1043     # check whether this message should be processed here
1044     if ($error == 0) {
1045         $header = @{$msg_hash->{'header'}}[0];
1046         $target = @{$msg_hash->{'target'}}[0];
1047         $source = @{$msg_hash->{'source'}}[0];
1048                 my $not_found_in_known_clients_db = 0;
1049                 my $not_found_in_known_server_db = 0;
1050                 my $not_found_in_foreign_clients_db = 0;
1051         my $local_address;
1052         my ($target_ip, $target_port) = split(':', $target);
1053                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1054                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1055                 } else {
1056             $local_address = $server_address;
1057         }
1059         # target and source is equal to GOSA -> process here
1060         if (not $done) {
1061             if ($target eq "GOSA" && $source eq "GOSA") {
1062                 $done = 1;                    
1063             }
1064         }
1066         # target is own address without forward_to_gosa-tag -> process here
1067         if (not $done) {
1068             if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1069                 $done = 1;
1070                 if ($source eq "GOSA") {
1071                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1072                 }
1073                 #print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
1074             }
1075         }
1077         # target is a client address in known_clients -> process here
1078                 if (not $done) {
1079                                 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1080                                 $res = $known_clients_db->select_dbentry($sql);
1081                                 if (keys(%$res) > 0) {
1082                                                 $done = 1; 
1083                                                 my $hostname = $res->{1}->{'hostname'};
1084                                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1085                                                 #print STDERR "target is a client address in known_clients -> process here\n";
1086                                 } else {
1087                                                 $not_found_in_known_clients_db = 1;
1088                                 }
1089                 }
1090         
1091         # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1092         if (not $done) {
1093             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1094             my $gosa_at;
1095             my $gosa_session_id;
1096             if (($target eq $local_address) && (defined $forward_to_gosa)){
1097                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1098                 if ($gosa_at ne $local_address) {
1099                     $done = 1;
1100                     #print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n"; 
1101                 }
1102             }
1103         }
1105         # if message should be processed here -> add message to incoming_db
1106                 if ($done) {
1107                                 # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1108                                 # so gosa-si-server knows how to process this kind of messages
1109                                 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1110                                                 $module = "GosaPackages";
1111                                 }
1113                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1114                                                                 primkey=>[],
1115                                                                 headertag=>$header,
1116                                                                 targettag=>$target,
1117                                                                 xmlmessage=>&encode_base64($msg),
1118                                                                 timestamp=>&get_time,
1119                                                                 module=>$module,
1120                                                                 sessionid=>$session_id,
1121                                                                 } );
1122                 }
1124         # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1125         if (not $done) {
1126             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1127             my $gosa_at;
1128             my $gosa_session_id;
1129             if (($target eq $local_address) && (defined $forward_to_gosa)){
1130                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1131                 if ($gosa_at eq $local_address) {
1132                     my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1133                     if( defined $session_reference ) {
1134                         $heap = $session_reference->get_heap();
1135                     }
1136                     if(exists $heap->{'client'}) {
1137                         $msg = &encrypt_msg($msg, $GosaPackages_key);
1138                         $heap->{'client'}->put($msg);
1139                     }
1140                     $done = 1;
1141                     #print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
1142                 }
1143             }
1145         }
1147         # target is a client address in foreign_clients -> forward to registration server
1148         if (not $done) {
1149             $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1150             $res = $foreign_clients_db->select_dbentry($sql);
1151             if (keys(%$res) > 0) {
1152                     my $hostname = $res->{1}->{'hostname'};
1153                     my ($host_ip, $host_port) = split(/:/, $hostname);
1154                     my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1155                 my $regserver = $res->{1}->{'regserver'};
1156                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1157                 my $res = $known_server_db->select_dbentry($sql);
1158                 if (keys(%$res) > 0) {
1159                     my $regserver_key = $res->{1}->{'hostkey'};
1160                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1161                     $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1162                     if ($source eq "GOSA") {
1163                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1164                     }
1165                     &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1166                 }
1167                 $done = 1;
1168                 #print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
1169             } else {
1170                                 $not_found_in_foreign_clients_db = 1;
1171                         }
1172         }
1174         # target is a server address -> forward to server
1175         if (not $done) {
1176             $sql = "SELECT * FROM $known_server_tn WHERE hostname='$target'";
1177             $res = $known_server_db->select_dbentry($sql);
1178             if (keys(%$res) > 0) {
1179                 my $hostkey = $res->{1}->{'hostkey'};
1181                 if ($source eq "GOSA") {
1182                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1183                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1185                 }
1187                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1188                 $done = 1;
1189                 #print STDERR "target is a server address -> forward to server\n";
1190             } else {
1191                                 $not_found_in_known_server_db = 1;
1192                         }
1193         }
1195                 
1196                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1197                 if ( $not_found_in_foreign_clients_db 
1198                                                 && $not_found_in_known_server_db
1199                                                 && $not_found_in_known_clients_db) {
1200                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1201                                                                 primkey=>[],
1202                                                                 headertag=>$header,
1203                                                                 targettag=>$target,
1204                                                                 xmlmessage=>&encode_base64($msg),
1205                                                                 timestamp=>&get_time,
1206                                                                 module=>$module,
1207                                                                 sessionid=>$session_id,
1208                                                                 } );
1209                                 $done = 1;
1210                 }
1213         if (not $done) {
1214             daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1215             if ($source eq "GOSA") {
1216                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1217                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1219                 my $session_reference = $kernel->ID_id_to_session($session_id);
1220                 if( defined $session_reference ) {
1221                     $heap = $session_reference->get_heap();
1222                 }
1223                 if(exists $heap->{'client'}) {
1224                     $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1225                     $heap->{'client'}->put($error_msg);
1226                 }
1227             }
1228         }
1230     }
1232     return;
1236 sub next_task {
1237     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1238     my $running_task = POE::Wheel::Run->new(
1239             Program => sub { process_task($session, $heap, $task) },
1240             StdioFilter => POE::Filter::Reference->new(),
1241             StdoutEvent  => "task_result",
1242             StderrEvent  => "task_debug",
1243             CloseEvent   => "task_done",
1244             );
1245     $heap->{task}->{ $running_task->ID } = $running_task;
1248 sub handle_task_result {
1249     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1250     my $client_answer = $result->{'answer'};
1251     if( $client_answer =~ s/session_id=(\d+)$// ) {
1252         my $session_id = $1;
1253         if( defined $session_id ) {
1254             my $session_reference = $kernel->ID_id_to_session($session_id);
1255             if( defined $session_reference ) {
1256                 $heap = $session_reference->get_heap();
1257             }
1258         }
1260         if(exists $heap->{'client'}) {
1261             $heap->{'client'}->put($client_answer);
1262         }
1263     }
1264     $kernel->sig(CHLD => "child_reap");
1267 sub handle_task_debug {
1268     my $result = $_[ARG0];
1269     print STDERR "$result\n";
1272 sub handle_task_done {
1273     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1274     delete $heap->{task}->{$task_id};
1277 sub process_task {
1278     no strict "refs";
1279     #CHECK: Not @_[...]?
1280     my ($session, $heap, $task) = @_;
1281     my $error = 0;
1282     my $answer_l;
1283     my ($answer_header, @answer_target_l, $answer_source);
1284     my $client_answer = "";
1286     # prepare all variables needed to process message
1287     #my $msg = $task->{'xmlmessage'};
1288     my $msg = &decode_base64($task->{'xmlmessage'});
1289     my $incoming_id = $task->{'id'};
1290     my $module = $task->{'module'};
1291     my $header =  $task->{'headertag'};
1292     my $session_id = $task->{'sessionid'};
1293     my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1294     my $source = @{$msg_hash->{'source'}}[0];
1295     
1296     # set timestamp of incoming client uptodate, so client will not 
1297     # be deleted from known_clients because of expiration
1298     my $act_time = &get_time();
1299     my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'"; 
1300     my $res = $known_clients_db->exec_statement($sql);
1302     ######################
1303     # process incoming msg
1304     if( $error == 0) {
1305         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1306         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1307         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1309         if ( 0 < @{$answer_l} ) {
1310             my $answer_str = join("\n", @{$answer_l});
1311             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1312                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1313             }
1314             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,8);
1315         } else {
1316             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,8);
1317         }
1319     }
1320     if( !$answer_l ) { $error++ };
1322     ########
1323     # answer
1324     if( $error == 0 ) {
1326         foreach my $answer ( @{$answer_l} ) {
1327             # check outgoing msg to xml validity
1328             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1329             if( not defined $answer_hash ) { next; }
1330             
1331             $answer_header = @{$answer_hash->{'header'}}[0];
1332             @answer_target_l = @{$answer_hash->{'target'}};
1333             $answer_source = @{$answer_hash->{'source'}}[0];
1335             # deliver msg to all targets 
1336             foreach my $answer_target ( @answer_target_l ) {
1338                 # targets of msg are all gosa-si-clients in known_clients_db
1339                 if( $answer_target eq "*" ) {
1340                     # answer is for all clients
1341                     my $sql_statement= "SELECT * FROM known_clients";
1342                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1343                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1344                         my $host_name = $hit->{hostname};
1345                         my $host_key = $hit->{hostkey};
1346                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1347                         &update_jobdb_status_for_send_msgs($answer, $error);
1348                     }
1349                 }
1351                 # targets of msg are all gosa-si-server in known_server_db
1352                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1353                     # answer is for all server in known_server
1354                     my $sql_statement= "SELECT * FROM $known_server_tn";
1355                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1356                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1357                         my $host_name = $hit->{hostname};
1358                         my $host_key = $hit->{hostkey};
1359                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
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                 # target of msg is GOsa
1366                                 elsif( $answer_target eq "GOSA" ) {
1367                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1368                                         my $add_on = "";
1369                     if( defined $session_id ) {
1370                         $add_on = ".session_id=$session_id";
1371                     }
1372                     # answer is for GOSA and has to returned to connected client
1373                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1374                     $client_answer = $gosa_answer.$add_on;
1375                 }
1377                 # target of msg is job queue at this host
1378                 elsif( $answer_target eq "JOBDB") {
1379                     $answer =~ /<header>(\S+)<\/header>/;   
1380                     my $header;
1381                     if( defined $1 ) { $header = $1; }
1382                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1383                     &update_jobdb_status_for_send_msgs($answer, $error);
1384                 }
1386                 # target of msg is a mac address
1387                 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 ) {
1388                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1389                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1390                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1391                     my $found_ip_flag = 0;
1392                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1393                         my $host_name = $hit->{hostname};
1394                         my $host_key = $hit->{hostkey};
1395                         $answer =~ s/$answer_target/$host_name/g;
1396                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1397                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1398                         &update_jobdb_status_for_send_msgs($answer, $error);
1399                         $found_ip_flag++ ;
1400                     }   
1401                     if( $found_ip_flag == 0) {
1402                         daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1403                     }
1405                 #  answer is for one specific host   
1406                 } else {
1407                     # get encrypt_key
1408                     my $encrypt_key = &get_encrypt_key($answer_target);
1409                     if( not defined $encrypt_key ) {
1410                         # unknown target
1411                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1412                         next;
1413                     }
1414                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1415                     &update_jobdb_status_for_send_msgs($answer, $error);
1416                 }
1417             }
1418         }
1419     }
1421     my $filter = POE::Filter::Reference->new();
1422     my %result = ( 
1423             status => "seems ok to me",
1424             answer => $client_answer,
1425             );
1427     my $output = $filter->put( [ \%result ] );
1428     print @$output;
1433 sub session_start {
1434     my ($kernel) = $_[KERNEL];
1435     $global_kernel = $kernel;
1436     $kernel->yield('register_at_foreign_servers');
1437         $kernel->yield('create_fai_server_db', $fai_server_tn );
1438         $kernel->yield('create_fai_release_db', $fai_release_tn );
1439     $kernel->yield('watch_for_next_tasks');
1440         $kernel->sig(USR1 => "sig_handler");
1441         $kernel->sig(USR2 => "recreate_packages_db");
1442         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1443         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1444     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1445         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1446     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1447         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1448     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1454 sub watch_for_done_jobs {
1455     #CHECK: $heap for what?
1456     my ($kernel,$heap) = @_[KERNEL, HEAP];
1458     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1459         my $res = $job_db->select_dbentry( $sql_statement );
1461     while( my ($id, $hit) = each %{$res} ) {
1462         my $jobdb_id = $hit->{id};
1463         my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1464         my $res = $job_db->del_dbentry($sql_statement); 
1465     }
1467     $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1471 # if a job got an update or was modified anyway, send to all other si-server an update message
1472 # of this jobs
1473 sub watch_for_modified_jobs {
1474     my ($kernel,$heap) = @_[KERNEL, HEAP];
1476     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE ((siserver='localhost') AND (modified='1'))"; 
1477     my $res = $job_db->select_dbentry( $sql_statement );
1478     
1479     # if db contains no jobs which should be update, do nothing
1480     if (keys %$res != 0) {
1482         if ($job_synchronization  eq "true") {
1483             # make out of the db result a gosa-si message   
1484             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1485  
1486             # update all other SI-server
1487             &inform_all_other_si_server($update_msg);
1488         }
1490         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1491         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1492         $res = $job_db->update_dbentry($sql_statement);
1493     }
1495     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1499 sub watch_for_new_jobs {
1500         if($watch_for_new_jobs_in_progress == 0) {
1501                 $watch_for_new_jobs_in_progress = 1;
1502                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1504                 # check gosa job quaeue for jobs with executable timestamp
1505                 my $timestamp = &get_time();
1506                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1507                 my $res = $job_db->exec_statement( $sql_statement );
1509                 # Merge all new jobs that would do the same actions
1510                 my @drops;
1511                 my $hits;
1512                 foreach my $hit (reverse @{$res} ) {
1513                         my $macaddress= lc @{$hit}[8];
1514                         my $headertag= @{$hit}[5];
1515                         if(
1516                                 defined($hits->{$macaddress}) &&
1517                                 defined($hits->{$macaddress}->{$headertag}) &&
1518                                 defined($hits->{$macaddress}->{$headertag}[0])
1519                         ) {
1520                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1521                         }
1522                         $hits->{$macaddress}->{$headertag}= $hit;
1523                 }
1525                 # Delete new jobs with a matching job in state 'processing'
1526                 foreach my $macaddress (keys %{$hits}) {
1527                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1528                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1529                                 if(defined($jobdb_id)) {
1530                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1531                                         my $res = $job_db->exec_statement( $sql_statement );
1532                                         foreach my $hit (@{$res}) {
1533                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1534                                         }
1535                                 } else {
1536                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1537                                 }
1538                         }
1539                 }
1541                 # Commit deletion
1542                 $job_db->exec_statementlist(\@drops);
1544                 # Look for new jobs that could be executed
1545                 foreach my $macaddress (keys %{$hits}) {
1547                         # Look if there is an executing job
1548                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1549                         my $res = $job_db->exec_statement( $sql_statement );
1551                         # Skip new jobs for host if there is a processing job
1552                         if(defined($res) and defined @{$res}[0]) {
1553                                 next;
1554                         }
1556                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1557                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1558                                 if(defined($jobdb_id)) {
1559                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1561                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1562                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1563                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1565                                         # expect macaddress is unique!!!!!!
1566                                         my $target = $res_hash->{1}->{hostname};
1568                                         # change header
1569                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1571                                         # add sqlite_id
1572                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1574                                         $job_msg =~ /<header>(\S+)<\/header>/;
1575                                         my $header = $1 ;
1576                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1578                                         # update status in job queue to 'processing'
1579                                         $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1580                                         my $res = $job_db->update_dbentry($sql_statement);
1581 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen                                        
1583                                         # We don't want parallel processing
1584                                         last;
1585                                 }
1586                         }
1587                 }
1589                 $watch_for_new_jobs_in_progress = 0;
1590                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1591         }
1596 sub watch_for_new_messages {
1597     my ($kernel,$heap) = @_[KERNEL, HEAP];
1598     my @coll_user_msg;   # collection list of outgoing messages
1599     
1600     # check messaging_db for new incoming messages with executable timestamp
1601     my $timestamp = &get_time();
1602     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1603     my $res = $messaging_db->exec_statement( $sql_statement );
1604         foreach my $hit (@{$res}) {
1606         # create outgoing messages
1607         my $message_to = @{$hit}[3];
1608         # translate message_to to plain login name
1609         my @message_to_l = split(/,/, $message_to);  
1610                 my %receiver_h; 
1611                 foreach my $receiver (@message_to_l) {
1612                         if ($receiver =~ /^u_([\s\S]*)$/) {
1613                                 $receiver_h{$1} = 0;
1614                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1615                                 my $group_name = $1;
1616                                 # fetch all group members from ldap and add them to receiver hash
1617                                 my $ldap_handle = &get_ldap_handle();
1618                                 if (defined $ldap_handle) {
1619                                                 my $mesg = $ldap_handle->search(
1620                                                                                 base => $ldap_base,
1621                                                                                 scope => 'sub',
1622                                                                                 attrs => ['memberUid'],
1623                                                                                 filter => "cn=$group_name",
1624                                                                                 );
1625                                                 if ($mesg->count) {
1626                                                                 my @entries = $mesg->entries;
1627                                                                 foreach my $entry (@entries) {
1628                                                                                 my @receivers= $entry->get_value("memberUid");
1629                                                                                 foreach my $receiver (@receivers) { 
1630                                                                                                 $receiver_h{$1} = 0;
1631                                                                                 }
1632                                                                 }
1633                                                 } 
1634                                                 # translating errors ?
1635                                                 if ($mesg->code) {
1636                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1637                                                 }
1638                                 # ldap handle error ?           
1639                                 } else {
1640                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1641                                 }
1642                         } else {
1643                                 my $sbjct = &encode_base64(@{$hit}[1]);
1644                                 my $msg = &encode_base64(@{$hit}[7]);
1645                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1646                         }
1647                 }
1648                 my @receiver_l = keys(%receiver_h);
1650         my $message_id = @{$hit}[0];
1652         #add each outgoing msg to messaging_db
1653         my $receiver;
1654         foreach $receiver (@receiver_l) {
1655             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1656                 "VALUES ('".
1657                 $message_id."', '".    # id
1658                 @{$hit}[1]."', '".     # subject
1659                 @{$hit}[2]."', '".     # message_from
1660                 $receiver."', '".      # message_to
1661                 "none"."', '".         # flag
1662                 "out"."', '".          # direction
1663                 @{$hit}[6]."', '".     # delivery_time
1664                 @{$hit}[7]."', '".     # message
1665                 $timestamp."'".     # timestamp
1666                 ")";
1667             &daemon_log("M DEBUG: $sql_statement", 1);
1668             my $res = $messaging_db->exec_statement($sql_statement);
1669             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1670         }
1672         # set incoming message to flag d=deliverd
1673         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1674         &daemon_log("M DEBUG: $sql_statement", 7);
1675         $res = $messaging_db->update_dbentry($sql_statement);
1676         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1677     }
1679     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1680     return;
1683 sub watch_for_delivery_messages {
1684     my ($kernel, $heap) = @_[KERNEL, HEAP];
1686     # select outgoing messages
1687     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1688     #&daemon_log("0 DEBUG: $sql", 7);
1689     my $res = $messaging_db->exec_statement( $sql_statement );
1690     
1691     # build out msg for each    usr
1692     foreach my $hit (@{$res}) {
1693         my $receiver = @{$hit}[3];
1694         my $msg_id = @{$hit}[0];
1695         my $subject = @{$hit}[1];
1696         my $message = @{$hit}[7];
1698         # resolve usr -> host where usr is logged in
1699         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1700         #&daemon_log("0 DEBUG: $sql", 7);
1701         my $res = $login_users_db->exec_statement($sql);
1703         # reciver is logged in nowhere
1704         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1706                 my $send_succeed = 0;
1707                 foreach my $hit (@$res) {
1708                                 my $receiver_host = @$hit[0];
1709                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1711                                 # fetch key to encrypt msg propperly for usr/host
1712                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1713                                 &daemon_log("0 DEBUG: $sql", 7);
1714                                 my $res = $known_clients_db->exec_statement($sql);
1716                                 # host is already down
1717                                 if (not ref(@$res[0]) eq "ARRAY") { next; }
1719                                 # host is on
1720                                 my $receiver_key = @{@{$res}[0]}[2];
1721                                 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1722                                 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1723                                 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1724                                 if ($error == 0 ) {
1725                                         $send_succeed++ ;
1726                                 }
1727                 }
1729                 if ($send_succeed) {
1730                                 # set outgoing msg at db to deliverd
1731                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
1732                                 &daemon_log("0 DEBUG: $sql", 7);
1733                                 my $res = $messaging_db->exec_statement($sql); 
1734                 }
1735         }
1737     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
1738     return;
1742 sub watch_for_done_messages {
1743     my ($kernel,$heap) = @_[KERNEL, HEAP];
1745     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
1746     #&daemon_log("0 DEBUG: $sql", 7);
1747     my $res = $messaging_db->exec_statement($sql); 
1749     foreach my $hit (@{$res}) {
1750         my $msg_id = @{$hit}[0];
1752         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
1753         #&daemon_log("0 DEBUG: $sql", 7); 
1754         my $res = $messaging_db->exec_statement($sql);
1756         # not all usr msgs have been seen till now
1757         if ( ref(@$res[0]) eq "ARRAY") { next; }
1758         
1759         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
1760         #&daemon_log("0 DEBUG: $sql", 7);
1761         $res = $messaging_db->exec_statement($sql);
1762     
1763     }
1765     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
1766     return;
1770 sub watch_for_old_known_clients {
1771     my ($kernel,$heap) = @_[KERNEL, HEAP];
1773     my $sql_statement = "SELECT * FROM $known_clients_tn";
1774     my $res = $known_clients_db->select_dbentry( $sql_statement );
1776     my $act_time = int(&get_time());
1778     while ( my ($hit_num, $hit) = each %$res) {
1779         my $expired_timestamp = int($hit->{'timestamp'});
1780         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
1781         my $dt = DateTime->new( year   => $1,
1782                 month  => $2,
1783                 day    => $3,
1784                 hour   => $4,
1785                 minute => $5,
1786                 second => $6,
1787                 );
1789         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
1790         $expired_timestamp = $dt->ymd('').$dt->hms('')."\n";
1791         if ($act_time > $expired_timestamp) {
1792             my $hostname = $hit->{'hostname'};
1793             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
1794             my $del_res = $known_clients_db->exec_statement($del_sql);
1796             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
1797         }
1799     }
1801     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1805 sub watch_for_next_tasks {
1806     my ($kernel,$heap) = @_[KERNEL, HEAP];
1808     my $sql = "SELECT * FROM $incoming_tn";
1809     my $res = $incoming_db->select_dbentry($sql);
1811     while ( my ($hit_num, $hit) = each %$res) {
1812         my $headertag = $hit->{'headertag'};
1813         if ($headertag =~ /^answer_(\d+)/) {
1814             # do not start processing, this message is for a still running POE::Wheel
1815             next;
1816         }
1817         my $message_id = $hit->{'id'};
1818         $kernel->yield('next_task', $hit);
1820         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
1821         my $res = $incoming_db->exec_statement($sql);
1822     }
1824     $kernel->delay_set('watch_for_next_tasks', 0.1); 
1828 sub get_ldap_handle {
1829         my ($session_id) = @_;
1830         my $heap;
1831         my $ldap_handle;
1833         if (not defined $session_id ) { $session_id = 0 };
1834         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1836         if ($session_id == 0) {
1837                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
1838                 $ldap_handle = Net::LDAP->new( $ldap_uri );
1839                 $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!"); 
1841         } else {
1842                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1843                 if( defined $session_reference ) {
1844                         $heap = $session_reference->get_heap();
1845                 }
1847                 if (not defined $heap) {
1848                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
1849                         return;
1850                 }
1852                 # TODO: This "if" is nonsense, because it doesn't prove that the
1853                 #       used handle is still valid - or if we've to reconnect...
1854                 #if (not exists $heap->{ldap_handle}) {
1855                         $ldap_handle = Net::LDAP->new( $ldap_uri );
1856                         $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!"); 
1857                         $heap->{ldap_handle} = $ldap_handle;
1858                 #}
1859         }
1860         return $ldap_handle;
1864 sub change_fai_state {
1865     my ($st, $targets, $session_id) = @_;
1866     $session_id = 0 if not defined $session_id;
1867     # Set FAI state to localboot
1868     my %mapActions= (
1869         reboot    => '',
1870         update    => 'softupdate',
1871         localboot => 'localboot',
1872         reinstall => 'install',
1873         rescan    => '',
1874         wake      => '',
1875         memcheck  => 'memcheck',
1876         sysinfo   => 'sysinfo',
1877         install   => 'install',
1878     );
1880     # Return if this is unknown
1881     if (!exists $mapActions{ $st }){
1882         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
1883       return;
1884     }
1886     my $state= $mapActions{ $st };
1888     my $ldap_handle = &get_ldap_handle($session_id);
1889     if( defined($ldap_handle) ) {
1891       # Build search filter for hosts
1892         my $search= "(&(objectClass=GOhard)";
1893         foreach (@{$targets}){
1894             $search.= "(macAddress=$_)";
1895         }
1896         $search.= ")";
1898       # If there's any host inside of the search string, procress them
1899         if (!($search =~ /macAddress/)){
1900             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
1901             return;
1902         }
1904       # Perform search for Unit Tag
1905       my $mesg = $ldap_handle->search(
1906           base   => $ldap_base,
1907           scope  => 'sub',
1908           attrs  => ['dn', 'FAIstate', 'objectClass'],
1909           filter => "$search"
1910           );
1912           if ($mesg->count) {
1913                   my @entries = $mesg->entries;
1914                   if (0 == @entries) {
1915                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
1916                   }
1918                   foreach my $entry (@entries) {
1919                           # Only modify entry if it is not set to '$state'
1920                           if ($entry->get_value("FAIstate") ne "$state"){
1921                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1922                                   my $result;
1923                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1924                                   if (exists $tmp{'FAIobject'}){
1925                                           if ($state eq ''){
1926                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1927                                                           delete => [ FAIstate => [] ] ]);
1928                                           } else {
1929                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1930                                                           replace => [ FAIstate => $state ] ]);
1931                                           }
1932                                   } elsif ($state ne ''){
1933                                           $result= $ldap_handle->modify($entry->dn, changes => [
1934                                                   add     => [ objectClass => 'FAIobject' ],
1935                                                   add     => [ FAIstate => $state ] ]);
1936                                   }
1938                                   # Errors?
1939                                   if ($result->code){
1940                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1941                                   }
1942                           } else {
1943                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
1944                           }  
1945                   }
1946           } else {
1947                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
1948           }
1950     # if no ldap handle defined
1951     } else {
1952         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
1953     }
1955         return;
1959 sub change_goto_state {
1960     my ($st, $targets, $session_id) = @_;
1961     $session_id = 0  if not defined $session_id;
1963     # Switch on or off?
1964     my $state= $st eq 'active' ? 'active': 'locked';
1966     my $ldap_handle = &get_ldap_handle($session_id);
1967     if( defined($ldap_handle) ) {
1969       # Build search filter for hosts
1970       my $search= "(&(objectClass=GOhard)";
1971       foreach (@{$targets}){
1972         $search.= "(macAddress=$_)";
1973       }
1974       $search.= ")";
1976       # If there's any host inside of the search string, procress them
1977       if (!($search =~ /macAddress/)){
1978         return;
1979       }
1981       # Perform search for Unit Tag
1982       my $mesg = $ldap_handle->search(
1983           base   => $ldap_base,
1984           scope  => 'sub',
1985           attrs  => ['dn', 'gotoMode'],
1986           filter => "$search"
1987           );
1989       if ($mesg->count) {
1990         my @entries = $mesg->entries;
1991         foreach my $entry (@entries) {
1993           # Only modify entry if it is not set to '$state'
1994           if ($entry->get_value("gotoMode") ne $state){
1996             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1997             my $result;
1998             $result= $ldap_handle->modify($entry->dn, changes => [
1999                                                 replace => [ gotoMode => $state ] ]);
2001             # Errors?
2002             if ($result->code){
2003               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2004             }
2006           }
2007         }
2008       } else {
2009                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2010           }
2012     }
2016 sub run_recreate_packages_db {
2017     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2018     my $session_id = $session->ID;
2019         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 4);
2020         $kernel->yield('create_fai_release_db');
2021         $kernel->yield('create_fai_server_db');
2022         return;
2026 sub run_create_fai_server_db {
2027     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2028     my $session_id = $session->ID;
2029     my $task = POE::Wheel::Run->new(
2030             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2031             StdoutEvent  => "session_run_result",
2032             StderrEvent  => "session_run_debug",
2033             CloseEvent   => "session_run_done",
2034             );
2036     $heap->{task}->{ $task->ID } = $task;
2037     return;
2041 sub create_fai_server_db {
2042     my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2043         my $result;
2045         if (not defined $session_id) { $session_id = 0; }
2046     my $ldap_handle = &get_ldap_handle();
2047         if(defined($ldap_handle)) {
2048                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2049                 my $mesg= $ldap_handle->search(
2050                         base   => $ldap_base,
2051                         scope  => 'sub',
2052                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2053                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2054                 );
2055                 if($mesg->{'resultCode'} == 0 &&
2056                    $mesg->count != 0) {
2057                    foreach my $entry (@{$mesg->{entries}}) {
2058                            if($entry->exists('FAIrepository')) {
2059                                    # Add an entry for each Repository configured for server
2060                                    foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2061                                                    my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2062                                                    my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2063                                                    $result= $fai_server_db->add_dbentry( { 
2064                                                                    table => $table_name,
2065                                                                    primkey => ['server', 'release', 'tag'],
2066                                                                    server => $tmp_url,
2067                                                                    release => $tmp_release,
2068                                                                    sections => $tmp_sections,
2069                                                                    tag => (length($tmp_tag)>0)?$tmp_tag:"",
2070                                                            } );
2071                                            }
2072                                    }
2073                            }
2074                    }
2075                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2077                 # TODO: Find a way to post the 'create_packages_list_db' event
2078                 if(not defined($dont_create_packages_list)) {
2079                         &create_packages_list_db(undef, undef, $session_id);
2080                 }
2081         }       
2082     
2083     $ldap_handle->disconnect;
2084         return $result;
2088 sub run_create_fai_release_db {
2089     my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2090         my $session_id = $session->ID;
2091     my $task = POE::Wheel::Run->new(
2092             Program => sub { &create_fai_release_db($table_name, $session_id) },
2093             StdoutEvent  => "session_run_result",
2094             StderrEvent  => "session_run_debug",
2095             CloseEvent   => "session_run_done",
2096             );
2098     $heap->{task}->{ $task->ID } = $task;
2099     return;
2103 sub create_fai_release_db {
2104         my ($table_name, $session_id) = @_;
2105         my $result;
2107     # used for logging
2108     if (not defined $session_id) { $session_id = 0; }
2110     my $ldap_handle = &get_ldap_handle();
2111         if(defined($ldap_handle)) {
2112                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2113                 my $mesg= $ldap_handle->search(
2114                         base   => $ldap_base,
2115                         scope  => 'sub',
2116                         attrs  => [],
2117                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2118                 );
2119                 if($mesg->{'resultCode'} == 0 &&
2120                         $mesg->count != 0) {
2121                         # Walk through all possible FAI container ou's
2122                         my @sql_list;
2123                         my $timestamp= &get_time();
2124                         foreach my $ou (@{$mesg->{entries}}) {
2125                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2126                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2127                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2128                                         if(@tmp_array) {
2129                                                 foreach my $entry (@tmp_array) {
2130                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2131                                                                 my $sql= 
2132                                                                 "INSERT INTO $table_name "
2133                                                                 ."(timestamp, release, class, type, state) VALUES ("
2134                                                                 .$timestamp.","
2135                                                                 ."'".$entry->{'release'}."',"
2136                                                                 ."'".$entry->{'class'}."',"
2137                                                                 ."'".$entry->{'type'}."',"
2138                                                                 ."'".$entry->{'state'}."')";
2139                                                                 push @sql_list, $sql;
2140                                                         }
2141                                                 }
2142                                         }
2143                                 }
2144                         }
2146                         daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2147                         if(@sql_list) {
2148                                 unshift @sql_list, "VACUUM";
2149                                 unshift @sql_list, "DELETE FROM $table_name";
2150                                 $fai_release_db->exec_statementlist(\@sql_list);
2151                         }
2152                         daemon_log("$session_id DEBUG: Done with inserting",7);
2153                 }
2154                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2155         }
2156     $ldap_handle->disconnect;
2157         return $result;
2160 sub get_fai_types {
2161         my $tmp_classes = shift || return undef;
2162         my @result;
2164         foreach my $type(keys %{$tmp_classes}) {
2165                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2166                         my $entry = {
2167                                 type => $type,
2168                                 state => $tmp_classes->{$type}[0],
2169                         };
2170                         push @result, $entry;
2171                 }
2172         }
2174         return @result;
2177 sub get_fai_state {
2178         my $result = "";
2179         my $tmp_classes = shift || return $result;
2181         foreach my $type(keys %{$tmp_classes}) {
2182                 if(defined($tmp_classes->{$type}[0])) {
2183                         $result = $tmp_classes->{$type}[0];
2184                         
2185                 # State is equal for all types in class
2186                         last;
2187                 }
2188         }
2190         return $result;
2193 sub resolve_fai_classes {
2194         my ($fai_base, $ldap_handle, $session_id) = @_;
2195         if (not defined $session_id) { $session_id = 0; }
2196         my $result;
2197         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2198         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2199         my $fai_classes;
2201         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2202         my $mesg= $ldap_handle->search(
2203                 base   => $fai_base,
2204                 scope  => 'sub',
2205                 attrs  => ['cn','objectClass','FAIstate'],
2206                 filter => $fai_filter,
2207         );
2208         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2210         if($mesg->{'resultCode'} == 0 &&
2211                 $mesg->count != 0) {
2212                 foreach my $entry (@{$mesg->{entries}}) {
2213                         if($entry->exists('cn')) {
2214                                 my $tmp_dn= $entry->dn();
2216                                 # Skip classname and ou dn parts for class
2217                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2219                                 # Skip classes without releases
2220                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2221                                         next;
2222                                 }
2224                                 my $tmp_cn= $entry->get_value('cn');
2225                                 my $tmp_state= $entry->get_value('FAIstate');
2227                                 my $tmp_type;
2228                                 # Get FAI type
2229                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2230                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2231                                                 $tmp_type= $oclass;
2232                                                 last;
2233                                         }
2234                                 }
2236                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2237                                         # A Subrelease
2238                                         my @sub_releases = split(/,/, $tmp_release);
2240                                         # Walk through subreleases and build hash tree
2241                                         my $hash;
2242                                         while(my $tmp_sub_release = pop @sub_releases) {
2243                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2244                                         }
2245                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2246                                 } else {
2247                                         # A branch, no subrelease
2248                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2249                                 }
2250                         } elsif (!$entry->exists('cn')) {
2251                                 my $tmp_dn= $entry->dn();
2252                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2254                                 # Skip classes without releases
2255                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2256                                         next;
2257                                 }
2259                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2260                                         # A Subrelease
2261                                         my @sub_releases= split(/,/, $tmp_release);
2263                                         # Walk through subreleases and build hash tree
2264                                         my $hash;
2265                                         while(my $tmp_sub_release = pop @sub_releases) {
2266                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2267                                         }
2268                                         # Remove the last two characters
2269                                         chop($hash);
2270                                         chop($hash);
2272                                         eval('$fai_classes->'.$hash.'= {}');
2273                                 } else {
2274                                         # A branch, no subrelease
2275                                         if(!exists($fai_classes->{$tmp_release})) {
2276                                                 $fai_classes->{$tmp_release} = {};
2277                                         }
2278                                 }
2279                         }
2280                 }
2282                 # The hash is complete, now we can honor the copy-on-write based missing entries
2283                 foreach my $release (keys %$fai_classes) {
2284                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2285                 }
2286         }
2287         return $result;
2290 sub apply_fai_inheritance {
2291        my $fai_classes = shift || return {};
2292        my $tmp_classes;
2294        # Get the classes from the branch
2295        foreach my $class (keys %{$fai_classes}) {
2296                # Skip subreleases
2297                if($class =~ /^ou=.*$/) {
2298                        next;
2299                } else {
2300                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2301                }
2302        }
2304        # Apply to each subrelease
2305        foreach my $subrelease (keys %{$fai_classes}) {
2306                if($subrelease =~ /ou=/) {
2307                        foreach my $tmp_class (keys %{$tmp_classes}) {
2308                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2309                                        $fai_classes->{$subrelease}->{$tmp_class} =
2310                                        deep_copy($tmp_classes->{$tmp_class});
2311                                } else {
2312                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2313                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2314                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2315                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2316                                                }
2317                                        }
2318                                }
2319                        }
2320                }
2321        }
2323        # Find subreleases in deeper levels
2324        foreach my $subrelease (keys %{$fai_classes}) {
2325                if($subrelease =~ /ou=/) {
2326                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2327                                if($subsubrelease =~ /ou=/) {
2328                                        apply_fai_inheritance($fai_classes->{$subrelease});
2329                                }
2330                        }
2331                }
2332        }
2334        return $fai_classes;
2337 sub get_fai_release_entries {
2338         my $tmp_classes = shift || return;
2339         my $parent = shift || "";
2340         my @result = shift || ();
2342         foreach my $entry (keys %{$tmp_classes}) {
2343                 if(defined($entry)) {
2344                         if($entry =~ /^ou=.*$/) {
2345                                 my $release_name = $entry;
2346                                 $release_name =~ s/ou=//g;
2347                                 if(length($parent)>0) {
2348                                         $release_name = $parent."/".$release_name;
2349                                 }
2350                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2351                                 foreach my $bufentry(@bufentries) {
2352                                         push @result, $bufentry;
2353                                 }
2354                         } else {
2355                                 my @types = get_fai_types($tmp_classes->{$entry});
2356                                 foreach my $type (@types) {
2357                                         push @result, 
2358                                         {
2359                                                 'class' => $entry,
2360                                                 'type' => $type->{'type'},
2361                                                 'release' => $parent,
2362                                                 'state' => $type->{'state'},
2363                                         };
2364                                 }
2365                         }
2366                 }
2367         }
2369         return @result;
2372 sub deep_copy {
2373         my $this = shift;
2374         if (not ref $this) {
2375                 $this;
2376         } elsif (ref $this eq "ARRAY") {
2377                 [map deep_copy($_), @$this];
2378         } elsif (ref $this eq "HASH") {
2379                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2380         } else { die "what type is $_?" }
2384 sub session_run_result {
2385     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2386     $kernel->sig(CHLD => "child_reap");
2389 sub session_run_debug {
2390     my $result = $_[ARG0];
2391     print STDERR "$result\n";
2394 sub session_run_done {
2395     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2396     delete $heap->{task}->{$task_id};
2400 sub create_sources_list {
2401         my $session_id = shift;
2402         my $ldap_handle = &main::get_ldap_handle;
2403         my $result="/tmp/gosa_si_tmp_sources_list";
2405         # Remove old file
2406         if(stat($result)) {
2407                 unlink($result);
2408                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2409         }
2411         my $fh;
2412         open($fh, ">$result");
2413         if (not defined $fh) {
2414                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2415                 return undef;
2416         }
2417         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2418                 my $mesg=$ldap_handle->search(
2419                         base    => $main::ldap_server_dn,
2420                         scope   => 'base',
2421                         attrs   => 'FAIrepository',
2422                         filter  => 'objectClass=FAIrepositoryServer'
2423                 );
2424                 if($mesg->count) {
2425                         foreach my $entry(@{$mesg->{'entries'}}) {
2426                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2427                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2428                                         my $line = "deb $server $release";
2429                                         $sections =~ s/,/ /g;
2430                                         $line.= " $sections";
2431                                         print $fh $line."\n";
2432                                 }
2433                         }
2434                 }
2435         } else {
2436                 if (defined $main::ldap_server_dn){
2437                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2438                 } else {
2439                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2440                 }
2441         }
2442         close($fh);
2444         return $result;
2448 sub run_create_packages_list_db {
2449     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2450         my $session_id = $session->ID;
2452         my $task = POE::Wheel::Run->new(
2453                                         Priority => +20,
2454                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2455                                         StdoutEvent  => "session_run_result",
2456                                         StderrEvent  => "session_run_debug",
2457                                         CloseEvent   => "session_run_done",
2458                                         );
2459         $heap->{task}->{ $task->ID } = $task;
2463 sub create_packages_list_db {
2464         my ($ldap_handle, $sources_file, $session_id) = @_;
2465         
2466         # it should not be possible to trigger a recreation of packages_list_db
2467         # while packages_list_db is under construction, so set flag packages_list_under_construction
2468         # which is tested befor recreation can be started
2469         if (-r $packages_list_under_construction) {
2470                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2471                 return;
2472         } else {
2473                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2474                 # set packages_list_under_construction to true
2475                 system("touch $packages_list_under_construction");
2476                 @packages_list_statements=();
2477         }
2479         if (not defined $session_id) { $session_id = 0; }
2480         if (not defined $ldap_handle) { 
2481                 $ldap_handle= &get_ldap_handle();
2483                 if (not defined $ldap_handle) {
2484                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2485                         unlink($packages_list_under_construction);
2486                         return;
2487                 }
2488         }
2489         if (not defined $sources_file) { 
2490                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2491                 $sources_file = &create_sources_list($session_id);
2492         }
2494         if (not defined $sources_file) {
2495                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2496                 unlink($packages_list_under_construction);
2497                 return;
2498         }
2500         my $line;
2502         open(CONFIG, "<$sources_file") or do {
2503                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2504                 unlink($packages_list_under_construction);
2505                 return;
2506         };
2508         # Read lines
2509         while ($line = <CONFIG>){
2510                 # Unify
2511                 chop($line);
2512                 $line =~ s/^\s+//;
2513                 $line =~ s/^\s+/ /;
2515                 # Strip comments
2516                 $line =~ s/#.*$//g;
2518                 # Skip empty lines
2519                 if ($line =~ /^\s*$/){
2520                         next;
2521                 }
2523                 # Interpret deb line
2524                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2525                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2526                         my $section;
2527                         foreach $section (split(' ', $sections)){
2528                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2529                         }
2530                 }
2531         }
2533         close (CONFIG);
2535         find(\&cleanup_and_extract, keys( %repo_dirs ));
2536         &main::strip_packages_list_statements();
2537         unshift @packages_list_statements, "VACUUM";
2538         $packages_list_db->exec_statementlist(\@packages_list_statements);
2539         unlink($packages_list_under_construction);
2540         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2541         return;
2544 # This function should do some intensive task to minimize the db-traffic
2545 sub strip_packages_list_statements {
2546     my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2547         my @new_statement_list=();
2548         my $hash;
2549         my $insert_hash;
2550         my $update_hash;
2551         my $delete_hash;
2552         my $local_timestamp=get_time();
2554         foreach my $existing_entry (@existing_entries) {
2555                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2556         }
2558         foreach my $statement (@packages_list_statements) {
2559                 if($statement =~ /^INSERT/i) {
2560                         # Assign the values from the insert statement
2561                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2562                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2563                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2564                                 # If section or description has changed, update the DB
2565                                 if( 
2566                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2567                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2568                                 ) {
2569                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2570                                 }
2571                         } else {
2572                                 # Insert a non-existing entry to db
2573                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2574                         }
2575                 } elsif ($statement =~ /^UPDATE/i) {
2576                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2577                         /^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;
2578                         foreach my $distribution (keys %{$hash}) {
2579                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2580                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2581                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2582                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2583                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2584                                                 my $section;
2585                                                 my $description;
2586                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2587                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2588                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2589                                                 }
2590                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2591                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2592                                                 }
2593                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2594                                         }
2595                                 }
2596                         }
2597                 }
2598         }
2600         # TODO: Check for orphaned entries
2602         # unroll the insert_hash
2603         foreach my $distribution (keys %{$insert_hash}) {
2604                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2605                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2606                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2607                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2608                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2609                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2610                                 ."'$local_timestamp')";
2611                         }
2612                 }
2613         }
2615         # unroll the update hash
2616         foreach my $distribution (keys %{$update_hash}) {
2617                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2618                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2619                                 my $set = "";
2620                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2621                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2622                                 }
2623                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2624                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2625                                 }
2626                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2627                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2628                                 }
2629                                 if(defined($set) and length($set) > 0) {
2630                                         $set .= "timestamp = '$local_timestamp'";
2631                                 } else {
2632                                         next;
2633                                 }
2634                                 push @new_statement_list, 
2635                                         "UPDATE $main::packages_list_tn SET $set WHERE"
2636                                         ." distribution = '$distribution'"
2637                                         ." AND package = '$package'"
2638                                         ." AND version = '$version'";
2639                         }
2640                 }
2641         }
2643         @packages_list_statements = @new_statement_list;
2647 sub parse_package_info {
2648     my ($baseurl, $dist, $section, $session_id)= @_;
2649     my ($package);
2650     if (not defined $session_id) { $session_id = 0; }
2651     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2652     $repo_dirs{ "${repo_path}/pool" } = 1;
2654     foreach $package ("Packages.gz"){
2655         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2656         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2657         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2658     }
2659     
2663 sub get_package {
2664     my ($url, $dest, $session_id)= @_;
2665     if (not defined $session_id) { $session_id = 0; }
2667     my $tpath = dirname($dest);
2668     -d "$tpath" || mkpath "$tpath";
2670     # This is ugly, but I've no time to take a look at "how it works in perl"
2671     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2672         system("gunzip -cd '$dest' > '$dest.in'");
2673         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2674         unlink($dest);
2675         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
2676     } else {
2677         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2678     }
2679     return 0;
2683 sub parse_package {
2684     my ($path, $dist, $srv_path, $session_id)= @_;
2685     if (not defined $session_id) { $session_id = 0;}
2686     my ($package, $version, $section, $description);
2687     my $PACKAGES;
2688     my $timestamp = &get_time();
2690     if(not stat("$path.in")) {
2691         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2692         return;
2693     }
2695     open($PACKAGES, "<$path.in");
2696     if(not defined($PACKAGES)) {
2697         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
2698         return;
2699     }
2701     # Read lines
2702     while (<$PACKAGES>){
2703         my $line = $_;
2704         # Unify
2705         chop($line);
2707         # Use empty lines as a trigger
2708         if ($line =~ /^\s*$/){
2709             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2710             push(@packages_list_statements, $sql);
2711             $package = "none";
2712             $version = "none";
2713             $section = "none";
2714             $description = "none"; 
2715             next;
2716         }
2718         # Trigger for package name
2719         if ($line =~ /^Package:\s/){
2720             ($package)= ($line =~ /^Package: (.*)$/);
2721             next;
2722         }
2724         # Trigger for version
2725         if ($line =~ /^Version:\s/){
2726             ($version)= ($line =~ /^Version: (.*)$/);
2727             next;
2728         }
2730         # Trigger for description
2731         if ($line =~ /^Description:\s/){
2732             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2733             next;
2734         }
2736         # Trigger for section
2737         if ($line =~ /^Section:\s/){
2738             ($section)= ($line =~ /^Section: (.*)$/);
2739             next;
2740         }
2742         # Trigger for filename
2743         if ($line =~ /^Filename:\s/){
2744             my ($filename) = ($line =~ /^Filename: (.*)$/);
2745             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2746             next;
2747         }
2748     }
2750     close( $PACKAGES );
2751     unlink( "$path.in" );
2755 sub store_fileinfo {
2756     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2758     my %fileinfo = (
2759         'package' => $package,
2760         'dist' => $dist,
2761         'version' => $vers,
2762     );
2764     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2768 sub cleanup_and_extract {
2769     my $fileinfo = $repo_files{ $File::Find::name };
2771     if( defined $fileinfo ) {
2773         my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2774         my $sql;
2775         my $package = $fileinfo->{ 'package' };
2776         my $newver = $fileinfo->{ 'version' };
2778         mkpath($dir);
2779         system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2781                 if( -f "$dir/DEBIAN/templates" ) {
2783                         daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2785                         my $tmpl= "";
2786                         {
2787                                 local $/=undef;
2788                                 open FILE, "$dir/DEBIAN/templates";
2789                                 $tmpl = &encode_base64(<FILE>);
2790                                 close FILE;
2791                         }
2792                         rmtree("$dir/DEBIAN/templates");
2794                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2795                 push @packages_list_statements, $sql;
2796                 }
2797     }
2799     return;
2803 sub register_at_foreign_servers {   
2804     my ($kernel) = $_[KERNEL];
2806     # hole alle bekannten server aus known_server_db
2807     my $server_sql = "SELECT * FROM $known_server_tn";
2808     my $server_res = $known_server_db->exec_statement($server_sql);
2810     # no entries in known_server_db
2811     if (not ref(@$server_res[0]) eq "ARRAY") { 
2812         # TODO
2813     }
2815     # detect already connected clients
2816     my $client_sql = "SELECT * FROM $known_clients_tn"; 
2817     my $client_res = $known_clients_db->exec_statement($client_sql);
2819     # send my server details to all other gosa-si-server within the network
2820     foreach my $hit (@$server_res) {
2821         my $hostname = @$hit[0];
2822         my $hostkey = &create_passwd;
2824         # add already connected clients to registration message 
2825         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
2826         &add_content2xml_hash($myhash, 'key', $hostkey);
2827         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
2828         
2829         # build registration message and send it
2830         my $foreign_server_msg = &create_xml_string($myhash);
2831         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
2832     }
2833     
2834     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
2835     return;
2839 #==== MAIN = main ==============================================================
2840 #  parse commandline options
2841 Getopt::Long::Configure( "bundling" );
2842 GetOptions("h|help" => \&usage,
2843         "c|config=s" => \$cfg_file,
2844         "f|foreground" => \$foreground,
2845         "v|verbose+" => \$verbose,
2846         "no-arp+" => \$no_arp,
2847            );
2849 #  read and set config parameters
2850 &check_cmdline_param ;
2851 &read_configfile;
2852 &check_pid;
2854 $SIG{CHLD} = 'IGNORE';
2856 # forward error messages to logfile
2857 if( ! $foreground ) {
2858   open( STDIN,  '+>/dev/null' );
2859   open( STDOUT, '+>&STDIN'    );
2860   open( STDERR, '+>&STDIN'    );
2863 # Just fork, if we are not in foreground mode
2864 if( ! $foreground ) { 
2865     chdir '/'                 or die "Can't chdir to /: $!";
2866     $pid = fork;
2867     setsid                    or die "Can't start a new session: $!";
2868     umask 0;
2869 } else { 
2870     $pid = $$; 
2873 # Do something useful - put our PID into the pid_file
2874 if( 0 != $pid ) {
2875     open( LOCK_FILE, ">$pid_file" );
2876     print LOCK_FILE "$pid\n";
2877     close( LOCK_FILE );
2878     if( !$foreground ) { 
2879         exit( 0 ) 
2880     };
2883 # parse head url and revision from svn
2884 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2885 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2886 $server_headURL = defined $1 ? $1 : 'unknown' ;
2887 $server_revision = defined $2 ? $2 : 'unknown' ;
2888 if ($server_headURL =~ /\/tag\// || 
2889         $server_headURL =~ /\/branches\// ) {
2890     $server_status = "stable"; 
2891 } else {
2892     $server_status = "developmental" ;
2896 daemon_log(" ", 1);
2897 daemon_log("$0 started!", 1);
2898 daemon_log("status: $server_status", 1);
2899 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
2901 # connect to incoming_db
2902 unlink($incoming_file_name);
2903 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
2904 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
2906 # connect to gosa-si job queue
2907 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2908 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2910 # connect to known_clients_db
2911 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2912 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2914 # connect to foreign_clients_db
2915 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
2916 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
2918 # connect to known_server_db
2919 unlink($known_server_file_name);
2920 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2921 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2923 # connect to login_usr_db
2924 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2925 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2927 # connect to fai_server_db and fai_release_db
2928 unlink($fai_server_file_name);
2929 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2930 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2932 unlink($fai_release_file_name);
2933 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2934 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2936 # connect to packages_list_db
2937 #unlink($packages_list_file_name);
2938 unlink($packages_list_under_construction);
2939 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2940 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2942 # connect to messaging_db
2943 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2944 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2947 # create xml object used for en/decrypting
2948 $xml = new XML::Simple();
2951 # foreign servers 
2952 my @foreign_server_list;
2954 # add foreign server from cfg file
2955 if ($foreign_server_string ne "") {
2956     my @cfg_foreign_server_list = split(",", $foreign_server_string);
2957     foreach my $foreign_server (@cfg_foreign_server_list) {
2958         push(@foreign_server_list, $foreign_server);
2959     }
2962 # add foreign server from dns
2963 my @tmp_servers;
2964 if ( !$server_domain) {
2965     # Try our DNS Searchlist
2966     for my $domain(get_dns_domains()) {
2967         chomp($domain);
2968         my @tmp_domains= &get_server_addresses($domain);
2969         if(@tmp_domains) {
2970             for my $tmp_server(@tmp_domains) {
2971                 push @tmp_servers, $tmp_server;
2972             }
2973         }
2974     }
2975     if(@tmp_servers && length(@tmp_servers)==0) {
2976         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
2977     }
2978 } else {
2979     @tmp_servers = &get_server_addresses($server_domain);
2980     if( 0 == @tmp_servers ) {
2981         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
2982     }
2984 foreach my $server (@tmp_servers) { 
2985     unshift(@foreign_server_list, $server); 
2987 # eliminate duplicate entries
2988 @foreign_server_list = &del_doubles(@foreign_server_list);
2989 my $all_foreign_server = join(", ", @foreign_server_list);
2990 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
2992 # add all found foreign servers to known_server
2993 my $act_timestamp = &get_time();
2994 foreach my $foreign_server (@foreign_server_list) {
2996         # do not add myself to known_server_db
2997         if (&is_local($foreign_server)) { next; }
2998         ######################################
3000     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3001             primkey=>['hostname'],
3002             hostname=>$foreign_server,
3003             status=>'not_jet_registered',
3004             hostkey=>"none",
3005             timestamp=>$act_timestamp,
3006             } );
3010 POE::Component::Server::TCP->new(
3011     Alias => "TCP_SERVER",
3012         Port => $server_port,
3013         ClientInput => sub {
3014         my ($kernel, $input) = @_[KERNEL, ARG0];
3015         push(@tasks, $input);
3016         push(@msgs_to_decrypt, $input);
3017         $kernel->yield("msg_to_decrypt");
3018         },
3019     InlineStates => {
3020         msg_to_decrypt => \&msg_to_decrypt,
3021         next_task => \&next_task,
3022         task_result => \&handle_task_result,
3023         task_done   => \&handle_task_done,
3024         task_debug  => \&handle_task_debug,
3025         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3026     }
3027 );
3029 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
3031 # create session for repeatedly checking the job queue for jobs
3032 POE::Session->create(
3033         inline_states => {
3034                 _start => \&session_start,
3035         register_at_foreign_servers => \&register_at_foreign_servers,
3036         sig_handler => \&sig_handler,
3037         next_task => \&next_task,
3038         task_result => \&handle_task_result,
3039         task_done   => \&handle_task_done,
3040         task_debug  => \&handle_task_debug,
3041         watch_for_next_tasks => \&watch_for_next_tasks,
3042         watch_for_new_messages => \&watch_for_new_messages,
3043         watch_for_delivery_messages => \&watch_for_delivery_messages,
3044         watch_for_done_messages => \&watch_for_done_messages,
3045                 watch_for_new_jobs => \&watch_for_new_jobs,
3046         watch_for_modified_jobs => \&watch_for_modified_jobs,
3047         watch_for_done_jobs => \&watch_for_done_jobs,
3048         watch_for_old_known_clients => \&watch_for_old_known_clients,
3049         create_packages_list_db => \&run_create_packages_list_db,
3050         create_fai_server_db => \&run_create_fai_server_db,
3051         create_fai_release_db => \&run_create_fai_release_db,
3052                 recreate_packages_db => \&run_recreate_packages_db,
3053         session_run_result => \&session_run_result,
3054         session_run_debug => \&session_run_debug,
3055         session_run_done => \&session_run_done,
3056         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3057         }
3058 );
3061 # import all modules
3062 &import_modules;
3064 # TODO
3065 # check wether all modules are gosa-si valid passwd check
3069 POE::Kernel->run();
3070 exit;