#!/usr/bin/perl -w
# ------------------------------------------------------------
# script:  ldapaddgroup.pl
# Author:  Brad Marshall (bmarshal@pisoftware.com)
# License: GPL
# Date:    20000203
#
# Purpose: Adds a group to LDAP
#
# Copyright (c) 2000 Plugged In Software Pty Ltd.  All rights reserved.

# TODO
# x Check group name ( =< 8 chars, all alpha, etc)
# x Check group number is not out of range (ie negative, > 65536, etc)
# x Handle ^C's gracefully
# x Get manager's password & bind as manager
#   Check if automatically allocated group number is ok
#   Display information, ask if ok before adding
#   Add a --force option ?

# Modules
use strict;
use Net::LDAP;
use Getopt::Std;
use Term::ReadKey;

use vars qw($opt_g);

# Variables
my($gidnumber);
my($gidref);
my($cn);
my($dn);
my($result);
my($entry);

my($root) = "dc=pisoftware,dc=com";
my($host) = "ldap.staff.plugged.com.au";
my(@objectclass) = [ 'posixGroup', 'top' ];
my $exception = 0;

# Signal catching
$SIG{'INT'} = $SIG{'QUIT'} = $SIG{'HUP'} = sub { $exception=1; };

# Get options
getopts('g:');

# groupadd [-g gid [-o]] [-r] [-f] group

if (! $ARGV[0]) {
    print "$0: $0 [-g gidnumber] groupname\n";
    exit 1;
}

# Get the group name
if ($ARGV[0]) {
	$cn = $ARGV[0];
	if ($cn !~ /[a-z]{2,8}/) {
		die "Sorry, group name must consist solely of letters and be between 2 and 8 characters.";
	}
} else {
	die "Sorry, you must specify a group name";
}
# If we have a -g option, take that as the gid number
if ($opt_g) {
	$gidnumber = $opt_g;
} else {
	# Find the next available gid
	$gidnumber = &lastgid;
	print "\$gidnumber = $gidnumber\n";
}

# Check the gids for validity
&checkgid;

#dn: cn=support,ou=Group,dc=pisoftware,dc=com
#objectclass: posixGroup
#objectclass: top
#cn: support
#gidnumber: 140

# Get the dn for the group
$dn = "cn=$cn,ou=Group,$root";

my($ldap) = Net::LDAP->new($host) or die "Can't bind to ldap: $!\n";

my($manager) = "cn=Manager,$root";
print "$manager\n";

print "Please enter LDAP Managers password: ";

ReadMode 'noecho';
my $password = ReadLine 0;
chomp $password;
ReadMode 'normal';
print "\n";

# Bind to ldap as Manager
$ldap->bind(
            dn       => $manager,
            password => $password,
        );

if ($exception) {
	die "Caught a signal";
} else {
	# Actually add the group
	$result = $ldap->add (
        dn   => $dn,
        attr => [ 'cn'          => $cn,
                  'objectClass' => @objectclass,
                  'gidnumber'   => $gidnumber,
                ]
	);
	$result->code && warn "failed to add entry: ", $result->error ;
};


# Subroutines

my(%results);

sub lastgid {
	# Find the last gid number

	my($entry);
	my(@gids);
	my($lastgid);

	my($ldap) = Net::LDAP->new($host) or die "Can't bind to ldap: $!\n";

	$ldap->bind;

	# Search for all the groups
	my($mesg) = $ldap->search( base => $root,
                               filter => '(objectclass=posixGroup)'
                             );

	$mesg->code && die $mesg->error;

	# Loop thru all the groups
	foreach $entry ($mesg->entries) {
		# Grab the cn and gidnumber
        my($tmpcn) = $entry->get('cn');
        my($tmpnum) = $entry->get('gidnumber');
		# Build up a hash of cn and gid
        $results{$tmpcn} = $tmpnum;
    }

	# Sort the groups by gid
	@gids = sort bygroup keys %results;
	# Then get the last one, and add one to it.
	$lastgid = $results{$gids[$#gids]} + 1;

	return $lastgid;
}

sub bygroup {
        $results{$a} <=> $results{$b}
}

sub checkgid {
	# Checks the gid for validity

	my($ldap) = Net::LDAP->new($host) or die "Can't bind to ldap: $!\n";

	$ldap->bind;

	# Search for all the groups
	my($mesg) = $ldap->search( base => $root,
                               filter => '(objectclass=posixGroup)'
                             );

	$mesg->code && die $mesg->error;

	# Loop thru all the groups
	foreach $entry ($mesg->entries) {
		# Grab the cn and gidnumbers
		my($tmpcn) = $entry->get('cn')->[0];
		my($tmpnum) = $entry->get('gidnumber')->[0];
		# If this group name already exists..
		if ($cn eq $tmpcn) {
			die "A group with name $cn already exists";
		}
		# If this group number already exists..
		if ($gidnumber == $tmpnum) {
			die "A group with gid $gidnumber already exists";
		}
	}

	# If the group number is outside a valid range
	# ie, not 0 < $gidnumber > 65535
	if (($gidnumber > 65535) || ($gidnumber < 0)) {
		die "Sorry, gid number must be less than 65535 and greater than 0";
	}
}
