#!/usr/bin/perl 

#***************************************************************************
#                          Virtfs  -  A Perl/Dialog Script
#                             -------------------
#    Started              : December 09 1999
#    This Version         : v. 0.70.1 -  June 4 2001
#    copyright            : (C) 1999-2001 by Afra Ahmad
#    email                : afra@prongs.org
#    homepage             : http://www.prongs.org/virtfs
#    documentation        : http://www.prongs.org/virtfs/docs
# ***************************************************************************

#***************************************************************************
#                                                                          
#    This program is free software; you can redistribute it and/or modify  
#    it under the terms of the GNU General Public License as published by  
#    the Free Software Foundation; either version 2 of the License, or     
#    (at your option) any later version.                                   
#                                                                          
# ***************************************************************************


use strict;


&root_check(); # check that only root is running this.
my $config_file="/etc/virt.conf"; # The configuration file - this is the important one!
my $template_conf="/etc/virt_template.conf"; # The configuration file for the template directory
my $version="0.70.1"; # The current version number

die "$template_conf not found!" if (!-e $template_conf);
die "$config_file not found!" if (!-e $config_file);

# chroot must be in the following directories.
$ENV{'PATH'} = "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/lib";

my $chroot="chroot"; # the chroot command should be in one of the directories above in $ENV{'PATH'}

# collect values of the variables needed by Virtfs, from the config. file
my $config_variables=&read_config($config_file);
my $dialog = $config_variables->{"Dialog"}; # The external program to use for the UI.
my $tmp=$config_variables->{"Temp Directory"}; # directory to keep logs and temporary files in.
my $default_shell=$config_variables->{"Default Shell"}; #default shells to assign users.
my $default_home=$config_variables->{"Default Home Directory"}; #default home dir. location, ie. /home
my $apache_conf=$config_variables->{"Apache Conf"}; # where Apache's httpd.conf resides.
my $logoption=$config_variables->{"Log"}; # If we should log actions into the logfile.
my $virtual_dir=$config_variables->{"Leading Virtual Directory"}; # where the virtual servers reside.
my $server_file=$config_variables->{"Servers File"}; # the file with info. on all virtual servers.
my $smail =$config_variables->{"Use Sendmail"}; # Does our system use Sendmail?
my $copy_file=$config_variables->{"Copy Script"}; # the copy system commands.
my $delete_root=$config_variables->{"Delete Root"}; # The root account 
my $ldconfig_libs=$config_variables->{"ldconfig libs"}; # the /etc/ld.so.conf contents
my $sendmail_cf=$config_variables->{"Sendmail CF"}; # the main servers sendmail.cf
my $sendmail_alias=$config_variables->{"Sendmail Aliases"}; # The aliases file
my $ftp_config_files=$config_variables->{"ftp files"}; # The ftp config files
my $ns1 = $config_variables->{"Primary DNS"};
my $ns2 = $config_variables->{"Secondary DNS"};


# Can the root account be removed or altered?
$delete_root =~ tr/A-Z/a-z/;
if ($delete_root =~ /yes/){ $delete_root = 0;}
else { $delete_root = 1;}

&check_required($config_variables); # check if all important variables are defined

# Are we using Sendmail as a MTA?
$smail =~ tr/A-Z/a-z/; $smail = 1 if ($smail eq "yes");
my $sendmail = &find_path("sendmail");
$smail = 0 if (!$sendmail);
my $mail_folder=$config_variables->{"Default Mail Folder"} if ($smail);
my $mail_grp=$config_variables->{"Default Mail Group"} if ($smail);
&check_sendmail_config($sendmail_cf) if ($smail); # check to see if sendmail.cf is there.

$server_file="$virtual_dir/$server_file"; # where the info. for virtual servers reside.

# remove trailing / from directory names that were retrieved from the conf file.
$tmp =~ s:/$::; $default_home =~ s:/$::; $mail_folder =~ s:/$:: if ($smail);

# Log file stuff
my ($log,$logfile);

$logoption =~ tr/A-Z/a-z/;
if ($logoption eq "on"){
   $log=1;
   $logfile=$config_variables->{"Log File"};
   $logfile =~ s:/$::; # in case..
}


my $backtitle="--backtitle \"Virtual Services - virtfs v.$version \""; # dialog stuff.
my $ansfile="$tmp/virtfs.tmp"; # the temporary file used to retrieve user's input
my $menu_title="\"Virtfs $version\""; # the title for the menus

# locate the names of the FTP and Mail service files, ie. conf.pop and conf.ftp .
my $ftp_file=$config_variables->{"FTP file"};
$ftp_file="$virtual_dir/$ftp_file";
my $pop_file=$config_variables->{"MAIL file"};
$pop_file="$virtual_dir/$pop_file";

if (!-d $virtual_dir){
# the virtual directory does not exist.
# The user will have to create a template directory first and if needed, re-configure

    my $text = "The directory for your virtual servers, $virtual_dir does not exist!\n";
    $text .= "This must mean you have not set up a template directory or your configuration is wrong.\n";
    $text .= "Please make sure you run virt_template.pl before proceeding with virtfs.\n";
    $text .= "Please check $config_file, $template_conf and try again...\n\n";
    print $text;
    exit(0);

}

# Find out where the template directory is

my $template_dir=&read_template_config($template_conf);
$template_dir = $virtual_dir ."/".$template_dir;

&parse_args(@ARGV); # parse arguments - needs to be done after the configuration file is read in.
&clean_up_lists();
# Phew, all variables collected - Virtfs now begins ....

# list the domains and get the user's response.
my ($domainname,$ip)=&list_domains($virtual_dir); 

if ($domainname eq "Create"){ # user chose to create a server. Do so and then exit.
    &create_virt_domain;
    exit(0);
} 

my $domain="$virtual_dir/$domainname";
my $passwd_file="$virtual_dir/$domainname/etc/passwd";
my $shell_file=$domain."/etc/shells";


&menu_list; # start the main menu for the specified domain.


sub menu_list{
#show the menu, get the choice, and do what the user asks for.
# cancel exits, btw...

    my ($choice,$ret_result);
    
    while (1){

    &list_menu($domain); # display the menu
    $choice=&get_answer($ansfile,1);  # get user's request
    last if (!$choice);
    $ret_result= &look_choice($choice);
    last if (!$ret_result);

}
    &logit("clear") if ($log); # add a linebreak to the log for easier parsing.
    exit(0); # end program - user chose to quit.
}



sub detect_new{
    # detect new servers.
    my @dirs=<$virtual_dir/*>;
   
    my ($found,$i,$server_dir,$dir);
   
    for ($i=0;$i<@dirs;$i++){
	if ($dirs[$i] eq $template_dir){
	    print "$template_dir ignored [ template directory ]...\n";
	    next;
	}
	if (-d "$dirs[$i]/etc"){
	    $found=1;
	  # print $dirs[$i]."\n";
	    
	    # split the server name from the full path...
	    $server_dir = $dirs[$i];
	    $server_dir =~ s/$virtual_dir//g,$server_dir; # remove the leading path name
	    $server_dir =~ s/^\///; # remove the first / in the full path
	#    print "Server_dir: ".$server_dir." and $dirs[$i]\n";
            print $server_dir;
	    &compare_server($server_dir,$dirs[$i]);
	    
	}
    }
    &clean_up_lists; # clean up stale entries

    if ($found != 1) { 
	print "No virtual servers found in $virtual_dir!\nPlease make sure configuration is set.\n";
	exit(0);
    }
}

sub compare_server{
    my ($server,$dir)=@_;
    my ($d,$ip_server,$found_entry);
    
    open (SD, $server_file);
  
    while (<SD>){
	chomp;
	s/#.*//;
	s/^\s+//;
	s/\s+$//;
	($d,$ip_server)=split(/\:/,$_);
	
	if ($server eq $d){
	    print " - detected and exists in configuration. IP: $ip_server";
	    $found_entry = 1;
	}
	
    }
    close (SD);
    
    if ($found_entry != 1){
	
	$domain =~ s#.*/##;
	print " ..  detected and does not exist in the configuration.\n";
	print "Please enter the IP number for $server or press Enter to skip: ";
	my $ip=<STDIN>;chomp $ip;
	if ($ip){
	open (FH,"+>>$server_file");
	print FH "$server:$ip\n";
	close (FH);
	print "Added.\n";
      }
	else{print "Skipped.\n";}
    }

    print "\n";
  
}  

