#!/usr/bin/perl -w
# 
# Module:     adduser    
# Date:       04/22/04    
# Version:    1.36    
# Edited by:  teon@teon.org    
# Description:    
#
#   AddUser-NG -- Next Generation of the adduser script.
#    
# License:   
#   
#  This program is free software; you can redistribute it and/or modify   
#  it under the terms of the GNU General Public License Version 2 as   
#  published by the Free Software Foundation.   
#   
#  This program is distributed in the hope that it will be useful,   
#  but WITHOUT ANY WARRANTY; without even the implied warranty of   
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   
#  GNU General Public License for more details.   
#   
#  You should have received a copy of the GNU General Public License   
#  along with this program; if not, write to the Free Software   
#  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,   
#  USA.   
#   
# This file is part of adduser-ng program.   
#   
# Copyright (C) 2003-2004 Bartosz Oler <liar@bzimage.us>   
# Copyright (C) 2003-2004 Robert Olejnik <teon@teon.org>   
# 

# perl modules
use strict;
use Getopt::Mixed qw{nextOption};
use Config::IniFiles;

# AddUser modules
use AddUser::StdLib qw{%ERRNO};
use AddUser::PluginLoader;


# VARIABLES {{{

my $VERSION = "0.1.2";
my $APIVERSION = 0x1;
my %Options;
my %plugins_options;
my %config_keywords;
my $verbose = 0;

# }}}



# GetOptions {{{

Getopt::Mixed::init("help version verbose user-interface=s skip h>help V>version v>verbose u>user-interface S>skip");
while (my ($k, $v) = nextOption()) {
	$Options{$k} = $v;
}
Getopt::Mixed::cleanup();

# print help message and exit

if (exists $Options{'help'}) {
  show_help();
  exit($ERRNO{'OK'});
} elsif (exists $Options{'version'}) {
  show_version();
  exit($ERRNO{'OK'});
}

# for verbose check
$verbose = 1 if (exists $Options{'verbose'});
# make this param available for plugins
$plugins_options{'verbose'} = $verbose;

# }}}



# M A I N {{{
# ===========

if ($> != 0) {
	print STDERR "$0: This program must be run by the super user.\n";
	exit $ERRNO{'NOTROOT'}
}

if (@ARGV < 2) {
	print "Try: '$0 --help' for more information\n";
	exit $ERRNO{'NOGROUPGIVEN'};
}

my $Login = pop(@ARGV);
my $GroupName = pop(@ARGV);

# make login available for plugins
$plugins_options{login} = $Login;
# make group available for plugins
$plugins_options{group} = $GroupName;

# K E Y W O R D S

# config parser will look for these values, if used in a configuration file
$config_keywords{"main.login"} = $Login;
$config_keywords{"main.group"} = $GroupName;

# this is the right place to make config keywords available for plugins
$plugins_options{keywords} = \%config_keywords;

# read the configuration file
my $MainConfig = new Config::IniFiles(-file => "adduser-ng/adduser-ng.conf");

# get groups' names
my %Groups = &get_groups($MainConfig->val('adduser', 'groups_dir'));

# find out, if the selected group exist
if (not exists($Groups{$GroupName})) {
	print "Group '$GroupName' is not defined.\n";
	exit $ERRNO{'NOSUCHGROUP'};
}

# ok, now the real code starts

# load ui (user interface)
my $UI = $Options{'user-interface'} || $MainConfig->val('adduser', 'default_ui');
$UI = load_ui($UI, {argv => \@ARGV},$verbose) or exit($ERRNO{'ERROR'});

# of course, the UI is a connection between the user and the plugins,
# so we have to make UI available to the plugins
$plugins_options{UI} = $UI;

# now, read the group configuration file, and get options for each plugin
my $GroupConfig = new Config::IniFiles(-file => $MainConfig->val('adduser', 'groups_dir')."/".$GroupName);
# make the configuration available for the plugins
$plugins_options{'GroupConfig'} = $GroupConfig;

$plugins_options{'documentation_dir'} = $MainConfig->val('adduser', 'documentation_dir');

# load plugins, UI is now available, so all messages should be displayed by
# UI->display
$UI->display("AddUser-NG verbose", "* Loading AddUser plugins..") if ($verbose);
my $ph = new AddUser::PluginLoader( plugins_dir => "AddUser/plugins", 
                            plugins_options => \%plugins_options, verbose => $verbose);
my @WorkPlugins = $ph->load_workplugins($GroupConfig->Sections()) or exit($ERRNO{'ERROR'});

# R U N   P L U G I N S
my $retcode = &run_plugins(@WorkPlugins);

# we're done... cleanup and exit
&cleanup_ui($UI);

exit $retcode;

# END OF MAIN
# }}}






# F U N C T I O N S {{{
# =====================


