HEX
Server: Apache/2.4.41 (FreeBSD) OpenSSL/1.0.2s mod_fcgid/2.3.9
System: FreeBSD salazo 12.0-RELEASE-p1303-ZFS hostBSD 12.0-RELEASE-p1303-ZFS DMR amd64
User: admin (1000)
PHP: 7.4.3
Disabled: NONE
Upload Files
File: /usr/local/www/apache24/cgi-bin/easytecc4/easytecc3.pm
package easytecc3;

BEGIN {
	if(-e '/etc/sudoers'){
		
		my $osversion = `/usr/bin/uname -K`;
		chomp $osversion;
		
		my $perl;
		
		if($osversion >= 1300000){
			
			$perl = '5.34';
		
		} elsif($osversion >= 1200000){
			
			$perl = '5.26';
		
		} elsif($osversion >= 1003000){
			
			$perl = '5.24';

		} else {
			
			$perl = '5.20';
			
		}
		
		@INC = (
				"/usr/iports/lib/perl5/site_perl",
				"/usr/iports/lib/perl5/amd64-freebsd-thread-multi",
				"/usr/iports/lib/perl5/$perl",
				"/usr/iports/lib/perl5/$perl/mach",
				"/usr/iports/lib/perl5/site_perl/mach/$perl",
				"/usr/local/lib/perl5/site_perl",
				"/usr/local/lib/perl5/amd64-freebsd-thread-multi",
				"/usr/local/lib/perl5/$perl",
				"/usr/local/lib/perl5/$perl/mach",
				"/usr/local/lib/perl5/site_perl/mach/$perl",
				"/usr/local/www/apache24/cgi-bin/easytecc4"
		);
				
			
	} else {
		push @INC, '/home/httpd/cgi-bin/easytecc4';
	}
}

require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(ascii_domain is_domain is_email is_tld);
use easytecc_class;
use Encode;
use Encode::Guess;
use IO::Socket::INET;
use Data::FormValidator;
use CGI::Session;
use Net::IDN::Encode ':all';
use HTML::Template;
use DBI;
use DBD::mysql;

#Jimmy: Log around the world
use Log::Dispatch;
use Log::Dispatch::File;
use File::Spec;
use Time::Format qw(%time %strftime);

use constant LOG_DIR    => '/home/web/log';
use constant LOG_FILE   => 'easytecc4.log';

our $loglevel = 'debug'; #'debug','info','notice','warning','error','critical','alert','emergency' # most reasonable is 'warning' or 'error' for production

sub dienice ($);
sub logline($$);

#
# Begin setup logging agent
# 

our $log = new Log::Dispatch(
		callbacks => sub {
	    	my %h=@_;
    	    my ($package, $filename, $line) = caller;
        	my $level = sprintf '%-8.8s', $h{level};
	        return $strftime{'%b %e %T'}." \[$$]: ".$level." => ".$h{message}."\n";
	    }
);

our $debug = 0;
$debug = 1 if(-e '/etc/easytecc'); #run logline("debug",...) only when $debug=1
if ($debug) {
	$log->add( Log::Dispatch::File->new( name      => 'logfile',
    	                                 min_level => $loglevel,
        	                             mode      => 'append',
            	                         filename  => File::Spec->catfile(LOG_DIR, LOG_FILE),
            	                    	)
	);
}


#
# End setup logging agent
#

my ($easytecc_prefix, $droot_prefix, $droot_regex);

my $fb = "";
if (-e '/etc/sudoers') {
    $fb = '1';
}

if ($fb) {
	$easytecc_prefix = '/usr/local/www/apache24/cgi-bin/easytecc4';
	$droot_prefix = '/usr/local/www/apache24/noexec';
	$droot_regex = '\/usr\/local\/www\/apache24\/noexec\/';
}
else{
	$easytecc_prefix = '/home/httpd/cgi-bin/easytecc4';
	$droot_prefix = '/home/httpd/docs';
	$droot_regex = '\/home\/httpd\/docs\/';
}

our %file_handler_store;

if ($fb) {
	my $easytecc_default_conf = '/usr/local/www/apache24/cgi-bin/easytecc4/config/easytecc.default.conf';
} else {
	my $easytecc_default_conf = 			'/home/httpd/cgi-bin/easytecc4/config/easytecc.default.conf';
}
my $easytecc_custom_conf  = '/usr/local/etc/easytecc/easytecc.custom.conf';
our %easytecc_custom_conf_entries;
if (-e $easytecc_custom_conf) {
	#my $username = getpwuid( $< );
	#my $fileowner = getpwuid((stat($easytecc_custom_conf))[4]);
	#if ($username ne $fileowner) {
	#	logline("error","file $easytecc_custom_conf belongs to $fileowner, but owner should be $username.");
	#}
	open(my $CUSTOMCONF, "<", "$easytecc_custom_conf") or die "Can't open < $easytecc_custom_conf: $!";
	if (length($easytecc_custom_conf))	{
		logline("info", "length(file $easytecc_custom_conf) true");
		while (my $line = <$CUSTOMCONF>) {   
			chomp($line);
			my ($custom_conf_key, $custom_conf_value) = split /=/, $line;
			$easytecc_custom_conf_entries{$custom_conf_key} = $custom_conf_value;
			logline("info", "\$easytec_custom_conf: $custom_conf_key= ###$custom_conf_value###");
		}
	}
	close($CUSTOMCONF) && logline("info","closed ") || logline("error","close failed: $!");
} else {
	logline("error", "file $easytecc_custom_conf does not exist.");
}

#fbsd
sub domain_alias_select{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $count = shift;
	my $domain_alias_select = qq~<SELECT id=<TMPL_VAR NAME=select_name> NAME=<TMPL_VAR NAME=select_name>>
	<TMPL_LOOP NAME=domain_alias_select$count>
	<OPTION <TMPL_VAR NAME=2> <TMPL_VAR NAME=option_selected>><TMPL_VAR NAME=1></OPTION>
	</TMPL_LOOP>
	</select><br />
	~;
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return(\$domain_alias_select);
}

sub get_mail_ftp_quota_vhost{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;
	
my $session = shift;
my $vhost = $session->param('vhost_no_www');
my $ftpuser = $session->param('user');

my($mail_quota_sum, $ftp_quota, $mailpasswd) = '';
my %passwd = ();
my %mailquota = ();

my ($quotaref, $sum_ftplimit, $sum_ftpuse, $ftpserverlimit) = easytecc3::getquota_ftp();
my %quota = %$quotaref;

logline("debug","vhost=$vhost ftpuser=$ftpuser fb=$fb") if $debug;

	if ($fb) {
		$mailpasswd = easytecc3::get_mailpasswd();
		%passwd = %{$mailpasswd->file_parsed_hash()};
		%mailquota = %passwd;
	}
	elsif (easytecc3::extern_mx()){
		$mailpasswd = easytecc3::get_mailpasswd();
		%passwd = %{$mailpasswd->file_parsed_hash()};

		#%passwd = %{easytecc3::get_mailpasswd()};
		($mailquotaref, $sum_maillimit, $sum_mailuse, $mailserverlimit) = easytecc3::getquota_mail();
		%mailquota = %$mailquotaref;
	}
	else{
		%passwd = %webserver_passwd;
		%mailquota = %quota;
		$sum_maillimit = $sum_ftplimit;
		$sum_mailuse = $sum_ftpuse;
		$mailserverlimit = $ftpserverlimit;
	}
	
	foreach my $user(sort keys %passwd){
		#logline("debug","mail_quota_sum=$mail_quota_sum user=$user gecos=" . $passwd{$user}{'gecos'} . " mailquota user=" . $mailquota{$user}{'quota'}) if $debug;
		next if $passwd{$user}{'gecos'} !~ /^$vhost - POP/;
		$mail_quota_sum += $mailquota{$user}{'quota'};
	}
	
	$ftp_quota = $quota{$ftpuser}{'quota'} / 1024;
	
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;

return($mail_quota_sum, $ftp_quota);
	
}

sub get_mailpasswd{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my @passwd = ();
	my $passwd = '';

	if(extern_mx()){
		my $socket = make_socket();
		logline("debug","nach make_socket()") if $debug;
		print $socket "getpasswd=\n";
		@passwd = <$socket>;
		# um die parsing-Funktion von file benutzen zu knnne ein neues file-Objekt erzeugen und @passwd reintun
		# anschlieend parsen und es kommt ein Hash raus
		$passwd = file->new({file_name => '/usr/local/etc/mailpasswd'});
		$passwd->file_content(\@passwd);
		$passwd->write_file;
		$passwd->parse_file;
	}
	elsif($fb){
		$passwd = file->new({file_name => '/etc/mail/mailuser'});
		$passwd->read_file;

		#my @new_mailpasswd = $passwd->file_content;
		#foreach(@new_mailpasswd){
		#	logline("debug","$passwd get_mailpasswd: $_ ########################################") if $debug;
		#my %passwd = %{$passwd->file_parsed_hash()};
		#return($passwd);
		#}

		#return($passwd);
	}
	else{
		$passwd = file->new({file_name => '/etc/passwd'});
		$passwd->read_file;
	}
	#my %passwd = %{$passwd->file_parsed_hash()};
	#foreach(sort keys %passwd){
	#	logline("debug","key=$_ value=" . $passwd{$_} . "") if $debug;
	#}
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return($passwd);
}