sub clean_up_lists{
    # Clean up the $server_file (server.lists) of old entries
    #print "\nNow trying to clean up stale entries in $server_file\n";
    my ($d,$ip_server,$delete);
    open (SD, $server_file);
   

    while (<SD>){
	chomp;
	s/#.*//;
	s/^\s+//;
	s/\s+$//;
	($d,$ip_server)=split(/\:/,$_);
	if (!-d "$virtual_dir/$d/etc" && $d){
	    print "$d ( $virtual_dir/$d ) does not have a proper filesystem. Delete entry [y/n]: ";
	    $delete=<STDIN>;chomp($delete);
	    if ($delete =~ /[Yy]/){
		&remove_server_entry($d);
	    }
	
	}
    }
    close (SD);
    close (TMP);
}
    
sub conf_files{
# Find out if a domain is configured for a certain service
# and display the results to the user.
    
    my ($conf)=@_;
    open (FD, "+< $conf");
    my ($i,$s,$found);
    while (<FD>){
	
	if ($_ =~ /$domain/){
	    $found=1;
	    last;
	}
    }
    close (FD);
    return "Yes" if ($found);
    return "No";
}


sub look_choice{
# We take the user input from the menu and execute accordingly...
    
# To implement your own functions here (after adding an entry to the menu), make sure
# that the return code from the function which is called, is caught in the variable $ans .

# (if a 0 is returned to the &menu_list, the program will exit.)

    my ($choice)=@_; # get the result from what the user chose.
    my ($text,$ans);

# Call the functions needed according to what the user requested.

    if ($choice eq "I"){
	
# simply display the server information...

	$text .= "Server Name: $domainname\n";
	$text .= "Server IP: $ip\n";
	$text .= "Server Directory: $domain\n";
	my ($ftp)=&conf_files($ftp_file); 
	$text .= "FTP Active: $ftp\n";
	my ($mail)=&conf_files($pop_file);
	$text .= "Mail Active: $mail\n";
	$ans=&info ($backtitle,$text,"15 65");
    }
    
    elsif ($choice eq "G"){
	#finger a list of accounts.
	my $finger=&find_path("finger"); # does finger exist?
	($text)=&list_accounts($domain) if ($finger);
	$ans=&finger_account($text,$finger) if ($finger);
    }
    
    elsif ($choice eq "T"){
	# Create a mail folder for a specified user.

	# If user has configured virt.conf to use with Sendmail,
	# we need to list all the users for the user to choose.

	if ($smail){
	($text)=&list_accounts($domain);
	$ans=&mail_folder($text);
    }
	else{
	    $ans=&text_box("Sendmail","Sendmail value in virt.conf is no.\nYou can't edit further!","21 62");
	}
    }


    elsif ($choice eq "R"){
# delete the server. Ask the user twice for confirmation, 
# and if (s)he agrees, remove the entry and delete the
# domain's directory. Use with caution!!
	
	$text .= "Remove Server?";
	my $ynstatus=&yes_no ($backtitle,$text);
	if ($ynstatus == 0){ 
	    $text = "Are you sure?\n(last chance)";
	    $ynstatus=&yes_no($backtitle,$text);
	    if ($ynstatus==0){
		&remove_server_entry($domainname); 
		system("rm -rf $domain");
		&delete_conf_entry($ftp_file);
		&delete_conf_entry($pop_file);
		print "Server Deleted.....\n";
		&logit("Server Deleted!") if ($log);
		return 0; #exit
	    } 

	}

    }
    
    elsif ($choice eq "C"){
	
# alter all configuration files to reflect the new domain name and IP address...
	my $text = "Please enter the domain name (server.com):\n";
	my $newdomain=&input($backtitle,$text,"$domainname","12 55");
	
	if (!$newdomain){ return 1; }
	$text = "Current IP: $ip\n"; 
	my ($stat,$newip);
	do{
	    $newip=&input($backtitle,$text,"","12 55");
	    $stat=&check_ip_format($newip);
	    return 1 if ($stat == 255);
	}until ($stat == 1);
	
	
	&remove_server_entry(); # remove old server entry from the servers.list file. 
	&delete_conf_entry($ftp_file);
	&delete_conf_entry($pop_file);
	open (FH, "+>> $server_file");
	print FH "$newdomain:$newip\n";
	close (FH);
	system ("echo $newdomain > $domain/etc/HOSTNAME") if ($newdomain ne $domainname);
	system ("mv $domain $virtual_dir/$newdomain") if ($newdomain ne $domainname);
	print "\nServer name changed $newdomain.\nIP changed to $newip.";
	print "\nDirectory has been changed to reflect this.\n";
	print "Re-configure IP aliasing and then reset FTP/Mail services through virtfs\n";
	return 0;
    }

    elsif ($choice eq "U"){
	# simply list all users.
	($text)=&list_users($domain);
	$ans=&text_box("Users",$text,"20 60");
    }
    elsif ($choice eq "S"){
	# su over to the domain as a specific user (you will be asked).
	my $su = &find_path("su");
	$ans = &su_exec($domain,$su) if ($su);
	
    }

    elsif ($choice eq "W"){
	# about screen - just list general info about virtfs.
	$text="Virtfs $version\n";
	$text .= "\nWritten by Afra Ahmad\n See http://www.prongs.org/virtfs for latest updates\n";
	$ans=&text_box("About",$text,"10 62");
    }

    elsif ($choice eq "A"){
	# Add a user!
	my $adduser=&find_path("adduser");
	$ans=&add_user($domain,$adduser) if $adduser;
    }
    elsif ($choice eq "P"){
	# change the password for a specified user
	
	my $passwd = &find_path("passwd");
	($text)=&list_accounts($domain) if $passwd;
	$ans=&change_passwd($text,$domain,$passwd) if $passwd;
	
    }
    elsif ($choice eq "D"){
	# Delete a user from a given list.
	my $userdel = &find_path("userdel");
	($text)=&list_accounts($domain) if $userdel;
	$ans = &delete_account($text,$domain,$userdel) if $userdel;

    }
    elsif($choice eq "F"){
	# add/remove FTP service...
	$ans = &services($ftp_file,"FTP");
	
    }
    elsif($choice eq "M"){
	# add/remove MAIL service.
	$ans = &services("$pop_file","Mail");
	
    }
    
    elsif ($choice eq "L"){
	$ans = &list_all();
    }

    elsif($choice eq "H"){
	# Help. I do plan on adding this soon..though, online help is available.
	
	my $text="Please read the online documentation\n at http://www.prongs.org/virtfs/docs/\n";
	$ans=&text_box("HELP",$text,"12 60");
    
    }
    elsif($choice eq "Q"){
	# view Sendmail queue.
	my $exec=`$chroot $domain $sendmail -bp </dev/null 2>&1`;
       if ($?) { &error("Error!:\n$exec");return;}
	$ans=&text_box("Mail Queue",$exec,"20 60");
	
    }
    elsif ($choice eq "E"){
	# list groups in the domain.
	$ans=&list_groups("list only");
    }

    elsif ($choice eq "B"){
	# restart sendmail.
	my $exec=`$chroot $domain $sendmail -bi </dev/null 2>&1`;
	if ($?) { 
	    &error("Error!:\n$exec");
	    &logit("Error with Sendmail: $?") if ($log);
		  return 1;
	}
	&logit("Sendmail alias and restart: $exec") if ($log);
	$ans=&text_box("Alias Database",$exec,"20 68");
    }

    elsif ($choice eq "O"){
	# add a group.
	my $addgroup=&findpath("addgroup");
	$ans=&add_group($addgroup) if ($addgroup);
	
    }

    elsif ($choice eq "V"){
	# Apache Configuration
	$ans=&apache_config();
    }
    
   
   
   	# return to listing the menu as we got a good response from the above functions.
    return 1;
 
}

sub change_passwd{
    my ($text,$server,$passwd)=@_;
    my ($acts,$i,$j);
    my (@accounts)=split(/\n/,$text);
    
    for ($i=0;$i<@accounts;$i++){
	$j=$i+1;
	$acts .= " $j \"$accounts[$i]\"";
    }
    &menu($menu_title,"\"Delete Accounts\"","20  70 11","$ansfile",$acts);
    my $choice=&get_answer("$ansfile");
    return 1 if (!$choice);
    $choice--;
    my $exec = "$chroot $server $passwd $accounts[$choice]";
    my $status=system("$exec");
    print "\nPress the enter key to re-enter virtfs\n";
    my $reentry=<STDIN>;
    &logit("Password for $accounts[$choice] reset") if ($log);
    $text = "Password for $accounts[$choice] changed.\n";
    &info ($backtitle,$text,"10 50");
    return 0;
}

