#!/usr/bin/perl -w

# pwreset.pl

# author: Peter Gietz

# Version 0.01
# 10.1.2011
# copyright DAASI international GmbH 2011

use warnings;

unshift (@INC,"../lib");
$ENV{PATH}= "/bin:/usr/bin/";
delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV' };
umask(077);


use vars qw($VERSION $NAME $PATH $PROT);

$VERSION = "0.1";
$NAME = 'pwReset';
$PATH =  '/cgi-bin';

$PROT = 'http';


use HTTP::Daemon;
use HTTP::Status;


use DAASIlib::CONF qw (is_debug);
use DAASIlib::LDAP;
use DAASIlib::LOG;
use DAASIlib::Convert;
use DAASIlib::Data;
use DAASIlib::SMTP;
#use DAASIlib::XML;
#use DAASIlib::Objects;
use DAASIlib::Gettext;

use Crypt::SmbHash qw(lmhash nthash);
use Digest::MD5 qw(md5 md5_hex md5_base64);
use Digest::SHA1  qw(sha1 sha1_hex sha1_base64);
use MIME::Base64;
use Encode qw(encode);
use Unicode::Map8;
use Unicode::String qw(utf16);
use Apache::Session::File;
require HTML::Template;
use URI::Escape;

use utf8;

use strict;
use vars qw( $count $config );
use CGI qw/:standard :html3/;
use CGI::Fast;

local $count =0;

my $conf = new DAASIlib::CONF;

#my $object = new DAASIlib::Objects;

our $data = new DAASIlib::Data;
our ($progname, $progpath, $etcdir, $sysconfig) = $data->getProgramFiles($0);
my $gt = $conf->loadConfig($sysconfig, $progpath, $etcdir, 0, $progname);
$data->init($progname, $conf->{data}{passwordfile});

my $is_verbose = $conf->{data}{verbose};

my $log = new DAASIlib::LOG;
$log->init($progname, $is_verbose);

#$conf->{data}{logfile} =~/^(.+)$/;
#my $logfile = $1;
my $logfile = $conf->{data}{logfile};
my $logger = $log->initLog4Perl($logfile, 
                                $conf->{data}{loglevel},
                                $conf->is_debug() );

$logger->info("$progname started");

if ( $conf->{data}{rewritepath} ) {
    $PATH = &rewritepath($PATH, $conf->{data}{rewritepath});
}



### may be that should become %inputfields ??
our %inputfields = ( 'mail' => { 
                         'name' => 'mail',
			 'type' => 'text',
			 'len' => 40,
			 'label' => 'Email address:',
			 'default' => '',
			 'regexp' => '.+\@.+\..+',
			 'ldapattr' => 'mail',
			 'status' => 'start', 
			 'priority' => 2, 
		     },
		     'login' => { 
                         'name' => 'login',
			 'type' => 'text',
			 'len' => 40,
			 'label' => 'Login name:',
			 'default' => '',
			 'regexp' => '',
			 'ldapattr' => 'uid',
			 'status' => 'start', 
			 'priority' => 1, 
		     },
		     'principalname' => {
                         'name' => 'principalname',
			 'type' => 'text',
			 'len' => 40,
			 'label' => 'TextGrid ID:',
			 'default' => '',
			 'regexp' => '.+\@.+',
			 'rule' => 'must contain an @ character',
			 'ldapattr' => 'edupersonprincipalname',
			 'status' => 'end;start', 
			 'priority' => 3,
			 'usage' => 'ID',
		     },	   
		     'password' => {
                         'name' => 'password',
			 'type' => 'password',
			 'len' => 40,
			 'label' => 'New password:',
			 'default' => '',
			 'regexp' => '.{8,}',
			 'rule' => 'must be at least 8 chars long',
			 'ldapattr' => 'userpassword',
			 'status' => 'printform', 
			 'priority' => 10, 
		     },
		     'retype' => {
                         'name' => 'retype',
			 'type' => 'password',
			 'len' => 40,
			 'label' => 'Please retype password:',
			 'default' => '',
#			 'regexp' => '.{8,}',
			 'ldapattr' => '',
			 'status' => 'printform',
			 'priority' => 11, 
		     },	   
   );