sub getquota_mail{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $mailpasswd = shift;
	my %mailpasswd;
	if ($fb && $mailpasswd) {
		%mailpasswd = %$mailpasswd;
	}
	my @qtst = ();
	my %qtst;
	my $sum_limit = '';
	my $sum_use = '';
	my $serverlimit = '';

	if(extern_mx()){
		my $socket = make_socket();
		print $socket "getquota=\n";
		@qtst = <$socket>;
	}
	elsif($fb){
		$serverlimit = 	`cat /etc/vsd/quota`;
		chomp $serverlimit;
		
		my $mailquotas_raw = `/usr/iports/bin/sudo /usr/sbin/doveadm -f flow quota get -A`;
		my $mailquota_line;
		foreach $mailquota_line (split(/[\r\n]+/,$mailquotas_raw)){
				
			if($mailquota_line =~ m/Type=MESSAGE/){
				next;
			}
			
			my ($user,$quota) = $mailquota_line =~ m/^([^\s]+).+Value=([0-9]+)/;
			
			$mailpasswd{$user}{'quota_used'} = $quota;
											
		}
				
		foreach my $user(sort keys %mailpasswd){
			$sum_limit += $mailpasswd{$user}{'quota'};
			$qtst{$user}{'quota'} = $mailpasswd{$user}{'quota'} * 1024;
			my $quota = $mailpasswd{$user}{'quota'};
			my $home = $mailpasswd{$user}{'home'};
#TODO: JIMMY: timeout setzen? Hatte schon verkackte logins weil er sich hier totgesucht hat
			#my $use = `/usr/iports/bin/sudo /usr/bin/du -Ask $home`;
			
			#my $use_kb = `/usr/iports/bin/doveadm -f tab mailbox status -t -u '$user' vsize '*' | tail -n1`;
			#chomp $use_kb /= 1024;
			
			# viele shellbefehle sind einfach zu lahm...
			#my $use_kb = `/usr/iports/bin/sudo /usr/iports/bin/doveadm -f flow quota get -u $user | sed '/Type=MESSAGE/d ; s/^.*Type=STORAGE Value=// ; s/ Limit=.*//'`;
			#chomp $use_kb;
			
			my $use_kb = $mailpasswd{$user}{'quota_used'};
						
			#my($use_kb, $dummy) = split /\s+/, $use;
			#logline("debug","get_mailpasswd fb user=$user quota=$quota du -sk $home=" . $use_kb . "") if $debug;
			$qtst{$user}{'use'} = $use_kb;
			$sum_use += $qtst{$user}{'use'};
		}
		#return(\%qtst, $sum_limit, $sum_use, $serverlimit);
	}
	else{
		open(QUOTASTATS,"/usr/sbin/sqt|");	
			while(<QUOTASTATS>){
				chomp;
				push @qtst, "$_";
			}
		close(QUOTASTATS);
	}

	if (! $fb) {
		foreach (@qtst){
			next if /^(=|User name)/;
			if(/^Allocated/){
				($sum_limit,$sum_use) = (split(/\|\s+/,$_))[2,3];
				$sum_limit =~ s/K//;
				$sum_use =~ s/K//;
				$sum_limit /= 1024;
				$sum_use /= 1024;
			}
			elsif (/^Max\/available/) {
				($serverlimit) = (split(/\|\s+/,$_))[2];
				$serverlimit =~ s/K//;
				$serverlimit /= 1024;
			}
			else{
				my ($mailuser,$mailquota,$mailuse) = (split(/\|\s+/,$_))[0,2,3];
				$mailuser =~ s/^\s+//;
				$mailquota  =~ s/K//;
				$mailuse  =~ s/K//;
				#prevent division by zero
				$mailquota = '1024' if ($mailquota eq '0');
				
				$qtst{$mailuser}{'quota'} = $mailquota;
				$qtst{$mailuser}{'use'} = $mailuse;
			}
		}
	}
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return(\%qtst, $sum_limit, $sum_use, $serverlimit);
}

sub getquota_ftp{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my %quota;
	my $sum_limit = '';
	my $sum_use = '';
	my $serverlimit = '';

	open(QUOTASTATS,"/usr/sbin/quotastats|") if $fb;
	open(QUOTASTATS,"/usr/sbin/sqt|") unless $fb;	
		while(<QUOTASTATS>){
			next if /^(=|User name)/;
			#Allocated/used   |        |    14588928K|    2950752K
			#Max/available    |        |    10240000K|    7289248K
			if (/^Allocated/) {
				($sum_limit,$sum_use) = (split(/\|\s+/,$_))[2,3];
				$sum_limit =~ s/K//;
				$sum_use =~ s/K//;
				$sum_limit /= 1024;
				$sum_use /= 1024;
			}
			elsif (/^Max\/available/) {
				($serverlimit) = (split(/\|\s+/,$_))[2];
				$serverlimit =~ s/K//;
				$serverlimit /= 1024;
			}
			else{
				my ($username,$quota,$use) = (split(/\|\s+/,$_))[0,2,3];
				$username =~ s/\s+//g;
				$quota  =~ s/K//;
				$use  =~ s/K//;
				#prevent division by zero
				$quota = '1024' if ($quota eq '0');
				
				$quota{$username}{"quota"} = $quota;
				$quota{$username}{"use"} = $use;
				
				#logline("debug","getquota_ftp username=$username quota=$quota use=$use") if $debug;
				
			}
		}
	close(QUOTASTATS);
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return(\%quota, $sum_limit, $sum_use, $serverlimit);
}

sub extern_mx{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $mxhost = `cat /etc/mxhost 2>/dev/null`;
	chomp $mxhost;
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return ($mxhost);
}

sub make_socket{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $localhost = `resolveip -s localhost`;
	chomp $localhost;
	my $mxhost = extern_mx();

	unless ($localhost =~ m#^(.*)$#) {
		dienice "'$localhost' has invalid characters.\n";
	}
	$localhost = $1;

	unless ($mxhost =~ m#^(.*)$#) {
		dienice "'$mxhost' has invalid characters.\n";
	}
	$mxhost = $1;
	
	my $socket=IO::Socket::INET->new(	PeerAddr => $mxhost,
										PeerPort => 6996,
										LocalAddr => $localhost,
										Proto    => 'tcp',
										Type     => SOCK_STREAM,
										Timeout  => 60
	)
	or dienice "couldn't open port\n";

	$socket->autoflush(1);
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return($socket);
}

sub close_socket{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $socket = shift;
	close($socket);
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
}

sub get_userchars {
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my (@allfiles, $userdirname, $user, $uid, $realname, @userlist, $domain, $unique_domain, %active_user_chars, %active_users, %active_domains, %active_domain_chars, %userarray, %domainarray);

	#kein oder leeres Userlisting?
	unless (-s "/usr/local/etc/easytecc/spamuser.conf"){
		logline("debug","no /usr/local/etc/easytecc/spamuser.conf") if $debug;

		opendir DIR, "/home" or die "You have no /home dir! Contact the support immediately!: $!";
		@allfiles = grep !/^\.\.?$/, readdir DIR;
		closedir DIR;

		while (@allfiles) {
			$userdirname = pop @allfiles;
			if (-s "/home/$userdirname/.spamproc"){
				push @userlist, $userdirname;
			}
		}
		logline("debug","userlist: @userlist") if $debug;

		if(extern_mx()){
			my $socket = make_socket();
			logline("debug","nach make_socket()") if $debug;
			print $socket "getpasswd=\n";
			while(<$socket>){
				($user,$uid,$realname) = (split(/:/,$_))[0,2,4];
				if($uid < 1000 ){
					#keine Systemuser
				}
				else {
					logline("debug","passwd:user=$user;uid=$uid;realname=$realname") if $debug;
					$userarray{$user}{'name'} = $user;
					$userarray{$user}{'realname'} = $realname;
				}
			}
		}
		elsif($fb){
			my $passwd = easytecc3::get_mailpasswd();
			my %passwd = %{$passwd->file_parsed_hash()};

			foreach(sort keys %passwd){
				logline("debug","fb passwd:user=$_") if $debug;
				$userarray{$user}{'name'} = $_;
				$userarray{$user}{'realname'} = $passwd{$_}{'gecos'};
			}
		}
		else{
			open(PASSWD,"/etc/passwd");
				while(<PASSWD>){
					($user,$uid,$realname) = (split(/:/,$_))[0,2,4];
					if($uid < 1000 ){
						#keine Systemuser
					}
					else {
						logline("debug","passwd:user=$user;uid=$uid;realname=$realname") if $debug;
						$userarray{$user}{'name'} = $user;
						$userarray{$user}{'realname'} = $realname;
					}
				}
			close(PASSWD);
		}

		#spamconf schreiben
		open(USERL,">/usr/local/etc/easytecc/spamuser.conf");
		foreach $user (@userlist){
			$domain = $userarray{$user}{'realname'};
			$domain =~ s/^\s+//;
			$domain =~ s/\s+.*//;
			print USERL "$user=$domain\n" if $domain;
			unless($domain){
				print USERL "$user=$user\n" if ($user =~ /\./);
			}
		}
		close(USERL);
		undef %userarray;
	}

	open(USERL,"/usr/local/etc/easytecc/spamuser.conf");
		while(<USERL>){
			my ($user,$domain) = split(/=/,$_);
			chomp($domain);
			logline("debug","user=$user domain=$domain") if $debug;
			next unless(-e "/home/$user/.spamd/user_prefs");

			if($user){
				##array fr user sortierung
				$userarray{$user}{'name'} = $user;
				$userarray{$user}{'domain'} = $domain;
				#userspezifische Einstellungen, gehört zu keiner Domain, z.B. FTPuser
				if(! is_domain($domain)){
					logline("debug","#$user# ne #$domain#") if $debug;
					$active_user_chars{substr(lc $user,0,1)} = 1;
					$active_users{$user} = 1;
				}
				#userspezifische Einstellungen, user ist regulärer emailuser
				elsif($user ne $domain && is_domain($domain)){
					$active_user_chars{substr(lc $user,0,1)} = 1;
					$active_users{$user} = 1;
				}
				#domainspezifische Einstellungen
				elsif($user eq $domain && is_domain($domain)){
					$domain = domain_to_unicode($domain);
					##array fr domain sortierung
					$unique_domain = "$domain$user";
					$domainarray{$unique_domain}{'name'} = $user;
					$domainarray{$unique_domain}{'domain'} = $domain;
					$active_domain_chars{substr(lc $domain,0,1)} = 1;
					$active_domains{$domain} = 1;
				}
			}
		}
	close(USERL);

	#my @user_charlist = keys %active_user_chars;
	#my @domain_charlist = keys %active_domain_chars;

	foreach(keys %active_domain_chars){
		logline("debug",'active_domain_chars=' . $_ . "") if $debug;
	}

	foreach(keys %active_user_chars){
		logline("debug",'active_user_chars=' . $_ . "") if $debug;
	}
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return(\%active_user_chars, \%active_domain_chars, \%active_users, \%active_domains);
}

sub decrypt {
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $passwordfile = shift;
	my @salt = ("43","112","120","36","96","94","68","43","62","15","89","12");
	my $plaintext = "";
	my $cyphertext = "";

	if($easytecc_custom_conf_entries{'ShowMailuserPasswords'} eq "NO"){
		
		return '';
		
	}
	
	if(-f $passwordfile){
	
		open (CRYPT , "$passwordfile");
			while(<CRYPT>) {
				$cyphertext .= $_;
			}
		close(CRYPT);
		
	} else {
	
		# keep it ugly	
		$cyphertext = $passwordfile;
		
	}	

	my $salt = 0;
	my @cypher = unpack('C*',$cyphertext);
	foreach my $number (@cypher){
		$number -= $salt[$salt];
		$plaintext .= pack('C*',$number);
		$salt++;
		if(! -f $passwordfile && $salt == scalar @salt){
				$salt = 0;
		}
	}

	$plaintext =~ s/\s+$//;
	#logline("debug","file=$passwordfile pass=$plaintext") if $debug;
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;    
	return $plaintext;
}