sub add_group{
    my ($addgroup)=@_;
    if (!$addgroup){&error("addgroup not found!\nYou may need to edit this script!");return;}
    my $text="Name of Group to add:";
    my ($group)=&input($backtitle,$text,"","12 55");
    return 1 if (!$group);
    my $exec=`$chroot $domain $addgroup $group </dev/null 2>&1`;
    my $log_text="Added group: $group";
    $log_text .= ". Error: $?" if ($?);
    if ($?) { &error("Couldn't create group:\n$exec\n$!");return;}
    else {
	$text = "Group was added.\n";
	&info($backtitle,$text,"12 50");
    }
    &logit($log_text) if ($log);
    return 1;
    
}



sub mail_folder{
    my ($text)=@_;
    my ($chown)=&find_path("chown");
    if (!$chown){&error("chown not found!\nYou may need to edit this script!");return;}
   
    my (@accounts)=split(/\n/,$text);
    my ($i,$acts,$j);
    for ($i=0;$i<@accounts;$i++){
	$j=$i+1;
	$acts .= " $j \"$accounts[$i]\"";
    }
    &menu($menu_title,"\"Mail Folders\"","20  70 11","$ansfile",$acts);
    my $choice=&get_answer("$ansfile",3);
    return 1 if (!$choice);
    $choice--;
    chomp ($accounts[$choice]);
    my $ex;
    if (! -d $domain.$mail_folder){
	$ex = "$mail_folder was not found within domain.\n";
	$ex .= "Edit $config_file. Are you running Sendmail?\n";
	&text_box("Mail Folder","$ex","15 62");
	return 1;
    }
    my $mailfile=$domain.$mail_folder."/".$accounts[$choice];
    
    if (-e $mailfile){
	$ex="Mail folder found:\n$mailfile .";
	$ex .= "\nDelete and re-create the mail folder?";
	
	my $del_folder=&yes_no("Mail Folder",$ex,"14 62");
	if ($del_folder == 0){ 
	    unlink $mailfile;
	}
	else {return 1;}
    }
   
    open (FH, "+> $mailfile");
    close (FH);
    $ex="Mail file $mailfile\ncreated.";
    $ex .= "\nPermissions were also passed..";
    my $mailfolder=$mail_folder."/".$accounts[$choice];
    chmod 0600, $mailfile || die "Couldn't change permissions of $mailfile: $!\n";
    my $exec=`$chroot $domain $chown $accounts[$choice].$mail_grp $mailfolder </dev/null 2>&1`;
    if ($?) {
	print $exec;
	&logit("Error when creating a mail folder for $accounts[$choice]: $?") if ($log);
	exit(0);
    }
    
    &text_box("Mail Folder","$ex","15 62");
    &logit("Mail folder for $accounts[$choice] created: $mailfolder") if ($log);
    return 1;
    
}
sub finger_account{
    my ($text,$finger)=@_;
       
    my (@accounts)=split(/\n/,$text);
    my ($i,$acts,$j);
    for ($i=0;$i<@accounts;$i++){
	$j=$i+1;
	$acts .= " $j \"$accounts[$i]\"";
    }
    &menu($menu_title,"\"Finger Account\"","20  70 11","$ansfile",$acts);
    my $choice=&get_answer("$ansfile",3);
    if (!$choice){return 1;}
    $choice--;
    chomp ($accounts[$choice]);
    my $ex=`$chroot $domain $finger -l $accounts[$choice] </dev/null 2>&1`;
    
    &text_box("Finger",$ex,"21 62");
    return 1;
    
 }

sub delete_account{
    my ($text,$server,$userdel)=@_;
    my ($i,$acts,$j,$log_text,$log_text2,$error_text);
    my (@accounts)=split(/\n/,$text);
    for ($i=0;$i<@accounts;$i++){
	$j=$i+1;
	$acts .= " $j \"$accounts[$i]\"";
    }
    &menu($menu_title,"\"Delete Accounts\"","20  70 11","$ansfile",$acts);
    my $choice=&get_answer("$ansfile",3);
    return 0 if (!$choice); # cancelled pressed.
    $choice--;
    if (($accounts[$choice] eq "root") && ($delete_root == 1)){
	$error_text = "The root account cannot be removed!\n";
	&info ($backtitle,$error_text,"10 60");
	return 1;
    }
    my ($exec,$ex);
    my $del_text = "Delete User's Home Directory?";
    my $del_home=&yes_no("Home Dir!",$del_text,"14 62");
    if ($del_home == 0){ 
	$ex="$chroot $server $userdel -r $accounts[$choice]";
	$log_text2 = " Home directory was also deleted.";
    }
    else { $ex = "$chroot $server $userdel $accounts[$choice]";}
    my $ctext="Are you sure you would like to delete $accounts[$choice]";
    $ctext .= "?" if ($del_home != 0);
    $ctext .= "\n";
    $ctext .= "and the home dir contents?" if ($del_home == 0);
    my $confirm=&yes_no("Confirm",$ctext,"14 62");
 
    if ($confirm == 0){
	my $exec=`$ex </dev/null 2>&1`;
	$exec = "User $accounts[$choice] deleted." if (!$exec);
	$log_text = "$domainname: User Account $accounts[$choice] deleted.";
	$log_text .= $log_text2;
	$log_text .= " -  $exec" if ($exec);
	&logit ($log_text) if ($log);
	&text_box("User Deleted",$exec,"20 60");
    }
		  return 1;
    
}

sub services{
# this function tweaks the service files, ie. conf.pop and conf.ftp.

# it takes the service file and the name of the service as arguments, 
# ie. "conf.samba" and "SAMBA" could be an example you may want to implement
# yourself.

    my ($file,$service)=@_;
    
    my ($active)=&conf_files($file);
    my ($text,$dialog,$copy);
    if ($active eq "Yes"){
	my $text = "The $service service is currently active.\nDisable It?";
	my $dialog=&yes_no("Home Dir!",$text,"14 62");
	if ($dialog == 0){ 
	    &delete_conf_entry($file);
	}
    }
    elsif ($active ne "Yes"){
	$text = "The $service service is not active.\nEnable It?";
	my $dialog=&yes_no("Service",$text,"14 62");
	
	if ($dialog == 0){ 
	    
	    if ($service eq "FTP"){
	 
		$text = "Copy over the FTP config. files?";
		my $copy=&yes_no("Files",$text,"14 62");
		if ($copy == 0){
		    &ftp_files();
		}
	    } # end if $service eq "FTP"
	    &edit_conf_file($file);
	
	}
    
    }
    my $final_active=&conf_files($file);
    if ($final_active eq "Yes"){
	$text = "$service service is currently active";}
    else{
	$text = "$service service is currently not active";
    }
    
    &info ($backtitle,$text,"10 50");
    return;
}

sub ftp_files(){
    # copy over the ftp config files
    my @ftp_config_files=split(/ /,$ftp_config_files);
    
    my ($i);
    
    for ($i=0;$i<@ftp_config_files;$i++){
	`cp $ftp_config_files[$i] $domain/etc`;
    }
    
    return;
}
sub su_exec{
# simly su over to the server as if you just telnetted into the virtual domain.
    
    my ($server,$su)=@_;
    
    my $text = "Su over to $server as which user?\n";
    $text .= "(Virtfs will exit)";
    my ($su_user)=&input($backtitle,$text,"root","12 55");
    if (!$su_user){return 1;}
    if (!$su){&error("su not found!\nYou may need to edit this script!");return;}
    &logit("su to server $domainname as $su_user") if ($log);
    exec ("$chroot $server $su $su_user") || &error("$!");
   
    exit(0); #in case
}

sub find_path{
# find the path of a given file for execution.
# If the file is not found, complain to the user.

    my ($file)=@_;
    
    my ($text,$error_apps,$app,$i);
    my @paths=("/usr/sbin","/usr/bin","/usr/local/bin","/bin","/usr/lib");

    for ($i=0;$i<@paths;$i++){
	if (-e "$domain$paths[$i]/$file"){
	    $app="$paths[$i]"."/".$file;
	    last;
	}
    }
    if ($app){ return $app; }

    else { 
	$text .= "$file could not be found in $domain \n";
	
	$text .= "\nCorrect this problem if you ";
	$text .= "wish to use \n'$file' from virtfs.";
	my $continue=&info($backtitle,$text,"15 62");
	return 0;
    }
    return 1;
}

