package Debian::Adduser::Backend::Account;

# Handle actions in local files.

# Copyright (C) 2006-2008 Davor Ocelic <docelic@mail.inet.hr> (rewrite)
# Copyright (C) 2000 Roland Bauerschmidt <rb@debian.org>
# Most of the functions are adopted from the original adduser
# Copyright (C) 1997, 1998, 1999 Guy Maor <maor@debian.org>
# Copyright (C) 1995 Ted Hajek <tedhajek@boombox.micro.umn.edu>
#                     Ian A. Murdock <imurdock@gnu.ai.mit.edu>

use strict;
use warnings;

require Exporter;
use Digest::MD5;

use Debian::Adduser::Init qw/:gettext :exprs/;
use Debian::Adduser::Common qw/invalidate_nscd checkname :exec :check/;
use Debian::Adduser::Common qw/:homedir user_is_member :reporting :del/;
use Debian::Adduser::Common qw/:find_ids ch_gecos ask_passwd ask_gecos/;
use Debian::Adduser::Config qw/:cfg/;

use File::Find qw//;

our @ISA = qw(Exporter);
our %EXPORT_TAGS = ( 'all' => [ qw( ) ] );
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
our @EXPORT = qw( );

sub new {
	my $class = shift;
	my $self  = {};

	bless $self, $class;
}


