Code

Added simple locale extractor
[gosa.git] / gosa-si / gosa-si-server.old
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 use strict;
25 use warnings;
26 use Getopt::Long;
27 use Config::IniFiles;
28 use POSIX;
30 use Fcntl;
31 use IO::Socket::INET;
32 use IO::Handle;
33 use IO::Select;
34 use Symbol qw(qualify_to_ref);
35 use Crypt::Rijndael;
36 use MIME::Base64;
37 use Digest::MD5  qw(md5 md5_hex md5_base64);
38 use XML::Simple;
39 use Data::Dumper;
40 use Sys::Syslog qw( :DEFAULT setlogsock);
41 use Cwd;
42 use File::Spec;
43 use File::Basename;
44 use File::Find;
45 use File::Copy;
46 use File::Path;
47 use GOSA::DBsqlite;
48 use GOSA::GosaSupportDaemon;
49 use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
50 use Net::LDAP;
51 use Net::LDAP::Util qw(:escape);
53 my $modules_path = "/usr/lib/gosa-si/modules";
54 use lib "/usr/lib/gosa-si/modules";
55 my $server_version = '$HeadURL$:$Rev$';
56 my $server_headURL;
57 my $server_revision;
58 my $server_status;
61 # TODO es gibt eine globale funktion get_ldap_handle
62 # - ist in einer session dieses ldap handle schon vorhanden, wird es zurückgegeben
63 # - ist es nicht vorhanden, wird es erzeugt, im heap für spätere ldap anfragen gespeichert und zurückgegeben
64 # - sessions die kein ldap handle brauchen, sollen auch keins haben
65 # - wird eine session geschlossen, muss das ldap verbindung vorher beendet werden
66 our $global_kernel;
68 my (%cfg_defaults, $foreground, $verbose, $ping_timeout);
69 my ($bus_activ, $bus, $msg_to_bus, $bus_cipher);
70 my ($server);
71 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
72 my ($messaging_db_loop_delay);
73 my ($known_modules);
74 my ($pid_file, $procid, $pid, $log_file);
75 my ($arp_activ, $arp_fifo);
76 my ($xml);
77 my $sources_list;
78 my $max_clients;
79 my %repo_files=();
80 my $repo_path;
81 my %repo_dirs=();
82 # variables declared in config file are always set to 'our'
83 our (%cfg_defaults, $log_file, $pid_file, 
84     $server_ip, $server_port, $SIPackages_key, 
85         $ClientPackages_key, $ServerPackages_key,
86     $arp_activ, $gosa_unit_tag,
87     $GosaPackages_key, $gosa_ip, $gosa_port, $gosa_timeout,
88 );
90 # additional variable which should be globaly accessable
91 our $server_address;
92 our $server_mac_address;
93 our $bus_address;
94 our $gosa_address;
95 our $no_bus;
96 our $no_arp;
97 our $verbose;
98 our $forground;
99 our $cfg_file;
100 #our ($ldap_handle, $ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
101 our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
104 # specifies the verbosity of the daemon_log
105 $verbose = 0 ;
107 # if foreground is not null, script will be not forked to background
108 $foreground = 0 ;
110 # specifies the timeout seconds while checking the online status of a registrating client
111 $ping_timeout = 5;
113 $no_bus = 0;
114 $bus_activ = "true";
115 $no_arp = 0;
116 my $packages_list_under_construction = "/tmp/packages_list_creation_in_progress";
117 my @packages_list_statements;
118 my $watch_for_new_jobs_in_progress = 0;
120 our $prg= basename($0);
122 # holds all gosa jobs
123 our $job_db;
124 our $job_queue_tn = 'jobs';
125 my $job_queue_file_name;
126 my @job_queue_col_names = ("id INTEGER PRIMARY KEY", 
127                 "timestamp DEFAULT 'none'", 
128                 "status DEFAULT 'none'", 
129                 "result DEFAULT 'none'", 
130                 "progress DEFAULT 'none'", 
131         "headertag DEFAULT 'none'", 
132                 "targettag DEFAULT 'none'", 
133                 "xmlmessage DEFAULT 'none'", 
134                 "macaddress DEFAULT 'none'",
135                 "plainname DEFAULT 'none'",
136                 );
138 # holds all other gosa-sd as well as the gosa-sd-bus
139 our $known_server_db;
140 our $known_server_tn = "known_server";
141 my $known_server_file_name;
142 my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
144 # holds all registrated clients
145 our $known_clients_db;
146 our $known_clients_tn = "known_clients";
147 my $known_clients_file_name;
148 my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events");
150 # holds all logged in user at each client 
151 our $login_users_db;
152 our $login_users_tn = "login_users";
153 my $login_users_file_name;
154 my @login_users_col_names = ("client", "user", "timestamp");
156 # holds all fai server, the debian release and tag
157 our $fai_server_db;
158 our $fai_server_tn = "fai_server"; 
159 my $fai_server_file_name;
160 our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag"); 
162 our $fai_release_db;
163 our $fai_release_tn = "fai_release"; 
164 my $fai_release_file_name;
165 our @fai_release_col_names = ("timestamp", "release", "class", "type", "state"); 
167 # holds all packages available from different repositories
168 our $packages_list_db;
169 our $packages_list_tn = "packages_list";
170 my $packages_list_file_name;
171 our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
172 my $outdir = "/tmp/packages_list_db";
173 my $arch = "i386"; 
175 # holds all messages which should be delivered to a user
176 our $messaging_db;
177 our $messaging_tn = "messaging"; 
178 our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to", 
179         "flag", "direction", "delivery_time", "message", "timestamp" );
180 my $messaging_file_name;
182 # path to directory to store client install log files
183 our $client_fai_log_dir = "/var/log/fai"; 
185 # queue which stores taskes until one of the $max_children children are ready to process the task
186 my @tasks = qw();
187 my $max_children = 2;
190 %cfg_defaults = (
191 "general" => {
192     "log-file" => [\$log_file, "/var/run/".$prg.".log"],
193     "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
194     },
195 "bus" => {
196     "activ" => [\$bus_activ, "true"],
197     },
198 "server" => {
199     "port" => [\$server_port, "20081"],
200     "known-clients" => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
201     "known-servers" => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
202     "login-users" => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
203     "fai-server" => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
204     "fai-release" => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
205     "packages-list" => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
206     "messaging" => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
207     "source-list" => [\$sources_list, '/etc/apt/sources.list'],
208     "repo-path" => [\$repo_path, '/srv/www/repository'],
209     "ldap-uri" => [\$ldap_uri, ""],
210     "ldap-base" => [\$ldap_base, ""],
211     "ldap-admin-dn" => [\$ldap_admin_dn, ""],
212     "ldap-admin-password" => [\$ldap_admin_password, ""],
213     "gosa-unit-tag" => [\$gosa_unit_tag, ""],
214     "max-clients" => [\$max_clients, 10],
215     },
216 "GOsaPackages" => {
217     "ip" => [\$gosa_ip, "0.0.0.0"],
218     "port" => [\$gosa_port, "20082"],
219     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
220     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
221     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
222     "key" => [\$GosaPackages_key, "none"],
223     },
224 "SIPackages" => {
225     "key" => [\$SIPackages_key, "none"],
226     },
227 "ClientPackages" => {
228         "key" => [\$ClientPackages_key, "none"],
229         },
230 "ServerPackages" => {
231         "key" => [\$ServerPackages_key, "none"],
232         },
233 );
236 #===  FUNCTION  ================================================================
237 #         NAME:  usage
238 #   PARAMETERS:  nothing
239 #      RETURNS:  nothing
240 #  DESCRIPTION:  print out usage text to STDERR
241 #===============================================================================
242 sub usage {
243     print STDERR << "EOF" ;
244 usage: $prg [-hvf] [-c config]
246            -h        : this (help) message
247            -c <file> : config file
248            -f        : foreground, process will not be forked to background
249            -v        : be verbose (multiple to increase verbosity)
250            -no-bus   : starts $prg without connection to bus
251            -no-arp   : starts $prg without connection to arp module
252  
253 EOF
254     print "\n" ;
258 #===  FUNCTION  ================================================================
259 #         NAME:  read_configfile
260 #   PARAMETERS:  cfg_file - string -
261 #      RETURNS:  nothing
262 #  DESCRIPTION:  read cfg_file and set variables
263 #===============================================================================
264 sub read_configfile {
265     my $cfg;
266     if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
267         if( -r $cfg_file ) {
268             $cfg = Config::IniFiles->new( -file => $cfg_file );
269         } else {
270             print STDERR "Couldn't read config file!\n";
271         }
272     } else {
273         $cfg = Config::IniFiles->new() ;
274     }
275     foreach my $section (keys %cfg_defaults) {
276         foreach my $param (keys %{$cfg_defaults{ $section }}) {
277             my $pinfo = $cfg_defaults{ $section }{ $param };
278             ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
279         }
280     }
284 #===  FUNCTION  ================================================================
285 #         NAME:  logging
286 #   PARAMETERS:  level - string - default 'info'
287 #                msg - string -
288 #                facility - string - default 'LOG_DAEMON'
289 #      RETURNS:  nothing
290 #  DESCRIPTION:  function for logging
291 #===============================================================================
292 sub daemon_log {
293     # log into log_file
294     my( $msg, $level ) = @_;
295     if(not defined $msg) { return }
296     if(not defined $level) { $level = 1 }
297     if(defined $log_file){
298         open(LOG_HANDLE, ">>$log_file");
299         chmod 0600, $log_file;
300         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
301             print STDERR "cannot open $log_file: $!";
302             return }
303             chomp($msg);
304                         $msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
305             if($level <= $verbose){
306                 my ($seconds, $minutes, $hours, $monthday, $month,
307                         $year, $weekday, $yearday, $sommertime) = localtime(time);
308                 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
309                 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
310                 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
311                 my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
312                 $month = $monthnames[$month];
313                 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
314                 $year+=1900;
315                 my $name = $prg;
317                 my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
318                 print LOG_HANDLE $log_msg;
319                 if( $foreground ) { 
320                     print STDERR $log_msg;
321                 }
322             }
323         close( LOG_HANDLE );
324     }
328 #===  FUNCTION  ================================================================
329 #         NAME:  check_cmdline_param
330 #   PARAMETERS:  nothing
331 #      RETURNS:  nothing
332 #  DESCRIPTION:  validates commandline parameter
333 #===============================================================================
334 sub check_cmdline_param () {
335     my $err_config;
336     my $err_counter = 0;
337         if(not defined($cfg_file)) {
338                 $cfg_file = "/etc/gosa-si/server.conf";
339                 if(! -r $cfg_file) {
340                         $err_config = "please specify a config file";
341                         $err_counter += 1;
342                 }
343     }
344     if( $err_counter > 0 ) {
345         &usage( "", 1 );
346         if( defined( $err_config)) { print STDERR "$err_config\n"}
347         print STDERR "\n";
348         exit( -1 );
349     }
353 #===  FUNCTION  ================================================================
354 #         NAME:  check_pid
355 #   PARAMETERS:  nothing
356 #      RETURNS:  nothing
357 #  DESCRIPTION:  handels pid processing
358 #===============================================================================
359 sub check_pid {
360     $pid = -1;
361     # Check, if we are already running
362     if( open(LOCK_FILE, "<$pid_file") ) {
363         $pid = <LOCK_FILE>;
364         if( defined $pid ) {
365             chomp( $pid );
366             if( -f "/proc/$pid/stat" ) {
367                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
368                 if( $stat ) {
369                                         daemon_log("ERROR: Already running",1);
370                     close( LOCK_FILE );
371                     exit -1;
372                 }
373             }
374         }
375         close( LOCK_FILE );
376         unlink( $pid_file );
377     }
379     # create a syslog msg if it is not to possible to open PID file
380     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
381         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
382         if (open(LOCK_FILE, '<', $pid_file)
383                 && ($pid = <LOCK_FILE>))
384         {
385             chomp($pid);
386             $msg .= "(PID $pid)\n";
387         } else {
388             $msg .= "(unable to read PID)\n";
389         }
390         if( ! ($foreground) ) {
391             openlog( $0, "cons,pid", "daemon" );
392             syslog( "warning", $msg );
393             closelog();
394         }
395         else {
396             print( STDERR " $msg " );
397         }
398         exit( -1 );
399     }
402 #===  FUNCTION  ================================================================
403 #         NAME:  import_modules
404 #   PARAMETERS:  module_path - string - abs. path to the directory the modules 
405 #                are stored
406 #      RETURNS:  nothing
407 #  DESCRIPTION:  each file in module_path which ends with '.pm' and activation 
408 #                state is on is imported by "require 'file';"
409 #===============================================================================
410 sub import_modules {
411     daemon_log(" ", 1);
413     if (not -e $modules_path) {
414         daemon_log("ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
415     }
417     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
418     while (defined (my $file = readdir (DIR))) {
419         if (not $file =~ /(\S*?).pm$/) {
420             next;
421         }
422                 my $mod_name = $1;
424         if( $file =~ /ArpHandler.pm/ ) {
425             if( $no_arp > 0 ) {
426                 next;
427             }
428         }
429         
430         eval { require $file; };
431         if ($@) {
432             daemon_log("ERROR: gosa-si-server could not load module $file", 1);
433             daemon_log("$@", 5);
434                 } else {
435                         my $info = eval($mod_name.'::get_module_info()');
436                         # Only load module if get_module_info() returns a non-null object
437                         if( $info ) {
438                                 my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
439                                 $known_modules->{$mod_name} = $info;
440                                 daemon_log("INFO: module $mod_name loaded", 5);
441                         }
442                 }
443     }   
444     close (DIR);
448 #===  FUNCTION  ================================================================
449 #         NAME:  sig_int_handler
450 #   PARAMETERS:  signal - string - signal arose from system
451 #      RETURNS:  noting
452 #  DESCRIPTION:  handels tasks to be done befor signal becomes active
453 #===============================================================================
454 sub sig_int_handler {
455     my ($signal) = @_;
457 #       if (defined($ldap_handle)) {
458 #               $ldap_handle->disconnect;
459 #       }
460     # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
461     
463     daemon_log("shutting down gosa-si-server", 1);
464     system("kill `ps -C gosa-si-server -o pid=`");
466 $SIG{INT} = \&sig_int_handler;
469 sub check_key_and_xml_validity {
470     my ($crypted_msg, $module_key, $session_id) = @_;
471     my $msg;
472     my $msg_hash;
473     my $error_string;
474     eval{
475         $msg = &decrypt_msg($crypted_msg, $module_key);
477         if ($msg =~ /<xml>/i){
478             $msg =~ s/\s+/ /g;  # just for better daemon_log
479             daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
480             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
482             ##############
483             # check header
484             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
485             my $header_l = $msg_hash->{'header'};
486             if( 1 > @{$header_l} ) { die 'empty header tag'; }
487             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
488             my $header = @{$header_l}[0];
489             if( 0 == length $header) { die 'empty string in header tag'; }
491             ##############
492             # check source
493             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
494             my $source_l = $msg_hash->{'source'};
495             if( 1 > @{$source_l} ) { die 'empty source tag'; }
496             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
497             my $source = @{$source_l}[0];
498             if( 0 == length $source) { die 'source error'; }
500             ##############
501             # check target
502             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
503             my $target_l = $msg_hash->{'target'};
504             if( 1 > @{$target_l} ) { die 'empty target tag'; }
505         }
506     };
507     if($@) {
508         daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
509         $msg = undef;
510         $msg_hash = undef;
511     }
513     return ($msg, $msg_hash);
517 sub check_outgoing_xml_validity {
518     my ($msg) = @_;
520     my $msg_hash;
521     eval{
522         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
524         ##############
525         # check header
526         my $header_l = $msg_hash->{'header'};
527         if( 1 != @{$header_l} ) {
528             die 'no or more than one headers specified';
529         }
530         my $header = @{$header_l}[0];
531         if( 0 == length $header) {
532             die 'header has length 0';
533         }
535         ##############
536         # check source
537         my $source_l = $msg_hash->{'source'};
538         if( 1 != @{$source_l} ) {
539             die 'no or more than 1 sources specified';
540         }
541         my $source = @{$source_l}[0];
542         if( 0 == length $source) {
543             die 'source has length 0';
544         }
545         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
546                 $source =~ /^GOSA$/i ) {
547             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
548         }
549         
550         ##############
551         # check target  
552         my $target_l = $msg_hash->{'target'};
553         if( 0 == @{$target_l} ) {
554             die "no targets specified";
555         }
556         foreach my $target (@$target_l) {
557             if( 0 == length $target) {
558                 die "target has length 0";
559             }
560             unless( $target =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
561                     $target =~ /^GOSA$/i ||
562                     $target =~ /^\*$/ ||
563                     $target =~ /KNOWN_SERVER/i ||
564                     $target =~ /JOBDB/i ||
565                     $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 ){
566                 die "target '$target' is not a complete ip-address with port or a valid target name or a mac-address";
567             }
568         }
569     };
570     if($@) {
571         daemon_log("WARNING: outgoing msg is not gosa-si envelope conform", 5);
572         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 8);
573         $msg_hash = undef;
574     }
576     return ($msg_hash);
580 sub input_from_known_server {
581     my ($input, $remote_ip, $session_id) = @_ ;  
582     my ($msg, $msg_hash, $module);
584     my $sql_statement= "SELECT * FROM known_server";
585     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
587     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
588         my $host_name = $hit->{hostname};
589         if( not $host_name =~ "^$remote_ip") {
590             next;
591         }
592         my $host_key = $hit->{hostkey};
593         daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
594         daemon_log("DEBUG: input_from_known_server: host_key: $host_key", 7);
596         # check if module can open msg envelope with module key
597         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
598         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
599             daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
600             daemon_log("$@", 8);
601             next;
602         }
603         else {
604             $msg = $tmp_msg;
605             $msg_hash = $tmp_msg_hash;
606             $module = "SIPackages";
607             last;
608         }
609     }
611     if( (!$msg) || (!$msg_hash) || (!$module) ) {
612         daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
613     }
614   
615     return ($msg, $msg_hash, $module);
619 sub input_from_known_client {
620     my ($input, $remote_ip, $session_id) = @_ ;  
621     my ($msg, $msg_hash, $module);
623     my $sql_statement= "SELECT * FROM known_clients";
624     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
625     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
626         my $host_name = $hit->{hostname};
627         if( not $host_name =~ /^$remote_ip:\d*$/) {
628                 next;
629                 }
630         my $host_key = $hit->{hostkey};
631         &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
632         &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
634         # check if module can open msg envelope with module key
635         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
637         if( (!$msg) || (!$msg_hash) ) {
638             &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
639             &daemon_log("$@", 8);
640             next;
641         }
642         else {
643             $module = "SIPackages";
644             last;
645         }
646     }
648     if( (!$msg) || (!$msg_hash) || (!$module) ) {
649         &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
650     }
652     return ($msg, $msg_hash, $module);
656 sub input_from_unknown_host {
657     no strict "refs";
658     my ($input, $session_id) = @_ ;
659     my ($msg, $msg_hash, $module);
660     my $error_string;
661     
662         my %act_modules = %$known_modules;
664         while( my ($mod, $info) = each(%act_modules)) {
666         # check a key exists for this module
667         my $module_key = ${$mod."_key"};
668         if( not defined $module_key ) {
669             if( $mod eq 'ArpHandler' ) {
670                 next;
671             }
672             daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
673             next;
674         }
675         daemon_log("$session_id DEBUG: $mod: $module_key", 7);
677         # check if module can open msg envelope with module key
678         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
679         if( (not defined $msg) || (not defined $msg_hash) ) {
680             next;
681         }
682         else {
683             $module = $mod;
684             last;
685         }
686     }
688     if( (!$msg) || (!$msg_hash) || (!$module)) {
689         daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
690     }
692     return ($msg, $msg_hash, $module);
696 sub create_ciphering {
697     my ($passwd) = @_;
698         if((!defined($passwd)) || length($passwd)==0) {
699                 $passwd = "";
700         }
701     $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
702     my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
703     my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
704     $my_cipher->set_iv($iv);
705     return $my_cipher;
709 sub encrypt_msg {
710     my ($msg, $key) = @_;
711     my $my_cipher = &create_ciphering($key);
712     my $len;
713     {
714             use bytes;
715             $len= 16-length($msg)%16;
716     }
717     $msg = "\0"x($len).$msg;
718     $msg = $my_cipher->encrypt($msg);
719     chomp($msg = &encode_base64($msg));
720     # there are no newlines allowed inside msg
721     $msg=~ s/\n//g;
722     return $msg;
726 sub decrypt_msg {
728     my ($msg, $key) = @_ ;
729     $msg = &decode_base64($msg);
730     my $my_cipher = &create_ciphering($key);
731     $msg = $my_cipher->decrypt($msg); 
732     $msg =~ s/\0*//g;
733     return $msg;
737 sub get_encrypt_key {
738     my ($target) = @_ ;
739     my $encrypt_key;
740     my $error = 0;
742     # target can be in known_server
743     if( not defined $encrypt_key ) {
744         my $sql_statement= "SELECT * FROM known_server WHERE hostname='$target'";
745         my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
746         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
747             my $host_name = $hit->{hostname};
748             if( $host_name ne $target ) {
749                 next;
750             }
751             $encrypt_key = $hit->{hostkey};
752             last;
753         }
754     }
756     # target can be in known_client
757     if( not defined $encrypt_key ) {
758         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$target'";
759         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
760         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
761             my $host_name = $hit->{hostname};
762             if( $host_name ne $target ) {
763                 next;
764             }
765             $encrypt_key = $hit->{hostkey};
766             last;
767         }
768     }
770     return $encrypt_key;
774 #===  FUNCTION  ================================================================
775 #         NAME:  open_socket
776 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
777 #                [PeerPort] string necessary if port not appended by PeerAddr
778 #      RETURNS:  socket IO::Socket::INET
779 #  DESCRIPTION:  open a socket to PeerAddr
780 #===============================================================================
781 sub open_socket {
782     my ($PeerAddr, $PeerPort) = @_ ;
783     if(defined($PeerPort)){
784         $PeerAddr = $PeerAddr.":".$PeerPort;
785     }
786     my $socket;
787     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
788             Porto => "tcp",
789             Type => SOCK_STREAM,
790             Timeout => 5,
791             );
792     if(not defined $socket) {
793         return;
794     }
795 #    &daemon_log("DEBUG: open_socket: $PeerAddr", 7);
796     return $socket;
800 #===  FUNCTION  ================================================================
801 #         NAME:  get_ip 
802 #   PARAMETERS:  interface name (i.e. eth0)
803 #      RETURNS:  (ip address) 
804 #  DESCRIPTION:  Uses ioctl to get ip address directly from system.
805 #===============================================================================
806 sub get_ip {
807         my $ifreq= shift;
808         my $result= "";
809         my $SIOCGIFADDR= 0x8915;       # man 2 ioctl_list
810         my $proto= getprotobyname('ip');
812         socket SOCKET, PF_INET, SOCK_DGRAM, $proto
813                 or die "socket: $!";
815         if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
816                 my ($if, $sin)    = unpack 'a16 a16', $ifreq;
817                 my ($port, $addr) = sockaddr_in $sin;
818                 my $ip            = inet_ntoa $addr;
820                 if ($ip && length($ip) > 0) {
821                         $result = $ip;
822                 }
823         }
825         return $result;
829 sub get_local_ip_for_remote_ip {
830         my $remote_ip= shift;
831         my $result="0.0.0.0";
833         if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
834                 if($remote_ip eq "127.0.0.1") {
835                         $result = "127.0.0.1";
836                 } else {
837                         my $PROC_NET_ROUTE= ('/proc/net/route');
839                         open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
840                                 or die "Could not open $PROC_NET_ROUTE";
842                         my @ifs = <PROC_NET_ROUTE>;
844                         close(PROC_NET_ROUTE);
846                         # Eat header line
847                         shift @ifs;
848                         chomp @ifs;
849                         foreach my $line(@ifs) {
850                                 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
851                                 my $destination;
852                                 my $mask;
853                                 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
854                                 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
855                                 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
856                                 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
857                                 if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
858                                         # destination matches route, save mac and exit
859                                         $result= &get_ip($Iface);
860                                         last;
861                                 }
862                         }
863                 }
864         } else {
865                 daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
866         }
867         return $result;
871 sub send_msg_to_target {
872     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
873     my $error = 0;
874     my $header;
875     my $new_status;
876     my $act_status;
877     my ($sql_statement, $res);
878   
879     if( $msg_header ) {
880         $header = "'$msg_header'-";
881     } else {
882         $header = "";
883     }
885         # Patch the source ip
886         if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
887                 my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
888                 $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
889         }
891     # encrypt xml msg
892     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
894     # opensocket
895     my $socket = &open_socket($address);
896     if( !$socket ) {
897         daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
898         $error++;
899     }
900     
901     if( $error == 0 ) {
902         # send xml msg
903         print $socket $crypted_msg."\n";
905         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
906         #daemon_log("DEBUG: message:\n$msg", 9);
907         
908     }
910     # close socket in any case
911     if( $socket ) {
912         close $socket;
913     }
915     if( $error > 0 ) { $new_status = "down"; }
916     else { $new_status = $msg_header; }
919     # known_clients
920     $sql_statement = "SELECT * FROM known_clients WHERE hostname='$address'";
921     $res = $known_clients_db->select_dbentry($sql_statement);
922     if( keys(%$res) > 0) {
923         $act_status = $res->{1}->{'status'};
924         if( $act_status eq "down" ) {
925             $sql_statement = "DELETE FROM known_clients WHERE hostname='$address'";
926             $res = $known_clients_db->del_dbentry($sql_statement);
927             daemon_log("$session_id WARNING: failed 2x to send msg to host '$address', delete host from known_clients", 3);
928         } else { 
929             $sql_statement = "UPDATE known_clients SET status='$new_status' WHERE hostname='$address'";
930             $res = $known_clients_db->update_dbentry($sql_statement);
931             if($new_status eq "down"){
932                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
933             } else {
934                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
935             }
936         }
937     }
939     # known_server
940     $sql_statement = "SELECT * FROM known_server WHERE hostname='$address'";
941     $res = $known_server_db->select_dbentry($sql_statement);
942     if( keys(%$res) > 0 ) {
943         $act_status = $res->{1}->{'status'};
944         if( $act_status eq "down" ) {
945             $sql_statement = "DELETE FROM known_server WHERE hostname='$address'";
946             $res = $known_server_db->del_dbentry($sql_statement);
947             daemon_log("$session_id WARNING: failed 2x to a send msg to host '$address', delete host from known_server", 3);
948         } 
949         else { 
950             $sql_statement = "UPDATE known_server SET status='$new_status' WHERE hostname='$address'";
951             $res = $known_server_db->update_dbentry($sql_statement);
952             if($new_status eq "down"){
953                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
954             }
955             else {
956                 daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
957             }
958         }
959     }
960     return $error; 
964 sub update_jobdb_status_for_send_msgs {
965     my ($answer, $error) = @_;
966     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
967         my $jobdb_id = $1;
968             
969         # sending msg faild
970         if( $error ) {
971             if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
972                 my $sql_statement = "UPDATE $job_queue_tn ".
973                     "SET status='error', result='can not deliver msg, please consult log file' ".
974                     "WHERE id=$jobdb_id";
975                 my $res = $job_db->update_dbentry($sql_statement);
976             }
978         # sending msg was successful
979         } else {
980             my $sql_statement = "UPDATE $job_queue_tn ".
981                 "SET status='done' ".
982                 "WHERE id=$jobdb_id AND status='processed'";
983             my $res = $job_db->update_dbentry($sql_statement);
984         }
985     }
988 sub _start {
989     my ($kernel) = $_[KERNEL];
990     &trigger_db_loop($kernel);
991     $global_kernel = $kernel;
992         $kernel->yield('create_fai_server_db', $fai_server_tn );
993         $kernel->yield('create_fai_release_db', $fai_release_tn );
994         $kernel->sig(USR1 => "sig_handler");
995         $kernel->sig(USR2 => "create_packages_list_db");
998 sub sig_handler {
999         my ($kernel, $signal) = @_[KERNEL, ARG0] ;
1000         daemon_log("0 INFO got signal '$signal'", 1); 
1001         $kernel->sig_handled();
1002         return;
1005 sub next_task {
1006     my ($session, $heap) = @_[SESSION, HEAP];
1008     while ( keys( %{ $heap->{task} } ) < $max_children ) {
1009         my $next_task = shift @tasks;
1010         last unless defined $next_task;
1012         my $task = POE::Wheel::Run->new(
1013                 Program => sub { process_task($session, $heap, $next_task) },
1014                 StdioFilter => POE::Filter::Reference->new(),
1015                 StdoutEvent  => "task_result",
1016                 StderrEvent  => "task_debug",
1017                 CloseEvent   => "task_done",
1018                );
1020         $heap->{task}->{ $task->ID } = $task;
1021     }
1024 sub handle_task_result {
1025     my ($kernel, $heap, $result) = @_[KERNEL, HEAP, ARG0];
1026     my $client_answer = $result->{'answer'};
1027     if( $client_answer =~ s/session_id=(\d+)$// ) {
1028         my $session_id = $1;
1029         if( defined $session_id ) {
1030             my $session_reference = $kernel->ID_id_to_session($session_id);
1031             if( defined $session_reference ) {
1032                 $heap = $session_reference->get_heap();
1033             }
1034         }
1036         if(exists $heap->{'client'}) {
1037             $heap->{'client'}->put($client_answer);
1038         }
1039     }
1040     $kernel->sig(CHLD => "child_reap");
1043 sub handle_task_debug {
1044     my $result = $_[ARG0];
1045     print STDERR "$result\n";
1048 sub handle_task_done {
1049     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
1050     delete $heap->{task}->{$task_id};
1051     $kernel->yield("next_task");
1054 sub process_task {
1055     no strict "refs";
1056     my ($session, $heap, $input) = @_;
1057     my $session_id = $session->ID;
1058     my ($msg, $msg_hash, $module);
1059     my $error = 0;
1060     my $answer_l;
1061     my ($answer_header, @answer_target_l, $answer_source);
1062     my $client_answer = "";
1064     daemon_log("", 5); 
1065     daemon_log("$session_id INFO: Incoming msg with session ID $session_id from '".$heap->{'remote_ip'}."'", 5);
1066     #daemon_log("$session_id DEBUG: Incoming msg:\n$input", 9);
1068     ####################
1069     # check incoming msg
1070     # msg is from a new client or gosa
1071     ($msg, $msg_hash, $module) = &input_from_unknown_host($input, $session_id);
1072     # msg is from a gosa-si-server or gosa-si-bus
1073     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1074         ($msg, $msg_hash, $module) = &input_from_known_server($input, $heap->{'remote_ip'}, $session_id);
1075     }
1076     # msg is from a gosa-si-client
1077     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1078         ($msg, $msg_hash, $module) = &input_from_known_client($input, $heap->{'remote_ip'}, $session_id);
1079     }
1080     # an error occurred
1081     if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
1082         # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
1083         # could not understand a msg from its server the client cause a re-registering process
1084         daemon_log("$session_id INFO cannot understand incoming msg, send 'ping'-msg to all host with ip '".$heap->{remote_ip}."' to cause a re-registering of the client if necessary", 5);
1085         my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
1086         my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1087         while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1088             my $host_name = $hit->{'hostname'};
1089             my $host_key = $hit->{'hostkey'};
1090             my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
1091             my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
1092             &update_jobdb_status_for_send_msgs($ping_msg, $error);
1093         }
1094         $error++;
1095     }
1097     ######################
1098     # process incoming msg
1099     if( $error == 0) {
1100         daemon_log("$session_id INFO: Incoming msg with header '".@{$msg_hash->{'header'}}[0].
1101                                 "' from '".$heap->{'remote_ip'}."'", 5); 
1102         daemon_log("$session_id DEBUG: Processing module ".$module, 7);
1103         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
1105         if ( 0 < @{$answer_l} ) {
1106             my $answer_str = join("\n", @{$answer_l});
1107             daemon_log("$session_id DEBUG: $module: Got answer from module: \n".$answer_str,8);
1108         } else {
1109             daemon_log("$session_id DEBUG: $module: Got no answer from module!" ,8);
1110         }
1112     }
1113     if( !$answer_l ) { $error++ };
1115     ########
1116     # answer
1117     if( $error == 0 ) {
1119         foreach my $answer ( @{$answer_l} ) {
1120             # for each answer in answer list
1121             
1122             # check outgoing msg to xml validity
1123             my $answer_hash = &check_outgoing_xml_validity($answer);
1124             if( not defined $answer_hash ) {
1125                 next;
1126             }
1127             
1128             $answer_header = @{$answer_hash->{'header'}}[0];
1129             @answer_target_l = @{$answer_hash->{'target'}};
1130             $answer_source = @{$answer_hash->{'source'}}[0];
1132             # deliver msg to all targets 
1133             foreach my $answer_target ( @answer_target_l ) {
1135                 # targets of msg are all gosa-si-clients in known_clients_db
1136                 if( $answer_target eq "*" ) {
1137                     # answer is for all clients
1138                     my $sql_statement= "SELECT * FROM known_clients";
1139                     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
1140                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1141                         my $host_name = $hit->{hostname};
1142                         my $host_key = $hit->{hostkey};
1143                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1144                         &update_jobdb_status_for_send_msgs($answer, $error);
1145                     }
1146                 }
1148                 # targets of msg are all gosa-si-server in known_server_db
1149                 elsif( $answer_target eq "KNOWN_SERVER" ) {
1150                     # answer is for all server in known_server
1151                     my $sql_statement= "SELECT * FROM known_server";
1152                     my $query_res = $known_server_db->select_dbentry( $sql_statement ); 
1153                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1154                         my $host_name = $hit->{hostname};
1155                         my $host_key = $hit->{hostkey};
1156                         $answer =~ s/KNOWN_SERVER/$host_name/g;
1157                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1158                         &update_jobdb_status_for_send_msgs($answer, $error);
1159                     }
1160                 }
1162                 # target of msg is GOsa
1163                                 elsif( $answer_target eq "GOSA" ) {
1164                                         my $session_id = ($1) if $answer =~ /<session_id>(\d+?)<\/session_id>/;
1165                                         my $add_on = "";
1166                     if( defined $session_id ) {
1167                         $add_on = ".session_id=$session_id";
1168                     }
1169                     # answer is for GOSA and has to returned to connected client
1170                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
1171                     $client_answer = $gosa_answer.$add_on;
1172                 }
1174                 # target of msg is job queue at this host
1175                 elsif( $answer_target eq "JOBDB") {
1176                     $answer =~ /<header>(\S+)<\/header>/;   
1177                     my $header;
1178                     if( defined $1 ) { $header = $1; }
1179                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
1180                     &update_jobdb_status_for_send_msgs($answer, $error);
1181                 }
1183                 # target of msg is a mac address
1184                 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 ) {
1185                     daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
1186                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
1187                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
1188                     my $found_ip_flag = 0;
1189                     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1190                         my $host_name = $hit->{hostname};
1191                         my $host_key = $hit->{hostkey};
1192                         $answer =~ s/$answer_target/$host_name/g;
1193                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
1194                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
1195                         &update_jobdb_status_for_send_msgs($answer, $error);
1196                         $found_ip_flag++ ;
1197                     }   
1198                     if( $found_ip_flag == 0) {
1199                         daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
1200                         if( $bus_activ eq "true" ) { 
1201                             daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1202                             my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1203                             my $query_res = $known_server_db->select_dbentry( $sql_statement );
1204                             while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1205                                 my $bus_address = $hit->{hostname};
1206                                 my $bus_key = $hit->{hostkey};
1207                                 my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header, $session_id);
1208                                 &update_jobdb_status_for_send_msgs($answer, $error);
1209                                 last;
1210                             }
1211                         }
1213                     }
1215                 #  answer is for one specific host   
1216                 } else {
1217                     # get encrypt_key
1218                     my $encrypt_key = &get_encrypt_key($answer_target);
1219                     if( not defined $encrypt_key ) {
1220                         # unknown target, forward msg to bus
1221                         daemon_log("$session_id WARNING: unknown target '$answer_target'", 3);
1222                         if( $bus_activ eq "true" ) { 
1223                             daemon_log("$session_id INFO: try to forward msg '$answer_header' to bus '$bus_address'", 5);
1224                             my $sql_statement = "SELECT * FROM known_server WHERE hostname='$bus_address'";
1225                             my $query_res = $known_server_db->select_dbentry( $sql_statement );
1226                             my $res_length = keys( %{$query_res} );
1227                             if( $res_length == 0 ){
1228                                 daemon_log("$session_id WARNING: send '$answer_header' to '$bus_address' failed, ".
1229                                         "no bus found in known_server", 3);
1230                             }
1231                             else {
1232                                 while( my ($hit_num, $hit) = each %{ $query_res } ) {    
1233                                     my $bus_key = $hit->{hostkey};
1234                                     my $error = &send_msg_to_target($answer, $bus_address, $bus_key, $answer_header,$session_id );
1235                                     &update_jobdb_status_for_send_msgs($answer, $error);
1236                                 }
1237                             }
1238                         }
1239                         next;
1240                     }
1241                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
1242                     &update_jobdb_status_for_send_msgs($answer, $error);
1243                 }
1244             }
1245         }
1246     }
1248     my $filter = POE::Filter::Reference->new();
1249     my %result = ( 
1250             status => "seems ok to me",
1251             answer => $client_answer,
1252             );
1254     my $output = $filter->put( [ \%result ] );
1255     print @$output;
1261 sub trigger_db_loop {
1262         my ($kernel) = @_ ;
1263         $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1264         $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
1265         $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
1266     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
1267         $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
1271 sub watch_for_done_jobs {
1272     my ($kernel,$heap) = @_[KERNEL, HEAP];
1274     my $sql_statement = "SELECT * FROM ".$job_queue_tn.
1275         " WHERE status='done'";
1276         my $res = $job_db->select_dbentry( $sql_statement );
1278     while( my ($id, $hit) = each %{$res} ) {
1279         my $jobdb_id = $hit->{id};
1280         my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
1281         my $res = $job_db->del_dbentry($sql_statement); 
1282     }
1284     $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
1288 sub watch_for_new_jobs {
1289         if($watch_for_new_jobs_in_progress == 0) {
1290                 $watch_for_new_jobs_in_progress = 1;
1291                 my ($kernel,$heap) = @_[KERNEL, HEAP];
1293                 # check gosa job queue for jobs with executable timestamp
1294                 my $timestamp = &get_time();
1295                 my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
1296                 my $res = $job_db->exec_statement( $sql_statement );
1298                 # Merge all new jobs that would do the same actions
1299                 my @drops;
1300                 my $hits;
1301                 foreach my $hit (reverse @{$res} ) {
1302                         my $macaddress= lc @{$hit}[8];
1303                         my $headertag= @{$hit}[5];
1304                         if(
1305                                 defined($hits->{$macaddress}) &&
1306                                 defined($hits->{$macaddress}->{$headertag}) &&
1307                                 defined($hits->{$macaddress}->{$headertag}[0])
1308                         ) {
1309                                 push @drops, "DELETE FROM $job_queue_tn WHERE id = $hits->{$macaddress}->{$headertag}[0]";
1310                         }
1311                         $hits->{$macaddress}->{$headertag}= $hit;
1312                 }
1314                 # Delete new jobs with a matching job in state 'processing'
1315                 foreach my $macaddress (keys %{$hits}) {
1316                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1317                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1318                                 if(defined($jobdb_id)) {
1319                                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND headertag='$jobdb_headertag' AND status='processing'";
1320                                         my $res = $job_db->exec_statement( $sql_statement );
1321                                         foreach my $hit (@{$res}) {
1322                                                 push @drops, "DELETE FROM $job_queue_tn WHERE id=$jobdb_id";
1323                                         }
1324                                 } else {
1325                                         daemon_log("J ERROR: Job without id exists for macaddress $macaddress!", 1);
1326                                 }
1327                         }
1328                 }
1330                 # Commit deletion
1331                 $job_db->exec_statementlist(\@drops);
1333                 # Look for new jobs that could be executed
1334                 foreach my $macaddress (keys %{$hits}) {
1336                         # Look if there is an executing job
1337                         my $sql_statement = "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='processing'";
1338                         my $res = $job_db->exec_statement( $sql_statement );
1340                         # Skip new jobs for host if there is a processing job
1341                         if(defined($res) and defined @{$res}[0]) {
1342                                 next;
1343                         }
1345                         foreach my $jobdb_headertag (keys %{$hits->{$macaddress}}) {
1346                                 my $jobdb_id = @{$hits->{$macaddress}->{$jobdb_headertag}}[0];
1347                                 if(defined($jobdb_id)) {
1348                                         my $job_msg = @{$hits->{$macaddress}->{$jobdb_headertag}}[7];
1350                                         daemon_log("J DEBUG: its time to execute $job_msg", 7);
1351                                         my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$macaddress'";
1352                                         my $res_hash = $known_clients_db->select_dbentry( $sql_statement );
1354                                         # expect macaddress is unique!!!!!!
1355                                         my $target = $res_hash->{1}->{hostname};
1357                                         # change header
1358                                         $job_msg =~ s/<header>job_/<header>gosa_/;
1360                                         # add sqlite_id
1361                                         $job_msg =~ s/<\/xml>$/<jobdb_id>$jobdb_id<\/jobdb_id><\/xml>/;
1363                                         $job_msg =~ /<header>(\S+)<\/header>/;
1364                                         my $header = $1 ;
1365                                         my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
1367                                         # update status in job queue to 'processing'
1368                                         $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
1369                                         my $res = $job_db->update_dbentry($sql_statement);
1370 # TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen                                        
1372                                         # We don't want parallel processing
1373                                         last;
1374                                 }
1375                         }
1376                 }
1378                 $watch_for_new_jobs_in_progress = 0;
1379                 $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
1380         }
1384 sub watch_for_new_messages {
1385     my ($kernel,$heap) = @_[KERNEL, HEAP];
1386     my @coll_user_msg;   # collection list of outgoing messages
1387     
1388     # check messaging_db for new incoming messages with executable timestamp
1389     my $timestamp = &get_time();
1390     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
1391     my $res = $messaging_db->exec_statement( $sql_statement );
1392         foreach my $hit (@{$res}) {
1394         # create outgoing messages
1395         my $message_to = @{$hit}[3];
1396         # translate message_to to plain login name
1397         my @message_to_l = split(/,/, $message_to);  
1398                 my %receiver_h; 
1399                 foreach my $receiver (@message_to_l) {
1400                         if ($receiver =~ /^u_([\s\S]*)$/) {
1401                                 $receiver_h{$1} = 0;
1402                         } elsif ($receiver =~ /^g_([\s\S]*)$/) {
1403 # TODO implement receiver translation
1404                         } else {
1405                                 my $sbjct = &encode_base64(@{$hit}[1]);
1406                                 my $msg = &encode_base64(@{$hit}[7]);
1407                                 &daemon_log("M WARNING: unknown receiver '$receiver' for a user-message 'sbjct - msg'", 3); 
1408                         }
1409                 }
1410                 my @receiver_l = keys(%receiver_h);
1412         my $message_id = @{$hit}[0];
1414         #add each outgoing msg to messaging_db
1415         my $receiver;
1416         foreach $receiver (@receiver_l) {
1417             my $sql_statement = "INSERT INTO $messaging_tn (id, subject, message_from, message_to, flag, direction, delivery_time, message, timestamp) ".
1418                 "VALUES ('".
1419                 $message_id."', '".    # id
1420                 @{$hit}[1]."', '".     # subject
1421                 @{$hit}[2]."', '".     # message_from
1422                 $receiver."', '".      # message_to
1423                 "none"."', '".         # flag
1424                 "out"."', '".          # direction
1425                 @{$hit}[6]."', '".     # delivery_time
1426                 @{$hit}[7]."', '".     # message
1427                 $timestamp."'".     # timestamp
1428                 ")";
1429             &daemon_log("M DEBUG: $sql_statement", 1);
1430             my $res = $messaging_db->exec_statement($sql_statement);
1431             &daemon_log("M INFO: message '".@{$hit}[0]."' is prepared for delivery to receiver '$receiver'", 5);
1432         }
1434         # set incoming message to flag d=deliverd
1435         $sql_statement = "UPDATE $messaging_tn SET flag='p' WHERE id='$message_id'"; 
1436         &daemon_log("M DEBUG: $sql_statement", 7);
1437         $res = $messaging_db->update_dbentry($sql_statement);
1438         &daemon_log("M INFO: message '$message_id' is set to flag 'p' (processed)", 5);
1439     }
1441     $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay); 
1442     return;
1445 sub watch_for_delivery_messages {
1446     my ($kernel, $heap) = @_[KERNEL, HEAP];
1448     # select outgoing messages
1449     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
1450     #&daemon_log("0 DEBUG: $sql", 7);
1451     my $res = $messaging_db->exec_statement( $sql_statement );
1452     
1453     # build out msg for each    usr
1454     foreach my $hit (@{$res}) {
1455         my $receiver = @{$hit}[3];
1456         my $msg_id = @{$hit}[0];
1457         my $subject = @{$hit}[1];
1458         my $message = @{$hit}[7];
1460         # resolve usr -> host where usr is logged in
1461         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
1462         #&daemon_log("0 DEBUG: $sql", 7);
1463         my $res = $login_users_db->exec_statement($sql);
1465         # reciver is logged in nowhere
1466         if (not ref(@$res[0]) eq "ARRAY") { next; }    
1468                 my $send_succeed = 0;
1469                 foreach my $hit (@$res) {
1470                                 my $receiver_host = @$hit[0];
1471                                 &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
1473                                 # fetch key to encrypt msg propperly for usr/host
1474                                 my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
1475                                 &daemon_log("0 DEBUG: $sql", 7);
1476                                 my $res = $known_clients_db->exec_statement($sql);
1478                                 # host is already down
1479                                 if (not ref(@$res[0]) eq "ARRAY") { next; }
1481                                 # host is on
1482                                 my $receiver_key = @{@{$res}[0]}[2];
1483                                 my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
1484                                 my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
1485                                 my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
1486                                 if ($error == 0 ) {
1487                                         $send_succeed++ ;
1488                                 }
1489                 }
1491                 if ($send_succeed) {
1492                                 # set outgoing msg at db to deliverd
1493                                 my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
1494                                 &daemon_log("0 DEBUG: $sql", 7);
1495                                 my $res = $messaging_db->exec_statement($sql); 
1496                 }
1497         }
1499     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
1500     return;
1504 sub watch_for_done_messages {
1505     my ($kernel,$heap) = @_[KERNEL, HEAP];
1507     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
1508     #&daemon_log("0 DEBUG: $sql", 7);
1509     my $res = $messaging_db->exec_statement($sql); 
1511     foreach my $hit (@{$res}) {
1512         my $msg_id = @{$hit}[0];
1514         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
1515         #&daemon_log("0 DEBUG: $sql", 7); 
1516         my $res = $messaging_db->exec_statement($sql);
1518         # not all usr msgs have been seen till now
1519         if ( ref(@$res[0]) eq "ARRAY") { next; }
1520         
1521         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
1522         #&daemon_log("0 DEBUG: $sql", 7);
1523         $res = $messaging_db->exec_statement($sql);
1524     
1525     }
1527     $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay); 
1528     return;
1532 sub get_ldap_handle {
1533         my ($session_id) = @_;
1534         my $heap;
1535         my $ldap_handle;
1537         if (not defined $session_id ) { $session_id = 0 };
1538         if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
1540         if ($session_id == 0) {
1541                 daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
1542                 $ldap_handle = Net::LDAP->new( $ldap_uri );
1543                 $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password); 
1545         } else {
1546                 my $session_reference = $global_kernel->ID_id_to_session($session_id);
1547                 if( defined $session_reference ) {
1548                         $heap = $session_reference->get_heap();
1549                 }
1551                 if (not defined $heap) {
1552                         daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
1553                         return;
1554                 }
1556                 # TODO: This "if" is nonsense, because it doesn't prove that the
1557                 #       used handle is still valid - or if we've to reconnect...
1558                 #if (not exists $heap->{ldap_handle}) {
1559                         $ldap_handle = Net::LDAP->new( $ldap_uri );
1560                         $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password); 
1561                         $heap->{ldap_handle} = $ldap_handle;
1562                 #}
1563         }
1564         return $ldap_handle;
1568 sub change_fai_state {
1569     my ($st, $targets, $session_id) = @_;
1570     $session_id = 0 if not defined $session_id;
1571     # Set FAI state to localboot
1572     my %mapActions= (
1573         reboot    => '',
1574         update    => 'softupdate',
1575         localboot => 'localboot',
1576         reinstall => 'install',
1577         rescan    => '',
1578         wake      => '',
1579         memcheck  => 'memcheck',
1580         sysinfo   => 'sysinfo',
1581         install   => 'install',
1582     );
1584     # Return if this is unknown
1585     if (!exists $mapActions{ $st }){
1586         daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
1587       return;
1588     }
1590     my $state= $mapActions{ $st };
1592     my $ldap_handle = &get_ldap_handle($session_id);
1593     if( defined($ldap_handle) ) {
1595       # Build search filter for hosts
1596         my $search= "(&(objectClass=GOhard)";
1597         foreach (@{$targets}){
1598             $search.= "(macAddress=$_)";
1599         }
1600         $search.= ")";
1602       # If there's any host inside of the search string, procress them
1603         if (!($search =~ /macAddress/)){
1604             daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
1605             return;
1606         }
1608       # Perform search for Unit Tag
1609       my $mesg = $ldap_handle->search(
1610           base   => $ldap_base,
1611           scope  => 'sub',
1612           attrs  => ['dn', 'FAIstate', 'objectClass'],
1613           filter => "$search"
1614           );
1616           if ($mesg->count) {
1617                   my @entries = $mesg->entries;
1618                   foreach my $entry (@entries) {
1619                           # Only modify entry if it is not set to '$state'
1620                           if ($entry->get_value("FAIstate") ne "$state"){
1621                                   daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
1622                                   my $result;
1623                                   my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
1624                                   if (exists $tmp{'FAIobject'}){
1625                                           if ($state eq ''){
1626                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1627                                                           delete => [ FAIstate => [] ] ]);
1628                                           } else {
1629                                                   $result= $ldap_handle->modify($entry->dn, changes => [
1630                                                           replace => [ FAIstate => $state ] ]);
1631                                           }
1632                                   } elsif ($state ne ''){
1633                                           $result= $ldap_handle->modify($entry->dn, changes => [
1634                                                   add     => [ objectClass => 'FAIobject' ],
1635                                                   add     => [ FAIstate => $state ] ]);
1636                                   }
1638                                   # Errors?
1639                                   if ($result->code){
1640                                           daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1641                                   }
1642                           } else {
1643                                   daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
1644                           }  
1645                   }
1646           }
1647     # if no ldap handle defined
1648     } else {
1649         daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
1650     }
1655 sub change_goto_state {
1656     my ($st, $targets, $session_id) = @_;
1657     $session_id = 0  if not defined $session_id;
1659     # Switch on or off?
1660     my $state= $st eq 'active' ? 'active': 'locked';
1662     my $ldap_handle = &get_ldap_handle($session_id);
1663     if( defined($ldap_handle) ) {
1665       # Build search filter for hosts
1666       my $search= "(&(objectClass=GOhard)";
1667       foreach (@{$targets}){
1668         $search.= "(macAddress=$_)";
1669       }
1670       $search.= ")";
1672       # If there's any host inside of the search string, procress them
1673       if (!($search =~ /macAddress/)){
1674         return;
1675       }
1677       # Perform search for Unit Tag
1678       my $mesg = $ldap_handle->search(
1679           base   => $ldap_base,
1680           scope  => 'sub',
1681           attrs  => ['dn', 'gotoMode'],
1682           filter => "$search"
1683           );
1685       if ($mesg->count) {
1686         my @entries = $mesg->entries;
1687         foreach my $entry (@entries) {
1689           # Only modify entry if it is not set to '$state'
1690           if ($entry->get_value("gotoMode") ne $state){
1692             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
1693             my $result;
1694             $result= $ldap_handle->modify($entry->dn, changes => [
1695                                                 replace => [ gotoMode => $state ] ]);
1697             # Errors?
1698             if ($result->code){
1699               &daemon_log("$session_id Error: Setting gotoMode to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
1700             }
1702           }
1703         }
1704       }
1706     }
1710 sub run_create_fai_server_db {
1711     my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
1712     my $session_id = $session->ID;
1713     my $task = POE::Wheel::Run->new(
1714             Program => sub { &create_fai_server_db($table_name,$kernel, undef, $session_id) },
1715             StdoutEvent  => "session_run_result",
1716             StderrEvent  => "session_run_debug",
1717             CloseEvent   => "session_run_done",
1718             );
1720     $heap->{task}->{ $task->ID } = $task;
1721     return;
1725 sub create_fai_server_db {
1726     my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
1727         my $result;
1729         if (not defined $session_id) { $session_id = 0; }
1730     my $ldap_handle = &get_ldap_handle();
1731         if(defined($ldap_handle)) {
1732                 daemon_log("$session_id INFO: create_fai_server_db: start", 5);
1733                 my $mesg= $ldap_handle->search(
1734                         base   => $ldap_base,
1735                         scope  => 'sub',
1736                         attrs  => ['FAIrepository', 'gosaUnitTag'],
1737                         filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
1738                 );
1739                 if($mesg->{'resultCode'} == 0 &&
1740                    $mesg->count != 0) {
1741                    foreach my $entry (@{$mesg->{entries}}) {
1742                            if($entry->exists('FAIrepository')) {
1743                                    # Add an entry for each Repository configured for server
1744                                    foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
1745                                                    my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
1746                                                    my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
1747                                                    $result= $fai_server_db->add_dbentry( { 
1748                                                                    table => $table_name,
1749                                                                    primkey => ['server', 'release', 'tag'],
1750                                                                    server => $tmp_url,
1751                                                                    release => $tmp_release,
1752                                                                    sections => $tmp_sections,
1753                                                                    tag => (length($tmp_tag)>0)?$tmp_tag:"",
1754                                                            } );
1755                                            }
1756                                    }
1757                            }
1758                    }
1759                 daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
1761                 # TODO: Find a way to post the 'create_packages_list_db' event
1762                 if(not defined($dont_create_packages_list)) {
1763                         &create_packages_list_db(undef, undef, $session_id);
1764                 }
1765         }       
1766     
1767     $ldap_handle->disconnect;
1768         return $result;
1772 sub run_create_fai_release_db {
1773     my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
1774         my $session_id = $session->ID;
1775     my $task = POE::Wheel::Run->new(
1776             Program => sub { &create_fai_release_db($table_name, $session_id) },
1777             StdoutEvent  => "session_run_result",
1778             StderrEvent  => "session_run_debug",
1779             CloseEvent   => "session_run_done",
1780             );
1782     $heap->{task}->{ $task->ID } = $task;
1783     return;
1787 sub create_fai_release_db {
1788         my ($table_name, $session_id) = @_;
1789         my $result;
1791     # used for logging
1792     if (not defined $session_id) { $session_id = 0; }
1794     my $ldap_handle = &get_ldap_handle();
1795         if(defined($ldap_handle)) {
1796                 daemon_log("$session_id INFO: create_fai_release_db: start",5);
1797                 my $mesg= $ldap_handle->search(
1798                         base   => $ldap_base,
1799                         scope  => 'sub',
1800                         attrs  => [],
1801                         filter => "(&(objectClass=organizationalUnit)(ou=fai))",
1802                 );
1803                 if($mesg->{'resultCode'} == 0 &&
1804                         $mesg->count != 0) {
1805                         # Walk through all possible FAI container ou's
1806                         my @sql_list;
1807                         my $timestamp= &get_time();
1808                         foreach my $ou (@{$mesg->{entries}}) {
1809                                 my $tmp_classes= resolve_fai_classes($ou->dn, $ldap_handle, $session_id);
1810                                 if(defined($tmp_classes) && ref($tmp_classes) eq 'HASH') {
1811                                         my @tmp_array=get_fai_release_entries($tmp_classes);
1812                                         if(@tmp_array) {
1813                                                 foreach my $entry (@tmp_array) {
1814                                                         if(defined($entry) && ref($entry) eq 'HASH') {
1815                                                                 my $sql= 
1816                                                                 "INSERT INTO $table_name "
1817                                                                 ."(timestamp, release, class, type, state) VALUES ("
1818                                                                 .$timestamp.","
1819                                                                 ."'".$entry->{'release'}."',"
1820                                                                 ."'".$entry->{'class'}."',"
1821                                                                 ."'".$entry->{'type'}."',"
1822                                                                 ."'".$entry->{'state'}."')";
1823                                                                 push @sql_list, $sql;
1824                                                         }
1825                                                 }
1826                                         }
1827                                 }
1828                         }
1830                         daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
1831                         if(@sql_list) {
1832                                 unshift @sql_list, "VACUUM";
1833                                 unshift @sql_list, "DELETE FROM $table_name";
1834                                 $fai_release_db->exec_statementlist(\@sql_list);
1835                         }
1836                         daemon_log("$session_id DEBUG: Done with inserting",7);
1837                 }
1838                 daemon_log("$session_id INFO: create_fai_release_db: finished",5);
1839         }
1840     $ldap_handle->disconnect;
1841         return $result;
1844 sub get_fai_types {
1845         my $tmp_classes = shift || return undef;
1846         my @result;
1848         foreach my $type(keys %{$tmp_classes}) {
1849                 if(defined($tmp_classes->{$type}[0]) && (!($tmp_classes->{$type}[0] =~ /^.*?removed.*?$/))) {
1850                         my $entry = {
1851                                 type => $type,
1852                                 state => $tmp_classes->{$type}[0],
1853                         };
1854                         push @result, $entry;
1855                 }
1856         }
1858         return @result;
1861 sub get_fai_state {
1862         my $result = "";
1863         my $tmp_classes = shift || return $result;
1865         foreach my $type(keys %{$tmp_classes}) {
1866                 if(defined($tmp_classes->{$type}[0])) {
1867                         $result = $tmp_classes->{$type}[0];
1868                         
1869                 # State is equal for all types in class
1870                         last;
1871                 }
1872         }
1874         return $result;
1877 sub resolve_fai_classes {
1878         my ($fai_base, $ldap_handle, $session_id) = @_;
1879         if (not defined $session_id) { $session_id = 0; }
1880         my $result;
1881         my @possible_fai_classes= ("FAIscript", "FAIhook", "FAIpartitionTable", "FAItemplate", "FAIvariable", "FAIprofile", "FAIpackageList");
1882         my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
1883         my $fai_classes;
1885         daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
1886         my $mesg= $ldap_handle->search(
1887                 base   => $fai_base,
1888                 scope  => 'sub',
1889                 attrs  => ['cn','objectClass','FAIstate'],
1890                 filter => $fai_filter,
1891         );
1892         daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
1894         if($mesg->{'resultCode'} == 0 &&
1895                 $mesg->count != 0) {
1896                 foreach my $entry (@{$mesg->{entries}}) {
1897                         if($entry->exists('cn')) {
1898                                 my $tmp_dn= $entry->dn();
1900                                 # Skip classname and ou dn parts for class
1901                                 my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
1903                                 # Skip classes without releases
1904                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
1905                                         next;
1906                                 }
1908                                 my $tmp_cn= $entry->get_value('cn');
1909                                 my $tmp_state= $entry->get_value('FAIstate');
1911                                 my $tmp_type;
1912                                 # Get FAI type
1913                                 for my $oclass(@{$entry->get_value('objectClass', asref => 1)}) {
1914                                         if(grep $_ eq $oclass, @possible_fai_classes) {
1915                                                 $tmp_type= $oclass;
1916                                                 last;
1917                                         }
1918                                 }
1920                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1921                                         # A Subrelease
1922                                         my @sub_releases = split(/,/, $tmp_release);
1924                                         # Walk through subreleases and build hash tree
1925                                         my $hash;
1926                                         while(my $tmp_sub_release = pop @sub_releases) {
1927                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
1928                                         }
1929                                         eval('push @{$fai_classes->'.$hash.'{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";');
1930                                 } else {
1931                                         # A branch, no subrelease
1932                                         push @{$fai_classes->{$tmp_release}->{$tmp_cn}->{$tmp_type}}, (defined($tmp_state) && length($tmp_state)>0)?$tmp_state:"";
1933                                 }
1934                         } elsif (!$entry->exists('cn')) {
1935                                 my $tmp_dn= $entry->dn();
1936                                 my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
1938                                 # Skip classes without releases
1939                                 if((!defined($tmp_release)) || length($tmp_release)==0) {
1940                                         next;
1941                                 }
1943                                 if($tmp_release =~ /^.*?,.*?$/ && (!($tmp_release =~ /^.*?\\,.*?$/))) {
1944                                         # A Subrelease
1945                                         my @sub_releases= split(/,/, $tmp_release);
1947                                         # Walk through subreleases and build hash tree
1948                                         my $hash;
1949                                         while(my $tmp_sub_release = pop @sub_releases) {
1950                                                 $hash .= "\{'$tmp_sub_release'\}->";                                            
1951                                         }
1952                                         # Remove the last two characters
1953                                         chop($hash);
1954                                         chop($hash);
1956                                         eval('$fai_classes->'.$hash.'= {}');
1957                                 } else {
1958                                         # A branch, no subrelease
1959                                         if(!exists($fai_classes->{$tmp_release})) {
1960                                                 $fai_classes->{$tmp_release} = {};
1961                                         }
1962                                 }
1963                         }
1964                 }
1966                 # The hash is complete, now we can honor the copy-on-write based missing entries
1967                 foreach my $release (keys %$fai_classes) {
1968                         $result->{$release}= deep_copy(apply_fai_inheritance($fai_classes->{$release}));
1969                 }
1970         }
1971         return $result;
1974 sub apply_fai_inheritance {
1975        my $fai_classes = shift || return {};
1976        my $tmp_classes;
1978        # Get the classes from the branch
1979        foreach my $class (keys %{$fai_classes}) {
1980                # Skip subreleases
1981                if($class =~ /^ou=.*$/) {
1982                        next;
1983                } else {
1984                        $tmp_classes->{$class}= deep_copy($fai_classes->{$class});
1985                }
1986        }
1988        # Apply to each subrelease
1989        foreach my $subrelease (keys %{$fai_classes}) {
1990                if($subrelease =~ /ou=/) {
1991                        foreach my $tmp_class (keys %{$tmp_classes}) {
1992                                if(!exists($fai_classes->{$subrelease}->{$tmp_class})) {
1993                                        $fai_classes->{$subrelease}->{$tmp_class} =
1994                                        deep_copy($tmp_classes->{$tmp_class});
1995                                } else {
1996                                        foreach my $type (keys %{$tmp_classes->{$tmp_class}}) {
1997                                                if(!exists($fai_classes->{$subrelease}->{$tmp_class}->{$type})) {
1998                                                        $fai_classes->{$subrelease}->{$tmp_class}->{$type}=
1999                                                        deep_copy($tmp_classes->{$tmp_class}->{$type});
2000                                                }
2001                                        }
2002                                }
2003                        }
2004                }
2005        }
2007        # Find subreleases in deeper levels
2008        foreach my $subrelease (keys %{$fai_classes}) {
2009                if($subrelease =~ /ou=/) {
2010                        foreach my $subsubrelease (keys %{$fai_classes->{$subrelease}}) {
2011                                if($subsubrelease =~ /ou=/) {
2012                                        apply_fai_inheritance($fai_classes->{$subrelease});
2013                                }
2014                        }
2015                }
2016        }
2018        return $fai_classes;
2021 sub get_fai_release_entries {
2022         my $tmp_classes = shift || return;
2023         my $parent = shift || "";
2024         my @result = shift || ();
2026         foreach my $entry (keys %{$tmp_classes}) {
2027                 if(defined($entry)) {
2028                         if($entry =~ /^ou=.*$/) {
2029                                 my $release_name = $entry;
2030                                 $release_name =~ s/ou=//g;
2031                                 if(length($parent)>0) {
2032                                         $release_name = $parent."/".$release_name;
2033                                 }
2034                                 my @bufentries = get_fai_release_entries($tmp_classes->{$entry}, $release_name, @result);
2035                                 foreach my $bufentry(@bufentries) {
2036                                         push @result, $bufentry;
2037                                 }
2038                         } else {
2039                                 my @types = get_fai_types($tmp_classes->{$entry});
2040                                 foreach my $type (@types) {
2041                                         push @result, 
2042                                         {
2043                                                 'class' => $entry,
2044                                                 'type' => $type->{'type'},
2045                                                 'release' => $parent,
2046                                                 'state' => $type->{'state'},
2047                                         };
2048                                 }
2049                         }
2050                 }
2051         }
2053         return @result;
2056 sub deep_copy {
2057         my $this = shift;
2058         if (not ref $this) {
2059                 $this;
2060         } elsif (ref $this eq "ARRAY") {
2061                 [map deep_copy($_), @$this];
2062         } elsif (ref $this eq "HASH") {
2063                 +{map { $_ => deep_copy($this->{$_}) } keys %$this};
2064         } else { die "what type is $_?" }
2068 sub session_run_result {
2069     my ($kernel, $heap, $client_answer) = @_[KERNEL, HEAP, ARG0];    
2070     $kernel->sig(CHLD => "child_reap");
2073 sub session_run_debug {
2074     my $result = $_[ARG0];
2075     print STDERR "$result\n";
2078 sub session_run_done {
2079     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
2080     delete $heap->{task}->{$task_id};
2084 sub create_sources_list {
2085         my $session_id = shift;
2086         my $ldap_handle = &main::get_ldap_handle;
2087         my $result="/tmp/gosa_si_tmp_sources_list";
2089         # Remove old file
2090         if(stat($result)) {
2091                 unlink($result);
2092                 &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
2093         }
2095         my $fh;
2096         open($fh, ">$result");
2097         if (not defined $fh) {
2098                 &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
2099                 return undef;
2100         }
2101         if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
2102                 my $mesg=$ldap_handle->search(
2103                         base    => $main::ldap_server_dn,
2104                         scope   => 'base',
2105                         attrs   => 'FAIrepository',
2106                         filter  => 'objectClass=FAIrepositoryServer'
2107                 );
2108                 if($mesg->count) {
2109                         foreach my $entry(@{$mesg->{'entries'}}) {
2110                                 foreach my $value(@{$entry->get_value('FAIrepository', asref => 1)}) {
2111                                         my ($server, $tag, $release, $sections)= split /\|/, $value;
2112                                         my $line = "deb $server $release";
2113                                         $sections =~ s/,/ /g;
2114                                         $line.= " $sections";
2115                                         print $fh $line."\n";
2116                                 }
2117                         }
2118                 }
2119         } else {
2120                 if (defined $main::ldap_server_dn){
2121                         &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
2122                 } else {
2123                         &main::daemon_log("$session_id ERROR: no ldap_server_dn found, abort create_sources_list", 1);
2124                 }
2125         }
2126         close($fh);
2128         return $result;
2132 sub run_create_packages_list_db {
2133     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
2134         my $session_id = $session->ID;
2136         my $task = POE::Wheel::Run->new(
2137                                         Priority => +20,
2138                                         Program => sub {&create_packages_list_db(undef, undef, $session_id)},
2139                                         StdoutEvent  => "session_run_result",
2140                                         StderrEvent  => "session_run_debug",
2141                                         CloseEvent   => "session_run_done",
2142                                         );
2143         $heap->{task}->{ $task->ID } = $task;
2147 sub create_packages_list_db {
2148         my ($ldap_handle, $sources_file, $session_id) = @_;
2149         
2150         # it should not be possible to trigger a recreation of packages_list_db
2151         # while packages_list_db is under construction, so set flag packages_list_under_construction
2152         # which is tested befor recreation can be started
2153         if (-r $packages_list_under_construction) {
2154                 daemon_log("$session_id WARNING: packages_list_db is right now under construction, please wait until this process is finished", 3);
2155                 return;
2156         } else {
2157                 daemon_log("$session_id INFO: create_packages_list_db: start", 5); 
2158                 # set packages_list_under_construction to true
2159                 system("touch $packages_list_under_construction");
2160                 @packages_list_statements=();
2161         }
2163         if (not defined $session_id) { $session_id = 0; }
2164         if (not defined $ldap_handle) { 
2165                 $ldap_handle= &get_ldap_handle();
2167                 if (not defined $ldap_handle) {
2168                         daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
2169                         unlink($packages_list_under_construction);
2170                         return;
2171                 }
2172         }
2173         if (not defined $sources_file) { 
2174                 &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
2175                 $sources_file = &create_sources_list($session_id);
2176         }
2178         if (not defined $sources_file) {
2179                 &main::daemon_log("$session_id ERROR: no sources_file given under '$sources_file', skip create_packages_list_db", 1); 
2180                 unlink($packages_list_under_construction);
2181                 return;
2182         }
2184         my $line;
2186         open(CONFIG, "<$sources_file") or do {
2187                 daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
2188                 unlink($packages_list_under_construction);
2189                 return;
2190         };
2192         # Read lines
2193         while ($line = <CONFIG>){
2194                 # Unify
2195                 chop($line);
2196                 $line =~ s/^\s+//;
2197                 $line =~ s/^\s+/ /;
2199                 # Strip comments
2200                 $line =~ s/#.*$//g;
2202                 # Skip empty lines
2203                 if ($line =~ /^\s*$/){
2204                         next;
2205                 }
2207                 # Interpret deb line
2208                 if ($line =~ /^deb [^\s]+\s[^\s]+\s[^\s]+/){
2209                         my( $baseurl, $dist, $sections ) = ($line =~ /^deb\s([^\s]+)\s+([^\s]+)\s+(.*)$/);
2210                         my $section;
2211                         foreach $section (split(' ', $sections)){
2212                                 &parse_package_info( $baseurl, $dist, $section, $session_id );
2213                         }
2214                 }
2215         }
2217         close (CONFIG);
2219         find(\&cleanup_and_extract, keys( %repo_dirs ));
2220         eval {
2221                 &main::strip_packages_list_statements();
2222         };
2223         if($@) {
2224                 daemon_log("$session_id ERROR: Preparation of statement list failed with '$@'!", 1);
2225         }
2226         unshift @packages_list_statements, "VACUUM";
2227         eval {
2228                 $packages_list_db->exec_statementlist(\@packages_list_statements);
2229         };
2230         if($@) {
2231                 daemon_log("$session_id ERROR: Updating package_list_db failed with '$@'!", 1);
2232         }
2233         unlink($packages_list_under_construction);
2234         daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
2235         return;
2239 # This function should do some intensive task to minimize the db-traffic
2240 sub strip_packages_list_statements {
2241     my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
2242         my @new_statement_list=();
2243         my $hash;
2244         my $insert_hash;
2245         my $update_hash;
2246         my $delete_hash;
2247         my $local_timestamp=get_time();
2249         foreach my $existing_entry (@existing_entries) {
2250                 $hash->{@{$existing_entry}[0]}->{@{$existing_entry}[1]}->{@{$existing_entry}[2]}= $existing_entry;
2251         }
2253         foreach my $statement (@packages_list_statements) {
2254                 if($statement =~ /^INSERT/i) {
2255                         # Assign the values from the insert statement
2256                         my ($distribution,$package,$version,$section,$description,$template,$timestamp) = ($1,$2,$3,$4,$5,$6,$7) if $statement =~ 
2257                         /^INSERT\s+?INTO\s+?$main::packages_list_tn\s+?VALUES\s*?\('(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)',\s*?'(.*?)'\s*?\)$/si;
2258                         if(exists($hash->{$distribution}->{$package}->{$version})) {
2259                                 # If section or description has changed, update the DB
2260                                 if( 
2261                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[3] eq $section)) or 
2262                                         (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
2263                                 ) {
2264                                         @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
2265                                 }
2266                         } else {
2267                                 # Insert a non-existing entry to db
2268                                 @{$insert_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2269                         }
2270                 } elsif ($statement =~ /^UPDATE/i) {
2271                         my ($template,$package,$version) = ($1,$2,$3) if $statement =~
2272                         /^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;
2273                         foreach my $distribution (keys %{$hash}) {
2274                                 if(exists($insert_hash->{$distribution}->{$package}->{$version})) {
2275                                         # update the insertion hash to execute only one query per package (insert instead insert+update)
2276                                         @{$insert_hash->{$distribution}->{$package}->{$version}}[5]= $template;
2277                                 } elsif(exists($hash->{$distribution}->{$package}->{$version})) {
2278                                         if( ! (@{$hash->{$distribution}->{$package}->{$version}}[5] eq $template)) {
2279                                                 my $section;
2280                                                 my $description;
2281                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) and
2282                                                         length(@{$update_hash->{$distribution}->{$package}->{$version}}[3]) > 0 ) {
2283                                                         $section= @{$update_hash->{$distribution}->{$package}->{$version}}[3];
2284                                                 }
2285                                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2286                                                         $description= @{$update_hash->{$distribution}->{$package}->{$version}}[4];
2287                                                 }
2288                                                 @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
2289                                         }
2290                                 }
2291                         }
2292                 }
2293         }
2295         # TODO: Check for orphaned entries
2297         # unroll the insert_hash
2298         foreach my $distribution (keys %{$insert_hash}) {
2299                 foreach my $package (keys %{$insert_hash->{$distribution}}) {
2300                         foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
2301                                 push @new_statement_list, "INSERT INTO $main::packages_list_tn VALUES ('$distribution','$package','$version',"
2302                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[3]',"
2303                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[4]',"
2304                                 ."'@{$insert_hash->{$distribution}->{$package}->{$version}}[5]',"
2305                                 ."'$local_timestamp')";
2306                         }
2307                 }
2308         }
2310         # unroll the update hash
2311         foreach my $distribution (keys %{$update_hash}) {
2312                 foreach my $package (keys %{$update_hash->{$distribution}}) {
2313                         foreach my $version (keys %{$update_hash->{$distribution}->{$package}}) {
2314                                 my $set = "";
2315                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[3])) {
2316                                         $set .= "section = '@{$update_hash->{$distribution}->{$package}->{$version}}[3]', ";
2317                                 }
2318                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[4])) {
2319                                         $set .= "description = '@{$update_hash->{$distribution}->{$package}->{$version}}[4]', ";
2320                                 }
2321                                 if(defined(@{$update_hash->{$distribution}->{$package}->{$version}}[5])) {
2322                                         $set .= "template = '@{$update_hash->{$distribution}->{$package}->{$version}}[5]', ";
2323                                 }
2324                                 if(defined($set) and length($set) > 0) {
2325                                         $set .= "timestamp = '$local_timestamp'";
2326                                 } else {
2327                                         next;
2328                                 }
2329                                 push @new_statement_list, 
2330                                         "UPDATE $main::packages_list_tn SET $set WHERE"
2331                                         ." distribution = '$distribution'"
2332                                         ." AND package = '$package'"
2333                                         ." AND version = '$version'";
2334                         }
2335                 }
2336         }
2338         @packages_list_statements = @new_statement_list;
2342 sub parse_package_info {
2343     my ($baseurl, $dist, $section, $session_id)= @_;
2344     my ($package);
2345     if (not defined $session_id) { $session_id = 0; }
2346     my ($path) = ($baseurl =~ m%://[^/]*(.*)$%);
2347     $repo_dirs{ "${repo_path}/pool" } = 1;
2349     foreach $package ("Packages.gz"){
2350         daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
2351         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
2352         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
2353     }
2354     
2358 sub get_package {
2359     my ($url, $dest, $session_id)= @_;
2360     if (not defined $session_id) { $session_id = 0; }
2362     my $tpath = dirname($dest);
2363     -d "$tpath" || mkpath "$tpath";
2365     # This is ugly, but I've no time to take a look at "how it works in perl"
2366     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
2367         system("gunzip -cd '$dest' > '$dest.in'");
2368         daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
2369         unlink($dest);
2370         daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
2371     } else {
2372         daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
2373     }
2374     return 0;
2378 sub parse_package {
2379     my ($path, $dist, $srv_path, $session_id)= @_;
2380     if (not defined $session_id) { $session_id = 0;}
2381     my ($package, $version, $section, $description);
2382     my $PACKAGES;
2383     my $timestamp = &get_time();
2385     if(not stat("$path.in")) {
2386         daemon_log("$session_id ERROR: create_packages_list: parse_package: file '$path.in' is not readable",1);
2387         return;
2388     }
2390     open($PACKAGES, "<$path.in");
2391     if(not defined($PACKAGES)) {
2392         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
2393         return;
2394     }
2396     # Read lines
2397     while (<$PACKAGES>){
2398         my $line = $_;
2399         # Unify
2400         chop($line);
2402         # Use empty lines as a trigger
2403         if ($line =~ /^\s*$/){
2404             my $sql = "INSERT INTO packages_list VALUES ('$dist', '$package', '$version', '$section', '$description', '', '$timestamp')";
2405             push(@packages_list_statements, $sql);
2406             $package = "none";
2407             $version = "none";
2408             $section = "none";
2409             $description = "none"; 
2410             next;
2411         }
2413         # Trigger for package name
2414         if ($line =~ /^Package:\s/){
2415             ($package)= ($line =~ /^Package: (.*)$/);
2416             next;
2417         }
2419         # Trigger for version
2420         if ($line =~ /^Version:\s/){
2421             ($version)= ($line =~ /^Version: (.*)$/);
2422             next;
2423         }
2425         # Trigger for description
2426         if ($line =~ /^Description:\s/){
2427             ($description)= &encode_base64(($line =~ /^Description: (.*)$/));
2428             next;
2429         }
2431         # Trigger for section
2432         if ($line =~ /^Section:\s/){
2433             ($section)= ($line =~ /^Section: (.*)$/);
2434             next;
2435         }
2437         # Trigger for filename
2438         if ($line =~ /^Filename:\s/){
2439             my ($filename) = ($line =~ /^Filename: (.*)$/);
2440             store_fileinfo( $package, $filename, $dist, $srv_path, $version, $repo_path );
2441             next;
2442         }
2443     }
2445     close( $PACKAGES );
2446     unlink( "$path.in" );
2447     &main::daemon_log("$session_id DEBUG: unlink '$path.in'", 1); 
2451 sub store_fileinfo {
2452     my( $package, $file, $dist, $path, $vers, $srvdir) = @_;
2454     my %fileinfo = (
2455         'package' => $package,
2456         'dist' => $dist,
2457         'version' => $vers,
2458     );
2460     $repo_files{ "${srvdir}/$file" } = \%fileinfo;
2464 sub cleanup_and_extract {
2465     my $fileinfo = $repo_files{ $File::Find::name };
2467     if( defined $fileinfo ) {
2469         my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
2470         my $sql;
2471         my $package = $fileinfo->{ 'package' };
2472         my $newver = $fileinfo->{ 'version' };
2474         mkpath($dir);
2475         system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
2477                 if( -f "$dir/DEBIAN/templates" ) {
2479                         daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
2481                         my $tmpl= "";
2482                         {
2483                                 local $/=undef;
2484                                 open FILE, "$dir/DEBIAN/templates";
2485                                 $tmpl = &encode_base64(<FILE>);
2486                                 close FILE;
2487                         }
2488                         rmtree("$dir/DEBIAN/templates");
2490                         $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
2491                 push @packages_list_statements, $sql;
2492                 }
2493     }
2495     return;
2499 #==== MAIN = main ==============================================================
2500 #  parse commandline options
2501 Getopt::Long::Configure( "bundling" );
2502 GetOptions("h|help" => \&usage,
2503         "c|config=s" => \$cfg_file,
2504         "f|foreground" => \$foreground,
2505         "v|verbose+" => \$verbose,
2506         "no-bus+" => \$no_bus,
2507         "no-arp+" => \$no_arp,
2508            );
2510 #  read and set config parameters
2511 &check_cmdline_param ;
2512 &read_configfile;
2513 &check_pid;
2515 $SIG{CHLD} = 'IGNORE';
2517 # forward error messages to logfile
2518 if( ! $foreground ) {
2519   open( STDIN,  '+>/dev/null' );
2520   open( STDOUT, '+>&STDIN'    );
2521   open( STDERR, '+>&STDIN'    );
2524 # Just fork, if we are not in foreground mode
2525 if( ! $foreground ) { 
2526     chdir '/'                 or die "Can't chdir to /: $!";
2527     $pid = fork;
2528     setsid                    or die "Can't start a new session: $!";
2529     umask 0;
2530 } else { 
2531     $pid = $$; 
2534 # Do something useful - put our PID into the pid_file
2535 if( 0 != $pid ) {
2536     open( LOCK_FILE, ">$pid_file" );
2537     print LOCK_FILE "$pid\n";
2538     close( LOCK_FILE );
2539     if( !$foreground ) { 
2540         exit( 0 ) 
2541     };
2544 # parse head url and revision from svn
2545 my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
2546 $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
2547 $server_headURL = defined $1 ? $1 : 'unknown' ;
2548 $server_revision = defined $2 ? $2 : 'unknown' ;
2549 if ($server_headURL =~ /\/tag\// || 
2550         $server_headURL =~ /\/branches\// ) {
2551     $server_status = "stable"; 
2552 } else {
2553     $server_status = "developmental" ;
2557 daemon_log(" ", 1);
2558 daemon_log("$0 started!", 1);
2559 daemon_log("status: $server_status", 1);
2560 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
2562 if ($no_bus > 0) {
2563     $bus_activ = "false"
2566 # connect to gosa-si job queue
2567 $job_db = GOSA::DBsqlite->new($job_queue_file_name);
2568 $job_db->create_table($job_queue_tn, \@job_queue_col_names);
2570 # connect to known_clients_db
2571 $known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
2572 $known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
2574 # connect to known_server_db
2575 $known_server_db = GOSA::DBsqlite->new($known_server_file_name);
2576 $known_server_db->create_table($known_server_tn, \@known_server_col_names);
2578 # connect to login_usr_db
2579 $login_users_db = GOSA::DBsqlite->new($login_users_file_name);
2580 $login_users_db->create_table($login_users_tn, \@login_users_col_names);
2582 # connect to fai_server_db and fai_release_db
2583 unlink($fai_server_file_name);
2584 $fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
2585 $fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
2587 unlink($fai_release_file_name);
2588 $fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
2589 $fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
2591 # connect to packages_list_db
2592 #unlink($packages_list_file_name);
2593 unlink($packages_list_under_construction);
2594 $packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
2595 $packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
2597 # connect to messaging_db
2598 $messaging_db = GOSA::DBsqlite->new($messaging_file_name);
2599 $messaging_db->create_table($messaging_tn, \@messaging_col_names);
2602 # create xml object used for en/decrypting
2603 $xml = new XML::Simple();
2605 # create socket for incoming xml messages
2607 POE::Component::Server::TCP->new(
2608         Port => $server_port,
2609         ClientInput => sub {
2610         my ($kernel, $input) = @_[KERNEL, ARG0];
2611         push(@tasks, $input);
2612         $kernel->yield("next_task");
2613         },
2614     InlineStates => {
2615         next_task => \&next_task,
2616         task_result => \&handle_task_result,
2617         task_done   => \&handle_task_done,
2618         task_debug  => \&handle_task_debug,
2619         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
2620     }
2621 );
2623 daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
2625 # create session for repeatedly checking the job queue for jobs
2626 POE::Session->create(
2627         inline_states => {
2628                 _start => \&_start,
2629                 sig_handler => \&sig_handler,
2630         watch_for_new_messages => \&watch_for_new_messages,
2631         watch_for_delivery_messages => \&watch_for_delivery_messages,
2632         watch_for_done_messages => \&watch_for_done_messages,
2633                 watch_for_new_jobs => \&watch_for_new_jobs,
2634         watch_for_done_jobs => \&watch_for_done_jobs,
2635         create_packages_list_db => \&run_create_packages_list_db,
2636         create_fai_server_db => \&run_create_fai_server_db,
2637         create_fai_release_db => \&run_create_fai_release_db,
2638         session_run_result => \&session_run_result,
2639         session_run_debug => \&session_run_debug,
2640         session_run_done => \&session_run_done,
2641         child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
2642         }
2643 );
2646 # import all modules
2647 &import_modules;
2649 # check wether all modules are gosa-si valid passwd check
2651 POE::Kernel->run();
2652 exit;