#!/usr/bin/perl -Tw

require 5.002;

use strict;

BEGIN { $ENV{PATH} = '/usr/ucb:/bin' }

use Socket;
use Carp;
use FileHandle;


sub spawn;
sub logmsg { print "$0 $$: @_ at ", scalar localtime, "\n" }

my $port = shift || 2345;
my $proto = getprotobyname('tcp');
socket(Server, PF_INET, SOCK_STREAM, $proto) or die "socket: $!";

setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die "setsockopt: $!";

bind(Server, sockaddr_in($port, INADDR_ANY)) or die "bind: $!";

listen(Server, SOMAXCONN) or die "listen: $!";

logmsg "Mirmail Web Mail Server started on port $port";

my $waitedpid = 0;
my $paddr;

sub REAPER {
	$waitedpid = wait;
	$SIG{CHLD} = \&REAPER;
	logmsg "reaper $waitedpid" . ($? ? " with exit $?" : "");
}

$SIG{CHLD} = \&REAPER;

for ( ; $paddr = accept(Client,Server); close Client) {
	my ($port,$iaddr) = sockaddr_in($paddr);
	my $name = gethostbyaddr($iaddr,AF_INET);

	next if $waitedpid and not $paddr;
	spawn sub {
my ($oldfh) = select(Client);
$| = 1;
select($oldfh);

logmsg "Mirmail Web Mail Server connection from $name [",
	inet_ntoa($iaddr), "] at port $port\n";

	if(inet_ntoa($iaddr) ne '127.0.0.1')
	{
		print Client "420 Not Allowed Your IP Address\n";
		close Client;
	}


	print Client "++OK Please Setup id:passwd\n";

	my $data = <Client>;
	chop $data;
	chop $data;

	my ($act,$id,$passwd,$n_passwd);

	if($data =~ /^(\S+)\s(\S+):(\S+):(\S+)$/)
	{
		$act = $1;
		$id = $2;
		$passwd = $3;
		$n_passwd = $4;

		if($act eq 'add')
		{
			my $RE_MSG = &add_user($id,$passwd);
			print Client $RE_MSG;
		}
		elsif($act eq 'edit')
		{
			my $home = &viewhome($id,$passwd);
			if($home !~ /^110/ && $home !~ /^120/)
			{
				&changepw($id,$passwd,$n_passwd);
				close Client;
			}
			else
			{
				print Client "120 Password fail\n";
				close Client;
			}
		}
		elsif($act eq 'find')
		{
			my $home = &viewhome($id,$passwd);
			print Client $home;
		}
		elsif($act eq 'del')
		{
                        my $home = &viewhome($id,$passwd);
                        if($home !~ /^110/ && $home !~ /^120/)
                        {
				my $code = &deluser($id,$passwd);
				print Client $code;
                                close Client;
                        }
                        else
                        {
                                print Client "120 Password fail\n";
                                close Client;
                        }
		}
		else
		{
	                print Client "100 Not Matched Insert Format\n";
	                close Client;
		}
	}
	else
	{
		print Client "100 Not Matched Insert Format\n";
		close Client;
	}

	if($data eq 'quit' or $data eq 'exit')
	{
		print Client $id,$passwd,"\n";
	}

	};
}

sub spawn {
	my $coderef = shift;
	unless (@_ == 0 && $coderef && ref($coderef) eq 'CODE')
	{
		confess "usage: spawn CODEREF";
	}

	my $pid;
	if(!defined($pid = fork)) 
	{
		logmsg "cannot fork: $!";
		return;
	}
	elsif ($pid)
	{
	logmsg "begat $pid";
	return;
	}

	open(STDIN, "<&Client") or die "can't dup client to stdin";
	open(STDIN, ">&Client") or die "can't dup Client to stdout";
	STDOUT->autoflush();
	exit &$coderef();
}

sub add_user {
	my ($id,$passwd) = @_;

	&_makepasswd(\$passwd);

	open PW, "/etc/passwd";
	my $flag;
	my $line;
	my $MSG;
	while($line = <PW>)
	{
		chop $line;
		my @pwd_li = split ':',$line;
		my $user = shift @pwd_li;
		if($user eq $id)
		{
			$flag=1;
			$MSG = "210 User exist\n";
		}
	}
	close PW;

	if(-e "/etc/shadow" && $flag ne '1')
	{
		open SD, ">>/etc/shadow";
		flock SD, 2;
		print SD "$id:$passwd:11030:0:99999:7:::\n";
		flock SD, 8;
		close SD;
		open PW, ">>/etc/passwd";
		flock PW, 2;
		print PW "$id:x:99:99::/mirmail-user/$id:/bin/false\n";
		flock PW, 8;
		close PW;
		$MSG = "310 Add /etc/passwd and /etc/shadow User\n";
	}
	elsif($flag ne '1')
	{
		open PW, ">>/etc/passwd";
		flock PW, 2;
		print PW "$id:$passwd:99:99::/home/$id:/bin/bash\n";
		flock PW, 8;
		close PW;
		$MSG = "310 Add /etc/passwd User\n";
	}
	else
	{
		return $MSG;
	}
}

