Code

* bugfix: gosaTriggered.pm: all functions work now
[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 ($bus_activ, $bus, $msg_to_bus, $bus_cipher);
72 my ($server);
73 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
74 my ($messaging_db_loop_delay);
75 my ($known_modules);
76 my ($procid, $pid);
77 my ($arp_fifo);
78 my ($xml);
79 my $sources_list;
80 my $max_clients;
81 my %repo_files=();
82 my $repo_path;
83 my %repo_dirs=();
84 # variables declared in config file are always set to 'our'
85 our (%cfg_defaults, $log_file, $pid_file, 
86     $server_ip, $server_port, $ClientPackages_key, 
87     $arp_activ, $gosa_unit_tag,
88     $GosaPackages_key, $gosa_ip, $gosa_port, $gosa_timeout,
89     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
90 );
92 # additional variable which should be globaly accessable
93 our $server_address;
94 our $server_mac_address;
95 our $bus_address;
96 our $gosa_address;
97 our $no_bus;
98 our $no_arp;
99 our $verbose;
100 our $forground;
101 our $cfg_file;
102 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
105 # specifies the verbosity of the daemon_log
106 $verbose = 0 ;
108 # if foreground is not null, script will be not forked to background
109 $foreground = 0 ;
111 # specifies the timeout seconds while checking the online status of a registrating client
112 $ping_timeout = 5;
114 $no_bus = 0;
115 $bus_activ = "true";
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-sd as well as the gosa-sd-bus
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 "bus" => {
215     "activ" => [\$bus_activ, "true"],
216     },
217 "server" => {
218     "port" => [\$server_port, "20081"],
219     "known-clients"        => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
220     "known-servers"        => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
221     "incoming"             => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
222     "login-users"          => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
223     "fai-server"           => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
224     "fai-release"          => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
225     "packages-list"        => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
226     "messaging"            => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
227     "foreign-clients"      => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
228     "source-list"          => [\$sources_list, '/etc/apt/sources.list'],
229     "repo-path"            => [\$repo_path, '/srv/www/repository'],
230     "ldap-uri"             => [\$ldap_uri, ""],
231     "ldap-base"            => [\$ldap_base, ""],
232     "ldap-admin-dn"        => [\$ldap_admin_dn, ""],
233     "ldap-admin-password"  => [\$ldap_admin_password, ""],
234     "gosa-unit-tag"        => [\$gosa_unit_tag, ""],
235     "max-clients"          => [\$max_clients, 10],
236     },
237 "GOsaPackages" => {
238     "ip" => [\$gosa_ip, "0.0.0.0"],
239     "port" => [\$gosa_port, "20082"],
240     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
241     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
242     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
243     "key" => [\$GosaPackages_key, "none"],
244     },
245 "ClientPackages" => {
246     "key" => [\$ClientPackages_key, "none"],
247     },
248 "ServerPackages"=> {
249     "address"      => [\$foreign_server_string, ""],
250     "domain"  => [\$server_domain, ""],
251     "key"     => [\$ServerPackages_key, "none"],
252     "key-lifetime" => [\$foreign_servers_register_delay, 120],
254 );
257 #===  FUNCTION  ================================================================
258 #         NAME:  usage
259 #   PARAMETERS:  nothing
260 #      RETURNS:  nothing
261 #  DESCRIPTION:  print out usage text to STDERR
262 #===============================================================================
263 sub usage {
264     print STDERR << "EOF" ;
265 usage: $prg [-hvf] [-c config]
267            -h        : this (help) message
268            -c <file> : config file
269            -f        : foreground, process will not be forked to background
270            -v        : be verbose (multiple to increase verbosity)
271            -no-bus   : starts $prg without connection to bus
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 or gosa-si-bus
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;
1066     # check whether this message should be processed here
1067     if ($error == 0) {
1068         $header = @{$msg_hash->{'header'}}[0];
1069         $target = @{$msg_hash->{'target'}}[0];
1070         $source = @{$msg_hash->{'source'}}[0];
1071         my ($target_ip, $target_port) = split(':', $target);
1072                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1073                         my $server_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1074                 }
1076         # target and source is equal to GOSA -> process here
1077         if (not $done) {
1078             if ($target eq "GOSA" && $source eq "GOSA") {
1079                 $done = 1;                    
1080             }
1081         }
1083         # target is own address without forward_to_gosa-tag -> process here
1084         if (not $done) {
1085             if (($target eq $server_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1086                 $done = 1;
1087                 if ($source eq "GOSA") {
1088                     $msg =~ s/<\/xml>/<forward_to_gosa>$server_address,$session_id<\/forward_to_gosa><\/xml>/;
1089                 }
1090                 print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
1091             }
1092         }
1094         # target is a client address in known_clients -> process here
1095         if (not $done) {
1096             $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1097             $res = $known_clients_db->select_dbentry($sql);
1098             if (keys(%$res) > 0) {
1099                 $done = 1; 
1100                 my $hostname = $res->{1}->{'hostname'};
1101                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1102                 print STDERR "target is a client address in known_clients -> process here\n";
1103             }
1104         }
1106         # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1107         if (not $done) {
1108             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1109             my $gosa_at;
1110             my $gosa_session_id;
1111             if (($target eq $server_address) && (defined $forward_to_gosa)){
1112                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1113                 if ($gosa_at ne $server_address) {
1114                     $done = 1;
1115                     print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n"; 
1116                 }
1117             }
1118         }
1120         # if message should be processed here -> add message to incoming_db
1121         if ($done) {
1123             # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1124             # so gosa-si-server knows how to process this kind of messages
1125             if ($header =~ /^gosa_/ || $header =~ /job_/) {
1126                 $module = "GosaPackages";
1127             }
1129             my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1130                     primkey=>[],
1131                     headertag=>$header,
1132                     targettag=>$target,
1133                     xmlmessage=>&encode_base64($msg),
1134                     timestamp=>&get_time,
1135                     module=>$module,
1136                     sessionid=>$session_id,
1137                     } );
1139         }
1141         # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1142         if (not $done) {
1143             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1144             my $gosa_at;
1145             my $gosa_session_id;
1146             if (($target eq $server_address) && (defined $forward_to_gosa)){
1147                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1148                 if ($gosa_at eq $server_address) {
1149                     my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1150                     if( defined $session_reference ) {
1151                         $heap = $session_reference->get_heap();
1152                     }
1153                     if(exists $heap->{'client'}) {
1154                         $msg = &encrypt_msg($msg, $GosaPackages_key);
1155                         $heap->{'client'}->put($msg);
1156                     }
1157                     $done = 1;
1158                     print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
1159                 }
1160             }
1162         }
1164         # target is a client address in foreign_clients -> forward to registration server
1165         if (not $done) {
1166             $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1167             $res = $foreign_clients_db->select_dbentry($sql);
1168             if (keys(%$res) > 0) {
1169                 my $hostname = $res->{1}->{'hostname'};
1170                 my $regserver = $res->{1}->{'regserver'};
1171                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1172                 my $res = $known_server_db->select_dbentry($sql);
1173                 if (keys(%$res) > 0) {
1174                     my $regserver_key = $res->{1}->{'hostkey'};
1175                     $msg =~ s/<source>GOSA<\/source>/<source>$server_address<\/source>/;
1176                     $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1177                     if ($source eq "GOSA") {
1178                         $msg =~ s/<\/xml>/<forward_to_gosa>$server_address,$session_id<\/forward_to_gosa><\/xml>/;
1179                     }
1180                     &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1181                 }
1182                 $done = 1;
1183                 print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
1184             }
1185         }
1187         # target is a server address -> forward to server
1188         if (not $done) {
1189             $sql = "SELECT * FROM $known_server_tn WHERE hostname='$target'";
1190             $res = $known_server_db->select_dbentry($sql);
1191             if (keys(%$res) > 0) {
1192                 my $hostkey = $res->{1}->{'hostkey'};
1194                 if ($source eq "GOSA") {
1195                     $msg =~ s/<source>GOSA<\/source>/<source>$server_address<\/source>/;
1196                     $msg =~ s/<\/xml>/<forward_to_gosa>$server_address,$session_id<\/forward_to_gosa><\/xml>/;
1198                 }
1200                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1201                 $done = 1;
1202                 print STDERR "target is a server address -> forward to server\n";
1203             }
1206         }
1208         if (not $done) {
1209             daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1210         }
1211     }
1213     return;
1217 sub next_task {
1218     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1219     my $running_task = POE::Wheel::Run->new(
1220             Program => sub { process_task($session, $heap, $task) },
1221             StdioFilter => POE::Filter::Reference->new(),
1222             StdoutEvent  => "task_result",
1223             StderrEvent  => "task_debug",
1224             CloseEvent   => "task_done",
1225             );
1226     $heap->{task}->{ $running_task->ID } = $running_task;
1229 sub handle_task_result {
1230     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1231     my $client_answer = $result->{'answer'};
1232     if( $client_answer =~ s/session_id=(\d+)$// ) {
1233         my $session_id = $1;
1234         if( defined $session_id ) {
1235             my $session_reference = $kernel->ID_id_to_session($session_id);
1236             if( defined $session_reference ) {
1237                 $heap = $session_reference->get_heap();
1238             }
1239         }
1241         if(exists $heap->{'client'}) {
1242             $heap->{'client'}->put($client_answer);
1243         }
1244     }
1245     $kernel->sig(CHLD => "child_reap");
1248 sub handle_task_debug {
1249     my $result = $_[ARG0];
1250     print STDERR "$result\n";
1253 sub handle_task_done {
1254     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1255     delete $heap->{task}->{$task_id};
1258 sub process_task {
1259     no strict "refs";
1260     my ($session, $heap, $task) = @_;
1261     my $error = 0;
1262     my $answer_l;
1263     my ($answer_header, @answer_target_l, $answer_source);
1264     my $client_answer = "";
1266     # prepare all variables needed to process message
1267     #my $msg = $task->{'xmlmessage'};
1268     my $msg = &decode_base64($task->{'xmlmessage'});
1269     my $incoming_id = $task->{'id'};
1270     my $module = $task->{'module'};
1271     my $header =  $task->{'headertag'};
1272     my $session_id = $task->{'sessionid'};
1273     my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1274     my $source = @{$msg_hash->{'source'}}[0];
1275     
1276     # set timestamp of incoming client uptodate, so client will not 
1277     # be deleted from known_clients because of expiration
1278     my $act_time = &get_time();
1279     my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'"; 
1280     my $res = $known_clients_db->exec_statement($sql);
1282     ######################
1283     # process incoming msg
1284     if( $error == 0) {
1285         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1286         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1287         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1289         if ( 0 < @{$answer_l} ) {
1290             my $answer_str = join("\n", @{$answer_l});
1291             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1292                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1293             }
1294             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,8);
1295         } else {
1296             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,8);
1297         }
1299     }
1300     if( !$answer_l ) { $error++ };
1302     ########
1303     # answer
1304     if( $error == 0 ) {
1306         foreach my $answer ( @{$answer_l} ) {
1307             # check outgoing msg to xml validity
1308             my $answer_hash = &check_outgoing_xml_validity($answer);
1309             if( not defined $answer_hash ) { next; }
1310             
1311             $answer_header = @{$answer_hash->{'header'}}[0];
1312             @answer_target_l = @{$answer_hash->{'target'}};
1313             $answer_source = @{$answer_hash->{'source'}}[0];
1315             # deliver msg to all targets 
1316             foreach my $answer_target ( @answer_target_l ) {
1318                 # targets of msg are all gosa-si-clients in known_clients_db
1319                 if( $answer_target eq "*" ) {
1320                     # answer is for all clients
1321                     my $sql_statement= "SELECT * FROM known_clients";
1322                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1323                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1324                         my $host_name = $hit->{hostname};
1325                         my $host_key = $hit->{hostkey};
1326                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1327                         &update_jobdb_status_for_send_msgs($answer, $error);
1328                     }
1329                 }
1331                 # targets of msg are all gosa-si-server in known_server_db
1332                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1333                     # answer is for all server in known_server
1334                     my $sql_statement= "SELECT * FROM $known_server_tn";
1335                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1336                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1337                         my $host_name = $hit->{hostname};
1338                         my $host_key = $hit->{hostkey};
1339                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1340                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1341                         &update_jobdb_status_for_send_msgs($answer, $error);
1342                     }
1343                 }
1345                 # target of msg is GOsa
1346                                 elsif( $answer_target eq "GOSA" ) {
1347                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1348                                         my $add_on = "";
1349                     if( defined $session_id ) {
1350                         $add_on = ".session_id=$session_id";
1351                     }
1352                     # answer is for GOSA and has to returned to connected client
1353                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1354                     $client_answer = $gosa_answer.$add_on;
1355                 }
1357                 # target of msg is job queue at this host
1358                 elsif( $answer_target eq "JOBDB") {
1359                     $answer =~ /<header>(\S+)<\/header>/;   
1360                     my $header;
1361                     if( defined $1 ) { $header = $1; }
1362                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1363                     &update_jobdb_status_for_send_msgs($answer, $error);
1364                 }
1366                 # target of msg is a mac address
1367                 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 ) {
1368                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1369                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1370                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1371                     my $found_ip_flag = 0;
1372                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1373                         my $host_name = $hit->{hostname};
1374                         my $host_key = $hit->{hostkey};
1375                         $answer =~ s/$answer_target/$host_name/g;
1376                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1377                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1378                         &update_jobdb_status_for_send_msgs($answer, $error);
1379                         $found_ip_flag++ ;
1380                     }   
1381                     if( $found_ip_flag == 0) {
1382                         daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1383                         if( $bus_activ eq "true" ) { 
1384                             daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1385                             my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1386                             my $query_res = $known_server_db->select_dbentry( $sql_statement );
1387                             while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1388                                 my $bus_address = $hit->{hostname};
1389                                 my $bus_key = $hit->{hostkey};
1390                                 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header, $session_id);
1391                                 &update_jobdb_status_for_send_msgs($answer, $error);
1392                                 last;
1393                             }
1394                         }
1396                     }
1398                 #  answer is for one specific host   
1399                 } else {
1400                     # get encrypt_key
1401                     my $encrypt_key = &get_encrypt_key($answer_target);
1402                     if( not defined $encrypt_key ) {
1403                         # unknown target, forward msg to bus
1404                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1405                         if( $bus_activ eq "true" ) { 
1406                             daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1407                             my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1408                             my $query_res = $known_server_db->select_dbentry( $sql_statement );
1409                             my $res_length = keys( %{$query_res} );
1410                             if( $res_length == 0 ){
1411                                 daemon_log("$session_id WARNING: send '$answer_header' to '$bus_address' failed, ".
1412                                         "no bus found in known_server", 3);
1413                             }
1414                             else {
1415                                 while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1416                                     my $bus_key = $hit->{hostkey};
1417                                     my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header,$session_id );
1418                                     &update_jobdb_status_for_send_msgs($answer, $error);
1419                                 }
1420                             }
1421                         }
1422                         next;
1423                     }
1424                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1425                     &update_jobdb_status_for_send_msgs($answer, $error);
1426                 }
1427             }
1428         }
1429     }
1431     my $filter = POE::Filter::Reference->new();
1432     my %result = ( 
1433             status => "seems ok to me",
1434             answer => $client_answer,
1435             );
1437     my $output = $filter->put( [ \%result ] );
1438     print @$output;
1443 sub session_start {
1444     my ($kernel) = $_[KERNEL];
1445     &trigger_db_loop($kernel);
1446     $global_kernel = $kernel;
1447     $kernel->yield('register_at_foreign_servers');
1448         $kernel->yield('create_fai_server_db', $fai_server_tn );
1449         $kernel->yield('create_fai_release_db', $fai_release_tn );
1450     $kernel->yield('watch_for_next_tasks');
1451         $kernel->sig(USR1 => "sig_handler");
1452         $kernel->sig(USR2 => "create_packages_list_db");
1453         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1454         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1455         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1456     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1457         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1458     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1462 sub trigger_db_loop {
1463         my ($kernel) = @_ ;
1464 #       $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1465 #       $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1466 #       $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1467 #    $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1468 #       $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1469 #    $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->select_dbentry($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', 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); 
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); 
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: 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" );
2731     &main::daemon_log("$session_id DEBUG: unlink '$path.in'", 1); 
2735 sub store_fileinfo {
2736     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2738     my %fileinfo = (
2739         'package' => $package,
2740         'dist' => $dist,
2741         'version' => $vers,
2742     );
2744     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2748 sub cleanup_and_extract {
2749     my $fileinfo = $repo_files{ $File::Find::name };
2751     if( defined $fileinfo ) {
2753         my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2754         my $sql;
2755         my $package = $fileinfo->{ 'package' };
2756         my $newver = $fileinfo->{ 'version' };
2758         mkpath($dir);
2759         system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2761                 if( -f "$dir/DEBIAN/templates" ) {
2763                         daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2765                         my $tmpl= "";
2766                         {
2767                                 local $/=undef;
2768                                 open FILE, "$dir/DEBIAN/templates";
2769                                 $tmpl = &encode_base64(<FILE>);
2770                                 close FILE;
2771                         }
2772                         rmtree("$dir/DEBIAN/templates");
2774                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2775                 push @packages_list_statements, $sql;
2776                 }
2777     }
2779     return;
2783 sub register_at_foreign_servers {   
2784     my ($kernel) = $_[KERNEL];
2786     # hole alle bekannten server aus known_server_db
2787     my $server_sql = "SELECT * FROM $known_server_tn";
2788     my $server_res = $known_server_db->exec_statement($server_sql);
2790     # no entries in known_server_db
2791     if (not ref(@$server_res[0]) eq "ARRAY") { 
2792         # TODO
2793     }
2795     # detect already connected clients
2796     my $client_sql = "SELECT * FROM $known_clients_tn"; 
2797     my $client_res = $known_clients_db->exec_statement($client_sql);
2799     # send my server details to all other gosa-si-server within the network
2800     foreach my $hit (@$server_res) {
2801         my $hostname = @$hit[0];
2802         my $hostkey = &create_passwd;
2804         # add already connected clients to registration message 
2805         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
2806         &add_content2xml_hash($myhash, 'key', $hostkey);
2807         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
2808         
2809         # build registration message and send it
2810         my $foreign_server_msg = &create_xml_string($myhash);
2811         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
2812     }
2813     
2814     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
2815     return;
2819 #==== MAIN = main ==============================================================
2820 #  parse commandline options
2821 Getopt::Long::Configure( "bundling" );
2822 GetOptions("h|help" => \&usage,
2823         "c|config=s" => \$cfg_file,
2824         "f|foreground" => \$foreground,
2825         "v|verbose+" => \$verbose,
2826         "no-bus+" => \$no_bus,
2827         "no-arp+" => \$no_arp,
2828            );
2830 #  read and set config parameters
2831 &check_cmdline_param ;
2832 &read_configfile;
2833 &check_pid;
2835 $SIG{CHLD} = 'IGNORE';
2837 # forward error messages to logfile
2838 if( ! $foreground ) {
2839   open( STDIN,  '+>/dev/null' );
2840   open( STDOUT, '+>&STDIN'    );
2841   open( STDERR, '+>&STDIN'    );
2844 # Just fork, if we are not in foreground mode
2845 if( ! $foreground ) { 
2846     chdir '/'                 or die "Can't chdir to /: $!";
2847     $pid = fork;
2848     setsid                    or die "Can't start a new session: $!";
2849     umask 0;
2850 } else { 
2851     $pid = $$; 
2854 # Do something useful - put our PID into the pid_file
2855 if( 0 != $pid ) {
2856     open( LOCK_FILE, ">$pid_file" );
2857     print LOCK_FILE "$pid\n";
2858     close( LOCK_FILE );
2859     if( !$foreground ) { 
2860         exit( 0 ) 
2861     };
2864 # parse head url and revision from svn
2865 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2866 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2867 $server_headURL = defined $1 ? $1 : 'unknown' ;
2868 $server_revision = defined $2 ? $2 : 'unknown' ;
2869 if ($server_headURL =~ /\/tag\// || 
2870         $server_headURL =~ /\/branches\// ) {
2871     $server_status = "stable"; 
2872 } else {
2873     $server_status = "developmental" ;
2877 daemon_log(" ", 1);
2878 daemon_log("$0 started!", 1);
2879 daemon_log("status: $server_status", 1);
2880 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
2882 if ($no_bus > 0) {
2883     $bus_activ = "false"
2886 # connect to incoming_db
2887 unlink($incoming_file_name);
2888 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
2889 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
2891 # connect to gosa-si job queue
2892 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2893 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2895 # connect to known_clients_db
2896 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2897 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2899 # connect to foreign_clients_db
2900 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
2901 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
2903 # connect to known_server_db
2904 unlink($known_server_file_name);
2905 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2906 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2908 # connect to login_usr_db
2909 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2910 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2912 # connect to fai_server_db and fai_release_db
2913 unlink($fai_server_file_name);
2914 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2915 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2917 unlink($fai_release_file_name);
2918 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2919 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2921 # connect to packages_list_db
2922 #unlink($packages_list_file_name);
2923 unlink($packages_list_under_construction);
2924 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2925 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2927 # connect to messaging_db
2928 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2929 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2932 # create xml object used for en/decrypting
2933 $xml = new XML::Simple();
2936 # foreign servers 
2937 my @foreign_server_list;
2939 # add foreign server from cfg file
2940 if ($foreign_server_string ne "") {
2941     my @cfg_foreign_server_list = split(",", $foreign_server_string);
2942     foreach my $foreign_server (@cfg_foreign_server_list) {
2943         push(@foreign_server_list, $foreign_server);
2944     }
2947 # add foreign server from dns
2948 my @tmp_servers;
2949 if ( !$server_domain) {
2950     # Try our DNS Searchlist
2951     for my $domain(get_dns_domains()) {
2952         chomp($domain);
2953         my @tmp_domains= &get_server_addresses($domain);
2954         if(@tmp_domains) {
2955             for my $tmp_server(@tmp_domains) {
2956                 push @tmp_servers, $tmp_server;
2957             }
2958         }
2959     }
2960     if(@tmp_servers && length(@tmp_servers)==0) {
2961         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
2962     }
2963 } else {
2964     @tmp_servers = &get_server_addresses($server_domain);
2965     if( 0 == @tmp_servers ) {
2966         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
2967     }
2969 foreach my $server (@tmp_servers) { 
2970     unshift(@foreign_server_list, $server); 
2972 # eliminate duplicate entries
2973 @foreign_server_list = &del_doubles(@foreign_server_list);
2974 my $all_foreign_server = join(", ", @foreign_server_list);
2975 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
2977 # add all found foreign servers to known_server
2978 my $act_timestamp = &get_time();
2979 foreach my $foreign_server (@foreign_server_list) {
2980     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
2981             primkey=>['hostname'],
2982             hostname=>$foreign_server,
2983             status=>'not_jet_registered',
2984             hostkey=>"none",
2985             timestamp=>$act_timestamp,
2986             } );
2990 POE::Component::Server::TCP->new(
2991     Alias => "TCP_SERVER",
2992         Port => $server_port,
2993         ClientInput => sub {
2994         my ($kernel, $input) = @_[KERNEL, ARG0];
2995         push(@tasks, $input);
2996         push(@msgs_to_decrypt, $input);
2997         $kernel->yield("msg_to_decrypt");
2998         },
2999     InlineStates => {
3000         msg_to_decrypt => \&msg_to_decrypt,
3001         next_task => \&next_task,
3002         task_result => \&handle_task_result,
3003         task_done   => \&handle_task_done,
3004         task_debug  => \&handle_task_debug,
3005         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3006     }
3007 );
3009 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
3011 # create session for repeatedly checking the job queue for jobs
3012 POE::Session->create(
3013         inline_states => {
3014                 _start => \&session_start,
3015         register_at_foreign_servers => \&register_at_foreign_servers,
3016         sig_handler => \&sig_handler,
3017         next_task => \&next_task,
3018         task_result => \&handle_task_result,
3019         task_done   => \&handle_task_done,
3020         task_debug  => \&handle_task_debug,
3021         watch_for_next_tasks => \&watch_for_next_tasks,
3022         watch_for_new_messages => \&watch_for_new_messages,
3023         watch_for_delivery_messages => \&watch_for_delivery_messages,
3024         watch_for_done_messages => \&watch_for_done_messages,
3025                 watch_for_new_jobs => \&watch_for_new_jobs,
3026         watch_for_done_jobs => \&watch_for_done_jobs,
3027         watch_for_old_known_clients => \&watch_for_old_known_clients,
3028         create_packages_list_db => \&run_create_packages_list_db,
3029         create_fai_server_db => \&run_create_fai_server_db,
3030         create_fai_release_db => \&run_create_fai_release_db,
3031         session_run_result => \&session_run_result,
3032         session_run_debug => \&session_run_debug,
3033         session_run_done => \&session_run_done,
3034         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3035         }
3036 );
3039 # import all modules
3040 &import_modules;
3042 # TODO
3043 # check wether all modules are gosa-si valid passwd check
3047 POE::Kernel->run();
3048 exit;