our $isdebug = 0;
our $istest = 0;

while ( my $cgi = new CGI::Fast ) {

    $count++;
    my $html = &buildResponse ( $cgi, $count );
    print $html;

}

sub buildResponse {
    my ( $q, $hitcount ) = @_;

    if ( $q->param('debug') ) {
	$isdebug=1;
    }

    if ( $conf->{'data'}{'emailconfig'} ) {
	foreach my $mailconf ( split /;/,  $conf->{'data'}{'emailconfig'} ) {
	    my ($key, $value ) = split (/=/, $mailconf, 2);
	    $key =~ s/^\\//;
	    $key =~ s/^#//;
	    $conf->{data}{emailinfo}{$key}=$value;
	    $logger->debug("found mail configuration: $key=$value");
	}
    }
#    else {
#	$response .= $q->("Sorry internal Error cannot perform, please send
#    }

    my $statusmessage = $q->param('status') ? $q->param('status') : 'none';

    $logger->info("pwreset ($VERSION) started, status: $statusmessage");
    $logger->debug("used sysfile $sysconfig");
    $logger->debug("used configfile $conf->{data}{configfile}");
#    $logger->debug("status is: ".$q->param('status')) if $q->param('status') ;

    my %session;
    my $sessionid = &handlesession($q, \%session, $statusmessage );

#    use Data::Dumper;
#    my $dumpstr = Dumper(%session);

    foreach my $key ( sort keys %session ) {
	if ( $key ) {
	    $logger->debug("session $key: ". $session{$key});
	}
    }

    my ($header, $response) = '';
    
    $header = &printheader($q);
    $response .= &debugcgi($q, $hitcount) if $isdebug;
#    $response .= $q->p("Session: $dumpstr") if $isdebug;

    $response .= $q->h1($conf->{'data'}{'title'});

    my $status = $q->param('status');

    if ( ! $status ) {
	$logger->debug("buildResponse(): No status thus stating start_process ...");
	$response .= &startprocess($q);
#	$response .= $q->h3("No Status, this shouldnd't happen here, please report");
    }
    elsif ( $status eq 'testcookie' ) {
	$response = &testcookie($q);
    }
    elsif ( $status eq 'sendlink' ) {
	$response .= &sendlink($q, \%session);
#	my ( $resp, $head ) = &sendlink($q);
#	$response .= $resp;
#	$header = $head if ($head );
    }
    elsif ( $status eq 'printform' ) {
	$response .= &printform($q, \%session);
    }
    elsif ( $status eq 'modify' ) {
	$response .= &modify($q, \%session);
    }
    elsif ( $conf->{data}{enableadminhelp} && $status eq 'adminhelp' ) {
#	my $lang = $q->param('lang') ? $q->param('lang') : 'en';
#	$response .= $q->pre(`./$NAME.pl -h -G $lang`);
	if ( my $feature = $q->param('helpfeature') ) {
	    $response .= $q->pre(`./$NAME.pl -H $feature`);
	}
	else {
	    $response .= $q->pre(`./$NAME.pl -h`);
	}
    }
    else {
	$response .= &printerror('status', $q);
    }


    if ( $conf->{data}{forcehttps} && ! $q->https ) {
	$response = $q->h3("ERROR Programm does not work without HTTPS");
    } 

    $response .= &printtail( $q);

    $logger->info("$progname ended");


    $response = "$header$response";

    return $response;

}