sub viewhome {
	my ($id,$passwd) = @_;

	&_makepasswd(\$passwd);

	my $line;
	my $id_flag;
	my $user_home;

	if(-e "/etc/shadow")
	{
		open PW, "/etc/shadow";
		my $flag;
		my $shadow_pwd;
		while($line=<PW>)
		{
			chop $line;
			my @user_info = split ':',$line;
			my $user = shift @user_info;
			my $pwd = shift @user_info;
			if($id eq $user && $passwd eq $pwd)
			{
				$flag=1;
				$shadow_pwd=$pwd;
			}
                        elsif($id eq $user)
                        {
                                $flag=2;
                        }
		}
		close PW;
	
		if($flag eq '1')
		{
	                open PW, "/etc/passwd";
	                while($line=<PW>)
	                {
	                        chop $line;
	                        my($user,$pwd,$name,$uid,$gid,$home,$shell) = split ':',$line;
	                        if($id eq $user)
	                        {
	                                $user_home = "$home:$shadow_pwd";
	                        }
	                }
			close PW;
			return $user_home;
		}
		elsif($flag eq '2')
		{
			return "120 Password fail\n";
		}
		else
		{
			return "110 User unknown\n";
		}

	}
	else
	{
		my $flag;
		open PW, "/etc/passwd";
                while($line=<PW>)
                {
                        chop $line;
			my $flag;
                        my($user,$pwd,$name,$uid,$gid,$home,$shell) = split ':',$line;
                        if($id eq $user)
                        {
				$flag=2;
                        }
			elsif($id eq $user && $passwd eq $pwd)
			{
				$flag=1;
				$user_home = "$home\n";
			}
                }

		if(!$flag)
		{
			return "110 User unkown\n";
		}
		elsif($flag eq '2')
		{
			return "120 Password fail\n";
		}
		else
		{
			return $user_home;
		}
	}		
}

sub deluser {
	my ($id,$passwd) = @_;
	&_makepasswd(\$passwd);
        if(-e "/etc/shadow")
        {

                open PW, "/etc/shadow";
                my $NEW_LIST;
                my $pwd_line = 0;
                while($pwd_line=<PW>) 
                {       
                        chop $pwd_line;
                        my @profile = split ':', $pwd_line;
                        my $id_pw = shift @profile;
                        my $pwd_pw = shift @profile;
			my $new_line;
			$new_line = $pwd_line."\n" if $id_pw ne $id;
	                $NEW_LIST .= $new_line;
                }
                close PW;

                open PW, ">/etc/shadow";
		flock PW, 2;
                print PW $NEW_LIST;
		flock PW, 8;
                close PW;
        }
                open PW, "/etc/passwd";
                my $NEW_LIST;
                my $pwd_line;
                while($pwd_line=<PW>)
                {
                        chop $pwd_line;
                        my @profile = split ':', $pwd_line;
                        my $id_pw = shift @profile;
                        my $pwd_pw = shift @profile;
                        my $uid_pw = shift @profile;
                        my $gid_pw = shift @profile;
                        my $org_pw = shift @profile;
                        my $home_pw = shift @profile;
                        my $shell_pw = shift @profile;
			my $new_line;
			$new_line = $pwd_line."\n" if $id_pw ne $id;
			print Client $id_pw;
	                $NEW_LIST .= $new_line;
                }
                close PW;

                open PW, ">/etc/passwd";
		flock PW, 2;
                print PW $NEW_LIST;
		flock PW, 8;
                close PW;
		return "410 User Deleted";
}	

sub changepw {
	my ($id,$passwd,$n_passwd) = @_;

	&_makepasswd(\$passwd);
	&_makepasswd(\$n_passwd);

	if(-e "/etc/shadow")
	{

                open PW, "/etc/shadow";
                my $NEW_LIST;
		my $pwd_line;
                while($pwd_line=<PW>) 
                {       
                        chop $pwd_line;
                        my @profile = split ':', $pwd_line;
                        my $id_pw = shift @profile;
                        my $pwd_pw = shift @profile;

                        if($id_pw eq $id)
                        {
                                if($pwd_pw eq $passwd)
                                {
                                        $pwd_pw = $passwd;
                                        $pwd_line = "$id_pw:$n_passwd:11030:0:99999:7:::"
                                }
                        }
                        $NEW_LIST .= "$pwd_line\n";
                }
		close PW;

                open H, ">/etc/shadow";
		flock H, 2;
                print H $NEW_LIST;
		flock H, 8;
                close H;
	}
	else
	{
                open PW, "/etc/passwd";
                my $NEW_LIST;
		my $pwd_line;
                while($pwd_line=<PW>)
                {
                        chop $pwd_line;
                        my @profile = split ':', $pwd_line;
                        my $id_pw = shift @profile;
                        my $pwd_pw = shift @profile;
                        my $uid_pw = shift @profile;
                        my $gid_pw = shift @profile;
                        my $org_pw = shift @profile;
                        my $home_pw = shift @profile;
                        my $shell_pw = shift @profile;

                        if($id_pw eq $id)
                        {
                                if($pwd_pw eq $passwd)
                                {
                                        $pwd_pw = $passwd;
                                        $pwd_line = "$id_pw:$n_passwd:$uid_pw:$gid_pw:$org_pw:$home_pw:$shell_pw"
                                }
                        }

                        $NEW_LIST .= "$pwd_line\n";
                }
		close PW;

                open H, ">/etc/passwd";
		flock H, 2;
                print H $NEW_LIST;
		flock H, 8;
                close H;
	}
}

sub _makepasswd{
        my($Pwd) = shift;
        my($EncPwd,$salt,@saltset);
        @saltset = ('a'..'z','A'..'Z','0'..'9','.','/');
        $salt = "$saltset[9]$saltset[3]";
        $EncPwd = crypt($$Pwd,$salt);
        $$Pwd = $EncPwd;
	\$$Pwd;
}
