#!/bin/perl -w



my $PID_FILE = "/var/run/hosting.pid";
my $FACILITY = "daemon";

my $pidFile = $PID_FILE;
my $facility = $FACILITY;
my $VERSION = "\nElectric Death Ferret v0.03b By Serge Egelman\n\n";
my $shell1 = "/etc/acct.mailonly";
my $shell2 = "/etc/acct.noshell";
my $aliases = "/etc/mail/virtualmail.txt";
my $mydomain = "esinet.net";
my $raddb = "/etc/raddb/users";
my $sendmail = "/etc/sendmail.cw";
my $userfile = "/etc/users.txt";

use DBI;
use Mail::Mailer;

my $from = "service\@yourdomain.com";
# from address for account creation welcome message
my $ftp_address = "mpl\@yourdomain.com";
# address where CC of account creation and ftp account creation goes
my $from_address = "root\@yourdomain.com";
# from address for all other notifications
my $to_address = "alerts\@yourdomain.com";
# to address for all other notifications
$dbname = 'hosting';
$user = 'root';
$password = 'password';
$dbd = 'mysql';

$dbh = DBI->connect ($dbname, $user, $password, $dbd);

if (!$dbh) {
     print "Error connecting to database; $DBI::errstr\n";
}


$pid = fork;
exit if $pid;
die "Couldn't fork: $!" unless defined($pid);


use POSIX;

POSIX::setsid()
    or die "Can't start a new session: $!";

$time_to_die = 0;

sub signal_handler {
    $time_to_die = 1;
}

$SIG{INT} = $SIG{TERM} = $SIG{HUP} = \&signal_handler;
# trap or ignore $SIG{PIPE}

if ($ARGV[0]){
if ($ARGV[0] eq '--start') {
	Start();
} elsif ($ARGV[0] eq '--stop') {
	Stop ();
} elsif ($ARGV[0] eq '--help') {
	Usage ();
} elsif ($ARGV[0] eq '--version') {
	Version ();
} else {
	Usage();
}
}
else { Usage(); }

sub Version () {
	print $VERSION; }

sub Usage () {
    print <<"USAGE";
Usage: $0 <action>
	--start		Start the daemon
	--stop		Stop the daemon
	--help		Help (this message)
	--version	Print version information

USAGE
    exit 1;
}