sub handlesession {
    my ( $q, $rh_session, $status ) = @_;

#    my %session;
#    my $exist = '';
    
    my $sid =  $q->cookie ( -name => $NAME );

    if ( $sid ) {

	my $sessionfile = $conf->{'data'}{ 'sessionpath' }."/$sid";
	if ( -e $sessionfile  ) {
#	    $exist = $sid;
	    $logger->debug( "Session-Management: Reading session $sid");
	    tie %{$rh_session}, "Apache::Session::File", $sid, 
	    { Directory => $conf->{'data'}{ 'sessionpath' }, 
	      LockDirectory => $conf->{'data'}{ 'sessionlockpath'} 
	    };
	}
	else {
	    $logger->debug( "Session-Management: creating new session $sid");

	    tie %{$rh_session}, "Apache::Session::File", undef, 
	    { Directory => $conf->{'data'}{ 'sessionpath' }, 
	      LockDirectory => $conf->{'data'}{ 'sessionlockpath'} 
	    };
	    $sid = $rh_session->{_session_id};
	    $rh_session->{'counter'}=0;
	    &setcookie( $q, $sid) ;
	}
    }
    else {
	tie %{$rh_session}, "Apache::Session::File", undef, 
	{ Directory => $conf->{'data'}{ 'sessionpath' }, 
	  LockDirectory => $conf->{'data'}{ 'sessionlockpath'} 
	};
	$sid = $rh_session->{_session_id};
	$rh_session->{'counter'}=0;
	&setcookie( $q, $sid) ;
    }

    if ( $rh_session->{'status'} ) {
	$rh_session->{'laststatus'} = $rh_session->{'status'};
    }
    $rh_session->{'status'} = $status;
    $rh_session->{'counter'} += 1;


    return $sid;
}
 


sub startprocess {
    my ( $q ) = @_;

    my $response = '';
    
    my $status = 'sendlink';

    my %params;
    $params{status}=$status;
    my $url = &createurl($q, \%params);

    $q->param('status', $status);

    $response .= $q->h2("Identify yourself");
    $response .= $q->p("my url: $url") if $isdebug;

    $response .= $q->start_form( -method => 'post',
				 -action => $url,
	);


    $response .= &printfields ($q, 'start', $conf->{'data'}{'oneinputfield'});
    $response .= &printhiddens($q, $status);

    $response .= $q->submit(-name =>'button_name',
			    -value =>'verify me',
	                   );

    $response .= $q->end_form;

    return $response;
}

sub createid {

    my $id = "xaOUd".rand(100000)."ff1A.846";
    

    return $id;
}

sub setcookie {
    my ( $q, $sid ) = @_;
    my $status = $q->param('status') ? $q->param('status'): 'none' ;
    $logger->debug("setcookie(): Status is: $status, SID is: $sid");

    my %params;
    if ( $status ne 'testcookie' && $status ne 'adminhelp' ) {
#    if ( $status ne 'testcookie' ) {
#	my $id = $sid;
	$params{'status'}='testcookie';
	my $nexturl = &createurl($q, \%params);
#       my $server = $q->server_name;
	#   my $id = &createid();
	my $cookietime = ($status eq 'modify') ? 'now' : &getcookietime();

	my $cookie = $q->cookie(-name => $NAME,
				-value   => $sid,
				-expires => $cookietime,
				-path    => $PATH,
#                             -domain  => 'XXX',
#                             -secure  => 1,
	    );
	
	$logger->debug(" setcookie(): sending |$cookie| via redirect to testcookie");
	print $q->redirect ( -url => $nexturl,
			     -cookie => $cookie );
	exit();
    }
}

sub createurl {
    my ( $q, $rh_params ) = @_;
    
    my $server = $q->server_name;
    my $url = $q->url();

    if ( $conf->{data}{rewritepath} ) {
	$url = &rewritepath($url, $conf->{data}{rewritepath});
    }

    my $token = '?';
    if ( $rh_params ) {
	foreach my $key ( keys %{$rh_params} ) {
	    $url .= $token;
	    $url .= $key.'='.$rh_params->{$key};
	    $token = '&';
	}
    }
    $url .= "${token}debug=".$q->param('debug') if $q->param('debug') ;

#    $status ? 
#	"$PROT://$server$PATH/$NAME.pl?status=$status" :
#	"$PROT://$server$PATH/$NAME.pl";
    
    $logger->debug("createurl(): created url $url");
    return $url;
}