sub list_all{
    open (SF, $server_file);
    my ($list,$d,$p);
    while (<SF>){
	
	s/#.*//;
	s/^\s+//;
	s/\s+$//;
	($d,$ip)=split(/\:/,$_);
	$list .= "$d - $ip\n" unless (!$d);
    }
     my $continue=&info($backtitle,$list,"15 50"); 
    return 1;
}
sub check_username{
# confirm if the username is a legal one.
    my ($user)=&input($backtitle,"Please Enter a Username","","12 55");
    if (!$user){return 0;}    
    
    if ($user !~ /^[a-z0-9_][a-z0-9_\-]*$/ || $user eq "a-z0-9_-") {
	# if this is an illegal username...
	my $badname= "Please use only lowercase characters or digits\n";
	&text_box("Illegal Name","$badname","15 62");
	return 0;
    }
    return $user;
}

sub add_user{
# Add a user to a domain. Similar to how virtexec would carry this out.
# It will ask for the username, the name of the account and the home directory.
    
    my ($server,$adduser)=@_;
    
    my $text = "Please enter the username:\n";
    my $log_text;
    my $user;
    while (1){ # check with input if username is correct, if it isn't display message and ask for input again.
	($user)=&check_username;
	last if ($user);
	last if (!$user);
    }
    return 1 if (!$user); # user cancelled

    
    $text = "What is the full name of $user\n";
    my ($name)=&input($backtitle,$text,"","12 55");
    $text = "Default group for $user:\n";
    my $group=&list_groups("useradd");
    if (!$group){return 1;}
    $text = "Home directory for $user (within $domainname):\n";
    my ($dir)=&input($backtitle,$text,"$default_home/$user","12 55");
    #$dir="$default_home/$user" if (!$dir);
    return 1 if (!$dir);
   my $shell=&list_shells();
    
    return 1 if (!$shell);

    #execute the command to su over as a user using '/' as the virtual directory.
    my $exec;

    if ($name){   
	
	$exec="$chroot $server $adduser -c \"$name\" -g $group -d $dir -s $shell $user";
    }
    else{
	$exec="$chroot $server $adduser -d $dir -g $group -s $shell $user";
    }
    
    if ($group eq "NO GROUP"){
	$exec =~ s/-g $group//;
    } # if no group is defined remove the entry from the command
    
    my $status=system("$exec 2>$tmp/user"); #execute the command.
    my $message; # in case of any errors
    if ($status != 0){
	open (TEMP, "+< $tmp/user");
	&logit("Error occured when creating $user") if ($log);
	while (<TEMP>){
	    $message .= $_; # display the error to the end user, if any..
	&logit("$_") if ($log);   
	}
       
	close (TEMP);
	unlink ("$tmp/user");
	if ($message){ 
	    &error("$message\n"); 
	    return;
	    
	}
    }    
    &logit("Account for $user (Name: $name) added") if ($log);

    my $chown=&find_path("chown");
    if (! -d $domain.$dir){ #just in case...
	mkdir($domain.$dir, 0700) ||  die "couldn't create $domain.$dir - user was still created..\n: $!";


	if ($chown){
	    if ($group){
	    $exec=`$chroot $domain $chown $user.$group $dir </dev/null 2>&1`; 
	}
	    else {$exec=`$chroot $domain $chown $user.$user $dir </dev/null 2>&1`;}
	    if ($?) { &text_box("Mail Folder","Couldn't chown directory:\n$exec","15 62");&logit("Error when creating $dir: $?") if ($log);}
	}
    }
    &logit ("Home directory for $user: $dir created") if ($log);
    $text = "$user has been added to $domainname\n";
    $text .= "Full Name: $name\n" if ($name);
    $text .= "Group: $group\n";
    $text .= "Shell: $shell\n";
    $text .= "Home Dir: $dir\n\n";
    $text .= "Configure password for $user?\n";
    $text .= "You can do this later from the menu\n";
    my $continue=&yes_no("Password?",$text,"18 53");
    if ($continue == 0){
	my $passwd = &find_path("passwd");
	if ($passwd){
	system("$chroot $domain $passwd $user");
	&logit ("Password for $user reset") if ($log);
    }
    }
    my ($ex);


    if ($smail){ # sendmail operations
    if ((! -d $mail_folder."/".$user) || (!$chown)){
	$text = "Create a mail folder for $user in:\n $mail_folder?\nYou can do this later from the menu";
	my $continue=&yes_no("Mail Folder?",$text,"12 53");
	if ($continue == 0){

	     if (! -d $domain.$mail_folder){
	# couldn't find the mail folder directory!
		 
		 $ex = "$mail_folder was not found within domain.\n";
		 $ex .= "Edit $config_file. Are you running Sendmail?\n";
		 &text_box("Mail Folder","$ex","15 62");
		 return 1;
	     }
	     my $mailfile=$domain.$mail_folder."/".$user;
	     open (FH, "+> $mailfile") || die "Didn't work - fix virt.conf for mail dir.- user still created: $!\n";
	     close (FH);
	     $ex="Mail file $mailfile\ncreated.";
	     $ex .= "\nPermissions were also passed..";
	     &logit ("Mail folder for $user was created.") if ($log);
	     my $mailfolder=$mail_folder."/".$user;
	     chmod 0600, $mailfile || &text_box("Mail","Couldn't change permissions of $mailfile: $!\n","15 62");
	     my $exec=`$chroot $domain $chown $user:$mail_grp $mailfolder </dev/null 2>&1`;
	     # if an error was found
	     if ($?) {
		 print $exec;
		 &logit($?) if ($log);
		 exit(0);
	     }
	  
	     &text_box("Mail Folder","$ex","15 62");
	     
	 } # end continue
    }
} #end if smail
    return 1;
}

sub apache_config{
    my $intro = "The next few questions will help you in setting up your ";
    $intro .= "virtual host for web services, through Apache.";
    $intro .= "After this is done, it is up to you to add more features ";
    $intro .= "that you may want manually, and restart Apache...";
    
    my $continue=&info($backtitle,$intro,"15 50"); 
   
    my $text = "Where is Apache's httpd.conf file?";
    my ($conf)=&input($backtitle,$text,"$apache_conf","15 55");
    return 1 if (!$conf);
    if (! -e $conf){
	$text = "Could not find or edit:\n $conf!\n";
	$text .= "Edit $config_file\n";
	&text_box("Missing Apache File",$text,"10 62");
	return 1;
    }
    my ($dom,$confirm,$virthost,$final,$sname);
    
    while (1){
    $text = "Enter the domain name for which you would like to configure:  ";
    $text .= " ie. www.$domainname ";
    $dom =&input($backtitle,$text,"www.$domainname","15 55");
    return 1 if (!$dom);
    $virthost = "<VirtualHost $dom>\n";
    $text = "Server Name? ie. $domainname";
    $sname=&input($backtitle,$text,$domainname,"15 55");
    return 1 if (!$sname);
    $virthost .= "ServerName $sname\n";
    $confirm = &apache_confirm($virthost);
    last if ($confirm);


}
    $final .= $virthost;

    my ($adminemail,$errorlog,$tlog,$alog,$rootdir,$cgi);
    while (1){
    $text = "Enter in the Virtual Host Admin E-Mail Address:";
    $adminemail=&input($backtitle,$text,"","15 55");
    return 1 if (!$adminemail);
    $text = "The Virtual Host Document Root is where the web site content";
    $text .= " will be stored.";
    $text .= " Please enter the full path for where this will be located:";
    $rootdir=&input ($backtitle,$text,"","16 55");
    return 1 if (!$rootdir);
    $virthost = $final;
    $virthost .= "ServerAdmin $adminemail\n";
    $virthost .= "DocumentRoot $rootdir\n";
   
    $confirm = &apache_confirm($virthost);
    last if ($confirm);

    
}
    
    $final = $virthost;
    while (1){
	$text = "Please enter in a full path filename for the error log. ";
	$text .= "Type 'none' if you don't want this feature.";
	$errorlog=&input($backtitle,$text,"","15 55");
	return 1 if (!$errorlog);
	$virthost = $final;
	$virthost .= "ErrorLog $errorlog\n" if ($errorlog ne "none");
	
	$text = "Please enter in a full path filename for the transfer log. ";
	$text .= "Type 'none' if you don't want this feature.";
	$tlog=&input($backtitle,$text,"","15 55");
	return 1 if (!$tlog);
	$virthost .= "TransferLog $tlog\n" if ($tlog ne "none");
	$confirm = &apache_confirm($virthost);
	last if ($confirm);
    }
    $final = $virthost;

    while (1){
	$text = "If you want CGI enabled, please enter the location of the ";
	$text .= "cgi-bin directory. The alias will also be created. ";
	$text .= "Enter 'none' if you do not want this feature.";
	$cgi=&input($backtitle,$text,"","15 55");
	return 1 if (!$cgi);
	$virthost = $final;
	$virthost .= "ScriptAlias /cgi-bin/ $cgi\n" if ($cgi ne "none");
	$virthost .= "AddHandler cgi-script .cgi .pl\n" if ($cgi ne "none");
	$confirm=&apache_confirm($virthost);
	last if ($confirm);
    }
    $final = $virthost;
    #open (CONF, "+< $conf");
   
    #close (CONF);
   
    $final .= "</VirtualHost>\n";
    my $added=0;
    # generate backups for httpd.conf operations..
    my $new=$conf . ".new"; 
    my $old=$conf . "-pre-".$domainname;
    link ($conf,$old); # backups
    open (NEW, ">$new");
    open(CURRENT, "+< $conf");
    while (<CURRENT>){
	print NEW $_;
	if ((/\<\/VirtualHost\>$/) && ($added == 0)) {
	    $added = 1;
	    print NEW "\n\n";
	    print NEW $final;
	    
	}
    }
    close (CURRENT);
    
    if ($added == 0){
	print NEW $final;
    }
    
    close (NEW);
    unlink ($conf);
    rename($new,$conf);

    $text = "Changes have been made to:\n $conf.\n\n";
    $text .= "A backup was saved as:\n$old\n";
    &text_box("Edits Done!",$text,"15 60"); 
    &logit("Apache configuration set:\n $final") if ($log);
    return 1;
}

