Code

831ca6f550d44c6b7f220b3f832785c9ffeeea97
[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 # specifies the verbosity of the daemon_log
104 $verbose = 0 ;
106 # if foreground is not null, script will be not forked to background
107 $foreground = 0 ;
109 # specifies the timeout seconds while checking the online status of a registrating client
110 $ping_timeout = 5;
112 $no_arp = 0;
113 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
114 my @packages_list_statements;
115 my $watch_for_new_jobs_in_progress = 0;
117 # holds all incoming decrypted messages
118 our $incoming_db;
119 our $incoming_tn = 'incoming';
120 my $incoming_file_name;
121 my @incoming_col_names = ("id INTEGER PRIMARY KEY", 
122         "timestamp DEFAULT 'none'", 
123         "headertag DEFAULT 'none'",
124                 "targettag DEFAULT 'none'",
125         "xmlmessage DEFAULT 'none'",
126         "module DEFAULT 'none'",
127         "sessionid DEFAULT '0'",
128         );
130 # holds all gosa jobs
131 our $job_db;
132 our $job_queue_tn = 'jobs';
133 my $job_queue_file_name;
134 my @job_queue_col_names = ("id INTEGER PRIMARY KEY", 
135                 "timestamp DEFAULT 'none'", 
136                 "status DEFAULT 'none'", 
137                 "result DEFAULT 'none'", 
138                 "progress DEFAULT 'none'", 
139         "headertag DEFAULT 'none'", 
140                 "targettag DEFAULT 'none'", 
141                 "xmlmessage DEFAULT 'none'", 
142                 "macaddress DEFAULT 'none'",
143                 "plainname DEFAULT 'none'",
144         "siserver DEFAULT 'none'",
145         "modified DEFAULT '0'",
146                 );
148 # holds all other gosa-si-server
149 our $known_server_db;
150 our $known_server_tn = "known_server";
151 my $known_server_file_name;
152 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
154 # holds all registrated clients
155 our $known_clients_db;
156 our $known_clients_tn = "known_clients";
157 my $known_clients_file_name;
158 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events", "keylifetime");
160 # holds all registered clients at a foreign server
161 our $foreign_clients_db;
162 our $foreign_clients_tn = "foreign_clients"; 
163 my $foreign_clients_file_name;
164 my @foreign_clients_col_names = ("hostname", "macaddress", "regserver", "timestamp");
166 # holds all logged in user at each client 
167 our $login_users_db;
168 our $login_users_tn = "login_users";
169 my $login_users_file_name;
170 my @login_users_col_names = ("client", "user", "timestamp");
172 # holds all fai server, the debian release and tag
173 our $fai_server_db;
174 our $fai_server_tn = "fai_server"; 
175 my $fai_server_file_name;
176 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag"); 
178 our $fai_release_db;
179 our $fai_release_tn = "fai_release"; 
180 my $fai_release_file_name;
181 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state"); 
183 # holds all packages available from different repositories
184 our $packages_list_db;
185 our $packages_list_tn = "packages_list";
186 my $packages_list_file_name;
187 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
188 my $outdir = "/tmp/packages_list_db";
189 my $arch = "i386"; 
191 # holds all messages which should be delivered to a user
192 our $messaging_db;
193 our $messaging_tn = "messaging"; 
194 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to", 
195         "flag", "direction", "delivery_time", "message", "timestamp" );
196 my $messaging_file_name;
198 # path to directory to store client install log files
199 our $client_fai_log_dir = "/var/log/fai"; 
201 # queue which stores taskes until one of the $max_children children are ready to process the task
202 my @tasks = qw();
203 my @msgs_to_decrypt = qw();
204 my $max_children = 2;
207 %cfg_defaults = (
208 "general" => {
209     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
210     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
211     },
212 "server" => {
213     "ip" => [\$server_ip, "0.0.0.0"],
214     "port" => [\$server_port, "20081"],
215     "known-clients"        => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
216     "known-servers"        => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
217     "incoming"             => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
218     "login-users"          => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
219     "fai-server"           => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
220     "fai-release"          => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
221     "packages-list"        => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
222     "messaging"            => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
223     "foreign-clients"      => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
224     "source-list"          => [\$sources_list, '/etc/apt/sources.list'],
225     "repo-path"            => [\$repo_path, '/srv/www/repository'],
226     "ldap-uri"             => [\$ldap_uri, ""],
227     "ldap-base"            => [\$ldap_base, ""],
228     "ldap-admin-dn"        => [\$ldap_admin_dn, ""],
229     "ldap-admin-password"  => [\$ldap_admin_password, ""],
230     "gosa-unit-tag"        => [\$gosa_unit_tag, ""],
231     "max-clients"          => [\$max_clients, 10],
232     "wol-password"           => [\$wake_on_lan_passwd, ""],
233     },
234 "GOsaPackages" => {
235     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
236     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
237     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
238     "key" => [\$GosaPackages_key, "none"],
239     },
240 "ClientPackages" => {
241     "key" => [\$ClientPackages_key, "none"],
242     },
243 "ServerPackages"=> {
244     "address"      => [\$foreign_server_string, ""],
245     "domain"  => [\$server_domain, ""],
246     "key"     => [\$ServerPackages_key, "none"],
247     "key-lifetime" => [\$foreign_servers_register_delay, 120],
248     "job-synchronization-enabled" => [\$job_synchronization, "true"],
249     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
250     },
251 "ArpHandler" => {
252     "enabled"   => [\$arp_enabled, "true"],
253     "interface" => [\$arp_interface, "all"],
254         },
256 );
259 #===  FUNCTION  ================================================================
260 #         NAME:  usage
261 #   PARAMETERS:  nothing
262 #      RETURNS:  nothing
263 #  DESCRIPTION:  print out usage text to STDERR
264 #===============================================================================
265 sub usage {
266     print STDERR << "EOF" ;
267 usage: $prg [-hvf] [-c config]
269            -h        : this (help) message
270            -c <file> : config file
271            -f        : foreground, process will not be forked to background
272            -v        : be verbose (multiple to increase verbosity)
273            -no-arp   : starts $prg without connection to arp module
274  
275 EOF
276     print "\n" ;
280 #===  FUNCTION  ================================================================
281 #         NAME:  read_configfile
282 #   PARAMETERS:  cfg_file - string -
283 #      RETURNS:  nothing
284 #  DESCRIPTION:  read cfg_file and set variables
285 #===============================================================================
286 sub read_configfile {
287     my $cfg;
288     if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
289         if( -r $cfg_file ) {
290             $cfg = Config::IniFiles->new( -file => $cfg_file );
291         } else {
292             print STDERR "Couldn't read config file!\n";
293         }
294     } else {
295         $cfg = Config::IniFiles->new() ;
296     }
297     foreach my $section (keys %cfg_defaults) {
298         foreach my $param (keys %{$cfg_defaults{ $section }}) {
299             my $pinfo = $cfg_defaults{ $section }{ $param };
300             ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
301         }
302     }
306 #===  FUNCTION  ================================================================
307 #         NAME:  logging
308 #   PARAMETERS:  level - string - default 'info'
309 #                msg - string -
310 #                facility - string - default 'LOG_DAEMON'
311 #      RETURNS:  nothing
312 #  DESCRIPTION:  function for logging
313 #===============================================================================
314 sub daemon_log {
315     # log into log_file
316     my( $msg, $level ) = @_;
317     if(not defined $msg) { return }
318     if(not defined $level) { $level = 1 }
319     if(defined $log_file){
320         open(LOG_HANDLE, ">>$log_file");
321         chmod 0600, $log_file;
322         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
323             print STDERR "cannot open $log_file: $!";
324             return 
325         }
326         chomp($msg);
327         $msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
328         if($level <= $verbose){
329             my ($seconds, $minutes, $hours, $monthday, $month,
330                     $year, $weekday, $yearday, $sommertime) = localtime(time);
331             $hours = $hours < 10 ? $hours = "0".$hours : $hours;
332             $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
333             $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
334             my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
335             $month = $monthnames[$month];
336             $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
337             $year+=1900;
338             my $name = $prg;
340             my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
341             print LOG_HANDLE $log_msg;
342             if( $foreground ) { 
343                 print STDERR $log_msg;
344             }
345         }
346         close( LOG_HANDLE );
347     }
351 #===  FUNCTION  ================================================================
352 #         NAME:  check_cmdline_param
353 #   PARAMETERS:  nothing
354 #      RETURNS:  nothing
355 #  DESCRIPTION:  validates commandline parameter
356 #===============================================================================
357 sub check_cmdline_param () {
358     my $err_config;
359     my $err_counter = 0;
360         if(not defined($cfg_file)) {
361                 $cfg_file = "/etc/gosa-si/server.conf";
362                 if(! -r $cfg_file) {
363                         $err_config = "please specify a config file";
364                         $err_counter += 1;
365                 }
366     }
367     if( $err_counter > 0 ) {
368         &usage( "", 1 );
369         if( defined( $err_config)) { print STDERR "$err_config\n"}
370         print STDERR "\n";
371         exit( -1 );
372     }
376 #===  FUNCTION  ================================================================
377 #         NAME:  check_pid
378 #   PARAMETERS:  nothing
379 #      RETURNS:  nothing
380 #  DESCRIPTION:  handels pid processing
381 #===============================================================================
382 sub check_pid {
383     $pid = -1;
384     # Check, if we are already running
385     if( open(LOCK_FILE, "<$pid_file") ) {
386         $pid = <LOCK_FILE>;
387         if( defined $pid ) {
388             chomp( $pid );
389             if( -f "/proc/$pid/stat" ) {
390                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
391                 if( $stat ) {
392                                         daemon_log("ERROR: Already running",1);
393                     close( LOCK_FILE );
394                     exit -1;
395                 }
396             }
397         }
398         close( LOCK_FILE );
399         unlink( $pid_file );
400     }
402     # create a syslog msg if it is not to possible to open PID file
403     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
404         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
405         if (open(LOCK_FILE, '<', $pid_file)
406                 && ($pid = <LOCK_FILE>))
407         {
408             chomp($pid);
409             $msg .= "(PID $pid)\n";
410         } else {
411             $msg .= "(unable to read PID)\n";
412         }
413         if( ! ($foreground) ) {
414             openlog( $0, "cons,pid", "daemon" );
415             syslog( "warning", $msg );
416             closelog();
417         }
418         else {
419             print( STDERR " $msg " );
420         }
421         exit( -1 );
422     }
425 #===  FUNCTION  ================================================================
426 #         NAME:  import_modules
427 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
428 #                are stored
429 #      RETURNS:  nothing
430 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
431 #                state is on is imported by "require 'file';"
432 #===============================================================================
433 sub import_modules {
434     daemon_log(" ", 1);
436     if (not -e $modules_path) {
437         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
438     }
440     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
441     while (defined (my $file = readdir (DIR))) {
442         if (not $file =~ /(\S*?).pm$/) {
443             next;
444         }
445                 my $mod_name = $1;
447         # ArpHandler switch
448         if( $file =~ /ArpHandler.pm/ ) {
449             if( $arp_enabled eq "false" ) { next; }
450         }
451         
452         eval { require $file; };
453         if ($@) {
454             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
455             daemon_log("$@", 5);
456                 } else {
457                         my $info = eval($mod_name.'::get_module_info()');
458                         # Only load module if get_module_info() returns a non-null object
459                         if( $info ) {
460                                 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
461                                 $known_modules->{$mod_name} = $info;
462                                 daemon_log("0 INFO: module $mod_name loaded", 5);
463                         }
464                 }
465     }   
466     close (DIR);
469 #===  FUNCTION  ================================================================
470 #         NAME:  password_check
471 #   PARAMETERS:  nothing
472 #      RETURNS:  nothing
473 #  DESCRIPTION:  escalates an critical error if two modules exist which are avaialable by 
474 #                the same password
475 #===============================================================================
476 sub password_check {
477     my $passwd_hash = {};
478     while (my ($mod_name, $mod_info) = each %$known_modules) {
479         my $mod_passwd = @$mod_info[1];
480         if (not defined $mod_passwd) { next; }
481         if (not exists $passwd_hash->{$mod_passwd}) {
482             $passwd_hash->{$mod_passwd} = $mod_name;
484         # escalates critical error
485         } else {
486             &daemon_log("0 ERROR: two loaded modules do have the same password. Please modify the 'key'-parameter in config file");
487             &daemon_log("0 ERROR: module='$mod_name' and module='".$passwd_hash->{$mod_passwd}."'");
488             exit( -1 );
489         }
490     }
495 #===  FUNCTION  ================================================================
496 #         NAME:  sig_int_handler
497 #   PARAMETERS:  signal - string - signal arose from system
498 #      RETURNS:  nothing
499 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
500 #===============================================================================
501 sub sig_int_handler {
502     my ($signal) = @_;
504 #       if (defined($ldap_handle)) {
505 #               $ldap_handle->disconnect;
506 #       }
507     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
508     
510     daemon_log("shutting down gosa-si-server", 1);
511     system("kill `ps -C gosa-si-server -o pid=`");
513 $SIG{INT} = \&sig_int_handler;
516 sub check_key_and_xml_validity {
517     my ($crypted_msg, $module_key, $session_id) = @_;
518     my $msg;
519     my $msg_hash;
520     my $error_string;
521     eval{
522         $msg = &decrypt_msg($crypted_msg, $module_key);
524         if ($msg =~ /<xml>/i){
525             $msg =~ s/\s+/ /g;  # just for better daemon_log
526             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
527             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
529             ##############
530             # check header
531             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
532             my $header_l = $msg_hash->{'header'};
533             if( 1 > @{$header_l} ) { die 'empty header tag'; }
534             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
535             my $header = @{$header_l}[0];
536             if( 0 == length $header) { die 'empty string in header tag'; }
538             ##############
539             # check source
540             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
541             my $source_l = $msg_hash->{'source'};
542             if( 1 > @{$source_l} ) { die 'empty source tag'; }
543             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
544             my $source = @{$source_l}[0];
545             if( 0 == length $source) { die 'source error'; }
547             ##############
548             # check target
549             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
550             my $target_l = $msg_hash->{'target'};
551             if( 1 > @{$target_l} ) { die 'empty target tag'; }
552         }
553     };
554     if($@) {
555         daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
556         $msg = undef;
557         $msg_hash = undef;
558     }
560     return ($msg, $msg_hash);
564 sub check_outgoing_xml_validity {
565     my ($msg, $session_id) = @_;
567     my $msg_hash;
568     eval{
569         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
571         ##############
572         # check header
573         my $header_l = $msg_hash->{'header'};
574         if( 1 != @{$header_l} ) {
575             die 'no or more than one headers specified';
576         }
577         my $header = @{$header_l}[0];
578         if( 0 == length $header) {
579             die 'header has length 0';
580         }
582         ##############
583         # check source
584         my $source_l = $msg_hash->{'source'};
585         if( 1 != @{$source_l} ) {
586             die 'no or more than 1 sources specified';
587         }
588         my $source = @{$source_l}[0];
589         if( 0 == length $source) {
590             die 'source has length 0';
591         }
592         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
593                 $source =~ /^GOSA$/i ) {
594             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
595         }
596         
597         ##############
598         # check target  
599         my $target_l = $msg_hash->{'target'};
600         if( 0 == @{$target_l} ) {
601             die "no targets specified";
602         }
603         foreach my $target (@$target_l) {
604             if( 0 == length $target) {
605                 die "target has length 0";
606             }
607             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
608                     $target =~ /^GOSA$/i ||
609                     $target =~ /^\*$/ ||
610                     $target =~ /KNOWN_SERVER/i ||
611                     $target =~ /JOBDB/i ||
612                     $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 ){
613                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
614             }
615         }
616     };
617     if($@) {
618         daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
619         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
620         $msg_hash = undef;
621     }
623     return ($msg_hash);
627 sub input_from_known_server {
628     my ($input, $remote_ip, $session_id) = @_ ;  
629     my ($msg, $msg_hash, $module);
631     my $sql_statement= "SELECT * FROM known_server";
632     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
634     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
635         my $host_name = $hit->{hostname};
636         if( not $host_name =~ "^$remote_ip") {
637             next;
638         }
639         my $host_key = $hit->{hostkey};
640         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
641         daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
643         # check if module can open msg envelope with module key
644         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
645         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
646             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
647             daemon_log("$@", 8);
648             next;
649         }
650         else {
651             $msg = $tmp_msg;
652             $msg_hash = $tmp_msg_hash;
653             $module = "ServerPackages";
654             last;
655         }
656     }
658     if( (!$msg) || (!$msg_hash) || (!$module) ) {
659         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
660     }
661   
662     return ($msg, $msg_hash, $module);
666 sub input_from_known_client {
667     my ($input, $remote_ip, $session_id) = @_ ;  
668     my ($msg, $msg_hash, $module);
670     my $sql_statement= "SELECT * FROM known_clients";
671     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
672     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
673         my $host_name = $hit->{hostname};
674         if( not $host_name =~ /^$remote_ip:\d*$/) {
675                 next;
676                 }
677         my $host_key = $hit->{hostkey};
678         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
679         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
681         # check if module can open msg envelope with module key
682         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
684         if( (!$msg) || (!$msg_hash) ) {
685             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
686             &daemon_log("$@", 8);
687             next;
688         }
689         else {
690             $module = "ClientPackages";
691             last;
692         }
693     }
695     if( (!$msg) || (!$msg_hash) || (!$module) ) {
696         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
697     }
699     return ($msg, $msg_hash, $module);
703 sub input_from_unknown_host {
704     no strict "refs";
705     my ($input, $session_id) = @_ ;
706     my ($msg, $msg_hash, $module);
707     my $error_string;
708     
709         my %act_modules = %$known_modules;
710         
711     while( my ($mod, $info) = each(%act_modules)) {
713         # check a key exists for this module
714         my $module_key = ${$mod."_key"};
715         if( not defined $module_key ) {
716             if( $mod eq 'ArpHandler' ) {
717                 next;
718             }
719             daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
720             next;
721         }
722         daemon_log("$session_id DEBUG: $mod: $module_key", 7);
724         # check if module can open msg envelope with module key
725         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
726         if( (not defined $msg) || (not defined $msg_hash) ) {
727             next;
728         }
729         else {
730             $module = $mod;
731             last;
732         }
733     }
735     if( (!$msg) || (!$msg_hash) || (!$module)) {
736         daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
737     }
739     return ($msg, $msg_hash, $module);
743 sub create_ciphering {
744     my ($passwd) = @_;
745         if((!defined($passwd)) || length($passwd)==0) {
746                 $passwd = "";
747         }
748     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
749     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
750     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
751     $my_cipher->set_iv($iv);
752     return $my_cipher;
756 sub encrypt_msg {
757     my ($msg, $key) = @_;
758     my $my_cipher = &create_ciphering($key);
759     my $len;
760     {
761             use bytes;
762             $len= 16-length($msg)%16;
763     }
764     $msg = "\0"x($len).$msg;
765     $msg = $my_cipher->encrypt($msg);
766     chomp($msg = &encode_base64($msg));
767     # there are no newlines allowed inside msg
768     $msg=~ s/\n//g;
769     return $msg;
773 sub decrypt_msg {
775     my ($msg, $key) = @_ ;
776     $msg = &decode_base64($msg);
777     my $my_cipher = &create_ciphering($key);
778     $msg = $my_cipher->decrypt($msg); 
779     $msg =~ s/\0*//g;
780     return $msg;
784 sub get_encrypt_key {
785     my ($target) = @_ ;
786     my $encrypt_key;
787     my $error = 0;
789     # target can be in known_server
790     if( not defined $encrypt_key ) {
791         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
792         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
793         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
794             my $host_name = $hit->{hostname};
795             if( $host_name ne $target ) {
796                 next;
797             }
798             $encrypt_key = $hit->{hostkey};
799             last;
800         }
801     }
803     # target can be in known_client
804     if( not defined $encrypt_key ) {
805         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
806         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
807         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
808             my $host_name = $hit->{hostname};
809             if( $host_name ne $target ) {
810                 next;
811             }
812             $encrypt_key = $hit->{hostkey};
813             last;
814         }
815     }
817     return $encrypt_key;
821 #===  FUNCTION  ================================================================
822 #         NAME:  open_socket
823 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
824 #                [PeerPort] string necessary if port not appended by PeerAddr
825 #      RETURNS:  socket IO::Socket::INET
826 #  DESCRIPTION:  open a socket to PeerAddr
827 #===============================================================================
828 sub open_socket {
829     my ($PeerAddr, $PeerPort) = @_ ;
830     if(defined($PeerPort)){
831         $PeerAddr = $PeerAddr.":".$PeerPort;
832     }
833     my $socket;
834     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
835             Porto => "tcp",
836             Type => SOCK_STREAM,
837             Timeout => 5,
838             );
839     if(not defined $socket) {
840         return;
841     }
842 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
843     return $socket;
847 sub get_local_ip_for_remote_ip {
848         my $remote_ip= shift;
849         my $result="0.0.0.0";
851         if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
852                 if($remote_ip eq "127.0.0.1") {
853                         $result = "127.0.0.1";
854                 } else {
855                         my $PROC_NET_ROUTE= ('/proc/net/route');
857                         open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
858                                 or die "Could not open $PROC_NET_ROUTE";
860                         my @ifs = <PROC_NET_ROUTE>;
862                         close(PROC_NET_ROUTE);
864                         # Eat header line
865                         shift @ifs;
866                         chomp @ifs;
867                         foreach my $line(@ifs) {
868                                 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
869                                 my $destination;
870                                 my $mask;
871                                 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
872                                 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
873                                 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
874                                 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
875                                 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
876                                         # destination matches route, save mac and exit
877                                         $result= &get_ip($Iface);
878                                         last;
879                                 }
880                         }
881                 }
882         } else {
883                 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
884         }
885         return $result;
889 sub send_msg_to_target {
890     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
891     my $error = 0;
892     my $header;
893     my $timestamp = &get_time();
894     my $new_status;
895     my $act_status;
896     my ($sql_statement, $res);
897   
898     if( $msg_header ) {
899         $header = "'$msg_header'-";
900     } else {
901         $header = "";
902     }
904         # Patch the source ip
905         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
906                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
907                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
908         }
910     # encrypt xml msg
911     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
913     # opensocket
914     my $socket = &open_socket($address);
915     if( !$socket ) {
916         daemon_log("$session_id WARNING: cannot send ".$header."msg to $address , host not reachable", 3);
917         $error++;
918     }
919     
920     if( $error == 0 ) {
921         # send xml msg
922         print $socket $crypted_msg."\n";
924         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
925         daemon_log("$session_id DEBUG: message:\n$msg", 9);
926         
927     }
929     # close socket in any case
930     if( $socket ) {
931         close $socket;
932     }
934     if( $error > 0 ) { $new_status = "down"; }
935     else { $new_status = $msg_header; }
938     # known_clients
939     $sql_statement = "SELECT * FROM $known_clients_tn WHERE hostname='$address'";
940     $res = $known_clients_db->select_dbentry($sql_statement);
941     if( keys(%$res) == 1) {
942         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
943         if ($act_status eq "down" && $new_status eq "down") {
944             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
945             $res = $known_clients_db->del_dbentry($sql_statement);
946             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
947         } else { 
948             $sql_statement = "UPDATE known_clients SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
949             $res = $known_clients_db->update_dbentry($sql_statement);
950             if($new_status eq "down"){
951                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
952             } else {
953                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
954             }
955         }
956     }
958     # known_server
959     $sql_statement = "SELECT * FROM $known_server_tn WHERE hostname='$address'";
960     $res = $known_server_db->select_dbentry($sql_statement);
961     if( keys(%$res) == 1) {
962         $act_status = exists $res->{1}->{'status'} ? $res->{1}->{'status'} : "";
963         if ($act_status eq "down" && $new_status eq "down") {
964             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
965             $res = $known_server_db->del_dbentry($sql_statement);
966             daemon_log("$session_id WARNING: failed 2x to send a message to host '$address', delete host from known_server", 3);
967         } 
968         else { 
969             $sql_statement = "UPDATE known_server SET status='$new_status', timestamp='$timestamp' WHERE hostname='$address'";
970             $res = $known_server_db->update_dbentry($sql_statement);
971             if($new_status eq "down"){
972                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
973             } else {
974                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
975             }
976         }
977     }
978     return $error; 
982 sub update_jobdb_status_for_send_msgs {
983     my ($answer, $error) = @_;
984     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
985         my $jobdb_id = $1;
986             
987         # sending msg faild
988         if( $error ) {
989             if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
990                 my $sql_statement = "UPDATE $job_queue_tn ".
991                     "SET status='error', result='can not deliver msg, please consult log file' ".
992                     "WHERE id=$jobdb_id";
993                 my $res = $job_db->update_dbentry($sql_statement);
994             }
996         # sending msg was successful
997         } else {
998             my $sql_statement = "UPDATE $job_queue_tn ".
999                 "SET status='done' ".
1000                 "WHERE id=$jobdb_id AND status='processed'";
1001             my $res = $job_db->update_dbentry($sql_statement);
1002         }
1003     }
1007 sub sig_handler {
1008         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1009         daemon_log("0 INFO got signal '$signal'", 1); 
1010         $kernel->sig_handled();
1011         return;
1015 sub msg_to_decrypt {
1016     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
1017     my $session_id = $session->ID;
1018     my ($msg, $msg_hash, $module);
1019     my $error = 0;
1021     # hole neue msg aus @msgs_to_decrypt
1022     my $next_msg = shift @msgs_to_decrypt;
1023     
1024     # entschlüssle sie
1026     # msg is from a new client or gosa
1027     ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
1028     # msg is from a gosa-si-server
1029     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1030         ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
1031     }
1032     # msg is from a gosa-si-client
1033     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1034         ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
1035     }
1036     # an error occurred
1037     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1038         # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1039         # could not understand a msg from its server the client cause a re-registering process
1040         daemon_log("$session_id WARNING cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}.
1041                 "' to cause a re-registering of the client if necessary", 3);
1042         my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1043         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1044         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1045             my $host_name = $hit->{'hostname'};
1046             my $host_key = $hit->{'hostkey'};
1047             my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1048             my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1049             &update_jobdb_status_for_send_msgs($ping_msg, $error);
1050         }
1051         $error++;
1052     }
1055     my $header;
1056     my $target;
1057     my $source;
1058     my $done = 0;
1059     my $sql;
1060     my $res;
1062     # check whether this message should be processed here
1063     if ($error == 0) {
1064         $header = @{$msg_hash->{'header'}}[0];
1065         $target = @{$msg_hash->{'target'}}[0];
1066         $source = @{$msg_hash->{'source'}}[0];
1067                 my $not_found_in_known_clients_db = 0;
1068                 my $not_found_in_known_server_db = 0;
1069                 my $not_found_in_foreign_clients_db = 0;
1070         my $local_address;
1071         my ($target_ip, $target_port) = split(':', $target);
1072                 if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
1073                         $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1074                 } else {
1075             $local_address = $server_address;
1076         }
1078         # target and source is equal to GOSA -> process here
1079         if (not $done) {
1080             if ($target eq "GOSA" && $source eq "GOSA") {
1081                 $done = 1;                    
1082             }
1083         }
1085         # target is own address without forward_to_gosa-tag -> process here
1086         if (not $done) {
1087             if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
1088                 $done = 1;
1089                 if ($source eq "GOSA") {
1090                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1091                 }
1092                 #print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
1093             }
1094         }
1096         # target is a client address in known_clients -> process here
1097                 if (not $done) {
1098                                 $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
1099                                 $res = $known_clients_db->select_dbentry($sql);
1100                                 if (keys(%$res) > 0) {
1101                                                 $done = 1; 
1102                                                 my $hostname = $res->{1}->{'hostname'};
1103                                                 $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1104                                                 #print STDERR "target is a client address in known_clients -> process here\n";
1105                         my $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
1106                         if ($source eq "GOSA") {
1107                             $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1108                         }
1110                                 } else {
1111                                                 $not_found_in_known_clients_db = 1;
1112                                 }
1113                 }
1114         
1115         # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
1116         if (not $done) {
1117             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1118             my $gosa_at;
1119             my $gosa_session_id;
1120             if (($target eq $local_address) && (defined $forward_to_gosa)){
1121                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1122                 if ($gosa_at ne $local_address) {
1123                     $done = 1;
1124                     #print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n"; 
1125                 }
1126             }
1127         }
1129         # if message should be processed here -> add message to incoming_db
1130                 if ($done) {
1131                                 # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
1132                                 # so gosa-si-server knows how to process this kind of messages
1133                                 if ($header =~ /^gosa_/ || $header =~ /^job_/) {
1134                                                 $module = "GosaPackages";
1135                                 }
1137                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1138                                                                 primkey=>[],
1139                                                                 headertag=>$header,
1140                                                                 targettag=>$target,
1141                                                                 xmlmessage=>&encode_base64($msg),
1142                                                                 timestamp=>&get_time,
1143                                                                 module=>$module,
1144                                                                 sessionid=>$session_id,
1145                                                                 } );
1146                 }
1148         # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
1149         if (not $done) {
1150             my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
1151             my $gosa_at;
1152             my $gosa_session_id;
1153             if (($target eq $local_address) && (defined $forward_to_gosa)){
1154                 my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
1155                 if ($gosa_at eq $local_address) {
1156                     my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
1157                     if( defined $session_reference ) {
1158                         $heap = $session_reference->get_heap();
1159                     }
1160                     if(exists $heap->{'client'}) {
1161                         $msg = &encrypt_msg($msg, $GosaPackages_key);
1162                         $heap->{'client'}->put($msg);
1163                     }
1164                     $done = 1;
1165                     #print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
1166                 }
1167             }
1169         }
1171         # target is a client address in foreign_clients -> forward to registration server
1172         if (not $done) {
1173             $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
1174             $res = $foreign_clients_db->select_dbentry($sql);
1175             if (keys(%$res) > 0) {
1176                     my $hostname = $res->{1}->{'hostname'};
1177                     my ($host_ip, $host_port) = split(/:/, $hostname);
1178                     my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
1179                 my $regserver = $res->{1}->{'regserver'};
1180                 my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
1181                 my $res = $known_server_db->select_dbentry($sql);
1182                 if (keys(%$res) > 0) {
1183                     my $regserver_key = $res->{1}->{'hostkey'};
1184                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1185                     $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
1186                     if ($source eq "GOSA") {
1187                         $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1188                     }
1189                     &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
1190                 }
1191                 $done = 1;
1192                 #print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
1193             } else {
1194                                 $not_found_in_foreign_clients_db = 1;
1195                         }
1196         }
1198         # target is a server address -> forward to server
1199         if (not $done) {
1200             $sql = "SELECT * FROM $known_server_tn WHERE hostname='$target'";
1201             $res = $known_server_db->select_dbentry($sql);
1202             if (keys(%$res) > 0) {
1203                 my $hostkey = $res->{1}->{'hostkey'};
1205                 if ($source eq "GOSA") {
1206                     $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
1207                     $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
1209                 }
1211                 &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
1212                 $done = 1;
1213                 #print STDERR "target is a server address -> forward to server\n";
1214             } else {
1215                                 $not_found_in_known_server_db = 1;
1216                         }
1217         }
1219                 
1220                 # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
1221                 if ( $not_found_in_foreign_clients_db 
1222                                                 && $not_found_in_known_server_db
1223                                                 && $not_found_in_known_clients_db) {
1224                                 my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
1225                                                                 primkey=>[],
1226                                                                 headertag=>$header,
1227                                                                 targettag=>$target,
1228                                                                 xmlmessage=>&encode_base64($msg),
1229                                                                 timestamp=>&get_time,
1230                                                                 module=>$module,
1231                                                                 sessionid=>$session_id,
1232                                                                 } );
1233                                 $done = 1;
1234                 }
1237         if (not $done) {
1238             daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
1239             if ($source eq "GOSA") {
1240                 my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
1241                 my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
1243                 my $session_reference = $kernel->ID_id_to_session($session_id);
1244                 if( defined $session_reference ) {
1245                     $heap = $session_reference->get_heap();
1246                 }
1247                 if(exists $heap->{'client'}) {
1248                     $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
1249                     $heap->{'client'}->put($error_msg);
1250                 }
1251             }
1252         }
1254     }
1256     return;
1260 sub next_task {
1261     my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
1262     my $running_task = POE::Wheel::Run->new(
1263             Program => sub { process_task($session, $heap, $task) },
1264             StdioFilter => POE::Filter::Reference->new(),
1265             StdoutEvent  => "task_result",
1266             StderrEvent  => "task_debug",
1267             CloseEvent   => "task_done",
1268             );
1269     $heap->{task}->{ $running_task->ID } = $running_task;
1272 sub handle_task_result {
1273     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1274     my $client_answer = $result->{'answer'};
1275     if( $client_answer =~ s/session_id=(\d+)$// ) {
1276         my $session_id = $1;
1277         if( defined $session_id ) {
1278             my $session_reference = $kernel->ID_id_to_session($session_id);
1279             if( defined $session_reference ) {
1280                 $heap = $session_reference->get_heap();
1281             }
1282         }
1284         if(exists $heap->{'client'}) {
1285             $heap->{'client'}->put($client_answer);
1286         }
1287     }
1288     $kernel->sig(CHLD => "child_reap");
1291 sub handle_task_debug {
1292     my $result = $_[ARG0];
1293     print STDERR "$result\n";
1296 sub handle_task_done {
1297     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1298     delete $heap->{task}->{$task_id};
1301 sub process_task {
1302     no strict "refs";
1303     #CHECK: Not @_[...]?
1304     my ($session, $heap, $task) = @_;
1305     my $error = 0;
1306     my $answer_l;
1307     my ($answer_header, @answer_target_l, $answer_source);
1308     my $client_answer = "";
1310     # prepare all variables needed to process message
1311     #my $msg = $task->{'xmlmessage'};
1312     my $msg = &decode_base64($task->{'xmlmessage'});
1313     my $incoming_id = $task->{'id'};
1314     my $module = $task->{'module'};
1315     my $header =  $task->{'headertag'};
1316     my $session_id = $task->{'sessionid'};
1317     my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
1318     my $source = @{$msg_hash->{'source'}}[0];
1319     
1320     # set timestamp of incoming client uptodate, so client will not 
1321     # be deleted from known_clients because of expiration
1322     my $act_time = &get_time();
1323     my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'"; 
1324     my $res = $known_clients_db->exec_statement($sql);
1326     ######################
1327     # process incoming msg
1328     if( $error == 0) {
1329         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
1330         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1331         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1333         if ( 0 < @{$answer_l} ) {
1334             my $answer_str = join("\n", @{$answer_l});
1335             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
1336                 daemon_log("$session_id INFO: got answer message with header '$1'", 5);
1337             }
1338             daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,8);
1339         } else {
1340             daemon_log("$session_id DEBUG: $module: got no answer from module!" ,8);
1341         }
1343     }
1344     if( !$answer_l ) { $error++ };
1346     ########
1347     # answer
1348     if( $error == 0 ) {
1350         foreach my $answer ( @{$answer_l} ) {
1351             # check outgoing msg to xml validity
1352             my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
1353             if( not defined $answer_hash ) { next; }
1354             
1355             $answer_header = @{$answer_hash->{'header'}}[0];
1356             @answer_target_l = @{$answer_hash->{'target'}};
1357             $answer_source = @{$answer_hash->{'source'}}[0];
1359             # deliver msg to all targets 
1360             foreach my $answer_target ( @answer_target_l ) {
1362                 # targets of msg are all gosa-si-clients in known_clients_db
1363                 if( $answer_target eq "*" ) {
1364                     # answer is for all clients
1365                     my $sql_statement= "SELECT * FROM known_clients";
1366                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1367                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1368                         my $host_name = $hit->{hostname};
1369                         my $host_key = $hit->{hostkey};
1370                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1371                         &update_jobdb_status_for_send_msgs($answer, $error);
1372                     }
1373                 }
1375                 # targets of msg are all gosa-si-server in known_server_db
1376                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1377                     # answer is for all server in known_server
1378                     my $sql_statement= "SELECT * FROM $known_server_tn";
1379                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1380                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1381                         my $host_name = $hit->{hostname};
1382                         my $host_key = $hit->{hostkey};
1383                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
1384                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1385                         &update_jobdb_status_for_send_msgs($answer, $error);
1386                     }
1387                 }
1389                 # target of msg is GOsa
1390                                 elsif( $answer_target eq "GOSA" ) {
1391                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1392                                         my $add_on = "";
1393                     if( defined $session_id ) {
1394                         $add_on = ".session_id=$session_id";
1395                     }
1396                     # answer is for GOSA and has to returned to connected client
1397                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1398                     $client_answer = $gosa_answer.$add_on;
1399                 }
1401                 # target of msg is job queue at this host
1402                 elsif( $answer_target eq "JOBDB") {
1403                     $answer =~ /<header>(\S+)<\/header>/;   
1404                     my $header;
1405                     if( defined $1 ) { $header = $1; }
1406                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1407                     &update_jobdb_status_for_send_msgs($answer, $error);
1408                 }
1410                 # target of msg is a mac address
1411                 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 ) {
1412                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1413                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1414                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1415                     my $found_ip_flag = 0;
1416                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1417                         my $host_name = $hit->{hostname};
1418                         my $host_key = $hit->{hostkey};
1419                         $answer =~ s/$answer_target/$host_name/g;
1420                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1421                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1422                         &update_jobdb_status_for_send_msgs($answer, $error);
1423                         $found_ip_flag++ ;
1424                     }   
1425                     if( $found_ip_flag == 0) {
1426                         daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1427                     }
1429                 #  answer is for one specific host   
1430                 } else {
1431                     # get encrypt_key
1432                     my $encrypt_key = &get_encrypt_key($answer_target);
1433                     if( not defined $encrypt_key ) {
1434                         # unknown target
1435                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1436                         next;
1437                     }
1438                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1439                     &update_jobdb_status_for_send_msgs($answer, $error);
1440                 }
1441             }
1442         }
1443     }
1445     my $filter = POE::Filter::Reference->new();
1446     my %result = ( 
1447             status => "seems ok to me",
1448             answer => $client_answer,
1449             );
1451     my $output = $filter->put( [ \%result ] );
1452     print @$output;
1457 sub session_start {
1458     my ($kernel) = $_[KERNEL];
1459     $global_kernel = $kernel;
1460     $kernel->yield('register_at_foreign_servers');
1461         $kernel->yield('create_fai_server_db', $fai_server_tn );
1462         $kernel->yield('create_fai_release_db', $fai_release_tn );
1463     $kernel->yield('watch_for_next_tasks');
1464         $kernel->sig(USR1 => "sig_handler");
1465         $kernel->sig(USR2 => "recreate_packages_db");
1466         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1467         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1468     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
1469         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1470     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1471         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1472     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1478 sub watch_for_done_jobs {
1479     #CHECK: $heap for what?
1480     my ($kernel,$heap) = @_[KERNEL, HEAP];
1482     my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
1483         my $res = $job_db->select_dbentry( $sql_statement );
1485     while( my ($id, $hit) = each %{$res} ) {
1486         my $jobdb_id = $hit->{id};
1487         my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1488         my $res = $job_db->del_dbentry($sql_statement); 
1489     }
1491     $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1495 # if a job got an update or was modified anyway, send to all other si-server an update message
1496 # of this jobs
1497 sub watch_for_modified_jobs {
1498     my ($kernel,$heap) = @_[KERNEL, HEAP];
1500     my $sql_statement = "SELECT * FROM $job_queue_tn WHERE ((siserver='localhost') AND (modified='1'))"; 
1501     my $res = $job_db->select_dbentry( $sql_statement );
1502     
1503     # if db contains no jobs which should be update, do nothing
1504     if (keys %$res != 0) {
1506         if ($job_synchronization  eq "true") {
1507             # make out of the db result a gosa-si message   
1508             my $update_msg = &db_res2si_msg ($res, "foreign_job_updates", "KNOWN_SERVER", "MY_LOCAL_ADDRESS");
1509  
1510             # update all other SI-server
1511             &inform_all_other_si_server($update_msg);
1512         }
1514         # set jobs all jobs to modified = 0, wait until the next modification for updates of other si-server
1515         $sql_statement = "UPDATE $job_queue_tn SET modified='0' ";
1516         $res = $job_db->update_dbentry($sql_statement);
1517     }
1519     $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
1523 sub watch_for_new_jobs {
1524         if($watch_for_new_jobs_in_progress == 0) {
1525                 $watch_for_new_jobs_in_progress = 1;
1526                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1528                 # check gosa job quaeue for jobs with executable timestamp
1529                 my $timestamp = &get_time();
1530                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1531                 my $res = $job_db->exec_statement( $sql_statement );
1533                 # Merge all new jobs that would do the same actions
1534                 my @drops;
1535                 my $hits;
1536                 foreach my $hit (reverse @{$res} ) {
1537                         my $macaddress= lc @{$hit}[8];
1538                         my $headertag= @{$hit}[5];
1539                         if(
1540                                 defined($hits->{$macaddress}) &&
1541                                 defined($hits->{$macaddress}->{$headertag}) &&
1542                                 defined($hits->{$macaddress}->{$headertag}[0])
1543                         ) {
1544                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1545                         }
1546                         $hits->{$macaddress}->{$headertag}= $hit;
1547                 }
1549                 # Delete new jobs with a matching job in state 'processing'
1550                 foreach my $macaddress (keys %{$hits}) {
1551                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1552                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1553                                 if(defined($jobdb_id)) {
1554                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1555                                         my $res = $job_db->exec_statement( $sql_statement );
1556                                         foreach my $hit (@{$res}) {
1557                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1558                                         }
1559                                 } else {
1560                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1561                                 }
1562                         }
1563                 }
1565                 # Commit deletion
1566                 $job_db->exec_statementlist(\@drops);
1568                 # Look for new jobs that could be executed
1569                 foreach my $macaddress (keys %{$hits}) {
1571                         # Look if there is an executing job
1572                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1573                         my $res = $job_db->exec_statement( $sql_statement );
1575                         # Skip new jobs for host if there is a processing job
1576                         if(defined($res) and defined @{$res}[0]) {
1577                                 next;
1578                         }
1580                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1581                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1582                                 if(defined($jobdb_id)) {
1583                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1585                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1586                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1587                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1589                                         # expect macaddress is unique!!!!!!
1590                                         my $target = $res_hash->{1}->{hostname};
1592                                         # change header
1593                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1595                                         # add sqlite_id
1596                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1598                                         $job_msg =~ /<header>(\S+)<\/header>/;
1599                                         my $header = $1 ;
1600                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1602                                         # update status in job queue to 'processing'
1603                                         $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1604                                         my $res = $job_db->update_dbentry($sql_statement);
1605 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen                                        
1607                                         # We don't want parallel processing
1608                                         last;
1609                                 }
1610                         }
1611                 }
1613                 $watch_for_new_jobs_in_progress = 0;
1614                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1615         }
1620 sub watch_for_new_messages {
1621     my ($kernel,$heap) = @_[KERNEL, HEAP];
1622     my @coll_user_msg;   # collection list of outgoing messages
1623     
1624     # check messaging_db for new incoming messages with executable timestamp
1625     my $timestamp = &get_time();
1626     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1627     my $res = $messaging_db->exec_statement( $sql_statement );
1628         foreach my $hit (@{$res}) {
1630         # create outgoing messages
1631         my $message_to = @{$hit}[3];
1632         # translate message_to to plain login name
1633         my @message_to_l = split(/,/, $message_to);  
1634                 my %receiver_h; 
1635                 foreach my $receiver (@message_to_l) {
1636                         if ($receiver =~ /^u_([\s\S]*)$/) {
1637                                 $receiver_h{$1} = 0;
1638                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1639                                 my $group_name = $1;
1640                                 # fetch all group members from ldap and add them to receiver hash
1641                                 my $ldap_handle = &get_ldap_handle();
1642                                 if (defined $ldap_handle) {
1643                                                 my $mesg = $ldap_handle->search(
1644                                                                                 base => $ldap_base,
1645                                                                                 scope => 'sub',
1646                                                                                 attrs => ['memberUid'],
1647                                                                                 filter => "cn=$group_name",
1648                                                                                 );
1649                                                 if ($mesg->count) {
1650                                                                 my @entries = $mesg->entries;
1651                                                                 foreach my $entry (@entries) {
1652                                                                                 my @receivers= $entry->get_value("memberUid");
1653                                                                                 foreach my $receiver (@receivers) { 
1654                                                                                                 $receiver_h{$1} = 0;
1655                                                                                 }
1656                                                                 }
1657                                                 } 
1658                                                 # translating errors ?
1659                                                 if ($mesg->code) {
1660                                                                 daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
1661                                                 }
1662                                 # ldap handle error ?           
1663                                 } else {
1664                                         daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
1665                                 }
1666                         } else {
1667                                 my $sbjct = &encode_base64(@{$hit}[1]);
1668                                 my $msg = &encode_base64(@{$hit}[7]);
1669                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message '$sbjct - $msg'", 3); 
1670                         }
1671                 }
1672                 my @receiver_l = keys(%receiver_h);
1674         my $message_id = @{$hit}[0];
1676         #add each outgoing msg to messaging_db
1677         my $receiver;
1678         foreach $receiver (@receiver_l) {
1679             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1680                 "VALUES ('".
1681                 $message_id."', '".    # id
1682                 @{$hit}[1]."', '".     # subject
1683                 @{$hit}[2]."', '".     # message_from
1684                 $receiver."', '".      # message_to
1685                 "none"."', '".         # flag
1686                 "out"."', '".          # direction
1687                 @{$hit}[6]."', '".     # delivery_time
1688                 @{$hit}[7]."', '".     # message
1689                 $timestamp."'".     # timestamp
1690                 ")";
1691             &daemon_log("M DEBUG: $sql_statement", 1);
1692             my $res = $messaging_db->exec_statement($sql_statement);
1693             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1694         }
1696         # set incoming message to flag d=deliverd
1697         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1698         &daemon_log("M DEBUG: $sql_statement", 7);
1699         $res = $messaging_db->update_dbentry($sql_statement);
1700         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1701     }
1703     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1704     return;
1707 sub watch_for_delivery_messages {
1708     my ($kernel, $heap) = @_[KERNEL, HEAP];
1710     # select outgoing messages
1711     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1712     #&daemon_log("0 DEBUG: $sql", 7);
1713     my $res = $messaging_db->exec_statement( $sql_statement );
1714     
1715     # build out msg for each    usr
1716     foreach my $hit (@{$res}) {
1717         my $receiver = @{$hit}[3];
1718         my $msg_id = @{$hit}[0];
1719         my $subject = @{$hit}[1];
1720         my $message = @{$hit}[7];
1722         # resolve usr -> host where usr is logged in
1723         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1724         #&daemon_log("0 DEBUG: $sql", 7);
1725         my $res = $login_users_db->exec_statement($sql);
1727         # reciver is logged in nowhere
1728         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1730                 my $send_succeed = 0;
1731                 foreach my $hit (@$res) {
1732                                 my $receiver_host = @$hit[0];
1733                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1735                                 # fetch key to encrypt msg propperly for usr/host
1736                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1737                                 &daemon_log("0 DEBUG: $sql", 7);
1738                                 my $res = $known_clients_db->exec_statement($sql);
1740                                 # host is already down
1741                                 if (not ref(@$res[0]) eq "ARRAY") { next; }
1743                                 # host is on
1744                                 my $receiver_key = @{@{$res}[0]}[2];
1745                                 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1746                                 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1747                                 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1748                                 if ($error == 0 ) {
1749                                         $send_succeed++ ;
1750                                 }
1751                 }
1753                 if ($send_succeed) {
1754                                 # set outgoing msg at db to deliverd
1755                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
1756                                 &daemon_log("0 DEBUG: $sql", 7);
1757                                 my $res = $messaging_db->exec_statement($sql); 
1758                 }
1759         }
1761     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
1762     return;
1766 sub watch_for_done_messages {
1767     my ($kernel,$heap) = @_[KERNEL, HEAP];
1769     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
1770     #&daemon_log("0 DEBUG: $sql", 7);
1771     my $res = $messaging_db->exec_statement($sql); 
1773     foreach my $hit (@{$res}) {
1774         my $msg_id = @{$hit}[0];
1776         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
1777         #&daemon_log("0 DEBUG: $sql", 7); 
1778         my $res = $messaging_db->exec_statement($sql);
1780         # not all usr msgs have been seen till now
1781         if ( ref(@$res[0]) eq "ARRAY") { next; }
1782         
1783         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
1784         #&daemon_log("0 DEBUG: $sql", 7);
1785         $res = $messaging_db->exec_statement($sql);
1786     
1787     }
1789     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
1790     return;
1794 sub watch_for_old_known_clients {
1795     my ($kernel,$heap) = @_[KERNEL, HEAP];
1797     my $sql_statement = "SELECT * FROM $known_clients_tn";
1798     my $res = $known_clients_db->select_dbentry( $sql_statement );
1800     my $act_time = int(&get_time());
1802     while ( my ($hit_num, $hit) = each %$res) {
1803         my $expired_timestamp = int($hit->{'timestamp'});
1804         $expired_timestamp =~ /(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/;
1805         my $dt = DateTime->new( year   => $1,
1806                 month  => $2,
1807                 day    => $3,
1808                 hour   => $4,
1809                 minute => $5,
1810                 second => $6,
1811                 );
1813         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
1814         $expired_timestamp = $dt->ymd('').$dt->hms('')."\n";
1815         if ($act_time > $expired_timestamp) {
1816             my $hostname = $hit->{'hostname'};
1817             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
1818             my $del_res = $known_clients_db->exec_statement($del_sql);
1820             &main::daemon_log("0 INFO: timestamp '".$hit->{'timestamp'}."' of client '$hostname' is expired('$expired_timestamp'), client will be deleted from known_clients_db", 5);
1821         }
1823     }
1825     $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
1829 sub watch_for_next_tasks {
1830     my ($kernel,$heap) = @_[KERNEL, HEAP];
1832     my $sql = "SELECT * FROM $incoming_tn";
1833     my $res = $incoming_db->select_dbentry($sql);
1835     while ( my ($hit_num, $hit) = each %$res) {
1836         my $headertag = $hit->{'headertag'};
1837         if ($headertag =~ /^answer_(\d+)/) {
1838             # do not start processing, this message is for a still running POE::Wheel
1839             next;
1840         }
1841         my $message_id = $hit->{'id'};
1842         $kernel->yield('next_task', $hit);
1844         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
1845         my $res = $incoming_db->exec_statement($sql);
1846     }
1848     $kernel->delay_set('watch_for_next_tasks', 0.1); 
1852 sub get_ldap_handle {
1853         my ($session_id) = @_;
1854         my $heap;
1855         my $ldap_handle;
1857         if (not defined $session_id ) { $session_id = 0 };
1858         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1860         if ($session_id == 0) {
1861                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
1862                 $ldap_handle = Net::LDAP->new( $ldap_uri );
1863                 $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!"); 
1865         } else {
1866                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1867                 if( defined $session_reference ) {
1868                         $heap = $session_reference->get_heap();
1869                 }
1871                 if (not defined $heap) {
1872                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
1873                         return;
1874                 }
1876                 # TODO: This "if" is nonsense, because it doesn't prove that the
1877                 #       used handle is still valid - or if we've to reconnect...
1878                 #if (not exists $heap->{ldap_handle}) {
1879                         $ldap_handle = Net::LDAP->new( $ldap_uri );
1880                         $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!"); 
1881                         $heap->{ldap_handle} = $ldap_handle;
1882                 #}
1883         }
1884         return $ldap_handle;
1888 sub change_fai_state {
1889     my ($st, $targets, $session_id) = @_;
1890     $session_id = 0 if not defined $session_id;
1891     # Set FAI state to localboot
1892     my %mapActions= (
1893         reboot    => '',
1894         update    => 'softupdate',
1895         localboot => 'localboot',
1896         reinstall => 'install',
1897         rescan    => '',
1898         wake      => '',
1899         memcheck  => 'memcheck',
1900         sysinfo   => 'sysinfo',
1901         install   => 'install',
1902     );
1904     # Return if this is unknown
1905     if (!exists $mapActions{ $st }){
1906         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
1907       return;
1908     }
1910     my $state= $mapActions{ $st };
1912     my $ldap_handle = &get_ldap_handle($session_id);
1913     if( defined($ldap_handle) ) {
1915       # Build search filter for hosts
1916         my $search= "(&(objectClass=GOhard)";
1917         foreach (@{$targets}){
1918             $search.= "(macAddress=$_)";
1919         }
1920         $search.= ")";
1922       # If there's any host inside of the search string, procress them
1923         if (!($search =~ /macAddress/)){
1924             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
1925             return;
1926         }
1928       # Perform search for Unit Tag
1929       my $mesg = $ldap_handle->search(
1930           base   => $ldap_base,
1931           scope  => 'sub',
1932           attrs  => ['dn', 'FAIstate', 'objectClass'],
1933           filter => "$search"
1934           );
1936           if ($mesg->count) {
1937                   my @entries = $mesg->entries;
1938                   if (0 == @entries) {
1939                                   daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
1940                   }
1942                   foreach my $entry (@entries) {
1943                           # Only modify entry if it is not set to '$state'
1944                           if ($entry->get_value("FAIstate") ne "$state"){
1945                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1946                                   my $result;
1947                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1948                                   if (exists $tmp{'FAIobject'}){
1949                                           if ($state eq ''){
1950                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1951                                                           delete => [ FAIstate => [] ] ]);
1952                                           } else {
1953                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1954                                                           replace => [ FAIstate => $state ] ]);
1955                                           }
1956                                   } elsif ($state ne ''){
1957                                           $result= $ldap_handle->modify($entry->dn, changes => [
1958                                                   add     => [ objectClass => 'FAIobject' ],
1959                                                   add     => [ FAIstate => $state ] ]);
1960                                   }
1962                                   # Errors?
1963                                   if ($result->code){
1964                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1965                                   }
1966                           } else {
1967                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
1968                           }  
1969                   }
1970           } else {
1971                 daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
1972           }
1974     # if no ldap handle defined
1975     } else {
1976         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
1977     }
1979         return;
1983 sub change_goto_state {
1984     my ($st, $targets, $session_id) = @_;
1985     $session_id = 0  if not defined $session_id;
1987     # Switch on or off?
1988     my $state= $st eq 'active' ? 'active': 'locked';
1990     my $ldap_handle = &get_ldap_handle($session_id);
1991     if( defined($ldap_handle) ) {
1993       # Build search filter for hosts
1994       my $search= "(&(objectClass=GOhard)";
1995       foreach (@{$targets}){
1996         $search.= "(macAddress=$_)";
1997       }
1998       $search.= ")";
2000       # If there's any host inside of the search string, procress them
2001       if (!($search =~ /macAddress/)){
2002         return;
2003       }
2005       # Perform search for Unit Tag
2006       my $mesg = $ldap_handle->search(
2007           base   => $ldap_base,
2008           scope  => 'sub',
2009           attrs  => ['dn', 'gotoMode'],
2010           filter => "$search"
2011           );
2013       if ($mesg->count) {
2014         my @entries = $mesg->entries;
2015         foreach my $entry (@entries) {
2017           # Only modify entry if it is not set to '$state'
2018           if ($entry->get_value("gotoMode") ne $state){
2020             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
2021             my $result;
2022             $result= $ldap_handle->modify($entry->dn, changes => [
2023                                                 replace => [ gotoMode => $state ] ]);
2025             # Errors?
2026             if ($result->code){
2027               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
2028             }
2030           }
2031         }
2032       } else {
2033                 daemon_log("$session_id ERROR: LDAP search failed in function change_goto_state: ldap_base=$ldap_base, filter=$search", 1);
2034           }
2036     }
2040 sub run_recreate_packages_db {
2041     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2042     my $session_id = $session->ID;
2043         &main::daemon_log("$session_id INFO: Recreating FAI Packages DB ('$fai_release_tn', '$fai_server_tn', '$packages_list_tn')", 5);
2044         $kernel->yield('create_fai_release_db', $fai_release_tn);
2045         $kernel->yield('create_fai_server_db', $fai_server_tn);
2046         return;
2050 sub run_create_fai_server_db {
2051     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
2052     my $session_id = $session->ID;
2053     my $task = POE::Wheel::Run->new(
2054             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
2055             StdoutEvent  => "session_run_result",
2056             StderrEvent  => "session_run_debug",
2057             CloseEvent   => "session_run_done",
2058             );
2060     $heap->{task}->{ $task->ID } = $task;
2061     return;
2065 sub create_fai_server_db {
2066     my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
2067         my $result;
2069         if (not defined $session_id) { $session_id = 0; }
2070     my $ldap_handle = &get_ldap_handle();
2071         if(defined($ldap_handle)) {
2072                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
2073                 my $mesg= $ldap_handle->search(
2074                         base   => $ldap_base,
2075                         scope  => 'sub',
2076                         attrs  => ['FAIrepository', 'gosaUnitTag'],
2077                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
2078                 );
2079                 if($mesg->{'resultCode'} == 0 &&
2080                    $mesg->count != 0) {
2081                    foreach my $entry (@{$mesg->{entries}}) {
2082                            if($entry->exists('FAIrepository')) {
2083                                    # Add an entry for each Repository configured for server
2084                                    foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
2085                                                    my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
2086                                                    my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
2087                                                    $result= $fai_server_db->add_dbentry( { 
2088                                                                    table => $table_name,
2089                                                                    primkey => ['server', 'release', 'tag'],
2090                                                                    server => $tmp_url,
2091                                                                    release => $tmp_release,
2092                                                                    sections => $tmp_sections,
2093                                                                    tag => (length($tmp_tag)>0)?$tmp_tag:"",
2094                                                            } );
2095                                            }
2096                                    }
2097                            }
2098                    }
2099                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
2101                 # TODO: Find a way to post the 'create_packages_list_db' event
2102                 if(not defined($dont_create_packages_list)) {
2103                         &create_packages_list_db(undef, undef, $session_id);
2104                 }
2105         }       
2106     
2107     $ldap_handle->disconnect;
2108         return $result;
2112 sub run_create_fai_release_db {
2113     my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
2114         my $session_id = $session->ID;
2115     my $task = POE::Wheel::Run->new(
2116             Program => sub { &create_fai_release_db($table_name, $session_id) },
2117             StdoutEvent  => "session_run_result",
2118             StderrEvent  => "session_run_debug",
2119             CloseEvent   => "session_run_done",
2120             );
2122     $heap->{task}->{ $task->ID } = $task;
2123     return;
2127 sub create_fai_release_db {
2128         my ($table_name, $session_id) = @_;
2129         my $result;
2131     # used for logging
2132     if (not defined $session_id) { $session_id = 0; }
2134     my $ldap_handle = &get_ldap_handle();
2135         if(defined($ldap_handle)) {
2136                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
2137                 my $mesg= $ldap_handle->search(
2138                         base   => $ldap_base,
2139                         scope  => 'sub',
2140                         attrs  => [],
2141                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
2142                 );
2143                 if($mesg->{'resultCode'} == 0 &&
2144                         $mesg->count != 0) {
2145                         # Walk through all possible FAI container ou's
2146                         my @sql_list;
2147                         my $timestamp= &get_time();
2148                         foreach my $ou (@{$mesg->{entries}}) {
2149                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
2150                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
2151                                         my @tmp_array=get_fai_release_entries($tmp_classes);
2152                                         if(@tmp_array) {
2153                                                 foreach my $entry (@tmp_array) {
2154                                                         if(defined($entry) && ref($entry) eq 'HASH') {
2155                                                                 my $sql= 
2156                                                                 "INSERT INTO $table_name "
2157                                                                 ."(timestamp, release, class, type, state) VALUES ("
2158                                                                 .$timestamp.","
2159                                                                 ."'".$entry->{'release'}."',"
2160                                                                 ."'".$entry->{'class'}."',"
2161                                                                 ."'".$entry->{'type'}."',"
2162                                                                 ."'".$entry->{'state'}."')";
2163                                                                 push @sql_list, $sql;
2164                                                         }
2165                                                 }
2166                                         }
2167                                 }
2168                         }
2170                         daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
2171                         if(@sql_list) {
2172                                 unshift @sql_list, "VACUUM";
2173                                 unshift @sql_list, "DELETE FROM $table_name";
2174                                 $fai_release_db->exec_statementlist(\@sql_list);
2175                         }
2176                         daemon_log("$session_id DEBUG: Done with inserting",7);
2177                 }
2178                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
2179         }
2180     $ldap_handle->disconnect;
2181         return $result;
2184 sub get_fai_types {
2185         my $tmp_classes = shift || return undef;
2186         my @result;
2188         foreach my $type(keys %{$tmp_classes}) {
2189                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
2190                         my $entry = {
2191                                 type => $type,
2192                                 state => $tmp_classes->{$type}[0],
2193                         };
2194                         push @result, $entry;
2195                 }
2196         }
2198         return @result;
2201 sub get_fai_state {
2202         my $result = "";
2203         my $tmp_classes = shift || return $result;
2205         foreach my $type(keys %{$tmp_classes}) {
2206                 if(defined($tmp_classes->{$type}[0])) {
2207                         $result = $tmp_classes->{$type}[0];
2208                         
2209                 # State is equal for all types in class
2210                         last;
2211                 }
2212         }
2214         return $result;
2217 sub resolve_fai_classes {
2218         my ($fai_base, $ldap_handle, $session_id) = @_;
2219         if (not defined $session_id) { $session_id = 0; }
2220         my $result;
2221         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
2222         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
2223         my $fai_classes;
2225         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
2226         my $mesg= $ldap_handle->search(
2227                 base   => $fai_base,
2228                 scope  => 'sub',
2229                 attrs  => ['cn','objectClass','FAIstate'],
2230                 filter => $fai_filter,
2231         );
2232         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
2234         if($mesg->{'resultCode'} == 0 &&
2235                 $mesg->count != 0) {
2236                 foreach my $entry (@{$mesg->{entries}}) {
2237                         if($entry->exists('cn')) {
2238                                 my $tmp_dn= $entry->dn();
2240                                 # Skip classname and ou dn parts for class
2241                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
2243                                 # Skip classes without releases
2244                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2245                                         next;
2246                                 }
2248                                 my $tmp_cn= $entry->get_value('cn');
2249                                 my $tmp_state= $entry->get_value('FAIstate');
2251                                 my $tmp_type;
2252                                 # Get FAI type
2253                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
2254                                         if(grep $_ eq $oclass, @possible_fai_classes) {
2255                                                 $tmp_type= $oclass;
2256                                                 last;
2257                                         }
2258                                 }
2260                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2261                                         # A Subrelease
2262                                         my @sub_releases = split(/,/, $tmp_release);
2264                                         # Walk through subreleases and build hash tree
2265                                         my $hash;
2266                                         while(my $tmp_sub_release = pop @sub_releases) {
2267                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2268                                         }
2269                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
2270                                 } else {
2271                                         # A branch, no subrelease
2272                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
2273                                 }
2274                         } elsif (!$entry->exists('cn')) {
2275                                 my $tmp_dn= $entry->dn();
2276                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
2278                                 # Skip classes without releases
2279                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
2280                                         next;
2281                                 }
2283                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
2284                                         # A Subrelease
2285                                         my @sub_releases= split(/,/, $tmp_release);
2287                                         # Walk through subreleases and build hash tree
2288                                         my $hash;
2289                                         while(my $tmp_sub_release = pop @sub_releases) {
2290                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
2291                                         }
2292                                         # Remove the last two characters
2293                                         chop($hash);
2294                                         chop($hash);
2296                                         eval('$fai_classes->'.$hash.'= {}');
2297                                 } else {
2298                                         # A branch, no subrelease
2299                                         if(!exists($fai_classes->{$tmp_release})) {
2300                                                 $fai_classes->{$tmp_release} = {};
2301                                         }
2302                                 }
2303                         }
2304                 }
2306                 # The hash is complete, now we can honor the copy-on-write based missing entries
2307                 foreach my $release (keys %$fai_classes) {
2308                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
2309                 }
2310         }
2311         return $result;
2314 sub apply_fai_inheritance {
2315        my $fai_classes = shift || return {};
2316        my $tmp_classes;
2318        # Get the classes from the branch
2319        foreach my $class (keys %{$fai_classes}) {
2320                # Skip subreleases
2321                if($class =~ /^ou=.*$/) {
2322                        next;
2323                } else {
2324                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
2325                }
2326        }
2328        # Apply to each subrelease
2329        foreach my $subrelease (keys %{$fai_classes}) {
2330                if($subrelease =~ /ou=/) {
2331                        foreach my $tmp_class (keys %{$tmp_classes}) {
2332                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
2333                                        $fai_classes->{$subrelease}->{$tmp_class} =
2334                                        deep_copy($tmp_classes->{$tmp_class});
2335                                } else {
2336                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
2337                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
2338                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
2339                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2340                                                }
2341                                        }
2342                                }
2343                        }
2344                }
2345        }
2347        # Find subreleases in deeper levels
2348        foreach my $subrelease (keys %{$fai_classes}) {
2349                if($subrelease =~ /ou=/) {
2350                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2351                                if($subsubrelease =~ /ou=/) {
2352                                        apply_fai_inheritance($fai_classes->{$subrelease});
2353                                }
2354                        }
2355                }
2356        }
2358        return $fai_classes;
2361 sub get_fai_release_entries {
2362         my $tmp_classes = shift || return;
2363         my $parent = shift || "";
2364         my @result = shift || ();
2366         foreach my $entry (keys %{$tmp_classes}) {
2367                 if(defined($entry)) {
2368                         if($entry =~ /^ou=.*$/) {
2369                                 my $release_name = $entry;
2370                                 $release_name =~ s/ou=//g;
2371                                 if(length($parent)>0) {
2372                                         $release_name = $parent."/".$release_name;
2373                                 }
2374                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2375                                 foreach my $bufentry(@bufentries) {
2376                                         push @result, $bufentry;
2377                                 }
2378                         } else {
2379                                 my @types = get_fai_types($tmp_classes->{$entry});
2380                                 foreach my $type (@types) {
2381                                         push @result, 
2382                                         {
2383                                                 'class' => $entry,
2384                                                 'type' => $type->{'type'},
2385                                                 'release' => $parent,
2386                                                 'state' => $type->{'state'},
2387                                         };
2388                                 }
2389                         }
2390                 }
2391         }
2393         return @result;
2396 sub deep_copy {
2397         my $this = shift;
2398         if (not ref $this) {
2399                 $this;
2400         } elsif (ref $this eq "ARRAY") {
2401                 [map deep_copy($_), @$this];
2402         } elsif (ref $this eq "HASH") {
2403                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2404         } else { die "what type is $_?" }
2408 sub session_run_result {
2409     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2410     $kernel->sig(CHLD => "child_reap");
2413 sub session_run_debug {
2414     my $result = $_[ARG0];
2415     print STDERR "$result\n";
2418 sub session_run_done {
2419     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2420     delete $heap->{task}->{$task_id};
2424 sub create_sources_list {
2425         my $session_id = shift;
2426         my $ldap_handle = &main::get_ldap_handle;
2427         my $result="/tmp/gosa_si_tmp_sources_list";
2429         # Remove old file
2430         if(stat($result)) {
2431                 unlink($result);
2432                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2433         }
2435         my $fh;
2436         open($fh, ">$result");
2437         if (not defined $fh) {
2438                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2439                 return undef;
2440         }
2441         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2442                 my $mesg=$ldap_handle->search(
2443                         base    => $main::ldap_server_dn,
2444                         scope   => 'base',
2445                         attrs   => 'FAIrepository',
2446                         filter  => 'objectClass=FAIrepositoryServer'
2447                 );
2448                 if($mesg->count) {
2449                         foreach my $entry(@{$mesg->{'entries'}}) {
2450                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2451                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2452                                         my $line = "deb $server $release";
2453                                         $sections =~ s/,/ /g;
2454                                         $line.= " $sections";
2455                                         print $fh $line."\n";
2456                                 }
2457                         }
2458                 }
2459         } else {
2460                 if (defined $main::ldap_server_dn){
2461                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2462                 } else {
2463                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2464                 }
2465         }
2466         close($fh);
2468         return $result;
2472 sub run_create_packages_list_db {
2473     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2474         my $session_id = $session->ID;
2476         my $task = POE::Wheel::Run->new(
2477                                         Priority => +20,
2478                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2479                                         StdoutEvent  => "session_run_result",
2480                                         StderrEvent  => "session_run_debug",
2481                                         CloseEvent   => "session_run_done",
2482                                         );
2483         $heap->{task}->{ $task->ID } = $task;
2487 sub create_packages_list_db {
2488         my ($ldap_handle, $sources_file, $session_id) = @_;
2489         
2490         # it should not be possible to trigger a recreation of packages_list_db
2491         # while packages_list_db is under construction, so set flag packages_list_under_construction
2492         # which is tested befor recreation can be started
2493         if (-r $packages_list_under_construction) {
2494                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2495                 return;
2496         } else {
2497                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2498                 # set packages_list_under_construction to true
2499                 system("touch $packages_list_under_construction");
2500                 @packages_list_statements=();
2501         }
2503         if (not defined $session_id) { $session_id = 0; }
2504         if (not defined $ldap_handle) { 
2505                 $ldap_handle= &get_ldap_handle();
2507                 if (not defined $ldap_handle) {
2508                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2509                         unlink($packages_list_under_construction);
2510                         return;
2511                 }
2512         }
2513         if (not defined $sources_file) { 
2514                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2515                 $sources_file = &create_sources_list($session_id);
2516         }
2518         if (not defined $sources_file) {
2519                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2520                 unlink($packages_list_under_construction);
2521                 return;
2522         }
2524         my $line;
2526         open(CONFIG, "<$sources_file") or do {
2527                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2528                 unlink($packages_list_under_construction);
2529                 return;
2530         };
2532         # Read lines
2533         while ($line = <CONFIG>){
2534                 # Unify
2535                 chop($line);
2536                 $line =~ s/^\s+//;
2537                 $line =~ s/^\s+/ /;
2539                 # Strip comments
2540                 $line =~ s/#.*$//g;
2542                 # Skip empty lines
2543                 if ($line =~ /^\s*$/){
2544                         next;
2545                 }
2547                 # Interpret deb line
2548                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2549                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2550                         my $section;
2551                         foreach $section (split(' ', $sections)){
2552                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2553                         }
2554                 }
2555         }
2557         close (CONFIG);
2559         find(\&cleanup_and_extract, keys( %repo_dirs ));
2560         &main::strip_packages_list_statements();
2561         unshift @packages_list_statements, "VACUUM";
2562         $packages_list_db->exec_statementlist(\@packages_list_statements);
2563         unlink($packages_list_under_construction);
2564         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2565         return;
2568 # This function should do some intensive task to minimize the db-traffic
2569 sub strip_packages_list_statements {
2570     my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2571         my @new_statement_list=();
2572         my $hash;
2573         my $insert_hash;
2574         my $update_hash;
2575         my $delete_hash;
2576         my $local_timestamp=get_time();
2578         foreach my $existing_entry (@existing_entries) {
2579                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2580         }
2582         foreach my $statement (@packages_list_statements) {
2583                 if($statement =~ /^INSERT/i) {
2584                         # Assign the values from the insert statement
2585                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2586                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2587                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2588                                 # If section or description has changed, update the DB
2589                                 if( 
2590                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2591                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2592                                 ) {
2593                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2594                                 }
2595                         } else {
2596                                 # Insert a non-existing entry to db
2597                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2598                         }
2599                 } elsif ($statement =~ /^UPDATE/i) {
2600                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2601                         /^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;
2602                         foreach my $distribution (keys %{$hash}) {
2603                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2604                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2605                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2606                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2607                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2608                                                 my $section;
2609                                                 my $description;
2610                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2611                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2612                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2613                                                 }
2614                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2615                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2616                                                 }
2617                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2618                                         }
2619                                 }
2620                         }
2621                 }
2622         }
2624         # TODO: Check for orphaned entries
2626         # unroll the insert_hash
2627         foreach my $distribution (keys %{$insert_hash}) {
2628                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2629                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2630                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2631                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2632                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2633                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2634                                 ."'$local_timestamp')";
2635                         }
2636                 }
2637         }
2639         # unroll the update hash
2640         foreach my $distribution (keys %{$update_hash}) {
2641                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2642                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2643                                 my $set = "";
2644                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2645                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2646                                 }
2647                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2648                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2649                                 }
2650                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2651                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2652                                 }
2653                                 if(defined($set) and length($set) > 0) {
2654                                         $set .= "timestamp = '$local_timestamp'";
2655                                 } else {
2656                                         next;
2657                                 }
2658                                 push @new_statement_list, 
2659                                         "UPDATE $main::packages_list_tn SET $set WHERE"
2660                                         ." distribution = '$distribution'"
2661                                         ." AND package = '$package'"
2662                                         ." AND version = '$version'";
2663                         }
2664                 }
2665         }
2667         @packages_list_statements = @new_statement_list;
2671 sub parse_package_info {
2672     my ($baseurl, $dist, $section, $session_id)= @_;
2673     my ($package);
2674     if (not defined $session_id) { $session_id = 0; }
2675     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2676     $repo_dirs{ "${repo_path}/pool" } = 1;
2678     foreach $package ("Packages.gz"){
2679         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2680         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2681         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2682     }
2683     
2687 sub get_package {
2688     my ($url, $dest, $session_id)= @_;
2689     if (not defined $session_id) { $session_id = 0; }
2691     my $tpath = dirname($dest);
2692     -d "$tpath" || mkpath "$tpath";
2694     # This is ugly, but I've no time to take a look at "how it works in perl"
2695     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2696         system("gunzip -cd '$dest' > '$dest.in'");
2697         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2698         unlink($dest);
2699         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
2700     } else {
2701         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2702     }
2703     return 0;
2707 sub parse_package {
2708     my ($path, $dist, $srv_path, $session_id)= @_;
2709     if (not defined $session_id) { $session_id = 0;}
2710     my ($package, $version, $section, $description);
2711     my $PACKAGES;
2712     my $timestamp = &get_time();
2714     if(not stat("$path.in")) {
2715         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2716         return;
2717     }
2719     open($PACKAGES, "<$path.in");
2720     if(not defined($PACKAGES)) {
2721         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
2722         return;
2723     }
2725     # Read lines
2726     while (<$PACKAGES>){
2727         my $line = $_;
2728         # Unify
2729         chop($line);
2731         # Use empty lines as a trigger
2732         if ($line =~ /^\s*$/){
2733             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2734             push(@packages_list_statements, $sql);
2735             $package = "none";
2736             $version = "none";
2737             $section = "none";
2738             $description = "none"; 
2739             next;
2740         }
2742         # Trigger for package name
2743         if ($line =~ /^Package:\s/){
2744             ($package)= ($line =~ /^Package: (.*)$/);
2745             next;
2746         }
2748         # Trigger for version
2749         if ($line =~ /^Version:\s/){
2750             ($version)= ($line =~ /^Version: (.*)$/);
2751             next;
2752         }
2754         # Trigger for description
2755         if ($line =~ /^Description:\s/){
2756             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2757             next;
2758         }
2760         # Trigger for section
2761         if ($line =~ /^Section:\s/){
2762             ($section)= ($line =~ /^Section: (.*)$/);
2763             next;
2764         }
2766         # Trigger for filename
2767         if ($line =~ /^Filename:\s/){
2768             my ($filename) = ($line =~ /^Filename: (.*)$/);
2769             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2770             next;
2771         }
2772     }
2774     close( $PACKAGES );
2775     unlink( "$path.in" );
2779 sub store_fileinfo {
2780     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2782     my %fileinfo = (
2783         'package' => $package,
2784         'dist' => $dist,
2785         'version' => $vers,
2786     );
2788     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2792 sub cleanup_and_extract {
2793     my $fileinfo = $repo_files{ $File::Find::name };
2795     if( defined $fileinfo ) {
2797         my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2798         my $sql;
2799         my $package = $fileinfo->{ 'package' };
2800         my $newver = $fileinfo->{ 'version' };
2802         mkpath($dir);
2803         system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2805                 if( -f "$dir/DEBIAN/templates" ) {
2807                         daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2809                         my $tmpl= "";
2810                         {
2811                                 local $/=undef;
2812                                 open FILE, "$dir/DEBIAN/templates";
2813                                 $tmpl = &encode_base64(<FILE>);
2814                                 close FILE;
2815                         }
2816                         rmtree("$dir/DEBIAN/templates");
2818                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2819                 push @packages_list_statements, $sql;
2820                 }
2821     }
2823     return;
2827 sub register_at_foreign_servers {   
2828     my ($kernel) = $_[KERNEL];
2830     # hole alle bekannten server aus known_server_db
2831     my $server_sql = "SELECT * FROM $known_server_tn";
2832     my $server_res = $known_server_db->exec_statement($server_sql);
2834     # no entries in known_server_db
2835     if (not ref(@$server_res[0]) eq "ARRAY") { 
2836         # TODO
2837     }
2839     # detect already connected clients
2840     my $client_sql = "SELECT * FROM $known_clients_tn"; 
2841     my $client_res = $known_clients_db->exec_statement($client_sql);
2843     # send my server details to all other gosa-si-server within the network
2844     foreach my $hit (@$server_res) {
2845         my $hostname = @$hit[0];
2846         my $hostkey = &create_passwd;
2848         # add already connected clients to registration message 
2849         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
2850         &add_content2xml_hash($myhash, 'key', $hostkey);
2851         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
2852         
2853         # build registration message and send it
2854         my $foreign_server_msg = &create_xml_string($myhash);
2855         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
2856     }
2857     
2858     $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
2859     return;
2863 #==== MAIN = main ==============================================================
2864 #  parse commandline options
2865 Getopt::Long::Configure( "bundling" );
2866 GetOptions("h|help" => \&usage,
2867         "c|config=s" => \$cfg_file,
2868         "f|foreground" => \$foreground,
2869         "v|verbose+" => \$verbose,
2870         "no-arp+" => \$no_arp,
2871            );
2873 #  read and set config parameters
2874 &check_cmdline_param ;
2875 &read_configfile;
2876 &check_pid;
2878 $SIG{CHLD} = 'IGNORE';
2880 # forward error messages to logfile
2881 if( ! $foreground ) {
2882   open( STDIN,  '+>/dev/null' );
2883   open( STDOUT, '+>&STDIN'    );
2884   open( STDERR, '+>&STDIN'    );
2887 # Just fork, if we are not in foreground mode
2888 if( ! $foreground ) { 
2889     chdir '/'                 or die "Can't chdir to /: $!";
2890     $pid = fork;
2891     setsid                    or die "Can't start a new session: $!";
2892     umask 0;
2893 } else { 
2894     $pid = $$; 
2897 # Do something useful - put our PID into the pid_file
2898 if( 0 != $pid ) {
2899     open( LOCK_FILE, ">$pid_file" );
2900     print LOCK_FILE "$pid\n";
2901     close( LOCK_FILE );
2902     if( !$foreground ) { 
2903         exit( 0 ) 
2904     };
2907 # parse head url and revision from svn
2908 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2909 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2910 $server_headURL = defined $1 ? $1 : 'unknown' ;
2911 $server_revision = defined $2 ? $2 : 'unknown' ;
2912 if ($server_headURL =~ /\/tag\// || 
2913         $server_headURL =~ /\/branches\// ) {
2914     $server_status = "stable"; 
2915 } else {
2916     $server_status = "developmental" ;
2920 daemon_log(" ", 1);
2921 daemon_log("$0 started!", 1);
2922 daemon_log("status: $server_status", 1);
2923 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
2925 # connect to incoming_db
2926 unlink($incoming_file_name);
2927 $incoming_db = GOSA::DBsqlite->new($incoming_file_name);
2928 $incoming_db->create_table($incoming_tn, \@incoming_col_names);
2930 # connect to gosa-si job queue
2931 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2932 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2934 # connect to known_clients_db
2935 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2936 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2938 # connect to foreign_clients_db
2939 $foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
2940 $foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
2942 # connect to known_server_db
2943 unlink($known_server_file_name);
2944 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2945 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2947 # connect to login_usr_db
2948 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2949 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2951 # connect to fai_server_db and fai_release_db
2952 unlink($fai_server_file_name);
2953 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2954 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2956 unlink($fai_release_file_name);
2957 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2958 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2960 # connect to packages_list_db
2961 #unlink($packages_list_file_name);
2962 unlink($packages_list_under_construction);
2963 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2964 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2966 # connect to messaging_db
2967 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2968 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2971 # create xml object used for en/decrypting
2972 $xml = new XML::Simple();
2975 # foreign servers 
2976 my @foreign_server_list;
2978 # add foreign server from cfg file
2979 if ($foreign_server_string ne "") {
2980     my @cfg_foreign_server_list = split(",", $foreign_server_string);
2981     foreach my $foreign_server (@cfg_foreign_server_list) {
2982         push(@foreign_server_list, $foreign_server);
2983     }
2986 # add foreign server from dns
2987 my @tmp_servers;
2988 if ( !$server_domain) {
2989     # Try our DNS Searchlist
2990     for my $domain(get_dns_domains()) {
2991         chomp($domain);
2992         my @tmp_domains= &get_server_addresses($domain);
2993         if(@tmp_domains) {
2994             for my $tmp_server(@tmp_domains) {
2995                 push @tmp_servers, $tmp_server;
2996             }
2997         }
2998     }
2999     if(@tmp_servers && length(@tmp_servers)==0) {
3000         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3001     }
3002 } else {
3003     @tmp_servers = &get_server_addresses($server_domain);
3004     if( 0 == @tmp_servers ) {
3005         daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
3006     }
3008 foreach my $server (@tmp_servers) { 
3009     unshift(@foreign_server_list, $server); 
3011 # eliminate duplicate entries
3012 @foreign_server_list = &del_doubles(@foreign_server_list);
3013 my $all_foreign_server = join(", ", @foreign_server_list);
3014 daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
3016 # add all found foreign servers to known_server
3017 my $act_timestamp = &get_time();
3018 foreach my $foreign_server (@foreign_server_list) {
3020         # do not add myself to known_server_db
3021         if (&is_local($foreign_server)) { next; }
3022         ######################################
3024     my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
3025             primkey=>['hostname'],
3026             hostname=>$foreign_server,
3027             status=>'not_jet_registered',
3028             hostkey=>"none",
3029             timestamp=>$act_timestamp,
3030             } );
3034 # import all modules
3035 &import_modules;
3036 # check wether all modules are gosa-si valid passwd check
3037 &password_check;
3040 POE::Component::Server::TCP->new(
3041     Alias => "TCP_SERVER",
3042         Port => $server_port,
3043         ClientInput => sub {
3044         my ($kernel, $input) = @_[KERNEL, ARG0];
3045         push(@tasks, $input);
3046         push(@msgs_to_decrypt, $input);
3047         $kernel->yield("msg_to_decrypt");
3048         },
3049     InlineStates => {
3050         msg_to_decrypt => \&msg_to_decrypt,
3051         next_task => \&next_task,
3052         task_result => \&handle_task_result,
3053         task_done   => \&handle_task_done,
3054         task_debug  => \&handle_task_debug,
3055         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3056     }
3057 );
3059 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
3061 # create session for repeatedly checking the job queue for jobs
3062 POE::Session->create(
3063         inline_states => {
3064                 _start => \&session_start,
3065         register_at_foreign_servers => \&register_at_foreign_servers,
3066         sig_handler => \&sig_handler,
3067         next_task => \&next_task,
3068         task_result => \&handle_task_result,
3069         task_done   => \&handle_task_done,
3070         task_debug  => \&handle_task_debug,
3071         watch_for_next_tasks => \&watch_for_next_tasks,
3072         watch_for_new_messages => \&watch_for_new_messages,
3073         watch_for_delivery_messages => \&watch_for_delivery_messages,
3074         watch_for_done_messages => \&watch_for_done_messages,
3075                 watch_for_new_jobs => \&watch_for_new_jobs,
3076         watch_for_modified_jobs => \&watch_for_modified_jobs,
3077         watch_for_done_jobs => \&watch_for_done_jobs,
3078         watch_for_old_known_clients => \&watch_for_old_known_clients,
3079         create_packages_list_db => \&run_create_packages_list_db,
3080         create_fai_server_db => \&run_create_fai_server_db,
3081         create_fai_release_db => \&run_create_fai_release_db,
3082                 recreate_packages_db => \&run_recreate_packages_db,
3083         session_run_result => \&session_run_result,
3084         session_run_debug => \&session_run_debug,
3085         session_run_done => \&session_run_done,
3086         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
3087         }
3088 );
3091 POE::Kernel->run();
3092 exit;