Code

262d1afd1ec962e081b6a4ed155ffdecd60d5c73
[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 );
91 # additional variable which should be globaly accessable
92 our $server_address;
93 our $server_mac_address;
94 our $gosa_address;
95 our $no_arp;
96 our $verbose;
97 our $forground;
98 our $cfg_file;
99 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
101 # dak variables
102 our $dak_base_directory;
103 our $dak_signing_keys_directory;
104 our $dak_queue_directory;
105 our $dak_user;
107 # specifies the verbosity of the daemon_log
108 $verbose = 0 ;
110 # if foreground is not null, script will be not forked to background
111 $foreground = 0 ;
113 # specifies the timeout seconds while checking the online status of a registrating client
114 $ping_timeout = 5;
116 $no_arp = 0;
117 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
118 my @packages_list_statements;
119 my $watch_for_new_jobs_in_progress = 0;
121 # holds all incoming decrypted messages
122 our $incoming_db;
123 our $incoming_tn = 'incoming';
124 my $incoming_file_name;
125 my @incoming_col_names = ("id INTEGER PRIMARY KEY", 
126         "timestamp DEFAULT 'none'", 
127         "headertag DEFAULT 'none'",
128                 "targettag DEFAULT 'none'",
129         "xmlmessage DEFAULT 'none'",
130         "module DEFAULT 'none'",
131         "sessionid DEFAULT '0'",
132         );
134 # holds all gosa jobs
135 our $job_db;
136 our $job_queue_tn = 'jobs';
137 my $job_queue_file_name;
138 my @job_queue_col_names = ("id INTEGER PRIMARY KEY", 
139                 "timestamp DEFAULT 'none'", 
140                 "status DEFAULT 'none'", 
141                 "result DEFAULT 'none'", 
142                 "progress DEFAULT 'none'", 
143         "headertag DEFAULT 'none'", 
144                 "targettag DEFAULT 'none'", 
145                 "xmlmessage DEFAULT 'none'", 
146                 "macaddress DEFAULT 'none'",
147                 "plainname DEFAULT 'none'",
148                 );
150 # holds all other gosa-si-server
151 our $known_server_db;
152 our $known_server_tn = "known_server";
153 my $known_server_file_name;
154 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
156 # holds all registrated clients
157 our $known_clients_db;
158 our $known_clients_tn = "known_clients";
159 my $known_clients_file_name;
160 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events", "keylifetime");
162 # holds all registered clients at a foreign server
163 our $foreign_clients_db;
164 our $foreign_clients_tn = "foreign_clients"; 
165 my $foreign_clients_file_name;
166 my @foreign_clients_col_names = ("hostname", "macaddress", "regserver", "timestamp");
168 # holds all logged in user at each client 
169 our $login_users_db;
170 our $login_users_tn = "login_users";
171 my $login_users_file_name;
172 my @login_users_col_names = ("client", "user", "timestamp");
174 # holds all fai server, the debian release and tag
175 our $fai_server_db;
176 our $fai_server_tn = "fai_server"; 
177 my $fai_server_file_name;
178 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag"); 
180 our $fai_release_db;
181 our $fai_release_tn = "fai_release"; 
182 my $fai_release_file_name;
183 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state"); 
185 # holds all packages available from different repositories
186 our $packages_list_db;
187 our $packages_list_tn = "packages_list";
188 my $packages_list_file_name;
189 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
190 my $outdir = "/tmp/packages_list_db";
191 my $arch = "i386"; 
193 # holds all messages which should be delivered to a user
194 our $messaging_db;
195 our $messaging_tn = "messaging"; 
196 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to", 
197         "flag", "direction", "delivery_time", "message", "timestamp" );
198 my $messaging_file_name;
200 # path to directory to store client install log files
201 our $client_fai_log_dir = "/var/log/fai"; 
203 # queue which stores taskes until one of the $max_children children are ready to process the task
204 my @tasks = qw();
205 my @msgs_to_decrypt = qw();
206 my $max_children = 2;
209 %cfg_defaults = (
210 "general" => {
211     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
212     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
213     },
214 "server" => {
215     "port" => [\$server_port, "20081"],
216     "known-clients"        => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
217     "known-servers"        => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
218     "incoming"             => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
219     "login-users"          => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
220     "fai-server"           => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
221     "fai-release"          => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
222     "packages-list"        => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
223     "messaging"            => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
224     "foreign-clients"      => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
225     "source-list"          => [\$sources_list, '/etc/apt/sources.list'],
226     "repo-path"            => [\$repo_path, '/srv/www/repository'],
227     "ldap-uri"             => [\$ldap_uri, ""],
228     "ldap-base"            => [\$ldap_base, ""],
229     "ldap-admin-dn"        => [\$ldap_admin_dn, ""],
230     "ldap-admin-password"  => [\$ldap_admin_password, ""],
231     "gosa-unit-tag"        => [\$gosa_unit_tag, ""],
232     "max-clients"          => [\$max_clients, 10],
233     },
234 "GOsaPackages" => {
235     "ip" => [\$gosa_ip, "0.0.0.0"],
236     "port" => [\$gosa_port, "20082"],
237     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
238     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
239     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
240     "key" => [\$GosaPackages_key, "none"],
241         "dak-base" => [\$dak_base_directory, "/srv/archive"],
242         "dak-keyring" => [\$dak_signing_keys_directory, "/srv/archive/keyrings"],
243         "dak-queue" => [\$dak_queue_directory, "/srv/archive/queue"],
244         "dak-user" => [\$dak_user, "deb-dak"],
245     },
246 "ClientPackages" => {
247     "key" => [\$ClientPackages_key, "none"],
248     },
249 "ServerPackages"=> {
250     "address"      => [\$foreign_server_string, ""],
251     "domain"  => [\$server_domain, ""],
252     "key"     => [\$ServerPackages_key, "none"],
253     "key-lifetime" => [\$foreign_servers_register_delay, 120],
255 );
258 #===  FUNCTION  ================================================================
259 #         NAME:  usage
260 #   PARAMETERS:  nothing
261 #      RETURNS:  nothing
262 #  DESCRIPTION:  print out usage text to STDERR
263 #===============================================================================
264 sub usage {
265     print STDERR << "EOF" ;
266 usage: $prg [-hvf] [-c config]
268            -h        : this (help) message
269            -c <file> : config file
270            -f        : foreground, process will not be forked to background
271            -v        : be verbose (multiple to increase verbosity)
272            -no-arp   : starts $prg without connection to arp module
273  
274 EOF
275     print "\n" ;
279 #===  FUNCTION  ================================================================
280 #         NAME:  read_configfile
281 #   PARAMETERS:  cfg_file - string -
282 #      RETURNS:  nothing
283 #  DESCRIPTION:  read cfg_file and set variables
284 #===============================================================================
285 sub read_configfile {
286     my $cfg;
287     if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
288         if( -r $cfg_file ) {
289             $cfg = Config::IniFiles->new( -file => $cfg_file );
290         } else {
291             print STDERR "Couldn't read config file!\n";
292         }
293     } else {
294         $cfg = Config::IniFiles->new() ;
295     }
296     foreach my $section (keys %cfg_defaults) {
297         foreach my $param (keys %{$cfg_defaults{ $section }}) {
298             my $pinfo = $cfg_defaults{ $section }{ $param };
299             ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
300         }
301     }
305 #===  FUNCTION  ================================================================
306 #         NAME:  logging
307 #   PARAMETERS:  level - string - default 'info'
308 #                msg - string -
309 #                facility - string - default 'LOG_DAEMON'
310 #      RETURNS:  nothing
311 #  DESCRIPTION:  function for logging
312 #===============================================================================
313 sub daemon_log {
314     # log into log_file
315     my( $msg, $level ) = @_;
316     if(not defined $msg) { return }
317     if(not defined $level) { $level = 1 }
318     if(defined $log_file){
319         open(LOG_HANDLE, ">>$log_file");
320         chmod 0600, $log_file;
321         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
322             print STDERR "cannot open $log_file: $!";
323             return 
324         }
325         chomp($msg);
326         $msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
327         if($level <= $verbose){
328             my ($seconds, $minutes, $hours, $monthday, $month,
329                     $year, $weekday, $yearday, $sommertime) = localtime(time);
330             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
331             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
332             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
333             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
334             $month = $monthnames[$month];
335             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
336             $year+=1900;
337             my $name = $prg;
339             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
340             print LOG_HANDLE $log_msg;
341             if( $foreground ) { 
342                 print STDERR $log_msg;
343             }
344         }
345         close( LOG_HANDLE );
346     }
350 #===  FUNCTION  ================================================================
351 #         NAME:  check_cmdline_param
352 #   PARAMETERS:  nothing
353 #      RETURNS:  nothing
354 #  DESCRIPTION:  validates commandline parameter
355 #===============================================================================
356 sub check_cmdline_param () {
357     my $err_config;
358     my $err_counter = 0;
359         if(not defined($cfg_file)) {
360                 $cfg_file = "/etc/gosa-si/server.conf";
361                 if(! -r $cfg_file) {
362                         $err_config = "please specify a config file";
363                         $err_counter += 1;
364                 }
365     }
366     if( $err_counter > 0 ) {
367         &usage( "", 1 );
368         if( defined( $err_config)) { print STDERR "$err_config\n"}
369         print STDERR "\n";
370         exit( -1 );
371     }
375 #===  FUNCTION  ================================================================
376 #         NAME:  check_pid
377 #   PARAMETERS:  nothing
378 #      RETURNS:  nothing
379 #  DESCRIPTION:  handels pid processing
380 #===============================================================================
381 sub check_pid {
382     $pid = -1;
383     # Check, if we are already running
384     if( open(LOCK_FILE, "<$pid_file") ) {
385         $pid = <LOCK_FILE>;
386         if( defined $pid ) {
387             chomp( $pid );
388             if( -f "/proc/$pid/stat" ) {
389                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
390                 if( $stat ) {
391                                         daemon_log("ERROR: Already running",1);
392                     close( LOCK_FILE );
393                     exit -1;
394                 }
395             }
396         }
397         close( LOCK_FILE );
398         unlink( $pid_file );
399     }
401     # create a syslog msg if it is not to possible to open PID file
402     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
403         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
404         if (open(LOCK_FILE, '<', $pid_file)
405                 && ($pid = <LOCK_FILE>))
406         {
407             chomp($pid);
408             $msg .= "(PID $pid)\n";
409         } else {
410             $msg .= "(unable to read PID)\n";
411         }
412         if( ! ($foreground) ) {
413             openlog( $0, "cons,pid", "daemon" );
414             syslog( "warning", $msg );
415             closelog();
416         }
417         else {
418             print( STDERR " $msg " );
419         }
420         exit( -1 );
421     }
424 #===  FUNCTION  ================================================================
425 #         NAME:  import_modules
426 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
427 #                are stored
428 #      RETURNS:  nothing
429 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
430 #                state is on is imported by "require 'file';"
431 #===============================================================================
432 sub import_modules {
433     daemon_log(" ", 1);
435     if (not -e $modules_path) {
436         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
437     }
439     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
440     while (defined (my $file = readdir (DIR))) {
441         if (not $file =~ /(\S*?).pm$/) {
442             next;
443         }
444                 my $mod_name = $1;
446         if( $file =~ /ArpHandler.pm/ ) {
447             if( $no_arp > 0 ) {
448                 next;
449             }
450         }
451         
452         eval { require $file; };
453         if ($@) {
454             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
455             daemon_log("$@", 5);
456                 } else {
457                         my $info = eval($mod_name.'::get_module_info()');
458                         # Only load module if get_module_info() returns a non-null object
459                         if( $info ) {
460                                 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
461                                 $known_modules->{$mod_name} = $info;
462                                 daemon_log("0 INFO: module $mod_name loaded", 5);
463                         }
464                 }
465     }   
466     close (DIR);
470 #===  FUNCTION  ================================================================
471 #         NAME:  sig_int_handler
472 #   PARAMETERS:  signal - string - signal arose from system
473 #      RETURNS:  noting
474 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
475 #===============================================================================
476 sub sig_int_handler {
477     my ($signal) = @_;
479 #       if (defined($ldap_handle)) {
480 #               $ldap_handle->disconnect;
481 #       }
482     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
483     
485     daemon_log("shutting down gosa-si-server", 1);
486     system("kill `ps -C gosa-si-server -o pid=`");
488 $SIG{INT} = \&sig_int_handler;
491 sub check_key_and_xml_validity {
492     my ($crypted_msg, $module_key, $session_id) = @_;
493     my $msg;
494     my $msg_hash;
495     my $error_string;
496     eval{
497         $msg = &decrypt_msg($crypted_msg, $module_key);
499         if ($msg =~ /<xml>/i){
500             $msg =~ s/\s+/ /g;  # just for better daemon_log
501             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
502             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
504             ##############
505             # check header
506             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
507             my $header_l = $msg_hash->{'header'};
508             if( 1 > @{$header_l} ) { die 'empty header tag'; }
509             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
510             my $header = @{$header_l}[0];
511             if( 0 == length $header) { die 'empty string in header tag'; }
513             ##############
514             # check source
515             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
516             my $source_l = $msg_hash->{'source'};
517             if( 1 > @{$source_l} ) { die 'empty source tag'; }
518             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
519             my $source = @{$source_l}[0];
520             if( 0 == length $source) { die 'source error'; }
522             ##############
523             # check target
524             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
525             my $target_l = $msg_hash->{'target'};
526             if( 1 > @{$target_l} ) { die 'empty target tag'; }
527         }
528     };
529     if($@) {
530         daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
531         $msg = undef;
532         $msg_hash = undef;
533     }
535     return ($msg, $msg_hash);
539 sub check_outgoing_xml_validity {
540     my ($msg) = @_;
542     my $msg_hash;
543     eval{
544         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
546         ##############
547         # check header
548         my $header_l = $msg_hash->{'header'};
549         if( 1 != @{$header_l} ) {
550             die 'no or more than one headers specified';
551         }
552         my $header = @{$header_l}[0];
553         if( 0 == length $header) {
554             die 'header has length 0';
555         }
557         ##############
558         # check source
559         my $source_l = $msg_hash->{'source'};
560         if( 1 != @{$source_l} ) {
561             die 'no or more than 1 sources specified';
562         }
563         my $source = @{$source_l}[0];
564         if( 0 == length $source) {
565             die 'source has length 0';
566         }
567         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
568                 $source =~ /^GOSA$/i ) {
569             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
570         }
571         
572         ##############
573         # check target  
574         my $target_l = $msg_hash->{'target'};
575         if( 0 == @{$target_l} ) {
576             die "no targets specified";
577         }
578         foreach my $target (@$target_l) {
579             if( 0 == length $target) {
580                 die "target has length 0";
581             }
582             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
583                     $target =~ /^GOSA$/i ||
584                     $target =~ /^\*$/ ||
585                     $target =~ /KNOWN_SERVER/i ||
586                     $target =~ /JOBDB/i ||
587                     $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 ){
588                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
589             }
590         }
591     };
592     if($@) {
593         daemon_log("WARNING: outgoing msg is not gosa-si envelope conform", 5);
594         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 8);
595         $msg_hash = undef;
596     }
598     return ($msg_hash);
602 sub input_from_known_server {
603     my ($input, $remote_ip, $session_id) = @_ ;  
604     my ($msg, $msg_hash, $module);
606     my $sql_statement= "SELECT * FROM known_server";
607     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
609     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
610         my $host_name = $hit->{hostname};
611         if( not $host_name =~ "^$remote_ip") {
612             next;
613         }
614         my $host_key = $hit->{hostkey};
615         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
616         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
618         # check if module can open msg envelope with module key
619         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
620         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
621             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
622             daemon_log("$@", 8);
623             next;
624         }
625         else {
626             $msg = $tmp_msg;
627             $msg_hash = $tmp_msg_hash;
628             $module = "ServerPackages";
629             last;
630         }
631     }
633     if( (!$msg) || (!$msg_hash) || (!$module) ) {
634         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
635     }
636   
637     return ($msg, $msg_hash, $module);
641 sub input_from_known_client {
642     my ($input, $remote_ip, $session_id) = @_ ;  
643     my ($msg, $msg_hash, $module);
645     my $sql_statement= "SELECT * FROM known_clients";
646     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
647     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
648         my $host_name = $hit->{hostname};
649         if( not $host_name =~ /^$remote_ip:\d*$/) {
650                 next;
651                 }
652         my $host_key = $hit->{hostkey};
653         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
654         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
656         # check if module can open msg envelope with module key
657         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
659         if( (!$msg) || (!$msg_hash) ) {
660             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
661             &daemon_log("$@", 8);
662             next;
663         }
664         else {
665             $module = "ClientPackages";
666             last;
667         }
668     }
670     if( (!$msg) || (!$msg_hash) || (!$module) ) {
671         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
672     }
674     return ($msg, $msg_hash, $module);
678 sub input_from_unknown_host {
679     no strict "refs";
680     my ($input, $session_id) = @_ ;
681     my ($msg, $msg_hash, $module);
682     my $error_string;
683     
684         my %act_modules = %$known_modules;
685         
686     while( my ($mod, $info) = each(%act_modules)) {
688         # check a key exists for this module
689         my $module_key = ${$mod."_key"};
690         if( not defined $module_key ) {
691             if( $mod eq 'ArpHandler' ) {
692                 next;
693             }
694             daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
695             next;
696         }
697         daemon_log("$session_id DEBUG: $mod: $module_key", 7);
699         # check if module can open msg envelope with module key
700         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
701         if( (not defined $msg) || (not defined $msg_hash) ) {
702             next;
703         }
704         else {
705             $module = $mod;
706             last;
707         }
708     }
710     if( (!$msg) || (!$msg_hash) || (!$module)) {
711         daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
712     }
714     return ($msg, $msg_hash, $module);
718 sub create_ciphering {
719     my ($passwd) = @_;
720         if((!defined($passwd)) || length($passwd)==0) {
721                 $passwd = "";
722         }
723     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
724     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
725     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
726     $my_cipher->set_iv($iv);
727     return $my_cipher;
731 sub encrypt_msg {
732     my ($msg, $key) = @_;
733     my $my_cipher = &create_ciphering($key);
734     my $len;
735     {
736             use bytes;
737             $len= 16-length($msg)%16;
738     }
739     $msg = "\0"x($len).$msg;
740     $msg = $my_cipher->encrypt($msg);
741     chomp($msg = &encode_base64($msg));
742     # there are no newlines allowed inside msg
743     $msg=~ s/\n//g;
744     return $msg;
748 sub decrypt_msg {
750     my ($msg, $key) = @_ ;
751     $msg = &decode_base64($msg);
752     my $my_cipher = &create_ciphering($key);
753     $msg = $my_cipher->decrypt($msg); 
754     $msg =~ s/\0*//g;
755     return $msg;
759 sub get_encrypt_key {
760     my ($target) = @_ ;
761     my $encrypt_key;
762     my $error = 0;
764     # target can be in known_server
765     if( not defined $encrypt_key ) {
766         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
767         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
768         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
769             my $host_name = $hit->{hostname};
770             if( $host_name ne $target ) {
771                 next;
772             }
773             $encrypt_key = $hit->{hostkey};
774             last;
775         }
776     }
778     # target can be in known_client
779     if( not defined $encrypt_key ) {
780         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
781         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
782         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
783             my $host_name = $hit->{hostname};
784             if( $host_name ne $target ) {
785                 next;
786             }
787             $encrypt_key = $hit->{hostkey};
788             last;
789         }
790     }
792     return $encrypt_key;
796 #===  FUNCTION  ================================================================
797 #         NAME:  open_socket
798 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
799 #                [PeerPort] string necessary if port not appended by PeerAddr
800 #      RETURNS:  socket IO::Socket::INET
801 #  DESCRIPTION:  open a socket to PeerAddr
802 #===============================================================================
803 sub open_socket {
804     my ($PeerAddr, $PeerPort) = @_ ;
805     if(defined($PeerPort)){
806         $PeerAddr = $PeerAddr.":".$PeerPort;
807     }
808     my $socket;
809     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
810             Porto => "tcp",
811             Type => SOCK_STREAM,
812             Timeout => 5,
813             );
814     if(not defined $socket) {
815         return;
816     }
817 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
818     return $socket;
822 # moved to GosaSupportDaemon: 03-06-2008: rettenbe
823 #===  FUNCTION  ================================================================
824 #         NAME:  get_ip 
825 #   PARAMETERS:  interface name (i.e. eth0)
826 #      RETURNS:  (ip address) 
827 #  DESCRIPTION:  Uses ioctl to get ip address directly from system.
828 #===============================================================================
829 #sub get_ip {
830 #       my $ifreq= shift;
831 #       my $result= "";
832 #       my $SIOCGIFADDR= 0x8915;       # man 2 ioctl_list
833 #       my $proto= getprotobyname('ip');
835 #       socket SOCKET, PF_INET, SOCK_DGRAM, $proto
836 #               or die "socket: $!";
838 #       if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
839 #               my ($if, $sin)    = unpack 'a16 a16', $ifreq;
840 #               my ($port, $addr) = sockaddr_in $sin;
841 #               my $ip            = inet_ntoa $addr;
843 #               if ($ip && length($ip) > 0) {
844 #                       $result = $ip;
845 #               }
846 #       }
848 #       return $result;
849 #}
852 sub get_local_ip_for_remote_ip {
853         my $remote_ip= shift;
854         my $result="0.0.0.0";
856         if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
857                 if($remote_ip eq "127.0.0.1") {
858                         $result = "127.0.0.1";
859                 } else {
860                         my $PROC_NET_ROUTE= ('/proc/net/route');
862                         open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
863                                 or die "Could not open $PROC_NET_ROUTE";
865                         my @ifs = <PROC_NET_ROUTE>;
867                         close(PROC_NET_ROUTE);
869                         # Eat header line
870                         shift @ifs;
871                         chomp @ifs;
872                         foreach my $line(@ifs) {
873                                 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
874                                 my $destination;
875                                 my $mask;
876                                 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
877                                 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
878                                 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
879                                 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
880                                 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
881                                         # destination matches route, save mac and exit
882                                         $result= &get_ip($Iface);
883                                         last;
884                                 }
885                         }
886                 }
887         } else {
888                 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
889         }
890         return $result;
894 sub send_msg_to_target {
895     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
896     my $error = 0;
897     my $header;
898     my $timestamp = &get_time();
899     my $new_status;
900     my $act_status;
901     my ($sql_statement, $res);
902   
903     if( $msg_header ) {
904         $header = "'$msg_header'-";
905     } else {
906         $header = "";
907     }
909         # Patch the source ip
910         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
911                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
912                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
913         }
915     # encrypt xml msg
916     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
918     # opensocket
919     my $socket = &open_socket($address);
920     if( !$socket ) {
921         daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
922         $error++;
923     }
924     
925     if( $error == 0 ) {
926         # send xml msg
927         print $socket $crypted_msg."\n";
929         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
930         daemon_log("$session_id DEBUG: message:\n$msg", 9);
931         
932     }
934     # close socket in any case
935     if( $socket ) {
936         close $socket;
937     }
939     if( $error > 0 ) { $new_status = "down"; }
940     else { $new_status = $msg_header; }
943     # known_clients
944     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
945     $res = $known_clients_db->select_dbentry($sql_statement);
946     if( keys(%$res) == 1) {
947         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
948         if ($act_status eq "down" && $new_status eq "down") {
949             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
950             $res = $known_clients_db->del_dbentry($sql_statement);
951             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
952         } else { 
953             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
954             $res = $known_clients_db->update_dbentry($sql_statement);
955             if($new_status eq "down"){
956                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
957             } else {
958                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
959             }
960         }
961     }
963     # known_server
964     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
965     $res = $known_server_db->select_dbentry($sql_statement);
966     if( keys(%$res) == 1) {
967         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
968         if ($act_status eq "down" && $new_status eq "down") {
969             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
970             $res = $known_server_db->del_dbentry($sql_statement);
971             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
972         } 
973         else { 
974             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
975             $res = $known_server_db->update_dbentry($sql_statement);
976             if($new_status eq "down"){
977                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
978             } else {
979                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
980             }
981         }
982     }
983     return $error; 
987 sub update_jobdb_status_for_send_msgs {
988     my ($answer, $error) = @_;
989     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
990         my $jobdb_id = $1;
991             
992         # sending msg faild
993         if( $error ) {
994             if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
995                 my $sql_statement = "UPDATE $job_queue_tn ".
996                     "SET status='error', result='can not deliver msg, please consult log file' ".
997                     "WHERE id=$jobdb_id";
998                 my $res = $job_db->update_dbentry($sql_statement);
999             }
1001         # sending msg was successful
1002         } else {
1003             my $sql_statement = "UPDATE $job_queue_tn ".
1004                 "SET status='done' ".
1005                 "WHERE id=$jobdb_id AND status='processed'";
1006             my $res = $job_db->update_dbentry($sql_statement);
1007         }
1008     }
1012 sub sig_handler {
1013         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1014         daemon_log("0 INFO got signal '$signal'", 1); 
1015         $kernel->sig_handled();
1016         return;
1020 sub msg_to_decrypt {
1021     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1022     my $session_id = $session->ID;
1023     my ($msg, $msg_hash, $module);
1024     my $error = 0;
1026     # hole neue msg aus @msgs_to_decrypt
1027     my $next_msg = shift @msgs_to_decrypt;
1028     
1029     # entschlüssle sie
1031     # msg is from a new client or gosa
1032     ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1033     # msg is from a gosa-si-server
1034     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1035         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1036     }
1037     # msg is from a gosa-si-client
1038     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1039         ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1040     }
1041     # an error occurred
1042     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1043         # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1044         # could not understand a msg from its server the client cause a re-registering process
1045         daemon_log("$session_id INFO cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1046                 "' to cause a re-registering of the client if necessary", 5);
1047         my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1048         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1049         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1050             my $host_name = $hit->{'hostname'};
1051             my $host_key = $hit->{'hostkey'};
1052             my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1053             my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1054             &update_jobdb_status_for_send_msgs($ping_msg, $error);
1055         }
1056         $error++;
1057     }
1060     my $header;
1061     my $target;
1062     my $source;
1063     my $done = 0;
1064     my $sql;
1065     my $res;
1067     # check whether this message should be processed here
1068     if ($error == 0) {
1069         $header = @{$msg_hash->{'header'}}[0];
1070         $target = @{$msg_hash->{'target'}}[0];
1071         $source = @{$msg_hash->{'source'}}[0];
1072                 my $not_found_in_known_clients_db = 0;
1073                 my $not_found_in_known_server_db = 0;
1074                 my $not_found_in_foreign_clients_db = 0;
1075         my $local_address;
1076         my ($target_ip, $target_port) = split(':', $target);
1077                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1078                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1079                 } else {
1080             $local_address = $server_address;
1081         }
1083         # target and source is equal to GOSA -> process here
1084         if (not $done) {
1085             if ($target eq "GOSA" && $source eq "GOSA") {
1086                 $done = 1;                    
1087             }
1088         }
1090         # target is own address without forward_to_gosa-tag -> process here
1091         if (not $done) {
1092             if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1093                 $done = 1;
1094                 if ($source eq "GOSA") {
1095                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1096                 }
1097                 #print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
1098             }
1099         }
1101         # target is a client address in known_clients -> process here
1102                 if (not $done) {
1103                                 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1104                                 $res = $known_clients_db->select_dbentry($sql);
1105                                 if (keys(%$res) > 0) {
1106                                                 $done = 1; 
1107                                                 my $hostname = $res->{1}->{'hostname'};
1108                                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1109                                                 #print STDERR "target is a client address in known_clients -> process here\n";
1110                                 } else {
1111                                                 $not_found_in_known_clients_db = 1;
1112                                 }
1113                 }
1114         
1115         # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1116         if (not $done) {
1117             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1118             my $gosa_at;
1119             my $gosa_session_id;
1120             if (($target eq $local_address) && (defined $forward_to_gosa)){
1121                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1122                 if ($gosa_at ne $local_address) {
1123                     $done = 1;
1124                     #print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n"; 
1125                 }
1126             }
1127         }
1129         # if message should be processed here -> add message to incoming_db
1130                 if ($done) {
1131                                 # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1132                                 # so gosa-si-server knows how to process this kind of messages
1133                                 if ($header =~ /^gosa_/ || $header =~ /job_/) {
1134                                                 $module = "GosaPackages";
1135                                 }
1137                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1138                                                                 primkey=>[],
1139                                                                 headertag=>$header,
1140                                                                 targettag=>$target,
1141                                                                 xmlmessage=>&encode_base64($msg),
1142                                                                 timestamp=>&get_time,
1143                                                                 module=>$module,
1144                                                                 sessionid=>$session_id,
1145                                                                 } );
1146                 }
1148         # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1149         if (not $done) {
1150             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1151             my $gosa_at;
1152             my $gosa_session_id;
1153             if (($target eq $local_address) && (defined $forward_to_gosa)){
1154                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1155                 if ($gosa_at eq $local_address) {
1156                     my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1157                     if( defined $session_reference ) {
1158                         $heap = $session_reference->get_heap();
1159                     }
1160                     if(exists $heap->{'client'}) {
1161                         $msg = &encrypt_msg($msg, $GosaPackages_key);
1162                         $heap->{'client'}->put($msg);
1163                     }
1164                     $done = 1;
1165                     #print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
1166                 }
1167             }
1169         }
1171         # target is a client address in foreign_clients -> forward to registration server
1172         if (not $done) {
1173             $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1174             $res = $foreign_clients_db->select_dbentry($sql);
1175             if (keys(%$res) > 0) {
1176                 my $hostname = $res->{1}->{'hostname'};
1177                 my $regserver = $res->{1}->{'regserver'};
1178                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1179                 my $res = $known_server_db->select_dbentry($sql);
1180                 if (keys(%$res) > 0) {
1181                     my $regserver_key = $res->{1}->{'hostkey'};
1182                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1183                     $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1184                     if ($source eq "GOSA") {
1185                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1186                     }
1187                     &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1188                 }
1189                 $done = 1;
1190                 #print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
1191             } else {
1192                                 $not_found_in_foreign_clients_db = 1;
1193                         }
1194         }
1196         # target is a server address -> forward to server
1197         if (not $done) {
1198             $sql = "SELECT * FROM $known_server_tn WHERE hostname='$target'";
1199             $res = $known_server_db->select_dbentry($sql);
1200             if (keys(%$res) > 0) {
1201                 my $hostkey = $res->{1}->{'hostkey'};
1203                 if ($source eq "GOSA") {
1204                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1205                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1207                 }
1209                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1210                 $done = 1;
1211                 #print STDERR "target is a server address -> forward to server\n";
1212             } else {
1213                                 $not_found_in_known_server_db = 1;
1214                         }
1215         }
1217                 
1218                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1219                 if ( $not_found_in_foreign_clients_db 
1220                                                 && $not_found_in_known_server_db
1221                                                 && $not_found_in_known_clients_db) {
1222                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1223                                                                 primkey=>[],
1224                                                                 headertag=>$header,
1225                                                                 targettag=>$target,
1226                                                                 xmlmessage=>&encode_base64($msg),
1227                                                                 timestamp=>&get_time,
1228                                                                 module=>$module,
1229                                                                 sessionid=>$session_id,
1230                                                                 } );
1231                                 $done = 1;
1232                 }
1235         if (not $done) {
1236             daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1237             if ($source eq "GOSA") {
1238                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1239                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1241                 my $session_reference = $kernel->ID_id_to_session($session_id);
1242                 if( defined $session_reference ) {
1243                     $heap = $session_reference->get_heap();
1244                 }
1245                 if(exists $heap->{'client'}) {
1246                     $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1247                     $heap->{'client'}->put($error_msg);
1248                 }
1249             }
1250         }
1252     }
1254     return;
1258 sub next_task {
1259     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1260     my $running_task = POE::Wheel::Run->new(
1261             Program => sub { process_task($session, $heap, $task) },
1262             StdioFilter => POE::Filter::Reference->new(),
1263             StdoutEvent  => "task_result",
1264             StderrEvent  => "task_debug",
1265             CloseEvent   => "task_done",
1266             );
1267     $heap->{task}->{ $running_task->ID } = $running_task;
1270 sub handle_task_result {
1271     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1272     my $client_answer = $result->{'answer'};
1273     if( $client_answer =~ s/session_id=(\d+)$// ) {
1274         my $session_id = $1;
1275         if( defined $session_id ) {
1276             my $session_reference = $kernel->ID_id_to_session($session_id);
1277             if( defined $session_reference ) {
1278                 $heap = $session_reference->get_heap();
1279             }
1280         }
1282         if(exists $heap->{'client'}) {
1283             $heap->{'client'}->put($client_answer);
1284         }
1285     }
1286     $kernel->sig(CHLD => "child_reap");
1289 sub handle_task_debug {
1290     my $result = $_[ARG0];
1291     print STDERR "$result\n";
1294 sub handle_task_done {
1295     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1296     delete $heap->{task}->{$task_id};
1299 sub process_task {
1300     no strict "refs";
1301     my ($session, $heap, $task) = @_;
1302     my $error = 0;
1303     my $answer_l;
1304     my ($answer_header, @answer_target_l, $answer_source);
1305     my $client_answer = "";
1307     # prepare all variables needed to process message
1308     #my $msg = $task->{'xmlmessage'};
1309     my $msg = &decode_base64($task->{'xmlmessage'});
1310     my $incoming_id = $task->{'id'};
1311     my $module = $task->{'module'};
1312     my $header =  $task->{'headertag'};
1313     my $session_id = $task->{'sessionid'};
1314     my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1315     my $source = @{$msg_hash->{'source'}}[0];
1316     
1317     # set timestamp of incoming client uptodate, so client will not 
1318     # be deleted from known_clients because of expiration
1319     my $act_time = &get_time();
1320     my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'"; 
1321     my $res = $known_clients_db->exec_statement($sql);
1323     ######################
1324     # process incoming msg
1325     if( $error == 0) {
1326         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1327         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1328         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1330         if ( 0 < @{$answer_l} ) {
1331             my $answer_str = join("\n", @{$answer_l});
1332             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1333                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1334             }
1335             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,8);
1336         } else {
1337             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,8);
1338         }
1340     }
1341     if( !$answer_l ) { $error++ };
1343     ########
1344     # answer
1345     if( $error == 0 ) {
1347         foreach my $answer ( @{$answer_l} ) {
1348             # check outgoing msg to xml validity
1349             my $answer_hash = &check_outgoing_xml_validity($answer);
1350             if( not defined $answer_hash ) { next; }
1351             
1352             $answer_header = @{$answer_hash->{'header'}}[0];
1353             @answer_target_l = @{$answer_hash->{'target'}};
1354             $answer_source = @{$answer_hash->{'source'}}[0];
1356             # deliver msg to all targets 
1357             foreach my $answer_target ( @answer_target_l ) {
1359                 # targets of msg are all gosa-si-clients in known_clients_db
1360                 if( $answer_target eq "*" ) {
1361                     # answer is for all clients
1362                     my $sql_statement= "SELECT * FROM known_clients";
1363                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1364                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1365                         my $host_name = $hit->{hostname};
1366                         my $host_key = $hit->{hostkey};
1367                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1368                         &update_jobdb_status_for_send_msgs($answer, $error);
1369                     }
1370                 }
1372                 # targets of msg are all gosa-si-server in known_server_db
1373                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1374                     # answer is for all server in known_server
1375                     my $sql_statement= "SELECT * FROM $known_server_tn";
1376                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1377                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1378                         my $host_name = $hit->{hostname};
1379                         my $host_key = $hit->{hostkey};
1380                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1381                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1382                         &update_jobdb_status_for_send_msgs($answer, $error);
1383                     }
1384                 }
1386                 # target of msg is GOsa
1387                                 elsif( $answer_target eq "GOSA" ) {
1388                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1389                                         my $add_on = "";
1390                     if( defined $session_id ) {
1391                         $add_on = ".session_id=$session_id";
1392                     }
1393                     # answer is for GOSA and has to returned to connected client
1394                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1395                     $client_answer = $gosa_answer.$add_on;
1396                 }
1398                 # target of msg is job queue at this host
1399                 elsif( $answer_target eq "JOBDB") {
1400                     $answer =~ /<header>(\S+)<\/header>/;   
1401                     my $header;
1402                     if( defined $1 ) { $header = $1; }
1403                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1404                     &update_jobdb_status_for_send_msgs($answer, $error);
1405                 }
1407                 # target of msg is a mac address
1408                 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 ) {
1409                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1410                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1411                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1412                     my $found_ip_flag = 0;
1413                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1414                         my $host_name = $hit->{hostname};
1415                         my $host_key = $hit->{hostkey};
1416                         $answer =~ s/$answer_target/$host_name/g;
1417                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1418                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1419                         &update_jobdb_status_for_send_msgs($answer, $error);
1420                         $found_ip_flag++ ;
1421                     }   
1422                     if( $found_ip_flag == 0) {
1423                         daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1424                     }
1426                 #  answer is for one specific host   
1427                 } else {
1428                     # get encrypt_key
1429                     my $encrypt_key = &get_encrypt_key($answer_target);
1430                     if( not defined $encrypt_key ) {
1431                         # unknown target
1432                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1433                         next;
1434                     }
1435                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1436                     &update_jobdb_status_for_send_msgs($answer, $error);
1437                 }
1438             }
1439         }
1440     }
1442     my $filter = POE::Filter::Reference->new();
1443     my %result = ( 
1444             status => "seems ok to me",
1445             answer => $client_answer,
1446             );
1448     my $output = $filter->put( [ \%result ] );
1449     print @$output;
1454 sub session_start {
1455     my ($kernel) = $_[KERNEL];
1456     $global_kernel = $kernel;
1457     $kernel->yield('register_at_foreign_servers');
1458         $kernel->yield('create_fai_server_db', $fai_server_tn );
1459         $kernel->yield('create_fai_release_db', $fai_release_tn );
1460     $kernel->yield('watch_for_next_tasks');
1461         $kernel->sig(USR1 => "sig_handler");
1462         $kernel->sig(USR2 => "create_packages_list_db");
1463         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1464         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1465         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1466     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1467         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1468     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1473 sub watch_for_done_jobs {
1474     my ($kernel,$heap) = @_[KERNEL, HEAP];
1476     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE status='done'";
1477         my $res = $job_db->select_dbentry( $sql_statement );
1479     while( my ($id, $hit) = each %{$res} ) {
1480         my $jobdb_id = $hit->{id};
1481         my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1482         my $res = $job_db->del_dbentry($sql_statement); 
1483     }
1485     $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1489 sub watch_for_new_jobs {
1490         if($watch_for_new_jobs_in_progress == 0) {
1491                 $watch_for_new_jobs_in_progress = 1;
1492                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1494                 # check gosa job queue for jobs with executable timestamp
1495                 my $timestamp = &get_time();
1496                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1497                 my $res = $job_db->exec_statement( $sql_statement );
1499                 # Merge all new jobs that would do the same actions
1500                 my @drops;
1501                 my $hits;
1502                 foreach my $hit (reverse @{$res} ) {
1503                         my $macaddress= lc @{$hit}[8];
1504                         my $headertag= @{$hit}[5];
1505                         if(
1506                                 defined($hits->{$macaddress}) &&
1507                                 defined($hits->{$macaddress}->{$headertag}) &&
1508                                 defined($hits->{$macaddress}->{$headertag}[0])
1509                         ) {
1510                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1511                         }
1512                         $hits->{$macaddress}->{$headertag}= $hit;
1513                 }
1515                 # Delete new jobs with a matching job in state 'processing'
1516                 foreach my $macaddress (keys %{$hits}) {
1517                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1518                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1519                                 if(defined($jobdb_id)) {
1520                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1521                                         my $res = $job_db->exec_statement( $sql_statement );
1522                                         foreach my $hit (@{$res}) {
1523                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1524                                         }
1525                                 } else {
1526                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1527                                 }
1528                         }
1529                 }
1531                 # Commit deletion
1532                 $job_db->exec_statementlist(\@drops);
1534                 # Look for new jobs that could be executed
1535                 foreach my $macaddress (keys %{$hits}) {
1537                         # Look if there is an executing job
1538                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1539                         my $res = $job_db->exec_statement( $sql_statement );
1541                         # Skip new jobs for host if there is a processing job
1542                         if(defined($res) and defined @{$res}[0]) {
1543                                 next;
1544                         }
1546                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1547                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1548                                 if(defined($jobdb_id)) {
1549                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1551                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1552                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1553                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1555                                         # expect macaddress is unique!!!!!!
1556                                         my $target = $res_hash->{1}->{hostname};
1558                                         # change header
1559                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1561                                         # add sqlite_id
1562                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1564                                         $job_msg =~ /<header>(\S+)<\/header>/;
1565                                         my $header = $1 ;
1566                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1568                                         # update status in job queue to 'processing'
1569                                         $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1570                                         my $res = $job_db->update_dbentry($sql_statement);
1571 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen                                        
1573                                         # We don't want parallel processing
1574                                         last;
1575                                 }
1576                         }
1577                 }
1579                 $watch_for_new_jobs_in_progress = 0;
1580                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1581         }
1585 sub watch_for_new_messages {
1586     my ($kernel,$heap) = @_[KERNEL, HEAP];
1587     my @coll_user_msg;   # collection list of outgoing messages
1588     
1589     # check messaging_db for new incoming messages with executable timestamp
1590     my $timestamp = &get_time();
1591     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1592     my $res = $messaging_db->exec_statement( $sql_statement );
1593         foreach my $hit (@{$res}) {
1595         # create outgoing messages
1596         my $message_to = @{$hit}[3];
1597         # translate message_to to plain login name
1598         my @message_to_l = split(/,/, $message_to);  
1599                 my %receiver_h; 
1600                 foreach my $receiver (@message_to_l) {
1601                         if ($receiver =~ /^u_([\s\S]*)$/) {
1602                                 $receiver_h{$1} = 0;
1603                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1604                                 my $group_name = $1;
1605                                 # fetch all group members from ldap and add them to receiver hash
1606                                 my $ldap_handle = &get_ldap_handle();
1607                                 if (defined $ldap_handle) {
1608                                                 my $mesg = $ldap_handle->search(
1609                                                                                 base => $ldap_base,
1610                                                                                 scope => 'sub',
1611                                                                                 attrs => ['memberUid'],
1612                                                                                 filter => "cn=$group_name",
1613                                                                                 );
1614                                                 if ($mesg->count) {
1615                                                                 my @entries = $mesg->entries;
1616                                                                 foreach my $entry (@entries) {
1617                                                                                 my @receivers= $entry->get_value("memberUid");
1618                                                                                 foreach my $receiver (@receivers) { 
1619                                                                                                 $receiver_h{$1} = 0;
1620                                                                                 }
1621                                                                 }
1622                                                 } 
1623                                                 # translating errors ?
1624                                                 if ($mesg->code) {
1625                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1626                                                 }
1627                                 # ldap handle error ?           
1628                                 } else {
1629                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1630                                 }
1631                         } else {
1632                                 my $sbjct = &encode_base64(@{$hit}[1]);
1633                                 my $msg = &encode_base64(@{$hit}[7]);
1634                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1635                         }
1636                 }
1637                 my @receiver_l = keys(%receiver_h);
1639         my $message_id = @{$hit}[0];
1641         #add each outgoing msg to messaging_db
1642         my $receiver;
1643         foreach $receiver (@receiver_l) {
1644             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1645                 "VALUES ('".
1646                 $message_id."', '".    # id
1647                 @{$hit}[1]."', '".     # subject
1648                 @{$hit}[2]."', '".     # message_from
1649                 $receiver."', '".      # message_to
1650                 "none"."', '".         # flag
1651                 "out"."', '".          # direction
1652                 @{$hit}[6]."', '".     # delivery_time
1653                 @{$hit}[7]."', '".     # message
1654                 $timestamp."'".     # timestamp
1655                 ")";
1656             &daemon_log("M DEBUG: $sql_statement", 1);
1657             my $res = $messaging_db->exec_statement($sql_statement);
1658             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1659         }
1661         # set incoming message to flag d=deliverd
1662         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1663         &daemon_log("M DEBUG: $sql_statement", 7);
1664         $res = $messaging_db->update_dbentry($sql_statement);
1665         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1666     }
1668     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1669     return;
1672 sub watch_for_delivery_messages {
1673     my ($kernel, $heap) = @_[KERNEL, HEAP];
1675     # select outgoing messages
1676     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1677     #&daemon_log("0 DEBUG: $sql", 7);
1678     my $res = $messaging_db->exec_statement( $sql_statement );
1679     
1680     # build out msg for each    usr
1681     foreach my $hit (@{$res}) {
1682         my $receiver = @{$hit}[3];
1683         my $msg_id = @{$hit}[0];
1684         my $subject = @{$hit}[1];
1685         my $message = @{$hit}[7];
1687         # resolve usr -> host where usr is logged in
1688         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1689         #&daemon_log("0 DEBUG: $sql", 7);
1690         my $res = $login_users_db->exec_statement($sql);
1692         # reciver is logged in nowhere
1693         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1695                 my $send_succeed = 0;
1696                 foreach my $hit (@$res) {
1697                                 my $receiver_host = @$hit[0];
1698                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1700                                 # fetch key to encrypt msg propperly for usr/host
1701                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1702                                 &daemon_log("0 DEBUG: $sql", 7);
1703                                 my $res = $known_clients_db->exec_statement($sql);
1705                                 # host is already down
1706                                 if (not ref(@$res[0]) eq "ARRAY") { next; }
1708                                 # host is on
1709                                 my $receiver_key = @{@{$res}[0]}[2];
1710                                 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1711                                 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1712                                 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1713                                 if ($error == 0 ) {
1714                                         $send_succeed++ ;
1715                                 }
1716                 }
1718                 if ($send_succeed) {
1719                                 # set outgoing msg at db to deliverd
1720                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
1721                                 &daemon_log("0 DEBUG: $sql", 7);
1722                                 my $res = $messaging_db->exec_statement($sql); 
1723                 }
1724         }
1726     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
1727     return;
1731 sub watch_for_done_messages {
1732     my ($kernel,$heap) = @_[KERNEL, HEAP];
1734     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
1735     #&daemon_log("0 DEBUG: $sql", 7);
1736     my $res = $messaging_db->exec_statement($sql); 
1738     foreach my $hit (@{$res}) {
1739         my $msg_id = @{$hit}[0];
1741         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
1742         #&daemon_log("0 DEBUG: $sql", 7); 
1743         my $res = $messaging_db->exec_statement($sql);
1745         # not all usr msgs have been seen till now
1746         if ( ref(@$res[0]) eq "ARRAY") { next; }
1747         
1748         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
1749         #&daemon_log("0 DEBUG: $sql", 7);
1750         $res = $messaging_db->exec_statement($sql);
1751     
1752     }
1754     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
1755     return;
1759 sub watch_for_old_known_clients {
1760     my ($kernel,$heap) = @_[KERNEL, HEAP];
1762     my $sql_statement = "SELECT * FROM $known_clients_tn";
1763     my $res = $known_clients_db->select_dbentry( $sql_statement );
1765     my $act_time = int(&get_time());
1767     while ( my ($hit_num, $hit) = each %$res) {
1768         my $expired_timestamp = int($hit->{'timestamp'});
1769         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
1770         my $dt = DateTime->new( year   => $1,
1771                 month  => $2,
1772                 day    => $3,
1773                 hour   => $4,
1774                 minute => $5,
1775                 second => $6,
1776                 );
1778         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
1779         $expired_timestamp = $dt->ymd('').$dt->hms('')."\n";
1780         if ($act_time > $expired_timestamp) {
1781             my $hostname = $hit->{'hostname'};
1782             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
1783             my $del_res = $known_clients_db->exec_statement($del_sql);
1785             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
1786         }
1788     }
1790     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1794 sub watch_for_next_tasks {
1795     my ($kernel,$heap) = @_[KERNEL, HEAP];
1797     my $sql = "SELECT * FROM $incoming_tn";
1798     my $res = $incoming_db->select_dbentry($sql);
1800     while ( my ($hit_num, $hit) = each %$res) {
1801         my $headertag = $hit->{'headertag'};
1802         if ($headertag =~ /^answer_(\d+)/) {
1803             # do not start processing, this message is for a still running POE::Wheel
1804             next;
1805         }
1806         my $message_id = $hit->{'id'};
1807         $kernel->yield('next_task', $hit);
1809         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
1810         my $res = $incoming_db->exec_statement($sql);
1811     }
1813     $kernel->delay_set('watch_for_next_tasks', 0.1); 
1817 sub get_ldap_handle {
1818         my ($session_id) = @_;
1819         my $heap;
1820         my $ldap_handle;
1822         if (not defined $session_id ) { $session_id = 0 };
1823         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1825         if ($session_id == 0) {
1826                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
1827                 $ldap_handle = Net::LDAP->new( $ldap_uri );
1828                 $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!"); 
1830         } else {
1831                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1832                 if( defined $session_reference ) {
1833                         $heap = $session_reference->get_heap();
1834                 }
1836                 if (not defined $heap) {
1837                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
1838                         return;
1839                 }
1841                 # TODO: This "if" is nonsense, because it doesn't prove that the
1842                 #       used handle is still valid - or if we've to reconnect...
1843                 #if (not exists $heap->{ldap_handle}) {
1844                         $ldap_handle = Net::LDAP->new( $ldap_uri );
1845                         $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!"); 
1846                         $heap->{ldap_handle} = $ldap_handle;
1847                 #}
1848         }
1849         return $ldap_handle;
1853 sub change_fai_state {
1854     my ($st, $targets, $session_id) = @_;
1855     $session_id = 0 if not defined $session_id;
1856     # Set FAI state to localboot
1857     my %mapActions= (
1858         reboot    => '',
1859         update    => 'softupdate',
1860         localboot => 'localboot',
1861         reinstall => 'install',
1862         rescan    => '',
1863         wake      => '',
1864         memcheck  => 'memcheck',
1865         sysinfo   => 'sysinfo',
1866         install   => 'install',
1867     );
1869     # Return if this is unknown
1870     if (!exists $mapActions{ $st }){
1871         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
1872       return;
1873     }
1875     my $state= $mapActions{ $st };
1877     my $ldap_handle = &get_ldap_handle($session_id);
1878     if( defined($ldap_handle) ) {
1880       # Build search filter for hosts
1881         my $search= "(&(objectClass=GOhard)";
1882         foreach (@{$targets}){
1883             $search.= "(macAddress=$_)";
1884         }
1885         $search.= ")";
1887       # If there's any host inside of the search string, procress them
1888         if (!($search =~ /macAddress/)){
1889             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
1890             return;
1891         }
1893       # Perform search for Unit Tag
1894       my $mesg = $ldap_handle->search(
1895           base   => $ldap_base,
1896           scope  => 'sub',
1897           attrs  => ['dn', 'FAIstate', 'objectClass'],
1898           filter => "$search"
1899           );
1901           if ($mesg->count) {
1902                   my @entries = $mesg->entries;
1903                   if (0 == @entries) {
1904                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
1905                   }
1907                   foreach my $entry (@entries) {
1908                           # Only modify entry if it is not set to '$state'
1909                           if ($entry->get_value("FAIstate") ne "$state"){
1910                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1911                                   my $result;
1912                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1913                                   if (exists $tmp{'FAIobject'}){
1914                                           if ($state eq ''){
1915                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1916                                                           delete => [ FAIstate => [] ] ]);
1917                                           } else {
1918                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1919                                                           replace => [ FAIstate => $state ] ]);
1920                                           }
1921                                   } elsif ($state ne ''){
1922                                           $result= $ldap_handle->modify($entry->dn, changes => [
1923                                                   add     => [ objectClass => 'FAIobject' ],
1924                                                   add     => [ FAIstate => $state ] ]);
1925                                   }
1927                                   # Errors?
1928                                   if ($result->code){
1929                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1930                                   }
1931                           } else {
1932                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
1933                           }  
1934                   }
1935           } else {
1936                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
1937           }
1939     # if no ldap handle defined
1940     } else {
1941         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
1942     }
1944         return;
1948 sub change_goto_state {
1949     my ($st, $targets, $session_id) = @_;
1950     $session_id = 0  if not defined $session_id;
1952     # Switch on or off?
1953     my $state= $st eq 'active' ? 'active': 'locked';
1955     my $ldap_handle = &get_ldap_handle($session_id);
1956     if( defined($ldap_handle) ) {
1958       # Build search filter for hosts
1959       my $search= "(&(objectClass=GOhard)";
1960       foreach (@{$targets}){
1961         $search.= "(macAddress=$_)";
1962       }
1963       $search.= ")";
1965       # If there's any host inside of the search string, procress them
1966       if (!($search =~ /macAddress/)){
1967         return;
1968       }
1970       # Perform search for Unit Tag
1971       my $mesg = $ldap_handle->search(
1972           base   => $ldap_base,
1973           scope  => 'sub',
1974           attrs  => ['dn', 'gotoMode'],
1975           filter => "$search"
1976           );
1978       if ($mesg->count) {
1979         my @entries = $mesg->entries;
1980         foreach my $entry (@entries) {
1982           # Only modify entry if it is not set to '$state'
1983           if ($entry->get_value("gotoMode") ne $state){
1985             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1986             my $result;
1987             $result= $ldap_handle->modify($entry->dn, changes => [
1988                                                 replace => [ gotoMode => $state ] ]);
1990             # Errors?
1991             if ($result->code){
1992               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1993             }
1995           }
1996         }
1997       } else {
1998                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
1999           }
2001     }
2005 sub run_create_fai_server_db {
2006     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2007     my $session_id = $session->ID;
2008     my $task = POE::Wheel::Run->new(
2009             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2010             StdoutEvent  => "session_run_result",
2011             StderrEvent  => "session_run_debug",
2012             CloseEvent   => "session_run_done",
2013             );
2015     $heap->{task}->{ $task->ID } = $task;
2016     return;
2020 sub create_fai_server_db {
2021     my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2022         my $result;
2024         if (not defined $session_id) { $session_id = 0; }
2025     my $ldap_handle = &get_ldap_handle();
2026         if(defined($ldap_handle)) {
2027                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2028                 my $mesg= $ldap_handle->search(
2029                         base   => $ldap_base,
2030                         scope  => 'sub',
2031                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2032                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2033                 );
2034                 if($mesg->{'resultCode'} == 0 &&
2035                    $mesg->count != 0) {
2036                    foreach my $entry (@{$mesg->{entries}}) {
2037                            if($entry->exists('FAIrepository')) {
2038                                    # Add an entry for each Repository configured for server
2039                                    foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2040                                                    my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2041                                                    my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2042                                                    $result= $fai_server_db->add_dbentry( { 
2043                                                                    table => $table_name,
2044                                                                    primkey => ['server', 'release', 'tag'],
2045                                                                    server => $tmp_url,
2046                                                                    release => $tmp_release,
2047                                                                    sections => $tmp_sections,
2048                                                                    tag => (length($tmp_tag)>0)?$tmp_tag:"",
2049                                                            } );
2050                                            }
2051                                    }
2052                            }
2053                    }
2054                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2056                 # TODO: Find a way to post the 'create_packages_list_db' event
2057                 if(not defined($dont_create_packages_list)) {
2058                         &create_packages_list_db(undef, undef, $session_id);
2059                 }
2060         }       
2061     
2062     $ldap_handle->disconnect;
2063         return $result;
2067 sub run_create_fai_release_db {
2068     my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2069         my $session_id = $session->ID;
2070     my $task = POE::Wheel::Run->new(
2071             Program => sub { &create_fai_release_db($table_name, $session_id) },
2072             StdoutEvent  => "session_run_result",
2073             StderrEvent  => "session_run_debug",
2074             CloseEvent   => "session_run_done",
2075             );
2077     $heap->{task}->{ $task->ID } = $task;
2078     return;
2082 sub create_fai_release_db {
2083         my ($table_name, $session_id) = @_;
2084         my $result;
2086     # used for logging
2087     if (not defined $session_id) { $session_id = 0; }
2089     my $ldap_handle = &get_ldap_handle();
2090         if(defined($ldap_handle)) {
2091                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2092                 my $mesg= $ldap_handle->search(
2093                         base   => $ldap_base,
2094                         scope  => 'sub',
2095                         attrs  => [],
2096                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2097                 );
2098                 if($mesg->{'resultCode'} == 0 &&
2099                         $mesg->count != 0) {
2100                         # Walk through all possible FAI container ou's
2101                         my @sql_list;
2102                         my $timestamp= &get_time();
2103                         foreach my $ou (@{$mesg->{entries}}) {
2104                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2105                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2106                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2107                                         if(@tmp_array) {
2108                                                 foreach my $entry (@tmp_array) {
2109                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2110                                                                 my $sql= 
2111                                                                 "INSERT INTO $table_name "
2112                                                                 ."(timestamp, release, class, type, state) VALUES ("
2113                                                                 .$timestamp.","
2114                                                                 ."'".$entry->{'release'}."',"
2115                                                                 ."'".$entry->{'class'}."',"
2116                                                                 ."'".$entry->{'type'}."',"
2117                                                                 ."'".$entry->{'state'}."')";
2118                                                                 push @sql_list, $sql;
2119                                                         }
2120                                                 }
2121                                         }
2122                                 }
2123                         }
2125                         daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2126                         if(@sql_list) {
2127                                 unshift @sql_list, "VACUUM";
2128                                 unshift @sql_list, "DELETE FROM $table_name";
2129                                 $fai_release_db->exec_statementlist(\@sql_list);
2130                         }
2131                         daemon_log("$session_id DEBUG: Done with inserting",7);
2132                 }
2133                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2134         }
2135     $ldap_handle->disconnect;
2136         return $result;
2139 sub get_fai_types {
2140         my $tmp_classes = shift || return undef;
2141         my @result;
2143         foreach my $type(keys %{$tmp_classes}) {
2144                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2145                         my $entry = {
2146                                 type => $type,
2147                                 state => $tmp_classes->{$type}[0],
2148                         };
2149                         push @result, $entry;
2150                 }
2151         }
2153         return @result;
2156 sub get_fai_state {
2157         my $result = "";
2158         my $tmp_classes = shift || return $result;
2160         foreach my $type(keys %{$tmp_classes}) {
2161                 if(defined($tmp_classes->{$type}[0])) {
2162                         $result = $tmp_classes->{$type}[0];
2163                         
2164                 # State is equal for all types in class
2165                         last;
2166                 }
2167         }
2169         return $result;
2172 sub resolve_fai_classes {
2173         my ($fai_base, $ldap_handle, $session_id) = @_;
2174         if (not defined $session_id) { $session_id = 0; }
2175         my $result;
2176         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2177         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2178         my $fai_classes;
2180         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2181         my $mesg= $ldap_handle->search(
2182                 base   => $fai_base,
2183                 scope  => 'sub',
2184                 attrs  => ['cn','objectClass','FAIstate'],
2185                 filter => $fai_filter,
2186         );
2187         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2189         if($mesg->{'resultCode'} == 0 &&
2190                 $mesg->count != 0) {
2191                 foreach my $entry (@{$mesg->{entries}}) {
2192                         if($entry->exists('cn')) {
2193                                 my $tmp_dn= $entry->dn();
2195                                 # Skip classname and ou dn parts for class
2196                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2198                                 # Skip classes without releases
2199                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2200                                         next;
2201                                 }
2203                                 my $tmp_cn= $entry->get_value('cn');
2204                                 my $tmp_state= $entry->get_value('FAIstate');
2206                                 my $tmp_type;
2207                                 # Get FAI type
2208                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2209                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2210                                                 $tmp_type= $oclass;
2211                                                 last;
2212                                         }
2213                                 }
2215                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2216                                         # A Subrelease
2217                                         my @sub_releases = split(/,/, $tmp_release);
2219                                         # Walk through subreleases and build hash tree
2220                                         my $hash;
2221                                         while(my $tmp_sub_release = pop @sub_releases) {
2222                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2223                                         }
2224                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2225                                 } else {
2226                                         # A branch, no subrelease
2227                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2228                                 }
2229                         } elsif (!$entry->exists('cn')) {
2230                                 my $tmp_dn= $entry->dn();
2231                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2233                                 # Skip classes without releases
2234                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2235                                         next;
2236                                 }
2238                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2239                                         # A Subrelease
2240                                         my @sub_releases= split(/,/, $tmp_release);
2242                                         # Walk through subreleases and build hash tree
2243                                         my $hash;
2244                                         while(my $tmp_sub_release = pop @sub_releases) {
2245                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2246                                         }
2247                                         # Remove the last two characters
2248                                         chop($hash);
2249                                         chop($hash);
2251                                         eval('$fai_classes->'.$hash.'= {}');
2252                                 } else {
2253                                         # A branch, no subrelease
2254                                         if(!exists($fai_classes->{$tmp_release})) {
2255                                                 $fai_classes->{$tmp_release} = {};
2256                                         }
2257                                 }
2258                         }
2259                 }
2261                 # The hash is complete, now we can honor the copy-on-write based missing entries
2262                 foreach my $release (keys %$fai_classes) {
2263                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2264                 }
2265         }
2266         return $result;
2269 sub apply_fai_inheritance {
2270        my $fai_classes = shift || return {};
2271        my $tmp_classes;
2273        # Get the classes from the branch
2274        foreach my $class (keys %{$fai_classes}) {
2275                # Skip subreleases
2276                if($class =~ /^ou=.*$/) {
2277                        next;
2278                } else {
2279                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2280                }
2281        }
2283        # Apply to each subrelease
2284        foreach my $subrelease (keys %{$fai_classes}) {
2285                if($subrelease =~ /ou=/) {
2286                        foreach my $tmp_class (keys %{$tmp_classes}) {
2287                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2288                                        $fai_classes->{$subrelease}->{$tmp_class} =
2289                                        deep_copy($tmp_classes->{$tmp_class});
2290                                } else {
2291                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2292                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2293                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2294                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2295                                                }
2296                                        }
2297                                }
2298                        }
2299                }
2300        }
2302        # Find subreleases in deeper levels
2303        foreach my $subrelease (keys %{$fai_classes}) {
2304                if($subrelease =~ /ou=/) {
2305                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2306                                if($subsubrelease =~ /ou=/) {
2307                                        apply_fai_inheritance($fai_classes->{$subrelease});
2308                                }
2309                        }
2310                }
2311        }
2313        return $fai_classes;
2316 sub get_fai_release_entries {
2317         my $tmp_classes = shift || return;
2318         my $parent = shift || "";
2319         my @result = shift || ();
2321         foreach my $entry (keys %{$tmp_classes}) {
2322                 if(defined($entry)) {
2323                         if($entry =~ /^ou=.*$/) {
2324                                 my $release_name = $entry;
2325                                 $release_name =~ s/ou=//g;
2326                                 if(length($parent)>0) {
2327                                         $release_name = $parent."/".$release_name;
2328                                 }
2329                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2330                                 foreach my $bufentry(@bufentries) {
2331                                         push @result, $bufentry;
2332                                 }
2333                         } else {
2334                                 my @types = get_fai_types($tmp_classes->{$entry});
2335                                 foreach my $type (@types) {
2336                                         push @result, 
2337                                         {
2338                                                 'class' => $entry,
2339                                                 'type' => $type->{'type'},
2340                                                 'release' => $parent,
2341                                                 'state' => $type->{'state'},
2342                                         };
2343                                 }
2344                         }
2345                 }
2346         }
2348         return @result;
2351 sub deep_copy {
2352         my $this = shift;
2353         if (not ref $this) {
2354                 $this;
2355         } elsif (ref $this eq "ARRAY") {
2356                 [map deep_copy($_), @$this];
2357         } elsif (ref $this eq "HASH") {
2358                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2359         } else { die "what type is $_?" }
2363 sub session_run_result {
2364     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2365     $kernel->sig(CHLD => "child_reap");
2368 sub session_run_debug {
2369     my $result = $_[ARG0];
2370     print STDERR "$result\n";
2373 sub session_run_done {
2374     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2375     delete $heap->{task}->{$task_id};
2379 sub create_sources_list {
2380         my $session_id = shift;
2381         my $ldap_handle = &main::get_ldap_handle;
2382         my $result="/tmp/gosa_si_tmp_sources_list";
2384         # Remove old file
2385         if(stat($result)) {
2386                 unlink($result);
2387                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2388         }
2390         my $fh;
2391         open($fh, ">$result");
2392         if (not defined $fh) {
2393                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2394                 return undef;
2395         }
2396         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2397                 my $mesg=$ldap_handle->search(
2398                         base    => $main::ldap_server_dn,
2399                         scope   => 'base',
2400                         attrs   => 'FAIrepository',
2401                         filter  => 'objectClass=FAIrepositoryServer'
2402                 );
2403                 if($mesg->count) {
2404                         foreach my $entry(@{$mesg->{'entries'}}) {
2405                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2406                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2407                                         my $line = "deb $server $release";
2408                                         $sections =~ s/,/ /g;
2409                                         $line.= " $sections";
2410                                         print $fh $line."\n";
2411                                 }
2412                         }
2413                 }
2414         } else {
2415                 if (defined $main::ldap_server_dn){
2416                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2417                 } else {
2418                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2419                 }
2420         }
2421         close($fh);
2423         return $result;
2427 sub run_create_packages_list_db {
2428     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2429         my $session_id = $session->ID;
2431         my $task = POE::Wheel::Run->new(
2432                                         Priority => +20,
2433                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2434                                         StdoutEvent  => "session_run_result",
2435                                         StderrEvent  => "session_run_debug",
2436                                         CloseEvent   => "session_run_done",
2437                                         );
2438         $heap->{task}->{ $task->ID } = $task;
2442 sub create_packages_list_db {
2443         my ($ldap_handle, $sources_file, $session_id) = @_;
2444         
2445         # it should not be possible to trigger a recreation of packages_list_db
2446         # while packages_list_db is under construction, so set flag packages_list_under_construction
2447         # which is tested befor recreation can be started
2448         if (-r $packages_list_under_construction) {
2449                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2450                 return;
2451         } else {
2452                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2453                 # set packages_list_under_construction to true
2454                 system("touch $packages_list_under_construction");
2455                 @packages_list_statements=();
2456         }
2458         if (not defined $session_id) { $session_id = 0; }
2459         if (not defined $ldap_handle) { 
2460                 $ldap_handle= &get_ldap_handle();
2462                 if (not defined $ldap_handle) {
2463                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2464                         unlink($packages_list_under_construction);
2465                         return;
2466                 }
2467         }
2468         if (not defined $sources_file) { 
2469                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2470                 $sources_file = &create_sources_list($session_id);
2471         }
2473         if (not defined $sources_file) {
2474                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2475                 unlink($packages_list_under_construction);
2476                 return;
2477         }
2479         my $line;
2481         open(CONFIG, "<$sources_file") or do {
2482                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2483                 unlink($packages_list_under_construction);
2484                 return;
2485         };
2487         # Read lines
2488         while ($line = <CONFIG>){
2489                 # Unify
2490                 chop($line);
2491                 $line =~ s/^\s+//;
2492                 $line =~ s/^\s+/ /;
2494                 # Strip comments
2495                 $line =~ s/#.*$//g;
2497                 # Skip empty lines
2498                 if ($line =~ /^\s*$/){
2499                         next;
2500                 }
2502                 # Interpret deb line
2503                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2504                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2505                         my $section;
2506                         foreach $section (split(' ', $sections)){
2507                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2508                         }
2509                 }
2510         }
2512         close (CONFIG);
2514         find(\&cleanup_and_extract, keys( %repo_dirs ));
2515         &main::strip_packages_list_statements();
2516         unshift @packages_list_statements, "VACUUM";
2517         $packages_list_db->exec_statementlist(\@packages_list_statements);
2518         unlink($packages_list_under_construction);
2519         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2520         return;
2523 # This function should do some intensive task to minimize the db-traffic
2524 sub strip_packages_list_statements {
2525     my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2526         my @new_statement_list=();
2527         my $hash;
2528         my $insert_hash;
2529         my $update_hash;
2530         my $delete_hash;
2531         my $local_timestamp=get_time();
2533         foreach my $existing_entry (@existing_entries) {
2534                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2535         }
2537         foreach my $statement (@packages_list_statements) {
2538                 if($statement =~ /^INSERT/i) {
2539                         # Assign the values from the insert statement
2540                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2541                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2542                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2543                                 # If section or description has changed, update the DB
2544                                 if( 
2545                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2546                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2547                                 ) {
2548                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2549                                 }
2550                         } else {
2551                                 # Insert a non-existing entry to db
2552                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2553                         }
2554                 } elsif ($statement =~ /^UPDATE/i) {
2555                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2556                         /^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;
2557                         foreach my $distribution (keys %{$hash}) {
2558                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2559                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2560                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2561                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2562                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2563                                                 my $section;
2564                                                 my $description;
2565                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2566                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2567                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2568                                                 }
2569                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2570                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2571                                                 }
2572                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2573                                         }
2574                                 }
2575                         }
2576                 }
2577         }
2579         # TODO: Check for orphaned entries
2581         # unroll the insert_hash
2582         foreach my $distribution (keys %{$insert_hash}) {
2583                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2584                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2585                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2586                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2587                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2588                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2589                                 ."'$local_timestamp')";
2590                         }
2591                 }
2592         }
2594         # unroll the update hash
2595         foreach my $distribution (keys %{$update_hash}) {
2596                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2597                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2598                                 my $set = "";
2599                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2600                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2601                                 }
2602                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2603                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2604                                 }
2605                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2606                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2607                                 }
2608                                 if(defined($set) and length($set) > 0) {
2609                                         $set .= "timestamp = '$local_timestamp'";
2610                                 } else {
2611                                         next;
2612                                 }
2613                                 push @new_statement_list, 
2614                                         "UPDATE $main::packages_list_tn SET $set WHERE"
2615                                         ." distribution = '$distribution'"
2616                                         ." AND package = '$package'"
2617                                         ." AND version = '$version'";
2618                         }
2619                 }
2620         }
2622         @packages_list_statements = @new_statement_list;
2626 sub parse_package_info {
2627     my ($baseurl, $dist, $section, $session_id)= @_;
2628     my ($package);
2629     if (not defined $session_id) { $session_id = 0; }
2630     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2631     $repo_dirs{ "${repo_path}/pool" } = 1;
2633     foreach $package ("Packages.gz"){
2634         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2635         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2636         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2637     }
2638     
2642 sub get_package {
2643     my ($url, $dest, $session_id)= @_;
2644     if (not defined $session_id) { $session_id = 0; }
2646     my $tpath = dirname($dest);
2647     -d "$tpath" || mkpath "$tpath";
2649     # This is ugly, but I've no time to take a look at "how it works in perl"
2650     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2651         system("gunzip -cd '$dest' > '$dest.in'");
2652         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2653         unlink($dest);
2654         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
2655     } else {
2656         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2657     }
2658     return 0;
2662 sub parse_package {
2663     my ($path, $dist, $srv_path, $session_id)= @_;
2664     if (not defined $session_id) { $session_id = 0;}
2665     my ($package, $version, $section, $description);
2666     my $PACKAGES;
2667     my $timestamp = &get_time();
2669     if(not stat("$path.in")) {
2670         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2671         return;
2672     }
2674     open($PACKAGES, "<$path.in");
2675     if(not defined($PACKAGES)) {
2676         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
2677         return;
2678     }
2680     # Read lines
2681     while (<$PACKAGES>){
2682         my $line = $_;
2683         # Unify
2684         chop($line);
2686         # Use empty lines as a trigger
2687         if ($line =~ /^\s*$/){
2688             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2689             push(@packages_list_statements, $sql);
2690             $package = "none";
2691             $version = "none";
2692             $section = "none";
2693             $description = "none"; 
2694             next;
2695         }
2697         # Trigger for package name
2698         if ($line =~ /^Package:\s/){
2699             ($package)= ($line =~ /^Package: (.*)$/);
2700             next;
2701         }
2703         # Trigger for version
2704         if ($line =~ /^Version:\s/){
2705             ($version)= ($line =~ /^Version: (.*)$/);
2706             next;
2707         }
2709         # Trigger for description
2710         if ($line =~ /^Description:\s/){
2711             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2712             next;
2713         }
2715         # Trigger for section
2716         if ($line =~ /^Section:\s/){
2717             ($section)= ($line =~ /^Section: (.*)$/);
2718             next;
2719         }
2721         # Trigger for filename
2722         if ($line =~ /^Filename:\s/){
2723             my ($filename) = ($line =~ /^Filename: (.*)$/);
2724             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2725             next;
2726         }
2727     }
2729     close( $PACKAGES );
2730     unlink( "$path.in" );
2734 sub store_fileinfo {
2735     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2737     my %fileinfo = (
2738         'package' => $package,
2739         'dist' => $dist,
2740         'version' => $vers,
2741     );
2743     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2747 sub cleanup_and_extract {
2748     my $fileinfo = $repo_files{ $File::Find::name };
2750     if( defined $fileinfo ) {
2752         my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2753         my $sql;
2754         my $package = $fileinfo->{ 'package' };
2755         my $newver = $fileinfo->{ 'version' };
2757         mkpath($dir);
2758         system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2760                 if( -f "$dir/DEBIAN/templates" ) {
2762                         daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2764                         my $tmpl= "";
2765                         {
2766                                 local $/=undef;
2767                                 open FILE, "$dir/DEBIAN/templates";
2768                                 $tmpl = &encode_base64(<FILE>);
2769                                 close FILE;
2770                         }
2771                         rmtree("$dir/DEBIAN/templates");
2773                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2774                 push @packages_list_statements, $sql;
2775                 }
2776     }
2778     return;
2782 sub register_at_foreign_servers {   
2783     my ($kernel) = $_[KERNEL];
2785     # hole alle bekannten server aus known_server_db
2786     my $server_sql = "SELECT * FROM $known_server_tn";
2787     my $server_res = $known_server_db->exec_statement($server_sql);
2789     # no entries in known_server_db
2790     if (not ref(@$server_res[0]) eq "ARRAY") { 
2791         # TODO
2792     }
2794     # detect already connected clients
2795     my $client_sql = "SELECT * FROM $known_clients_tn"; 
2796     my $client_res = $known_clients_db->exec_statement($client_sql);
2798     # send my server details to all other gosa-si-server within the network
2799     foreach my $hit (@$server_res) {
2800         my $hostname = @$hit[0];
2801         my $hostkey = &create_passwd;
2803         # add already connected clients to registration message 
2804         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
2805         &add_content2xml_hash($myhash, 'key', $hostkey);
2806         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
2807         
2808         # build registration message and send it
2809         my $foreign_server_msg = &create_xml_string($myhash);
2810         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
2811     }
2812     
2813     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
2814     return;
2818 #==== MAIN = main ==============================================================
2819 #  parse commandline options
2820 Getopt::Long::Configure( "bundling" );
2821 GetOptions("h|help" => \&usage,
2822         "c|config=s" => \$cfg_file,
2823         "f|foreground" => \$foreground,
2824         "v|verbose+" => \$verbose,
2825         "no-arp+" => \$no_arp,
2826            );
2828 #  read and set config parameters
2829 &check_cmdline_param ;
2830 &read_configfile;
2831 &check_pid;
2833 $SIG{CHLD} = 'IGNORE';
2835 # forward error messages to logfile
2836 if( ! $foreground ) {
2837   open( STDIN,  '+>/dev/null' );
2838   open( STDOUT, '+>&STDIN'    );
2839   open( STDERR, '+>&STDIN'    );
2842 # Just fork, if we are not in foreground mode
2843 if( ! $foreground ) { 
2844     chdir '/'                 or die "Can't chdir to /: $!";
2845     $pid = fork;
2846     setsid                    or die "Can't start a new session: $!";
2847     umask 0;
2848 } else { 
2849     $pid = $$; 
2852 # Do something useful - put our PID into the pid_file
2853 if( 0 != $pid ) {
2854     open( LOCK_FILE, ">$pid_file" );
2855     print LOCK_FILE "$pid\n";
2856     close( LOCK_FILE );
2857     if( !$foreground ) { 
2858         exit( 0 ) 
2859     };
2862 # parse head url and revision from svn
2863 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2864 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2865 $server_headURL = defined $1 ? $1 : 'unknown' ;
2866 $server_revision = defined $2 ? $2 : 'unknown' ;
2867 if ($server_headURL =~ /\/tag\// || 
2868         $server_headURL =~ /\/branches\// ) {
2869     $server_status = "stable"; 
2870 } else {
2871     $server_status = "developmental" ;
2875 daemon_log(" ", 1);
2876 daemon_log("$0 started!", 1);
2877 daemon_log("status: $server_status", 1);
2878 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
2880 # connect to incoming_db
2881 unlink($incoming_file_name);
2882 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
2883 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
2885 # connect to gosa-si job queue
2886 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2887 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2889 # connect to known_clients_db
2890 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2891 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2893 # connect to foreign_clients_db
2894 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
2895 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
2897 # connect to known_server_db
2898 unlink($known_server_file_name);
2899 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2900 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2902 # connect to login_usr_db
2903 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2904 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2906 # connect to fai_server_db and fai_release_db
2907 unlink($fai_server_file_name);
2908 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2909 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2911 unlink($fai_release_file_name);
2912 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2913 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2915 # connect to packages_list_db
2916 #unlink($packages_list_file_name);
2917 unlink($packages_list_under_construction);
2918 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2919 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2921 # connect to messaging_db
2922 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2923 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2926 # create xml object used for en/decrypting
2927 $xml = new XML::Simple();
2930 # foreign servers 
2931 my @foreign_server_list;
2933 # add foreign server from cfg file
2934 if ($foreign_server_string ne "") {
2935     my @cfg_foreign_server_list = split(",", $foreign_server_string);
2936     foreach my $foreign_server (@cfg_foreign_server_list) {
2937         push(@foreign_server_list, $foreign_server);
2938     }
2941 # add foreign server from dns
2942 my @tmp_servers;
2943 if ( !$server_domain) {
2944     # Try our DNS Searchlist
2945     for my $domain(get_dns_domains()) {
2946         chomp($domain);
2947         my @tmp_domains= &get_server_addresses($domain);
2948         if(@tmp_domains) {
2949             for my $tmp_server(@tmp_domains) {
2950                 push @tmp_servers, $tmp_server;
2951             }
2952         }
2953     }
2954     if(@tmp_servers && length(@tmp_servers)==0) {
2955         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
2956     }
2957 } else {
2958     @tmp_servers = &get_server_addresses($server_domain);
2959     if( 0 == @tmp_servers ) {
2960         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
2961     }
2963 foreach my $server (@tmp_servers) { 
2964     unshift(@foreign_server_list, $server); 
2966 # eliminate duplicate entries
2967 @foreign_server_list = &del_doubles(@foreign_server_list);
2968 my $all_foreign_server = join(", ", @foreign_server_list);
2969 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
2971 # add all found foreign servers to known_server
2972 my $act_timestamp = &get_time();
2973 foreach my $foreign_server (@foreign_server_list) {
2975         #######################################
2976         # TODO for jan
2977         # do not add myself to known_server_db
2978         # work around!!!
2979         if ($foreign_server eq '172.16.2.89:20081') { next; }
2980         ######################################
2982     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
2983             primkey=>['hostname'],
2984             hostname=>$foreign_server,
2985             status=>'not_jet_registered',
2986             hostkey=>"none",
2987             timestamp=>$act_timestamp,
2988             } );
2992 POE::Component::Server::TCP->new(
2993     Alias => "TCP_SERVER",
2994         Port => $server_port,
2995         ClientInput => sub {
2996         my ($kernel, $input) = @_[KERNEL, ARG0];
2997         push(@tasks, $input);
2998         push(@msgs_to_decrypt, $input);
2999         $kernel->yield("msg_to_decrypt");
3000         },
3001     InlineStates => {
3002         msg_to_decrypt => \&msg_to_decrypt,
3003         next_task => \&next_task,
3004         task_result => \&handle_task_result,
3005         task_done   => \&handle_task_done,
3006         task_debug  => \&handle_task_debug,
3007         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3008     }
3009 );
3011 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
3013 # create session for repeatedly checking the job queue for jobs
3014 POE::Session->create(
3015         inline_states => {
3016                 _start => \&session_start,
3017         register_at_foreign_servers => \&register_at_foreign_servers,
3018         sig_handler => \&sig_handler,
3019         next_task => \&next_task,
3020         task_result => \&handle_task_result,
3021         task_done   => \&handle_task_done,
3022         task_debug  => \&handle_task_debug,
3023         watch_for_next_tasks => \&watch_for_next_tasks,
3024         watch_for_new_messages => \&watch_for_new_messages,
3025         watch_for_delivery_messages => \&watch_for_delivery_messages,
3026         watch_for_done_messages => \&watch_for_done_messages,
3027                 watch_for_new_jobs => \&watch_for_new_jobs,
3028         watch_for_done_jobs => \&watch_for_done_jobs,
3029         watch_for_old_known_clients => \&watch_for_old_known_clients,
3030         create_packages_list_db => \&run_create_packages_list_db,
3031         create_fai_server_db => \&run_create_fai_server_db,
3032         create_fai_release_db => \&run_create_fai_release_db,
3033         session_run_result => \&session_run_result,
3034         session_run_debug => \&session_run_debug,
3035         session_run_done => \&session_run_done,
3036         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3037         }
3038 );
3041 # import all modules
3042 &import_modules;
3044 # TODO
3045 # check wether all modules are gosa-si valid passwd check
3049 POE::Kernel->run();
3050 exit;