sub testcookie {
    my ( $q ) = @_;
    my $response='';

    my $cookie = $q->cookie ( -name => $NAME );
    my $nexturl = &createurl( $q, undef);
    $logger->debug("testcookie(): Cookie is: $cookie");

    if ( defined $cookie ) {
#	$response .= $q->h2("TESTCOOKIE: SUCCESS");
	$logger->debug("testcookie(): Found cookie, will proceed");
	$q->param('status', 'sendlink');
	$response .= $q->h1($conf->{'data'}{'title'});
	$response .= &startprocess($q);
    }
    else {
	$logger->warn("testcookie(): Cookies seem to be disabled");

	$response .= $q->h2( "Cookies Disabled!" );
	$response .= $q->h3( "Your browser is not accepting cookies");
	$response  .= $q->p( "Please enable cookies in your browser preferences and ".
		   $q->a( { -href => $nexturl }, "return to start").
		   "."
	    );
    }
   
    return $response;
}

sub sendlink {
    my ( $q, $rh_session ) = @_;
    my ($header, $response) = '';

    $response .= $q->h2("Verification and sending email");

    if ( my $paramerrors = &getparamerrors ( $q ) ) {
#    if ( $paramerrors ) {
	$response .= $paramerrors;
	$response .= $q->p("Please try again");
	$response .=  &startprocess($q);
	return $response;
    }

    my $searchquery = &getsearchquery($q, 'start');
    $logger->debug("sendlinkl(): searchquery is $searchquery");

    my $filter = &createfilter( $q, $searchquery, 'start');
    my ( $searchresponse, $mail, $entry ) =  &searchentry($q, $rh_session, $filter);
    $response .= $q->b($searchresponse) if $searchresponse  ;
    my $user = " (".$q->param($searchquery).")";

    if ( ! $mail ) {					     
	$response .= &startprocess($q);
    }
    else {
	$response .=$q->p("Your TextGrid account $user exists.");
	$response .= $q->p("\nsmtpserver: ".$conf->{data}{emailinfo}{smtprelay}) if $isdebug;
	$response .= &sendlinkmail($q, $mail, $rh_session);
    }

    return $response;
}

sub getsearchquery {
    my ( $q, $status ) = @_;
    my $searchquery = '';

    if ( $q->param('search') ) {
	$searchquery = 'search';
    }
    else {
	foreach my $f ( sort prioritysort keys %inputfields ) {
	    if ( grep ( /$status/, split(/;/, $inputfields{$f}{status}) ) ) { 
		if ( $q->param($inputfields{$f}{name}) ) {
		    $searchquery = $inputfields{$f}{name};
		    last;
		}
	    }
	}
    }
    
    return $searchquery;
}


sub sendlinkmail {
    my ( $q, $mail, $rh_session ) = @_;
    my $response = '';

    my $status = 'printform';
    my $smtp = new DAASIlib::SMTP;

    
    $smtp->set_server($conf->{data}{emailinfo}{smtprelay});
    my $subject = $conf->{data}{emailinfo}{subjectpart};
    $subject .= " How to reset your TextGrid password"; 
    my $body = $conf->{data}{linkmail};
    my ($cc, $bcc, $pw) = '';
    $body =~ s/\$/\n/g;

    my $secret = &createid();

#    my $url = $q->url();
#    $url .= "?status=printform&sessionid=$secret";
#    $url .= "&debug=".$q->param('debug') if $q->param('debug') ;

    my %params;
    $params{status}=$status;
    $params{sessionid}=$secret;
    my $url = &createurl($q, \%params);

    $rh_session->{'secret'} = $secret;

    $body =~ s/%URL%/$url/;

    my $result = $smtp->send_mail($conf->{data}{emailinfo}{from},
		    $subject,$body,$mail,$cc,$bcc,
	            $conf->{data}{emailinfo}{hello},
	            $conf->{data}{emailinfo}{smtpuser},
	) if ! $istest;

    $response .= $q->p("An email has been sent to $mail.");

    

    return $response;

}


