#!/usr/bin/perl
#
# Name: add-popa3d-user
# Author: Tim van Erven <tve@vormig.net>
# Version: 1.3.1
# Website: http://gene.science.uva.nl/~talerven/software/
#
# Copyright (c) 2002,2003 Tim van Erven
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

use strict;
use warnings;
use Crypt::PasswdMD5;
use Fcntl;

# Options

# auth and mail subdirectories will be created in the
# virtual_domain_root_directory.  They will contain authentication
# information and mail respectively for your (virtual) users.

my $virtual_domain_root_directory = "/home/virtual/popa3d/127.0.0.1";

# Auth file creation properties

my $auth_mask = 0600;
my $auth_owner = "root";
my $auth_group = "root";

# Mail file creation properties

my $mail_mask = 0660;

# Mail for real users is owned by the respective user; other mail is
# owned by the virtual mail owner.  You should make this a dedicated
# pseudo-user (a system user that can't actually log in).  Better yet,
# use a separate pseudo-user for each domain.

my $virtual_mail_owner = "vmail";
my $mail_group = "mail";

# End of options.  No need to change anything below this line.

#######################################################################

my $user;
my $is_real_user;
my $passwd;

my $undo_auth_file;
my $undo_mail_file;

# Sanitize environment

delete @ENV{keys %ENV};
$ENV{'PATH'} = '/bin:/usr/bin';

# Handle interrupts gracefully

$SIG{'INT'} = $SIG{'QUIT'} = $SIG{'HUP'} = 'sighandler';

# Select username

if ($#ARGV == 0)
{
        $user = $ARGV[0];
} else
{
        print "Enter a username to add: ";
        chomp($user = <STDIN>);
}

$user =~ /^[[:print:]]{1,40}$/ or &cleanup("Invalid username\n");
$user =~ /^[^\.\/]*$/ or &cleanup("Dangerous username unsupported\n");

# Does the user exist in /etc/passwd?

$is_real_user = getpwnam($user);

# Create auth file for user

print "Creating auth file for ",
        defined($is_real_user) ? "real" : "virtual",
        " user $user...\n";
        
sysopen(AUTH, "$virtual_domain_root_directory/auth/$user",
              O_WRONLY | O_CREAT | O_EXCL, $auth_mask)
        or &cleanup("Can't create ",
                    "$virtual_domain_root_directory/auth/$user: $!\n");

$undo_auth_file = "$virtual_domain_root_directory/auth/$user";

# Set auth file ownership and permissions

my $auth_uid;
my $auth_gid;

$auth_uid = getpwnam($auth_owner);
defined($auth_uid) or &cleanup("Can't get uid for user $auth_owner: $!\n");

$auth_gid = getgrnam($auth_group);
defined($auth_gid) or &cleanup("Can't get gid for group $auth_group: $!\n");

chown $auth_uid, $auth_gid, "$virtual_domain_root_directory/auth/$user" or
        &cleanup("Can't change ownership for ",
            "$virtual_domain_root_directory/auth/$user: $!\n");

chmod $auth_mask, "$virtual_domain_root_directory/auth/$user" or
        &cleanup("Can't change permissions for ",
            "$virtual_domain_root_directory/auth/$user: $!\n");

# Get password

my $passwd_retyped;

system "stty", "-echo";
print "Enter new password for $user: ";
chomp($passwd = <STDIN>);

print "\nRetype new password for $user: ";
chomp($passwd_retyped = <STDIN>);
print "\n";
system "stty", "echo";

$passwd eq $passwd_retyped or &cleanup("Sorry, passwords do not match\n");

# Write auth file

my $enc_passwd;

print "Writing auth file contents for $user...\n";

$enc_passwd =
        Crypt::PasswdMD5::unix_md5_crypt( $passwd, substr(time(), -8) );

print AUTH defined($is_real_user) ? $user : $virtual_mail_owner,
        ":", $enc_passwd, "::\n";

# Close auth file

close(AUTH);

# Create mail file for user

print "Creating mail file for $user...\n";

sysopen(MAIL, "$virtual_domain_root_directory/mail/$user",
              O_RDONLY | O_CREAT | O_EXCL, $mail_mask)
        or &cleanup("Can't create ",
                    "$virtual_domain_root_directory/mail/$user: $!\n");

$undo_mail_file = "$virtual_domain_root_directory/mail/$user";

close(MAIL);

# Set mail file ownership and permissions

my $mail_owner;
my $mail_uid;
my $mail_gid;

$mail_owner = defined($is_real_user) ? $user : $virtual_mail_owner;

$mail_uid = getpwnam($mail_owner);
defined($mail_uid) or &cleanup("Can't get uid for user $mail_owner: $!\n");

$mail_gid = getgrnam($mail_group);
defined($mail_gid) or &cleanup("Can't get gid for group $mail_group: $!\n");

chown $mail_uid, $mail_gid, "$virtual_domain_root_directory/mail/$user" or
        &cleanup("Can't change ownership for ",
            "$virtual_domain_root_directory/mail/$user: $!\n");

chmod $mail_mask, "$virtual_domain_root_directory/mail/$user" or
        &cleanup("Can't change permissions for ",
            "$virtual_domain_root_directory/mail/$user: $!\n");

sub cleanup
{
        print STDERR "@{_}Cleaning up...\n";

        print STDERR "Echo input characters...\n";
        system "stty", "echo";

        if ($undo_auth_file)
        {
                print STDERR "Removing auth file...\n";
                unlink $undo_auth_file or
                        print STDERR "Can't delete $undo_auth_file: $!\n";
        }
        if ($undo_mail_file)
        {
                print STDERR "Removing mail file...\n";
                unlink $undo_mail_file or
                        print STDERR "Can't delete $undo_mail_file: $!\n";
        }

        exit 1;
}

sub sighandler
{
        &cleanup("Caught a SIG@_.\n");
}