###########################################################################
# Add user
#
sub adduser_check {
	WU 'new_user', R 'ARGV0';
    WU 'existing_gid', getgrnam(R 'ingroup_name') if R 'ingroup_name';

	printv q{Going to add Unix user `%s' (UID `%s') ...},
        R('new_user'), R('new_uid') || R 'existing_uid' || 'AUTOSELECT';

	if ( getpwnam R 'new_user') {
		dief q{The Unix user `%s' already exists.}, R 'new_user'
	}

	W 'new_uid', first_avail_uid if !R 'new_uid';
	if (defined(R 'new_uid') and getpwuid(R 'new_uid')) {
		dief q{The UID %d is already in use.}, R 'new_uid'
	}

	W 'new_home', homedir(R 'new_user', R 'ingroup_name') if
		!R 'new_home';
	WU 'new_shell', R 'dshell';

    # Deal with GECOS
    # XXX Somewhere, add Other=policy if --policy specified
    if ( !R 'found_sys_opt' and R 'ask_gecos') {
        if (! R 'new_gecos') {
            W 'new_gecos', (join ',', ask_gecos());
        }
    }

	1
}

sub adduser_exec_check {

		# XXX --gid does not mean --ingroup, so this block is wrong
    #if (defined(R 'new_gid')) {
    #    W 'ingroup_name',  getgrgid(R 'new_gid')
    #      or dief q{System configuration error: } .
    #        q{Unix group not visible through NSS.};
		#
    #} elsif (R 'ingroup_name') {
    if (R 'ingroup_name') {
        if ( not defined (W 'new_gid', getgrnam(R 'ingroup_name'))) {
            dief q{The Unix group `%s' does not exist.}, R 'ingroup_name'
        }

    } else {
        R 'new_group' and R 'new_user' or
            dief q{Unix group supposed to, but wasn't created.}
    }
}

sub adduser_execute {
	local $_;

    invoke
     [ q{Adding new Unix user `%s' (UID `%d') with group `%s' (GID `%s') ...},
         R('new_user'), R('new_uid'), R 'ingroup_name', R 'new_gid'   ],
     [ 'useradd', '-d', R('new_home'), '-g', R('ingroup_name'), '-s',
	 	R 'new_shell', '-u', R('new_uid'), R('new_user')             ],
     # XXX pass -r to userdel ? If we created the home dir?
     [ 'userdel', R('new_user')                                       ],
     { invalidate_nscd => 1, sudo => 1                                };

    create_homedir 1; # copy skeleton data

    $_ = R 'new_user';

    # useradd without -p has left the account disabled (password string is '!')
    if (!R 'found_sys_opt' and R 'ask_passwd' and !R 'new_passwd') {
        printv q{Setting user's Unix password ...};

        # Set user PW
        W 'new_passwd', ask_passwd('new user password');
        my $pw = R 'new_passwd';

        if (R 'literal_password') {
            systemcall 'usermod', '-p', $pw, $_
        } else {
            $pw = '$1$' . Digest::MD5::md5_hex($pw);
            systemcall 'usermod', '-p', $pw, $_;
        }

    } else {
        my $pw = R 'new_passwd';
        if (!$pw and R 'disabled_password' ) { $pw = '*' }
        else { $pw = '!' }

        if (R 'literal_password' or
                (!R 'ask_passwd' and !R 'new_passwd')) {

            systemcall 'usermod', '-p', $pw, $_
        } else {
            $pw = '$1$' . Digest::MD5::md5_hex($pw);
            systemcall 'usermod', '-p', $pw, $_;
        }
    }

    # Adjust GECOS
    if (local $_ = R 'new_gecos' and $_ !~ /^,*$/) {
        ch_gecos($_);
    } else {
        # Figure out how to word it without sounding like problem
        #printd q{Skipping empty GECOS, not setting.};
    }

    # Deal with extra groups
		# XXX Solve at backend level
    #if ( R 'add_extra_groups' and defined(R 'extra_groups') ) {
    #    printf gtx("Adding new Unix user `%s' to extra groups `%s'...\n"),
    #           R('new_user'), R 'extra_groups';

    #    foreach my $newgrp ( split ' ', R 'extra_groups' ) {
    #        if (!defined getgrnam($newgrp)) {
    #            warnf q{The Unix group `%s' does not exist.}, $newgrp;
    #            next;
    #        }

    #        if (user_is_member(R('new_user'), $newgrp)) {
    #            warnv q{The Unix user `%s' is already a member of `%s'.},
    #                R('new_user'), $newgrp;
    #            next;
    #        }

    #        invoke
    #         [ q{Adding Unix user `%s' to group `%s' ...},
    #             R('new_user'), $newgrp                ],
    #         # XXX Why was it done this way? Old code?
    #         #[ 'gpasswd', '-M',
    #         #  join(',', get_group_members($newgrp), R('new_user')), $newgrp ],
    #         [ 'gpasswd', '-a', R('new_user'), $newgrp ],
    #         [ 'gpasswd', '-d', R('new_user'), $newgrp ],
    #         { invalidate_nscd => 1                    };
    #    }
    #}

    # XXX
    #if ($_ = R 'quotauser') {
    #    printf (gtx("Setting quota for user `%s' to values of user `%s' ...\n"), R('new_user'), $_);
    #    systemcall 'edquota', '-p', $_, R('new_user');
    #}

    1
}

*addsysuser_check = \*adduser_check;
*addsysuser_exec_check = \*adduser_exec_check;
*addsysuser_execute = \*adduser_execute;


###########################################################################
# Delete user
#
my (@files, @dirs);
sub deluser_check {
	my $user = WU 'existing_user', R 'ARGV0';

    my @pwent = getpwnam(R 'existing_user');
    WU 'existing_uid', $pwent[2];
    WU 'existing_gid', $pwent[3];

	printv q{Going to delete Unix user `%s' (UID `%s', GID `%s') ...},
        R('existing_user'),
				R('existing_uid') || 'UNKNOWN',
				R('existing_gid') || 'UNKNOWN';

    if (! @pwent) {
        dief q{The Unix user `%s' does not exist.}, R 'existing_user'
    }

    #
    # Here's a big block that deals with removal of all user files
    #
    if (R('remove_home') or R('remove_all_files')) {
      warnf q{Looking for files to backup/remove ...};

      my @mountpoints;
      open(MOUNT, 'mount |') or
	      dief q{fork for `mount' to parse mount points failed: %s.}, $!;
            # exit 4

      while (<MOUNT>) {
	      my @temparray = split;
	      my $fstype = $temparray[4];
	      my $exclude_fstypes = R 'exclude_fstypes';
	      if (defined($exclude_fstypes)) {
	      	next if $fstype =~ /$exclude_fstypes/
	      }
	      push @mountpoints, $temparray[2];
      }

      close MOUNT or
          dief q{Pipe of command `mount' could not be closed: %s.}, $!;

      if ( R 'remove_home' and !R 'remove_all_files' ) {

          if ( -d $pwent[7]) {
              File::Find::find({
                wanted =>
                    \&decide_delete_homefile(\@files, \@dirs, \@mountpoints),
                untaint => 1,
                no_chdir => 1
                },
                $pwent[7]) 
          }

          push(@files, "/var/mail/$user") if -e "/var/mail/$user";

      } else {
          File::Find::find({
            wanted => \&decide_delete_sysfile(\@files, \@dirs, \@pwent),
            untaint => 1,
            no_chdir => 1
            },
            '/');
      }

      print "FINALNO, skidam @dirs\nI @files\n";
      die "\n";
    }

#      if ( R 'backup' ) {
#          warnf q{Backing up files to be removed to `%s' ...}, R 'backup_to';
#
#          my $filesfile = new File::Temp(
#            TEMPLATE => 'deluser.XXXXX',
#            DIR      => '/tmp'
#            );
#          my $filesfilename = $filesfile->filename;
#          my $backup_name = R('backup_to') . "/$user.tar";
#          warnv "Backup name = $backup_name.";
#          print $filesfile join("\n", @files);
#          $filesfile->close();
#
#          my $tar = which('tar');
#          # XXX work on this
#          #systemcall($tar, "-cf", $backup_name, "--files-from", $filesfilename);
#          chmod 600, $backup_name;
#
#          # XXX turn rootid into user configurable
#          my $rootid = 0;
#          chown $rootid, $rootid, $backup_name;
#
#          unlink($filesfilename);
#          my $bzip2 = which('bzip2', 1);
#          my $gzip = which('gzip', 1);
#
#          # XXX work on this
#          #if($bzip2) {
#          #    systemcall($bzip2, $backup_name);
#          #} elsif($gzip) {
#          #    systemcall($gzip, "--best", $backup_name);
#          #}
#      }
#
#      if ( @files or @dirs ) {
#          warnf q{Removing files ...};
#          unlink(@files) if @files;
#          for (reverse sort(@dirs)) { rmdir $_; }
#      }
#    }
#
#
#    if ( R 'backup' ) {
#        printv q{Backing up files to be removed to `%s'...},
#            R 'backup_to';
#
#        my $filesfile = new File::Temp(
#            TEMPLATE => 'deluser.XXXXX',
#            DIR      => '/tmp'
#        );
#        my $filesfilename = $filesfile->filename;
#        my $backup_name = R('backup_to') . "/$user.tar";
#
#        #print "backup_name = $backup_name";
#
#        print $filesfile join("\n", @files);
#        $filesfile->close();
#
#        my $tar = which('tar');
#        systemcall($tar, 'cf', $backup_name, '--files-from', $filesfilename);
#        chmod 600, $backup_name;
#
#        # XXX turn into configurable
#        my $rootid = 0;
#        chown $rootid, $rootid, $backup_name;
#        unlink($filesfilename);
#
#        my $bzip2 = which('bzip2', 0);
#        my $gzip = which('gzip', 0);
#        if ( $bzip2 ) {
#            systemcall($bzip2, $backup_name);
#        } elsif ( $gzip ) {
#            #systemcall($gzip, '--best', $backup_name);
#            systemcall($gzip, $backup_name);
#        }
#    }
#
#    if ( @files or @dirs ) {
#        warnv q{Removing files ...};
#        unlink @files if @files;
#        for ( reverse sort(@dirs)) { rmdir $_ }
#    }
#}
#
#if (system("crontab -l $user >/dev/null 2>&1") == 0) {
## crontab -l returns 1 if there is no crontab
#    my $crontab = &which('crontab');
#    &systemcall($crontab, "-r", $user);
#    s_print (gtx("Removing crontab ...\n"));
#}
#
#s_printf (gtx("Removing user `%s' ...\n"),$user);
#my $userdel = &which('userdel');
#&systemcall($userdel, $user);
#&invalidate_nscd();
#
#systemcall('/usr/local/sbin/deluser.local', $user, $pw_uid,
#        $pw_gid, $pw_homedir) if (-x "/usr/local/sbin/deluser.local");
#
#s_print (gtx("Done.\n"));
#exit 0;
#
    1
}

sub deluser_exec_check {
}

sub deluser_execute {
	local $_;

    invoke
     [ q{Deleting existing Unix user `%s' (UID `%d', GID `%s') ...},
         R('existing_user'), R('existing_uid'), R('existing_gid')    ],
     [ 'userdel', R('existing_user')                                  ],
		 undef, # XXX undo action is hard for this
     { invalidate_nscd => 1, sudo => 1                                };

    1
}

*delsysuser_check = \*deluser_check;
*delsysuser_exec_check = \*deluser_exec_check;
*delsysuser_execute = \*deluser_execute;

# Local Variables:
# mode:cperl
# End:
# vim:set ai et ts=4 sts=4 sw=4 tw=0:
1