sub createfilter {
    my ( $q, $searchquery, $status ) = @_;

    my ( $searchattr, $filter ) = '';

    if ( $searchquery eq 'search' ) {
	$filter = '( | ';
	foreach my $f ( sort prioritysort keys %inputfields ) {
	    if ( grep ( /$status/, split(/;/, $inputfields{$f}{status}) ) ) { 
		$searchattr = $inputfields{$f}->{'ldapattr'};
		if ( $searchattr ) {
		    $filter .= "($searchattr=".$q->param($searchquery).")";
		}
	    }
	}
	$filter .= ')';
    }
    else {
	$searchattr = $inputfields{$searchquery}{'ldapattr'};
	$filter = "($searchattr=".$q->param($searchquery).")";
    }

    $logger->debug("createfilter(): created filter $filter");
    return $filter;
}

sub searchentry {
    my ( $q, $rh_session, $filter, $ldap ) = @_;
    my $response = '';

    my $searchattr;
#    my $filter;
    my $mail;
    my $entry;
    my $shouldclose = 0;

    my @attrs = &getattrs('start');


    $response .= $q->p("searching with Filter $filter") if $isdebug; 
    

    my $libldap = new DAASIlib::LDAP;
    my $rh_ldap = $libldap->defineServerFromURI($conf->{data}{ldapuri}, 
						'ldapuri', 0);
    $rh_ldap->{is_tls} = 1 if $conf->{data}{forcetls};

#    $rh_ldap->{scope}  = 'sub'; 

    $rh_ldap->{attribs}  = \@attrs; 
    $rh_ldap->{filter}  = $filter; 

    if ( ! $ldap ) {
#    my $ldap = $libldap->connectServer($rh_ldap, 1);
	$ldap = $libldap->connectServer($rh_ldap);
	if ( ! $ldap ) {
	    $response .= $q->p("could not connect to LDAP-Server");
	    return $response;
	}
	$shouldclose = 1;
    }

    my $mesg = $libldap->doSearch($ldap, $rh_ldap);
    if ($mesg->count()) {
	if ( $mesg->count() > 1 ) {
	    $response .= $q->p("Error: found more than one entry!!!");
	    $logger->error("Error: found more than one entry!!!");
	}

	$entry = $mesg->pop_entry();
	my $entrydn= $entry->dn();
	$logger->debug("found entry $entrydn");	
        $mail = $entry->get_value('mail');
	foreach my $attr ( @attrs ) {
	    $rh_session->{$attr} = $entry->get_value($attr);
	}
	if ( ! $mail ) {
	    $response .= $q->p("Error: could not find an email to send message to");
	    $logger->error("Error: could not find an email to send message to");
	}
    }
    else {
	$logger->debug("found no entry with filter $filter");	
	$response .= $q->p("Error: there is no user with $filter");
	$logger->error("Error: there is no user with $filter");
    }

    if ( $shouldclose ) {
	$ldap->unbind;
    }

    return $response, $mail, $entry;
}

sub printform {
    my ( $q, $rh_session ) = @_;

    $logger->debug("printform(): started");
    my $status = '';
    my $response = '';
    $response .= $q->h2("Input of new password");
    my $cookie = $q->cookie(-name => 'pwReset');

#   use Data::Dumper;
#    $response .= $q->p(Dumper($cookie));
    my $secret = $rh_session->{'secret'};

    if ( $q->param('sessionid') eq $secret ) {

	$response .= $q->h3("Everything went fine, you can now reset your Password");
	$status = 'modify';
	my %params;
	$params{'status'}=$status;
	my $url = &createurl($q, \%params);

	$response .= $q->start_form( -method => 'post',
				 -action => $url,
	    );
	$response .= &printfields ($q, 'printform');
	$logger->debug("printform(): status: $status");
	$response .= &printhiddens($q, $status);

	$logger->debug("printform(): fine so I set status to $status");
	$response .= $q->submit(-name =>'button_name',
				-value =>'reset password',
	    );
	$response .= $q->end_form;

    }
    else {
	my $cookietimestring  = &getcookietimestring();
	$response .= $q->h3("There has been an Error").
	    $q->p("This can be out of one of the following reasons:"). 
	    $q->ul( $q->li({-type=>'disc'},
			   [
			    "Your browser didn't allow to set a cookie",
			    "You used a different browser, when requesting the reset",
			    "The request is too old (older than $cookietimestring)",
			    "Any other unforseen error",
			   ]));
    }

    return $response;
}