# get_groups($dir)
#
# Return a hash with groups defined in $dir.
# 
# {{{

sub get_groups {
	my $dir = shift;

	opendir(DH, $dir) || die "Can't open directory '$dir': $!\n";
	my @groups = grep { ! /^\.\.?$/ } readdir(DH);
	closedir(DH);

	return map { $_ => 1 } @groups;
}
# }}}




# ========================================================================
# PLUGINS functions
# ========================================================================


# run_plugins(%plugins)
#
# Run all loaded plugins.
# 
# {{{

sub run_plugins {
	my @plugins = @_;
	my @run;
	my $err = $ERRNO{'OK'};
        my $plugin;

        $UI->display("AddUser-NG verbose", "* Checking plugins API versions..\n") if ($verbose);
	#
	# Check plugins' version.
	#
	for $plugin (@plugins) {
		if ($plugin->{APIVERSION} != $APIVERSION) {
			$UI->display_error("AddUser-NG ERROR", "Plugin [ $plugin->{NAME} ] has incompatible plugin version [ $plugin->{APIVERSION} ] ! Current AddUser-NG api version is [ $APIVERSION ]\n");
			return $ERRNO{'APIVERSION'};
		} else {
                        $UI->display("AddUser-NG verbose", "Plugin [ $plugin->{NAME} ] API version [ $APIVERSION ] is ok.") if ($verbose);
                }
	}

	#
	# Configure plugins.
	# 
	for $plugin (@plugins) {
		next if $plugin->configure() == $ERRNO{'OK'};
		if (exists $Options{'skip'}) {
			$UI->display_error("AddUser-NG/$plugin->{NAME}", "Configuration failed. Skipping this plugin.\n");
		} else {
			return $ERRNO{'DONE'};
		}
	}

	#
	# Let plugins work.
	# 
	for $plugin (@plugins) {
		push(@run, $plugin);
		if ($plugin->execute() != $ERRNO{'OK'}) {
			#
			# An error occurred during the execution.
			#
			$err = 1;
			last;
		}
	}

	#
	# If something went wrong rollback.
	# 
	if ($err) {
		while (my $plugin = pop(@run)) {
			$plugin->rollback();
		}
	}

	return $err;
}
# }}}



# ========================================================================
# UI functions
# ========================================================================

# load_ui($ui_name, $options, $verbose)
# 
# Load User Interface
#
# Returns handle to the ui
#
# {{{

sub load_ui
{
        my $ui_name = shift;
	my $options = shift;
        my $verbose = shift;
        my $UI_ph;
        my %UI;

        if (not defined $ui_name) {
                print "load_ui: ui_name was not specified.. exiting.\n";
                exit $ERRNO{'ERROR'};
        }

        print "* Loading default UI plugin:\n" if ($verbose);

        $UI_ph = new AddUser::PluginLoader ( plugins_dir => 'AddUser/UI', plugins_options => $options, verbose => $verbose );

        $UI = ($UI_ph->load_workplugins($ui_name))[0];

	if($UI) {
                print "* Initializing UI.. " if ($verbose);
                
                # initialize UI
                $UI->init();
                
                print "done\n" if ($verbose);
                return $UI;
        }

        return undef;
}

# }}}



# cleanup_ui($UI_handle)
# 
# Clean up after the ui
#
# {{{

sub cleanup_ui
{
        $UI = shift;
        $UI->end();
}

#}}}



# ========================================================================
# functions for help messages
# ========================================================================


# strip_tabs($s)
#
# Remove tabs indicating document-here.
#
# Return changed string.
#
# {{{

sub strip_tabs($) {
	$_ = shift;
	my $i = 0;

	my @s = split("");
	$i++ while ($s[$i] eq "\t");
	s/^\t{1,$i}//gm;
	return $_;
}
# }}}



# show_version()
#
# Shows program version
# 
# {{{

sub show_version
{
	print strip_tabs <<"		EOF";
		adduser-ng, version: $VERSION

		Copyright 2003-2004 (C)  Bartosz Oler   <liar\@bzimage.us>
		Copyright 2003-2004 (C)  Robert Olejnik <teon\@teon.org>

		EOF
}

# }}}



# show_help()
#
# Prints help message
#
# {{{

sub show_help
{
        show_version();

        print strip_tabs <<"		EOF";
		Usage: adduser-ng [options] <groupname> <login>

		Options:
		    -u | --user-interface <UI>    Use UI user interface instead of default
		    -h | --help                   Print this help message and exit
		    -V | --version                Print program version and exit
		    -v | --verbose                Print more verobose output
		    -S | --skip                   Skip already executed plugins

		Report bugs to <bugs\@adduser.linux.pl>
		EOF
}

# }}}

# ENF OF FUNCTIONS
# }}}