sub apache_confirm{
    my ($virthost)=@_;
    my ($confirm,$continue);
    $confirm = "This is the configuration so far:\n";
    $confirm .= $virthost;
    $confirm .= "</VirtualHost>\n\n";
    $confirm .= "Is this correct?";
    $continue=&yes_no("Confirm",$confirm,"20 53");
    if ($continue == 0){
	return 1;
    }
    return 0;
}


sub list_shells{

    if (! -e $shell_file){return 0;}
  open(S, $shell_file) || die "$shell_file:$!\n";
    my (@shells,$sh,$i,$j);
    push (@shells,"/bin/false");
    while(<S>) {
	if (/^\s*\//) {
	    s/^\s*//; s/\s+.*//; # chop
	    $sh = $_;
	 
	    if (-x  $domain."/".$sh) {
		push (@shells,$sh);
	    } 

	}
    }
    close (S);
    
    my $shells;
    for ($i=0;$i<@shells;$i++){
	$j=$i+1;
	$shells .= " $j \"$shells[$i]\"";
    }
&menu($menu_title,"\"Choose a Shell\"","20 70 12","$ansfile",$shells);
my $choice=&get_answer("$ansfile",3);
	
	if ((!$choice)||($choice == 0)){return 0;}
	$choice--;
	return $shells[$choice];
}



sub list_groups{
# open /etc/group within the virtual directory and read in the group information.

    my ($method)=@_;
    my $group_file=$domain."/etc/group";
    if (! -e $group_file){return 0;}
    my $LOCK_SH=1;
    my $LOCK_EX=2;
    my $LOCK_NB=4;
    my $LOCK_UN=8;
    my ($group,$i,$passwd,$gid,@group,%gid,$users,%users,%group,$grps,$j);
    open (GROUP, "$group_file") || die "Error: can't open $group_file\n";
    
    flock(GROUP, $LOCK_EX | $LOCK_NB) || die "The password file is currently busy: $!\n";
    
    while (<GROUP>) {
	chop;
	($group,$passwd,$gid,$users) = split(/:/);
	$group{$group}=$group;
	$gid{$group}=$gid;
	$users{$group}=$users;
    }
   
    flock(GROUP, $LOCK_UN);
    close(GROUP);
   
    
    if ($method eq "useradd"){
	$group{"NO GROUP"}="NO GROUP";
	@group=sort keys (%group);
	
	for ($i=0;$i<@group;$i++){
	    $j=$i+1;
	    $grps .= " $j \"$group[$i] : $gid{$group[$i]}\"";
	}
	
	&menu($menu_title,"Groups","20 70 12","$ansfile",$grps);
	my $choice=&get_answer("$ansfile",3);
	
	if ((!$choice)||($choice == 0)){return 0;}
	$choice--;
     
	return $group[$choice];
    }
    
    else {
	@group=sort keys (%group);
	for $i (@group){
	    $grps .= "$i : $gid{$i} : $users{$i}\n";
	}
	&text_box("Groups List",$grps,"20 60");
	return 1;
    }
} # end list_groups


sub list_accounts{
   # list all the user accounts in an alpahabetical order.

    my ($server)=@_;
    my ($login,$uid,$pwgid,$gecos,$home_dir,$shell)= &readpasswd(); #read in all usernames
    
    my ($name,$text,$i);
    my @tmp = sort keys(%$login);
    
    for $i (@tmp){
        $text .= "$i\n";
    }
    return $text;
} 

sub list_users{
# Simply parse the passwd file of the domain and send it off to the 
# text file viewer. Display the Username, their home directory and the shell.
    
    my ($server)=@_;
    my ($login,$uid,$pwgid,$gecos,$home_dir,$shell)= &readpasswd();
    my @tmp=sort keys(%$login);
    my $i;
    my $text = "Account      Dir      Shell\n";
    $text .="----------------------------\n";
    
    for $i (@tmp){
	$text .= "$i - ";
	$text .= "$home_dir->{$i} - " if ($home_dir->{$i});
	$text .= "[none] - " if (!$home_dir->{$i});
	$text .= "$shell->{$i} " if ($shell->{$i});
	$text .= "[none]" if (!$shell->{$i});
	$text .= "\n";
	
    } 
    return $text;
} 

sub root_check{
my ($test);
die "You are not root - you need to have root access to run virtfs .\n" if $< && !$test;
return;
}

sub list_domains{
    my ($virtual)=@_;
    
# List all domains. We first check the virtfs configuration files, and
# then the leading virtual directory for (unlisted) domains.   
    
#automatically add virtual domains to $server_file with $domain/etc entries
    
    my (@dirs,$name,$i,@domains,$list,$ip,@list,@sorted_list);

    if (-e $server_file){
    
open (FH,"+< $server_file");
        
    while (<FH>){
	chomp;
	s/#.*//;
	s/^\s+//;
	s/\s+$//;
	next unless length;
	($name,$ip)=split(/\:/, $_,2);
	$i++;
	
	$list .= "$name $ip " if $name && $ip && (-d "$virtual_dir/$name/etc"); # look for a filesystem
	#push (@list, "$name $ip ") if $name && $ip && (-d "$virtual_dir/$name/etc"); # look for a filesystem
	}
close (FH);
}

    else{
	open (FH, ">> $server_file");
	print FH "# Created by virtfs - please don't edit this unless you know what you're doing\n";
	close (FH);
    }
    $list .= " Create Server";
    
    
# add directories!
    
    
    &menu($menu_title,"Domains","20 70 12","$tmp/domain",$list);
    my $choice=&get_answer("$tmp/domain"); # ,1
    if (!$choice){print "\n";exit(0);}
    open (FH,"+< $server_file") || die "Cannot open $server_file. Please make sure the configuration is alright: $!\n";
    my $found;
    if ($choice ne "Create"){    
	# user is editing an existing virtual domain.
	# find out what the user inputted and send the domainname and IP address back.
	while (<FH>){
	    next unless length;
	    ($name,$ip)=split(/\s*\:\s*/,$_,2);
	    if ($name eq $choice){
		$found=1;
		
	    }
	}
    }
    close (FH);
    return ($choice,$ip) if ($found);
    return ($choice);
}

sub radiolist{
# Give the end user a choice of radio buttons.
    
    my ($title,$subtitle,$size,$menu_file,$list)=@_;
    
    system ($dialog . " --clear --title $title --radiolist $subtitle $size $list 2> $menu_file");
    return;
}

sub text_box{
    
# Display the text file viewer of a certain file.

    my ($title,$text,$size)=@_;
    open (TEMP, ">$tmp/text");
    print TEMP $text;
    close (TEMP);
    
    system ($dialog ." --clear --title \"$title\" --textbox $tmp/text $size");
    unlink "$tmp/text";
    return 1;
}

sub error{
# Generic error message. Here the user is given two choices. To return to the menu or 
# exit once and for all.

    my ($message)=@_;
    chomp ($message);
    my $text .= "Error Found:\n$message\n";
    &logit("Caught Error: $message\n") if ($log);
    $text .= "Continue?\n";
    my $continue=&yes_no("Error!",$text,"12 53");
    if ($continue==0){
	return;
    }
    else{
	exit(0);
    }
}


