#!/usr/bin/perl

#smtpauth
#called by exim to verify if an smtp user is allowed to
#send email through the server
#possible success:
# user is in /etc/virtual/domain.com/passwd and password matches
# user is in /etc/passwd and password matches in /etc/shadow

sub smtpauth
{
	$username	= Exim::expand_string('$1');
	$password	= Exim::expand_string('$2');
	$extra		= Exim::expand_string('$3');
	$domain		= "";
	$unixuser	= 1;

	#check for netscape that offsets the login/pass by one
	if ($username eq "" && length($extra) > 0)
	{
		$username = $password;
		$password = $extra;
	}

	if ($username =~ /\@/)
	{
		$unixuser = 0;
		($username,$domain) = split(/\@/, $username);
		if ($domain eq "") { return "no"; }
	}

	if ($unixuser == 1)
	{
		#the username passed doesn't have a domain, so its a system account
		$homepath = (getpwnam($username))[7];
		if ($homepath eq "") { return 0; }
		open(PASSFILE, "< $homepath/.shadow") || return "no";
		$crypted_pass = <PASSFILE>;
		close PASSFILE;

		if ($crypted_pass eq crypt($password, $crypted_pass)) { return "yes"; }
		else { return "no"; }
	}
	else
	{
		#the username contain a domain, which is now in $domain.
		#this is a pure virtual pop account.

		open(PASSFILE, "< /etc/virtual/$domain/passwd") || return "no";
		while (<PASSFILE>)
		{
			($test_user,$test_pass) = split(/:/,$_);
			$test_pass =~ s/\n//g; #snip out the newline at the end
			if ($test_user eq $username)
			{
				if ($test_pass eq crypt($password, $test_pass))
				{
					close PASSFILE;
					return "yes";
				}
			}
		}
		close PASSFILE;
		return "no";
	}

	return "no";
}

sub find_uid_apache
{
	my ($work_path) = @_;
	my @pw;
	
	# $pwd will probably look like '/home/username/domains/domain.com/public_html'
	# it may or may not use /home though. others are /usr/home, but it's ultimately
	# specified in the /etc/passwd file.  We *could* parse through it, but for efficiency
	# reasons, we'll only check /home and /usr/home ..   if they change it, they can
	# manually adjust if needed.

	@dirs = split(/\//, $work_path);
	foreach $dir (@dirs)
	{
		# check the dir name for a valid user
		# get the home dir for that user
		# compare it with the first part of the work_path

		if ( (@pw = getpwnam($dir))  )
		{
			if ($work_path =~/^$pw[7]/)
			{
				return $pw[2];
			}
		}
	}
	return -1;
}

sub get_domain_owner
{
	my ($domain) = @_;
	my $username="";
	open(DOMAINOWNERS,"/etc/virtual/domainowners");
	while (<DOMAINOWNERS>)
	{
		$_ =~ s/\n//;
		my ($dmn,$usr) = split(/: /, $_);
		if ($dmn eq $domain)
		{
			return $usr;
		}
	}
	close(DOMAINOWNERS);

	return -1;
}

sub find_uid_auth_id
{
	# this will be passwed either
	# 'username' or 'user@domain.com'

	my ($auth_id) = @_;
	my $unixuser = 1;
	my $domain = "";
	my $user = "";
	my $username = $auth_id;
	my @pw;

	if ($auth_id =~ /\@/)
	{
		$unixuser = 0;
		($user,$domain) = split(/\@/, $auth_id);
		if ($domain eq "") { return "-1"; }
        }

	if (!$unixuser)
	{
		# we need to take $domain and get the user from /etc/virtual/domainowners
		# once we find it, set $username
		my $u = get_domain_owner($domain);;
		if ($u != -1)
		{
			$username = $u;
		}
	}

	#log_str("username found from $auth_id: $username:\n");

	if ( (@pw = getpwnam($username))  )
	{
		return $pw[2];
	}

	return -1;
}

sub find_uid_sender
{
	my $sender_address = Exim::expand_string('$sender_address');

	my ($user,$domain) = split(/\@/, $sender_address);

	my $username = get_domain_owner($domain);

	if ( (@pw = getpwnam($username))  )
	{
		return $pw[2];
	}

	return -1;
}

sub find_uid
{
        my $uid = Exim::expand_string('$originator_uid');
	my $username = getpwuid($uid);
        my $auth_id = Exim::expand_string('$authenticated_id');
        my $work_path = $ENV{'PWD'};

	if ($username eq "apache" || $username eq "nobody")
	{
		$uid = find_uid_apache($work_path);
		if ($uid != -1) { return $uid; }
	}
	
	$uid = find_uid_auth_id($auth_id);
	if ($uid != -1) { return $uid; }

	# we don't want to rely on this, but it's all thats left.
	return find_uid_sender;
}

sub uid_exempt
{
        my ($uid) = @_;
        if ($uid == 0) { return 1; }

        my $name = getpwuid($uid);
        if ($name eq "root") { return 1; }
        if ($name eq "diradmin") { return 1; }

        return 0;
}


#check_limits
#used to enforce limits for the number of emails sent
#by a user.  It also logs the bandwidth of the data
#for received mail.

sub check_limits
{
	my $count = 0;
	open (LIMIT, "/etc/virtual/limit");
	my $email_limit = int(<LIMIT>);
	close(LIMIT);	

	#find the curent user
	$uid = find_uid();

	#log_str("Found uid: $uid\n");

	if (uid_exempt($uid)) { return "yes"; }

	my $name="";
	if ($email_limit > 0)
	{
		#check this users limit
		if (($name = getpwuid($uid)))
		{
			$count = (stat("/etc/virtual/usage/$name"))[7];
			if ($count > $email_limit)
			{
				die("You ($name) have reach your daily email limit of $email_limit emails\n");
			}
		}
	}
	
	open(USAGE, ">>/etc/virtual/usage/$name");
	print USAGE "1";
	close(USAGE);
	chmod (0660, "/etc/virtual/usage/$name");

	log_bandwidth($uid);

	return "yes"
}

sub log_bandwidth
{
	my ($uid) = @_;
	my $name = getpwuid($uid);

	if (uid_exempt($uid)) { return; }

	if ($name eq "") { return; }

	my $bytes = Exim::expand_string('$message_size');

	if ($bytes == -1) { return; }

	open (BYTES, ">>/etc/virtual/usage/$name.bytes");
	print BYTES "$bytes\n";
	close(BYTES);
	chmod (0660, "/etc/virtual/usage/$name.bytes");
}

sub log_str
{
	my ($str) = @_;

	open (LOG, ">> /tmp/test.txt");

	print LOG $str;

	close(LOG);
}
