Code

0b895da7d0a3c1890f150bf179c2d5160b95a932
[gosa.git] / gosa-si / gosa-si-server
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 #         FILE:  gosa-sd
5 #
6 #        USAGE:  ./gosa-sd
7 #
8 #  DESCRIPTION:
9 #
10 #      OPTIONS:  ---
11 # REQUIREMENTS:  libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl 
12 #                libdata-dumper-simple-perl libdbd-sqlite3-perl libnet-ldap-perl
13 #                libpoe-perl
14 #         BUGS:  ---
15 #        NOTES:
16 #       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
17 #      COMPANY:
18 #      VERSION:  1.0
19 #      CREATED:  12.09.2007 08:54:41 CEST
20 #     REVISION:  ---
21 #===============================================================================
24 # TODO
25 #
26 # max_children wird momentan nicht mehr verwendet, jede eingehende nachricht bekommt ein eigenes POE child
28 use strict;
29 use warnings;
30 use Getopt::Long;
31 use Config::IniFiles;
32 use POSIX;
34 use Fcntl;
35 use IO::Socket::INET;
36 use IO::Handle;
37 use IO::Select;
38 use Symbol qw(qualify_to_ref);
39 use Crypt::Rijndael;
40 use MIME::Base64;
41 use Digest::MD5  qw(md5 md5_hex md5_base64);
42 use XML::Simple;
43 use Data::Dumper;
44 use Sys::Syslog qw( :DEFAULT setlogsock);
45 use Cwd;
46 use File::Spec;
47 use File::Basename;
48 use File::Find;
49 use File::Copy;
50 use File::Path;
51 use GOSA::DBsqlite;
52 use GOSA::GosaSupportDaemon;
53 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
54 use Net::LDAP;
55 use Net::LDAP::Util qw(:escape);
56 use Time::HiRes qw( usleep);
57 use DateTime;
59 my $modules_path = "/usr/lib/gosa-si/modules";
60 use lib "/usr/lib/gosa-si/modules";
62 # revision number of server and program name
63 my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev: 10826 $';
64 my $server_headURL;
65 my $server_revision;
66 my $server_status;
67 our $prg= basename($0);
69 our $global_kernel;
70 my ($foreground, $ping_timeout);
71 my ($server);
72 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
73 my ($messaging_db_loop_delay);
74 my ($known_modules);
75 my ($procid, $pid);
76 my ($arp_fifo);
77 my ($xml);
78 my $sources_list;
79 my $max_clients;
80 my %repo_files=();
81 my $repo_path;
82 my %repo_dirs=();
83 # variables declared in config file are always set to 'our'
84 our (%cfg_defaults, $log_file, $pid_file, 
85     $server_ip, $server_port, $ClientPackages_key, 
86     $arp_activ, $gosa_unit_tag,
87     $GosaPackages_key, $gosa_timeout,
88     $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
89     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
90     $arp_enabled, $arp_interface,
91 );
93 # additional variable which should be globaly accessable
94 our $server_address;
95 our $server_mac_address;
96 our $gosa_address;
97 our $no_arp;
98 our $verbose;
99 our $forground;
100 our $cfg_file;
101 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
103 # dak variables
104 our $dak_base_directory;
105 our $dak_signing_keys_directory;
106 our $dak_queue_directory;
107 our $dak_user;
109 # specifies the verbosity of the daemon_log
110 $verbose = 0 ;
112 # if foreground is not null, script will be not forked to background
113 $foreground = 0 ;
115 # specifies the timeout seconds while checking the online status of a registrating client
116 $ping_timeout = 5;
118 $no_arp = 0;
119 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
120 my @packages_list_statements;
121 my $watch_for_new_jobs_in_progress = 0;
123 # holds all incoming decrypted messages
124 our $incoming_db;
125 our $incoming_tn = 'incoming';
126 my $incoming_file_name;
127 my @incoming_col_names = ("id INTEGER PRIMARY KEY", 
128         "timestamp DEFAULT 'none'", 
129         "headertag DEFAULT 'none'",
130                 "targettag DEFAULT 'none'",
131         "xmlmessage DEFAULT 'none'",
132         "module DEFAULT 'none'",
133         "sessionid DEFAULT '0'",
134         );
136 # holds all gosa jobs
137 our $job_db;
138 our $job_queue_tn = 'jobs';
139 my $job_queue_file_name;
140 my @job_queue_col_names = ("id INTEGER PRIMARY KEY", 
141                 "timestamp DEFAULT 'none'", 
142                 "status DEFAULT 'none'", 
143                 "result DEFAULT 'none'", 
144                 "progress DEFAULT 'none'", 
145         "headertag DEFAULT 'none'", 
146                 "targettag DEFAULT 'none'", 
147                 "xmlmessage DEFAULT 'none'", 
148                 "macaddress DEFAULT 'none'",
149                 "plainname DEFAULT 'none'",
150         "siserver DEFAULT 'none'",
151         "modified DEFAULT '0'",
152                 );
154 # holds all other gosa-si-server
155 our $known_server_db;
156 our $known_server_tn = "known_server";
157 my $known_server_file_name;
158 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
160 # holds all registrated clients
161 our $known_clients_db;
162 our $known_clients_tn = "known_clients";
163 my $known_clients_file_name;
164 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events", "keylifetime");
166 # holds all registered clients at a foreign server
167 our $foreign_clients_db;
168 our $foreign_clients_tn = "foreign_clients"; 
169 my $foreign_clients_file_name;
170 my @foreign_clients_col_names = ("hostname", "macaddress", "regserver", "timestamp");
172 # holds all logged in user at each client 
173 our $login_users_db;
174 our $login_users_tn = "login_users";
175 my $login_users_file_name;
176 my @login_users_col_names = ("client", "user", "timestamp");
178 # holds all fai server, the debian release and tag
179 our $fai_server_db;
180 our $fai_server_tn = "fai_server"; 
181 my $fai_server_file_name;
182 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag"); 
184 our $fai_release_db;
185 our $fai_release_tn = "fai_release"; 
186 my $fai_release_file_name;
187 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state"); 
189 # holds all packages available from different repositories
190 our $packages_list_db;
191 our $packages_list_tn = "packages_list";
192 my $packages_list_file_name;
193 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
194 my $outdir = "/tmp/packages_list_db";
195 my $arch = "i386"; 
197 # holds all messages which should be delivered to a user
198 our $messaging_db;
199 our $messaging_tn = "messaging"; 
200 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to", 
201         "flag", "direction", "delivery_time", "message", "timestamp" );
202 my $messaging_file_name;
204 # path to directory to store client install log files
205 our $client_fai_log_dir = "/var/log/fai"; 
207 # queue which stores taskes until one of the $max_children children are ready to process the task
208 my @tasks = qw();
209 my @msgs_to_decrypt = qw();
210 my $max_children = 2;
213 %cfg_defaults = (
214 "general" => {
215     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
216     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
217     },
218 "server" => {
219     "ip" => [\$server_ip, "0.0.0.0"],
220     "port" => [\$server_port, "20081"],
221     "known-clients"        => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
222     "known-servers"        => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
223     "incoming"             => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
224     "login-users"          => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
225     "fai-server"           => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
226     "fai-release"          => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
227     "packages-list"        => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
228     "messaging"            => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
229     "foreign-clients"      => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
230     "source-list"          => [\$sources_list, '/etc/apt/sources.list'],
231     "repo-path"            => [\$repo_path, '/srv/www/repository'],
232     "ldap-uri"             => [\$ldap_uri, ""],
233     "ldap-base"            => [\$ldap_base, ""],
234     "ldap-admin-dn"        => [\$ldap_admin_dn, ""],
235     "ldap-admin-password"  => [\$ldap_admin_password, ""],
236     "gosa-unit-tag"        => [\$gosa_unit_tag, ""],
237     "max-clients"          => [\$max_clients, 10],
238     "wol-password"           => [\$wake_on_lan_passwd, ""],
239     },
240 "GOsaPackages" => {
241     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
242     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
243     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
244     "key" => [\$GosaPackages_key, "none"],
245         "dak-base" => [\$dak_base_directory, "/srv/archive"],
246         "dak-keyring" => [\$dak_signing_keys_directory, "/srv/archive/keyrings"],
247         "dak-queue" => [\$dak_queue_directory, "/srv/archive/queue"],
248         "dak-user" => [\$dak_user, "deb-dak"],
249     },
250 "ClientPackages" => {
251     "key" => [\$ClientPackages_key, "none"],
252     },
253 "ServerPackages"=> {
254     "address"      => [\$foreign_server_string, ""],
255     "domain"  => [\$server_domain, ""],
256     "key"     => [\$ServerPackages_key, "none"],
257     "key-lifetime" => [\$foreign_servers_register_delay, 120],
258     "job-synchronization-enabled" => [\$job_synchronization, "true"],
259     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
260     },
261 "ArpHandler" => {
262     "enabled"   => [\$arp_enabled, "true"],
263     "interface" => [\$arp_interface, "all"],
264         },
266 );
269 #===  FUNCTION  ================================================================
270 #         NAME:  usage
271 #   PARAMETERS:  nothing
272 #      RETURNS:  nothing
273 #  DESCRIPTION:  print out usage text to STDERR
274 #===============================================================================
275 sub usage {
276     print STDERR << "EOF" ;
277 usage: $prg [-hvf] [-c config]
279            -h        : this (help) message
280            -c <file> : config file
281            -f        : foreground, process will not be forked to background
282            -v        : be verbose (multiple to increase verbosity)
283            -no-arp   : starts $prg without connection to arp module
284  
285 EOF
286     print "\n" ;
290 #===  FUNCTION  ================================================================
291 #         NAME:  read_configfile
292 #   PARAMETERS:  cfg_file - string -
293 #      RETURNS:  nothing
294 #  DESCRIPTION:  read cfg_file and set variables
295 #===============================================================================
296 sub read_configfile {
297     my $cfg;
298     if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
299         if( -r $cfg_file ) {
300             $cfg = Config::IniFiles->new( -file => $cfg_file );
301         } else {
302             print STDERR "Couldn't read config file!\n";
303         }
304     } else {
305         $cfg = Config::IniFiles->new() ;
306     }
307     foreach my $section (keys %cfg_defaults) {
308         foreach my $param (keys %{$cfg_defaults{ $section }}) {
309             my $pinfo = $cfg_defaults{ $section }{ $param };
310             ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
311         }
312     }
316 #===  FUNCTION  ================================================================
317 #         NAME:  logging
318 #   PARAMETERS:  level - string - default 'info'
319 #                msg - string -
320 #                facility - string - default 'LOG_DAEMON'
321 #      RETURNS:  nothing
322 #  DESCRIPTION:  function for logging
323 #===============================================================================
324 sub daemon_log {
325     # log into log_file
326     my( $msg, $level ) = @_;
327     if(not defined $msg) { return }
328     if(not defined $level) { $level = 1 }
329     if(defined $log_file){
330         open(LOG_HANDLE, ">>$log_file");
331         chmod 0600, $log_file;
332         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
333             print STDERR "cannot open $log_file: $!";
334             return 
335         }
336         chomp($msg);
337         $msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
338         if($level <= $verbose){
339             my ($seconds, $minutes, $hours, $monthday, $month,
340                     $year, $weekday, $yearday, $sommertime) = localtime(time);
341             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
342             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
343             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
344             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
345             $month = $monthnames[$month];
346             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
347             $year+=1900;
348             my $name = $prg;
350             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
351             print LOG_HANDLE $log_msg;
352             if( $foreground ) { 
353                 print STDERR $log_msg;
354             }
355         }
356         close( LOG_HANDLE );
357     }
361 #===  FUNCTION  ================================================================
362 #         NAME:  check_cmdline_param
363 #   PARAMETERS:  nothing
364 #      RETURNS:  nothing
365 #  DESCRIPTION:  validates commandline parameter
366 #===============================================================================
367 sub check_cmdline_param () {
368     my $err_config;
369     my $err_counter = 0;
370         if(not defined($cfg_file)) {
371                 $cfg_file = "/etc/gosa-si/server.conf";
372                 if(! -r $cfg_file) {
373                         $err_config = "please specify a config file";
374                         $err_counter += 1;
375                 }
376     }
377     if( $err_counter > 0 ) {
378         &usage( "", 1 );
379         if( defined( $err_config)) { print STDERR "$err_config\n"}
380         print STDERR "\n";
381         exit( -1 );
382     }
386 #===  FUNCTION  ================================================================
387 #         NAME:  check_pid
388 #   PARAMETERS:  nothing
389 #      RETURNS:  nothing
390 #  DESCRIPTION:  handels pid processing
391 #===============================================================================
392 sub check_pid {
393     $pid = -1;
394     # Check, if we are already running
395     if( open(LOCK_FILE, "<$pid_file") ) {
396         $pid = <LOCK_FILE>;
397         if( defined $pid ) {
398             chomp( $pid );
399             if( -f "/proc/$pid/stat" ) {
400                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
401                 if( $stat ) {
402                                         daemon_log("ERROR: Already running",1);
403                     close( LOCK_FILE );
404                     exit -1;
405                 }
406             }
407         }
408         close( LOCK_FILE );
409         unlink( $pid_file );
410     }
412     # create a syslog msg if it is not to possible to open PID file
413     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
414         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
415         if (open(LOCK_FILE, '<', $pid_file)
416                 && ($pid = <LOCK_FILE>))
417         {
418             chomp($pid);
419             $msg .= "(PID $pid)\n";
420         } else {
421             $msg .= "(unable to read PID)\n";
422         }
423         if( ! ($foreground) ) {
424             openlog( $0, "cons,pid", "daemon" );
425             syslog( "warning", $msg );
426             closelog();
427         }
428         else {
429             print( STDERR " $msg " );
430         }
431         exit( -1 );
432     }
435 #===  FUNCTION  ================================================================
436 #         NAME:  import_modules
437 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
438 #                are stored
439 #      RETURNS:  nothing
440 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
441 #                state is on is imported by "require 'file';"
442 #===============================================================================
443 sub import_modules {
444     daemon_log(" ", 1);
446     if (not -e $modules_path) {
447         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
448     }
450     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
451     while (defined (my $file = readdir (DIR))) {
452         if (not $file =~ /(\S*?).pm$/) {
453             next;
454         }
455                 my $mod_name = $1;
457         # ArpHandler switch
458         if( $file =~ /ArpHandler.pm/ ) {
459             if( $arp_enabled eq "false" ) { next; }
460         }
461         
462         eval { require $file; };
463         if ($@) {
464             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
465             daemon_log("$@", 5);
466                 } else {
467                         my $info = eval($mod_name.'::get_module_info()');
468                         # Only load module if get_module_info() returns a non-null object
469                         if( $info ) {
470                                 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
471                                 $known_modules->{$mod_name} = $info;
472                                 daemon_log("0 INFO: module $mod_name loaded", 5);
473                         }
474                 }
475     }   
476     close (DIR);
479 #===  FUNCTION  ================================================================
480 #         NAME:  password_check
481 #   PARAMETERS:  nothing
482 #      RETURNS:  nothing
483 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
484 #                the same password
485 #===============================================================================
486 sub password_check {
487     my $passwd_hash = {};
488     while (my ($mod_name, $mod_info) = each %$known_modules) {
489         my $mod_passwd = @$mod_info[1];
490         if (not defined $mod_passwd) { next; }
491         if (not exists $passwd_hash->{$mod_passwd}) {
492             $passwd_hash->{$mod_passwd} = $mod_name;
494         # escalates critical error
495         } else {
496             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
497             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
498             exit( -1 );
499         }
500     }
505 #===  FUNCTION  ================================================================
506 #         NAME:  sig_int_handler
507 #   PARAMETERS:  signal - string - signal arose from system
508 #      RETURNS:  nothing
509 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
510 #===============================================================================
511 sub sig_int_handler {
512     my ($signal) = @_;
514 #       if (defined($ldap_handle)) {
515 #               $ldap_handle->disconnect;
516 #       }
517     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
518     
520     daemon_log("shutting down gosa-si-server", 1);
521     system("kill `ps -C gosa-si-server -o pid=`");
523 $SIG{INT} = \&sig_int_handler;
526 sub check_key_and_xml_validity {
527     my ($crypted_msg, $module_key, $session_id) = @_;
528     my $msg;
529     my $msg_hash;
530     my $error_string;
531     eval{
532         $msg = &decrypt_msg($crypted_msg, $module_key);
534         if ($msg =~ /<xml>/i){
535             $msg =~ s/\s+/ /g;  # just for better daemon_log
536             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
537             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
539             ##############
540             # check header
541             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
542             my $header_l = $msg_hash->{'header'};
543             if( 1 > @{$header_l} ) { die 'empty header tag'; }
544             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
545             my $header = @{$header_l}[0];
546             if( 0 == length $header) { die 'empty string in header tag'; }
548             ##############
549             # check source
550             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
551             my $source_l = $msg_hash->{'source'};
552             if( 1 > @{$source_l} ) { die 'empty source tag'; }
553             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
554             my $source = @{$source_l}[0];
555             if( 0 == length $source) { die 'source error'; }
557             ##############
558             # check target
559             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
560             my $target_l = $msg_hash->{'target'};
561             if( 1 > @{$target_l} ) { die 'empty target tag'; }
562         }
563     };
564     if($@) {
565         daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
566         $msg = undef;
567         $msg_hash = undef;
568     }
570     return ($msg, $msg_hash);
574 sub check_outgoing_xml_validity {
575     my ($msg, $session_id) = @_;
577     my $msg_hash;
578     eval{
579         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
581         ##############
582         # check header
583         my $header_l = $msg_hash->{'header'};
584         if( 1 != @{$header_l} ) {
585             die 'no or more than one headers specified';
586         }
587         my $header = @{$header_l}[0];
588         if( 0 == length $header) {
589             die 'header has length 0';
590         }
592         ##############
593         # check source
594         my $source_l = $msg_hash->{'source'};
595         if( 1 != @{$source_l} ) {
596             die 'no or more than 1 sources specified';
597         }
598         my $source = @{$source_l}[0];
599         if( 0 == length $source) {
600             die 'source has length 0';
601         }
602         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
603                 $source =~ /^GOSA$/i ) {
604             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
605         }
606         
607         ##############
608         # check target  
609         my $target_l = $msg_hash->{'target'};
610         if( 0 == @{$target_l} ) {
611             die "no targets specified";
612         }
613         foreach my $target (@$target_l) {
614             if( 0 == length $target) {
615                 die "target has length 0";
616             }
617             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
618                     $target =~ /^GOSA$/i ||
619                     $target =~ /^\*$/ ||
620                     $target =~ /KNOWN_SERVER/i ||
621                     $target =~ /JOBDB/i ||
622                     $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 ){
623                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
624             }
625         }
626     };
627     if($@) {
628         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
629         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
630         $msg_hash = undef;
631     }
633     return ($msg_hash);
637 sub input_from_known_server {
638     my ($input, $remote_ip, $session_id) = @_ ;  
639     my ($msg, $msg_hash, $module);
641     my $sql_statement= "SELECT * FROM known_server";
642     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
644     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
645         my $host_name = $hit->{hostname};
646         if( not $host_name =~ "^$remote_ip") {
647             next;
648         }
649         my $host_key = $hit->{hostkey};
650         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
651         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
653         # check if module can open msg envelope with module key
654         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
655         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
656             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
657             daemon_log("$@", 8);
658             next;
659         }
660         else {
661             $msg = $tmp_msg;
662             $msg_hash = $tmp_msg_hash;
663             $module = "ServerPackages";
664             last;
665         }
666     }
668     if( (!$msg) || (!$msg_hash) || (!$module) ) {
669         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
670     }
671   
672     return ($msg, $msg_hash, $module);
676 sub input_from_known_client {
677     my ($input, $remote_ip, $session_id) = @_ ;  
678     my ($msg, $msg_hash, $module);
680     my $sql_statement= "SELECT * FROM known_clients";
681     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
682     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
683         my $host_name = $hit->{hostname};
684         if( not $host_name =~ /^$remote_ip:\d*$/) {
685                 next;
686                 }
687         my $host_key = $hit->{hostkey};
688         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
689         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
691         # check if module can open msg envelope with module key
692         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
694         if( (!$msg) || (!$msg_hash) ) {
695             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
696             &daemon_log("$@", 8);
697             next;
698         }
699         else {
700             $module = "ClientPackages";
701             last;
702         }
703     }
705     if( (!$msg) || (!$msg_hash) || (!$module) ) {
706         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
707     }
709     return ($msg, $msg_hash, $module);
713 sub input_from_unknown_host {
714     no strict "refs";
715     my ($input, $session_id) = @_ ;
716     my ($msg, $msg_hash, $module);
717     my $error_string;
718     
719         my %act_modules = %$known_modules;
720         
721     while( my ($mod, $info) = each(%act_modules)) {
723         # check a key exists for this module
724         my $module_key = ${$mod."_key"};
725         if( not defined $module_key ) {
726             if( $mod eq 'ArpHandler' ) {
727                 next;
728             }
729             daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
730             next;
731         }
732         daemon_log("$session_id DEBUG: $mod: $module_key", 7);
734         # check if module can open msg envelope with module key
735         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
736         if( (not defined $msg) || (not defined $msg_hash) ) {
737             next;
738         }
739         else {
740             $module = $mod;
741             last;
742         }
743     }
745     if( (!$msg) || (!$msg_hash) || (!$module)) {
746         daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
747     }
749     return ($msg, $msg_hash, $module);
753 sub create_ciphering {
754     my ($passwd) = @_;
755         if((!defined($passwd)) || length($passwd)==0) {
756                 $passwd = "";
757         }
758     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
759     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
760     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
761     $my_cipher->set_iv($iv);
762     return $my_cipher;
766 sub encrypt_msg {
767     my ($msg, $key) = @_;
768     my $my_cipher = &create_ciphering($key);
769     my $len;
770     {
771             use bytes;
772             $len= 16-length($msg)%16;
773     }
774     $msg = "\0"x($len).$msg;
775     $msg = $my_cipher->encrypt($msg);
776     chomp($msg = &encode_base64($msg));
777     # there are no newlines allowed inside msg
778     $msg=~ s/\n//g;
779     return $msg;
783 sub decrypt_msg {
785     my ($msg, $key) = @_ ;
786     $msg = &decode_base64($msg);
787     my $my_cipher = &create_ciphering($key);
788     $msg = $my_cipher->decrypt($msg); 
789     $msg =~ s/\0*//g;
790     return $msg;
794 sub get_encrypt_key {
795     my ($target) = @_ ;
796     my $encrypt_key;
797     my $error = 0;
799     # target can be in known_server
800     if( not defined $encrypt_key ) {
801         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
802         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
803         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
804             my $host_name = $hit->{hostname};
805             if( $host_name ne $target ) {
806                 next;
807             }
808             $encrypt_key = $hit->{hostkey};
809             last;
810         }
811     }
813     # target can be in known_client
814     if( not defined $encrypt_key ) {
815         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
816         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
817         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
818             my $host_name = $hit->{hostname};
819             if( $host_name ne $target ) {
820                 next;
821             }
822             $encrypt_key = $hit->{hostkey};
823             last;
824         }
825     }
827     return $encrypt_key;
831 #===  FUNCTION  ================================================================
832 #         NAME:  open_socket
833 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
834 #                [PeerPort] string necessary if port not appended by PeerAddr
835 #      RETURNS:  socket IO::Socket::INET
836 #  DESCRIPTION:  open a socket to PeerAddr
837 #===============================================================================
838 sub open_socket {
839     my ($PeerAddr, $PeerPort) = @_ ;
840     if(defined($PeerPort)){
841         $PeerAddr = $PeerAddr.":".$PeerPort;
842     }
843     my $socket;
844     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
845             Porto => "tcp",
846             Type => SOCK_STREAM,
847             Timeout => 5,
848             );
849     if(not defined $socket) {
850         return;
851     }
852 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
853     return $socket;
857 sub get_local_ip_for_remote_ip {
858         my $remote_ip= shift;
859         my $result="0.0.0.0";
861         if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
862                 if($remote_ip eq "127.0.0.1") {
863                         $result = "127.0.0.1";
864                 } else {
865                         my $PROC_NET_ROUTE= ('/proc/net/route');
867                         open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
868                                 or die "Could not open $PROC_NET_ROUTE";
870                         my @ifs = <PROC_NET_ROUTE>;
872                         close(PROC_NET_ROUTE);
874                         # Eat header line
875                         shift @ifs;
876                         chomp @ifs;
877                         foreach my $line(@ifs) {
878                                 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
879                                 my $destination;
880                                 my $mask;
881                                 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
882                                 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
883                                 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
884                                 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
885                                 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
886                                         # destination matches route, save mac and exit
887                                         $result= &get_ip($Iface);
888                                         last;
889                                 }
890                         }
891                 }
892         } else {
893                 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
894         }
895         return $result;
899 sub send_msg_to_target {
900     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
901     my $error = 0;
902     my $header;
903     my $timestamp = &get_time();
904     my $new_status;
905     my $act_status;
906     my ($sql_statement, $res);
907   
908     if( $msg_header ) {
909         $header = "'$msg_header'-";
910     } else {
911         $header = "";
912     }
914         # Patch the source ip
915         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
916                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
917                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
918         }
920     # encrypt xml msg
921     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
923     # opensocket
924     my $socket = &open_socket($address);
925     if( !$socket ) {
926         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
927         $error++;
928     }
929     
930     if( $error == 0 ) {
931         # send xml msg
932         print $socket $crypted_msg."\n";
934         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
935         daemon_log("$session_id DEBUG: message:\n$msg", 9);
936         
937     }
939     # close socket in any case
940     if( $socket ) {
941         close $socket;
942     }
944     if( $error > 0 ) { $new_status = "down"; }
945     else { $new_status = $msg_header; }
948     # known_clients
949     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
950     $res = $known_clients_db->select_dbentry($sql_statement);
951     if( keys(%$res) == 1) {
952         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
953         if ($act_status eq "down" && $new_status eq "down") {
954             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
955             $res = $known_clients_db->del_dbentry($sql_statement);
956             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
957         } else { 
958             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
959             $res = $known_clients_db->update_dbentry($sql_statement);
960             if($new_status eq "down"){
961                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
962             } else {
963                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
964             }
965         }
966     }
968     # known_server
969     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
970     $res = $known_server_db->select_dbentry($sql_statement);
971     if( keys(%$res) == 1) {
972         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
973         if ($act_status eq "down" && $new_status eq "down") {
974             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
975             $res = $known_server_db->del_dbentry($sql_statement);
976             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
977         } 
978         else { 
979             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
980             $res = $known_server_db->update_dbentry($sql_statement);
981             if($new_status eq "down"){
982                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
983             } else {
984                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
985             }
986         }
987     }
988     return $error; 
992 sub update_jobdb_status_for_send_msgs {
993     my ($answer, $error) = @_;
994     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
995         my $jobdb_id = $1;
996             
997         # sending msg faild
998         if( $error ) {
999             if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
1000                 my $sql_statement = "UPDATE $job_queue_tn ".
1001                     "SET status='error', result='can not deliver msg, please consult log file' ".
1002                     "WHERE id=$jobdb_id";
1003                 my $res = $job_db->update_dbentry($sql_statement);
1004             }
1006         # sending msg was successful
1007         } else {
1008             my $sql_statement = "UPDATE $job_queue_tn ".
1009                 "SET status='done' ".
1010                 "WHERE id=$jobdb_id AND status='processed'";
1011             my $res = $job_db->update_dbentry($sql_statement);
1012         }
1013     }
1017 sub sig_handler {
1018         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1019         daemon_log("0 INFO got signal '$signal'", 1); 
1020         $kernel->sig_handled();
1021         return;
1025 sub msg_to_decrypt {
1026     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1027     my $session_id = $session->ID;
1028     my ($msg, $msg_hash, $module);
1029     my $error = 0;
1031     # hole neue msg aus @msgs_to_decrypt
1032     my $next_msg = shift @msgs_to_decrypt;
1033     
1034     # entschlüssle sie
1036     # msg is from a new client or gosa
1037     ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1038     # msg is from a gosa-si-server
1039     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1040         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1041     }
1042     # msg is from a gosa-si-client
1043     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1044         ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1045     }
1046     # an error occurred
1047     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1048         # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1049         # could not understand a msg from its server the client cause a re-registering process
1050         daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1051                 "' to cause a re-registering of the client if necessary", 3);
1052         my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1053         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1054         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1055             my $host_name = $hit->{'hostname'};
1056             my $host_key = $hit->{'hostkey'};
1057             my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1058             my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1059             &update_jobdb_status_for_send_msgs($ping_msg, $error);
1060         }
1061         $error++;
1062     }
1065     my $header;
1066     my $target;
1067     my $source;
1068     my $done = 0;
1069     my $sql;
1070     my $res;
1072     # check whether this message should be processed here
1073     if ($error == 0) {
1074         $header = @{$msg_hash->{'header'}}[0];
1075         $target = @{$msg_hash->{'target'}}[0];
1076         $source = @{$msg_hash->{'source'}}[0];
1077                 my $not_found_in_known_clients_db = 0;
1078                 my $not_found_in_known_server_db = 0;
1079                 my $not_found_in_foreign_clients_db = 0;
1080         my $local_address;
1081         my ($target_ip, $target_port) = split(':', $target);
1082                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1083                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1084                 } else {
1085             $local_address = $server_address;
1086         }
1088         # target and source is equal to GOSA -> process here
1089         if (not $done) {
1090             if ($target eq "GOSA" && $source eq "GOSA") {
1091                 $done = 1;                    
1092             }
1093         }
1095         # target is own address without forward_to_gosa-tag -> process here
1096         if (not $done) {
1097             if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1098                 $done = 1;
1099                 if ($source eq "GOSA") {
1100                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1101                 }
1102                 #print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
1103             }
1104         }
1106         # target is a client address in known_clients -> process here
1107                 if (not $done) {
1108                                 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1109                                 $res = $known_clients_db->select_dbentry($sql);
1110                                 if (keys(%$res) > 0) {
1111                                                 $done = 1; 
1112                                                 my $hostname = $res->{1}->{'hostname'};
1113                                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1114                                                 #print STDERR "target is a client address in known_clients -> process here\n";
1115                                 } else {
1116                                                 $not_found_in_known_clients_db = 1;
1117                                 }
1118                 }
1119         
1120         # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1121         if (not $done) {
1122             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1123             my $gosa_at;
1124             my $gosa_session_id;
1125             if (($target eq $local_address) && (defined $forward_to_gosa)){
1126                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1127                 if ($gosa_at ne $local_address) {
1128                     $done = 1;
1129                     #print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n"; 
1130                 }
1131             }
1132         }
1134         # if message should be processed here -> add message to incoming_db
1135                 if ($done) {
1136                                 # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1137                                 # so gosa-si-server knows how to process this kind of messages
1138                                 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1139                                                 $module = "GosaPackages";
1140                                 }
1142                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1143                                                                 primkey=>[],
1144                                                                 headertag=>$header,
1145                                                                 targettag=>$target,
1146                                                                 xmlmessage=>&encode_base64($msg),
1147                                                                 timestamp=>&get_time,
1148                                                                 module=>$module,
1149                                                                 sessionid=>$session_id,
1150                                                                 } );
1151                 }
1153         # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1154         if (not $done) {
1155             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1156             my $gosa_at;
1157             my $gosa_session_id;
1158             if (($target eq $local_address) && (defined $forward_to_gosa)){
1159                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1160                 if ($gosa_at eq $local_address) {
1161                     my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1162                     if( defined $session_reference ) {
1163                         $heap = $session_reference->get_heap();
1164                     }
1165                     if(exists $heap->{'client'}) {
1166                         $msg = &encrypt_msg($msg, $GosaPackages_key);
1167                         $heap->{'client'}->put($msg);
1168                     }
1169                     $done = 1;
1170                     #print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
1171                 }
1172             }
1174         }
1176         # target is a client address in foreign_clients -> forward to registration server
1177         if (not $done) {
1178             $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1179             $res = $foreign_clients_db->select_dbentry($sql);
1180             if (keys(%$res) > 0) {
1181                     my $hostname = $res->{1}->{'hostname'};
1182                     my ($host_ip, $host_port) = split(/:/, $hostname);
1183                     my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1184                 my $regserver = $res->{1}->{'regserver'};
1185                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1186                 my $res = $known_server_db->select_dbentry($sql);
1187                 if (keys(%$res) > 0) {
1188                     my $regserver_key = $res->{1}->{'hostkey'};
1189                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1190                     $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1191                     if ($source eq "GOSA") {
1192                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1193                     }
1194                     &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1195                 }
1196                 $done = 1;
1197                 #print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
1198             } else {
1199                                 $not_found_in_foreign_clients_db = 1;
1200                         }
1201         }
1203         # target is a server address -> forward to server
1204         if (not $done) {
1205             $sql = "SELECT * FROM $known_server_tn WHERE hostname='$target'";
1206             $res = $known_server_db->select_dbentry($sql);
1207             if (keys(%$res) > 0) {
1208                 my $hostkey = $res->{1}->{'hostkey'};
1210                 if ($source eq "GOSA") {
1211                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1212                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1214                 }
1216                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1217                 $done = 1;
1218                 #print STDERR "target is a server address -> forward to server\n";
1219             } else {
1220                                 $not_found_in_known_server_db = 1;
1221                         }
1222         }
1224                 
1225                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1226                 if ( $not_found_in_foreign_clients_db 
1227                                                 && $not_found_in_known_server_db
1228                                                 && $not_found_in_known_clients_db) {
1229                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1230                                                                 primkey=>[],
1231                                                                 headertag=>$header,
1232                                                                 targettag=>$target,
1233                                                                 xmlmessage=>&encode_base64($msg),
1234                                                                 timestamp=>&get_time,
1235                                                                 module=>$module,
1236                                                                 sessionid=>$session_id,
1237                                                                 } );
1238                                 $done = 1;
1239                 }
1242         if (not $done) {
1243             daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1244             if ($source eq "GOSA") {
1245                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1246                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1248                 my $session_reference = $kernel->ID_id_to_session($session_id);
1249                 if( defined $session_reference ) {
1250                     $heap = $session_reference->get_heap();
1251                 }
1252                 if(exists $heap->{'client'}) {
1253                     $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1254                     $heap->{'client'}->put($error_msg);
1255                 }
1256             }
1257         }
1259     }
1261     return;
1265 sub next_task {
1266     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1267     my $running_task = POE::Wheel::Run->new(
1268             Program => sub { process_task($session, $heap, $task) },
1269             StdioFilter => POE::Filter::Reference->new(),
1270             StdoutEvent  => "task_result",
1271             StderrEvent  => "task_debug",
1272             CloseEvent   => "task_done",
1273             );
1274     $heap->{task}->{ $running_task->ID } = $running_task;
1277 sub handle_task_result {
1278     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1279     my $client_answer = $result->{'answer'};
1280     if( $client_answer =~ s/session_id=(\d+)$// ) {
1281         my $session_id = $1;
1282         if( defined $session_id ) {
1283             my $session_reference = $kernel->ID_id_to_session($session_id);
1284             if( defined $session_reference ) {
1285                 $heap = $session_reference->get_heap();
1286             }
1287         }
1289         if(exists $heap->{'client'}) {
1290             $heap->{'client'}->put($client_answer);
1291         }
1292     }
1293     $kernel->sig(CHLD => "child_reap");
1296 sub handle_task_debug {
1297     my $result = $_[ARG0];
1298     print STDERR "$result\n";
1301 sub handle_task_done {
1302     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1303     delete $heap->{task}->{$task_id};
1306 sub process_task {
1307     no strict "refs";
1308     #CHECK: Not @_[...]?
1309     my ($session, $heap, $task) = @_;
1310     my $error = 0;
1311     my $answer_l;
1312     my ($answer_header, @answer_target_l, $answer_source);
1313     my $client_answer = "";
1315     # prepare all variables needed to process message
1316     #my $msg = $task->{'xmlmessage'};
1317     my $msg = &decode_base64($task->{'xmlmessage'});
1318     my $incoming_id = $task->{'id'};
1319     my $module = $task->{'module'};
1320     my $header =  $task->{'headertag'};
1321     my $session_id = $task->{'sessionid'};
1322     my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1323     my $source = @{$msg_hash->{'source'}}[0];
1324     
1325     # set timestamp of incoming client uptodate, so client will not 
1326     # be deleted from known_clients because of expiration
1327     my $act_time = &get_time();
1328     my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'"; 
1329     my $res = $known_clients_db->exec_statement($sql);
1331     ######################
1332     # process incoming msg
1333     if( $error == 0) {
1334         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1335         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1336         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1338         if ( 0 < @{$answer_l} ) {
1339             my $answer_str = join("\n", @{$answer_l});
1340             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1341                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1342             }
1343             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,8);
1344         } else {
1345             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,8);
1346         }
1348     }
1349     if( !$answer_l ) { $error++ };
1351     ########
1352     # answer
1353     if( $error == 0 ) {
1355         foreach my $answer ( @{$answer_l} ) {
1356             # check outgoing msg to xml validity
1357             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1358             if( not defined $answer_hash ) { next; }
1359             
1360             $answer_header = @{$answer_hash->{'header'}}[0];
1361             @answer_target_l = @{$answer_hash->{'target'}};
1362             $answer_source = @{$answer_hash->{'source'}}[0];
1364             # deliver msg to all targets 
1365             foreach my $answer_target ( @answer_target_l ) {
1367                 # targets of msg are all gosa-si-clients in known_clients_db
1368                 if( $answer_target eq "*" ) {
1369                     # answer is for all clients
1370                     my $sql_statement= "SELECT * FROM known_clients";
1371                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1372                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1373                         my $host_name = $hit->{hostname};
1374                         my $host_key = $hit->{hostkey};
1375                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1376                         &update_jobdb_status_for_send_msgs($answer, $error);
1377                     }
1378                 }
1380                 # targets of msg are all gosa-si-server in known_server_db
1381                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1382                     # answer is for all server in known_server
1383                     my $sql_statement= "SELECT * FROM $known_server_tn";
1384                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1385                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1386                         my $host_name = $hit->{hostname};
1387                         my $host_key = $hit->{hostkey};
1388                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1389                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1390                         &update_jobdb_status_for_send_msgs($answer, $error);
1391                     }
1392                 }
1394                 # target of msg is GOsa
1395                                 elsif( $answer_target eq "GOSA" ) {
1396                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1397                                         my $add_on = "";
1398                     if( defined $session_id ) {
1399                         $add_on = ".session_id=$session_id";
1400                     }
1401                     # answer is for GOSA and has to returned to connected client
1402                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1403                     $client_answer = $gosa_answer.$add_on;
1404                 }
1406                 # target of msg is job queue at this host
1407                 elsif( $answer_target eq "JOBDB") {
1408                     $answer =~ /<header>(\S+)<\/header>/;   
1409                     my $header;
1410                     if( defined $1 ) { $header = $1; }
1411                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1412                     &update_jobdb_status_for_send_msgs($answer, $error);
1413                 }
1415                 # target of msg is a mac address
1416                 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 ) {
1417                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1418                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1419                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1420                     my $found_ip_flag = 0;
1421                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1422                         my $host_name = $hit->{hostname};
1423                         my $host_key = $hit->{hostkey};
1424                         $answer =~ s/$answer_target/$host_name/g;
1425                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1426                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1427                         &update_jobdb_status_for_send_msgs($answer, $error);
1428                         $found_ip_flag++ ;
1429                     }   
1430                     if( $found_ip_flag == 0) {
1431                         daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1432                     }
1434                 #  answer is for one specific host   
1435                 } else {
1436                     # get encrypt_key
1437                     my $encrypt_key = &get_encrypt_key($answer_target);
1438                     if( not defined $encrypt_key ) {
1439                         # unknown target
1440                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1441                         next;
1442                     }
1443                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1444                     &update_jobdb_status_for_send_msgs($answer, $error);
1445                 }
1446             }
1447         }
1448     }
1450     my $filter = POE::Filter::Reference->new();
1451     my %result = ( 
1452             status => "seems ok to me",
1453             answer => $client_answer,
1454             );
1456     my $output = $filter->put( [ \%result ] );
1457     print @$output;
1462 sub session_start {
1463     my ($kernel) = $_[KERNEL];
1464     $global_kernel = $kernel;
1465     $kernel->yield('register_at_foreign_servers');
1466         $kernel->yield('create_fai_server_db', $fai_server_tn );
1467         $kernel->yield('create_fai_release_db', $fai_release_tn );
1468     $kernel->yield('watch_for_next_tasks');
1469         $kernel->sig(USR1 => "sig_handler");
1470         $kernel->sig(USR2 => "recreate_packages_db");
1471         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1472         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1473     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1474         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1475     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1476         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1477     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1483 sub watch_for_done_jobs {
1484     #CHECK: $heap for what?
1485     my ($kernel,$heap) = @_[KERNEL, HEAP];
1487     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1488         my $res = $job_db->select_dbentry( $sql_statement );
1490     while( my ($id, $hit) = each %{$res} ) {
1491         my $jobdb_id = $hit->{id};
1492         my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1493         my $res = $job_db->del_dbentry($sql_statement); 
1494     }
1496     $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1500 # if a job got an update or was modified anyway, send to all other si-server an update message
1501 # of this jobs
1502 sub watch_for_modified_jobs {
1503     my ($kernel,$heap) = @_[KERNEL, HEAP];
1505     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE ((siserver='localhost') AND (modified='1'))"; 
1506     my $res = $job_db->select_dbentry( $sql_statement );
1507     
1508     # if db contains no jobs which should be update, do nothing
1509     if (keys %$res != 0) {
1511         if ($job_synchronization  eq "true") {
1512             # make out of the db result a gosa-si message   
1513             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1514  
1515             # update all other SI-server
1516             &inform_all_other_si_server($update_msg);
1517         }
1519         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1520         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1521         $res = $job_db->update_dbentry($sql_statement);
1522     }
1524     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1528 sub watch_for_new_jobs {
1529         if($watch_for_new_jobs_in_progress == 0) {
1530                 $watch_for_new_jobs_in_progress = 1;
1531                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1533                 # check gosa job quaeue for jobs with executable timestamp
1534                 my $timestamp = &get_time();
1535                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1536                 my $res = $job_db->exec_statement( $sql_statement );
1538                 # Merge all new jobs that would do the same actions
1539                 my @drops;
1540                 my $hits;
1541                 foreach my $hit (reverse @{$res} ) {
1542                         my $macaddress= lc @{$hit}[8];
1543                         my $headertag= @{$hit}[5];
1544                         if(
1545                                 defined($hits->{$macaddress}) &&
1546                                 defined($hits->{$macaddress}->{$headertag}) &&
1547                                 defined($hits->{$macaddress}->{$headertag}[0])
1548                         ) {
1549                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1550                         }
1551                         $hits->{$macaddress}->{$headertag}= $hit;
1552                 }
1554                 # Delete new jobs with a matching job in state 'processing'
1555                 foreach my $macaddress (keys %{$hits}) {
1556                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1557                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1558                                 if(defined($jobdb_id)) {
1559                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1560                                         my $res = $job_db->exec_statement( $sql_statement );
1561                                         foreach my $hit (@{$res}) {
1562                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1563                                         }
1564                                 } else {
1565                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1566                                 }
1567                         }
1568                 }
1570                 # Commit deletion
1571                 $job_db->exec_statementlist(\@drops);
1573                 # Look for new jobs that could be executed
1574                 foreach my $macaddress (keys %{$hits}) {
1576                         # Look if there is an executing job
1577                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1578                         my $res = $job_db->exec_statement( $sql_statement );
1580                         # Skip new jobs for host if there is a processing job
1581                         if(defined($res) and defined @{$res}[0]) {
1582                                 next;
1583                         }
1585                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1586                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1587                                 if(defined($jobdb_id)) {
1588                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1590                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1591                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1592                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1594                                         # expect macaddress is unique!!!!!!
1595                                         my $target = $res_hash->{1}->{hostname};
1597                                         # change header
1598                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1600                                         # add sqlite_id
1601                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1603                                         $job_msg =~ /<header>(\S+)<\/header>/;
1604                                         my $header = $1 ;
1605                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1607                                         # update status in job queue to 'processing'
1608                                         $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1609                                         my $res = $job_db->update_dbentry($sql_statement);
1610 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen                                        
1612                                         # We don't want parallel processing
1613                                         last;
1614                                 }
1615                         }
1616                 }
1618                 $watch_for_new_jobs_in_progress = 0;
1619                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1620         }
1625 sub watch_for_new_messages {
1626     my ($kernel,$heap) = @_[KERNEL, HEAP];
1627     my @coll_user_msg;   # collection list of outgoing messages
1628     
1629     # check messaging_db for new incoming messages with executable timestamp
1630     my $timestamp = &get_time();
1631     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1632     my $res = $messaging_db->exec_statement( $sql_statement );
1633         foreach my $hit (@{$res}) {
1635         # create outgoing messages
1636         my $message_to = @{$hit}[3];
1637         # translate message_to to plain login name
1638         my @message_to_l = split(/,/, $message_to);  
1639                 my %receiver_h; 
1640                 foreach my $receiver (@message_to_l) {
1641                         if ($receiver =~ /^u_([\s\S]*)$/) {
1642                                 $receiver_h{$1} = 0;
1643                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1644                                 my $group_name = $1;
1645                                 # fetch all group members from ldap and add them to receiver hash
1646                                 my $ldap_handle = &get_ldap_handle();
1647                                 if (defined $ldap_handle) {
1648                                                 my $mesg = $ldap_handle->search(
1649                                                                                 base => $ldap_base,
1650                                                                                 scope => 'sub',
1651                                                                                 attrs => ['memberUid'],
1652                                                                                 filter => "cn=$group_name",
1653                                                                                 );
1654                                                 if ($mesg->count) {
1655                                                                 my @entries = $mesg->entries;
1656                                                                 foreach my $entry (@entries) {
1657                                                                                 my @receivers= $entry->get_value("memberUid");
1658                                                                                 foreach my $receiver (@receivers) { 
1659                                                                                                 $receiver_h{$1} = 0;
1660                                                                                 }
1661                                                                 }
1662                                                 } 
1663                                                 # translating errors ?
1664                                                 if ($mesg->code) {
1665                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1666                                                 }
1667                                 # ldap handle error ?           
1668                                 } else {
1669                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1670                                 }
1671                         } else {
1672                                 my $sbjct = &encode_base64(@{$hit}[1]);
1673                                 my $msg = &encode_base64(@{$hit}[7]);
1674                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1675                         }
1676                 }
1677                 my @receiver_l = keys(%receiver_h);
1679         my $message_id = @{$hit}[0];
1681         #add each outgoing msg to messaging_db
1682         my $receiver;
1683         foreach $receiver (@receiver_l) {
1684             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1685                 "VALUES ('".
1686                 $message_id."', '".    # id
1687                 @{$hit}[1]."', '".     # subject
1688                 @{$hit}[2]."', '".     # message_from
1689                 $receiver."', '".      # message_to
1690                 "none"."', '".         # flag
1691                 "out"."', '".          # direction
1692                 @{$hit}[6]."', '".     # delivery_time
1693                 @{$hit}[7]."', '".     # message
1694                 $timestamp."'".     # timestamp
1695                 ")";
1696             &daemon_log("M DEBUG: $sql_statement", 1);
1697             my $res = $messaging_db->exec_statement($sql_statement);
1698             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1699         }
1701         # set incoming message to flag d=deliverd
1702         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1703         &daemon_log("M DEBUG: $sql_statement", 7);
1704         $res = $messaging_db->update_dbentry($sql_statement);
1705         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1706     }
1708     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1709     return;
1712 sub watch_for_delivery_messages {
1713     my ($kernel, $heap) = @_[KERNEL, HEAP];
1715     # select outgoing messages
1716     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1717     #&daemon_log("0 DEBUG: $sql", 7);
1718     my $res = $messaging_db->exec_statement( $sql_statement );
1719     
1720     # build out msg for each    usr
1721     foreach my $hit (@{$res}) {
1722         my $receiver = @{$hit}[3];
1723         my $msg_id = @{$hit}[0];
1724         my $subject = @{$hit}[1];
1725         my $message = @{$hit}[7];
1727         # resolve usr -> host where usr is logged in
1728         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1729         #&daemon_log("0 DEBUG: $sql", 7);
1730         my $res = $login_users_db->exec_statement($sql);
1732         # reciver is logged in nowhere
1733         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1735                 my $send_succeed = 0;
1736                 foreach my $hit (@$res) {
1737                                 my $receiver_host = @$hit[0];
1738                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1740                                 # fetch key to encrypt msg propperly for usr/host
1741                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1742                                 &daemon_log("0 DEBUG: $sql", 7);
1743                                 my $res = $known_clients_db->exec_statement($sql);
1745                                 # host is already down
1746                                 if (not ref(@$res[0]) eq "ARRAY") { next; }
1748                                 # host is on
1749                                 my $receiver_key = @{@{$res}[0]}[2];
1750                                 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1751                                 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1752                                 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1753                                 if ($error == 0 ) {
1754                                         $send_succeed++ ;
1755                                 }
1756                 }
1758                 if ($send_succeed) {
1759                                 # set outgoing msg at db to deliverd
1760                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
1761                                 &daemon_log("0 DEBUG: $sql", 7);
1762                                 my $res = $messaging_db->exec_statement($sql); 
1763                 }
1764         }
1766     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
1767     return;
1771 sub watch_for_done_messages {
1772     my ($kernel,$heap) = @_[KERNEL, HEAP];
1774     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
1775     #&daemon_log("0 DEBUG: $sql", 7);
1776     my $res = $messaging_db->exec_statement($sql); 
1778     foreach my $hit (@{$res}) {
1779         my $msg_id = @{$hit}[0];
1781         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
1782         #&daemon_log("0 DEBUG: $sql", 7); 
1783         my $res = $messaging_db->exec_statement($sql);
1785         # not all usr msgs have been seen till now
1786         if ( ref(@$res[0]) eq "ARRAY") { next; }
1787         
1788         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
1789         #&daemon_log("0 DEBUG: $sql", 7);
1790         $res = $messaging_db->exec_statement($sql);
1791     
1792     }
1794     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
1795     return;
1799 sub watch_for_old_known_clients {
1800     my ($kernel,$heap) = @_[KERNEL, HEAP];
1802     my $sql_statement = "SELECT * FROM $known_clients_tn";
1803     my $res = $known_clients_db->select_dbentry( $sql_statement );
1805     my $act_time = int(&get_time());
1807     while ( my ($hit_num, $hit) = each %$res) {
1808         my $expired_timestamp = int($hit->{'timestamp'});
1809         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
1810         my $dt = DateTime->new( year   => $1,
1811                 month  => $2,
1812                 day    => $3,
1813                 hour   => $4,
1814                 minute => $5,
1815                 second => $6,
1816                 );
1818         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
1819         $expired_timestamp = $dt->ymd('').$dt->hms('')."\n";
1820         if ($act_time > $expired_timestamp) {
1821             my $hostname = $hit->{'hostname'};
1822             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
1823             my $del_res = $known_clients_db->exec_statement($del_sql);
1825             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
1826         }
1828     }
1830     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1834 sub watch_for_next_tasks {
1835     my ($kernel,$heap) = @_[KERNEL, HEAP];
1837     my $sql = "SELECT * FROM $incoming_tn";
1838     my $res = $incoming_db->select_dbentry($sql);
1840     while ( my ($hit_num, $hit) = each %$res) {
1841         my $headertag = $hit->{'headertag'};
1842         if ($headertag =~ /^answer_(\d+)/) {
1843             # do not start processing, this message is for a still running POE::Wheel
1844             next;
1845         }
1846         my $message_id = $hit->{'id'};
1847         $kernel->yield('next_task', $hit);
1849         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
1850         my $res = $incoming_db->exec_statement($sql);
1851     }
1853     $kernel->delay_set('watch_for_next_tasks', 0.1); 
1857 sub get_ldap_handle {
1858         my ($session_id) = @_;
1859         my $heap;
1860         my $ldap_handle;
1862         if (not defined $session_id ) { $session_id = 0 };
1863         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1865         if ($session_id == 0) {
1866                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
1867                 $ldap_handle = Net::LDAP->new( $ldap_uri );
1868                 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!"); 
1870         } else {
1871                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1872                 if( defined $session_reference ) {
1873                         $heap = $session_reference->get_heap();
1874                 }
1876                 if (not defined $heap) {
1877                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
1878                         return;
1879                 }
1881                 # TODO: This "if" is nonsense, because it doesn't prove that the
1882                 #       used handle is still valid - or if we've to reconnect...
1883                 #if (not exists $heap->{ldap_handle}) {
1884                         $ldap_handle = Net::LDAP->new( $ldap_uri );
1885                         $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!"); 
1886                         $heap->{ldap_handle} = $ldap_handle;
1887                 #}
1888         }
1889         return $ldap_handle;
1893 sub change_fai_state {
1894     my ($st, $targets, $session_id) = @_;
1895     $session_id = 0 if not defined $session_id;
1896     # Set FAI state to localboot
1897     my %mapActions= (
1898         reboot    => '',
1899         update    => 'softupdate',
1900         localboot => 'localboot',
1901         reinstall => 'install',
1902         rescan    => '',
1903         wake      => '',
1904         memcheck  => 'memcheck',
1905         sysinfo   => 'sysinfo',
1906         install   => 'install',
1907     );
1909     # Return if this is unknown
1910     if (!exists $mapActions{ $st }){
1911         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
1912       return;
1913     }
1915     my $state= $mapActions{ $st };
1917     my $ldap_handle = &get_ldap_handle($session_id);
1918     if( defined($ldap_handle) ) {
1920       # Build search filter for hosts
1921         my $search= "(&(objectClass=GOhard)";
1922         foreach (@{$targets}){
1923             $search.= "(macAddress=$_)";
1924         }
1925         $search.= ")";
1927       # If there's any host inside of the search string, procress them
1928         if (!($search =~ /macAddress/)){
1929             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
1930             return;
1931         }
1933       # Perform search for Unit Tag
1934       my $mesg = $ldap_handle->search(
1935           base   => $ldap_base,
1936           scope  => 'sub',
1937           attrs  => ['dn', 'FAIstate', 'objectClass'],
1938           filter => "$search"
1939           );
1941           if ($mesg->count) {
1942                   my @entries = $mesg->entries;
1943                   if (0 == @entries) {
1944                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
1945                   }
1947                   foreach my $entry (@entries) {
1948                           # Only modify entry if it is not set to '$state'
1949                           if ($entry->get_value("FAIstate") ne "$state"){
1950                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1951                                   my $result;
1952                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1953                                   if (exists $tmp{'FAIobject'}){
1954                                           if ($state eq ''){
1955                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1956                                                           delete => [ FAIstate => [] ] ]);
1957                                           } else {
1958                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1959                                                           replace => [ FAIstate => $state ] ]);
1960                                           }
1961                                   } elsif ($state ne ''){
1962                                           $result= $ldap_handle->modify($entry->dn, changes => [
1963                                                   add     => [ objectClass => 'FAIobject' ],
1964                                                   add     => [ FAIstate => $state ] ]);
1965                                   }
1967                                   # Errors?
1968                                   if ($result->code){
1969                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1970                                   }
1971                           } else {
1972                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
1973                           }  
1974                   }
1975           } else {
1976                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
1977           }
1979     # if no ldap handle defined
1980     } else {
1981         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
1982     }
1984         return;
1988 sub change_goto_state {
1989     my ($st, $targets, $session_id) = @_;
1990     $session_id = 0  if not defined $session_id;
1992     # Switch on or off?
1993     my $state= $st eq 'active' ? 'active': 'locked';
1995     my $ldap_handle = &get_ldap_handle($session_id);
1996     if( defined($ldap_handle) ) {
1998       # Build search filter for hosts
1999       my $search= "(&(objectClass=GOhard)";
2000       foreach (@{$targets}){
2001         $search.= "(macAddress=$_)";
2002       }
2003       $search.= ")";
2005       # If there's any host inside of the search string, procress them
2006       if (!($search =~ /macAddress/)){
2007         return;
2008       }
2010       # Perform search for Unit Tag
2011       my $mesg = $ldap_handle->search(
2012           base   => $ldap_base,
2013           scope  => 'sub',
2014           attrs  => ['dn', 'gotoMode'],
2015           filter => "$search"
2016           );
2018       if ($mesg->count) {
2019         my @entries = $mesg->entries;
2020         foreach my $entry (@entries) {
2022           # Only modify entry if it is not set to '$state'
2023           if ($entry->get_value("gotoMode") ne $state){
2025             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2026             my $result;
2027             $result= $ldap_handle->modify($entry->dn, changes => [
2028                                                 replace => [ gotoMode => $state ] ]);
2030             # Errors?
2031             if ($result->code){
2032               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2033             }
2035           }
2036         }
2037       } else {
2038                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2039           }
2041     }
2045 sub run_recreate_packages_db {
2046     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2047     my $session_id = $session->ID;
2048         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2049         $kernel->yield('create_fai_release_db', $fai_release_tn);
2050         $kernel->yield('create_fai_server_db', $fai_server_tn);
2051         return;
2055 sub run_create_fai_server_db {
2056     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2057     my $session_id = $session->ID;
2058     my $task = POE::Wheel::Run->new(
2059             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2060             StdoutEvent  => "session_run_result",
2061             StderrEvent  => "session_run_debug",
2062             CloseEvent   => "session_run_done",
2063             );
2065     $heap->{task}->{ $task->ID } = $task;
2066     return;
2070 sub create_fai_server_db {
2071     my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2072         my $result;
2074         if (not defined $session_id) { $session_id = 0; }
2075     my $ldap_handle = &get_ldap_handle();
2076         if(defined($ldap_handle)) {
2077                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2078                 my $mesg= $ldap_handle->search(
2079                         base   => $ldap_base,
2080                         scope  => 'sub',
2081                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2082                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2083                 );
2084                 if($mesg->{'resultCode'} == 0 &&
2085                    $mesg->count != 0) {
2086                    foreach my $entry (@{$mesg->{entries}}) {
2087                            if($entry->exists('FAIrepository')) {
2088                                    # Add an entry for each Repository configured for server
2089                                    foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2090                                                    my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2091                                                    my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2092                                                    $result= $fai_server_db->add_dbentry( { 
2093                                                                    table => $table_name,
2094                                                                    primkey => ['server', 'release', 'tag'],
2095                                                                    server => $tmp_url,
2096                                                                    release => $tmp_release,
2097                                                                    sections => $tmp_sections,
2098                                                                    tag => (length($tmp_tag)>0)?$tmp_tag:"",
2099                                                            } );
2100                                            }
2101                                    }
2102                            }
2103                    }
2104                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2106                 # TODO: Find a way to post the 'create_packages_list_db' event
2107                 if(not defined($dont_create_packages_list)) {
2108                         &create_packages_list_db(undef, undef, $session_id);
2109                 }
2110         }       
2111     
2112     $ldap_handle->disconnect;
2113         return $result;
2117 sub run_create_fai_release_db {
2118     my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2119         my $session_id = $session->ID;
2120     my $task = POE::Wheel::Run->new(
2121             Program => sub { &create_fai_release_db($table_name, $session_id) },
2122             StdoutEvent  => "session_run_result",
2123             StderrEvent  => "session_run_debug",
2124             CloseEvent   => "session_run_done",
2125             );
2127     $heap->{task}->{ $task->ID } = $task;
2128     return;
2132 sub create_fai_release_db {
2133         my ($table_name, $session_id) = @_;
2134         my $result;
2136     # used for logging
2137     if (not defined $session_id) { $session_id = 0; }
2139     my $ldap_handle = &get_ldap_handle();
2140         if(defined($ldap_handle)) {
2141                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2142                 my $mesg= $ldap_handle->search(
2143                         base   => $ldap_base,
2144                         scope  => 'sub',
2145                         attrs  => [],
2146                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2147                 );
2148                 if($mesg->{'resultCode'} == 0 &&
2149                         $mesg->count != 0) {
2150                         # Walk through all possible FAI container ou's
2151                         my @sql_list;
2152                         my $timestamp= &get_time();
2153                         foreach my $ou (@{$mesg->{entries}}) {
2154                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2155                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2156                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2157                                         if(@tmp_array) {
2158                                                 foreach my $entry (@tmp_array) {
2159                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2160                                                                 my $sql= 
2161                                                                 "INSERT INTO $table_name "
2162                                                                 ."(timestamp, release, class, type, state) VALUES ("
2163                                                                 .$timestamp.","
2164                                                                 ."'".$entry->{'release'}."',"
2165                                                                 ."'".$entry->{'class'}."',"
2166                                                                 ."'".$entry->{'type'}."',"
2167                                                                 ."'".$entry->{'state'}."')";
2168                                                                 push @sql_list, $sql;
2169                                                         }
2170                                                 }
2171                                         }
2172                                 }
2173                         }
2175                         daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2176                         if(@sql_list) {
2177                                 unshift @sql_list, "VACUUM";
2178                                 unshift @sql_list, "DELETE FROM $table_name";
2179                                 $fai_release_db->exec_statementlist(\@sql_list);
2180                         }
2181                         daemon_log("$session_id DEBUG: Done with inserting",7);
2182                 }
2183                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2184         }
2185     $ldap_handle->disconnect;
2186         return $result;
2189 sub get_fai_types {
2190         my $tmp_classes = shift || return undef;
2191         my @result;
2193         foreach my $type(keys %{$tmp_classes}) {
2194                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2195                         my $entry = {
2196                                 type => $type,
2197                                 state => $tmp_classes->{$type}[0],
2198                         };
2199                         push @result, $entry;
2200                 }
2201         }
2203         return @result;
2206 sub get_fai_state {
2207         my $result = "";
2208         my $tmp_classes = shift || return $result;
2210         foreach my $type(keys %{$tmp_classes}) {
2211                 if(defined($tmp_classes->{$type}[0])) {
2212                         $result = $tmp_classes->{$type}[0];
2213                         
2214                 # State is equal for all types in class
2215                         last;
2216                 }
2217         }
2219         return $result;
2222 sub resolve_fai_classes {
2223         my ($fai_base, $ldap_handle, $session_id) = @_;
2224         if (not defined $session_id) { $session_id = 0; }
2225         my $result;
2226         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2227         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2228         my $fai_classes;
2230         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2231         my $mesg= $ldap_handle->search(
2232                 base   => $fai_base,
2233                 scope  => 'sub',
2234                 attrs  => ['cn','objectClass','FAIstate'],
2235                 filter => $fai_filter,
2236         );
2237         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2239         if($mesg->{'resultCode'} == 0 &&
2240                 $mesg->count != 0) {
2241                 foreach my $entry (@{$mesg->{entries}}) {
2242                         if($entry->exists('cn')) {
2243                                 my $tmp_dn= $entry->dn();
2245                                 # Skip classname and ou dn parts for class
2246                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2248                                 # Skip classes without releases
2249                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2250                                         next;
2251                                 }
2253                                 my $tmp_cn= $entry->get_value('cn');
2254                                 my $tmp_state= $entry->get_value('FAIstate');
2256                                 my $tmp_type;
2257                                 # Get FAI type
2258                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2259                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2260                                                 $tmp_type= $oclass;
2261                                                 last;
2262                                         }
2263                                 }
2265                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2266                                         # A Subrelease
2267                                         my @sub_releases = split(/,/, $tmp_release);
2269                                         # Walk through subreleases and build hash tree
2270                                         my $hash;
2271                                         while(my $tmp_sub_release = pop @sub_releases) {
2272                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2273                                         }
2274                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2275                                 } else {
2276                                         # A branch, no subrelease
2277                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2278                                 }
2279                         } elsif (!$entry->exists('cn')) {
2280                                 my $tmp_dn= $entry->dn();
2281                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2283                                 # Skip classes without releases
2284                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2285                                         next;
2286                                 }
2288                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2289                                         # A Subrelease
2290                                         my @sub_releases= split(/,/, $tmp_release);
2292                                         # Walk through subreleases and build hash tree
2293                                         my $hash;
2294                                         while(my $tmp_sub_release = pop @sub_releases) {
2295                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2296                                         }
2297                                         # Remove the last two characters
2298                                         chop($hash);
2299                                         chop($hash);
2301                                         eval('$fai_classes->'.$hash.'= {}');
2302                                 } else {
2303                                         # A branch, no subrelease
2304                                         if(!exists($fai_classes->{$tmp_release})) {
2305                                                 $fai_classes->{$tmp_release} = {};
2306                                         }
2307                                 }
2308                         }
2309                 }
2311                 # The hash is complete, now we can honor the copy-on-write based missing entries
2312                 foreach my $release (keys %$fai_classes) {
2313                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2314                 }
2315         }
2316         return $result;
2319 sub apply_fai_inheritance {
2320        my $fai_classes = shift || return {};
2321        my $tmp_classes;
2323        # Get the classes from the branch
2324        foreach my $class (keys %{$fai_classes}) {
2325                # Skip subreleases
2326                if($class =~ /^ou=.*$/) {
2327                        next;
2328                } else {
2329                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2330                }
2331        }
2333        # Apply to each subrelease
2334        foreach my $subrelease (keys %{$fai_classes}) {
2335                if($subrelease =~ /ou=/) {
2336                        foreach my $tmp_class (keys %{$tmp_classes}) {
2337                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2338                                        $fai_classes->{$subrelease}->{$tmp_class} =
2339                                        deep_copy($tmp_classes->{$tmp_class});
2340                                } else {
2341                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2342                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2343                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2344                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2345                                                }
2346                                        }
2347                                }
2348                        }
2349                }
2350        }
2352        # Find subreleases in deeper levels
2353        foreach my $subrelease (keys %{$fai_classes}) {
2354                if($subrelease =~ /ou=/) {
2355                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2356                                if($subsubrelease =~ /ou=/) {
2357                                        apply_fai_inheritance($fai_classes->{$subrelease});
2358                                }
2359                        }
2360                }
2361        }
2363        return $fai_classes;
2366 sub get_fai_release_entries {
2367         my $tmp_classes = shift || return;
2368         my $parent = shift || "";
2369         my @result = shift || ();
2371         foreach my $entry (keys %{$tmp_classes}) {
2372                 if(defined($entry)) {
2373                         if($entry =~ /^ou=.*$/) {
2374                                 my $release_name = $entry;
2375                                 $release_name =~ s/ou=//g;
2376                                 if(length($parent)>0) {
2377                                         $release_name = $parent."/".$release_name;
2378                                 }
2379                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2380                                 foreach my $bufentry(@bufentries) {
2381                                         push @result, $bufentry;
2382                                 }
2383                         } else {
2384                                 my @types = get_fai_types($tmp_classes->{$entry});
2385                                 foreach my $type (@types) {
2386                                         push @result, 
2387                                         {
2388                                                 'class' => $entry,
2389                                                 'type' => $type->{'type'},
2390                                                 'release' => $parent,
2391                                                 'state' => $type->{'state'},
2392                                         };
2393                                 }
2394                         }
2395                 }
2396         }
2398         return @result;
2401 sub deep_copy {
2402         my $this = shift;
2403         if (not ref $this) {
2404                 $this;
2405         } elsif (ref $this eq "ARRAY") {
2406                 [map deep_copy($_), @$this];
2407         } elsif (ref $this eq "HASH") {
2408                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2409         } else { die "what type is $_?" }
2413 sub session_run_result {
2414     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2415     $kernel->sig(CHLD => "child_reap");
2418 sub session_run_debug {
2419     my $result = $_[ARG0];
2420     print STDERR "$result\n";
2423 sub session_run_done {
2424     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2425     delete $heap->{task}->{$task_id};
2429 sub create_sources_list {
2430         my $session_id = shift;
2431         my $ldap_handle = &main::get_ldap_handle;
2432         my $result="/tmp/gosa_si_tmp_sources_list";
2434         # Remove old file
2435         if(stat($result)) {
2436                 unlink($result);
2437                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2438         }
2440         my $fh;
2441         open($fh, ">$result");
2442         if (not defined $fh) {
2443                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2444                 return undef;
2445         }
2446         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2447                 my $mesg=$ldap_handle->search(
2448                         base    => $main::ldap_server_dn,
2449                         scope   => 'base',
2450                         attrs   => 'FAIrepository',
2451                         filter  => 'objectClass=FAIrepositoryServer'
2452                 );
2453                 if($mesg->count) {
2454                         foreach my $entry(@{$mesg->{'entries'}}) {
2455                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2456                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2457                                         my $line = "deb $server $release";
2458                                         $sections =~ s/,/ /g;
2459                                         $line.= " $sections";
2460                                         print $fh $line."\n";
2461                                 }
2462                         }
2463                 }
2464         } else {
2465                 if (defined $main::ldap_server_dn){
2466                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2467                 } else {
2468                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2469                 }
2470         }
2471         close($fh);
2473         return $result;
2477 sub run_create_packages_list_db {
2478     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2479         my $session_id = $session->ID;
2481         my $task = POE::Wheel::Run->new(
2482                                         Priority => +20,
2483                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2484                                         StdoutEvent  => "session_run_result",
2485                                         StderrEvent  => "session_run_debug",
2486                                         CloseEvent   => "session_run_done",
2487                                         );
2488         $heap->{task}->{ $task->ID } = $task;
2492 sub create_packages_list_db {
2493         my ($ldap_handle, $sources_file, $session_id) = @_;
2494         
2495         # it should not be possible to trigger a recreation of packages_list_db
2496         # while packages_list_db is under construction, so set flag packages_list_under_construction
2497         # which is tested befor recreation can be started
2498         if (-r $packages_list_under_construction) {
2499                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2500                 return;
2501         } else {
2502                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2503                 # set packages_list_under_construction to true
2504                 system("touch $packages_list_under_construction");
2505                 @packages_list_statements=();
2506         }
2508         if (not defined $session_id) { $session_id = 0; }
2509         if (not defined $ldap_handle) { 
2510                 $ldap_handle= &get_ldap_handle();
2512                 if (not defined $ldap_handle) {
2513                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2514                         unlink($packages_list_under_construction);
2515                         return;
2516                 }
2517         }
2518         if (not defined $sources_file) { 
2519                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2520                 $sources_file = &create_sources_list($session_id);
2521         }
2523         if (not defined $sources_file) {
2524                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2525                 unlink($packages_list_under_construction);
2526                 return;
2527         }
2529         my $line;
2531         open(CONFIG, "<$sources_file") or do {
2532                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2533                 unlink($packages_list_under_construction);
2534                 return;
2535         };
2537         # Read lines
2538         while ($line = <CONFIG>){
2539                 # Unify
2540                 chop($line);
2541                 $line =~ s/^\s+//;
2542                 $line =~ s/^\s+/ /;
2544                 # Strip comments
2545                 $line =~ s/#.*$//g;
2547                 # Skip empty lines
2548                 if ($line =~ /^\s*$/){
2549                         next;
2550                 }
2552                 # Interpret deb line
2553                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2554                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2555                         my $section;
2556                         foreach $section (split(' ', $sections)){
2557                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2558                         }
2559                 }
2560         }
2562         close (CONFIG);
2564         find(\&cleanup_and_extract, keys( %repo_dirs ));
2565         &main::strip_packages_list_statements();
2566         unshift @packages_list_statements, "VACUUM";
2567         $packages_list_db->exec_statementlist(\@packages_list_statements);
2568         unlink($packages_list_under_construction);
2569         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2570         return;
2573 # This function should do some intensive task to minimize the db-traffic
2574 sub strip_packages_list_statements {
2575     my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2576         my @new_statement_list=();
2577         my $hash;
2578         my $insert_hash;
2579         my $update_hash;
2580         my $delete_hash;
2581         my $local_timestamp=get_time();
2583         foreach my $existing_entry (@existing_entries) {
2584                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2585         }
2587         foreach my $statement (@packages_list_statements) {
2588                 if($statement =~ /^INSERT/i) {
2589                         # Assign the values from the insert statement
2590                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2591                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2592                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2593                                 # If section or description has changed, update the DB
2594                                 if( 
2595                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2596                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2597                                 ) {
2598                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2599                                 }
2600                         } else {
2601                                 # Insert a non-existing entry to db
2602                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2603                         }
2604                 } elsif ($statement =~ /^UPDATE/i) {
2605                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2606                         /^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;
2607                         foreach my $distribution (keys %{$hash}) {
2608                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2609                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2610                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2611                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2612                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2613                                                 my $section;
2614                                                 my $description;
2615                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2616                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2617                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2618                                                 }
2619                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2620                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2621                                                 }
2622                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2623                                         }
2624                                 }
2625                         }
2626                 }
2627         }
2629         # TODO: Check for orphaned entries
2631         # unroll the insert_hash
2632         foreach my $distribution (keys %{$insert_hash}) {
2633                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2634                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2635                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2636                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2637                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2638                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2639                                 ."'$local_timestamp')";
2640                         }
2641                 }
2642         }
2644         # unroll the update hash
2645         foreach my $distribution (keys %{$update_hash}) {
2646                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2647                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2648                                 my $set = "";
2649                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2650                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2651                                 }
2652                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2653                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2654                                 }
2655                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2656                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2657                                 }
2658                                 if(defined($set) and length($set) > 0) {
2659                                         $set .= "timestamp = '$local_timestamp'";
2660                                 } else {
2661                                         next;
2662                                 }
2663                                 push @new_statement_list, 
2664                                         "UPDATE $main::packages_list_tn SET $set WHERE"
2665                                         ." distribution = '$distribution'"
2666                                         ." AND package = '$package'"
2667                                         ." AND version = '$version'";
2668                         }
2669                 }
2670         }
2672         @packages_list_statements = @new_statement_list;
2676 sub parse_package_info {
2677     my ($baseurl, $dist, $section, $session_id)= @_;
2678     my ($package);
2679     if (not defined $session_id) { $session_id = 0; }
2680     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2681     $repo_dirs{ "${repo_path}/pool" } = 1;
2683     foreach $package ("Packages.gz"){
2684         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2685         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2686         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2687     }
2688     
2692 sub get_package {
2693     my ($url, $dest, $session_id)= @_;
2694     if (not defined $session_id) { $session_id = 0; }
2696     my $tpath = dirname($dest);
2697     -d "$tpath" || mkpath "$tpath";
2699     # This is ugly, but I've no time to take a look at "how it works in perl"
2700     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2701         system("gunzip -cd '$dest' > '$dest.in'");
2702         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2703         unlink($dest);
2704         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
2705     } else {
2706         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2707     }
2708     return 0;
2712 sub parse_package {
2713     my ($path, $dist, $srv_path, $session_id)= @_;
2714     if (not defined $session_id) { $session_id = 0;}
2715     my ($package, $version, $section, $description);
2716     my $PACKAGES;
2717     my $timestamp = &get_time();
2719     if(not stat("$path.in")) {
2720         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2721         return;
2722     }
2724     open($PACKAGES, "<$path.in");
2725     if(not defined($PACKAGES)) {
2726         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
2727         return;
2728     }
2730     # Read lines
2731     while (<$PACKAGES>){
2732         my $line = $_;
2733         # Unify
2734         chop($line);
2736         # Use empty lines as a trigger
2737         if ($line =~ /^\s*$/){
2738             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2739             push(@packages_list_statements, $sql);
2740             $package = "none";
2741             $version = "none";
2742             $section = "none";
2743             $description = "none"; 
2744             next;
2745         }
2747         # Trigger for package name
2748         if ($line =~ /^Package:\s/){
2749             ($package)= ($line =~ /^Package: (.*)$/);
2750             next;
2751         }
2753         # Trigger for version
2754         if ($line =~ /^Version:\s/){
2755             ($version)= ($line =~ /^Version: (.*)$/);
2756             next;
2757         }
2759         # Trigger for description
2760         if ($line =~ /^Description:\s/){
2761             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2762             next;
2763         }
2765         # Trigger for section
2766         if ($line =~ /^Section:\s/){
2767             ($section)= ($line =~ /^Section: (.*)$/);
2768             next;
2769         }
2771         # Trigger for filename
2772         if ($line =~ /^Filename:\s/){
2773             my ($filename) = ($line =~ /^Filename: (.*)$/);
2774             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2775             next;
2776         }
2777     }
2779     close( $PACKAGES );
2780     unlink( "$path.in" );
2784 sub store_fileinfo {
2785     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2787     my %fileinfo = (
2788         'package' => $package,
2789         'dist' => $dist,
2790         'version' => $vers,
2791     );
2793     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2797 sub cleanup_and_extract {
2798     my $fileinfo = $repo_files{ $File::Find::name };
2800     if( defined $fileinfo ) {
2802         my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2803         my $sql;
2804         my $package = $fileinfo->{ 'package' };
2805         my $newver = $fileinfo->{ 'version' };
2807         mkpath($dir);
2808         system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2810                 if( -f "$dir/DEBIAN/templates" ) {
2812                         daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2814                         my $tmpl= "";
2815                         {
2816                                 local $/=undef;
2817                                 open FILE, "$dir/DEBIAN/templates";
2818                                 $tmpl = &encode_base64(<FILE>);
2819                                 close FILE;
2820                         }
2821                         rmtree("$dir/DEBIAN/templates");
2823                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2824                 push @packages_list_statements, $sql;
2825                 }
2826     }
2828     return;
2832 sub register_at_foreign_servers {   
2833     my ($kernel) = $_[KERNEL];
2835     # hole alle bekannten server aus known_server_db
2836     my $server_sql = "SELECT * FROM $known_server_tn";
2837     my $server_res = $known_server_db->exec_statement($server_sql);
2839     # no entries in known_server_db
2840     if (not ref(@$server_res[0]) eq "ARRAY") { 
2841         # TODO
2842     }
2844     # detect already connected clients
2845     my $client_sql = "SELECT * FROM $known_clients_tn"; 
2846     my $client_res = $known_clients_db->exec_statement($client_sql);
2848     # send my server details to all other gosa-si-server within the network
2849     foreach my $hit (@$server_res) {
2850         my $hostname = @$hit[0];
2851         my $hostkey = &create_passwd;
2853         # add already connected clients to registration message 
2854         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
2855         &add_content2xml_hash($myhash, 'key', $hostkey);
2856         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
2857         
2858         # build registration message and send it
2859         my $foreign_server_msg = &create_xml_string($myhash);
2860         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
2861     }
2862     
2863     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
2864     return;
2868 #==== MAIN = main ==============================================================
2869 #  parse commandline options
2870 Getopt::Long::Configure( "bundling" );
2871 GetOptions("h|help" => \&usage,
2872         "c|config=s" => \$cfg_file,
2873         "f|foreground" => \$foreground,
2874         "v|verbose+" => \$verbose,
2875         "no-arp+" => \$no_arp,
2876            );
2878 #  read and set config parameters
2879 &check_cmdline_param ;
2880 &read_configfile;
2881 &check_pid;
2883 $SIG{CHLD} = 'IGNORE';
2885 # forward error messages to logfile
2886 if( ! $foreground ) {
2887   open( STDIN,  '+>/dev/null' );
2888   open( STDOUT, '+>&STDIN'    );
2889   open( STDERR, '+>&STDIN'    );
2892 # Just fork, if we are not in foreground mode
2893 if( ! $foreground ) { 
2894     chdir '/'                 or die "Can't chdir to /: $!";
2895     $pid = fork;
2896     setsid                    or die "Can't start a new session: $!";
2897     umask 0;
2898 } else { 
2899     $pid = $$; 
2902 # Do something useful - put our PID into the pid_file
2903 if( 0 != $pid ) {
2904     open( LOCK_FILE, ">$pid_file" );
2905     print LOCK_FILE "$pid\n";
2906     close( LOCK_FILE );
2907     if( !$foreground ) { 
2908         exit( 0 ) 
2909     };
2912 # parse head url and revision from svn
2913 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2914 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2915 $server_headURL = defined $1 ? $1 : 'unknown' ;
2916 $server_revision = defined $2 ? $2 : 'unknown' ;
2917 if ($server_headURL =~ /\/tag\// || 
2918         $server_headURL =~ /\/branches\// ) {
2919     $server_status = "stable"; 
2920 } else {
2921     $server_status = "developmental" ;
2925 daemon_log(" ", 1);
2926 daemon_log("$0 started!", 1);
2927 daemon_log("status: $server_status", 1);
2928 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
2930 # connect to incoming_db
2931 unlink($incoming_file_name);
2932 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
2933 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
2935 # connect to gosa-si job queue
2936 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2937 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2939 # connect to known_clients_db
2940 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2941 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2943 # connect to foreign_clients_db
2944 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
2945 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
2947 # connect to known_server_db
2948 unlink($known_server_file_name);
2949 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2950 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2952 # connect to login_usr_db
2953 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2954 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2956 # connect to fai_server_db and fai_release_db
2957 unlink($fai_server_file_name);
2958 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2959 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2961 unlink($fai_release_file_name);
2962 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2963 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2965 # connect to packages_list_db
2966 #unlink($packages_list_file_name);
2967 unlink($packages_list_under_construction);
2968 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2969 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2971 # connect to messaging_db
2972 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2973 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2976 # create xml object used for en/decrypting
2977 $xml = new XML::Simple();
2980 # foreign servers 
2981 my @foreign_server_list;
2983 # add foreign server from cfg file
2984 if ($foreign_server_string ne "") {
2985     my @cfg_foreign_server_list = split(",", $foreign_server_string);
2986     foreach my $foreign_server (@cfg_foreign_server_list) {
2987         push(@foreign_server_list, $foreign_server);
2988     }
2991 # add foreign server from dns
2992 my @tmp_servers;
2993 if ( !$server_domain) {
2994     # Try our DNS Searchlist
2995     for my $domain(get_dns_domains()) {
2996         chomp($domain);
2997         my @tmp_domains= &get_server_addresses($domain);
2998         if(@tmp_domains) {
2999             for my $tmp_server(@tmp_domains) {
3000                 push @tmp_servers, $tmp_server;
3001             }
3002         }
3003     }
3004     if(@tmp_servers && length(@tmp_servers)==0) {
3005         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3006     }
3007 } else {
3008     @tmp_servers = &get_server_addresses($server_domain);
3009     if( 0 == @tmp_servers ) {
3010         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3011     }
3013 foreach my $server (@tmp_servers) { 
3014     unshift(@foreign_server_list, $server); 
3016 # eliminate duplicate entries
3017 @foreign_server_list = &del_doubles(@foreign_server_list);
3018 my $all_foreign_server = join(", ", @foreign_server_list);
3019 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
3021 # add all found foreign servers to known_server
3022 my $act_timestamp = &get_time();
3023 foreach my $foreign_server (@foreign_server_list) {
3025         # do not add myself to known_server_db
3026         if (&is_local($foreign_server)) { next; }
3027         ######################################
3029     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3030             primkey=>['hostname'],
3031             hostname=>$foreign_server,
3032             status=>'not_jet_registered',
3033             hostkey=>"none",
3034             timestamp=>$act_timestamp,
3035             } );
3039 # import all modules
3040 &import_modules;
3041 # check wether all modules are gosa-si valid passwd check
3042 &password_check;
3045 POE::Component::Server::TCP->new(
3046     Alias => "TCP_SERVER",
3047         Port => $server_port,
3048         ClientInput => sub {
3049         my ($kernel, $input) = @_[KERNEL, ARG0];
3050         push(@tasks, $input);
3051         push(@msgs_to_decrypt, $input);
3052         $kernel->yield("msg_to_decrypt");
3053         },
3054     InlineStates => {
3055         msg_to_decrypt => \&msg_to_decrypt,
3056         next_task => \&next_task,
3057         task_result => \&handle_task_result,
3058         task_done   => \&handle_task_done,
3059         task_debug  => \&handle_task_debug,
3060         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3061     }
3062 );
3064 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
3066 # create session for repeatedly checking the job queue for jobs
3067 POE::Session->create(
3068         inline_states => {
3069                 _start => \&session_start,
3070         register_at_foreign_servers => \&register_at_foreign_servers,
3071         sig_handler => \&sig_handler,
3072         next_task => \&next_task,
3073         task_result => \&handle_task_result,
3074         task_done   => \&handle_task_done,
3075         task_debug  => \&handle_task_debug,
3076         watch_for_next_tasks => \&watch_for_next_tasks,
3077         watch_for_new_messages => \&watch_for_new_messages,
3078         watch_for_delivery_messages => \&watch_for_delivery_messages,
3079         watch_for_done_messages => \&watch_for_done_messages,
3080                 watch_for_new_jobs => \&watch_for_new_jobs,
3081         watch_for_modified_jobs => \&watch_for_modified_jobs,
3082         watch_for_done_jobs => \&watch_for_done_jobs,
3083         watch_for_old_known_clients => \&watch_for_old_known_clients,
3084         create_packages_list_db => \&run_create_packages_list_db,
3085         create_fai_server_db => \&run_create_fai_server_db,
3086         create_fai_release_db => \&run_create_fai_release_db,
3087                 recreate_packages_db => \&run_recreate_packages_db,
3088         session_run_result => \&session_run_result,
3089         session_run_debug => \&session_run_debug,
3090         session_run_done => \&session_run_done,
3091         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3092         }
3093 );
3096 POE::Kernel->run();
3097 exit;