sub list_menu{
    
# List the menu to the user and return the user's choice back to the main 
# part of the script.
    
# print the sendmail menu options IF and only IF sendmail is configured in /etc/virt.conf
    
    my ($domain)=@_;
    
    my $list = "I \"Server Information\"";
    $list .= " C \"Change Name or IP\"";
    $list .= " R \"Delete Server\"";
    $list .= " V \"Configure Apache\"";
    $list .= " S \"su to Server\"";
    $list .= " F \"FTP Service\"";
    $list .= " M \"Mail Service\"";
    $list .= " A \"Add Account\"";
    $list .= " D \"Delete Account\"";
    $list .= " G \"Finger/Confirm Account\"";
    $list .= " P \"Change Password\"";
    $list .= " U \"List Users\"";
    $list .= " E \"List Groups\"";
    $list .= " O \"Create Group\"";
    $list .= " T \"Create Mail Folder\"" if ($smail);
    $list .= " Q \"View Sendmail Mail Queue\"" if ($smail);
    $list .= " B \"Rebuild Sendmail Aliases\"" if ($smail);
    $list .= " L \"List All Virtual Domains\"";
    $list .= " H \"Help\"";
    $list .= " W \"About\"";
    
    &menu($menu_title,"\"Menu For $domainname\"","20 50 11","$ansfile",$list);
    return;
}


sub menu{
    my ($title,$subtitle,$size,$menu_file,$list)=@_;
   
    system ($dialog . " --clear --title $title --menu $subtitle $size $list 2> $menu_file");
    return;
}

sub yes_no{
    my ($title,$text,$size)=@_;
    
    if (!$size){
        $size = "8 35";
    }
    my $status=system ($dialog ." --backtitle \"Virtual Services - virtfs\" --clear --yesno \"$text\" $size 2> $ansfile");
       # 0 is returned if 'Yes' was chosen.
    return $status;
    
}

sub parse_args{
# parse the arguments...
    my (@argv)=@_;

     while ($_ = $argv[0]) {
	 
	 shift @argv;
	
	if (/detect$/){&detect_new(); exit(0);}
	else { &usage($_);exit(0);}
    }
    return;
}

sub usage{
#user entered in an invalid argument to virtfs. 
# output the proper arguments and exit.

    my ($arg)=@_;
  warn <<USAGE;
$arg is not a valid option!

usage: virtfs
       [-detect] - detect virtual servers within $virtual_dir

virtfs $version

USAGE

    exit(0);
}

sub input{
# This function outputs a Dialog box which takes in an input from the user
# which we can later read in.
    
    my ($title,$subtitle,$text,$size,$ex)=@_;
    
    if ($size eq ""){
        $size="9 35";
    }
    unlink "$ansfile";
    my $exec=system ($dialog . " $title --inputbox \"$subtitle\" $size \"$text\" 2> $ansfile");
    
    
  if (($exec != 0) && ($ex)){ # user hit 'Cancel' so let's exit....
	print "Exiting...\nThank you for using virtfs.\n";
        exit(0);
    }
   if ($exec != 0){
	#when cancel is pressed and we do not want to exit the program, as we do above.
       return 0;
    }
        
    my ($choice)=&get_answer("",$ex);
    
    return $choice;
}

sub delete_conf_entry{
    my ($conf)=@_;
    chomp($ip);
    chomp($domain);
    open (CONF, "+< $conf");
    open (TEMP, "+> $tmp/conf");
    my $format="$ip $domain";  
    while (<CONF>){
	print TEMP $_ unless $_ =~ /$format/;
    }
    close (CONF);
    close (TEMP);
    
    system ("mv $tmp/conf $conf");
    &logit("Server entry in $conf (service file) was removed") if ($log);
    return;
}


