Code

Temporarily disable mailQueue plugins
[gosa.git] / gosa-si / gosa-si-server
index ada883ba08d8c225d9040d5cb39a4217c8586dd2..9d92dc9971e4d9fe7c5f66a4cce57c8c4eca45d4 100755 (executable)
 #!/usr/bin/perl
-#===============================================================================
-#
-#         FILE:  gosa-sd
+#*********************************************************************
 #
-#        USAGE:  ./gosa-sd
+# gosa-si-client -- client for the gosa-si-server
 #
-#  DESCRIPTION:
+# (c) 2007-2009 by Andreas Rettenberger <rettenberger@gonicus.de>
+# (c) 2008-2010 by Cajus Pollmeier <pollmeier@gonicus.de>
+# (c) 2008-2009 by Jan Wenzel <wenzel@gonicus.de>
+# (c) 2010 by Benoit Mortier <benoit.mortier@opensides.be>
 #
-#      OPTIONS:  ---
-# REQUIREMENTS:  libconfig-inifiles-perl libcrypt-rijndael-perl libxml-simple-perl 
-#                libdata-dumper-simple-perl libdbd-sqlite3-perl libnet-ldap-perl
-#                libpoe-perl
-#         BUGS:  ---
-#        NOTES:
-#       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
-#      COMPANY:
-#      VERSION:  1.0
-#      CREATED:  12.09.2007 08:54:41 CEST
-#     REVISION:  ---
-#===============================================================================
+#*********************************************************************
 
 
-# TODO
-#
-# max_children wird momentan nicht mehr verwendet, jede eingehende nachricht bekommt ein eigenes POE child
+=head1 NAME
+
+gosa-si-server -Support infrastructure for GOsa
+
+=head1 SYNOPSIS
+
+gosa-si-server [-hvf] [-c config] [-x dump ]
+
+=head1 OPTIONS
+
+B<-h>, B<--help>
+    print out this help message
+
+B<-v>, B<--verbose>
+    be verbose (multiple v's will increase verbosity) 
+    -v          ERROR level
+    -vvv        WARNING  + ERROR level
+    -vvvvv      INFO + WARNING level
+    -vvvvvvv    DEBUG + INFO level
+    -vvvvvvvvv  in and out going xml messages will be displayed
+
+B<-f>, B<--foreground> 
+    foregroud, process will not be forked to background
+
+B<-c> I<file>, B<--config=>I<file>
+    configuration file, default F</etc/gosa-si/server.conf>
+
+B<--no-arp>
+    starts script without connection to arp module
+
+B<-d> <int> 
+    if verbose level is higher than 7 'v' specified parts can be debugged
+
+      1 : receiving messages
+      2 : sending messages
+      4 : encrypting/decrypting messages
+      8 : verification if a message complies gosa-si requirements
+     16 : message processing
+     32 : ldap connectivity
+     64 : database status and connectivity
+    128 : main process
+    256 : creation of packages_list_db
+    512 : ARP debug information
+
+B<-x> <dump>
+     dump configuration to stdout
+     ( 1 = current, 2 = default )
+
+=head1 DESCRIPTION
+
+gosa-si-server  belongs  to  the  support infrastructure of GOsa.  Several gosa-si-clients can connect to one gosa-si-server.  The server take care of the message forwarding from GOsa to si-clients.  At the client site each message is related to a working instruction which will be executed there.  Depending of the message an answer from the client to GOsa via the server is possible.  Additional to answers clients reporting events  or  information  to the server.  The server registers himself at other servers in network and shares his knowledge with them.  So messages to clients which are no registrated locally will be forward to the client corresponding server. The communication within the complete SI nework is realised by XML messages.
+
+
+=head1 BUGS 
+
+Please report any bugs, or post any suggestions, to the GOsa mailing list <gosa-devel@oss.gonicus.de> or to <https://oss.gonicus.de/labs/gosa>
+
+
+=head1 LICENCE AND COPYRIGHT
+
+This code is part of GOsa (L<http://www.gosa-project.org>)
+
+Copyright (C) 2003-2010 GONICUS GmbH
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+=cut
 
 use strict;
 use warnings;
+
 use Getopt::Long;
 use Config::IniFiles;
-use POSIX;
-
-use Fcntl;
 use IO::Socket::INET;
 use IO::Handle;
 use IO::Select;
-use Symbol qw(qualify_to_ref);
 use Crypt::Rijndael;
 use MIME::Base64;
 use Digest::MD5  qw(md5 md5_hex md5_base64);
 use XML::Simple;
 use Data::Dumper;
 use Sys::Syslog qw( :DEFAULT setlogsock);
-use Cwd;
+use Time::HiRes qw( usleep clock_gettime );
 use File::Spec;
 use File::Basename;
 use File::Find;
 use File::Copy;
 use File::Path;
-use GOSA::DBsqlite;
-use GOSA::GosaSupportDaemon;
-use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
 use Net::LDAP;
 use Net::LDAP::Util qw(:escape);
-use Time::HiRes qw( usleep);
-use DateTime;
+use File::Pid;
+use GOsaSI::GosaSupportDaemon;
 
-my $modules_path = "/usr/lib/gosa-si/modules";
-use lib "/usr/lib/gosa-si/modules";
+use POE qw(Component::Server::TCP Wheel::Run Filter::Reference);
+use Symbol qw(qualify_to_ref);
+use Fcntl qw/:flock/;
+use POSIX;
+
+my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev$';
 
 # revision number of server and program name
-my $server_version = '$HeadURL: https://oss.gonicus.de/repositories/gosa/trunk/gosa-si/gosa-si-server $:$Rev: 10826 $';
 my $server_headURL;
 my $server_revision;
 my $server_status;
-our $prg= basename($0);
 
-our $global_kernel;
+my $db_module = "DBsqlite";
+{
+no strict "refs";
+require ("GOsaSI/".$db_module.".pm");
+("GOsaSI/".$db_module)->import;
+}
+
+my $modules_path = "/usr/lib/gosa-si/modules";
+use lib "/usr/lib/gosa-si/modules";
+
 my ($foreground, $ping_timeout);
 my ($server);
 my ($gosa_server, $job_queue_timeout, $job_queue_loop_delay);
 my ($messaging_db_loop_delay);
-my ($known_modules);
-my ($procid, $pid);
-my ($arp_fifo);
+my $procid;
+my $arp_fifo;
+my $debug_parts = 0;
+my $debug_parts_bitstring;
 my ($xml);
 my $sources_list;
 my $max_clients;
 my %repo_files=();
 my $repo_path;
 my %repo_dirs=();
-# variables declared in config file are always set to 'our'
-our (%cfg_defaults, $log_file, $pid_file, 
-    $server_ip, $server_port, $ClientPackages_key, 
+
+my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
+
+# Variables declared in config file are always set to 'our'
+our (%cfg_defaults, $log_file, $pid_file, $pid,
+    $server_ip, $server_port, $ClientPackages_key, $dns_lookup,
     $arp_activ, $gosa_unit_tag,
-    $GosaPackages_key, $gosa_ip, $gosa_port, $gosa_timeout,
-    $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
+    $GosaPackages_key, $gosa_timeout,
+    $serverPackages_enabled, $foreign_server_string, $server_domain, $ServerPackages_key, $foreign_servers_register_delay,
     $wake_on_lan_passwd, $job_synchronization, $modified_jobs_loop_delay,
+    $arp_enabled, $arp_interface,
+    $opsi_enabled, $opsi_server, $opsi_admin, $opsi_password,
+    $new_systems_ou,
+    $arch,
 );
 
 # additional variable which should be globaly accessable
@@ -94,19 +164,22 @@ our $server_address;
 our $server_mac_address;
 our $gosa_address;
 our $no_arp;
-our $verbose;
 our $forground;
 our $cfg_file;
-our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn);
+our ($ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $ldap_server_dn, $ldap_version, $ldap_retry_sec);
+our ($mysql_username, $mysql_password, $mysql_database, $mysql_host);
+our $known_modules;
+our $known_functions;
+our $root_uid;
+our $adm_gid;
+our $verbose= 0;
+our $global_kernel;
 
-# dak variables
-our $dak_base_directory;
-our $dak_signing_keys_directory;
-our $dak_queue_directory;
-our $dak_user;
+# where is the config stored by default and his name
+our $config = '/etc/gosa-si/server.conf';
 
-# specifies the verbosity of the daemon_log
-$verbose = 0 ;
+# by default dumping of config is undefined
+our $dump_config = undef;
 
 # if foreground is not null, script will be not forked to background
 $foreground = 0 ;
@@ -123,188 +196,298 @@ my $watch_for_new_jobs_in_progress = 0;
 our $incoming_db;
 our $incoming_tn = 'incoming';
 my $incoming_file_name;
-my @incoming_col_names = ("id INTEGER PRIMARY KEY", 
-        "timestamp DEFAULT 'none'", 
-        "headertag DEFAULT 'none'",
-               "targettag DEFAULT 'none'",
-        "xmlmessage DEFAULT 'none'",
-        "module DEFAULT 'none'",
-        "sessionid DEFAULT '0'",
-        );
+my @incoming_col_names = ("id INTEGER PRIMARY KEY",
+       "timestamp VARCHAR(14) DEFAULT 'none'", 
+       "headertag VARCHAR(255) DEFAULT 'none'",
+       "targettag VARCHAR(255) DEFAULT 'none'",
+       "xmlmessage TEXT",
+       "module VARCHAR(255) DEFAULT 'none'",
+       "sessionid VARCHAR(255) DEFAULT '0'",
+);
 
 # holds all gosa jobs
 our $job_db;
 our $job_queue_tn = 'jobs';
 my $job_queue_file_name;
-my @job_queue_col_names = ("id INTEGER PRIMARY KEY", 
-               "timestamp DEFAULT 'none'", 
-               "status DEFAULT 'none'", 
-               "result DEFAULT 'none'", 
-               "progress DEFAULT 'none'", 
-        "headertag DEFAULT 'none'", 
-               "targettag DEFAULT 'none'", 
-               "xmlmessage DEFAULT 'none'", 
-               "macaddress DEFAULT 'none'",
-               "plainname DEFAULT 'none'",
-        "siserver DEFAULT 'none'",
-        "modified DEFAULT '0'",
-               );
+my @job_queue_col_names = ("id INTEGER PRIMARY KEY",
+       "timestamp VARCHAR(14) DEFAULT 'none'", 
+       "status VARCHAR(255) DEFAULT 'none'", 
+       "result TEXT",
+       "progress VARCHAR(255) DEFAULT 'none'",
+       "headertag VARCHAR(255) DEFAULT 'none'",
+       "targettag VARCHAR(255) DEFAULT 'none'", 
+       "xmlmessage TEXT", 
+       "macaddress VARCHAR(17) DEFAULT 'none'",
+       "plainname VARCHAR(255) DEFAULT 'none'",
+       "siserver VARCHAR(255) DEFAULT 'none'",
+       "modified INTEGER DEFAULT '0'",
+       "periodic VARCHAR(6) DEFAULT 'none'",
+);
 
 # holds all other gosa-si-server
 our $known_server_db;
 our $known_server_tn = "known_server";
 my $known_server_file_name;
-my @known_server_col_names = ("hostname", "status", "hostkey", "timestamp");
+my @known_server_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "loaded_modules TEXT", "timestamp VARCHAR(14)", "update_time VARCHAR(14)");
 
 # holds all registrated clients
 our $known_clients_db;
 our $known_clients_tn = "known_clients";
 my $known_clients_file_name;
-my @known_clients_col_names = ("hostname", "status", "hostkey", "timestamp", "macaddress", "events", "keylifetime");
+my @known_clients_col_names = ("hostname VARCHAR(255)", "status VARCHAR(255)", "hostkey VARCHAR(255)", "timestamp VARCHAR(14)", "macaddress VARCHAR(17)", "events TEXT", "keylifetime VARCHAR(255)");
 
 # holds all registered clients at a foreign server
 our $foreign_clients_db;
 our $foreign_clients_tn = "foreign_clients"; 
 my $foreign_clients_file_name;
-my @foreign_clients_col_names = ("hostname", "macaddress", "regserver", "timestamp");
+my @foreign_clients_col_names = ("hostname VARCHAR(255)", "macaddress VARCHAR(17)", "regserver VARCHAR(255)", "timestamp VARCHAR(14)");
 
 # holds all logged in user at each client 
 our $login_users_db;
 our $login_users_tn = "login_users";
 my $login_users_file_name;
-my @login_users_col_names = ("client", "user", "timestamp");
+my @login_users_col_names = ("client VARCHAR(255)", "user VARCHAR(255)", "timestamp VARCHAR(14)", "regserver VARCHAR(255) DEFAULT 'localhost'");
 
 # holds all fai server, the debian release and tag
 our $fai_server_db;
 our $fai_server_tn = "fai_server"; 
 my $fai_server_file_name;
-our @fai_server_col_names = ("timestamp", "server", "release", "sections", "tag"); 
+our @fai_server_col_names = ("timestamp VARCHAR(14)", "server VARCHAR(255)", "fai_release VARCHAR(255)", "sections VARCHAR(255)", "tag VARCHAR(255)"); 
 
 our $fai_release_db;
 our $fai_release_tn = "fai_release"; 
 my $fai_release_file_name;
-our @fai_release_col_names = ("timestamp", "release", "class", "type", "state"); 
+our @fai_release_col_names = ("timestamp VARCHAR(14)", "fai_release VARCHAR(255)", "class VARCHAR(255)", "type VARCHAR(255)", "state VARCHAR(255)"); 
 
 # holds all packages available from different repositories
 our $packages_list_db;
 our $packages_list_tn = "packages_list";
 my $packages_list_file_name;
-our @packages_list_col_names = ("distribution", "package", "version", "section", "description", "template", "timestamp");
+our @packages_list_col_names = ("distribution VARCHAR(255)", "package VARCHAR(255)", "version VARCHAR(255)", "section VARCHAR(255)", "description TEXT", "template LONGBLOB", "timestamp VARCHAR(14)");
 my $outdir = "/tmp/packages_list_db";
-my $arch = "i386"; 
 
 # holds all messages which should be delivered to a user
 our $messaging_db;
 our $messaging_tn = "messaging"; 
-our @messaging_col_names = ("id INTEGER", "subject", "message_from", "message_to", 
-        "flag", "direction", "delivery_time", "message", "timestamp" );
+our @messaging_col_names = ("id INTEGER", "subject TEXT", "message_from VARCHAR(255)", "message_to VARCHAR(255)", 
+        "flag VARCHAR(255)", "direction VARCHAR(255)", "delivery_time VARCHAR(255)", "message TEXT", "timestamp VARCHAR(14)" );
 my $messaging_file_name;
 
 # path to directory to store client install log files
 our $client_fai_log_dir = "/var/log/fai"; 
 
 # queue which stores taskes until one of the $max_children children are ready to process the task
-my @tasks = qw();
 my @msgs_to_decrypt = qw();
 my $max_children = 2;
 
 
+# loop delay for job queue to look for opsi jobs
+my $job_queue_opsi_delay = 10;
+our $opsi_client;
+our $opsi_url;
+# Lifetime of logged in user information. If no update information comes after n seconds, 
+# the user is expeceted to be no longer logged in or the host is no longer running. Because
+# of this, the user is deleted from login_users_db
+our $logged_in_user_date_of_expiry = 600;
+
+# List of month names, used in function daemon_log
+my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
+
+# List of accepted periodical xml tags related to cpan modul DateTime
+our $check_periodic = {"months"=>'', "weeks"=>'', "days"=>'', "hours"=>'', "minutes"=>''};
+
+
 %cfg_defaults = (
-"general" => {
-    "log-file" => [\$log_file, "/var/run/".$prg.".log"],
-    "pid-file" => [\$pid_file, "/var/run/".$prg.".pid"],
+"General" => {
+    "log-file" => [\$log_file, "/var/log/gosa-si/gosa-si-server.log"],
+    "pid-file" => [\$pid_file, "/var/run/gosa-si/gosa-si-server.pid"],
     },
-"server" => {
-    "port" => [\$server_port, "20081"],
-    "known-clients"        => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
-    "known-servers"        => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
-    "incoming"             => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
-    "login-users"          => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
-    "fai-server"           => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
-    "fai-release"          => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
-    "packages-list"        => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
-    "messaging"            => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
-    "foreign-clients"      => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
-    "source-list"          => [\$sources_list, '/etc/apt/sources.list'],
-    "repo-path"            => [\$repo_path, '/srv/www/repository'],
-    "ldap-uri"             => [\$ldap_uri, ""],
-    "ldap-base"            => [\$ldap_base, ""],
-    "ldap-admin-dn"        => [\$ldap_admin_dn, ""],
-    "ldap-admin-password"  => [\$ldap_admin_password, ""],
-    "gosa-unit-tag"        => [\$gosa_unit_tag, ""],
-    "max-clients"          => [\$max_clients, 10],
-    "wol-password"           => [\$wake_on_lan_passwd, ""],
+"Server" => {
+    "ip"                    => [\$server_ip, "0.0.0.0"],
+    "port"                  => [\$server_port, "20081"],
+    "known-clients"         => [\$known_clients_file_name, '/var/lib/gosa-si/clients.db' ],
+    "known-servers"         => [\$known_server_file_name, '/var/lib/gosa-si/servers.db'],
+    "incoming"              => [\$incoming_file_name, '/var/lib/gosa-si/incoming.db'],
+    "login-users"           => [\$login_users_file_name, '/var/lib/gosa-si/users.db'],
+    "fai-server"            => [\$fai_server_file_name, '/var/lib/gosa-si/fai_server.db'],
+    "fai-release"           => [\$fai_release_file_name, '/var/lib/gosa-si/fai_release.db'],
+    "packages-list"         => [\$packages_list_file_name, '/var/lib/gosa-si/packages.db'],
+    "messaging"             => [\$messaging_file_name, '/var/lib/gosa-si/messaging.db'],
+    "foreign-clients"       => [\$foreign_clients_file_name, '/var/lib/gosa-si/foreign_clients.db'],
+    "source-list"           => [\$sources_list, '/etc/apt/sources.list'],
+    "repo-path"             => [\$repo_path, '/srv/www/debian'],
+    "debian-arch"           => [\$arch, 'i386'],     
+    "ldap-uri"              => [\$ldap_uri, ""],
+    "ldap-base"             => [\$ldap_base, ""],
+    "ldap-admin-dn"         => [\$ldap_admin_dn, ""],
+    "ldap-admin-password"   => [\$ldap_admin_password, ""],
+    "ldap-version"          => [\$ldap_version, 3],
+    "ldap-retry-sec"        => [\$ldap_retry_sec, 10],
+    "gosa-unit-tag"         => [\$gosa_unit_tag, ""],
+    "max-clients"           => [\$max_clients, 10],
+    "wol-password"          => [\$wake_on_lan_passwd, ""],
+    "mysql-username"        => [\$mysql_username, "gosa_si"],
+    "mysql-password"        => [\$mysql_password, ""],
+    "mysql-database"        => [\$mysql_database, "gosa_si"],
+    "mysql-host"            => [\$mysql_host, "127.0.0.1"],
     },
 "GOsaPackages" => {
-    "ip" => [\$gosa_ip, "0.0.0.0"],
-    "port" => [\$gosa_port, "20082"],
     "job-queue" => [\$job_queue_file_name, '/var/lib/gosa-si/jobs.db'],
     "job-queue-loop-delay" => [\$job_queue_loop_delay, 3],
     "messaging-db-loop-delay" => [\$messaging_db_loop_delay, 3],
     "key" => [\$GosaPackages_key, "none"],
-       "dak-base" => [\$dak_base_directory, "/srv/archive"],
-       "dak-keyring" => [\$dak_signing_keys_directory, "/srv/archive/keyrings"],
-       "dak-queue" => [\$dak_queue_directory, "/srv/archive/queue"],
-       "dak-user" => [\$dak_user, "deb-dak"],
+    "new-systems-ou" => [\$new_systems_ou, 'ou=workstations,ou=systems'],
     },
 "ClientPackages" => {
     "key" => [\$ClientPackages_key, "none"],
+    "user-date-of-expiry" => [\$logged_in_user_date_of_expiry, 600],
     },
 "ServerPackages"=> {
+    "enabled"      => [\$serverPackages_enabled, "true"],
     "address"      => [\$foreign_server_string, ""],
-    "domain"  => [\$server_domain, ""],
-    "key"     => [\$ServerPackages_key, "none"],
+    "dns-lookup"   => [\$dns_lookup, "true"],
+    "domain"       => [\$server_domain, ""],
+    "key"          => [\$ServerPackages_key, "none"],
     "key-lifetime" => [\$foreign_servers_register_delay, 120],
     "job-synchronization-enabled" => [\$job_synchronization, "true"],
     "synchronization-loop" => [\$modified_jobs_loop_delay, 5],
-}
+    },
+"ArpHandler" => {
+    "enabled"   => [\$arp_enabled, "false"],
+    "interface" => [\$arp_interface, "all"],
+       },
+"Opsi" => {
+    "enabled"  => [\$opsi_enabled, "false"], 
+    "server"   => [\$opsi_server, "localhost"],
+    "admin"    => [\$opsi_admin, "opsi-admin"],
+    "password" => [\$opsi_password, "secret"],
+   },
+
 );
 
+#############################
+#
+# @brief Display error message and/or help text.
+#
+# In correspondence to previous GetOptions
+#
+# @param $text - string to print as error message
+# @param $help - set true, if you want to show usage help
+#
+sub usage
+{
+  my( $text, $help ) = @_;
 
-#===  FUNCTION  ================================================================
-#         NAME:  usage
-#   PARAMETERS:  nothing
-#      RETURNS:  nothing
-#  DESCRIPTION:  print out usage text to STDERR
-#===============================================================================
-sub usage {
-    print STDERR << "EOF" ;
-usage: $prg [-hvf] [-c config]
+  $text = undef if( 'h' eq $text );
+  (defined $text) && print STDERR "\n$text\n";
+
+  if( (defined $help && $help)
+      || (!defined $help && !defined $text) )
+{
+    print STDERR << "EOF";
 
-           -h        : this (help) message
-           -c <file> : config file
-           -f        : foreground, process will not be forked to background
+  usage: $0 [-hvf] [-c config] [-d number]
+
+   -h        : this (help) message
+   -c <file> : config file (default: ${config})
+   -x <cfg>  : dump configuration to stdout
+             ( 1 = current, 2 = default )
+   -f        : foreground (don't fork)
            -v        : be verbose (multiple to increase verbosity)
-           -no-arp   : starts $prg without connection to arp module
+                              'v': error logs
+                            'vvv': warning plus error logs                                             
+                          'vvvvv': info plus warning logs
+                        'vvvvvvv': debug plus info logs
+           -no-arp   : starts gosa-si-server without connection to arp module
+           -d <int>  : if verbose level is higher than 7x 'v' specified parts can be debugged
+                           1 : report incoming messages
+                           2 : report unencrypted outgoing messages 
+                           4 : report encrypting key for messages
+                           8 : report decrypted incoming message and verification if the message complies gosa-si requirements
+                          16 : message processing
+                          32 : ldap connectivity
+                          64 : database status and connectivity
+                         128 : main process 
+                         256 : creation of packages_list_db
+                         512 : ARP debug information
 EOF
-    print "\n" ;
 }
+  print( "\n" );
 
+  exit( -1 );
+}
 
-#===  FUNCTION  ================================================================
-#         NAME:  read_configfile
-#   PARAMETERS:  cfg_file - string -
-#      RETURNS:  nothing
-#  DESCRIPTION:  read cfg_file and set variables
-#===============================================================================
-sub read_configfile {
-    my $cfg;
-    if( defined( $cfg_file) && ( (-s $cfg_file) > 0 )) {
-        if( -r $cfg_file ) {
-            $cfg = Config::IniFiles->new( -file => $cfg_file );
-        } else {
-            print STDERR "Couldn't read config file!\n";
-        }
-    } else {
-        $cfg = Config::IniFiles->new() ;
-    }
-    foreach my $section (keys %cfg_defaults) {
-        foreach my $param (keys %{$cfg_defaults{ $section }}) {
-            my $pinfo = $cfg_defaults{ $section }{ $param };
-            ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
-        }
-    }
+#############################
+#
+# @brief Manage gosa-si-client configuration.
+#
+# Will exit after successfull dump to stdout (type = 1 | 2)
+#
+# Dump type can be:
+#   1: Current gosa-si-client configuration in config file (exit)
+#   2: Default gosa-si-client configuration (exit)
+#   3: Dump to logfile (no exit)
+#
+# @param int config type
+#
+sub dump_configuration {
+
+  my( $cfg_type ) = @_;
+
+  return if( ! defined $cfg_type );
+
+  if(1==$cfg_type ) {
+    print( "# Current gosa-si-server configuration\n" );
+       } elsif (2==$cfg_type) {
+    print( "# Default gosa-si-server configuration\n" );
+       } elsif (3==$cfg_type) {
+    daemon_log( "Dumping gosa-si-server configuration\n", 2 );
+       } else {
+    return;
+       }
+
+  foreach my $section (keys %cfg_defaults) {
+    if( 3 != $cfg_type ) { 
+      print( "\n[${section}]\n" ); 
+       } else {
+      daemon_log( "\n  [${section}]\n", 3 ); 
+       }
+
+               foreach my $param (sort( keys %{$cfg_defaults{ $section }})) {
+      my $pinfo = $cfg_defaults{ $section }{ $param };
+      my $value;
+      if (1==$cfg_type) {
+        if( defined( ${@$pinfo[ 0 ]} ) ) {
+          $value = ${@$pinfo[ 0 ]};
+          print( "$param=$value\n" );
+                               } else {
+          print( "#${param}=\n" ); 
+                               }
+                       } elsif (2==$cfg_type) {
+        $value = @{$pinfo}[ 1 ];
+        if( defined( @$pinfo[ 1 ] ) ) {
+          $value = @{$pinfo}[ 1 ];
+          print( "$param=$value\n" );
+                               } else {
+          print( "#${param}=\n" ); 
+                               }
+                       } elsif (3==$cfg_type) {
+        if( defined(  ${@$pinfo[ 0 ]} ) ) {
+          $value = ${@$pinfo[ 0 ]};
+          daemon_log( "  $param=$value\n", 3 )
+                               }
+                       }
+               }
+       }
+
+
+# We just exit at stdout dump
+  if( 3 == $cfg_type ) { 
+    daemon_log( "\n", 3 );
+       } else {
+    exit( 0 );
+       }
 }
 
 
@@ -317,114 +500,69 @@ sub read_configfile {
 #  DESCRIPTION:  function for logging
 #===============================================================================
 sub daemon_log {
-    # log into log_file
     my( $msg, $level ) = @_;
-    if(not defined $msg) { return }
-    if(not defined $level) { $level = 1 }
-    if(defined $log_file){
-        open(LOG_HANDLE, ">>$log_file");
-        chmod 0600, $log_file;
-        if(not defined open( LOG_HANDLE, ">>$log_file" )) {
-            print STDERR "cannot open $log_file: $!";
-            return 
-        }
-        chomp($msg);
-        $msg =~s/\n//g;   # no newlines are allowed in log messages, this is important for later log parsing
-        if($level <= $verbose){
-            my ($seconds, $minutes, $hours, $monthday, $month,
-                    $year, $weekday, $yearday, $sommertime) = localtime(time);
-            $hours = $hours < 10 ? $hours = "0".$hours : $hours;
-            $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
-            $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
-            my @monthnames = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
-            $month = $monthnames[$month];
-            $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
-            $year+=1900;
-            my $name = $prg;
-
-            my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
-            print LOG_HANDLE $log_msg;
-            if( $foreground ) { 
-                print STDERR $log_msg;
-            }
-        }
-        close( LOG_HANDLE );
-    }
-}
-
+    if (not defined $msg) { return }
+    if (not defined $level) { $level = 1 }
+       my $to_be_logged = 0;
+
+       # Write log line if line level is lower than verbosity given in commandline
+       if ($level <= $verbose) 
+       { 
+               $to_be_logged = 1 ;
+       }
 
-#===  FUNCTION  ================================================================
-#         NAME:  check_cmdline_param
-#   PARAMETERS:  nothing
-#      RETURNS:  nothing
-#  DESCRIPTION:  validates commandline parameter
-#===============================================================================
-sub check_cmdline_param () {
-    my $err_config;
-    my $err_counter = 0;
-       if(not defined($cfg_file)) {
-               $cfg_file = "/etc/gosa-si/server.conf";
-               if(! -r $cfg_file) {
-                       $err_config = "please specify a config file";
-                       $err_counter += 1;
+       # Write if debug flag is set and bitstring matches
+       if ($debug_parts > 0)
+       {
+               my $tmp_level = ($level - 10 >= 0) ? $level - 10 : 0 ;
+               my $tmp_level_bitstring = unpack("B32", pack("N", $tmp_level));
+               if (int($debug_parts_bitstring & $tmp_level_bitstring)) 
+               {
+                       $to_be_logged = 1;
                }
-    }
-    if( $err_counter > 0 ) {
-        &usage( "", 1 );
-        if( defined( $err_config)) { print STDERR "$err_config\n"}
-        print STDERR "\n";
-        exit( -1 );
-    }
-}
-
+       }
 
-#===  FUNCTION  ================================================================
-#         NAME:  check_pid
-#   PARAMETERS:  nothing
-#      RETURNS:  nothing
-#  DESCRIPTION:  handels pid processing
-#===============================================================================
-sub check_pid {
-    $pid = -1;
-    # Check, if we are already running
-    if( open(LOCK_FILE, "<$pid_file") ) {
-        $pid = <LOCK_FILE>;
-        if( defined $pid ) {
-            chomp( $pid );
-            if( -f "/proc/$pid/stat" ) {
-                my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
-                if( $stat ) {
-                                       daemon_log("ERROR: Already running",1);
-                    close( LOCK_FILE );
-                    exit -1;
-                }
-            }
-        }
-        close( LOCK_FILE );
-        unlink( $pid_file );
-    }
+       if ($to_be_logged) 
+       {
+               if(defined $log_file){
+                       my $open_log_fh = sysopen(LOG_HANDLE, $log_file, O_WRONLY | O_CREAT | O_APPEND , 0440);
+                       if(not $open_log_fh) {
+                               print STDERR "cannot open $log_file: $!";
+                               return;
+                       }
+                       # Check owner and group of log_file and update settings if necessary
+                       my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($log_file);
+                       if((not $uid eq $root_uid) || (not $gid eq $adm_gid)) {
+                               chown($root_uid, $adm_gid, $log_file);
+                       }
 
-    # create a syslog msg if it is not to possible to open PID file
-    if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
-        my($msg) = "Couldn't obtain lockfile '$pid_file' ";
-        if (open(LOCK_FILE, '<', $pid_file)
-                && ($pid = <LOCK_FILE>))
-        {
-            chomp($pid);
-            $msg .= "(PID $pid)\n";
-        } else {
-            $msg .= "(unable to read PID)\n";
-        }
-        if( ! ($foreground) ) {
-            openlog( $0, "cons,pid", "daemon" );
-            syslog( "warning", $msg );
-            closelog();
-        }
-        else {
-            print( STDERR " $msg " );
-        }
-        exit( -1 );
-    }
+                       # Prepare time string for log message
+                       my ($seconds,$minutes,$hours,$monthday,$month,$year,$weekday,$yearday,$sommertime) = localtime(time);
+                       $hours = $hours < 10 ? $hours = "0".$hours : $hours;
+                       $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
+                       $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
+                       $month = $monthnames[$month];
+                       $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
+                       $year+=1900;
+
+                       # Microseconds since epoch
+                       my $microSeconds = sprintf("%.2f", &Time::HiRes::clock_gettime());
+                       $microSeconds =~ s/^\d*(.\d\d)$/$1/;
+
+                       # Build log message and write it to log file and commandline
+                       chomp($msg);
+                       my $log_msg = "$month $monthday $hours:$minutes:$seconds$microSeconds $0 $msg\n";
+                       flock(LOG_HANDLE, LOCK_EX);
+                       seek(LOG_HANDLE, 0, 2);
+                       print LOG_HANDLE $log_msg;
+                       flock(LOG_HANDLE, LOCK_UN);
+                       if( $foreground ) 
+                       { 
+                               print STDERR $log_msg;
+                       }
+                       close( LOG_HANDLE );
+               }
+       }
 }
 
 #===  FUNCTION  ================================================================
@@ -436,34 +574,40 @@ sub check_pid {
 #                state is on is imported by "require 'file';"
 #===============================================================================
 sub import_modules {
-    daemon_log(" ", 1);
-
     if (not -e $modules_path) {
         daemon_log("0 ERROR: cannot find directory or directory is not readable: $modules_path", 1);   
     }
 
     opendir (DIR, $modules_path) or die "ERROR while loading modules from directory $modules_path : $!\n";
+
     while (defined (my $file = readdir (DIR))) {
         if (not $file =~ /(\S*?).pm$/) {
             next;
         }
                my $mod_name = $1;
 
+        # ArpHandler switch
         if( $file =~ /ArpHandler.pm/ ) {
-            if( $no_arp > 0 ) {
-                next;
-            }
+            if( $arp_enabled eq "false" ) { next; }
         }
+
+               # ServerPackages switch
+               if ($file eq "ServerPackages.pm" && $serverPackages_enabled eq "false") 
+               {
+                       $dns_lookup = "false";
+                       next; 
+               }
         
         eval { require $file; };
         if ($@) {
             daemon_log("0 ERROR: gosa-si-server could not load module $file", 1);
-            daemon_log("$@", 5);
+            daemon_log("$@", 1);
+           exit;
                } else {
                        my $info = eval($mod_name.'::get_module_info()');
                        # Only load module if get_module_info() returns a non-null object
                        if( $info ) {
-                               my ($input_address, $input_key, $input, $input_active, $input_type) = @{$info};
+                               my ($input_address, $input_key, $event_hash) = @{$info};
                                $known_modules->{$mod_name} = $info;
                                daemon_log("0 INFO: module $mod_name loaded", 5);
                        }
@@ -500,21 +644,27 @@ sub password_check {
 
 #===  FUNCTION  ================================================================
 #         NAME:  sig_int_handler
-#   PARAMETERS:  signal - string - signal arose from system
+#   PARAMETERS:  signal - string - signal came from system
 #      RETURNS:  nothing
-#  DESCRIPTION:  handels tasks to be done befor signal becomes active
+#  DESCRIPTION:  handle tasks to be done before signal becomes active
 #===============================================================================
 sub sig_int_handler {
-    my ($signal) = @_;
+       my ($signal) = @_;
 
 #      if (defined($ldap_handle)) {
 #              $ldap_handle->disconnect;
 #      }
-    # TODO alle verbliebenden ldap verbindungen aus allen heaps beenden
+# TODO all ldap connections shoudl be closed
     
+       daemon_log("shutting down gosa-si-server", 1);
+
+       # asking the poe kernel to shutdown the server
+       $global_kernel->yield(TCP_SERVER => 'shutdown');
+
+       # removing the pidfile
+       $pid->remove or warn "Could not remove $pid_file\n";
 
-    daemon_log("shutting down gosa-si-server", 1);
-    system("kill `ps -C gosa-si-server -o pid=`");
+       exit(0);
 }
 $SIG{INT} = \&sig_int_handler;
 
@@ -529,14 +679,14 @@ sub check_key_and_xml_validity {
 
         if ($msg =~ /<xml>/i){
             $msg =~ s/\s+/ /g;  # just for better daemon_log
-            daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 8);
+            daemon_log("$session_id DEBUG: decrypted_msg: \n$msg", 18);
             $msg_hash = $xml->XMLin($msg, ForceArray=>1);
 
             ##############
             # check header
             if( not exists $msg_hash->{'header'} ) { die "no header specified"; }
             my $header_l = $msg_hash->{'header'};
-            if( 1 > @{$header_l} ) { die 'empty header tag'; }
+            if( (1 > @{$header_l}) || ( ( 'HASH' eq ref @{$header_l}[0]) && (1 > keys %{@{$header_l}[0]}) ) ) { die 'empty header tag'; }
             if( 1 < @{$header_l} ) { die 'more than one header specified'; }
             my $header = @{$header_l}[0];
             if( 0 == length $header) { die 'empty string in header tag'; }
@@ -545,7 +695,7 @@ sub check_key_and_xml_validity {
             # check source
             if( not exists $msg_hash->{'source'} ) { die "no source specified"; }
             my $source_l = $msg_hash->{'source'};
-            if( 1 > @{$source_l} ) { die 'empty source tag'; }
+            if( (1 > @{$source_l}) || ( ( 'HASH' eq ref @{$source_l}[0]) && (1 > keys %{@{$source_l}[0]}) ) ) { die 'empty source tag'; }
             if( 1 < @{$source_l} ) { die 'more than one source specified'; }
             my $source = @{$source_l}[0];
             if( 0 == length $source) { die 'source error'; }
@@ -554,11 +704,11 @@ sub check_key_and_xml_validity {
             # check target
             if( not exists $msg_hash->{'target'} ) { die "no target specified"; }
             my $target_l = $msg_hash->{'target'};
-            if( 1 > @{$target_l} ) { die 'empty target tag'; }
+            if( (1 > @{$target_l}) || ( ('HASH' eq ref @{$target_l}[0]) && (1 > keys %{@{$target_l}[0]}) ) ) { die 'empty target tag'; }
         }
     };
     if($@) {
-        daemon_log("$session_id DEBUG: do not understand the message: $@", 7);
+        daemon_log("$session_id ERROR: do not understand the message: $@", 1);
         $msg = undef;
         $msg_hash = undef;
     }
@@ -595,8 +745,20 @@ sub check_outgoing_xml_validity {
         if( 0 == length $source) {
             die 'source has length 0';
         }
+
+               # Check if source contains hostname instead of ip address
+               if($source =~ /^[a-z][\w\-\.]+:\d+$/i) {
+                       my ($hostname,$port) = split(/:/, $source);
+                       my $ip_address = inet_ntoa(scalar gethostbyname($hostname));
+                       if(defined($ip_address) && $ip_address =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ && $port =~ /^\d+$/) {
+                               # Write ip address to $source variable
+                               $source = "$ip_address:$port";
+                               $msg_hash->{source}[0] = $source ;
+                               $msg =~ s/<source>.*<\/source>/<source>$source<\/source>/; 
+                       }
+               }
         unless( $source =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+$/ ||
-                $source =~ /^GOSA$/i ) {
+                $source =~ /^GOSA$/i) {
             die "source '$source' is neither a complete ip-address with port nor 'GOSA'";
         }
         
@@ -621,12 +783,12 @@ sub check_outgoing_xml_validity {
         }
     };
     if($@) {
-        daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: ", 1);
+        daemon_log("$session_id ERROR: outgoing msg is not gosa-si envelope conform: $@", 1);
         daemon_log("$@ ".(defined($msg) && length($msg)>0)?$msg:"Empty Message", 1);
         $msg_hash = undef;
     }
 
-    return ($msg_hash);
+    return ($msg, $msg_hash);
 }
 
 
@@ -643,26 +805,27 @@ sub input_from_known_server {
             next;
         }
         my $host_key = $hit->{hostkey};
-        daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 7);
-        daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 7);
+        daemon_log("$session_id DEBUG: input_from_known_server: host_name: $host_name", 14);
+        daemon_log("$session_id DEBUG: input_from_known_server: host_key: $host_key", 14);
 
         # check if module can open msg envelope with module key
         my ($tmp_msg, $tmp_msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
         if( (!$tmp_msg) || (!$tmp_msg_hash) ) {
-            daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 7);
-            daemon_log("$@", 8);
+            daemon_log("$session_id DEBUG: input_from_known_server: deciphering raise error", 14);
+            daemon_log("$@", 14);
             next;
         }
         else {
             $msg = $tmp_msg;
             $msg_hash = $tmp_msg_hash;
             $module = "ServerPackages";
+            daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
             last;
         }
     }
 
     if( (!$msg) || (!$msg_hash) || (!$module) ) {
-        daemon_log("$session_id DEBUG: Incoming message is not from a known server", 7);
+        daemon_log("$session_id DEBUG: Incoming message is not from a known server", 14);
     }
   
     return ($msg, $msg_hash, $module);
@@ -677,29 +840,29 @@ sub input_from_known_client {
     my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
     while( my ($hit_num, $hit) = each %{ $query_res } ) {    
         my $host_name = $hit->{hostname};
-        if( not $host_name =~ /^$remote_ip:\d*$/) {
+        if( not $host_name =~ /^$remote_ip/) {
                 next;
                }
         my $host_key = $hit->{hostkey};
-        &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 7);
-        &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 7);
+        &daemon_log("$session_id DEBUG: input_from_known_client: host_name: $host_name", 14);
+        &daemon_log("$session_id DEBUG: input_from_known_client: host_key: $host_key", 14);
 
         # check if module can open msg envelope with module key
         ($msg, $msg_hash) = &check_key_and_xml_validity($input, $host_key, $session_id);
 
         if( (!$msg) || (!$msg_hash) ) {
-            &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 7);
-            &daemon_log("$@", 8);
+            &daemon_log("$session_id DEGUG: input_from_known_client: deciphering raise error", 14);
             next;
         }
         else {
             $module = "ClientPackages";
+            daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 14);
             last;
         }
     }
 
     if( (!$msg) || (!$msg_hash) || (!$module) ) {
-        &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 7);
+        &daemon_log("$session_id DEBUG: Incoming message is not from a known client", 14);
     }
 
     return ($msg, $msg_hash, $module);
@@ -707,42 +870,42 @@ sub input_from_known_client {
 
 
 sub input_from_unknown_host {
-    no strict "refs";
-    my ($input, $session_id) = @_ ;
-    my ($msg, $msg_hash, $module);
-    my $error_string;
-    
+       no strict "refs";
+       my ($input, $session_id) = @_ ;
+       my ($msg, $msg_hash, $module);
+       my $error_string;
+
        my %act_modules = %$known_modules;
-       
-    while( my ($mod, $info) = each(%act_modules)) {
 
-        # check a key exists for this module
-        my $module_key = ${$mod."_key"};
-        if( not defined $module_key ) {
-            if( $mod eq 'ArpHandler' ) {
-                next;
-            }
-            daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
-            next;
-        }
-        daemon_log("$session_id DEBUG: $mod: $module_key", 7);
+       while( my ($mod, $info) = each(%act_modules)) {
 
-        # check if module can open msg envelope with module key
-        ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
-        if( (not defined $msg) || (not defined $msg_hash) ) {
-            next;
-        }
-        else {
-            $module = $mod;
-            last;
-        }
-    }
+               # check a key exists for this module
+               my $module_key = ${$mod."_key"};
+               if( not defined $module_key ) {
+                       if( $mod eq 'ArpHandler' ) {
+                               next;
+                       }
+                       daemon_log("$session_id ERROR: no key specified in config file for $mod", 1);
+                       next;
+               }
+               daemon_log("$session_id DEBUG: $mod: $module_key", 14);
 
-    if( (!$msg) || (!$msg_hash) || (!$module)) {
-        daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 7);
-    }
+               # check if module can open msg envelope with module key
+               ($msg, $msg_hash) = &check_key_and_xml_validity($input, $module_key, $session_id);
+               if( (not defined $msg) || (not defined $msg_hash) ) {
+                       next;
+               } else {
+                       $module = $mod;
+            daemon_log("$session_id DEBUG: check_key_and_xml_validity... ok", 18);
+                       last;
+               }
+       }
 
-    return ($msg, $msg_hash, $module);
+       if( (!$msg) || (!$msg_hash) || (!$module)) {
+               daemon_log("$session_id DEBUG: Incoming message is not from an unknown host", 14);
+       }
+
+       return ($msg, $msg_hash, $module);
 }
 
 
@@ -850,48 +1013,6 @@ sub open_socket {
 }
 
 
-sub get_local_ip_for_remote_ip {
-       my $remote_ip= shift;
-       my $result="0.0.0.0";
-
-       if($remote_ip =~ /^(\d\d?\d?\.){3}\d\d?\d?$/) {
-               if($remote_ip eq "127.0.0.1") {
-                       $result = "127.0.0.1";
-               } else {
-                       my $PROC_NET_ROUTE= ('/proc/net/route');
-
-                       open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
-                               or die "Could not open $PROC_NET_ROUTE";
-
-                       my @ifs = <PROC_NET_ROUTE>;
-
-                       close(PROC_NET_ROUTE);
-
-                       # Eat header line
-                       shift @ifs;
-                       chomp @ifs;
-                       foreach my $line(@ifs) {
-                               my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
-                               my $destination;
-                               my $mask;
-                               my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
-                               $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
-                               ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
-                               $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
-                               if(new NetAddr::IP($remote_ip)->within(new NetAddr::IP($destination, $mask))) {
-                                       # destination matches route, save mac and exit
-                                       $result= &get_ip($Iface);
-                                       last;
-                               }
-                       }
-               }
-       } else {
-               daemon_log("get_local_ip_for_remote_ip was called with a non-ip parameter: $remote_ip", 1);
-       }
-       return $result;
-}
-
-
 sub send_msg_to_target {
     my ($msg, $address, $encrypt_key, $msg_header, $session_id) = @_ ;
     my $error = 0;
@@ -907,11 +1028,14 @@ sub send_msg_to_target {
         $header = "";
     }
 
-       # Patch the source ip
-       if($msg =~ /<source>0\.0\.0\.0:\d*?<\/source>/) {
-               my $remote_ip = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
-               $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$remote_ip:$2<\/source>/s;
-       }
+       # Memorize own source address
+       my $own_source_address = &get_local_ip_for_remote_ip(sprintf("%s", $address =~ /^([0-9\.]*?):.*$/));
+       $own_source_address .= ":".$server_port;
+
+       # Patch 0.0.0.0 source to real address
+       $msg =~ s/<source>(0\.0\.0\.0):(\d*?)<\/source>/<source>$own_source_address<\/source>/s;
+       # Patch GOSA source to real address and add forward_to_gosa tag
+       $msg =~ s/<source>GOSA<\/source>/<source>$own_source_address<\/source> <forward_to_gosa>$own_source_address,$session_id<\/forward_to_gosa>/ ;
 
     # encrypt xml msg
     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
@@ -919,16 +1043,15 @@ sub send_msg_to_target {
     # opensocket
     my $socket = &open_socket($address);
     if( !$socket ) {
-        daemon_log("$session_id ERROR: cannot send ".$header."msg to $address , host not reachable", 1);
+        daemon_log("$session_id ERROR: Cannot open socket to host '$address'. Message processing aborted!", 1);
         $error++;
     }
     
     if( $error == 0 ) {
         # send xml msg
-        print $socket $crypted_msg."\n";
-
+        print $socket $crypted_msg.";$own_source_address\n";
         daemon_log("$session_id INFO: send ".$header."msg to $address", 5);
-        daemon_log("$session_id DEBUG: message:\n$msg", 9);
+        daemon_log("$session_id DEBUG: message:\n$msg", 12);
         
     }
 
@@ -956,7 +1079,7 @@ sub send_msg_to_target {
             if($new_status eq "down"){
                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
             } else {
-                daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
+                daemon_log("$session_id DEBUG: set '$address' from status '$act_status' to '$new_status'", 138);
             }
         }
     }
@@ -977,7 +1100,7 @@ sub send_msg_to_target {
             if($new_status eq "down"){
                 daemon_log("$session_id WARNING: set '$address' from status '$act_status' to '$new_status'", 3);
             } else {
-                daemon_log("$session_id INFO: set '$address' from status '$act_status' to '$new_status'", 5);
+                daemon_log("$session_id DEBUG: set '$address' from status '$act_status' to '$new_status'", 138);
             }
         }
     }
@@ -986,29 +1109,93 @@ sub send_msg_to_target {
 
 
 sub update_jobdb_status_for_send_msgs {
-    my ($answer, $error) = @_;
+    my ($session_id, $answer, $error) = @_;
     if( $answer =~ /<jobdb_id>(\d+)<\/jobdb_id>/ ) {
+               &daemon_log("$session_id DEBUG: try to update job status", 138); 
         my $jobdb_id = $1;
+    
+        $answer =~ /<header>(.*)<\/header>/;
+        my $job_header = $1;
+
+        $answer =~ /<target>(.*)<\/target>/;
+        my $job_target = $1;
             
-        # sending msg faild
+        # Sending msg failed
         if( $error ) {
-            if (not $answer =~ /<header>trigger_action_reinstall<\/header>/) {
+
+            # Set jobs to done, jobs do not need to deliver their message in any case
+            if (($job_header eq "trigger_action_localboot")
+                    ||($job_header eq "trigger_action_lock")
+                    ||($job_header eq "trigger_action_halt") 
+                    ) {
+                my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
+                my $res = $job_db->update_dbentry($sql_statement);
+                
+            # Reactivate jobs, jobs need to deliver their message
+            } elsif (($job_header eq "trigger_action_activate")
+                    ||($job_header eq "trigger_action_update")
+                    ||($job_header eq "trigger_action_reinstall") 
+                    ||($job_header eq "trigger_activate_new")
+                    ) {
+                                               if ($job_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) {
+                                                       &reactivate_job_with_delay($session_id, $job_target, $job_header, 30 );
+                                               } else {
+                                                       # If we don't have the mac adress at this time, we use the plainname
+                                                       my $plainname_result = $job_db->select_dbentry("SELECT plainname from jobs where id=$jobdb_id");
+                                                       my $plainname = $job_target;
+                                                       if ((keys(%$plainname_result) > 0) ) {
+                                                               $plainname = $plainname_result->{1}->{$job_target};
+                                                       }
+                                                       &reactivate_job_with_delay($session_id, $plainname, $job_header, 30 );
+                                               }
+            # For all other messages
+            } else {
                 my $sql_statement = "UPDATE $job_queue_tn ".
                     "SET status='error', result='can not deliver msg, please consult log file' ".
                     "WHERE id=$jobdb_id";
                 my $res = $job_db->update_dbentry($sql_statement);
             }
 
-        # sending msg was successful
+        # Sending msg was successful
         } else {
-            my $sql_statement = "UPDATE $job_queue_tn ".
-                "SET status='done' ".
-                "WHERE id=$jobdb_id AND status='processed'";
-            my $res = $job_db->update_dbentry($sql_statement);
-        }
+            # Set jobs localboot, lock, activate, halt, reboot and wake to done
+            # jobs reinstall, update, inst_update do themself setting to done
+            if (($job_header eq "trigger_action_localboot")
+                    ||($job_header eq "trigger_action_lock")
+                    ||($job_header eq "trigger_action_activate")
+                    ||($job_header eq "trigger_action_halt") 
+                    ||($job_header eq "trigger_action_reboot")
+                    ||($job_header eq "trigger_action_wake")
+                    ||($job_header eq "trigger_wake")
+                    ) {
+
+                my $sql_statement = "UPDATE $job_queue_tn ".
+                    "SET status='done' ".
+                    "WHERE id=$jobdb_id AND status='processed'";
+                my $res = $job_db->update_dbentry($sql_statement);
+            } else { 
+                &daemon_log("$session_id DEBUG: sending message succeed but cannot update job status.", 138); 
+            } 
+        } 
+    } else { 
+        &daemon_log("$session_id DEBUG: cannot update job status, msg has no jobdb_id-tag.", 138); 
     }
 }
 
+sub reactivate_job_with_delay {
+    my ($session_id, $target, $header, $delay) = @_ ;
+    # Sometimes the client is still booting or does not wake up, in this case reactivate the job (if it exists) with a delay of n sec
+    
+    if (not defined $delay) { $delay = 30 } ;
+    my $delay_timestamp = &calc_timestamp(&get_time(), "plus", $delay);
+
+    my $sql = "UPDATE $job_queue_tn Set timestamp='$delay_timestamp', status='waiting' WHERE (macaddress LIKE '$target' OR plainname LIKE '$target') AND headertag='$header'"; 
+    my $res = $job_db->update_dbentry($sql);
+    daemon_log("$session_id INFO: '$header'-job will be reactivated at '$delay_timestamp' ".
+            "cause client '$target' is currently not available", 5);
+    return;
+}
+
 
 sub sig_handler {
        my ($kernel, $signal) = @_[KERNEL, ARG0] ;
@@ -1019,247 +1206,340 @@ sub sig_handler {
 
 
 sub msg_to_decrypt {
-    my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
-    my $session_id = $session->ID;
-    my ($msg, $msg_hash, $module);
-    my $error = 0;
+       my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
+       my $session_id = $session->ID;
+       my ($msg, $msg_hash, $module);
+       my $error = 0;
+
+       # fetch new msg out of @msgs_to_decrypt
+       my $tmp_next_msg = shift @msgs_to_decrypt;
+    my ($next_msg, $msg_source) = split(/;/, $tmp_next_msg);
+
+       # msg is from a new client or gosa
+       ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
+
+       # msg is from a gosa-si-server
+       if(((!$msg) || (!$msg_hash) || (!$module)) && ($serverPackages_enabled eq "true")){
+               if (not defined $msg_source) 
+               {
+                       # Only needed, to be compatible with older gosa-si-server versions
+                       ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
+               }
+               else
+               {
+                       ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $msg_source, $session_id);
+               }
+       }
+       # msg is from a gosa-si-client
+       if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
+               if (not defined $msg_source) 
+               {
+                       # Only needed, to be compatible with older gosa-si-server versions
+                       ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
+               }
+               else
+               {
+                       ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $msg_source, $session_id);
+               }
+       }
+       # an error occurred
+       if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
+               # If an incoming msg could not be decrypted (maybe a wrong key), decide if msg comes from a client 
+               # or a server.  In case of a client, send a ping. If the client could not understand a msg from its 
+               # server the client cause a re-registering process. In case of a server, decrease update_time in kown_server_db
+               # and trigger a re-registering process for servers
+               if (defined $msg_source && $msg_source =~ /:$server_port$/ && $serverPackages_enabled eq "true")
+               {
+                       daemon_log("$session_id WARNING: Cannot understand incoming msg from server '$msg_source'. Cause re-registration process for servers.", 3);
+                       my $update_statement = "UPDATE $known_server_tn SET update_time='19700101000000' WHERE hostname='$msg_source'"; 
+                       daemon_log("$session_id DEBUG: $update_statement", 7);
+                       my $upadte_res = $known_server_db->exec_statement($update_statement);
+                       $kernel->yield("register_at_foreign_servers");
+               }
+               elsif ((defined $msg_source) && (not $msg_source =~ /:$server_port$/))
+               {
+                       daemon_log("$session_id WARNING: Cannot understand incoming msg from client '$msg_source'. Send ping-msg to cause a re-registering of the client if necessary", 3);
+                       #my $remote_ip = $heap->{'remote_ip'};
+                       #my $remote_port = $heap->{'remote_port'};
+                       my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source><target>$msg_source</target></xml>";
+                       my ($test_error, $test_error_string) = &send_msg_to_target($ping_msg, "$msg_source", "dummy-key", "gosa_ping", $session_id);
+                       daemon_log("$session_id WARNING: Sending msg to cause re-registering: $ping_msg", 3);
+               }
+               else
+               {
+                       my $foreign_host = defined $msg_source ? $msg_source : $heap->{'remote_ip'};
+                       daemon_log("$session_id ERROR: Incoming message from host '$foreign_host' cannot be understood. Processing aborted!", 1);
+                       daemon_log("$session_id DEBUG: Aborted message: $tmp_next_msg", 11);
+               }
 
-    # hole neue msg aus @msgs_to_decrypt
-    my $next_msg = shift @msgs_to_decrypt;
-    
-    # entschlüssle sie
+               $error++
+       }
 
-    # msg is from a new client or gosa
-    ($msg, $msg_hash, $module) = &input_from_unknown_host($next_msg, $session_id);
-    # msg is from a gosa-si-server
-    if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
-        ($msg, $msg_hash, $module) = &input_from_known_server($next_msg, $heap->{'remote_ip'}, $session_id);
-    }
-    # msg is from a gosa-si-client
-    if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
-        ($msg, $msg_hash, $module) = &input_from_known_client($next_msg, $heap->{'remote_ip'}, $session_id);
-    }
-    # an error occurred
-    if(( !$msg ) || ( !$msg_hash ) || ( !$module )){
-        # if an incoming msg could not be decrypted (maybe a wrong key), send client a ping. If the client
-        # could not understand a msg from its server the client cause a re-registering process
-        daemon_log("$session_id WARNING 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", 3);
-        my $sql_statement = "SELECT * FROM $main::known_clients_tn WHERE (hostname LIKE '".$heap->{'remote_ip'}."%')";
-        my $query_res = $known_clients_db->select_dbentry( $sql_statement ); 
-        while( my ($hit_num, $hit) = each %{ $query_res } ) {    
-            my $host_name = $hit->{'hostname'};
-            my $host_key = $hit->{'hostkey'};
-            my $ping_msg = "<xml> <header>gosa_ping</header> <source>$server_address</source> <target>$host_name</target></xml>";
-            my $error = &send_msg_to_target($ping_msg, $host_name, $host_key, "gosa_ping", $session_id);
-            &update_jobdb_status_for_send_msgs($ping_msg, $error);
-        }
-        $error++;
-    }
 
+       my $header;
+       my $target;
+       my $source;
+       my $done = 0;
+       my $sql;
+       my $res;
 
-    my $header;
-    my $target;
-    my $source;
-    my $done = 0;
-    my $sql;
-    my $res;
-
-    # check whether this message should be processed here
-    if ($error == 0) {
-        $header = @{$msg_hash->{'header'}}[0];
-        $target = @{$msg_hash->{'target'}}[0];
-        $source = @{$msg_hash->{'source'}}[0];
+       # check whether this message should be processed here
+       if ($error == 0) {
+               $header = @{$msg_hash->{'header'}}[0];
+               $target = @{$msg_hash->{'target'}}[0];
+               $source = @{$msg_hash->{'source'}}[0];
                my $not_found_in_known_clients_db = 0;
                my $not_found_in_known_server_db = 0;
                my $not_found_in_foreign_clients_db = 0;
-        my $local_address;
-        my ($target_ip, $target_port) = split(':', $target);
+               my $local_address;
+               my $local_mac;
+               my ($target_ip, $target_port) = split(':', $target);
+
+               # Determine the local ip address if target is an ip address
                if ($target =~ /^\d+\.\d+\.\d+\.\d+:\d+$/) {
                        $local_address = &get_local_ip_for_remote_ip($target_ip).":$server_port";
                } else {
-            $local_address = $server_address;
-        }
+                       $local_address = $server_address;
+               }
 
-        # target and source is equal to GOSA -> process here
-        if (not $done) {
-            if ($target eq "GOSA" && $source eq "GOSA") {
-                $done = 1;                    
-            }
-        }
+               # Determine the local mac address if target is a mac address
+               if ($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) {
+                       my $loc_ip = &get_local_ip_for_remote_ip($heap->{'remote_ip'});
+                       my $network_interface= &get_interface_for_ip($loc_ip);
+                       $local_mac = &get_mac_for_interface($network_interface);
+               } else {
+                       $local_mac = $server_mac_address;
+               }
 
-        # target is own address without forward_to_gosa-tag -> process here
-        if (not $done) {
-            if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
-                $done = 1;
-                if ($source eq "GOSA") {
-                    $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
-                }
-                #print STDERR "target is own address without forward_to_gosa-tag -> process here\n";
-            }
-        }
+               # target and source is equal to GOSA -> process here
+               if (not $done) {
+                       if ($target eq "GOSA" && $source eq "GOSA") {
+                               $done = 1;                    
+                               &daemon_log("$session_id DEBUG: target and source is 'GOSA' -> process '$header' here", 11);
+                       }
+               }
 
-        # target is a client address in known_clients -> process here
+               # target is own address without forward_to_gosa-tag -> process here
                if (not $done) {
-                               $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
-                               $res = $known_clients_db->select_dbentry($sql);
-                               if (keys(%$res) > 0) {
-                                               $done = 1; 
-                                               my $hostname = $res->{1}->{'hostname'};
-                                               $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
-                                               #print STDERR "target is a client address in known_clients -> process here\n";
-                               } else {
-                                               $not_found_in_known_clients_db = 1;
+                       #if ((($target eq $local_address) || ($target eq $local_mac) ) && (not exists $msg_hash->{'forward_to_gosa'})) {
+                       if (($target eq $local_address) && (not exists $msg_hash->{'forward_to_gosa'})) {
+                               $done = 1;
+                               if ($source eq "GOSA") {
+                                       $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
                                }
+                               &daemon_log("$session_id DEBUG: target is own address without forward_to_gosa-tag -> process '$header' here", 11);
+                       }
                }
-       
-        # target ist own address with forward_to_gosa-tag not pointing to myself -> process here
-        if (not $done) {
-            my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
-            my $gosa_at;
-            my $gosa_session_id;
-            if (($target eq $local_address) && (defined $forward_to_gosa)){
-                my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
-                if ($gosa_at ne $local_address) {
-                    $done = 1;
-                    #print STDERR "target is own address with forward_to_gosa-tag not pointing to myself -> process here\n"; 
-                }
-            }
-        }
 
-        # if message should be processed here -> add message to incoming_db
-               if ($done) {
-                               # if a job or a gosa message comes from a foreign server, fake module to GosaPackages
-                               # so gosa-si-server knows how to process this kind of messages
-                               if ($header =~ /^gosa_/ || $header =~ /^job_/) {
-                                               $module = "GosaPackages";
+               # target is own address with forward_to_gosa-tag not pointing to myself -> process here
+               if (not $done) {
+                       my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
+                       my $gosa_at;
+                       my $gosa_session_id;
+                       if (($target eq $local_address) && (defined $forward_to_gosa)){
+                               my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
+                               if ($gosa_at ne $local_address) {
+                                       $done = 1;
+                                       &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag not pointing to myself -> process '$header' here", 11); 
                                }
+                       }
+               }
 
-                               my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
-                                                               primkey=>[],
-                                                               headertag=>$header,
-                                                               targettag=>$target,
-                                                               xmlmessage=>&encode_base64($msg),
-                                                               timestamp=>&get_time,
-                                                               module=>$module,
-                                                               sessionid=>$session_id,
-                                                               } );
+               # Target is a client address and there is a processing function within a plugin -> process loaclly
+               if (not $done)
+               {
+                       # Check if target is a client address
+                       $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
+                       $res = $known_clients_db->select_dbentry($sql);
+                       if ((keys(%$res) > 0) ) 
+                       {
+                               my $hostname = $res->{1}->{'hostname'};
+                               my $reduced_header = $header;
+                               $reduced_header =~ s/gosa_//;
+                               # Check if there is a processing function within a plugin
+                               if (exists $known_functions->{$reduced_header}) 
+                               {
+                                       $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
+                                       $done = 1;
+                                       &daemon_log("$session_id DEBUG: Target is client address with processing function within a plugin -> process '$header' here", 11);
+                               }
+                       }
                }
 
-        # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
-        if (not $done) {
-            my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
-            my $gosa_at;
-            my $gosa_session_id;
-            if (($target eq $local_address) && (defined $forward_to_gosa)){
-                my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
-                if ($gosa_at eq $local_address) {
-                    my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
-                    if( defined $session_reference ) {
-                        $heap = $session_reference->get_heap();
-                    }
-                    if(exists $heap->{'client'}) {
-                        $msg = &encrypt_msg($msg, $GosaPackages_key);
-                        $heap->{'client'}->put($msg);
-                    }
-                    $done = 1;
-                    #print STDERR "target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa\n";
-                }
-            }
+               # If header has a 'job_' prefix, do always process message locally
+               # which means put it into job queue
+               if ((not $done) && ($header =~ /job_/))
+               {
+                       $done = 1;
+                       &daemon_log("$session_id DEBUG: Header has a 'job_' prefix. Put it into job queue. -> process '$header' here", 11);
+               }
 
-        }
+               # if message should be processed here -> add message to incoming_db
+               if ($done) {
+                       # if a 'job_' or a 'gosa_' message comes from a foreign server, fake module from
+                       # ServerPackages to GosaPackages so gosa-si-server knows how to process this kind of messages
+                       if ($header =~ /^gosa_/ || $header =~ /^job_/) {
+                               $module = "GosaPackages";
+                       }
 
-        # target is a client address in foreign_clients -> forward to registration server
-        if (not $done) {
-            $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
-            $res = $foreign_clients_db->select_dbentry($sql);
-            if (keys(%$res) > 0) {
-                   my $hostname = $res->{1}->{'hostname'};
-                   my ($host_ip, $host_port) = split(/:/, $hostname);
-                   my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
-                my $regserver = $res->{1}->{'regserver'};
-                my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
-                my $res = $known_server_db->select_dbentry($sql);
-                if (keys(%$res) > 0) {
-                    my $regserver_key = $res->{1}->{'hostkey'};
-                    $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
-                    $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
-                    if ($source eq "GOSA") {
-                        $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
-                    }
-                    &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
-                }
-                $done = 1;
-                #print STDERR "target is a client address in foreign_clients -> forward to registration server\n";
-            } else {
+                       my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
+                                       primkey=>[],
+                                       headertag=>$header,
+                                       targettag=>$target,
+                                       xmlmessage=>&encode_base64($msg),
+                                       timestamp=>&get_time,
+                                       module=>$module,
+                                       sessionid=>$session_id,
+                               } );
+                       $kernel->yield('watch_for_next_tasks');
+               }
+
+               # target is own address with forward_to_gosa-tag pointing at myself -> forward to gosa
+               if (not $done) {
+                       my $forward_to_gosa =  @{$msg_hash->{'forward_to_gosa'}}[0];
+                       my $gosa_at;
+                       my $gosa_session_id;
+                       if (($target eq $local_address) && (defined $forward_to_gosa)){
+                               my ($gosa_at, $gosa_session_id) = split(/,/, $forward_to_gosa);
+                               if ($gosa_at eq $local_address) {
+                                       my $session_reference = $kernel->ID_id_to_session($gosa_session_id);
+                                       if( defined $session_reference ) {
+                                               $heap = $session_reference->get_heap();
+                                       }
+                                       if(exists $heap->{'client'}) {
+                                               $msg = &encrypt_msg($msg, $GosaPackages_key);
+                                               $heap->{'client'}->put($msg);
+                                               &daemon_log("$session_id DEBUG: incoming '$header' message forwarded to GOsa", 11); 
+                                       }
+                                       $done = 1;
+                                       &daemon_log("$session_id DEBUG: target is own address with forward_to_gosa-tag pointing at myself -> forward '$header' to gosa", 11);
+                               }
+                       }
+
+               }
+
+               # target is a client address in known_clients -> forward to client
+               if (not $done) {
+                       $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')"; 
+                       $res = $known_clients_db->select_dbentry($sql);
+                       if (keys(%$res) > 0) 
+                       {
+                               $done = 1; 
+                               &daemon_log("$session_id DEBUG: target is a client address in known_clients -> forward '$header' to client", 11);
+                               my $hostkey = $res->{1}->{'hostkey'};
+                               my $hostname = $res->{1}->{'hostname'};
+                               $msg =~ s/<target>\S*<\/target>/<target>$hostname<\/target>/;
+                               $msg =~ s/<header>gosa_/<header>/;
+                               my $error= &send_msg_to_target($msg, $hostname, $hostkey, $header, $session_id);
+                               if ($error) {
+                                       &daemon_log("$session_id ERROR: Some problems occurred while trying to send msg to client '$hostname': $msg", 1);
+                               }
+                       } 
+                       else 
+                       {
+                               $not_found_in_known_clients_db = 1;
+                       }
+               }
+
+               # target is a client address in foreign_clients -> forward to registration server
+               if (not $done) {
+                       $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
+                       $res = $foreign_clients_db->select_dbentry($sql);
+                       if (keys(%$res) > 0) {
+                               my $hostname = $res->{1}->{'hostname'};
+                               my ($host_ip, $host_port) = split(/:/, $hostname);
+                               my $local_address =  &get_local_ip_for_remote_ip($host_ip).":$server_port";
+                               my $regserver = $res->{1}->{'regserver'};
+                               my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$regserver'"; 
+                               my $res = $known_server_db->select_dbentry($sql);
+                               if (keys(%$res) > 0) {
+                                       my $regserver_key = $res->{1}->{'hostkey'};
+                                       $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
+                                       $msg =~ s/<target>$target<\/target>/<target>$hostname<\/target>/;
+                                       if ($source eq "GOSA") {
+                                               $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
+                                       }
+                                       my $error= &send_msg_to_target($msg, $regserver, $regserver_key, $header, $session_id);
+                                       if ($error) {
+                                               &daemon_log("$session_id ERROR: some problems occurred while trying to send msg to registration server: $msg", 1); 
+                                       }
+                               }
+                               $done = 1;
+                               &daemon_log("$session_id DEBUG: target is a client address in foreign_clients -> forward '$header' to registration server", 11);
+                       } else {
                                $not_found_in_foreign_clients_db = 1;
                        }
-        }
+               }
 
-        # target is a server address -> forward to server
-        if (not $done) {
-            $sql = "SELECT * FROM $known_server_tn WHERE hostname='$target'";
-            $res = $known_server_db->select_dbentry($sql);
-            if (keys(%$res) > 0) {
-                my $hostkey = $res->{1}->{'hostkey'};
+               # target is a server address -> forward to server
+               if (not $done) {
+                       $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$target' OR macaddress LIKE '$target')";
+                       $res = $known_server_db->select_dbentry($sql);
+                       if (keys(%$res) > 0) {
+                               my $hostkey = $res->{1}->{'hostkey'};
 
-                if ($source eq "GOSA") {
-                    $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
-                    $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
+                               if ($source eq "GOSA") {
+                                       $msg =~ s/<source>GOSA<\/source>/<source>$local_address<\/source>/;
+                                       $msg =~ s/<\/xml>/<forward_to_gosa>$local_address,$session_id<\/forward_to_gosa><\/xml>/;
 
-                }
+                               }
 
-                &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
-                $done = 1;
-                #print STDERR "target is a server address -> forward to server\n";
-            } else {
+                               &send_msg_to_target($msg, $target, $hostkey, $header, $session_id);
+                               $done = 1;
+                               &daemon_log("$session_id DEBUG: target is a server address -> forward '$header' to server", 11);
+                       } else {
                                $not_found_in_known_server_db = 1;
                        }
-        }
+               }
+
 
-               
                # target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process here
                if ( $not_found_in_foreign_clients_db 
-                                               && $not_found_in_known_server_db
-                                               && $not_found_in_known_clients_db) {
-                               my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
-                                                               primkey=>[],
-                                                               headertag=>$header,
-                                                               targettag=>$target,
-                                                               xmlmessage=>&encode_base64($msg),
-                                                               timestamp=>&get_time,
-                                                               module=>$module,
-                                                               sessionid=>$session_id,
-                                                               } );
-                               $done = 1;
+                       && $not_found_in_known_server_db
+                       && $not_found_in_known_clients_db) {
+                       &daemon_log("$session_id DEBUG: target is not in foreign_clients_db, known_server_db or known_clients_db, maybe it is a complete new one -> process '$header' here", 11);
+            if ($header =~ /^gosa_/ || $header =~ /^job_/) { 
+                $module = "GosaPackages"; 
+            }
+                       my $res = $incoming_db->add_dbentry( {table=>$incoming_tn,
+                                       primkey=>[],
+                                       headertag=>$header,
+                                       targettag=>$target,
+                                       xmlmessage=>&encode_base64($msg),
+                                       timestamp=>&get_time,
+                                       module=>$module,
+                                       sessionid=>$session_id,
+                               } );
+                       $done = 1;
+                       $kernel->yield('watch_for_next_tasks');
                }
 
 
-        if (not $done) {
-            daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
-            if ($source eq "GOSA") {
-                my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
-                my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
-
-                my $session_reference = $kernel->ID_id_to_session($session_id);
-                if( defined $session_reference ) {
-                    $heap = $session_reference->get_heap();
-                }
-                if(exists $heap->{'client'}) {
-                    $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
-                    $heap->{'client'}->put($error_msg);
-                }
-            }
-        }
+               if (not $done) {
+                       daemon_log("$session_id ERROR: do not know what to do with this message: $msg", 1);
+                       if ($source eq "GOSA") {
+                               my %data = ('error_msg' => &encode_base64($msg), 'error_string' => "Do not know what to do with this message!");
+                               my $error_msg = &build_msg("error", $local_address, "GOSA", \%data ); 
+
+                               my $session_reference = $kernel->ID_id_to_session($session_id);
+                               if( defined $session_reference ) {
+                                       $heap = $session_reference->get_heap();
+                               }
+                               if(exists $heap->{'client'}) {
+                                       $error_msg = &encrypt_msg($error_msg, $GosaPackages_key);
+                                       $heap->{'client'}->put($error_msg);
+                               }
+                       }
+               }
 
-    }
+       }
 
