#!/pkg/perl/5.8/5.6.1/bin/perl

# tel - AD Contact Lookup tool
# adam.prato @ gmail.com

use Net::LDAP;
use Getopt::Std;
use Data::Dumper;
use strict;

my ($binddn, $bindpass, $ldap, $mesg, $search, $acct, $filter, $base);
my ($output, $outfh, $stdout, $me, $ldapbound, $limit, $dnfile, $dcc);

my $delim = "|";

my @defatts = ('givenName','sn','cn','title','description','department','streetAddress','physicalDeliveryOfficeName','l','st','postalCode','c','telephoneNumber','mobile','pager','mail','sAMAccountName','mailnickname');

my $defattr = 'sAMAccountName';

my %o;

if (defined($ENV{'TELDN'})) {
    $dnfile = $ENV{'TELDN'};
} else {
    $dnfile = $ENV{'HOME'} . "/.addn";
}

### Replace the following with your site specific information

# The binddn can be stored in a file ~/.addn (make sure the permisisons on this
# file is protected) in the format - DN:PASSWORD
$binddn  = "CN=USER,OU=Users,OU=New York,OU=_USA,DC=DOMAIN,DC=DOM";

# This is the base to search for ldap entries
$base = "dc=DOMAIN,dc=COM";
# This should be the domain controller 
$dcc = "DCHOSTNAME";


sub Usage {
    ($me = $0 ) =~ s,^.*/,,;
    print "Usage: $me [options] pattern | -s pattern | -f filter | -u accountname\n" .
        "\t-s pattern     search $defattr for 'pattern'\n" .
        "\t-f filter      search for filter\n" .
        "\t-u username    match $defattr for 'username'\n" .
        "\t-r number      reverse lookup\n" . 
        "Options:\n" .
        "\t-a attributes  comma separated list of attributes to dump\n" .
	"\t-A attribute   set the default attribute for searching\n".
        "\t-b base        base dn for ldap search\n" .
        "\t-B binddn      base dn for ldap search\n" .
        "\t-c             output in csv\n" .
	"\t-C             set default attribute to 'cn'\n" .
        "\t-d delimiter   delimiter for csv output\n" .
        "\t-D dnfile      path to file with dn information\n" .
        "\t-l limit       ldap sizelimit\n" .
        "\t-o filename    output to file\n" .
        "\t-P password    password for bind to AD\n" . 
        "\t-v             dump all attributes of matching search\n" .
        "" ;
    exit(0);
}

$stdout = *STDOUT;
$output = $stdout;

if (@ARGV < 1) {
    Usage;
} elsif ((@ARGV == 1) && ($ARGV[0] ne "-h")) {
    $filter = "($defattr=*" . $ARGV[0] . "*)";
} else {

    getopts("cCvxA:a:B:b:d:D:f:h:l:o:P:r:s:u:v:x:",\%o);

    if (!(defined($o{s}) | defined($o{u}) | defined($o{f}) | defined($o{r}))) {
            Usage;
    }

    if (defined($o{h})) {
        Usage;
    }

    if (defined($o{s}))  {
        $acct = "*$o{s}*";
    }

    if (defined($o{u}))  {
        $acct = $o{u};
    }

    if (defined($o{C}))  {
        $defattr = "cn";
    }

    if (defined($o{f})) {
        $filter = $o{f};
    } else {
        $filter = "($defattr=$acct)";
    }

    if (defined($o{r})) {
        $filter = "(|(telephoneNumber=*$o{r}*)(mobile=*$o{r}*)(pager=*$o{r}*))";
    }

    if (defined($o{B})) {
        if (!defined $o{P}) {
            die "Must provide -p password with -B binddn\n";
        }
        $binddn   = $o{B};
        $bindpass = $o{P};
        $dnfile = "";
        
    }

    if (defined($o{D})) {
        $dnfile = $o{D};
    }

    if (defined($o{P})) {
        $bindpass = $o{P};
    } else {
        $bindpass = '';
    }

    if ($bindpass != "") {
        print STDERR "Use -p to set an AD password (bindpass), or set one in the script\n";
        exit(1);
    }

    if (defined($o{x})) {
        push @defatts, ('extensionAttribute1','extensionAttribute3');
    }

    if (defined($o{o})) {
        if (!open(OUTFH, "> $o{o}")) {
            print  STDERR"Couldn't open file ($o{o}) for writing: $!\n";
            exit(1);
        } else {
            print  STDERR"Will write results to $o{o}\n";
            $output = *OUTFH;
        }
    }

    if (defined($o{a})) {
        @defatts = split(/\,/,$o{a});
    }

    if (defined($o{d})) { 
        $delim = $o{d};
    }
    if (defined($o{b})) {
        $base = $o{b};
    }
    if (defined($o{l})) {
        $limit = $o{l};
    }

}