sub edit_conf_file{
    # change the service file, ie. conf.pop or conf.ftp adding the specified virtual server
    # information. 
    my ($conf)=@_;
    chomp($ip);
    chomp($domain);
    my ($found,$temp,$format);
    if (!-e $conf){
        open (CONF, ">> $conf") || die "Couldn't create $conf\n";
        print CONF "# Automatically written by Virtfs.\n";
        print CONF "# Please don't edit this unless you know how to.\n";
        print CONF "# ie. IP space DIRECTORY\n\n";
        print CONF "$ip $domain\n";
        print CONF "\ndefault /\n";
	close (CONF);
    }
    else {
        open (CONF, "+< $conf");
        unlink "$tmp/conf" if (-e "$tmp/conf");
        $temp="$tmp/conf";
	$format="$ip $domain";
        open (TEMP, ">> $temp") || die "Couldn't open $temp!\n";
        while (<CONF>){
	    if (($_ =~ /default \//) || ($_ =~ /$format/)){$found=1;}
	    else{
		print TEMP $_  unless ($_ =~ /^$/);
		# strip the blank lines.
	    }
	}
	close (CONF);
        print TEMP "$format\n" if ($found);
        print TEMP "\ndefault /\n"; # we must always keep default / as the last line!
        close (TEMP);
	&logit("Entry for server was added to $conf (service file)") if ($log);
	system ("mv $temp $conf");
        
    }
    return;
}

sub check_ip_format{
    my ($ipaddr)=@_;
    # check ip format is in x.x.x.x format, otherwise return false.
    if (!$ipaddr){return 255;}
    return 1 if ($ipaddr =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/ &&
		 $1 >= 0 && $1 <= 255 &&
		 $2 >= 0 && $2 <= 255 &&
		 $3 >= 0 && $3 <= 255 &&
		 $4 >= 0 && $4 <= 255);
    
    return 0;
}

sub remove_server_entry{
    #delete the virtual domain and ip entries in the servers.list file.
    my ($domainname)=@_;
    chomp ($ip);
    
    my $list_format=$domainname.":".$ip;
    open (SR, "+< $server_file");
    
    open (TEMP, "+>>$tmp/domain.tmp");
    while (<SR>){
	chomp;
	print TEMP "$_\n" unless $_ =~ /^$domainname/;
    }
    close (TEMP);
    close (SR);
    system ("mv $tmp/domain.tmp $server_file"); #lazy method
    &logit("Information for this server was removed from $server_file") if ($log);
    return;
}


sub get_answer{
    # This functions gets the user input from the dialog boxes.
    my ($file,$ex)=@_;
    if (!$file){$file = "$ansfile";}
    
    my ($choice);
    
    open (ANSWER, "$file");
    while (<ANSWER>){
        chomp;
	$choice=$_;
    }
    close (ANSWER);
    unlink ($ansfile);
    if ((!$choice) && ($ex == 3)){
	
	return;
    }

    elsif ((!$choice) && ($ex == 1)){
        return 0; #exit
    } 
    elsif (!$choice){
	return 0;
      
    }
  
    
    return $choice;
}


sub write_up{
    # This function writes in the domain-specific configuration stuff into a temp file.
    my ($key,$value)=@_;
    open (TEMP, "+>> $tmp/choices");
    print TEMP "$key:$value\n";
    close (TEMP);
    return;
}


sub info{
# this function simply builds the Info box from Dialog.
    
    my ($title,$text,$size)=@_;
    if (!$size){
        $size="22 53";
    }
    system($dialog . " $title  --msgbox \"$text\" $size");
    return 1;
} 

sub logit{
    # log the procedures carried out if log was set.
    
    my ($log_message)=@_;
    
my @make_date_month = ("Jan", "Feb", "Mar", "Apr", "May", "Jun",
		 	   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
    my @gmt = gmtime(time());
    my @lct = localtime(time());
    my $days = $lct[3] - $gmt[3];
    my $hours = ($days < -1 ? 24 : 1 < $days ? -24 : $days * 24) + $lct[2] - $gmt[2];
    my $mins = $hours * 60 + $lct[1] - $gmt[1];
    my $timezone = ($mins < 0 ? "-" : "+"); $mins = abs($mins);
    $timezone .= sprintf "%2.2d%2.2d", $mins/60, $mins%60;
    # end
    my (@tm, $dstr, $addr, $user, $ident);
    
    @tm = localtime(time());
    
    $dstr = sprintf "%2.2d/%s/%4.4d - %2.2d:%2.2d:%2.2d %s",
    $tm[3], $make_date_month[$tm[4]], $tm[5]+1900,
    $tm[2], $tm[1], $tm[0], $timezone;
    open (LOG, ">>$logfile") || die "Couldn't open $logfile for logging. Check configuration";
    if ($log_message eq "clear"){
	print LOG "\n";
    }
    else{
	print LOG "[$dstr] $domainname: $log_message\n";
    }
    close (LOG);
    return;
    
}


sub create_virt_domain{
# main function for creating a virtual directory.
    
    unlink ("$tmp/choices");
    my $domain=&get_virtual_dir; # specify all the directories needed
    &get_virtual_ip($domain); # And the IP address needed
   
    &write_up("copyfile",$copy_file);
    my ($config)=&config_lookup;
    
# Summarise what is going on to the user:
    my $text .= "This is the current configuration\n\n";
    $text .= "V. Server Directory: $config->{'virtdir'}\n";
    $text .= "Server Name: $config->{'domain'}\n";
    $text .= "Server IP Address: $config->{'virtip'}\n";
    $text .= "\nConfiguration File: $config->{'copyfile'}\n";
    $text .= "\nProceed?\n";
    my ($status)=&yes_no($backtitle,$text,"18 53"); 
    
    if ($status != 0){ # user did not like what (s)he saw...leave...
	print "User did not like the configuration..Exiting...\n";
	exit(0);
    }
    
    &execute($config); # Configuration is set, carry out operations
    &create_virtual_server($config); # copy all the files as read in.
    return;
    
    
    
}


sub create_virtual_server{
    my ($config)=@_;
    my ($copy_files)=$config->{'copyfile'};
    my $domain=$config->{'domain'};
    my $text = "The files are about to be hard linked from:\n $template_dir.\n";
    $text .= "A log will be kept in $tmp/$config->{'domain'}.\n\n";
    $text .= "Please view that once the linking is done.\n";
    $text .= "\nProceeding will start the linking process...\n";
    &info($backtitle,$text,"15 55");   
    
    my (@ex,$i);
    open (FH, "+< $copy_files");
    unlink ("$tmp/$config->{'domain'}");
   
    my ($system,$execute);
    # Hard link ALL the files in the template directory to the files in the new virtual server.
    system ("cp -alv $template_dir/* $config->{'virtdir'} >>$tmp/$config->{'domain'} 2>&1");

    # Copy over the custom commands defined in /etc/virt.conf under the <Shell Commands> 
    
    &execute_custom_commands($config_file,$config->{'virtdir'},$config->{'domain'});
    &write_hosts_file($config->{'virtdir'},$config->{'domain'},$config->{'virtip'});
    &create_virtual_directories($config->{'virtdir'});
    
    
    close (FH);
   
    if (!-e "$config->{'mainvirtdir'}/servers.lists"){
	open (RD, ">>$config->{'mainvirtdir'}/servers.lists");
	print RD "# DO NOT TOUCH THIS FILE\n";
	print RD "$config->{'domain'}:$config->{'virtip'}\n";
    }
    else {
	open (RD, ">>$config->{'mainvirtdir'}/servers.lists");
	print RD "$config->{'domain'}:$config->{'virtip'}\n"; }
    close (RD);
    
    &sendmail_config($config->{'domain'},$config->{'virtdir'}) if ($smail == 1); # configure sendmail
    &create_ldconfig_conf($config->{'virtdir'});
    &create_resolv_conf($config->{'domain'},$config->{'virtdir'});

    $text = "Congratulations!\n";
    $text .= "$config->{'domain'} has been setup.\n";
    $text .= "\n\nThe administrator for the server\n will be 'admin'.";
    &info($backtitle,$text,"15 50");  
    &set_admin_rights($config->{'virtdir'});
    return;
}

sub create_virtual_directories{
    # create new directories within the virtual server.
    # These directories should not be hard linked as they are virtual server
    # specific.
    my ($virtdir)=@_;
    my ($x,$y,$minor);
    my @prefix1 = ("p".."z", "a".."e");
    my @prefix2 = (0..9, "a".."f");
  for ($x = 0; $x <= 15; $x++) {
      for ($y = 0; $y <= 15; $y++) {
          $minor = ($x * 16) + $y;
          `mknod -m 0620 $virtdir/dev/pty$prefix1[$x]$prefix2[$y] c 2 $minor`;
          `mknod -m 0620 $virtdir/dev/tty$prefix1[$x]$prefix2[$y] c 3 $minor`;
      }
  } # end for

   `mknod -m 0666 $virtdir/dev/random c 1 8`;
   `mknod -m 0666 $virtdir /dev/null c 1 5`;
   `mknod -m 0666 $virtdir/dev/urandom c 1 9`;
   `mknod -m 0666 $virtdir/dev/tty c 5 0`;
    
    # Create usr/local stuff
  
}


sub sendmail_config{
    # Configure Sendmail.
    # Copy, and edit files: sendmail.cw, sendmail.cf, /etc/mail/* and aliases
    my ($domain,$virtdir)=@_;
    
    my @sendmail_domains=("localhost","$domain","mail.$domain");
    
    my $text = "You have set Virtfs up for Sendmail configuration.\n";
    $text .= "Should we configure $domain for Sendmail?\n";
    
    my ($status)=&yes_no($backtitle,$text,"15 58"); 
    
    if ($status != 0){ # user chose no
	return;
    }
    
    else{
	my $i; # counter
	open (CW, "+>>$virtdir/etc/sendmail.cw");
	print CW "# sendmail.cw - aliases for your virtual server here\n";
	for ($i=0;$i<@sendmail_domains;$i++){
	    print CW $sendmail_domains[$i]."\n";
	}
	close (CW);

	`cp -fp $sendmail_cf $virtdir/etc/`;
	`cp -fp $sendmail_alias $virtdir/etc/`;
	`cp -a /etc/mail $virtdir/etc`;

	&alter_sendmail_cf($domain,$virtdir);

	mkdir ("$virtdir/var/spool/mqueue",0755);

	$text = "$virtdir/etc/sendmail.cw configured...\n";
	$text .= "$virtdir/etc/sendmail.cf configured...\n";
	$text .= "$virtdir/var/spool/mqueue created...\n";
	$text .= "$virtdir/etc/aliases copied...\n";
	$text .= "$virtdir/etc/mail copied...\n";
	&info($backtitle,$text,"13 50"); 
    }
    return;
}

sub set_admin_rights{
    my ($virtdir)=@_;
    
    my $admin_list =  &read_admin_config($config_file);
    chroot ($virtdir); chdir("/");

    # Create the admin user and group 
    my $adduser=&find_path("adduser");
    my $groupadd = &find_path("groupadd");
    my $chown = &find_path("chown");
    `$groupadd admin`;
    my ($admin_info,$gid,$users)=&get_gid("/etc/group");
    my $admin_gid = $gid->{'admin'};
    my $admin_uid = $admin_gid;
    `$adduser -c \"Administrator\" -d /root -u $admin_uid -g admin -n -s /bin/bash admin`;
    
    # Give ownership of /usr/local to the admin
    my @new_directories=("/usr/local","/usr/local/etc","/usr/local/include","/usr/local/src",
			 "/usr/local/man","/usr/local/bin","/usr/local/sbin","/usr/local/lib");
    foreach (@new_directories){
	mkdir ($_, 0755);
	chown $admin_uid, $admin_gid, $_;
    }
   
    my @admin_dirs = split(/ /, $admin_list->{"Admin Directories"});
    my @admin_files = split (/ /, $admin_list->{"Admin Files"});

    foreach (@admin_files){
	if (!-d $_){
	    chown $admin_uid, $admin_gid, $_;
	}
      }

    foreach (@admin_dirs){
       
	if ($_ =~ /\*$/){
	    my ($chown_dir,$tmp)=split(/\*$/,$_);
	    `$chown -R $admin_uid.$admin_gid $chown_dir`;
	}
	else{
	    if (!-d $_){mkdir ($_, 0755);}
	    chown $admin_uid, $admin_gid, $_;
	}
    }

    return;
}


sub alter_sendmail_cf(){
    my ($domain,$virtdir)=@_;
    
    my $cf=$virtdir."/etc/sendmail.cf";
    open (TMP, "+>>$tmp/$domain.cf");
    open (CF, $cf);

    print TMP "### This sendmail.cf file has been generated for $domain by Virtfs.\n\n";
    
    while (<CF>){
	if ($_ =~ /Dj/){ # replace the Dj setting to tell sendmail which domain it is
	    print TMP "\n# This is how we tell Sendmail which domain this is - edited by Virtfs.\n";
	    print TMP "Dj$domain\n\n";

	}
	print TMP $_;
    }

    close (TMP); close (CF);
    rename ("$tmp/$domain.cf",$cf);
    chmod (0644,$cf);
    return;
}
    

sub check_sendmail_config{
    my ($sendmailcf)=@_;
    
    if (!-e $sendmailcf){
	print "I could not find $sendmailcf even though you have specified that Virtfs should use Sendmail.\n";
	print "Please edit $config_file and alter the Sendmail section.\n";
	exit;
    }

    return;
}

sub create_ldconfig_conf{ # create a default conf file for ldconfig
    my ($virtdir)=@_;
    my $i;
    my @ld_dirs=split (/ /, $ldconfig_libs);
    
    open (LDCONF, "+>>$virtdir/etc/ld.so.conf");
    
    for ($i=0;$i<@ld_dirs;$i++){
	print LDCONF $ld_dirs[$i]."\n";
    }

    close (LDCONF);
    return;
}

sub create_resolv_conf{
    my ($domain,$virtdir)=@_;
    open (RESOLV, "+>>$virtdir/etc/resolv.conf");
    print RESOLV "domain ".$domain."\n";
    print RESOLV "nameserver ". $ns1."\n";
    print RESOLV "nameserver ". $ns2."\n";
    
    close (RESOLV);
    return;
}

sub execute{
    #carry out the directory creation and pass on the variables
    
    
    my ($config)=@_;
    
    if (!-d $config->{'mainvirtdir'}){
	mkdir $config->{'mainvirtdir'},0755 || die "Couldn't create directory!";
    }
    mkdir $config->{'virtdir'},0755 || die "Couldn't create directory!";
    
}


sub get_virtual_dir{
    
    if (!-d $virtual_dir){
	my $warningtext="The virtual directory $virtual_dir does not exist. Please enter in a default directory";
	$warningtext .= " which will be created for you.";
	
        ($virtual_dir)=&input($backtitle,$warningtext,"$virtual_dir","15 55");
    }
    
    
    my ($domain)=&input($backtitle,"Please Enter Domain Name (domain.com)","","15 55",1);
    if (-d "$virtual_dir/$domain"){
	print "$domain seems to already exist! Try removing the server before trying to set it up again!\n";
	exit(0);
    }
    if (!$domain){exit (0);}
    &write_up("domain",$domain);
    &write_up("virtdir","$virtual_dir/$domain");
    &write_up("mainvirtdir",$virtual_dir);
    return $domain;
}

sub get_virtual_ip{
    my ($domain)=@_;
    my ($ip,$stat);
    while (1){
	($ip)=&input($backtitle,"Please enter the IP address for $domain","","15 55");
	$stat=&check_ip_format($ip);
	exit(0) if ($stat == 255);
	last if ($stat);
    }
    &write_up("virtip",$ip); # send to write it into the configuration file.
    return;
    
}

sub execute_custom_commands{
    my ($file,$virtual,$domain)=@_;
    open (FH, "+< $file");
    $virtual =~ s:/$::;
    my ($name,$value,%config);
    while (<FH>){
	chomp;
	s/#.*//;
	s/^\s+//;
	s/\s+$//;
	next unless length;
	
	if (/^<Shell Commands>/){
	while (<FH>){
	chomp;
	s/#.*//;
	s/^\s+//;
	s/\s+$//;
	next unless length;
	last if (/^<\/Shell Commands>/);
	s/!!virtual/$virtual/g;
	s/!!domain/$domain/g;
	system ("echo $_ >>$tmp/$domain");
	system ("$_ 2>>$tmp/$domain");
    }
    }
    }

    
    close (FH);
    return;
}




sub write_hosts_file{
    my ($virtdir,$domain,$ip)=@_;
    # Writes the /etc/hosts file within the virtual server
    open (HOSTS, ">$virtdir/etc/hosts");
    print HOSTS $ip."\t" . $domain ."\t localhost";
    close (HOSTS);
    return;
}
sub read_template_config{
    my ($template_conf)=@_;
# read the template conf file..
    my ($template_dir,$name,$value);


open (TEMPLATE, $template_conf);
while (<TEMPLATE>){
    chomp;
    s/#.*//;
    s/^\s+//;
    s/\s+$//;
    next unless length;
    
    if (/^<Template>/){
	while (<TEMPLATE>){
	    chomp;
	    s/#.*//;
	    s/^\s+//;
	    s/\s+$//;
	    next unless length;
	    last if (/<\/Template>/);
	    ($name,$value)=split(/\s*=\s*/, $_,2);
	    $template_dir = $value if ($name =~ /Template Directory/);
	}
    }
 }
close (TEMPLATE);

    if (!$template_dir){
	die "Could not find the template directory! Please check your configuration in $template_conf.\n";
    }
    else{
	return $template_dir;
    }
}

sub read_config{
    my ($file)=@_;
    open (FH, "+< $file");
    my ($name,$value,%config);
    while (<FH>){
	chomp;
	s/#.*//;
	s/^\s+//;
	s/\s+$//;
	next unless length;
	
	if (/^<Configuration>/){
	while (<FH>){
	chomp;
	s/#.*//;
	s/^\s+//;
	s/\s+$//;
	next unless length;
	last if (/^<\/Configuration>/);
	($name,$value)=split(/\s*=\s*/, $_,2);
	$config{$name}=$value;

    }
    }
    }
    close (FH);
    return (\%config);
}

sub read_admin_config{
     my ($file)=@_;
    open (FH, "+< $file");
    my ($name,$value,%admin_config);
    while (<FH>){
	chomp;
	s/#.*//;
	s/^\s+//;
	s/\s+$//;
	next unless length;
	
	if (/^<Admin Rights>/){
	while (<FH>){
	chomp;
	s/#.*//;
	s/^\s+//;
	s/\s+$//;
	next unless length;
	last if (/^<\/Admin Rights>/);
	($name,$value)=split(/\s*=\s*/, $_,2);
	$admin_config{$name}=$value;

    }
    }
    }
    close (FH);
    return (\%admin_config);
}

	
sub config_lookup{
# Read in all the user-specific configuration for what is going to be carried out.
    
    my ($file)="$tmp/choices";
    my ($key,%config);
    open (FH, "+< $file");
    while (<FH>){
	chomp;
	my ($name,$value)=split(/\:/,$_);
	$config{$name}=$value;
    }
    print $config{"Temporary Directory"};
 return (\%config);
   
}

sub check_required{
    my ($config_variables)=@_;
    my ($i);

my @required_variables=("Temp Directory","Default Shell","Default Home Directory","Apache Conf","Log","Leading Virtual Directory","Servers File","Use Sendmail","Copy Script");

for ($i=0;$i<@required_variables;$i++){
    if (!$config_variables->{$required_variables[$i]}){
	die "No value for $required_variables[$i] not found!\n";
    }
    
}
}

sub get_gid{
    my ($file)=@_;
    my $LOCK_SH=1;
       my $LOCK_EX=2;
       my $LOCK_NB=4;
       my $LOCK_UN=8;

        open (GROUP, "$file") || die "Error: can't open $file\n";

        flock(GROUP, $LOCK_EX | $LOCK_NB) || die "The group file is currently busy: $!\n";
    
    my (%group,$group,%passwd,%gid,%users);
    
    while (<GROUP>) {
	chop;
	my ($group,$passwd,$gid,$users) = split(/:/);
	$group{$group}=$group;
	$gid{$group}=$gid;
	$users{$group}=$users;
    }
    
    flock(GROUP, $LOCK_UN);
    close(GROUP);
    return (\%group,\%gid,%users);
}

sub readpasswd {
       
        my ($login, $passwd, $uid, $pwgid, $gecos,$home_dir, $shell, $tmp);
        my (%login,%passwd,%uid,%pwgid,%gecos , %home_dir,%shell)= ();

        # flocks
       my $LOCK_SH=1;
       my $LOCK_EX=2;
       my $LOCK_NB=4;
       my $LOCK_UN=8;

        open (PASSWD, "$passwd_file") || die "Error: can't open $passwd_file\n";

        flock(PASSWD, $LOCK_EX | $LOCK_NB) || die "The password file is currently busy: $!\n";

	while (<PASSWD>) {
                chop;
                ($login, $passwd, $uid, $pwgid,$gecos, $home_dir, $shell) = split(/:/);
                $login{$login} = $uid;
                $uid{$login} = $uid; 
                $pwgid{$login} = $pwgid;
                $passwd{$login} = $passwd;
                $gecos{$login} = $gecos;
                $home_dir{$login} = $home_dir;
                $shell{$login} = $shell;
        }
        flock(PASSWD, $LOCK_UN);
        close(PASSWD);
	return (\%login,\%uid,\%pwgid,\%gecos,\%home_dir,\%shell);
}