sub modify {
    my ( $q, $rh_session ) = @_;
    
    my $response = '';
    my $cookie = $q->cookie(-name => 'pwReset');
    my $secret = $rh_session->{'secret'};
    my $user = $rh_session->{'edupersonprincipalname'};
    my $maxcount = 40;

    my $libldap = new DAASIlib::LDAP;
    my $rh_ldap = $libldap->defineServerFromURI($conf->{data}{ldapuri}, 
						'ldapuri', 0);
    $rh_ldap->{is_tls} = 1 if $conf->{data}{forcetls};
    my $ldap = $libldap->connectServer($rh_ldap);
#    my $maxcount = 4;

    $response .= $q->h2("Modifying Password");
    
    if ( $q->param('sessionid') eq $secret ) {
	if ( $q->param('password') eq $q->param('retype') ) {
	    if ( $rh_session->{'counter'} > $maxcount ) {
		$response .= $q->p("ERROR: Session is not valid any more, because it was called too often. You have to begin the process again");
		tied(%{$rh_session})->delete;
		my $url = &createurl($q, undef);
		$response .= $q->a( { -href => $url }, "return to start");
#		$response .= &startprocess($q);		
	    }
	    elsif (  my $paramerrors = &getparamerrors ( $q ) ) {
		$response .= $paramerrors;
		$response .= $q->p("Please try again");
		$response .= &printform($q, $rh_session);
	    }
	    else {
		my $idattr = $inputfields{&getidfield()}{'ldapattr'};
		my $user = $rh_session->{$idattr};
		my $filter = "($idattr=$user)";
		my ( $searchresponse, $mail, $entry ) =  &searchentry($q, $rh_session, $filter);
		my $entrydn = $entry->dn();
		$response .= $q->p("Password for user $user will be reset");
#		$response .= "entry: $entrydn";
		my $plain = $q->param('password');
		my $digest =  $conf->{'data'}{'pwhash'};
		my $encodedPassword = `/usr/sbin/slappasswd -h {$digest} -s \'$plain\'`;

		$logger->debug("changing Password of entry $entrydn at ". 
			       $conf->{data}{ldapuri});
		$entry->replace ( 
		    $inputfields{'password'}{'ldapattr'} => $encodedPassword
		    );
		my $res = $entry->update($ldap); 

## The following produces crypt passwords, that didn't work
#		use Net::LDAP::Extension::SetPassword;
#		my $res = $ldap->set_password(
#		    newpasswd => $q->param('password'),
#		    user      => $entrydn
#		    );
		$logger->debug("Result of password change: ".$res->error());
		$response .= $q->p("result of update: ".$res->error());
		$ldap->unbind;		
	    }
	}
	else {
	    $response .= $q->p("ERROR: the password and the retyped password did not match, please try again");
	    $response .= &printform($q, $rh_session);
	}
    }
    else {
	$response .= $q->p("ERROR: sessionid: $secret not returned in query: ".$q->param('sessionid') );
    }
    
    return $response;
}

sub getidfield {
    my $idfield='';
    foreach my $key ( keys %inputfields ) {
	if ( $inputfields{$key}{ 'usage'} eq 'ID') {
	    $idfield = $key;
	}
    }
    $idfield = $idfield ? $idfield : 'edupersonprincipalname';
    return $idfield;
}