sub mysql_connect{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	# User und Pass wird von anderer Funktion übergeben, wenn nicht ist es root und bei Entryservern admin
	my $dbuser = shift;
	my $dbpass = shift;

	$dbuser = 'root' if not defined $dbuser;
	# wenn Rückgabewert = 0, dann _warscheinlich entryserver und dbuser ist admin anstatt root
	# kann ggf. aber durch /usr/local/etc/easytecc/.dbconf überschrieben werden.
	$dbuser = 'admin' if(defined is_ftp_limited());

	# Achtung, da $password über require gesetzt wird, darf es hier nicht mit my definiert werden, da ansonsten leer
	#my $password = '';
	my $db_host	= $ENV{HTTP_HOST};
	if(! $db_host){
		#open(GET_HOST, "</etc/hosts");
		#	my $hosts = (<GET_HOST>);
		#close (GET_HOST);

		#my ($tmp, $host, $tmp2) = split (/\s+/, $hosts);
		#$db_host = $host;
		$db_host = `sed 's/^.* // ; s/\$/.han-solo.net/' /etc/FQDN 2>/dev/null`;
	}
	if ($fb) {
		$db_host = 'localhost';
	}
	my $database     = "mysql:$db_host:3306";
	$database     = "db01:$db_host:3306" if(defined is_ftp_limited());
	my $dbh = '';
	my $mysql_connect_error = '';

	# kommt von Formular, um mysqluser anzugeben, der Datenbanken und Backups anlegen kann
	if($dbpass){
		
		$dbh = DBI->connect('DBI:mysql:database=mysql;mysql_socket=/tmp/mysql.sock', "$dbuser", "$dbpass" ) or $mysql_connect_error = '1';
		logline("debug","mysql socket connect mysql_connect_error=$mysql_connect_error") if $debug;
		
		
		if ($mysql_connect_error) {
			$mysql_connect_error = '';
			$dbh = DBI->connect('DBI:mysql:'.$database, "$dbuser", "$dbpass" ) or $mysql_connect_error = '1';
			logline("debug", "add_mysql_password mysql_connect_error=$mysql_connect_error user=$dbuser pass=$dbpass host=$db_host") if $debug;
			
		}
	}
	elsif(-e "/usr/local/etc/easytecc/.dbconf"){
		my $dbconf = `cat /usr/local/etc/easytecc/.dbconf`;
		my ($user,$password) = split /\n/, $dbconf;
		$user =~ s/^\$user=[\"\'](.*)[\"\']\;/$1/;
		$password =~ s/^\$password=[\"\'](.*)[\"\']\;/$1/;
		$user = $dbuser if $user eq '';
		
		$dbh = DBI->connect('DBI:mysql:database=mysql;mysql_socket=/tmp/mysql.sock', "$user", "$password" ) or $mysql_connect_error = '1';
		logline("debug","mysql socket connect mysql_connect_error=$mysql_connect_error") if $debug;
				
		if ($mysql_connect_error) {
			$mysql_connect_error = '';
			$dbh = DBI->connect('DBI:mysql:'.$database, "$user", "$password" ) or $mysql_connect_error = '1';
			
		}
		
		
		logline("debug", "mit .dbconf mysql_connect_error=$mysql_connect_error user=$user pass=$password host=$db_host") if $debug;
	}
	else{
		if (-e "/home/httpd/docs/twig/config/dbconfig.inc.php3"){
			open (GETPASSWORD,"</home/httpd/docs/twig/config/dbconfig.inc.php3");
				($dbh, $mysql_connect_error) = get_mysql_password();
			close (GETPASSWORD);
		}
		elsif (-e "/home/httpd/docs/twig/config/dbconfig.inc.php"){
			open (GETPASSWORD,"</home/httpd/docs/twig/config/dbconfig.inc.php");
				($dbh, $mysql_connect_error) = get_mysql_password();
			close (GETPASSWORD);		
		}
		elsif (-e "/home/httpd/docs/twig/config/dbconfig.inc.php4"){
			open (GETPASSWORD,"</home/httpd/docs/twig/config/dbconfig.inc.php4");
				($dbh, $mysql_connect_error) = get_mysql_password();
			close (GETPASSWORD);
		}
		else{
			$mysql_connect_error = '1';
		}
	}
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return($dbh, $mysql_connect_error);
}

sub get_mysql_password{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $db_host	= $ENV{HTTP_HOST};
	$db_host = 'localhost' if $fb;
	my $database     = "mysql:$db_host:3306";
	my $user = 'root';
	$user = 'admin' if(defined is_ftp_limited());

	my $password = '';
	my $mysql_connect_error = '';

	while(<GETPASSWORD>){
		if (/sqlpassword/){
			$_ =~ s/\s+//g;
			$_ =~ s/.*=\"//g;
			$_ =~ s/\".*//g;
			$password = $_;
		}
	}

	my $dbh = DBI->connect('DBI:mysql:database=mysql;mysql_socket=/tmp/mysql.sock', "$user", "$password" ) or $mysql_connect_error = '1';
	logline("debug","mysql socket connect mysql_connect_error=$mysql_connect_error") if $debug;
	
	if ($mysql_connect_error) {
			$mysql_connect_error = '';
			$dbh = DBI->connect('DBI:mysql:'.$database, $user, $password ) or $mysql_connect_error = '1';			
	}

	if ( ! $mysql_connect_error){
		open (WRITEPASSWORD, "> /usr/local/etc/easytecc/.dbconf");
			print  WRITEPASSWORD qq~\$user=\"$user\";\n\$password=\"$password\";\n~;
		close WRITEPASSWORD;
	}
	
	# wenn Passwort nicht funktioniert, dann Fehler zurckgeben
	logline("debug","<<< Leaving ".(caller(0))[3]."() = $mysql_connect_error") if $debug;
	return($dbh, $mysql_connect_error);
}

sub zeit {
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $formatedtime_ref = shift;
	my @formatedtime = @$formatedtime_ref;
	#my $wtag = ("So", "Mo", "Di", "Mi", "Do", "Fr", "Sa")[(localtime)[6]];
	my $wtag = ("So", "Mo", "Di", "Mi", "Do", "Fr", "Sa")[(@formatedtime)[6]];
	my $mtag = sprintf("%02d", (@formatedtime)[3]);
	my $monat = ("Jan"," Feb", "M&auml;r", "Apr"," Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez")[(@formatedtime)[4]];
	my $jahr = (@formatedtime)[5] + 1900;
	my $stunde = sprintf("%02d", (@formatedtime)[2]);
	my $min = sprintf("%02d", (@formatedtime)[1]);
	my $zeit = "$wtag, $mtag\. $monat $jahr $stunde\:$min Uhr";
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return $zeit;
}

sub cronjob_selected_time{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $template_ref = shift;
	my $cronjob_ref = shift;
	my $template = $$template_ref;
	my %cronjob = %$cronjob_ref;
	
	#    $cronjob{'job'}
	#    $cronjob{'min'}
	#    $cronjob{'std'}
	#    $cronjob{'mday'}
	#    $cronjob{'mon'}
	#    $cronjob{'wday'}
	# Null vorweg wenn nur eine Ziffer
	foreach(keys %cronjob){
		logline("debug",qq~key=$_ value=$cronjob{$_}\n~) if $debug;
		# wday kann direkt bernommen werden
		next if $_ eq 'wday';
		$cronjob{$_} = '0' . $cronjob{$_} if length($cronjob{$_}) == '1';
	}

	# in cronjob-Template Zeiten als "selected" ausgeben:
	#<SELECT NAME="min">
	#<OPTION <TMPL_VAR NAME=option_selected_all_min>>Min&uuml;tlich</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_all2_min>>Alle 2</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_all5_min>>Alle 5</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_all10_min>>Alle 10</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_all15_min>>Alle 15</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_all30_min>>Alle 30</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_00_min>>00</OPTION>
	#.
	#.
	#<OPTION <TMPL_VAR NAME=option_selected_59_min>>59</OPTION>
	my %time_conversion_min = (
		'*' => 'all',
		'*/2' => 'all2',
		'*/5' => 'all5',
		'*/10' => 'all10',
		'*/15' => 'all15',
		'*/30' => 'all30'
	);
	if(not exists $time_conversion_min{$cronjob{'min'}}){
		$time_conversion_min{$cronjob{'min'}} = $cronjob{'min'};
	}
	logline("debug",qq~cronjob{'min'}=$cronjob{'min'} conversion=$time_conversion_min{$cronjob{'min'}}\n~) if $debug;
	$template->param('option_selected_' .  $time_conversion_min{$cronjob{'min'}} .  '_min' => 'selected');

	#<SELECT NAME="std">
	#<OPTION <TMPL_VAR NAME=option_selected_all_std>>St&uuml;ndlich</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_all2_std>>Alle 2</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_all4_std>>Alle 4</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_all6_std>>Alle 6</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_all8_std>>Alle 8</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_all12_std>>Alle 12</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_00_std>>00</OPTION>
	#.
	#.
	#<OPTION <TMPL_VAR NAME=option_selected_23_std>>23</OPTION>
	my %time_conversion_std = (
		'*' => 'all',
		'*/2' => 'all2',
		'*/4' => 'all4',
		'*/6' => 'all6',
		'*/8' => 'all8',
		'*/12' => 'all12'
	);
	if(not exists $time_conversion_std{$cronjob{'std'}}){
		$time_conversion_std{$cronjob{'std'}} = $cronjob{'std'};
	}
	$template->param('option_selected_' .  $time_conversion_std{$cronjob{'std'}} .  '_std' => 'selected');

	#<SELECT NAME="mday">
	#<OPTION <TMPL_VAR NAME=option_selected_all_mday>>Jeden</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_all2_mday>>Alle 2</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_first_half_mday>>1.+15.</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_01_mday>>01</OPTION>
	#.
	#.
	#<OPTION <TMPL_VAR NAME=option_selected_31_mday>>31</OPTION>
	my %time_conversion_mday = (
		'*' => 'all',
		'*/2' => 'all2',
		'1,15' => 'first_half'
	);
	if(not exists $time_conversion_mday{$cronjob{'mday'}}){
		$time_conversion_mday{$cronjob{'mday'}} = $cronjob{'mday'};
	}
	$template->param('option_selected_' .  $time_conversion_mday{$cronjob{'mday'}} .  '_mday' => 'selected');

	#<SELECT NAME="mon">
	#<OPTION <TMPL_VAR NAME=option_selected_all_mon>>Jeden</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_even_mon>>Feb.,Apr.,Jun.,Aug.,Okt.,Dez.</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_quarter_mon>>Mar.,Jun.,Sep.,Dez.</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_6_12_mon>>Jun.,Dez.</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_1_mon>>Januar</OPTION>
	#.
	#.
	#<OPTION <TMPL_VAR NAME=option_selected_12_mon>>Dezember</OPTION>
	my %time_conversion_mon = (
		'*' => 'all',
		'2,4,6,8,10,12' => 'even',
		'3,6,9,12' => 'quarter',
		'6,12' => '6_12'
	);
	if(not exists $time_conversion_mon{$cronjob{'mon'}}){
		$time_conversion_mon{$cronjob{'mon'}} = $cronjob{'mon'};
	}
	$template->param('option_selected_' .  $time_conversion_mon{$cronjob{'mon'}} .  '_mon' => 'selected');

	#<SELECT NAME="wday">
	#<OPTION <TMPL_VAR NAME=option_selected_all_wday>>Jeden</OPTION>
	#<OPTION <TMPL_VAR NAME=option_selected_1_wday>>Montag</OPTION>
	#.
	#.
	#<OPTION <TMPL_VAR NAME=option_selected_0_wday>>Sonntag</OPTION>
	my %time_conversion_wday = (
		$cronjob{'wday'} => $cronjob{'wday'}
	);
	$template->param('option_selected_' .  $time_conversion_wday{$cronjob{'wday'}} .  '_wday' => 'selected');
	$template->param('job' => $cronjob{'job'});
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
}

sub store_file_handle{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $handle = shift;
	$file_handler_store{$handle->file_name} = $handle;
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
}

sub return_file_handler_store{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return(\%file_handler_store);
}

sub form_to_cron{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my %form_to_cron = (
		'all' => '*',
		'all2' => '*/2',
		'all5' => '*/5',
		'all4' => '*/4',
		'all6' => '*/6',
		'all8' => '*/8',
		'all10' => '*/10',
		'all12' => '*/12',
		'all15' => '*/15',
		'all30' => '*/30',
		'first_half' => '1,15',
		'even' => '2,4,6,8,10,12',
		'quarter' => '3,6,9,12',
		'6_12' => '6,12'
	);
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return(\%form_to_cron);
}

sub string_to_utf8{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $string = shift;
	my $decoder = Encode::Guess->guess($string, qw/ascii utf8 latin1/);

	logline("debug", "decoder=" . $decoder->name . "string = $string") if ref($decoder);

	if(ref($decoder) && $decoder->name =~ /utf8/){
		logline("debug","nix") if $debug;
		#nix, ist schon utf8
	}
	else{
		logline("debug","nach utf8 codieren") if $debug;
		$string = $decoder->decode($string) if ref($decoder);
	}

	$string =~ s/ä/&auml;/g;
	$string =~ s/ö/&ouml;/g;
	$string =~ s/ü/&uuml;/g;
	$string =~ s/Ä/&Auml;/g;
	$string =~ s/Ö/&Ouml;/g;
	$string =~ s/Ü/&Uuml;/g;

	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return($string);
}

sub prepare_user_prefs {
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;


	my ($self, $input_ref) = @_;
	my %input_tmp = %$input_ref;
	my %input;
	foreach my $key(keys %input_tmp){
		if ($key =~ /^special_/) {
			$key =~ s/^special_//;
			$input{$key} = $input_tmp{'special_'.$key};
		}
		else{
			$input{$key} = $input_tmp{$key};	
		}
		
	}
	

	my @new_user_prefs;
	my @ok_languages = ();
	my @ok_locales = ();
	my $ok_locales_all = '';
	my %lang = spam_lang();
	my %locale = spam_locale();

	foreach(keys %input){
		next unless (/^lang_/);
		next if (/^lang_(europe|china|russia|other)$/);
		
		logline("debug","input=$_ lang=".$input{$_}) if $debug;
		
		push @ok_languages, $input{$_};
		push @ok_locales, 'en' if $_ eq 'en';
		push @ok_locales, 'ru' if $_ eq 'ru';
		push @ok_locales, 'ja' if $_ eq 'ja';
		push @ok_locales, 'ko' if $_ eq 'ko';
		push @ok_locales, 'th' if $_ eq 'th';
		push @ok_locales, 'zh' if $_ eq 'zh';

		$ok_locales_all = '1' if (/^$locale{'all'}$/);
	}

	
	
	foreach(@{$self->file_content}){
		if(/^required_hits\s+/){
			push @new_user_prefs, "required_hits\t\t$input{'pspam_level'}";
		}
		elsif(/^report_safe\s+/){
			push @new_user_prefs, "report_safe\t\t$input{'report_safe'}";
		}
		elsif(/^ok_languages\s+/){
			push @new_user_prefs, "ok_languages\t\t" . join(' ', @ok_languages);
		}
		elsif(/^ok_locales\s+/){
			push @new_user_prefs, "ok_locales\t\t" . join(' ', @ok_locales) unless $ok_locales_all;
			push @new_user_prefs, "ok_locales\t\tall" if $ok_locales_all;
		}
		elsif(! /^\s*score\s+/i) {
			push @new_user_prefs, $_;
		}
	}
	
	# gather scores 
	my %scores;
	my @scores_content =  split m(\r\n|\r|\n), $input{'scores'};
	
	foreach(@scores_content){
		
		next unless(/\s+\-?[0-9\.\-]+\s*$/);
		s/^\s*score\s*//i;
		
		my ($rule,$score) = split m(\s+), $_, 2;
		
		if(!defined($rule) || !defined($score)){
				next;
		}
		
		$scores{$rule} = $score;
				
		logline("debug",'score ' . $rule . ' ' . $score) if $debug;

	}
	
	foreach my $rule (sort keys %scores){
		push @new_user_prefs, 'score ' . $rule . ' ' . $scores{$rule};
	
		# no more shortcircuit, schwanzus longus	
		#if($scores{$rule} >= $input{'cspam_level'}){
		
		#	push @new_user_prefs, 'shortcircuit ' . $rule . ' spam';
		
		#}
				
	}
		
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return(\@new_user_prefs);
}

sub del_array_duplicates{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;
	my %all=();
	@all{@_}=1;
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return (keys %all);
}

sub is_ftp_limited{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $ftpcount = '';

	if(-e "/etc/vsd/ftp"){
		logline("debug","/etc/vsd/ftp existiert") if $debug;
		#also entry server
		open(FTP,"/etc/vsd/ftp");
			while(<FTP>){
				$ftpcount = $_;
				chomp $ftpcount;
			}
		close(FTP);
		$ftpcount = '0' if !$ftpcount;
		logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
		return($ftpcount);
	}
	else{
		logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
		return(undef);
	}
}

sub add_mysqldb{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $input_ref = shift;
	my %input = %$input_ref;
	my($dbh, $mysql_connection_error) =  mysql_connect();
	my $db_host	= $ENV{HTTP_HOST};
	$db_host = 'localhost' if $fb;
	my $db = $input{'db'};

	#achtung,achtung: $db müssen in Backtickets, ' und " funktionieren nicht, ebenso prepared statements. Fehler in DBD::mysql
	my $query = qq~CREATE DATABASE IF NOT EXISTS `$db`~;
	my $sth = $dbh->prepare($query);
	if (!$sth) {
		logline("debug","<<< Leaving ".(caller(0))[3]."() by error.") if $debug;
		return("Error:" . $dbh->errstr . "\n");
	}

	if(!$sth->execute){
		logline("debug","<<< Leaving ".(caller(0))[3]."() by error.") if $debug;
		return("Error:" . $dbh->errstr . "\n");
	}

	$dbh->disconnect if $dbh;
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return();
}

sub add_mysqluser{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $input_ref = shift;
	my %input = %$input_ref;
	my($dbh, $mysql_connection_error) =  mysql_connect();
	
	#my $db_host = `grep -m1 han-solo.net /etc/hosts | awk '{print \$2}'`;
	my $db_host = `sed 's/^.* // ; s/\$/.han-solo.net/' /etc/FQDN 2>/dev/null`;
	chomp $db_host;
	#my $db_host	= $ENV{HTTP_HOST};
	#$db_host = 'localhost' if $fb;
	my $db = $input{'db'};
	my $dbuser = $input{'dbuser'};
	my $dbpass = $input{'dbpass'};

	#aus unerklärlichen Gründen darf das Passwort nicht in Backtickets sondern muss in einfachen Anführungszeichen
	# mysql8
	my $query = qq~CREATE USER `$dbuser`\@`$db_host` IDENTIFIED BY '$dbpass'~;
	my $sth = $dbh->prepare($query);
	if (!$sth) {
		logline("debug","<<< Leaving ".(caller(0))[3]."() by error.") if $debug;
		return("Error:" . $dbh->errstr . "\n");
	}
	
	if(!$sth->execute){
		logline("debug","<<< Leaving ".(caller(0))[3]."() by error.") if $debug;
		return("Error:" . $dbh->errstr . "\n");
	}
		
	$query = qq~GRANT ALL PRIVILEGES ON `$db`.* TO `$dbuser`\@`$db_host`~;
	my $sth = $dbh->prepare($query);
	if (!$sth) {
		logline("debug","<<< Leaving ".(caller(0))[3]."() by error.") if $debug;
		return("Error:" . $dbh->errstr . "\n");
	}

	if(!$sth->execute){
		logline("debug","<<< Leaving ".(caller(0))[3]."() by error.") if $debug;
		return("Error:" . $dbh->errstr . "\n");
	}
	
	#additional user@localhost for use via socket or localhost
	$query = qq~CREATE USER `$dbuser`\@`localhost` IDENTIFIED BY '$dbpass'~;
	$sth = $dbh->prepare($query);
	if (!$sth) {
		logline("debug","<<< Leaving ".(caller(0))[3]."() by error.") if $debug;
		return("Error:" . $dbh->errstr . "\n");
	}

	if(!$sth->execute){
		logline("debug","<<< Leaving ".(caller(0))[3]."() by error.") if $debug;
		return("Error:" . $dbh->errstr . "\n");
	}
	
	$query = qq~GRANT ALL PRIVILEGES ON `$db`.* TO `$dbuser`\@`localhost`~;
	$sth = $dbh->prepare($query);
	if (!$sth) {
		logline("debug","<<< Leaving ".(caller(0))[3]."() by error.") if $debug;
		return("Error:" . $dbh->errstr . "\n");
	}

	if(!$sth->execute){
		logline("debug","<<< Leaving ".(caller(0))[3]."() by error.") if $debug;
		return("Error:" . $dbh->errstr . "\n");
	}

	$dbh->disconnect if $dbh;
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return();
}

sub add_ftpuser{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $input_ref = shift;
	my %input = %$input_ref;

	#foreach(keys %input){
	#logline("debug",qq~$_=$input{$_}\n~) if $debug;
	#}

	my $path = $input{'home'};
	my $ftpcount = '';
	my $error = '';

	# user immer klein
	$input{'ftpuser'} = lc($input{'ftpuser'});
	
	if(!length($input{'gecos'})){
		$input{'gecos'} = $input{'ftpuser'};
	}
	
	# wenn in Formular kein Pfad angegeben, dann Standardpfad /home/httpd/docs/username
	if ($fb) {
		$path = '/usr/local/www/apache24/noexec/' . $input{'ftpuser'} unless $input{'home'};
	}
	else{
		$path = '/home/httpd/docs/' . $input{'ftpuser'} unless $input{'home'};
	}

	# wenn neuer vhost mit FTP-User angelegt wird, dann homeverzeichnis auf documentroot legen
	$path = $input{'droot'} if $input{'droot'};

	#checken ob Anzahl ftp user begrenzt (entry server)
	if(-e "/etc/vsd/ftp"){
		logline("debug","/etc/vsd/ftp existiert") if $debug;
		#also entry server
		open(FTP,"/etc/vsd/ftp");
			while(<FTP>){
				$ftpcount = $_;
				chomp $ftpcount;
			}
		close(FTP);
		logline("debug","/etc/vsd/ftp -> $ftpcount") if $debug;
		if($ftpcount > 0 && $path =~ /^\/home\/httpd\//){
			#ok - ftp User vorhanden und Pfad des Users im Apache Bereich
			$ftpcount--;
			#-f für FTP Fähigkeiten
			$error = `/usr/sbin/adduser -f -h '$path' -d '$input{'gecos'}' -q $input{'ftpquota'}M -p '$input{'ftppass'}' -n '$input{'ftpuser'}' 2>&1`;
			open(FTP,">/etc/vsd/ftp");
				print FTP $ftpcount;
			close(FTP);
			logline("debug","/etc/vsd/ftp -> $ftpcount # neu geschrieben") if $debug;
		}
		else {
			# wenn /etc/vsd/ftp leer oder null, dann können keine weiteren FTP- user angelegt werden.
			$error = "Auf diesem Servertyp können keine weiteren FTP-Benutzer angelegt werden";
		}
	}
	else {
		# wenn /etc/vsd/ftp nicht existiert, dann gibt es keine ftp-Begrenzung, z.Z. alle Reseller- und ded. Server
		logline("debug","/etc/vsd/ftp existiert nicht") if $debug;
		##User anlegen
		#-f für FTP Fähigkeiten

		if($fb){
			logline("debug",qq~/usr/iports/bin/sudo /usr/sbin/useradd -f -h \'$path\' -d \'$input{'gecos'}\' -q $input{'ftpquota'}M -p \'$input{'ftppass'}\' -n \'$input{'ftpuser'}\'\n~) if $debug;
			$error = `/usr/iports/bin/sudo /usr/sbin/useradd -f -h '$path' -d '$input{'gecos'}' -q $input{'ftpquota'}M -p '$input{'ftppass'}' -n '$input{'ftpuser'}' 2>&1`;
			#chpass ist ne tratschtante.
			$error =~ s/chpass: user information updated\n//;
		}
		else{
			logline("debug",qq~/usr/sbin/adduser -f -h \'$path\' -d \'$input{'gecos'}\' -q $input{'ftpquota'}M -p \'$input{'ftppass'}\' -n \'$input{'ftpuser'}\'\n~) if $debug;
			$error = `/usr/sbin/adduser -f -h '$path' -d '$input{'gecos'}' -q $input{'ftpquota'}M -p '$input{'ftppass'}' -n '$input{'ftpuser'}' 2>&1`;
		}
	}
        
        #solange es geht nehmen wir mal usermod
        if($fb && $input{'suexec'}){
                $error= `/usr/iports/bin/sudo /usr/sbin/usermod -h '$path' -u '$input{'ftpuser'}' -d "'$input{'gecos'}'" -o 2>&1`;
        }
        
	logline("error",$error) if $error;
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return($error);
}

sub del_ftpuser{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $ftpuser = shift;
	my $delete_home = shift;
	my $ftpcount = '';
	my $result = '';

	unless ($fb){
		my $rights = `listrights $ftpuser`;
		chomp $rights;
		$rights =~ s/^[^:]*: ?//;
		my @rights = split(/\, /,$rights);
		logline("debug","privs  [@rights] von $ftpuser") if $debug;
		foreach my $right (@rights){
			# wenn es Datei /etc/vsd/ftp gibt, dann ist Anzahl an FTP-Usern begrenzt.
			# Anzahl der FTP-User, die noch angelegt werden dürfen um einen erhöhen.
			if($right eq 'ftp' && -e "/etc/vsd/ftp"){
				logline("debug","/etc/vsd/ftp existiert") if $debug;
				#also entry server
				open(FTP,"/etc/vsd/ftp");
					while(<FTP>){
						$ftpcount = $_;
						chomp $ftpcount;
					}
				close(FTP);
				$ftpcount++;
				open(FTP,">/etc/vsd/ftp");
				print FTP $ftpcount;
				close(FTP);
			}
		}
	}

	logline("debug","delete_home=$delete_home") if $debug;

	# entweder soll home-Verzeichnis gelöscht werden
	if($delete_home eq '1'){
		$result = `/usr/iports/bin/sudo /usr/sbin/userdel -r -n '$ftpuser'` if $fb;
		$result = `/usr/sbin/deluser -r -n '$ftpuser'` unless $fb;
	}
	# oder home-Ordner des gelöschten Users dem User admin zugeordnet werden
	elsif($delete_home eq '0'){
		$result = `/usr/iports/bin/sudo /usr/sbin/userdel -a -n '$ftpuser'` if $fb;
		$result = `/usr/sbin/deluser -a -n '$ftpuser'` unless $fb;
	}
	# oder user löschen und nichts mit home-Ordner machen, k wie keep
	elsif($delete_home eq '2'){
		# zuerst gucken, ob es Dateien gibt, die dem User zugeordnet sind. Wenn ja dann Fehlermeldung
		# da nach Löschen von User sonst Dateien ohne User entstehen und vuserquota nicht mehr der summierten userquota entspricht
		my $passwd = file->new({file_name => '/etc/passwd'});
		$passwd->read_file;
		my %passwd = %{$passwd->file_parsed_hash()};
		my $home = $passwd{$ftpuser}{'home'};
		logline("debug","<<< Leaving ".(caller(0))[3]."().") if ($debug && !$home);
		return('L__Das Verzeichnis existiert nicht__L') unless $home;

		$result = `find $home -user $ftpuser 2>/dev/null`;
		if($result){
			$result =~ s/\n/<br \/>/g;
			logline("debug","<<< Leaving ".(caller(0))[3]."() - existing files.") if $debug;
			return("L__Es existieren noch Dateien oder Verzeichnisse die dem zu löschenden FTP-User gehören:__L <br />$result<br />L__Wählen Sie eine andere Löschoption oder ordnen die Dateien einem anderen FTP-User zu.__L");
		}

		$result = `/usr/iports/bin/sudo /usr/sbin/userdel -k -n '$ftpuser'` if $fb;
		$result = `/usr/sbin/deluser -k -n '$ftpuser'` unless $fb;
	}
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;        
	return($result);
}

sub encrypt_htpasswd{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $password = shift;
	my @salt_chars = ('A' .. 'Z', 0 .. 9, 'a' .. 'z', '.', '/');
	my $salt = join '', @salt_chars[rand 64, rand 64];
	my $encrypted = crypt($password, $salt);

	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return($encrypted);
}

sub encrypt {
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my ($passwordfile,$encpass) = @_;
	$encpass = encode('utf-8', "$encpass");
	my $cyphertext = "";
	my $number = '';
	my $salt = 0;
	my @cypher = unpack('C*',$encpass);
	my @salt = ("43","112","120","36","96","94","68","43","62","15","89","12");

	foreach $number (@cypher){
		$number += $salt[$salt];
		$cyphertext .= pack('C*',$number);
		$salt++;
				
		if(! length($passwordfile) && $salt == scalar @salt){
				$salt = 0;
		}
		
	}
	
	if(length($passwordfile) && $easytecc_custom_conf_entries{'ShowMailuserPasswords'} ne "NO" ){
		system("rm -f $passwordfile") if -e $passwordfile;

		open (CRYPT , ">>$passwordfile");
			print CRYPT $cyphertext;
		close(CRYPT);
		
		logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	} else {
		return $cyphertext;
	}	
}

sub spam_lang{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my %lang = (
		europe => 'eu|ca|da|nl|fi|fr|fy|el|hu|is|ga|it|no|pl|pt|sco|gd|sr|sk|sl|es|sv|cy',
		china => 'zh|id|ja|ko|ms|ne|tl|th|vi|hi',
		russia => 'sq|ar|hy|bs|bg|be|hr|cs|et|ka|lv|lt|mr|fa|ro|ru|sa|ta|tr|uk',
		other => 'af|am|eo|he|la|qu|rm|sw|yi'
	);
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return(%lang);
}

sub spam_locale{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my %locale = (
		en => 'en',
		zh => 'zh',
		ja => 'ja',
		ko => 'ko',
		th => 'th',
		ru => 'ru',
		all => 'eu|ca|da|nl|fi|fr|fy|el|hu|is|ga|it|no|pl|pt|sco|gd|sr|sk|sl|es|sv|cy|af|am|eo|he|la|qu|rm|sw|yi|id|ms|ne|tl|vi|hi|sq|ar|hy|bs|bg|be|hr|cs|et|ka|lv|lt|mr|fa|ro|sa|ta|tr|uk'
	);
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return(%locale);
}

sub system_quota_reached{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $sum_limit = '';
	my $sum_use = '';
	my $serverlimit = '';

	if ($fb) {
		open(QUOTASTATS,"/usr/sbin/quotastats|");
	}
	else{
		open(QUOTASTATS,"/usr/sbin/sqt|");	
	}
	while(<QUOTASTATS>){
		next if /^(=|User name)/;
		#Allocated/used   |        |    14588928K|    2950752K
		#Max/available    |        |    10240000K|    7289248K
		if (/^Allocated/) {
			($sum_limit,$sum_use) = (split(/\|\s+/,$_))[2,3];
			$sum_limit =~ s/K//;
			$sum_use =~ s/K//;
			$sum_limit /= 1024;
			$sum_use /= 1024;
		}
		elsif (/^Max\/available/) {
			($serverlimit) = (split(/\|\s+/,$_))[2];
			$serverlimit =~ s/K//;
			$serverlimit /= 1024;
		}
	}
	close(QUOTASTATS);

	if($serverlimit - $sum_use -10 < 0){
		logline("debug","<<< Leaving ".(caller(0))[3]."() - Quotagrenze erreicht.") if $debug;
		return('Quotagrenze erreicht');
	}
	else{
		logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
		return();
	}
}

sub result_debug{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $results = shift;
	my %error_fields = ();

	if( $results->success ){
		logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
		return();
	}

	# Print the name of missing fields
	if ( $results->has_missing ) {
		for my $f ( $results->missing ) {
			logline("debug","$f is missing") if $debug;
			$error_fields{$f} = 'L__Fehlende Eingabe.__L';
		}
	}

	# Print the name of invalid fields
	if ( $results->has_invalid ) {
		for my $f ( $results->invalid ) {
			logline("debug","$f is invalid: ".${$results->invalid($f)}[0]) if $debug;
			$error_fields{$f} =  ${$results->invalid( $f )}[0];
			#$error_fields{$f} =  "L__Ungültige Eingabe__L: " . ${$results->invalid( $f )}[0];

			# meta können wir nicht nehmen:
			# http://search.cpan.org/~markstos/Data-FormValidator-4.63/lib/Data/FormValidator/Results.pm#meta%28%29
			# This function does not currently support multi-valued fields. If it does in the future, the above syntax will still work.
			#
			#Darum mißbrauchern wir inm validator set_current_constraint_name() zur Übergabe von Fehlermeldung aus Validator um Fehler dem
			#Formularfeld zuzuordnen. Z.B.
			# $dfv->set_current_constraint_name('gibs nich');
			#

			#foreach(@{$results->invalid( $f )}){
			#logline("debug",$_ . "") if $debug;
			#$html_result .=  $_ . '<br />';
			#$html_result .=  $results->meta($f)->{'error'} . '<br />';
			#}
		}
	}

	# Print unknown fields to debug log
	if ( $results->has_unknown ) {
		for my $f ( $results->unknown ) {
			logline("debug","$f is unknown") if $debug;
		}
	}

	# Print valid fields to debug log
	for my $f ( $results->valid() ) {
		logline("debug",'valid ' . $f. " =  ". $results->valid( $f )) if $debug;
	}

	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return(\%error_fields);
}

sub filesize{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $filesize = shift;
	my $filesize_text = '';

	if ($filesize > 1023999){
		#MB
		$filesize /= 1024000;
		$filesize *= 100;
		$filesize = int($filesize);
		$filesize /= 100;
		$filesize =~ s/\./\,/;
		$filesize .= ",00" unless ($filesize =~ /\,/);
		$filesize .= "0" if ($filesize =~ /\,\d{1}$/);
		$filesize_text = "MB";
	}
	elsif ($filesize > 10){
		#KB
		$filesize /= 1024;
		$filesize *= 100;
		$filesize = int($filesize);
		$filesize /= 100;
		$filesize =~ s/\./\,/;
		$filesize .= ",00" unless ($filesize =~ /\,/);
		$filesize .= "0" if ($filesize =~ /\,\d{1}$/);
		$filesize_text = "kB";
	}
	elsif ($filesize > 0){
		#bytes
		$filesize = "0,01";
		$filesize_text = "kB";
	}
	else {
		#bytes
		$filesize = "0,00";
		$filesize_text = "kB";
	}
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;	   
	return($filesize . ' ' . $filesize_text);
}

sub getsize{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $filesize = shift;
	my $filesize_text = '';

	if ($filesize >= 1024000){
		#GB
		$filesize /= 1024000;
		$filesize *= 100;
		$filesize = int($filesize);
		$filesize /= 100;
		#$filesize .= ".00" unless ($filesize =~ /\./);
		#$filesize .= "0" if ($filesize =~ /\.\d{1}$/);
		$filesize_text = "GB";
	}
	elsif ($filesize > 1023){
		#MB
		$filesize /= 1024;
		$filesize *= 100;
		$filesize = int($filesize);
		$filesize /= 100;
		#$filesize .= ".00" unless ($filesize =~ /\./);
		#$filesize .= "0" if ($filesize =~ /\.\d{1}$/);
		$filesize_text = "MB";
	}
	elsif ($filesize > 0){
		#KB
		$filesize *= 100;
		$filesize = int($filesize);
		$filesize /= 100;
		$filesize .= ".00" unless ($filesize =~ /\./);
		$filesize .= "0" if ($filesize =~ /\.\d{1}$/);
		$filesize_text = "kB";
	}
	else {
		#bytes
		$filesize = "0.00";
		$filesize_text = "kB";
	}

	$filesize .= " $filesize_text";
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;    
	return ($filesize);
}

sub getdate {
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $filedate = shift;

	my @months = ('Jan','Feb','Mar','Apr','Mai','Jun','Jul','Aug','Sep','Okt','Nov','Dez');
	my ($min, $hr, $day, $mon, $yr) = (localtime($filedate))[1,2,3,4,5];

	if ($min < 10) { $min = "0$min"; }
	if ($hr < 10) { $hr = "0$hr"; }
	if ($day < 10) { $day = "0$day"; }

	if ($yr > 99) {
		$yr-=100;

		if ($yr < 10) { $yr = "200$yr"; }
		elsif ($yr >= 10 && $yr < 100) { $yr = "20$yr"; }
		else { $yr = "2$yr"; }
	}
	else {
		if ($yr < 10) { $yr = "190$yr"; }
		else { $yr = "19$yr"; }
	}

	$filedate = "$day-$months[$mon]-$yr ($hr\:$min)";
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return($filedate);
}

sub quotabar{
	#obsolete, no longer used
	return();
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my($quota, $used, $quotainfo) = @_;
	# wegen Illegal division by zero
	my $percentval = ($used/$quota)*100 if $quota > 0;
	$percentval = 0 if $quota == 0;
	$percentval = sprintf("%.0f", $percentval);
	my $bar_usage = $percentval;
	my $bar_free = 100 - $bar_usage;

	my $bar_template = '';

	if(($quota * .8) <= $used){
		$bar_template = HTML::Template->new(filename => 'bar_red.html') unless $quotainfo eq 'MB_form';
		$bar_template = HTML::Template->new(filename => 'bar_red_MB_form.html') if $quotainfo eq 'MB_form';
	}
	else{
		$bar_template = HTML::Template->new(filename => 'bar_green.html') unless $quotainfo eq 'MB_form';
		$bar_template = HTML::Template->new(filename => 'bar_green_MB_form.html') if $quotainfo eq 'MB_form';
	}

	# wenn Quota überschritten würde für Balken ungültiger Wert rauskommen, so daß Balken zu lang
	if($bar_usage > 100){
		$bar_usage = '100';
		$bar_free = '0';
	}

	$bar_template->param('bar_usage' => $bar_usage);
	$bar_template->param('bar_free' => $bar_free);

	if($quotainfo eq 'all'){
		$bar_template->param('quotainfo' => easytecc3::getsize($quota) . ' L__Quota__L');
		$bar_template->param('quotaused' => $percentval .  'L__% in Benutzung__L');
	}
	elsif($quotainfo eq 'used'){
		$bar_template->param('quotaused' => easytecc3::getsize($used) . ' / ' . $percentval .  '%');
	}
	elsif($quotainfo eq 'MB_form'){
		$bar_template->param('quotainfo' => 'MB' . ' L__Quota__L');
		$bar_template->param('quotaused' => $percentval .  'L__% in Benutzung__L');
	}
	elsif($quotainfo eq 'table'){
		# aktuell nix, nur Balken anzeigen
	}

	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return($bar_template->output);
}

sub quota_alert{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my($quota, $used, $alert_factor) = @_;

	if(($quota * $alert_factor) <= $used){
		return(1);
	}

	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
}

sub table{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $template_ref = shift;
	my $table_header = shift;
	my $table_content_ref = shift;
	my $table_id = shift;

	my @table_content = @$table_content_ref;
	my $template = $$template_ref;
	my @loop_data = ();

	my $table_template = HTML::Template->new(filename => 'table-template.html');

	my $odd = '1';
	my $odd_class = ' class="odd"';
	foreach(@table_content){
		my %row_data = ();
		my($field1, $field2, $field3, $field4, $field5, $field6, $field7) = split/\|/, $_;
		$row_data{'1'} = '<tr' . $odd_class . '><td>' . $field1 . '</td>' unless $odd % 2;
		$row_data{'1'} = '<tr><td>' . $field1 . '</td>' if $odd % 2;
		$row_data{'2'} = '<td>' . $field2 . '</td>';
		$row_data{'3'} = '<td>' . $field3 . '</td>';
		$row_data{'4'} = '<td>' . $field4 . '</td>';
		$row_data{'5'} = '<td>' . $field5 . '</td>';
		$row_data{'6'} = '<td>' . $field6 . '</td>';
		$row_data{'7'} = '<td>' . $field7 . '</td></tr>';
		#logline("debug","table fields=$field1, $field2, $field3, $field4, $field5, $field6, $field7") if $debug; 
		push(@loop_data, \%row_data);
		$odd++;
	}

	$table_template->param('table_header' => $table_header);
	$table_template->param('table_content' => \@loop_data);

	$template->param($table_id => $table_template->output);
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;    
}

sub substr_e4{
	my $string = shift;
	
	$string =~ s#^/usr/local/www/apache24/(noexec|data)/#.../#;
		
	if (length($string) > 100) {
		return('...' . substr($string, -100));
	}
	else{
		return($string);
	}
	
}

sub table_e4{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $template = shift;
	my $table_data = shift;
	my $table_template = shift;
	
	logline("debug","template=$template table_data=$table_data table_template=$table_template") if $debug; 
	
	my $table_var = $table_template;
	$table_var =~ s/\.html//;
	my $tr_html = '';
	my $odd = '1';
	
	foreach my $hash_domain( sort keys %{$table_data}){
		my %tr_hash = %{$table_data->{$hash_domain}};
		#logline("debug","table tr hash_domain=$hash_domain") if $debug; 
		my $tr = HTML::Template->new(filename => "html-templates/$table_template");	
			
			foreach my $td_entry(sort keys %tr_hash){				
			$tr->param($td_entry => $tr_hash{$td_entry});
			#obsolete, now done by jquery table plugin
			#$tr->param('odd_even' => ' odd ') unless $odd % 2;
			#$tr->param('odd_even' => ' even ') if $odd % 2;
			#logline("debug","table tr key=$td_entry") if $debug; 
			}
			
			if($fb){
				$tr->param(FB => '1');
			}
			
		$tr_html .= $tr->output;
		$odd++;
	}

	#logline("debug","table tr_html=$tr_html") if $debug; 
	$template->param($table_var => $tr_html);
	#logline("debug","table_e4=$tr_html") if $debug; 
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;    
}

sub template_loop{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $input_ref = shift;
	my %input = %$input_ref;
	my $template_ref = shift;
	my $loop_content_ref = shift;
	my $loop = shift;
	my $selected = shift;

	my @loop_content = @$loop_content_ref;
	my $template = $$template_ref;
	my @loop_data = ();
	my $count = '1';

	# leeres Feld an den Anfang einfügen, damit de-selektieren mglich, &nbsp; damit html validiert
	# aber nicht bei IP auswählen
	unshift @loop_content, '&nbsp;' unless $loop eq 'ip_select' or $loop eq 'php_select' or $loop eq 'mailserver_cert_select';

	foreach(@loop_content){
		logline("debug","template_loop=$_ selected=$selected") if $debug;
		my %row_data = ();

		$row_data{'option_selected'} = 'SELECTED' if $selected eq $_;
		#logline("debug","loop:$selected\tloopcon:$_") if $debug;

		my($field1, $field2, $field3, $field4, $field5, $field6, $field7) = split/\|/, $_;
		$row_data{'1'} =  $field1;
		$row_data{'2'} = $field2;
		$row_data{'3'} = $field3;
		$row_data{'4'} = $field4;
		$row_data{'5'} = $field5;
		$row_data{'6'} = $field6;
		$row_data{'7'} = $field7;
		$row_data{'count'} = $count;
		push(@loop_data, \%row_data);
		$count++;
	}

	#error_class wird nur nach absenden von Formular angezeigt
	if($input{'action'} =~ /^exec_/ || $input{'form_submit'}){
		$template->param('select_name' => qq~"$loop"~ . qq~ <TMPL_VAR NAME=error_class_$loop> ~);
	}
	else{
		$template->param('select_name' => qq~"$loop"~);
	}
	# in Template für loop kann der Name der loop nicht als Templatevariable einefügt werden, da Verschachtelung von Templatevariablen nicht möglich
	# $loop hat am Ende Ziffer, die weg ergibt Template-loop-namen
	my $loopname = $loop;
	#$loopname =~ s/[0-9]{1,5}$//g;
	$template->param($loopname => \@loop_data);
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;    
}

sub template_loop_e4{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $input_ref = shift;
	my $template_ref = shift;
	my $loop_content_ref = shift;
	my $loop = shift;
	my $selected = shift;
	
	#bei change_forward können mehrere vorselektiert sein, was | separiert übergeben wird
	#splitten, in Hash rein und wenn ich foreach hashkey vorhanden, dann vorselektieren
	my @selected = split /\|/, $selected;
	my %selected_hash = map { $_ => 1 } @selected;

	my %input = %$input_ref;
	my @loop_content = @$loop_content_ref;
	my $template = $$template_ref;
	my @loop_data = ();
	my $count = '1';

	# leeres Feld an den Anfang einfügen, damit de-selektieren mglich, &nbsp; damit html validiert
	# aber nicht bei IP auswählen
	#unshift @loop_content, '&nbsp;' unless ($loop eq 'ip_select'||
	#										$loop eq 'freefields');

	my $loop_content_count = scalar @loop_content;
	foreach(@loop_content){
		#logline("debug","template_loop_e4 loop=$loop loop_content=$_ selected=$selected") if $debug;
		my %row_data = ();

		#$row_data{'option_selected'} = 'SELECTED' if $selected eq $_;
		$row_data{'option_selected'} = 'SELECTED' if exists $selected_hash{$_};
		#logline("debug","loop:$selected\tloopcon:$_") if $debug;

		my($field1, $field2, $field3, $field4, $field5, $field6, $field7) = split/\|/, $_;
		#logline("debug","foreach loop_content:field1=$field1 field2=$field2 field3=$field3 field4=$field4 field5=$field5 field6=$field6 field7=$field7") if $debug;
		$row_data{'1'} =  $field1;
		$row_data{'2'} = $field2;
		$row_data{'3'} = $field3;
		$row_data{'4'} = $field4;
		$row_data{'5'} = $field5;
		$row_data{'6'} = $field6;
		$row_data{'7'} = $field7;
		$row_data{'count'} = $count;
		
		if ($loop eq 'freefields') {
			my $form_field = 'freefield' . $field1 . '_' . $field2;
			
			#logline("debug","loop freefields form_field=$form_field main error_fields=" . $main::error_fields{$form_field}) if $debug;
			
			if (length $main::error_fields{$form_field}) {
			$row_data{'error_text'} = qq~<span class="help-block">$main::error_fields{$form_field}</span>~;
			}
			
			if (length $input{$form_field}) {
				$row_data{'3'} = $input{$form_field};
			}
			
		}
		
		
			if ($count == $loop_content_count) {
				$row_data{'last_count'} = $count;
			}
		
		push(@loop_data, \%row_data);
		$count++;
	}

	#logline("debug","foreach loop_content: error_field user_select1=" . $main::error_fields{"user_select1"}) if $debug;

	if (length $main::error_fields{$loop}) {
		$template->param('error_text' => qq~<span class="help-block">$main::error_fields{$loop}</span>~);
	}
	
	
	#error_class wird nur nach absenden von Formular verwendet und damit html_error() HTML-Templatevariablen nach Ausgabe von Untertemplate
	#benutzen kann ist Trick notwendig. Z.B. in Untertemplate user_select.html, welches in table_tr_change_forward.html inkludiert wird befindet sich Variable
	#<TMPL_VAR NAME=error_class>
	#Nun füllen wir diese Variable mit dem Text
	#<TMPL_VAR NAME=error_class_$loop>
	#so dass im html-Quelltext z.B.
	#<TMPL_VAR NAME=error_class_user_select2>
	#steht. Dies landet bei Fehler so bei html_error(), wo ein foreach alle %error_fields durchgeht und die CSS-Klasse has-error setzt
	#ist Zeile
	#$html_tmp2->param('error_class_' . $_ => "has-error");
		
	if($input{'action'} =~ /^exec_/ || $input{'form_submit'}){
		#$template->param('select_name' => qq~"$loop"~ . qq~ <TMPL_VAR NAME=error_class_$loop> ~);
		$template->param('select_name' => $loop);
		$template->param('error_class' => qq~<TMPL_VAR NAME=error_class_$loop>~);
	}
	else{
		$template->param('select_name' => $loop);
	}
	# in Template für loop kann der Name der loop nicht als Templatevariable einefügt werden, da Verschachtelung von Templatevariablen nicht möglich
	# $loop hat am Ende Ziffer, die weg ergibt Template-loop-namen
	my $loopname = $loop;
	$loopname =~ s/[0-9]{1,5}$//g;
	$template->param($loopname => \@loop_data);
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;    
}

sub generatewebalizer{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	#($domainnames[0],$input{'documentroot'})
	my($domain,$home) = @_;
	
	$home = lc($home);

	##/stats Verzeichnis
	#`mkdir $home/stats`;
	#`chmod 777 $home/stats`;

	my $got = `/usr/sbin/admmkdir -p $home` if (! -d "$home");
	$got = `/usr/sbin/admmkdir -p $home/stats` if (! -d "$home/stats");

	##conf Datei für diesen Host
	open (OF,">/usr/local/etc/$domain.conf");
		open (IF,"/usr/local/etc/webalizer.conf.sample");
			while (<IF>) {
				s/^LogFile.*$/LogFile        \/home\/web\/log\/access_log_$domain/g;
				s/^OutputDir.*$/OutputDir         $home\/stats/g;
				s/^#?HistoryName.*$/HistoryName         $domain.hist/g;
				s/^#?HostName.*$/HostName       www.$domain/g;
				s/^#Incremental\s.*$/Incremental    yes/g;
				s/^#IncrementalName.*$/IncrementalName        webalizer.current/g;
				print OF $_;
			}
		close(IF);
	close(OF);

	protect("$home/stats");

	#Zeit/Datum
	my($sek,$min,$std,$mday,$mon,$year) = (localtime(time))[0,1,2,3,4,5];
	$mon++;
	if ($mon < 10) { $mon = "0".$mon; }
	if ($mday< 10) { $mday= "0".$mday; }
	if ($year< 10) { $year= "0".$year; }
	if ($std< 10) { $std= "0".$std; }
	if ($min< 10) { $min= "0".$min; }
	if ($sek< 10) { $sek= "0".$sek; }
	$year += 1900;

	my %monlist = '';
	$monlist{'01'} = "Jan";
	$monlist{'02'} = "Feb";
	$monlist{'03'} = "Mar";
	$monlist{'04'} = "Apr";
	$monlist{'05'} = "May";
	$monlist{'06'} = "Jun";
	$monlist{'07'} = "Jul";
	$monlist{'08'} = "Aug";
	$monlist{'09'} = "Sep";
	$monlist{'10'} = "Oct";
	$monlist{'11'} = "Nov";
	$monlist{'12'} = "Dec";
	$mon = $monlist{$mon};

	open (OF,">/home/web/log/access_log_$domain");
		print OF "217.80.124.83 - admin [$mday/$mon/$year:$std:$min:$sek +0000] \"GET / HTTP/1.1\" 200 155 \"http://$domain/stats\" \"Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)\"";
	close(OF);

	system("/usr/local/bin/webalizer -c /usr/local/etc/$domain.conf >> /home/web/log/error_log_$domain");

	#alle Dateien auf den Besitzer der Domain setzen:
	my($fileuid, $filegid) = (stat("$home/stats"))[4,5];
	system("/usr/sbin/admchown -u $fileuid -g $filegid -d $home/stats");
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
}

sub protect {
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my($statsdir) = @_;
	my($homedir);

	$homedir = $statsdir;
	$homedir =~ s/\/stats$//;
	my $username = getuserofhome($homedir);

	open (HTACCESS,">$statsdir/.htaccess") || dienice ("Fehler beim erstellen von $statsdir/.htaccess: $!");
		print HTACCESS "AuthUserFile /etc/passwd\n";
		print HTACCESS "AuthName \"Statistikbereich\"\n";
		print HTACCESS "AuthType Basic\n\n";
		print HTACCESS "<Limit POST GET>\n";
		##admin ist immer Berechtigt
		print HTACCESS "require user $username admin\n";
		print HTACCESS "</Limit>\n";
	close(HTACCESS);
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
}

sub getuserofhome {
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my ($homedir) = @_;
	my $user = '';

	##/etc/passwd durchlesen
	open(PASS,"/etc/passwd");
		while(<PASS>){
			chomp;
			push @passwd, "$_";
		}
	close(PASS);

	foreach $line (@passwd){
		if($line =~ /\:$homedir\:/){
			$user = $line;
		}
	}
	($user) = (split(":",$user))[0];

	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return $user;
}

sub clean_session{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $session = shift;
	return unless $session;

	my $params_hashref = $session->dataref();
	my %params = %$params_hashref;

	foreach(keys %params){
		#Sessionkeys die mit _ anfangen sind Systemkeys, diese nicht löschen
		next if /^_/;
		$session->clear($_);
	}
	$session->flush();

	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return();
}


sub mailuser_exists{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $user = email_to_ascii(shift);
	my $mailpasswd = easytecc3::get_mailpasswd();
    my %mailpasswd = %{$mailpasswd->file_parsed_hash()};
	#my %mailpasswd = %{get_mailpasswd()};
	
	logline("debug","checking user $user") if $debug;
	logline("debug","result: $mailpasswd{$user}") if $debug;

	if(not exists $mailpasswd{$user}){
		logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
		return();
	}
	else{
		logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
		return('1');
	}
}

sub ascii_domain{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $domain = shift;
	# Domain kann dddd.de oder auch wildcarddomain *.ddd.de sein
	#wenn wildcard, dann *vor Umwandlung abschneiden, domain umwandlen und * wieder ran, ansonsten schlägt domain_to_ascii fehl
	#domains mit _ nicht erlaubt, direkt zurückgeben
	logline("debug","Testing \$domain = ".$domain) if $debug;
	if($domain =~ /_/){
		logline("debug","<<< Leaving ".(caller(0))[3]."() by _.") if $debug;
		return($domain);
	}
	elsif($domain =~ /^\*\.(.*)$/){
		my $domain = $1;
		$domain = domain_to_ascii($domain);
		logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
		return('*.' . $domain);
	}
	#nur wenn ein . vorkommt kann es Domain sein
	elsif($domain =~ /.+\..+/){
		logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
		return(domain_to_ascii($domain));
	}
	#ohne . nicht umwandeln, würde mit
	#Invalid domain name (toASCII, step 3)
	#fehlschlagen
	else{
		logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
		return($domain);
	}
}

sub is_tld{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;
	my $tld = shift;
	if($tld =~ /^\.[a-z]{2,}$/){
		logline("debug","<<< Leaving ".(caller(0))[3]."() by _.") if $debug;
		return(1);
	}
	else{
		return(undef);	
	}
}

sub is_domain{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;
#ersetzt is_domain aus Data::Validate::Domain, da es nicht mit den neuen TLDs klarkommt
	my $domain = shift;
	if($domain =~ /^([a-z0-9]+(-[a-z0-9\-]+)*\.)+[a-z]{2,}$/){
		logline("debug","<<< Leaving ".(caller(0))[3]."() by _.") if $debug;
		return(1);
	}
	else{
		return(undef);	
	}
	
}

sub is_email{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;
#ersetzt is_email aus Data::Validate::Email, da es nicht mit den neuen TLDs klarkommt
	my $email = shift;
	if($email !~ /^[A-Za-z\.\-\_0-9]{1,}\@[A-Za-z\-\.0-9]{2,}\.[A-Za-z0-9]{2,25}$/){
		logline("debug","<<< Leaving ".(caller(0))[3]."() by _.") if $debug;
		return(undef);
	}
	else{
		return(1);	
	}
	
}

sub get_size_mysql{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	# Grösse der Datenbanken ermitteln
	my $datadir = '';
	my %database_size;
	my $command = '';
	my $mysql = `grep mysql_enable /etc/rc.conf`;

	# > 3
	if($mysql =~ /NO/){
		$datadir = '/var/lib/mysql/data';
	}
	else{
		$datadir = '/var/lib/mysql';
	}

	if ($fb) {
		$datadir = '/var/db/mysql';		
	}

	my ($dbh, $mysql_connect_error) = easytecc3::mysql_connect();
	logline("debug","######################dbh=$dbh error=$mysql_connect_error");
	
	my $query = "SELECT SUM(data_length + index_length) / 1024 AS total_size, table_schema FROM information_schema.TABLES GROUP BY table_schema";
	my $sth = $dbh->prepare( $query );
	$sth->execute;
	while ( my @row = $sth->fetchrow_array ){ # alle rows auslesen
		
		my($size, $database) = @row;
		print DEBUG "size=$size, database=$database\n";
		#$database_size{$database} = easytecc3::getsize($size);
		$database_size{$database} = $size;
	}
		
	#$command = "$easytecc_prefix/get_size_mysql.pl " . $datadir;
	
	#open(COMMAND, "$command|");
	#while(<COMMAND>){
	#	logline("debug","com = $_") if $debug;
	#	# Output in kB
	#	#676     mysql
	#	#88996   ndb_3_fs
	#	my($size, $database) = split /\s+/, $_;
	#	print DEBUG "size=$size, database=$database\n";
	#	#$database_size{$database} = easytecc3::getsize($size);
	#	$database_size{$database} = $size;
	#}
	#close COMMAND;

	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	return(%database_size, $datadir);
}

sub del_autoreply_file{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $file = shift;

	if(extern_mx()){
		my $socket = make_socket();
		logline("debug","nach make_socket()") if $debug;
		print $socket "del_autoreply_file=$file\n" if $file;
	}
	
	if ($file =~ /^\/usr\/local\/etc/){
	`/usr/iports/bin/sudo /usr/sbin/rm.pl $file` if $fb;
	`rm -f $file` unless $fb;
	}
	
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
 }

sub backup_file{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $file = shift;
	return("backup_file: missing argument") unless $file;
	my $backup_dir = '/usr/local/etc/easytecc/backup_files';

	if (! -e "$backup_dir") {
	`mkdir $backup_dir`;
	`chmod 775 $backup_dir`;
	}

	my $dir = $file;
	$dir =~ s/\/[^\/]*$//g;

	my @pieces = split (/\//,$file);
	$file = pop(@pieces);

	opendir DIR, "$dir" or dienice "Kann Verzeichnis $dir nicht öffnen: $!";
		my @allfiles = grep /$file\./, readdir DIR;
	closedir DIR;

	logline("debug","dir=$dir file=$file") if $debug;

	@allfiles = sort @allfiles;
	
	#hat das mal jemand gelesen?		
	#if(scalar(@allfiles) > 9){
	#	logline("debug","#### will delete $dir/$allfiles[9] using $easytecc_prefix/rm.pl") if $debug;		
	#	my $got = `$easytecc_prefix/rm.pl $dir/$allfiles[9]`;
	#	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
	#	return("L__Fehler beim Löschen von__L $dir/$allfiles[9]: $got") if $got;
	#}

	#Zeit/Datum
	my ($sek,$min,$std,$mday,$mon,$year) = (localtime(time))[0,1,2,3,4,5];
	$mon++;
	$year += 1900;
	if ($mon < 10) { $mon = "0".$mon; }
	if ($mday< 10) { $mday= "0".$mday; }
	if ($std < 10) { $std = "0".$std; }
	if ($min< 10) { $min= "0".$min; }
	if ($sek< 10) { $sek= "0".$sek; }

	# Kopie mit Zeit setzen
	# bei httpd.conf|virtmaps|aliases in Verzeichnis der Dateien sichern, ansonsten easytecc-backup-Verzeichnis, weil
	# z.B. in /etc aufgrund fehlender Schreibrechte keine Dateien angelegt werden können.
	
	#$backup_dir = $dir if($file =~ /^(httpd.conf|virtmaps|aliases)$/ && !$fb);
	# nur Backup wenn Datei existiert, weil z.B. bei Neuanlage von spamregeln noch keine Dateien zum sichern vorhanden sind
	if(-e "$dir/$file"){
		logline("debug","$easytecc_prefix/cp.pl $dir/$file $backup_dir/$file.$year$mon$mday$std$min$sek") if $debug;
		my $got = `$easytecc_prefix/cp.pl $dir/$file $backup_dir/$file.$year$mon$mday$std$min$sek` unless $fb;
		my $got = `/usr/iports/bin/sudo /usr/sbin/cp.pl $dir/$file $backup_dir/$file.$year$mon$mday$std$min$sek` if $fb;
		#`chmod 664 $dir/$file $backup_dir/$file.$year$mon$mday$std$min$sek`;
		`chmod 664 $backup_dir/$file.$year$mon$mday$std$min$sek`;
		logline("error","<<< Leaving ".(caller(0))[3]."() by error.") if $got;
		return("L__Fehler beim Backup von__L $dir/$file: $got") if $got;
	}
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
}

sub delete_from_spamconf{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my $special_spamfilter = shift;
	return unless $special_spamfilter;

	my $tmperror = system("cp -f /usr/local/etc/easytecc/spamuser.conf /tmp/spamuser.conf.$$");
	return("can't copy /usr/local/etc/easytecc/spamuser.conf to /tmp/spamuser.conf.$$: $! $tmperror") if $tmperror;
	my @new_file_content;
	open (ORIGF, "/tmp/spamuser.conf.$$") or die "can't open /tmp/spamuser.conf.$$: $!";
	while (<ORIGF>){
		my ($user,$domain) = split(/=/,$_);
		if($user ne $special_spamfilter){
			push @new_file_content, $_;
		}
	}
	close(ORIGF);
	my $file = file->new({file_name => '/usr/local/etc/easytecc/spamuser.conf', file_content => \@new_file_content});
	my $got = $file->write_file;
	logline("error","<<< Leaving ".(caller(0))[3]."().") if $got;
	return($got) if $got;
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;    
}

sub has_twig{
	logline("debug",">>> Entering ".(caller(0))[3]."() from ".(caller(1))[3]."() Line: ".(caller(0))[2]) if $debug;

	my ($filesize, $filedate, $owner, $fileperm) = (stat('/home/httpd/docs/twig'))[7,9,4,2];
	$fileperm = sprintf "%lo", ($fileperm & 07777);

	#twig-roundcube-migration: Button nur anzeigen, wenn /home/httpd/docs/twig existiert und nicht Rechte 700 hat
	if(-d '/home/httpd/docs/twig' && $fileperm != '0700'){
		logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;
		return(1);
	}
	logline("debug","<<< Leaving ".(caller(0))[3]."().") if $debug;	
}


sub logline($$) {
	my $level = shift;
	my $message = shift;
	if($debug){
		chomp $message;
		my ($package, $filename, $line) = caller;
		my $logline = sprintf '(%5.5d)', $line;
		$log->log( level => $level, message => $logline." ".$message);
	}
}

sub dienice ($) {
	# write die messages to the log before die'ing
	my ($package, $filename, $line) = caller;
	logline("alert","$_[0] at line $line in $filename");
	die $_[0];
}

1;