sub Start () {
	
    if (defined($pid)  &&  ($pid = PidOfProc('hosting'))) {
        print STDERR "A process 'hosting' is already running.\n";
        return;
    }


open(PID, ">$pidFile");
print PID "$$\n";
close(PID);

	until($time_to_die)
	{
	sleep 120;
$sth = $dbh->prepare("SELECT alias FROM alias") || die "Preparing: ", $dbh->errstr;
$sth->execute || die "Executing: ", $sth->errstr;

        @aliaslist = $sth->fetchrow_array();
        $sth->finish;
        $i = 0;
        while ($aliaslist[$i])
                {

$sth = $dbh->prepare("SELECT destination FROM alias WHERE alias=\"$aliaslist[$i]\"") || die "Preparing: ", $dbh->errstr;
                        $sth->execute || die "Executing: ", $sth->errstr;
                        @dest = $sth->fetchrow_array();
                        $sth->finish;
                        if ($dest[0]) {
                                open(ALIASES, ">>$aliases") || die "$aliases not found!\n\n";
                                $string="$aliaslist[$i]\t\t$dest[0]\n";

                                print ALIASES $string;
                                `/usr/bin/makemap hash /etc/mail/virtusertable.db < $aliases`;
                                close ALIASES;
                                }
			$sth = $dbh->prepare("DELETE FROM alias WHERE alias=\"$aliaslist[$i]\"") || die "Preparing: ", $dbh->errstr;
			$sth->execute || die "Executing: ", $sth->errstr;
			$sth->finish;
		$i++;
		}

$sth = $dbh->prepare("SELECT username FROM radius") || die "Preparing: ", $dbh->errstr;
$sth->execute || die "Executing: ", $sth->errstr;

        @radlist = $sth->fetchrow_array();
        $sth->finish;
        $i = 0;
        while ($radlist[$i])
                {
                                open(RAD, "<$raddb");
                                @radius = <RAD>;
                                close RAD;
                                open(RAD, ">$raddb");
                                print RAD "$radlist[$i]\tPassword=\"UNIX\"\n\n";
                                print RAD @radius;
                                close RAD;
                        $sth = $dbh->prepare("DELETE FROM radius WHERE username=\"$radlist[$i]\"") || die "Preparing: ", $dbh->errstr;
                        $sth->execute || die "Executing: ", $sth->errstr;
                        $sth->finish;


				$i++;
		}








$sth = $dbh->prepare("SELECT username FROM users") || die "Preparing: ", $dbh->errstr;
$sth->execute || die "Executing: ", $sth->errstr;

	@usernames = $sth->fetchrow_array();
	$sth->finish;
	$i = 0;
	while ($usernames[$i])
		{
		$sth = $dbh->prepare("SELECT mail from users WHERE username=\"$usernames[$i]\"") || die "Preparing: ", $dbh->errstr;
		$sth->execute || die "Executing: ", $sth->errstr;
		@mailacc = $sth->fetchrow_array();
		if ($mailacc[0] =~ /yes/) {
			$shell = $shell1;
			}
		else {
			$shell = $shell2;
			}
		$sth->finish;
		$sth = $dbh->prepare("SELECT name from users WHERE username=\"$usernames[$i]\"") || die "Preparing: ", $dbh->errstr;
		$sth->execute || die "Executing: ", $sth->errstr;
		@realname = $sth->fetchrow_array();
		$sth->finish;

		$sth = $dbh->prepare("SELECT password from users WHERE username=\"$usernames[$i]\"") || die "Preparing: ", $dbh->errstr;
		$sth->execute || die "Executing: ", $sth->errstr;
		@password = $sth->fetchrow_array();
		$sth->finish;
		@chars = ( "A" .. "Z", "a" .. "z", 0 .. 9 );
		$salt = join("", @chars[ map { rand @chars } ( 1 .. 2 ) ]);
		$newpass = crypt($password[0],"$salt");
		$error = `/usr/sbin/adduser -s $shell -c "$realname[0]" -p $newpass $usernames[$i]`;
		if (!($error)) {
			open(USERS,">>$userfile");
			print USERS $usernames[$i];
			close USERS;
			$sth = $dbh->prepare("SELECT alias FROM users WHERE username=\"$usernames[$i]\"") || die "Preparing: ", $dbh->errstr;
			$sth->execute || die "Executing: ", $sth->errstr;
			@alias = $sth->fetchrow_array();
			$sth->finish;
			if ($alias[0]) {
				open(ALIASES, ">>$aliases") || die "$aliases not found!\n\n";
				$string="$alias[0]\t\t$usernames[$i]\n";
				print ALIASES $string;
				`/usr/bin/makemap hash /etc/mail/virtusertable.db < $aliases`;
				close ALIASES;
				}
			if ($mailacc[0]) {
				open(RAD, "<$raddb");
				@radius = <RAD>;
				close RAD;
				open(RAD, ">$raddb");
				print RAD "$usernames[$i]\tPassword=\"UNIX\"\n\n";
				print RAD @radius;
				close RAD;
				$type = "mail-only";
			}
			elsif (!($mailacc[0])) {
				$type = "dialup";
			}
                        $sth = $dbh->prepare("SELECT contact FROM users WHERE username=\"$usernames[$i]\"") || die "Preparing: ", $dbh->errstr;
                        $sth->execute || die "Executing: ", $sth->errstr;
                        @contact = $sth->fetchrow_array();
                        $sth->finish;
			if ($contact[0] =~ /yes/) {
				$other_address = "$usernames[$i]\@$mydomain, $ftp_address";
				}
			if ($contact[0] =~ /\@/) {
				$other_address = "$usernames[$i]\@$mydomain, $ftp_address, ";
				$other_address .= $contact[0];
			}
			else {
				$other_address = "$ftp_address";
			}
			$subject = "New account at Broadband Network Services";
                        if ($alias[0]) {
                        $body2 = "Additionally, the following alias has been setup on your email account: $alias[0] -> $usernames[$i]\@$mydomain.";
                        }
			else { $body2 = ""; }

			$body = "Welcome to Broadband Network Services, Inc!  Your $type account at BNSI has been set up and is now ready to be used!  You will need to write down the following information and keep it in a safe place:\n\n\tUsername: $usernames[$i]\n\tPassword: $password[0]\n\n$body2 \n\nAdditional Information:\n\n\tModem pool #:\t817-0050\n\tDomain name:\tesinet.net\n\tPOP3 Server:\tmail.esinet.net\n\tSMTP Server:\tmail.esinet.net\n\tPrimary DNS:\t12.5.48.2\n\tSecondary DNS:\t12.5.48.4\n\tNews Server:\tnews.esinet.net\n\tProxy Server:\tproxy.esinet.net\n\nProxy Server:\n\n\tFor faster and more reliable web browsing, you can set your web browser to connect to our proxy server.  For instructions on this, see: http://www.yourdomain.com/helpdesk/proxy.shtml\n\nWeb Mail:\n\n\tAll users have the ability to check their email online using our web-based email software running on our secure server, simply go to https://secure.yourdomain.com/mail and log in using your username and password.  This service is especially benificial for those customers who frequently go out of town and would like to check their email from a remote site (mail remains on our server until you request it when you return home).\n\n

Internet Roaming:\n\n\tFor those customers who are in need of internet access on a laptop while out of the Charlottesville area, BNSI has a partnership with iPass which gives you local access numbers from around the world.  See our web site for more information: http://www.yourdomain.com/ and look under \"Services\".\n\nTUCOWS:\n\n\tWe are also pleased to announce that we are an official TUCOWS affiliate, to get access to the latest shareware and freeware, simply go to http://tucows.yourdomain.com/\n\n";
		$body .= "\n\nOther Services from BNSI:\n\n\tServer Colocation:\nProvide optimum internet visibility while off-loading the burden of external traffic from your internet connection.\n\n\tVirtual Web Hosting:\nAt BNSI we understand the need for World Wide Web presence on the Internet.  FREE one year domain name registration included with Web Site Hosting.\n\n\tDomain Registration:\nIf your company wants to have its own domain to be used for e-mail (your name\@ company name) or a web site (www.company name.com), BNSI now offers direct registration.  The cost is \$25 for one year registration.\n\n\tCollaborative Tools:\neRoom by eRoom Technology, Inc.  Manage your projects effectively and share knowledge efficiently with the power of this Internet-ready application.\n\n\tManaged E-mail Service:\nAllows you to manage your business while BNSI manages your e-mail.\n\n";
		$body .= "\n\nIf you have any questions, feel free to contact us at (804) 817-7300\n\n\tThanks,\n\n\tBroadband Network Services, Inc.\n\n";

	}
	elsif ($errors) {
			$subject = "New account at BNSI.net";
			$other_address = '$to_address, $ftp_address';
			$body = "Hi,\n\nYou're getting this message because the creation of a system account for $usernames[$i] failed with the following error: $errors";
		}

$mailer = Mail::Mailer->new("sendmail");
$mailer->open({ From    => $from,
                To      => $other_address,
                Subject => $subject,
              })
    or die "Can't open: $!\n";
print $mailer $body;
$mailer->close();

$sth = $dbh->prepare("DELETE FROM users WHERE username=\"$usernames[$i]\"")
        or die "Preparing: ", $dbh->errstr;
$sth->execute
        or die "Executing: ", $sth->errstr;

    @domains = $sth->fetchrow_array();
    $sth->finish;


		$i++;
		}
$sth = $dbh->prepare("SELECT domain FROM named")
        or die "Preparing: ", $dbh->errstr;
$sth->execute
        or die "Executing: ", $sth->errstr;

    @domains = $sth->fetchrow_array();
    $sth->finish;
	$i = 0;
	while ($domains[$i])
		{
		$sth = $dbh->prepare("SELECT db from named WHERE domain = \"$domains[$i]\"")
	or die "Preparing: ", $dbh->errstr;
		$sth->execute or die "Executing: ", $sth->errstr;
		@dbfile = $sth->fetchrow_array();
		$sth->finish;
		open(OUT,">/var/named/db.$domains[$i]") || die "Cannot open /var/named/db.$domains[$i] for writing\n";
		print OUT $dbfile[0];
		close OUT;

		$sth = $dbh->prepare("SELECT conf from named WHERE domain = \"$domains[$i]\"") || die "Preparing: ", $dbh->errstr;
		$sth->execute or die "Executing: ", $sth->errstr;
		@conf = $sth->fetchrow_array();
		$sth->finish;
                open(OUT,">>/etc/named.conf") || die "Cannot open /etc/named.conf for writing!\n";
		print OUT $conf[0];
		close OUT;

		$sth = $dbh->prepare("SELECT secondary from named WHERE domain = \"$domains[$i]\"") || die "Preparing: ", $dbh->errstr;
		$sth->execute or die "Executing: ", $sth->errstr;
		@second = $sth->fetchrow_array();
		$sth->finish;
		if ($second[0]) {
			open(OUT,">>/etc/hosting/secondary.conf") || die "Cannot open /etc/hosting/secondary.conf for writing!\n";
			`chown nobody.nobody /etc/hosting/secondary.conf`;
			print OUT $second[0];
			close OUT;
		}

                $sth = $dbh->prepare("SELECT sendmail from named WHERE domain = \"$domains[$i]\"") || die "Preparing: ", $dbh->errstr;
                $sth->execute or die "Executing: ", $sth->errstr;
                @mailcw = $sth->fetchrow_array();
                $sth->finish;
                if ($mailcw[0] =~ /yes/) {
                        open(OUT,">>$sendmail") || die "Cannot open $sendmail for writing!\n";
                        print OUT $domains[$i];
                        close OUT;
                }

                $sth = $dbh->prepare("DELETE from named WHERE domain = \"$domains[$i]\"") || die "Preparing: ", $dbh->errstr;
                $sth->execute or die "Executing: ", $sth->errstr;
                $sth->finish;

	$subject = "New DNS Entry";
	$body = "The following changes have been made to the named setup:\n\n\nnamed.conf:\n$conf[$i]\n\ndb.$domains[$i]:\n$dbfile[$i]\n\nAs always, remember to restart named via \"ndc reload\"\n\n";
	if ($mailcw[0] =~ /yes/) {
		$body .= "Additionally, the $sendmail has been updated, so you must restart sendmail.";
		}
$mailer = Mail::Mailer->new("sendmail");
$mailer->open({ From    => $from_address,
                To      => $to_address,
                Subject => $subject,
              })
    or die "Can't open: $!\n";
print $mailer $body;
$mailer->close();
		$i++;


		}
$sth = $dbh->prepare("SELECT domain FROM apache")
        or die "Preparing: ", $dbh->errstr;
$sth->execute
        or die "Executing: ", $sth->errstr;

    @domains = $sth->fetchrow_array();
    $sth->finish;
        $i = 0;
        while ($domains[$i])
                {
                $sth = $dbh->prepare("SELECT httpd_conf from apache WHERE domain = \"$domains[$i]\"")
        or die "Preparing: ", $dbh->errstr;
                $sth->execute or die "Executing: ", $sth->errstr;
                @apache = $sth->fetchrow_array();
                $sth->finish;
                open(OUT,">>/usr/local/apache/conf/httpd.conf") || die "Cannot open /usr/local/apache/conf/httpd.conf for writing\n";
                print OUT $apache[0];
                close OUT;
		`/bin/mkdir /websites/www.$domains[$i]`;
		`/bin/mkdir /websites/www.$domains[$i]/webstats`;
		`/bin/chown website.webadmin /websites/www.$domains[$i]`;
		`/bin/chmod 2775 /websites/www.$domains[$i]`;
		`/bin/chown webtrend.webadmin /websites/www.$domains[$i]/webstats`;
		`/bin/chmod 2775 /websites/www.$domains[$i]/webstats`;
		`/bin/ln -s /raid1/www/www.yourdomain.com/www-stats/index.php3 /websites/www.$domains[$i]/webstats/index.php3`;
		$sth = $dbh->prepare("SELECT cgi from apache WHERE domain = \"$domains[$i]\"") || die "Preparing: ", $dbh->errstr;
		$sth->execute or die "Executing: ", $sth->errstr;
		@cgi = $sth->fetchrow_array();
		$sth->finish;
		if ($cgi[0] eq 'yes') {
		`/bin/mkdir /websites/www.$domains[$i]/cgi-bin`;
		`/bin/chmod 755 /websites/www.$domains[$i]/cgi-bin`;
			}
		$sth = $dbh->prepare("SELECT suexec from apache WHERE domain = \"$domains[$i]\"") || die "Preparing: ", $dbh->errstr;
		$sth->execute or die "Executing: ", $sth->errstr;
		@suexec = $sth->fetchrow_array();
		$sth->finish;
		if ($suexec[0]) {
			`/usr/sbin/adduser -d /raid1/www/www.$domains[$i] -s /etc/acct.noshell -c www.$domains[$i] $suexec[0]`;
			`/bin/chown $suexec[0].$suexec[0] /websites/www.$domains[$i]`;
			`/bin/chown $suexec[0].$suexec[0] /websites/www.$domains[$i]/cgi-bin`;
			}
		$sth = $dbh->prepare("SELECT ftp from apache WHERE domain = \"$domains[$i]\"") || die "Preparing: ", $dbh->errstr;
		$sth->execute or die "Executing: ", $sth->errstr;
		@ftp = $sth->fetchrow_array();
		$sth->finish;
		if ($ftp[0] && $suexec[0]) {
			$line = `cat /etc/passwd|grep $suexec[0]`;
			@fields = split(/:/,$line);
			$id = $fields[2];
			`/usr/local/ncftpd/ncftpd_passwd -f /usr/local/ncftpd/passwd -a \"www.$domains[$i]:$ftp[0]:$id:$id:www.$domains[$i]:/raid1/www/www.$domains[$i]:\" -c`;
			$ftppass = crypt($ftp[0],"$salt");	
			`/usr/local/ncftpd/ncftpd_passwd -f /usr/local/ncftpd/passwd-ftp.esinet.net -p www.$domains[$i] -P $ftppass`;
			        $subject = "New FTP Account";
        $body = "\n\n\tThis message is to inform you that the web directory for $domains[$i] has been setup and is fully operational.\n\nTo add content to your new website, simply FTP to:\n\n ftp.esinet.net\n\tUsername: www.$domains[$i]\n\tPassword: $ftp[0]\n\nIf you have any questions, please call 817-7300\n\n";
$mailer = Mail::Mailer->new("sendmail");
$mailer->open({ From    => $from_address,
                To      => $ftp_address, 
                Subject => $subject,
              })
    or die "Can't open: $!\n";
print $mailer $body;
$mailer->close();
		$sth = $dbh->prepare("SELECT email from apache WHERE domain = \"$domains[$i]\"") || die "Preparing: ", $dbh->errstr;
                $sth->execute or die "Executing: ", $sth->errstr;
                @email = $sth->fetchrow_array();
                $sth->finish;
		if ($email[0])
			{
$mailer = Mail::Mailer->new("sendmail"); 
$mailer->open({ From    => $from_address, 
                To      => $email[0], 
                Subject => $subject,
              })
    or die "Can't open: $!\n";
print $mailer $body;    
$mailer->close();


			}	
			}	
			$sth = $dbh->prepare("DELETE from apache WHERE domain = \"$domains[$i]\"") || die "Preparing: ", $dbh->errstr;
                $sth->execute or die "Executing: ", $sth->errstr;
                $sth->finish;
$subject = "New changes to httpd.conf";
$body = "The following changes were made to httpd.conf:\n\n\n$apache[0]\n\n";
if ($suexec[0]) {
	$body .= "The following suexec account was created:\n\n\t$suexec[0]";
		}

$mailer = Mail::Mailer->new("sendmail");
$mailer->open({ From    => $from_address,
                To      => $to_address,
                Subject => $subject,
              })
    or die "Can't open: $!\n";
print $mailer $body;
$mailer->close();


		$i++;
		}
	}
}