sub getcookietime {
    my $string='';

    if ( $conf->{'data'}{'sessiontime'} =~/(\d+)([smhdMy])/ ) {
	$string = '+'.$conf->{'data'}{'sessiontime'};
    }
    else {
	$logger->error("ERROR: wrong Format in configuration of sessiontime");
    }

    return $string;
}	     


sub getcookietimestring {
    my $string='';

    my %units = (
	's' => 'seconds',
	'm' => 'minutes',
	'h' => 'hours',
	'd' => 'days',
	'M' => 'months',
	'y' => 'years',
	);

    my $num;
    my $unitstring;

    if ( $conf->{'data'}{'sessiontime'} =~/(\d+)([smhdMy])/ ) {
	$num = $1;
        $unitstring = $units{$2};
	$string = "$num $unitstring";
    }

    return $string;
}	     


sub rewritepath { 
    my ($path,$rules) = @_;
    
    foreach my $rule ( split / ?; ?/, $rules ) {
	my ($from, $to ) = split / ?=> ?/, $rule;
	$path =~s/$from/$to/;
    }
    return $path;
}

sub printhiddens {
    my ( $q, $state ) = @_;
    my $fields='';


    $logger->debug("printhiddens(): got status:        $state");
    $state = $state ? $state : $q->param('status');
    $logger->debug("printhiddens(): changed status to: $state");
###    $fields .= $q->hidden( 'status', "$state");
#### There seems to be a bug in the CGI-library, since the above didn't work ( 
    $fields .= "<input type=\"hidden\" name=\"status\" value=\"$state\"/>";

    $fields .= $q->hidden( 'sessionid', $q->param('sessionid') ) 
	if $q->param('sessionid') ;

#    $fields .= $q->hidden( -name => 'status', -default => "XXX${status}YYY");
#    $fields .= $q->hidden( -name => 'sxxxx', -default => "XXX${status}YYY");
#    $fields .= $q->hidden( -name => 'sessionid', -default => $q->param('sessionid') ) 
#	if $q->param('sessionid') ;

    if ( $q->param('debug') ) {
	$fields .= $q->hidden( -name => 'debug', -default => $q->param('debug'));
	$fields .= $q->p("debug is on");
    }
    $logger->debug("printhiddens(): returning fields: $fields");
    return $fields;
    
}

sub prioritysort {
    $inputfields{$a}->{'priority'}<=>$inputfields{$b}->{'priority'}
}

sub getattrs {
    my ($status) = @_;
    my @attrs=();
    $logger->debug("getattrs(): got status $status");

    foreach my $f ( sort prioritysort keys %inputfields ) {
	if ( grep ( /$status/, split(/;/, $inputfields{$f}{status}) ) ) { 
	    push @attrs, $inputfields{$f}{ldapattr};
	    $logger->debug("getattrs(): found attribute: ".$inputfields{$f}{ldapattr});
	}
    }
    return @attrs;
}

sub printfields {
    my ($q, $status, $isonefield) = @_;
    my $fields='';
    my $labels = 'Please input either ';
#Please input either your Login Name, your E-Mail address or your TextGrid ID. Your password can only be changed for @textgrid.de TextGrid IDs
    foreach my $f ( 
	sort { $inputfields{$a}->{'priority'}<=>$inputfields{$b}->{'priority'} }
	keys %inputfields ) {

	if ( grep ( /$status/, split(/;/, $inputfields{$f}{status}) ) ) { 
	    if  ( $inputfields{$f}{type} eq 'text' ) {
		my $label =  $inputfields{$f}->{'label'};
		$label =~ s/(.*):\s?/$1/;
		$labels .= "your $label or ";
		$fields .= &printtextfield($q,  $inputfields{$f});
	    }
	    elsif (  $inputfields{$f}->{type} eq 'password' ) {
		$fields .= &printpasswordfield($q, $inputfields{$f});
	    }
	    else {
		$fields .= &printerror('input type', $q); 
	    }
	    $fields .= $q->br;
	}
    }

    if ( $isonefield ) {
	$labels =~ s/ or $//;
        $labels .= "<br>\n (Your password can only be changed for \@textgrid.de TextGrid IDs)";
	$fields = $labels.": ".$q->br.$q->br;
	$fields .= $q->textfield(
	    -name => 'search',
	    -value => '',
	    -size =>40,
	    -maxlen => 40,
	);
    }
   
    return $fields;
}