-    return;
+       return;
 }
 
 
 sub next_task {
-    my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0];
+    my ($session, $heap, $task) = @_[SESSION, HEAP, ARG0, ARG1];
     my $running_task = POE::Wheel::Run->new(
             Program => sub { process_task($session, $heap, $task) },
             StdioFilter => POE::Filter::Reference->new(),
@@ -1297,6 +1577,9 @@ sub handle_task_debug {
 sub handle_task_done {
     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
     delete $heap->{task}->{$task_id};
+       if (exists $heap->{ldap_handle}->{$task_id}) {
+               &release_ldap_handle($heap->{ldap_handle}->{$task_id});
+       }
 }
 
 sub process_task {
@@ -1315,30 +1598,35 @@ sub process_task {
     my $module = $task->{'module'};
     my $header =  $task->{'headertag'};
     my $session_id = $task->{'sessionid'};
-    my $msg_hash = $xml->XMLin($msg, ForceArray=>1);
+               my $msg_hash;
+               eval {
+       $msg_hash = $xml->XMLin($msg, ForceArray=>1);
+               }; 
+               daemon_log("ERROR: XML failure '$@'") if ($@);
     my $source = @{$msg_hash->{'source'}}[0];
     
     # set timestamp of incoming client uptodate, so client will not 
     # be deleted from known_clients because of expiration
-    my $act_time = &get_time();
-    my $sql = "UPDATE $known_clients_tn SET timestamp='$act_time' WHERE hostname='$source'"; 
+    my $cur_time = &get_time();
+    my $sql = "UPDATE $known_clients_tn SET timestamp='$cur_time' WHERE hostname='$source'"; 
     my $res = $known_clients_db->exec_statement($sql);
 
     ######################
     # process incoming msg
     if( $error == 0) {
         daemon_log("$session_id INFO: Incoming msg (session_id=$session_id) with header '".@{$msg_hash->{'header'}}[0]."'", 5); 
-        daemon_log("$session_id DEBUG: Processing module ".$module, 7);
+        daemon_log("$session_id DEBUG: Processing module ".$module, 26);
         $answer_l = &{ $module."::process_incoming_msg" }($msg, $msg_hash, $session_id);
 
         if ( 0 < @{$answer_l} ) {
             my $answer_str = join("\n", @{$answer_l});
+                       my @headers; 
             while ($answer_str =~ /<header>(\w+)<\/header>/g) {
-                daemon_log("$session_id INFO: got answer message with header '$1'", 5);
+                               push(@headers, $1);
             }
-            daemon_log("$session_id DEBUG: $module: got answer from module: \n".$answer_str,8);
+                       daemon_log("$session_id DEBUG: got answer message(s) with header: '".join("', '", @headers)."'", 26);
         } else {
-            daemon_log("$session_id DEBUG: $module: got no answer from module!" ,8);
+            daemon_log("$session_id DEBUG: $module: got no answer from module!" ,26);
         }
 
     }
@@ -1350,7 +1638,7 @@ sub process_task {
 
         foreach my $answer ( @{$answer_l} ) {
             # check outgoing msg to xml validity
-            my $answer_hash = &check_outgoing_xml_validity($answer, $session_id);
+            my ($answer, $answer_hash) = &check_outgoing_xml_validity($answer, $session_id);
             if( not defined $answer_hash ) { next; }
             
             $answer_header = @{$answer_hash->{'header'}}[0];
@@ -1369,7 +1657,7 @@ sub process_task {
                         my $host_name = $hit->{hostname};
                         my $host_key = $hit->{hostkey};
                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
-                        &update_jobdb_status_for_send_msgs($answer, $error);
+                        &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
                     }
                 }
 
@@ -1383,7 +1671,7 @@ sub process_task {
                         my $host_key = $hit->{hostkey};
                         $answer =~ s/<target>\S+<\/target>/<target>$host_name<\/target>/g;
                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
-                        &update_jobdb_status_for_send_msgs($answer, $error);
+                        &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
                     }
                 }
 
@@ -1394,6 +1682,9 @@ sub process_task {
                     if( defined $session_id ) {
                         $add_on = ".session_id=$session_id";
                     }
+                                       my $header = ($1) if $answer =~ /<header>(\S*)<\/header>/;
+                                       daemon_log("$session_id INFO: send ".$header." message to GOsa", 5);
+                                       daemon_log("$session_id DEBUG: message:\n$answer", 12);
                     # answer is for GOSA and has to returned to connected client
                     my $gosa_answer = &encrypt_msg($answer, $GosaPackages_key);
                     $client_answer = $gosa_answer.$add_on;
@@ -1405,12 +1696,14 @@ sub process_task {
                     my $header;
                     if( defined $1 ) { $header = $1; }
                     my $error = &send_msg_to_target($answer, $server_address, $GosaPackages_key, $header, $session_id);
-                    &update_jobdb_status_for_send_msgs($answer, $error);
+                    &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
                 }
 
-                # target of msg is a mac address
+                # Target of msg is a mac address
                 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 ) {
-                    daemon_log("$session_id INFO: target is mac address '$answer_target', looking for host in known_clients", 5);
+                    daemon_log("$session_id DEBUG: target is mac address '$answer_target', looking for host in known_clients and foreign_clients", 138);
+
+                    # Looking for macaddress in known_clients
                     my $sql_statement = "SELECT * FROM known_clients WHERE macaddress LIKE '$answer_target'";
                     my $query_res = $known_clients_db->select_dbentry( $sql_statement );
                     my $found_ip_flag = 0;
@@ -1420,14 +1713,48 @@ sub process_task {
                         $answer =~ s/$answer_target/$host_name/g;
                         daemon_log("$session_id INFO: found host '$host_name', associated to '$answer_target'", 5);
                         my $error = &send_msg_to_target($answer, $host_name, $host_key, $answer_header, $session_id);
-                        &update_jobdb_status_for_send_msgs($answer, $error);
+                        &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
                         $found_ip_flag++ ;
                     }   
+
+                    # Looking for macaddress in foreign_clients
+                    if ($found_ip_flag == 0) {
+                        my $sql = "SELECT * FROM $foreign_clients_tn WHERE macaddress LIKE '$answer_target'";
+                        my $res = $foreign_clients_db->select_dbentry($sql);
+                        while( my ($hit_num, $hit) = each %{ $res } ) {
+                            my $host_name = $hit->{hostname};
+                            my $reg_server = $hit->{regserver};
+                            daemon_log("$session_id INFO: found host '$host_name' with mac '$answer_target', registered at '$reg_server'", 5);
+                            
+                            # Fetch key for reg_server
+                            my $reg_server_key;
+                            my $sql = "SELECT * FROM $known_server_tn WHERE hostname='$reg_server'";
+                            my $res = $known_server_db->select_dbentry($sql);
+                            if (exists $res->{1}) {
+                                $reg_server_key = $res->{1}->{'hostkey'}; 
+                            } else {
+                                daemon_log("$session_id ERROR: cannot find hostkey for '$host_name' in '$known_server_tn'", 1); 
+                                daemon_log("$session_id ERROR: unable to forward answer to correct registration server, processing is aborted!", 1); 
+                                $reg_server_key = undef;
+                            }
+
+                            # Send answer to server where client is registered
+                            if (defined $reg_server_key) {
+                                $answer =~ s/$answer_target/$host_name/g;
+                                my $error = &send_msg_to_target($answer, $reg_server, $reg_server_key, $answer_header, $session_id);
+                                &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
+                                $found_ip_flag++ ;
+                            }
+                        }
+                    }
+
+                    # No mac to ip matching found
                     if( $found_ip_flag == 0) {
-                        daemon_log("$session_id WARNING: no host found in known_clients with mac address '$answer_target'", 3);
+                        daemon_log("$session_id WARNING: no host found in known_clients or foreign_clients with mac address '$answer_target'", 3);
+                        &reactivate_job_with_delay($session_id, $answer_target, $answer_header, 30);
                     }
 
-                #  answer is for one specific host   
+                # Answer is for one specific host   
                 } else {
                     # get encrypt_key
                     my $encrypt_key = &get_encrypt_key($answer_target);
@@ -1437,7 +1764,7 @@ sub process_task {
                         next;
                     }
                     my $error = &send_msg_to_target($answer, $answer_target, $encrypt_key, $answer_header,$session_id);
-                    &update_jobdb_status_for_send_msgs($answer, $error);
+                    &update_jobdb_status_for_send_msgs($session_id, $answer, $error);
                 }
             }
         }
@@ -1456,49 +1783,176 @@ sub process_task {
 }
 
 sub session_start {
-    my ($kernel) = $_[KERNEL];
-    $global_kernel = $kernel;
-    $kernel->yield('register_at_foreign_servers');
+       my ($kernel) = $_[KERNEL];
+       $global_kernel = $kernel;
+       $kernel->yield('register_at_foreign_servers');
        $kernel->yield('create_fai_server_db', $fai_server_tn );
        $kernel->yield('create_fai_release_db', $fai_release_tn );
-    $kernel->yield('watch_for_next_tasks');
        $kernel->sig(USR1 => "sig_handler");
        $kernel->sig(USR2 => "recreate_packages_db");
        $kernel->delay_set('watch_for_new_jobs', $job_queue_loop_delay);
        $kernel->delay_set('watch_for_done_jobs', $job_queue_loop_delay); 
-    $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay); 
+       $kernel->delay_set('watch_for_modified_jobs', $modified_jobs_loop_delay);
        $kernel->delay_set('watch_for_new_messages', $messaging_db_loop_delay);
-    $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
+       $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay);
        $kernel->delay_set('watch_for_done_messages', $messaging_db_loop_delay);
-    $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
+       $kernel->delay_set('watch_for_old_known_clients', $job_queue_loop_delay);
 
+       # Start opsi check
+       if ($opsi_enabled eq "true") {
+               $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
+       }
 
 }
 
 
 sub watch_for_done_jobs {
-    #CHECK: $heap for what?
-    my ($kernel,$heap) = @_[KERNEL, HEAP];
+       my $kernel = $_[KERNEL];
+
+       my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
+       my $res = $job_db->select_dbentry( $sql_statement );
+
+       while( my ($number, $hit) = each %{$res} ) 
+       {
+               # Non periodical jobs can be deleted.
+               if ($hit->{periodic} eq "none")
+               {
+                       my $jobdb_id = $hit->{id};
+                       my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
+                       my $res = $job_db->del_dbentry($sql_statement); 
+               }
+
+               # Periodical jobs should not be deleted but reactivated with new timestamp instead.
+               else
+               {
+                       my ($p_time, $periodic) = split("_", $hit->{periodic});
+                       my $reactivated_ts = $hit->{timestamp};
+                       my $act_ts = int(&get_time());
+                       while ($act_ts > int($reactivated_ts))   # Redo calculation to avoid multiple jobs in the past
+                       {
+                               $reactivated_ts = &calc_timestamp($reactivated_ts, "plus", $p_time, $periodic);
+                       }
+                       my $sql = "UPDATE $job_queue_tn SET status='waiting', timestamp='$reactivated_ts' WHERE id='".$hit->{id}."'"; 
+                       my $res = $job_db->exec_statement($sql);
+                       &daemon_log("J INFO: Update periodical job '".$hit->{headertag}."' for client '".$hit->{targettag}."'. New execution time '$reactivated_ts'.", 5);
+               }
+       }
+
+       $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
+}
+
+
+sub watch_for_opsi_jobs {
+    my ($kernel) = $_[KERNEL];
+    # This is not very nice to look for opsi install jobs, but headertag has to be trigger_action_reinstall. The only way to identify a 
+    # opsi install job is to parse the xml message. There is still the correct header.
+    my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((xmlmessage LIKE '%opsi_install_client</header>%') AND (status='processing') AND (siserver='localhost'))";
+       my $res = $job_db->select_dbentry( $sql_statement );
+
+    # Ask OPSI for an update of the running jobs
+    while (my ($id, $hit) = each %$res ) {
+        # Determine current parameters of the job
+        my $hostId = $hit->{'plainname'};
+        my $macaddress = $hit->{'macaddress'};
+        my $progress = $hit->{'progress'};
+
+        my $result= {};
+        
+        # For hosts, only return the products that are or get installed
+        my $callobj;
+        $callobj = {
+            method  => 'getProductStates_hash',
+            params  => [ $hostId ],
+            id  => 1,
+        };
+        
+        my $hres = $opsi_client->call($opsi_url, $callobj);
+               # TODO : move all opsi relevant statments to opsi plugin
+               # The following 3 lines must be tested befor they can replace the rubbish above and below !!!
+               #my ($res, $err) = &opsi_com::_getProductStates_hash(hostId=>$hostId)
+               #if (not $err) {
+               #       my $htmp = $res->{$hostId};
+               # 
+        if (not &check_opsi_res($hres)) {
+            my $htmp= $hres->result->{$hostId};
+            # Check state != not_installed or action == setup -> load and add
+            my $products= 0;
+            my $installed= 0;
+            my $installing = 0;
+            my $error= 0;  
+            my @installed_list;
+            my @error_list;
+            my $act_status = "none";
+            foreach my $product (@{$htmp}){
+
+                if ($product->{'installationStatus'} ne "not_installed" or
+                        $product->{'actionRequest'} eq "setup"){
+
+                    # Increase number of products for this host
+                    $products++;
+        
+                    if ($product->{'installationStatus'} eq "failed"){
+                        $result->{$product->{'productId'}}= "error";
+                        unshift(@error_list, $product->{'productId'});
+                        $error++;
+                    }
+                    if ($product->{'installationStatus'} eq "installed" && $product->{'actionRequest'}  eq "none"){
+                        $result->{$product->{'productId'}}= "installed";
+                        unshift(@installed_list, $product->{'productId'});
+                        $installed++;
+                    }
+                    if ($product->{'installationStatus'} eq "installing"){
+                        $result->{$product->{'productId'}}= "installing";
+                        $installing++;
+                        $act_status = "installing - ".$product->{'productId'};
+                    }
+                }
+            }
+        
+            # Estimate "rough" progress, avoid division by zero
+            if ($products == 0) {
+                $result->{'progress'}= 0;
+            } else {
+                $result->{'progress'}= int($installed * 100 / $products);
+            }
+
+            # Set updates in job queue
+            if ((not $error) && (not $installing) && ($installed)) {
+                $act_status = "installed - ".join(", ", @installed_list);
+            }
+            if ($error) {
+                $act_status = "error - ".join(", ", @error_list);
+            }
+            if ($progress ne $result->{'progress'} ) {
+                # Updating progress and result 
+                my $update_statement = "UPDATE $job_queue_tn SET modified='1', progress='".$result->{'progress'}."', result='$act_status' WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
+                my $update_res = $job_db->update_dbentry($update_statement);
+            }
+            if ($progress eq 100) { 
+                # Updateing status
+                my $done_statement = "UPDATE $job_queue_tn SET modified='1', ";
+                if ($error) {
+                    $done_statement .= "status='error'";
+                } else {
+                    $done_statement .= "status='done'";
+                }
+                $done_statement .= " WHERE macaddress LIKE '$macaddress' AND siserver='localhost'";
+                my $done_res = $job_db->update_dbentry($done_statement);
+            }
 
-    my $sql_statement = "SELECT * FROM ".$job_queue_tn." WHERE ((status='done') AND (modified='0'))";
-       my $res = $job_db->select_dbentry( $sql_statement );
 
-    while( my ($id, $hit) = each %{$res} ) {
-        my $jobdb_id = $hit->{id};
-        my $sql_statement = "DELETE FROM $job_queue_tn WHERE id=$jobdb_id"; 
-        my $res = $job_db->del_dbentry($sql_statement);        
+        }
     }
 
-    $kernel->delay_set('watch_for_done_jobs',$job_queue_loop_delay);
+    $kernel->delay_set('watch_for_opsi_jobs', $job_queue_opsi_delay);
 }
 
 
-# if a job got an update or was modified anyway, send to all other si-server an update message
-# of this jobs
+# If a job got an update or was modified anyway, send to all other si-server an update message of this jobs.
 sub watch_for_modified_jobs {
     my ($kernel,$heap) = @_[KERNEL, HEAP];
 
-    my $sql_statement = "SELECT * FROM $job_queue_tn WHERE ((siserver='localhost') AND (modified='1'))"; 
+    my $sql_statement = "SELECT * FROM $job_queue_tn WHERE (modified='1')"; 
     my $res = $job_db->select_dbentry( $sql_statement );
     
     # if db contains no jobs which should be update, do nothing
@@ -1526,9 +1980,9 @@ sub watch_for_new_jobs {
                $watch_for_new_jobs_in_progress = 1;
                my ($kernel,$heap) = @_[KERNEL, HEAP];
 
-               # check gosa job quaeue for jobs with executable timestamp
+               # check gosa job queue for jobs with executable timestamp
                my $timestamp = &get_time();
-               my $sql_statement = "SELECT * FROM $job_queue_tn WHERE status='waiting' AND (CAST (timestamp AS INTEGER)) < $timestamp ORDER BY timestamp";
+               my $sql_statement = "SELECT * FROM $job_queue_tn WHERE siserver='localhost' AND status='waiting' AND (CAST(timestamp AS UNSIGNED)) < $timestamp ORDER BY timestamp";
                my $res = $job_db->exec_statement( $sql_statement );
 
                # Merge all new jobs that would do the same actions
@@ -1575,6 +2029,16 @@ sub watch_for_new_jobs {
 
                        # Skip new jobs for host if there is a processing job
                        if(defined($res) and defined @{$res}[0]) {
+                               # Prevent race condition if there is a trigger_activate job waiting and a goto-activation job processing
+                               my $row = @{$res}[0] if (ref $res eq 'ARRAY');
+                               if(@{$row}[5] eq 'trigger_action_reinstall') {
+                                       my $sql_statement_2 =  "SELECT * FROM $job_queue_tn WHERE macaddress LIKE '$macaddress' AND status='waiting' AND headertag = 'trigger_activate_new'"; 
+                                       my $res_2 = $job_db->exec_statement( $sql_statement_2 );
+                                       if(defined($res_2) and defined @{$res_2}[0]) {
+                                               # Set status from goto-activation to 'waiting' and update timestamp
+                                               $job_db->exec_statement("UPDATE $job_queue_tn SET status='waiting', timestamp='".&calc_timestamp(&get_time(), 'plus', 30)."' WHERE macaddress LIKE '$macaddress' AND headertag = 'trigger_action_reinstall'");
+                                       }
+                               }
                                next;
                        }
 
@@ -1598,12 +2062,23 @@ sub watch_for_new_jobs {
 
                                        $job_msg =~ /<header>(\S+)<\/header>/;
                                        my $header = $1 ;
-                                       my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");
+                                       my $func_error = &send_msg_to_target($job_msg, $server_address, $GosaPackages_key, $header, "J");                    
+
+                                       # update status in job queue to ...
+                    # ... 'processing', for jobs: 'reinstall', 'update', activate_new
+                    if (($header =~ /gosa_trigger_action_reinstall/) 
+                            || ($header =~ /gosa_trigger_activate_new/)
+                            || ($header =~ /gosa_trigger_action_update/)) {
+                        my $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
+                        my $dbres = $job_db->update_dbentry($sql_statement);
+                    }
 
-                                       # update status in job queue to 'processing'
-                                       $sql_statement = "UPDATE $job_queue_tn SET status='processing' WHERE id=$jobdb_id";
-                                       my $res = $job_db->update_dbentry($sql_statement);
-# TODO: abfangen ob alles in ordnung ist oder nicht, wenn nicht error schmeißen                                       
+                    # ... 'done', for all other jobs, they are no longer needed in the jobqueue
+                    else {
+                        my $sql_statement = "UPDATE $job_queue_tn SET status='done' WHERE id=$jobdb_id";
+                        my $dbres = $job_db->exec_statement($sql_statement);
+                    }
+                
 
                                        # We don't want parallel processing
                                        last;
@@ -1617,14 +2092,13 @@ sub watch_for_new_jobs {
 }
 
 
-
 sub watch_for_new_messages {
     my ($kernel,$heap) = @_[KERNEL, HEAP];
     my @coll_user_msg;   # collection list of outgoing messages
     
     # check messaging_db for new incoming messages with executable timestamp
     my $timestamp = &get_time();
-    my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS INTEGER))<$timestamp AND flag='n' AND direction='in' )";
+    my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( (CAST(timestamp AS UNSIGNED))<$timestamp AND flag='n' AND direction='in' )";
     my $res = $messaging_db->exec_statement( $sql_statement );
        foreach my $hit (@{$res}) {
 
@@ -1652,7 +2126,7 @@ sub watch_for_new_messages {
                                                                foreach my $entry (@entries) {
                                                                                my @receivers= $entry->get_value("memberUid");
                                                                                foreach my $receiver (@receivers) { 
-                                                                                               $receiver_h{$1} = 0;
+                                                                                               $receiver_h{$receiver} = 0;
                                                                                }
                                                                }
                                                } 
@@ -1660,6 +2134,7 @@ sub watch_for_new_messages {
                                                if ($mesg->code) {
                                                                daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: $mesg->error", 1);
                                                }
+                                               &release_ldap_handle($ldap_handle);
                                # ldap handle error ?           
                                } else {
                                        daemon_log("M ERROR: unable to translate group '$group_name' to user list for message delivery: no ldap handle available", 1);
@@ -1710,7 +2185,6 @@ sub watch_for_delivery_messages {
 
     # select outgoing messages
     my $sql_statement = "SELECT * FROM $messaging_tn WHERE ( flag='p' AND direction='out' )";
-    #&daemon_log("0 DEBUG: $sql", 7);
     my $res = $messaging_db->exec_statement( $sql_statement );
     
     # build out msg for each    usr
@@ -1722,41 +2196,84 @@ sub watch_for_delivery_messages {
 
         # resolve usr -> host where usr is logged in
         my $sql = "SELECT * FROM $login_users_tn WHERE (user='$receiver')"; 
-        #&daemon_log("0 DEBUG: $sql", 7);
         my $res = $login_users_db->exec_statement($sql);
 
-        # reciver is logged in nowhere
+        # receiver is logged in nowhere
         if (not ref(@$res[0]) eq "ARRAY") { next; }    
 
+        # receiver ist logged in at a client registered at local server
                my $send_succeed = 0;
                foreach my $hit (@$res) {
                                my $receiver_host = @$hit[0];
+                my $delivered2host = 0;
                                &daemon_log("M DEBUG: user '$receiver' is logged in at host '$receiver_host'", 7);
 
-                               # fetch key to encrypt msg propperly for usr/host
+                               # Looking for host in know_clients_db 
                                my $sql = "SELECT * FROM $known_clients_tn WHERE (hostname='$receiver_host')";
-                               &daemon_log("0 DEBUG: $sql", 7);
                                my $res = $known_clients_db->exec_statement($sql);
 
-                               # host is already down
-                               if (not ref(@$res[0]) eq "ARRAY") { next; }
-
-                               # host is on
-                               my $receiver_key = @{@{$res}[0]}[2];
-                               my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
-                               my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
-                               my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
-                               if ($error == 0 ) {
-                                       $send_succeed++ ;
-                               }
+                # Host is known in known_clients_db
+                if (ref(@$res[0]) eq "ARRAY") {
+                    my $receiver_key = @{@{$res}[0]}[2];
+                    my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
+                    my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
+                    my $error = &send_msg_to_target($out_msg, $receiver_host, $receiver_key, "usr_msg", 0); 
+                    if ($error == 0 ) {
+                        $send_succeed++ ;
+                        $delivered2host++ ;
+                        &daemon_log("M DEBUG: send message for user '$receiver' to host '$receiver_host'", 7); 
+                    } else {
+                        &daemon_log("M DEBUG: cannot send message for user '$receiver' to host '$receiver_host'", 7); 
+                    }
+                }
+                
+                # Message already send, do not need to do anything more, otherwise ...
+                if ($delivered2host) { next;}
+    
+                # ...looking for host in foreign_clients_db
+                $sql = "SELECT * FROM $foreign_clients_tn WHERE (hostname='$receiver_host')";
+                $res = $foreign_clients_db->exec_statement($sql);
+  
+                               # Host is known in foreign_clients_db 
+                               if (ref(@$res[0]) eq "ARRAY") { 
+                    my $registration_server = @{@{$res}[0]}[2];
+                    
+                    # Fetch encryption key for registration server
+                    my $sql = "SELECT * FROM $known_server_tn WHERE (hostname='$registration_server')";
+                    my $res = $known_server_db->exec_statement($sql);
+                    if (ref(@$res[0]) eq "ARRAY") { 
+                        my $registration_server_key = @{@{$res}[0]}[3];
+                        my %data = ('subject' => $subject, 'message' => $message, 'usr' => $receiver);
+                        my $out_msg = &build_msg("usr_msg", $server_address, $receiver_host, \%data ); 
+                        my $error = &send_msg_to_target($out_msg, $registration_server, $registration_server_key, "usr_msg", 0); 
+                        if ($error == 0 ) {
+                            $send_succeed++ ;
+                            $delivered2host++ ;
+                            &daemon_log("M DEBUG: send message for user '$receiver' to server '$registration_server'", 7); 
+                        } else {
+                            &daemon_log("M ERROR: cannot send message for user '$receiver' to server '$registration_server'", 1); 
+                        }
+
+                    } else {
+                        &daemon_log("M ERROR: host '$receiver_host' is reported to be ".
+                                "registrated at server '$registration_server', ".
+                                "but no data available in known_server_db ", 1); 
+                    }
+                }
+                
+                if (not $delivered2host) {
+                    &daemon_log("M ERROR: unable to send user message to host '$receiver_host'", 1);
+                }
                }
 
                if ($send_succeed) {
                                # set outgoing msg at db to deliverd
                                my $sql = "UPDATE $messaging_tn SET flag='d' WHERE (id='$msg_id' AND direction='out' AND message_to='$receiver')"; 
-                               &daemon_log("0 DEBUG: $sql", 7);
                                my $res = $messaging_db->exec_statement($sql); 
-               }
+                &daemon_log("M INFO: send message for user '$receiver' to logged in hosts", 5);
+               } else {
+            &daemon_log("M WARNING: failed to deliver message for user '$receiver'", 3); 
+        }
        }
 
     $kernel->delay_set('watch_for_delivery_messages', $messaging_db_loop_delay); 
@@ -1768,21 +2285,18 @@ sub watch_for_done_messages {
     my ($kernel,$heap) = @_[KERNEL, HEAP];
 
     my $sql = "SELECT * FROM $messaging_tn WHERE (flag='p' AND direction='in')"; 
-    #&daemon_log("0 DEBUG: $sql", 7);
     my $res = $messaging_db->exec_statement($sql); 
 
     foreach my $hit (@{$res}) {
         my $msg_id = @{$hit}[0];
 
         my $sql = "SELECT * FROM $messaging_tn WHERE (id='$msg_id' AND direction='out' AND (NOT flag='s'))"; 
-        #&daemon_log("0 DEBUG: $sql", 7); 
         my $res = $messaging_db->exec_statement($sql);
 
         # not all usr msgs have been seen till now
         if ( ref(@$res[0]) eq "ARRAY") { next; }
         
         $sql = "DELETE FROM $messaging_tn WHERE (id='$msg_id')"; 
-        #&daemon_log("0 DEBUG: $sql", 7);
         $res = $messaging_db->exec_statement($sql);
     
     }
@@ -1798,7 +2312,7 @@ sub watch_for_old_known_clients {
     my $sql_statement = "SELECT * FROM $known_clients_tn";
     my $res = $known_clients_db->select_dbentry( $sql_statement );
 
-    my $act_time = int(&get_time());
+    my $cur_time = int(&get_time());
 
     while ( my ($hit_num, $hit) = each %$res) {
         my $expired_timestamp = int($hit->{'timestamp'});
@@ -1812,8 +2326,8 @@ sub watch_for_old_known_clients {
                 );
 
         $dt->add( seconds => 2 * int($hit->{'keylifetime'}) );
-        $expired_timestamp = $dt->ymd('').$dt->hms('')."\n";
-        if ($act_time > $expired_timestamp) {
+        $expired_timestamp = $dt->ymd('').$dt->hms('');
+        if ($cur_time > $expired_timestamp) {
             my $hostname = $hit->{'hostname'};
             my $del_sql = "DELETE FROM $known_clients_tn WHERE hostname='$hostname'"; 
             my $del_res = $known_clients_db->exec_statement($del_sql);
@@ -1832,7 +2346,7 @@ sub watch_for_next_tasks {
 
     my $sql = "SELECT * FROM $incoming_tn";
     my $res = $incoming_db->select_dbentry($sql);
-
+    
     while ( my ($hit_num, $hit) = each %$res) {
         my $headertag = $hit->{'headertag'};
         if ($headertag =~ /^answer_(\d+)/) {
@@ -1840,142 +2354,138 @@ sub watch_for_next_tasks {
             next;
         }
         my $message_id = $hit->{'id'};
+        my $session_id = $hit->{'sessionid'};
+        &daemon_log("$session_id DEBUG: start processing for message with incoming id: '$message_id'", 11);
+
         $kernel->yield('next_task', $hit);
 
         my $sql = "DELETE FROM $incoming_tn WHERE id=$message_id";
         my $res = $incoming_db->exec_statement($sql);
     }
-
-    $kernel->delay_set('watch_for_next_tasks', 0.1); 
 }
 
 
 sub get_ldap_handle {
        my ($session_id) = @_;
        my $heap;
-       my $ldap_handle;
 
        if (not defined $session_id ) { $session_id = 0 };
        if ($session_id =~ /[^0-9]*/) { $session_id = 0 };
 
-       if ($session_id == 0) {
-               daemon_log("$session_id DEBUG: get_ldap_handle invoked without a session_id, create a new ldap_handle", 7); 
-               $ldap_handle = Net::LDAP->new( $ldap_uri );
-               $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!"); 
-
+       my ($package, $file, $row, $subroutine, $hasArgs, $wantArray, $evalText, $isRequire) = caller(1);
+       my $caller_text = "subroutine $subroutine";
+       if ($subroutine eq "(eval)") {
+               $caller_text = "eval block within file '$file' for '$evalText'"; 
+       }
+       daemon_log("$session_id DEBUG: new ldap handle for '$caller_text' required!", 42);
+
+get_handle:
+       my $ldap_handle = Net::LDAP->new( $ldap_uri );
+       if (not ref $ldap_handle) {
+               daemon_log("$session_id ERROR: Connection to LDAP URI '$ldap_uri' failed! Retrying in $ldap_retry_sec seconds.", 1); 
+               sleep($ldap_retry_sec);
+               goto get_handle;
        } else {
-               my $session_reference = $global_kernel->ID_id_to_session($session_id);
-               if( defined $session_reference ) {
-                       $heap = $session_reference->get_heap();
-               }
-
-               if (not defined $heap) {
-                       daemon_log("$session_id DEBUG: cannot get heap for session_id '$session_id'", 7); 
-                       return;
-               }
-
-               # TODO: This "if" is nonsense, because it doesn't prove that the
-                #       used handle is still valid - or if we've to reconnect...
-               #if (not exists $heap->{ldap_handle}) {
-                       $ldap_handle = Net::LDAP->new( $ldap_uri );
-                       $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or daemon_log("$session_id ERROR: Bind to LDAP $ldap_uri as $ldap_admin_dn failed!"); 
-                       $heap->{ldap_handle} = $ldap_handle;
-               #}
+               daemon_log("$session_id DEBUG: Connection to LDAP URI '$ldap_uri' established.", 42);
        }
+
+       $ldap_handle->bind($ldap_admin_dn, password => $ldap_admin_password) or &daemon_log("$session_id ERROR: Could not bind as '$ldap_admin_dn' to LDAP URI '$ldap_uri'!", 1);
        return $ldap_handle;
 }
 
 
+sub release_ldap_handle {
+       my ($ldap_handle, $session_id) = @_ ;
+       if (not defined $session_id ) { $session_id = 0 };
+
+       if(ref $ldap_handle) {
+         $ldap_handle->disconnect();
+  }
+       &main::daemon_log("$session_id DEBUG: Released a ldap handle!", 42);
+       return;
+}
+
+
 sub change_fai_state {
-    my ($st, $targets, $session_id) = @_;
-    $session_id = 0 if not defined $session_id;
-    # Set FAI state to localboot
-    my %mapActions= (
-        reboot    => '',
-        update    => 'softupdate',
-        localboot => 'localboot',
-        reinstall => 'install',
-        rescan    => '',
-        wake      => '',
-        memcheck  => 'memcheck',
-        sysinfo   => 'sysinfo',
-        install   => 'install',
-    );
+       my ($st, $targets, $session_id) = @_;
+       $session_id = 0 if not defined $session_id;
+       # Set FAI state to localboot
+       my %mapActions= (
+               reboot    => '',
+               update    => 'softupdate',
+               localboot => 'localboot',
+               reinstall => 'install',
+               rescan    => '',
+               wake      => '',
+               memcheck  => 'memcheck',
+               sysinfo   => 'sysinfo',
+               install   => 'install',
+       );
 
-    # Return if this is unknown
-    if (!exists $mapActions{ $st }){
-        daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
-      return;
-    }
+       # Return if this is unknown
+       if (!exists $mapActions{ $st }){
+               daemon_log("$session_id ERROR: unknown action '$st', can not translate ot FAIstate", 1); 
+               return;
+       }
 
-    my $state= $mapActions{ $st };
+       my $state= $mapActions{ $st };
 
-    my $ldap_handle = &get_ldap_handle($session_id);
-    if( defined($ldap_handle) ) {
+       # Build search filter for hosts
+       my $search= "(&(objectClass=GOhard)";
+       foreach (@{$targets}){
+               $search.= "(macAddress=$_)";
+       }
+       $search.= ")";
 
-      # Build search filter for hosts
-        my $search= "(&(objectClass=GOhard)";
-        foreach (@{$targets}){
-            $search.= "(macAddress=$_)";
-        }
-        $search.= ")";
+       # If there's any host inside of the search string, procress them
+       if (!($search =~ /macAddress/)){
+               daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
+               return;
+       }
 
-      # If there's any host inside of the search string, procress them
-        if (!($search =~ /macAddress/)){
-            daemon_log("$session_id ERROR: no macAddress found in filter statement for LDAP search: '$search'", 1);    
-            return;
-        }
+       my $ldap_handle = &get_ldap_handle($session_id);
+       # Perform search for Unit Tag
+       my $mesg = $ldap_handle->search(
+               base   => $ldap_base,
+               scope  => 'sub',
+               attrs  => ['dn', 'FAIstate', 'objectClass'],
+               filter => "$search"
+       );
 
-      # Perform search for Unit Tag
-      my $mesg = $ldap_handle->search(
-          base   => $ldap_base,
-          scope  => 'sub',
-          attrs  => ['dn', 'FAIstate', 'objectClass'],
-          filter => "$search"
-          );
+       if ($mesg->count) {
+               my @entries = $mesg->entries;
+               if (0 == @entries) {
+                       daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
+               }
 
-         if ($mesg->count) {
-                 my @entries = $mesg->entries;
-                 if (0 == @entries) {
-                                 daemon_log("$session_id ERROR: ldap search failed: ldap_base=$ldap_base, filter=$search", 1); 
-                 }
-
-                 foreach my $entry (@entries) {
-                         # Only modify entry if it is not set to '$state'
-                         if ($entry->get_value("FAIstate") ne "$state"){
-                                 daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
-                                 my $result;
-                                 my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
-                                 if (exists $tmp{'FAIobject'}){
-                                         if ($state eq ''){
-                                                 $result= $ldap_handle->modify($entry->dn, changes => [
-                                                         delete => [ FAIstate => [] ] ]);
-                                         } else {
-                                                 $result= $ldap_handle->modify($entry->dn, changes => [
-                                                         replace => [ FAIstate => $state ] ]);
-                                         }
-                                 } elsif ($state ne ''){
-                                         $result= $ldap_handle->modify($entry->dn, changes => [
-                                                 add     => [ objectClass => 'FAIobject' ],
-                                                 add     => [ FAIstate => $state ] ]);
-                                 }
-
-                                 # Errors?
-                                 if ($result->code){
-                                         daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
-                                 }
-                         } else {
-                                 daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 7); 
-                         }  
-                 }
-         } else {
-               daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
-         }
+               foreach my $entry (@entries) {
+                       # Only modify entry if it is not set to '$state'
+                       if ($entry->get_value("FAIstate") ne "$state"){
+                               daemon_log("$session_id INFO: Setting FAIstate to '$state' for ".$entry->dn, 5);
+                               my $result;
+                               my %tmp = map { $_ => 1 } $entry->get_value("objectClass");
+                               if (exists $tmp{'FAIobject'}){
+                                       if ($state eq ''){
+                                               $result= $ldap_handle->modify($entry->dn, changes => [ delete => [ FAIstate => [] ] ]);
+                                       } else {
+                                               $result= $ldap_handle->modify($entry->dn, changes => [ replace => [ FAIstate => $state ] ]);
+                                       }
+                               } elsif ($state ne ''){
+                                       $result= $ldap_handle->modify($entry->dn, changes => [ add => [ objectClass => 'FAIobject' ], add => [ FAIstate => $state ] ]);
+                               }
 
-    # if no ldap handle defined
-    } else {
-        daemon_log("$session_id ERROR: no LDAP handle defined for update FAIstate", 1); 
-    }
+                               # Errors?
+                               if ($result->code){
+                                       daemon_log("$session_id Error: Setting FAIstate to '$state' for ".$entry->dn. "failed: ".$result->error, 1);
+                               }
+                       } else {
+                               daemon_log("$session_id DEBUG FAIstate at host '".$entry->dn."' already at state '$st'", 42); 
+                       }  
+               }
+       } else {
+               daemon_log("$session_id ERROR: LDAP search failed: ldap_base=$ldap_base, filter=$search", 1);
+       }
+       &release_ldap_handle($ldap_handle, $session_id);                  
 
        return;
 }
@@ -2000,6 +2510,7 @@ sub change_goto_state {
 
       # If there's any host inside of the search string, procress them
       if (!($search =~ /macAddress/)){
+             &release_ldap_handle($ldap_handle);
         return;
       }
 
@@ -2020,8 +2531,7 @@ sub change_goto_state {
 
             daemon_log("$session_id INFO: Setting gotoMode to '$state' for ".$entry->dn, 5);
             my $result;
-            $result= $ldap_handle->modify($entry->dn, changes => [
-                                                replace => [ gotoMode => $state ] ]);
+            $result= $ldap_handle->modify($entry->dn, changes => [replace => [ gotoMode => $state ] ]);
 
             # Errors?
             if ($result->code){
@@ -2035,6 +2545,8 @@ sub change_goto_state {
          }
 
     }
+       &release_ldap_handle($ldap_handle, $session_id);
+       return;
 }
 
 
@@ -2064,11 +2576,11 @@ sub run_create_fai_server_db {
 
 
 sub create_fai_server_db {
-    my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
+       my ($table_name, $kernel, $dont_create_packages_list, $session_id) = @_;
        my $result;
 
        if (not defined $session_id) { $session_id = 0; }
-    my $ldap_handle = &get_ldap_handle();
+       my $ldap_handle = &get_ldap_handle($session_id);
        if(defined($ldap_handle)) {
                daemon_log("$session_id INFO: create_fai_server_db: start", 5);
                my $mesg= $ldap_handle->search(
@@ -2078,50 +2590,50 @@ sub create_fai_server_db {
                        filter => "(&(FAIrepository=*)(objectClass=FAIrepositoryServer))",
                );
                if($mesg->{'resultCode'} == 0 &&
-                  $mesg->count != 0) {
-                  foreach my $entry (@{$mesg->{entries}}) {
-                          if($entry->exists('FAIrepository')) {
-                                  # Add an entry for each Repository configured for server
-                                  foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
-                                                  my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
-                                                  my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
-                                                  $result= $fai_server_db->add_dbentry( { 
-                                                                  table => $table_name,
-                                                                  primkey => ['server', 'release', 'tag'],
-                                                                  server => $tmp_url,
-                                                                  release => $tmp_release,
-                                                                  sections => $tmp_sections,
-                                                                  tag => (length($tmp_tag)>0)?$tmp_tag:"",
-                                                          } );
-                                          }
-                                  }
-                          }
-                  }
+                       $mesg->count != 0) {
+                       foreach my $entry (@{$mesg->{entries}}) {
+                               if($entry->exists('FAIrepository')) {
+                                       # Add an entry for each Repository configured for server
+                                       foreach my $repo(@{$entry->get_value('FAIrepository', asref => 1)}) {
+                                               my($tmp_url,$tmp_server,$tmp_release,$tmp_sections) = split(/\|/, $repo);
+                                               my $tmp_tag= $entry->get_value('gosaUnitTag') || "";
+                                               $result= $fai_server_db->add_dbentry( { 
+                                                               table => $table_name,
+                                                               primkey => ['server', 'fai_release', 'tag'],
+                                                               server => $tmp_url,
+                                                               fai_release => $tmp_release,
+                                                               sections => $tmp_sections,
+                                                               tag => (length($tmp_tag)>0)?$tmp_tag:"",
+                                                       } );
+                                       }
+                               }
+                       }
+               }
                daemon_log("$session_id INFO: create_fai_server_db: finished", 5);
+               &release_ldap_handle($ldap_handle);
 
                # TODO: Find a way to post the 'create_packages_list_db' event
                if(not defined($dont_create_packages_list)) {
-                       &create_packages_list_db(undef, undef, $session_id);
+                       &create_packages_list_db(undef, $session_id);
                }
        }       
-    
-    $ldap_handle->disconnect;
-       return $result;
+
+       return $result;
 }
 
 
 sub run_create_fai_release_db {
-    my ($session, $heap, $table_name) = @_[SESSION, HEAP, ARG0];
+       my ($kernel, $session, $heap, $table_name) = @_[KERNEL, SESSION, HEAP, ARG0];
        my $session_id = $session->ID;
-    my $task = POE::Wheel::Run->new(
-            Program => sub { &create_fai_release_db($table_name, $session_id) },
-            StdoutEvent  => "session_run_result",
-            StderrEvent  => "session_run_debug",
-            CloseEvent   => "session_run_done",
-            );
+       my $task = POE::Wheel::Run->new(
+               Program => sub { &create_fai_release_db($table_name, $session_id) },
+               StdoutEvent  => "session_run_result",
+               StderrEvent  => "session_run_debug",
+               CloseEvent   => "session_run_done",
+       );
 
-    $heap->{task}->{ $task->ID } = $task;
-    return;
+       $heap->{task}->{ $task->ID } = $task;
+       return;
 }
 
 
@@ -2129,10 +2641,10 @@ sub create_fai_release_db {
        my ($table_name, $session_id) = @_;
        my $result;
 
-    # used for logging
-    if (not defined $session_id) { $session_id = 0; }
+       # used for logging
+       if (not defined $session_id) { $session_id = 0; }
 
-    my $ldap_handle = &get_ldap_handle();
+       my $ldap_handle = &get_ldap_handle($session_id);
        if(defined($ldap_handle)) {
                daemon_log("$session_id INFO: create_fai_release_db: start",5);
                my $mesg= $ldap_handle->search(
@@ -2141,8 +2653,10 @@ sub create_fai_release_db {
                        attrs  => [],
                        filter => "(&(objectClass=organizationalUnit)(ou=fai))",
                );
-               if($mesg->{'resultCode'} == 0 &&
-                       $mesg->count != 0) {
+               if(($mesg->code == 0) && ($mesg->count != 0))
+               {
+                       daemon_log("$session_id DEBUG: create_fai_release_db: count " . $mesg->count,138);
+
                        # Walk through all possible FAI container ou's
                        my @sql_list;
                        my $timestamp= &get_time();
@@ -2155,7 +2669,7 @@ sub create_fai_release_db {
                                                        if(defined($entry) && ref($entry) eq 'HASH') {
                                                                my $sql= 
                                                                "INSERT INTO $table_name "
-                                                               ."(timestamp, release, class, type, state) VALUES ("
+                                                               ."(timestamp, fai_release, class, type, state) VALUES ("
                                                                .$timestamp.","
                                                                ."'".$entry->{'release'}."',"
                                                                ."'".$entry->{'class'}."',"
@@ -2168,17 +2682,19 @@ sub create_fai_release_db {
                                }
                        }
 
-                       daemon_log("$session_id DEBUG: Inserting ".scalar @sql_list." entries to DB",8);
+                       daemon_log("$session_id DEBUG: create_fai_release_db: Inserting ".scalar @sql_list." entries to DB",138);
+           &release_ldap_handle($ldap_handle);
                        if(@sql_list) {
                                unshift @sql_list, "VACUUM";
                                unshift @sql_list, "DELETE FROM $table_name";
                                $fai_release_db->exec_statementlist(\@sql_list);
                        }
-                       daemon_log("$session_id DEBUG: Done with inserting",7);
+                       daemon_log("$session_id DEBUG: create_fai_release_db: Done with inserting",138);
+               } else {
+                       daemon_log("$session_id INFO: create_fai_release_db: error: " . $mesg->code, 5);
                }
                daemon_log("$session_id INFO: create_fai_release_db: finished",5);
        }
-    $ldap_handle->disconnect;
        return $result;
 }
 
@@ -2223,23 +2739,25 @@ sub resolve_fai_classes {
        my $fai_filter= "(|(&(objectClass=FAIclass)(|(objectClass=".join(")(objectClass=", @possible_fai_classes).")))(objectClass=FAIbranch))";
        my $fai_classes;
 
-       daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base",7);
+       daemon_log("$session_id DEBUG: Searching for FAI entries in base $fai_base", 138);
        my $mesg= $ldap_handle->search(
                base   => $fai_base,
                scope  => 'sub',
                attrs  => ['cn','objectClass','FAIstate'],
                filter => $fai_filter,
        );
-       daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries",7);
+       daemon_log("$session_id DEBUG: Found ".$mesg->count()." FAI entries", 138);
 
        if($mesg->{'resultCode'} == 0 &&
                $mesg->count != 0) {
                foreach my $entry (@{$mesg->{entries}}) {
                        if($entry->exists('cn')) {
                                my $tmp_dn= $entry->dn();
+                               $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
+                                       - length($fai_base) - 1 );
 
                                # Skip classname and ou dn parts for class
-                               my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?),$fai_base$/;
+                               my $tmp_release = ($1) if $tmp_dn =~ /^[^,]+,[^,]+,(.*?)$/;
 
                                # Skip classes without releases
                                if((!defined($tmp_release)) || length($tmp_release)==0) {
@@ -2274,7 +2792,9 @@ sub resolve_fai_classes {
                                }
                        } elsif (!$entry->exists('cn')) {
                                my $tmp_dn= $entry->dn();
-                               my $tmp_release = ($1) if $tmp_dn =~ /^(.*?),$fai_base$/;
+                               $tmp_dn= substr( $tmp_dn, 0, length($tmp_dn)
+                                       - length($fai_base) - 1 );
+                               my $tmp_release = ($1) if $tmp_dn =~ /^(.*?)$/;
 
                                # Skip classes without releases
                                if((!defined($tmp_release)) || length($tmp_release)==0) {
@@ -2419,12 +2939,15 @@ sub session_run_debug {
 sub session_run_done {
     my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];
     delete $heap->{task}->{$task_id};
+       if (exists $heap->{ldap_handle}->{$task_id}) {
+               &release_ldap_handle($heap->{ldap_handle}->{$task_id});
+       }
+       delete $heap->{ldap_handle}->{$task_id};
 }
 
 
 sub create_sources_list {
-       my $session_id = shift;
-       my $ldap_handle = &main::get_ldap_handle;
+       my $session_id = shift || 0;
        my $result="/tmp/gosa_si_tmp_sources_list";
 
        # Remove old file
@@ -2433,13 +2956,13 @@ sub create_sources_list {
                &main::daemon_log("$session_id DEBUG: remove an old version of '$result'", 7); 
        }
 
-       my $fh;
-       open($fh, ">$result");
+       open(my $fh, ">", "$result");
        if (not defined $fh) {
                &main::daemon_log("$session_id DEBUG: cannot open '$result' for writing", 7); 
                return undef;
        }
        if(defined($main::ldap_server_dn) and length($main::ldap_server_dn) > 0) {
+               my $ldap_handle = &get_ldap_handle($session_id);
                my $mesg=$ldap_handle->search(
                        base    => $main::ldap_server_dn,
                        scope   => 'base',
@@ -2457,6 +2980,7 @@ sub create_sources_list {
                                }
                        }
                }
+               &release_ldap_handle($ldap_handle);
        } else {
                if (defined $main::ldap_server_dn){
                        &main::daemon_log("$session_id ERROR: something wrong with ldap_server_dn '$main::ldap_server_dn', abort create_sources_list", 1); 
@@ -2473,10 +2997,9 @@ sub create_sources_list {
 sub run_create_packages_list_db {
     my ($kernel, $session, $heap) = @_[KERNEL, SESSION, HEAP];
        my $session_id = $session->ID;
-
        my $task = POE::Wheel::Run->new(
                                        Priority => +20,
-                                       Program => sub {&create_packages_list_db(undef, undef, $session_id)},
+                                       Program => sub {&create_packages_list_db(undef, $session_id)},
                                        StdoutEvent  => "session_run_result",
                                        StderrEvent  => "session_run_debug",
                                        CloseEvent   => "session_run_done",
@@ -2486,7 +3009,7 @@ sub run_create_packages_list_db {
 
 
 sub create_packages_list_db {
-       my ($ldap_handle, $sources_file, $session_id) = @_;
+       my ($sources_file, $session_id) = @_;
        
        # it should not be possible to trigger a recreation of packages_list_db
        # while packages_list_db is under construction, so set flag packages_list_under_construction
@@ -2502,15 +3025,7 @@ sub create_packages_list_db {
        }
 
        if (not defined $session_id) { $session_id = 0; }
-       if (not defined $ldap_handle) { 
-               $ldap_handle= &get_ldap_handle();
 
-               if (not defined $ldap_handle) {
-                       daemon_log("$session_id ERROR: no ldap_handle available to create_packages_list_db", 1);
-                       unlink($packages_list_under_construction);
-                       return;
-               }
-       }
        if (not defined $sources_file) { 
                &main::daemon_log("$session_id INFO: no sources_file given for creating packages list so trigger creation of it", 5); 
                $sources_file = &create_sources_list($session_id);
@@ -2524,14 +3039,14 @@ sub create_packages_list_db {
 
        my $line;
 
-       open(CONFIG, "<$sources_file") or do {
+       open(my $CONFIG, "<", "$sources_file") or do {
                daemon_log( "$session_id ERROR: create_packages_list_db: Failed to open '$sources_file'", 1);
                unlink($packages_list_under_construction);
                return;
        };
 
        # Read lines
-       while ($line = <CONFIG>){
+       while ($line = <$CONFIG>){
                # Unify
                chop($line);
                $line =~ s/^\s+//;
@@ -2553,14 +3068,18 @@ sub create_packages_list_db {
                                &parse_package_info( $baseurl, $dist, $section, $session_id );
                        }
                }
+    else {
+        daemon_log("$session_id ERROR: cannot parse line '$line'", 1);
+    }
        }
 
-       close (CONFIG);
+       close ($CONFIG);
 
-       find(\&cleanup_and_extract, keys( %repo_dirs ));
-       &main::strip_packages_list_statements();
-       unshift @packages_list_statements, "VACUUM";
-       $packages_list_db->exec_statementlist(\@packages_list_statements);
+       if(keys(%repo_dirs)) {
+               find(\&cleanup_and_extract, keys( %repo_dirs ));
+               &main::strip_packages_list_statements();
+               $packages_list_db->exec_statementlist(\@packages_list_statements);
+       }
        unlink($packages_list_under_construction);
        daemon_log("$session_id INFO: create_packages_list_db: finished", 5); 
        return;
@@ -2568,12 +3087,13 @@ sub create_packages_list_db {
 
 # This function should do some intensive task to minimize the db-traffic
 sub strip_packages_list_statements {
-    my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
+       my @existing_entries= @{$packages_list_db->exec_statement("SELECT * FROM $main::packages_list_tn")};
        my @new_statement_list=();
        my $hash;
        my $insert_hash;
        my $update_hash;
        my $delete_hash;
+       my $known_packages_hash;
        my $local_timestamp=get_time();
 
        foreach my $existing_entry (@existing_entries) {
@@ -2592,6 +3112,9 @@ sub strip_packages_list_statements {
                                        (! (@{$hash->{$distribution}->{$package}->{$version}}[4] eq $description))
                                ) {
                                        @{$update_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,undef);
+                               } else {
+                                       # package is already present in database. cache this knowledge for later use
+                                       @{$known_packages_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section,$description,$template);
                                }
                        } else {
                                # Insert a non-existing entry to db
@@ -2622,9 +3145,26 @@ sub strip_packages_list_statements {
                }
        }
 
-       # TODO: Check for orphaned entries
+       # Check for orphaned entries
+       foreach my $existing_entry (@existing_entries) {
+               my $distribution= @{$existing_entry}[0];
+               my $package= @{$existing_entry}[1];
+               my $version= @{$existing_entry}[2];
+               my $section= @{$existing_entry}[3];
+
+               if(
+                       exists($insert_hash->{$distribution}->{$package}->{$version}) ||
+                       exists($update_hash->{$distribution}->{$package}->{$version}) ||
+                       exists($known_packages_hash->{$distribution}->{$package}->{$version})
+               ) {
+                       next;
+               } else {
+                       # Insert entry to delete hash
+                       @{$delete_hash->{$distribution}->{$package}->{$version}} = ($distribution,$package,$version,$section);
+               }
+       }
 
-       # unroll the insert_hash
+       # unroll the insert hash
        foreach my $distribution (keys %{$insert_hash}) {
                foreach my $package (keys %{$insert_hash->{$distribution}}) {
                        foreach my $version (keys %{$insert_hash->{$distribution}->{$package}}) {
@@ -2657,14 +3197,26 @@ sub strip_packages_list_statements {
                                        next;
                                }
                                push @new_statement_list, 
-                                       "UPDATE $main::packages_list_tn SET $set WHERE"
-                                       ." distribution = '$distribution'"
-                                       ." AND package = '$package'"
-                                       ." AND version = '$version'";
+                               "UPDATE $main::packages_list_tn SET $set WHERE"
+                               ." distribution = '$distribution'"
+                               ." AND package = '$package'"
+                               ." AND version = '$version'";
+                       }
+               }
+       }
+       
+       # unroll the delete hash
+       foreach my $distribution (keys %{$delete_hash}) {
+               foreach my $package (keys %{$delete_hash->{$distribution}}) {
+                       foreach my $version (keys %{$delete_hash->{$distribution}->{$package}}) {
+                               my $section = @{$delete_hash->{$distribution}->{$package}->{$version}}[3];
+                               push @new_statement_list, "DELETE FROM $main::packages_list_tn WHERE distribution='$distribution' AND package='$package' AND version='$version' AND section='$section'";
                        }
                }
        }
 
+       unshift(@new_statement_list, "VACUUM");
+
        @packages_list_statements = @new_statement_list;
 }
 
@@ -2677,7 +3229,7 @@ sub parse_package_info {
     $repo_dirs{ "${repo_path}/pool" } = 1;
 
     foreach $package ("Packages.gz"){
-        daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 7);
+        daemon_log("$session_id DEBUG: create_packages_list: fetch $baseurl, $dist, $section", 266);
         get_package( "$baseurl/dists/$dist/$section/binary-$arch/$package", "$outdir/$dist/$section", $session_id );
         parse_package( "$outdir/$dist/$section", $dist, $path, $session_id );
     }
@@ -2695,11 +3247,11 @@ sub get_package {
     # This is ugly, but I've no time to take a look at "how it works in perl"
     if(0 == system("wget '$url' -O '$dest' 2>/dev/null") ) {
         system("gunzip -cd '$dest' > '$dest.in'");
-        daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 5);
+        daemon_log("$session_id DEBUG: run command: gunzip -cd '$dest' > '$dest.in'", 266);
         unlink($dest);
-        daemon_log("$session_id DEBUG: delete file '$dest'", 5); 
+        daemon_log("$session_id DEBUG: delete file '$dest'", 266); 
     } else {
-        daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' failed!", 1);
+        daemon_log("$session_id ERROR: create_packages_list_db: get_packages: fetching '$url' into '$dest' failed!", 1);
     }
     return 0;
 }
@@ -2709,7 +3261,6 @@ sub parse_package {
     my ($path, $dist, $srv_path, $session_id)= @_;
     if (not defined $session_id) { $session_id = 0;}
     my ($package, $version, $section, $description);
-    my $PACKAGES;
     my $timestamp = &get_time();
 
     if(not stat("$path.in")) {
@@ -2717,7 +3268,7 @@ sub parse_package {
         return;
     }
 
-    open($PACKAGES, "<$path.in");
+    open(my $PACKAGES, "<", "$path.in");
     if(not defined($PACKAGES)) {
         daemon_log("$session_id ERROR: create_packages_list_db: parse_package: cannot open '$path.in'",1); 
         return;
@@ -2791,296 +3342,503 @@ sub store_fileinfo {
 
 
 sub cleanup_and_extract {
-    my $fileinfo = $repo_files{ $File::Find::name };
+       my $fileinfo = $repo_files{ $File::Find::name };
 
-    if( defined $fileinfo ) {
+       if( defined $fileinfo ) {
+               my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
+               my $sql;
+               my $package = $fileinfo->{ 'package' };
+               my $newver = $fileinfo->{ 'version' };
 
-        my $dir = "$outdir/$fileinfo->{ 'dist' }/debconf.d";
-        my $sql;
-        my $package = $fileinfo->{ 'package' };
-        my $newver = $fileinfo->{ 'version' };
-
-        mkpath($dir);
-        system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
+               mkpath($dir);
+               system( "dpkg -e '$File::Find::name' '$dir/DEBIAN'" );
 
                if( -f "$dir/DEBIAN/templates" ) {
 
-                       daemon_log("DEBUG: Found debconf templates in '$package' - $newver", 5);
+                       daemon_log("0 DEBUG: Found debconf templates in '$package' - $newver", 266);
 
-                       my $tmpl= "";
-                       {
+                       my $tmpl= ""; {
                                local $/=undef;
-                               open FILE, "$dir/DEBIAN/templates";
-                               $tmpl = &encode_base64(<FILE>);
-                               close FILE;
+                               open(my $FILE, "$dir/DEBIAN/templates");
+                               $tmpl = &encode_base64(<$FILE>);
+                               close($FILE);
                        }
                        rmtree("$dir/DEBIAN/templates");
 
                        $sql= "update $main::packages_list_tn set template = '$tmpl' where package = '$package' and version = '$newver';";
-               push @packages_list_statements, $sql;
+                       push @packages_list_statements, $sql;
                }
-    }
+       }
 
-    return;
+       return;
 }
 
 
+sub prepare_server_registration 
+{
+       # Add foreign server from cfg file
+       my @foreign_server_list;
+       if ($foreign_server_string ne "") {
+           my @cfg_foreign_server_list = split(",", $foreign_server_string);
+           foreach my $foreign_server (@cfg_foreign_server_list) {
+               push(@foreign_server_list, $foreign_server);
+           }
+       
+           daemon_log("0 INFO: found foreign server in config file: ".join(", ", @foreign_server_list), 5);
+       }
+       
+       # Perform a DNS lookup for server registration if flag is true
+       if ($dns_lookup eq "true") {
+           # Add foreign server from dns
+           my @tmp_servers;
+           if (not $server_domain) {
+               # Try our DNS Searchlist
+               for my $domain(get_dns_domains()) {
+                   chomp($domain);
+                   my ($tmp_domains, $error_string) = &get_server_addresses($domain);
+                   if(@$tmp_domains) {
+                       for my $tmp_server(@$tmp_domains) {
+                           push @tmp_servers, $tmp_server;
+                       }
+                   }
+               }
+               if(@tmp_servers && length(@tmp_servers)==0) {
+                   daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
+               }
+           } else {
+               @tmp_servers = &get_server_addresses($server_domain);
+               if( 0 == @tmp_servers ) {
+                   daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
+               }
+           }
+       
+           daemon_log("0 INFO: found foreign server via DNS ".join(", ", @tmp_servers), 5);    
+       
+           foreach my $server (@tmp_servers) { 
+               unshift(@foreign_server_list, $server); 
+           }
+       } else {
+           daemon_log("0 INFO: DNS lookup for server registration is disabled", 5);
+       }
+       
+       # eliminate duplicate entries
+       @foreign_server_list = &del_doubles(@foreign_server_list);
+       my $all_foreign_server = join(", ", @foreign_server_list);
+       daemon_log("0 INFO: found foreign server in config file and DNS: '$all_foreign_server'", 5);
+       
+       # add all found foreign servers to known_server
+       my $cur_timestamp = &get_time();
+       foreach my $foreign_server (@foreign_server_list) {
+       
+               # do not add myself to known_server_db
+               if (&is_local($foreign_server)) { next; }
+               ######################################
+       
+           my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
+                   primkey=>['hostname'],
+                   hostname=>$foreign_server,
+                   macaddress=>"",
+                   status=>'not_yet_registered',
+                   hostkey=>"none",
+                   loaded_modules => "none", 
+                   timestamp=>$cur_timestamp,
+                               update_time=>'19700101000000',
+                   } );
+       }
+}
+
 sub register_at_foreign_servers {   
     my ($kernel) = $_[KERNEL];
 
-    # hole alle bekannten server aus known_server_db
-    my $server_sql = "SELECT * FROM $known_server_tn";
-    my $server_res = $known_server_db->exec_statement($server_sql);
+       # Update status and update-time of all si-server with expired update_time and 
+       # block them for race conditional registration processes of other si-servers.
+       my $act_time = &get_time();
+       my $block_statement = "UPDATE $known_server_tn SET status='new_server',update_time='19700101000000' WHERE (CAST(update_time AS UNSIGNED))<$act_time ";
+       my $block_res = $known_server_db->exec_statement($block_statement);
 
-    # no entries in known_server_db
-    if (not ref(@$server_res[0]) eq "ARRAY") { 
-        # TODO
-    }
+       # Fetch all si-server from db where update_time is younger than act_time
+       my $fetch_statement = "SELECT * FROM $known_server_tn WHERE update_time='19700101000000'"; 
+       my $fetch_res = $known_server_db->exec_statement($fetch_statement);
 
-    # detect already connected clients
+    # Detect already connected clients. Will be added to registration msg later. 
     my $client_sql = "SELECT * FROM $known_clients_tn"; 
     my $client_res = $known_clients_db->exec_statement($client_sql);
 
-    # send my server details to all other gosa-si-server within the network
-    foreach my $hit (@$server_res) {
+       # Send registration messag to all fetched si-server
+    foreach my $hit (@$fetch_res) {
         my $hostname = @$hit[0];
         my $hostkey = &create_passwd;
 
-        # add already connected clients to registration message 
+        # Add already connected clients to registration message 
         my $myhash = &create_xml_hash('new_server', $server_address, $hostname);
         &add_content2xml_hash($myhash, 'key', $hostkey);
         map(&add_content2xml_hash($myhash, 'client', @{$_}[0].",".@{$_}[4]), @$client_res);
+
+        # Add locally loaded gosa-si modules to registration message
+        my $loaded_modules = {};
+        while (my ($package, $pck_info) = each %$known_modules) {
+                       next if ((!defined(@$pck_info[2])) || (!(ref (@$pck_info[2]) eq 'HASH')));
+                       foreach my $act_module (keys(%{@$pck_info[2]})) {
+                               $loaded_modules->{$act_module} = ""; 
+                       }
+               }
+        map(&add_content2xml_hash($myhash, "loaded_modules", $_), keys(%$loaded_modules));
+
+        # Add macaddress to registration message
+        my ($host_ip, $host_port) = split(/:/, $hostname);
+        my $local_ip = &get_local_ip_for_remote_ip($host_ip);
+        my $network_interface= &get_interface_for_ip($local_ip);
+        my $host_mac = &get_mac_for_interface($network_interface);
+        &add_content2xml_hash($myhash, 'macaddress', $host_mac);
         
-        # build registration message and send it
+        # Build registration message and send it
         my $foreign_server_msg = &create_xml_string($myhash);
         my $error = &send_msg_to_target($foreign_server_msg, $hostname, $ServerPackages_key, "new_server", 0); 
     }
-    
-    $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay); 
-    return;
+
+
+       # After n sec perform a check of all server registration processes
+    $kernel->delay_set("control_server_registration", 2); 
+
+       return;
 }
 
 
-#==== MAIN = main ==============================================================
-#  parse commandline options
-Getopt::Long::Configure( "bundling" );
-GetOptions("h|help" => \&usage,
-        "c|config=s" => \$cfg_file,
-        "f|foreground" => \$foreground,
-        "v|verbose+" => \$verbose,
-        "no-arp+" => \$no_arp,
-           );
+sub control_server_registration {
+       my ($kernel) = $_[KERNEL];
+       
+       # Check if all registration processes succeed or not
+       my $select_statement = "SELECT * FROM $known_server_tn WHERE status='new_server'"; 
+       my $select_res = $known_server_db->exec_statement($select_statement);
+
+       # If at least one registration process failed, maybe in case of a race condition
+       # with a foreign registration process
+       if (@$select_res > 0) 
+       {
+               # Release block statement 'new_server' to make the server accessible
+               # for foreign registration processes
+               my $update_statement = "UPDATE $known_server_tn SET status='waiting' WHERE status='new_server'";        
+               my $update_res = $known_server_db->exec_statement($update_statement);
+
+               # Set a random delay to avoid the registration race condition
+               my $new_foreign_servers_register_delay = int(rand(4))+1;
+               $kernel->delay_set("register_at_foreign_servers", $new_foreign_servers_register_delay);
+       }
+       # If all registration processes succeed
+       else
+       {
+               $kernel->delay_set("register_at_foreign_servers", $foreign_servers_register_delay);
+       }
 
-#  read and set config parameters
-&check_cmdline_param ;
-&read_configfile;
-&check_pid;
-
-$SIG{CHLD} = 'IGNORE';
-
-# forward error messages to logfile
-if( ! $foreground ) {
-  open( STDIN,  '+>/dev/null' );
-  open( STDOUT, '+>&STDIN'    );
-  open( STDERR, '+>&STDIN'    );
-}
-
-# Just fork, if we are not in foreground mode
-if( ! $foreground ) { 
-    chdir '/'                 or die "Can't chdir to /: $!";
-    $pid = fork;
-    setsid                    or die "Can't start a new session: $!";
-    umask 0;
-} else { 
-    $pid = $$; 
-}
-
-# Do something useful - put our PID into the pid_file
-if( 0 != $pid ) {
-    open( LOCK_FILE, ">$pid_file" );
-    print LOCK_FILE "$pid\n";
-    close( LOCK_FILE );
-    if( !$foreground ) { 
-        exit( 0 ) 
-    };
+       return;
 }
 
-# parse head url and revision from svn
-my $server_status_hash = { 'developmental'=>'revision', 'stable'=>'release'};
-$server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
-$server_headURL = defined $1 ? $1 : 'unknown' ;
-$server_revision = defined $2 ? $2 : 'unknown' ;
-if ($server_headURL =~ /\/tag\// || 
+sub start_daemon {
+
+       #to prevent zombie child
+  $SIG{CHLD} = 'IGNORE';
+
+       if( ! $foreground ) { 
+               chdir '/'                 or die "Can't chdir to /: $!";
+               umask 0;
+               open STDIN, '+>/dev/null'   or die "Can't read /dev/null: $!";
+               open STDOUT, '+>&STDIN' or die "Can't write to /dev/null: $!";
+               open STDERR, '+>&STDIN' or die "Can't write to /dev/null: $!";
+               defined($pid = fork)   or die "Can't fork: $!";
+               exit if $pid;
+               setsid                    or die "Can't start a new session: $!";
+       }
+       return;
+}
+
+sub put_version {
+
+       # parse head url and revision from svn
+       $server_version =~ /^\$HeadURL: (\S+) \$:\$Rev: (\d+) \$$/;
+       $server_headURL = defined $1 ? $1 : 'unknown' ;
+       $server_revision = defined $2 ? $2 : 'unknown' ;
+       if ($server_headURL =~ /\/tag\// ||
         $server_headURL =~ /\/branches\// ) {
-    $server_status = "stable"; 
-} else {
+    $server_status = "stable";
+       } else {
     $server_status = "developmental" ;
+       }
+       return;
+}
+
+sub get_perms_admin {
+# Determine root uid and adm gid, used for creating log files
+       $root_uid = getpwnam('root');
+       $adm_gid = getgrnam('adm');
+       if(not defined $adm_gid){
+               $adm_gid = getgrnam('root');
+       }
+       return;
+}
+
+sub open_log {
+       # Prepare log file and set permissions
+       open(my $log, ">>", "$log_file");
+       close($log);
+       chmod(0440, $log_file);
+       chown($root_uid, $adm_gid, $log_file);
+
+       return;
+}
+
+sub create_pid {
+       # Create the PID object
+       $pid = File::Pid->new({ file  => $pid_file });
+
+       # Write the PID file
+       $pid->write;
+
+       return;
+}
+
+#
+#==== MAIN = main ==============================================================
+#
+# Parse options and allow '-vvv'
+Getopt::Long::Configure( 'bundling' );
+GetOptions( 'v|verbose+' => \$verbose,
+            'h|help' => \&usage,
+            'c|config=s' => \$config,
+            'x|dump-config=i' => \$dump_config,
+            'f|foreground' => \$foreground,
+                                               'd=s' => \$debug_parts)
+  or usage( '', 1 );
+
+# We may want to dump the default configuration
+if( defined $dump_config ) {
+  if($dump_config==1) {
+       } elsif ($dump_config==2) {
+    dump_configuration( $dump_config ); 
+       } else {
+    usage( "Dump configuration value has to be 1 or 2" );
+       }
 }
 
+#  read and set config parameters
+&read_configfile($config, %cfg_defaults);
+
+# daemonize the program
+&start_daemon($pid, $foreground);
+
+# create pid file
+&create_pid($pid, $pid_file);
+
+# Determine root uid and adm gid, used for creating log files
+&get_perms_admin($root_uid, $adm_gid);
+
+# put version
+&put_version($server_status_hash, $server_version, $server_headURL, $server_revision, $server_status);
+
+#open log file
+&open_log($root_uid, $adm_gid, $log_file);
+
+# prepare directory for databases
+mkpath('/var/lib/gosa-si', 0, {owner=>'root', group=>'root', mode=> '0755'});
+
+# remove leftover files in tmp for packaged.db populate
+rmtree( '/tmp/packages_list_db',0,1);
+
+# remove list of sources from apt.sources.list
+unlink '/tmp/gosa_si_tmp_sources_list';
+
+# remove marker that the list creation is in progress
+unlink '/tmp/packages_list_creation_in_progress';
 
 daemon_log(" ", 1);
 daemon_log("$0 started!", 1);
 daemon_log("status: $server_status", 1);
 daemon_log($server_status_hash->{$server_status}.": $server_revision", 1); 
 
-# connect to incoming_db
-unlink($incoming_file_name);
-$incoming_db = GOSA::DBsqlite->new($incoming_file_name);
-$incoming_db->create_table($incoming_tn, \@incoming_col_names);
+# Buildup data bases
+{
+    no strict "refs";
 
-# connect to gosa-si job queue
-$job_db = GOSA::DBsqlite->new($job_queue_file_name);
-$job_db->create_table($job_queue_tn, \@job_queue_col_names);
+               daemon_log("0 INFO: importing database module '$db_module'", 1);
 
-# connect to known_clients_db
-$known_clients_db = GOSA::DBsqlite->new($known_clients_file_name);
-$known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
+    if ($db_module eq "DBmysql") {
+    
+        # connect to incoming_db
+        $incoming_db = ("GOsaSI::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
 
-# connect to foreign_clients_db
-$foreign_clients_db = GOSA::DBsqlite->new($foreign_clients_file_name);
-$foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
+        # connect to gosa-si job queue
+        $job_db = ("GOsaSI::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
 
-# connect to known_server_db
-unlink($known_server_file_name);
-$known_server_db = GOSA::DBsqlite->new($known_server_file_name);
-$known_server_db->create_table($known_server_tn, \@known_server_col_names);
+        # connect to known_clients_db
+        $known_clients_db = ("GOsaSI::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
 
-# connect to login_usr_db
-$login_users_db = GOSA::DBsqlite->new($login_users_file_name);
-$login_users_db->create_table($login_users_tn, \@login_users_col_names);
+        # connect to foreign_clients_db
+        $foreign_clients_db = ("GOsaSI::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
 
-# connect to fai_server_db and fai_release_db
-unlink($fai_server_file_name);
-$fai_server_db = GOSA::DBsqlite->new($fai_server_file_name);
-$fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
+        # connect to known_server_db
+        $known_server_db = ("GOsaSI::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
 
-unlink($fai_release_file_name);
-$fai_release_db = GOSA::DBsqlite->new($fai_release_file_name);
-$fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
+        # connect to login_usr_db
+        $login_users_db = ("GOsaSI::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
 
-# connect to packages_list_db
-#unlink($packages_list_file_name);
-unlink($packages_list_under_construction);
-$packages_list_db = GOSA::DBsqlite->new($packages_list_file_name);
-$packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
+        # connect to fai_server_db 
+        $fai_server_db = ("GOsaSI::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
 
-# connect to messaging_db
-$messaging_db = GOSA::DBsqlite->new($messaging_file_name);
-$messaging_db->create_table($messaging_tn, \@messaging_col_names);
+        # connect to fai_release_db
+        $fai_release_db = ("GOsaSI::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
 
+        # connect to packages_list_db
+        $packages_list_db = ("GOsaSI::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
 
-# create xml object used for en/decrypting
-$xml = new XML::Simple();
+        # connect to messaging_db
+        $messaging_db = ("GOsaSI::".$db_module)->new($main::mysql_database, $main::mysql_host, $main::mysql_username, $main::mysql_password);
 
+    } elsif ($db_module eq "DBsqlite") {
 
-# foreign servers 
-my @foreign_server_list;
+        daemon_log("0 INFO: Removing SQLlite lock files", 1);
 
-# add foreign server from cfg file
-if ($foreign_server_string ne "") {
-    my @cfg_foreign_server_list = split(",", $foreign_server_string);
-    foreach my $foreign_server (@cfg_foreign_server_list) {
-        push(@foreign_server_list, $foreign_server);
-    }
-}
+                               # delete old DBsqlite lock files to be replace by rmtree !!
+                               system('rm -f /var/lib/gosa-si/*.si.lock*');
+                               #rmtree( '/var/lib/gosa-si/',0,1);
 
-# add foreign server from dns
-my @tmp_servers;
-if ( !$server_domain) {
-    # Try our DNS Searchlist
-    for my $domain(get_dns_domains()) {
-        chomp($domain);
-        my @tmp_domains= &get_server_addresses($domain);
-        if(@tmp_domains) {
-            for my $tmp_server(@tmp_domains) {
-                push @tmp_servers, $tmp_server;
-            }
-        }
-    }
-    if(@tmp_servers && length(@tmp_servers)==0) {
-        daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
-    }
-} else {
-    @tmp_servers = &get_server_addresses($server_domain);
-    if( 0 == @tmp_servers ) {
-        daemon_log("0 WARNING: no foreign gosa-si-server found in DNS for domain '$server_domain'", 3);
+        # connect to incoming_db
+        unlink($incoming_file_name);
+        $incoming_db = ("GOsaSI::".$db_module)->new($incoming_file_name);
+
+        # connect to gosa-si job queue
+        $job_db = ("GOsaSI::".$db_module)->new($job_queue_file_name);
+        
+        # connect to known_clients_db
+        $known_clients_db = ("GOsaSI::".$db_module)->new($known_clients_file_name);
+        
+        # connect to foreign_clients_db
+        $foreign_clients_db = ("GOsaSI::".$db_module)->new($foreign_clients_file_name);
+        
+        # connect to known_server_db
+        $known_server_db = ("GOsaSI::".$db_module)->new($known_server_file_name);
+        
+        # connect to login_usr_db
+        $login_users_db = ("GOsaSI::".$db_module)->new($login_users_file_name);
+        
+        # connect to fai_server_db
+        $fai_server_db = ("GOsaSI::".$db_module)->new($fai_server_file_name);
+        
+        # connect to fai_release_db
+        $fai_release_db = ("GOsaSI::".$db_module)->new($fai_release_file_name);
+        
+        # connect to packages_list_db
+        $packages_list_db = ("GOsaSI::".$db_module)->new($packages_list_file_name);
+        
+        # connect to messaging_db
+        $messaging_db = ("GOsaSI::".$db_module)->new($messaging_file_name);
     }
 }
-foreach my $server (@tmp_servers) { 
-    unshift(@foreign_server_list, $server); 
-}
-# eliminate duplicate entries
-@foreign_server_list = &del_doubles(@foreign_server_list);
-my $all_foreign_server = join(", ", @foreign_server_list);
-daemon_log("0 INFO: found foreign server in config file and DNS: $all_foreign_server", 5);
 
-# add all found foreign servers to known_server
-my $act_timestamp = &get_time();
-foreach my $foreign_server (@foreign_server_list) {
+# Creating tables
 
-       # do not add myself to known_server_db
-       if (&is_local($foreign_server)) { next; }
-       ######################################
+daemon_log("0 INFO: creating tables in database with '$db_module'", 1);
 
-    my $res = $known_server_db->add_dbentry( {table=>$known_server_tn, 
-            primkey=>['hostname'],
-            hostname=>$foreign_server,
-            status=>'not_jet_registered',
-            hostkey=>"none",
-            timestamp=>$act_timestamp,
-            } );
-}
+$messaging_db->create_table($messaging_tn, \@messaging_col_names);
+$packages_list_db->create_table($packages_list_tn, \@packages_list_col_names);
+$fai_release_db->create_table($fai_release_tn, \@fai_release_col_names);
+$fai_server_db->create_table($fai_server_tn, \@fai_server_col_names);
+$login_users_db->create_table($login_users_tn, \@login_users_col_names);
+$known_server_db->create_table($known_server_tn, \@known_server_col_names);
+$foreign_clients_db->create_table($foreign_clients_tn, \@foreign_clients_col_names);
+$known_clients_db->create_table($known_clients_tn, \@known_clients_col_names);
+$incoming_db->create_table($incoming_tn, \@incoming_col_names);
+$job_db->create_table($job_queue_tn, \@job_queue_col_names);
 
+# create xml object used for en/decrypting
+$xml = new XML::Simple();
 
-# import all modules
+# Import all modules
 &import_modules;
-# check wether all modules are gosa-si valid passwd check
+
+# Check wether all modules are gosa-si valid passwd check
 &password_check;
 
+# Check DNS and config file for server registration
+if ($serverPackages_enabled eq "true") { &prepare_server_registration; }
+
+# Create functions hash
+while (my ($module, @mod_info) = each %$known_modules) 
+{
+       while (my ($plugin, $functions) = each %{$mod_info[0][2]})
+       {
+               while (my ($function, $nothing) = each %$functions )
+               {
+                       $known_functions->{$function} = $nothing;
+               }
+       }
+}
+
+# Prepare for using Opsi 
+if ($opsi_enabled eq "true") {
+    use JSON::RPC::Client;
+    use XML::Quote qw(:all);
+    $opsi_url= "https://".$opsi_admin.":".$opsi_password."@".$opsi_server.":4447/rpc";
+    $opsi_client = new JSON::RPC::Client;
+}
+
+daemon_log("0 INFO: start socket for incoming xml messages at port '$server_port' ", 1);
 
 POE::Component::Server::TCP->new(
-    Alias => "TCP_SERVER",
+       Alias => "TCP_SERVER",
        Port => $server_port,
        ClientInput => sub {
-        my ($kernel, $input) = @_[KERNEL, ARG0];
-        push(@tasks, $input);
-        push(@msgs_to_decrypt, $input);
-        $kernel->yield("msg_to_decrypt");
-        },
-    InlineStates => {
-        msg_to_decrypt => \&msg_to_decrypt,
-        next_task => \&next_task,
-        task_result => \&handle_task_result,
-        task_done   => \&handle_task_done,
-        task_debug  => \&handle_task_debug,
-        child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
-    }
+               my ($kernel, $input, $heap, $session) = @_[KERNEL, ARG0, HEAP, SESSION];
+        my $session_id = $session->ID;
+               if ($input =~ /;([\d\.]+):([\d]+)$/) 
+               {
+                       # Messages from other servers should be blocked if config option is set
+                       if (($2 eq $server_port) && ($serverPackages_enabled eq "false"))
+                       {
+                               return;
+                       }
+                       &daemon_log("$session_id DEBUG: incoming message from '$1:$2'", 11);
+               }
+               else
+               {
+                       my $remote_ip = $heap->{'remote_ip'};
+                       &daemon_log("$session_id DEBUG: incoming message from '$remote_ip'", 11);
+               }
+               push(@msgs_to_decrypt, $input);
+               $kernel->yield("msg_to_decrypt");
+       },
+       InlineStates => {
+               msg_to_decrypt => \&msg_to_decrypt,
+               watch_for_next_tasks => \&watch_for_next_tasks,
+               next_task => \&next_task,
+               task_result => \&handle_task_result,
+               task_done   => \&handle_task_done,
+               task_debug  => \&handle_task_debug,
+               child_reap => sub { "Do nothing special. I'm just a comment, but i'm necessary!"  },
+       }
 );
 
-daemon_log("start socket for incoming xml messages at port '$server_port' ", 1);
-
 # create session for repeatedly checking the job queue for jobs
 POE::Session->create(
        inline_states => {
-               _start => \&session_start,
+                               _start => \&session_start,
         register_at_foreign_servers => \&register_at_foreign_servers,
+                               control_server_registration => \&control_server_registration,
         sig_handler => \&sig_handler,
         next_task => \&next_task,
         task_result => \&handle_task_result,
         task_done   => \&handle_task_done,
         task_debug  => \&handle_task_debug,
-        watch_for_next_tasks => \&watch_for_next_tasks,
         watch_for_new_messages => \&watch_for_new_messages,
         watch_for_delivery_messages => \&watch_for_delivery_messages,
         watch_for_done_messages => \&watch_for_done_messages,
-               watch_for_new_jobs => \&watch_for_new_jobs,
+                               watch_for_new_jobs => \&watch_for_new_jobs,
         watch_for_modified_jobs => \&watch_for_modified_jobs,
         watch_for_done_jobs => \&watch_for_done_jobs,
+        watch_for_opsi_jobs => \&watch_for_opsi_jobs,
         watch_for_old_known_clients => \&watch_for_old_known_clients,
         create_packages_list_db => \&run_create_packages_list_db,
         create_fai_server_db => \&run_create_fai_server_db,
         create_fai_release_db => \&run_create_fai_release_db,
-               recreate_packages_db => \&run_recreate_packages_db,
+                               recreate_packages_db => \&run_recreate_packages_db,
         session_run_result => \&session_run_result,
         session_run_debug => \&session_run_debug,
         session_run_done => \&session_run_done,