sub Stop () {
    my $line;
    my $pid;

    if (open(PID, "<$pidFile")  &&  defined($line = <PID>)  &&  close(PID)
        &&  $line =~ /(\d+)/) {
        $pid = $1;
        if (CommandOfPid($pid) !~ /hosting/) {
            undef $pid;
        }
    }

    if (!defined($pid)  &&  !($pid = PidOfProc('hosting'))) {
        print STDERR "A process 'hosting' is not running.\n";
        return;
    }

    kill SigNo('TERM'), $pid;
}

sub CommandOfPid ($) {
    my($pid) = @_;
    my $cmd;
    if ($^O =~ /linux/) {
        $cmd = "/bin/ps p $pid 2>&1";
    } elsif($^O =~ /solaris/) {
        $cmd = "/bin/ps -fp $pid 2>&1";
    } else {
        die "Unknown operarting system: $^O\n"
            . "You need to configure the 'CommmandOfPid' function.\n";
    }

    my $line;
    if (open(PID, "$cmd |")) {
        while (defined($line = <PID>)) {
            if ($line =~ /\d+.*\d+\:\d+\s+(.*)/) {
                return $1;
            }
        }
    }
}

sub PidOfProc ($) {
    my($proc) = @_;
    my $cmd;
    if ($^O =~ /linux/) {
        $cmd = '/bin/ps axuw';
    } elsif ($^O =~ /solaris/) {
        $cmd = '/bin/ps -ef';
    } else {
        die "Unknown operarting system: $^O\n"
            . "You need to configure the 'ProcList' function.\n";
    }

    my $line;
    if (open(PID, "$cmd |")) {
        while (defined($line = <PID>)) {
            if ($line =~ /\S+\s+(\d+).*\d+\:\d+\s+(.*)/) {
                my $p = $1;
                my $c = $2;
                if ($c =~ /$proc/  &&  $p != $$) {
                    return $p;
                }
            }
        }
    }
    undef;
}
    sub SigNo($) {
        my($sig) = @_;
        if (!$signo) {
            require Config;
            if (!defined($Config::Config{sig_name})) {
                die "No signals configured?";
            }
            $signo = {};
            $signame = [];
            my $name;
            my $i = 0;
            foreach $name (split(' ', $Config::Config{sig_name})) {
                $signo->{$name} = $i;
                $signame->[$i] = $name;
                ++$i;
            }
        }
        if (!exists($signo->{$sig})) {
            die "No such signal: $sig";
        }
        $signo->{$sig};
    }