sub cleanup {
    print STDERR "\nCleaning up...\n";
    if ($ldapbound > 0) {
        $mesg = $ldap->unbind;
        print STDERR "Unbinding the LDAP connection...\n";
        die $mesg->error;
    }
}

# 
if ( -f $dnfile) {
    if (open(TELPASS, $dnfile)) {
        my $line = <TELPASS>;
        close(TELPASS);
        if ($line =~ /(^[^:]*):(.*)/) {
            if ($1 !~ /^$/) {
                $binddn = $1;
            }
            if ($2 !~ /^$/) {
                $bindpass = $2;
            }
            if ($binddn !~ /DC\=/i) {
                $binddn = "CN=" . $binddn . 
                          ",OU=Users,OU=New York,OU=_USA,DC=DOMAIN,DC=COM";
            }
        }
    } 
} 


# /* main() */
$SIG{'INT'} = \&cleanup;
$SIG{'QUIT'} = \&cleanup;

$|++;

$ldap = Net::LDAP->new($dcc, version => 3)
    or die "Could not connect to the AD: $!\n" .
           "\n\tusing DN: $binddn\n";

$mesg = $ldap->bind($binddn, password => $bindpass);
#$mesg = $ldap->bind;

if ($mesg->code) {
    die "Could not bind to the AD : " . $mesg->error . 
        "\n\tUsing DN [$binddn]\n";
} else {
    print STDERR "[Bound ldap using dn: $binddn]\n\n";
    $ldapbound++
}

$search = $ldap->search ( base      => $base,
                          filter    => $filter,
			  sizelimit => 0,
			  timelimit => 0
			); 

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

if ($search->count < 1) {
    print STDERR "AD lookup (filter = $filter, base = $base) failed (" . $search->count . ")\n";
} else {
    print STDERR "Results (" . $search->count . ") for filter : $filter, base = $base :\n";
}


my $h = $search->as_struct;
my @e = $search->entries;


#foreach my $ent (sort keys(%{$h})) {
#   my $line;
#    my @atts;
#
#    if (defined($o{d})) {
#        @atts = sort keys(%{$h->{$ent}});
#    } else {
#        @atts = @defatts;
#    }
#
#    if (!defined($o{c})) {
#        print "-" x 60 . "\n";
#        print "[$ent]\n";
#    }
#
#    foreach my $att (@atts) { 
#        if (defined($o{c})) {
#            $line .= $att . $delim ;
#        } else {
#            $att =~ tr/A-Z/a-z/;
#            $line .= sprintf ("%31s:", $att);
#            foreach my $attderef (@{$h->{$ent}{$att}}) {
#                $line .= " $attderef"
#            }
#        }
#        $line .= "\n";
#    }
#    print $output $line ;
#}
#$mesg = $ldap->unbind ;
#exit;

if (defined($o{c}) && !defined($o{v})) {
    print $output join($delim, @defatts) . "\n";
}

foreach my $entry ($search->entries) { 
    my $line;
    my @atts;
    my @csv;

    if (defined($o{v})) {
        @atts = sort $entry->attributes;
    } else {
        @atts = @defatts;
    }

    if (!defined($o{c})) {
        print $output "-=" x 39 . "\n";
        print $output "[" . $entry->dn . "]\n";
    }

    foreach my $att (@atts) {
        if (defined($o{c})) {
            push @csv, $entry->get_value($att);
        } else {
            $line .= sprintf ("%31s: %s\n" , $att, $entry->get_value($att));
        }
    }

    if (defined($o{c})) {
        $line = join($delim, @csv);
    }

    print $output $line . "\n";
}

$mesg = $ldap->unbind ;

print STDERR "\n";
# /* end main() */