sub printtextfield {
    my ($q,$f, $label) = @_;
    
    my $field = '';
    $label = $label ? $label : $f->{'label'};
    $field .= $label.$q->br;
    $field .= $q->textfield(
	-name => $f->{'name'},
	-value => $f->{'default'},
	-size => $f->{'len'},
	-maxlen => $f->{'len'},
	);

    return $field;
}

sub printpasswordfield {
    my ($q,$f) = @_;
    
    my $field = '';
    $field .= $f->{'label'}.$q->br;
    $field .= $q->password_field(
	-name => $f->{'name'},
	-value => $f->{'default'},
	-size => $f->{'len'},
	-maxlen => $f->{'len'},
	);

    return $field;
}



sub printtail {
    my ( $q) = @_;

    my $tail = $q->end_html();

    return $tail;
}


sub printheader {
    my ( $q ) = @_;

    my $header = $q->header(
	                -type   => 'text/html',
                        -charset=> $conf->{'data'}{'charset'},
        #               -pragma => 'no-cache', # for debugging only
        #               -expires=> 'now', # for debugging only
                        -expires=> '+3m', # should be used in production
#	                -cookie => $cookie,
                        );

#    if ( $cookie ) {
#	$logger->debug("Setting Cookie");
#    }

    my $color = $conf->{'data'}{'bgcolor'};

#    if ( $color !~ /#/ ) {
#	$color = '#'.$color;
#    }

    my %metas;

    foreach my $metadef ( split /\|/, $conf->{'data'}{'meta'} ) {
	my ($name,$content) = split /:/, $metadef, 2;
	$metas{$name}=$content;
    }
    $header .= $q->start_html(
	                -title => $conf->{'data'}{'title'},
	                -bgcolor => $color,
	                -meta => \%metas,
#	                -style => { -src => [ $conf->{'data'}{'cssfile'} ], 
#                                    -media => 'all'},
	);

 
    return $header;
}


sub printerror {
    my ( $err, $q) = @_;

    my $error = $q->p("Error: wrong $err");

    return $error;
}

sub getparamerrors {
    my ( $q ) = @_;

    my $errors = '';
    my @names = $q->param();

    foreach (@names) {
	my $value = $q->param($_);
	my $regexp = $inputfields{$_}{'regexp'};
	my $rule = $inputfields{$_}{'rule'};
	$rule = $rule ? 
	    "rule \"$rule\"" :
	    "Regular Expression /$regexp/";
	my $display;

	if (  $inputfields{$_}{'type'} eq 'password' ) {
	    $display = "[not displayed]";
	}
	else {
	    $display = $value;
	}
	if ( $value && $regexp && $value !~ /$regexp/ ) {
	    $errors .= $q->p("Error in inputfield $_: value \"$display\" does not conform to $rule");
	}
    }    

    return $errors;
}

sub debugcgi {
    my ( $q, $hitcount ) = @_;

    my $response = '';
    $response .= $q->h3("Debuginfo");
    $response .= $q->p("This is hit number <b>$hitcount</b>");

    my @keywords = $q->keywords();
    my @names = $q->param();

    my @values;

    foreach (@names) {
	push @values,"$_: ".$q->param($_);
    }    
    $response .= $q->ul( $q->li({-type=>'disc'},["Q keywords: @keywords", "Q param: @names"])); 
    if ( @values ) {
        $response .= $q->p("Query has following param / value pairs:"); 
	$response .= $q->ul( $q->li({-type=>'disc'}, @values)); 
    }

    $response .= $q->p("remote host: ".$q->remote_host());
    $response .= $q->p("remote addr: ".$q->remote_addr());
    $response .= $q->p("user agent: ".$q->user_agent());

    $response .= $conf->getConf("current configuration:");


    return $response;

}