#!/s/std/bin/perl

#
# User directory information provider based on PaperCut sambausers script
#

use strict 'vars';
use REST::Client;
use JSON;
use Net::LDAP;
use Net::LDAP::Control::Paged;
use Net::LDAP::Constant qw( LDAP_CONTROL_PAGED );

use Data::Dumper;

#
# Global variables
#
our $resttoken = "D53pGGhIiyS6rKTylFwvlqksoWTrKrKc9lKkKdUoDk8";
our %restheader = (
    'X-API-Key' => $resttoken,
);

#our $resturl = "https://restproxy.cs.wisc.edu:8001/api/";

our $restclient = REST::Client->new();
$restclient->setHost('https://restproxy.cs.wisc.edu:8001');

my $cafile = "/etc/csl/openssl/cacert.pem";
#my $bindpw = "LSKgl#a3\$l\$wO^oK";
#my $binddn = "uid=ldapquery,ou=People,dc=cs,dc=wisc,dc=edu";
my $binddn = "cn=admin,dc=cs,dc=wisc,dc=edu";
my $bindpw = "wihos2";
our $ldap = Net::LDAP->new("ldaps://ldap-c.cs.wisc.edu", 
                            version => 3,
                            verify => 'require',
                            cafile => $cafile,
                            timeout => 120) 
                        or die "Can't open LDAP object\n";
my $mesg = $ldap->bind( $binddn, password => $bindpw );
our @userresults;

#print "Calling Main()\n";
Main();
#foreach (@userresults) {
#    print $_;
#}


sub
Main {

    #my $lookup;
    if (@ARGV < 2) {
        die("Error: Invalid Command\n" . 
        "Usage: options-string command [arguments...]\n" .
        "  options-string - unused (can pass in dummy argument)\n" .
        "  command:\n" .
        "    is-valid - does net command actually work\n" .
        "    all-users\n" .
        "    all-groups\n" .
        "    group-member-names group\n" .
        "    group-members group\n" .
        "    is-user-in-group group user\n" .
        "    get-user-details\n"
        );
    }

    #open(FH, '>', "/tmp/pcut.$$") or die "can't open log file\n";
    #print FH Dumper(@ARGV);
    #print Dumper(@ARGV);

    if ($ARGV[1] eq "is-valid") {
        IsValid();
        exit;
    }

    #my $netid   = $ARGV[2];
    #my $cslogin = NetidToLogin($netid);

    # $lookup = "/api/people/by-netid/" . $ARGV[2] . "?apikey=" . $resttoken;
    # #$lookup = "/api/uds/xwalk/by-netid/" . $ARGV[2] . "?apikey=" . $resttoken;
    # $restclient->GET($lookup);
    # my $response = $restclient->responseContent();

    # # can check if person is known to CSDB
    # my $json_result = decode_json $response;
    # my $cslogin;
    # print Dumper($json_result);
    # if ($json_result->{'status'} != 404) {
    #     $cslogin = $json_result->{'login'};
    # }

    # print "Netid: $Username\n";
    # print "CS username: $cslogin\n";

    #print "Main: Checking $ARGV[1]\n";

    if ($ARGV[1] eq "all-users") {

        AllUsers();

    } elsif ($ARGV[1] eq "all-groups") {

        AllGroups();

    } elsif ($ARGV[1] eq "group-member-names") {

        if ($ARGV[2] =~ /^\s*$/) {
            die ("Error: group-member-names needs an argument\n");
        }
        GroupMemberNames($ARGV[2]);

    } elsif ($ARGV[1] eq "group-members") {
        if ($ARGV[2] =~ /^\s*$/) {
            die ("Error: group-members needs an argument\n");
        }
        #print "Main: calling GroupMembers()\n";
        #print "group name: $ARGV[2]\n";
        GroupMembers($ARGV[2]);

    } elsif ($ARGV[1] eq "is-user-in-group") {

        if ($ARGV[2] =~ /^\s*$/ || $ARGV[3] =~ /^\s*$/) {
            die ("Error: Missing arguments for is-user-in-group\n");
        }
        IsUserInGroup($ARGV[2], $ARGV[3]);

    } elsif ($ARGV[1] eq "get-user-details") {
        PrintUser($ARGV[2]);

    } else {

       die("Error: Unknown Command\n");

    }

    #print Dumper(@userresults);
    #close(FH);
}


sub
IsValid {

    my $out;

    # maybe consideer adding a check that the REST proxy is answering before continuing

    # Check that Samba net command exists.
    #if ($NetCmd =~ /^\s*$/) {
    #    OutputBoolean(0);
    #}

    # Check we can read the version (ie. command outputs something).
    #$out = `$NetCmd --version`;
    #if ($out =~ /^\s*$/) {
    #    OutputBoolean(0);
    #}

    OutputBoolean(1);
}


sub
AllUsers {

    print "AllUsers() method not implemented\n";
}


sub
AllGroups {

    my $entry;
    my $gid;
    my $group;
    my $search;
    my $page;
    my $cookie;

    #while (1) {
        $search = $ldap->search( base => "ou=Group,dc=cs,dc=wisc,dc=edu",
                                filter => "objectClass=groupOfNames",
                                );
      #                          control => [$page],  

        #print Dumper($search);

        foreach $entry ($search->entries()) 
        #while ($entry = $search->pop_entry()) 
        {
            #print Dumper($entry);
            $group = $entry->get_value('cn');
            $gid = $entry->get_value('gidNumber');
            if ($gid >= 800 && $gid < 900) {
                # group match, valid group within gid range we are looking for
                print "$group\n";
            }
        }

    #     my ($resp) = $mesg->control(LDAP_CONTROL_PAGED) or last;
    #     $cookie = $resp->cookie or last;
    #     # Paging Control
    #     $page->cookie($cookie);
    # }

    # if ($cookie) {
    #     print "abnormal exit\n";
    #     # Abnormal exit, so let the server know we do not want any more
    #     $page->cookie($cookie);
    #     $page->size(0);
    #     $ldap->search(control => [$page]);
    # }

    return 0;

}


sub
GroupMemberNames {

    my ($groupname) = @_;
    #print "GroupMemberNames: getting membership for $groupname\n";
    my %members = GetGroupMembers($groupname);
    my $member;
    foreach (keys(%members)) {
        print "$members{$_}\n";
    }
}


sub
GroupMembers {

    my ($groupname) =@_;

    #print "GroupMembers entered (group: $groupname)\n" ;
    my %members = GetGroupMembers($groupname);
    #my $member;
    
    #print Dumper(sort(keys(%members)));
    #print Dumper(%members);
    foreach  (keys(%members)) {
        #print "Printing data for user " . %members{$_} . "\n";
        #print "GroupMembers: getting data for member %members{$_}\n";
        PrintUser($members{$_});
    }
}


sub
IsUserInGroup {
    my ($groupname, $username) = @_;

    my %members = GetGroupMembers($groupname);

    my $member;
    foreach $member (sort(keys(%members))) {
        if ($username eq $member) {
            # user found
            OutputBoolean(1);
            return;
        }
    }
    # user not found
    OutputBoolean(0);
}


sub
GetGroupMembers {

    my ($groupname) = @_;
    my %members;
    my $entry;
    my $mesg = $ldap->bind ("");
    my $gid;
    my $groupmembers;
    my $member;
    my $search;

    #print "GetGroupMembers: calling LDAP search for group $groupname\n";
    $search = $ldap->search( base => "ou=Group,dc=cs,dc=wisc,dc=edu",
                            filter => "cn=$groupname",
                            attrs => ['gidNumber', 'memberUid'],
                            );
    
    #print Dumper($search);

    #print "GetGroupMembers: checking entries\n";
    foreach $entry ($search->entries) {
        $gid = $entry->get_value('gidNumber');
        $groupmembers = $entry->get_value('memberUid', asref => 1);
        # process groups 800-899, ignore others
        if ( $gid >= 800 && $gid < 900 ) {
            foreach (@$groupmembers) {
                # store group members as NetID
                if ( !$members{$_}) {
                    #print "adding member $_\n";
                    $member = LoginToNetid($_);
                    if ($member ne '') {
                        $member = $member . "_wisc_edu";
                        $members{$_} = $member;
                    }
                } else {
                    # skip duplicate entries if they exist
                    # print "Duplicate user: $_";
                }
            }
        }
    }

    #print Dumper(sort(keys(%members)));

    # sort the output and return
    return %members;
}


sub
IsValidGroup {
    my ($groupname) = @_;
    my $search;
    my $entry;
    my $gid;

    if ($groupname =~ /\$$/) {
        return 0;
    } elsif ($groupname =~ /^none$/i) {
        return 0;
    } elsif ($groupname =~ /^__vmware__$/i) {
        return 0;
    }

    $search = $ldap->search( base => "ou=Group,dc=cs,dc=wisc,dc=edu",
                            filter => "cn=$groupname",
                            );

    if ($search->count() > 0 ) {
        foreach $entry ($search->entries()) {
            if ($entry->get_value('cn') eq $groupname)  {
                $gid = $entry->get_value('gidNumber');
                if ($gid >= 800 && $gid < 900) {
                    # group match, valid group within gid range we are looking for
                    return 0;
                }
            }
        }
    }

    return 1;
}

sub
IsGroup {
    my ($groupname) = @_;
    my $entry;
    my $gid;
    my $search;

    $search = $ldap->search( base => "ou=Group,dc=cs,dc=wisc,dc=edu",
                            filter => "cn=$groupname",
                            );

    if ($search->count() > 0 ) {
        foreach $entry ($search->entries()) {
            if ($entry->get_value('cn') eq $groupname)  {
                $gid = $entry->get_value('gidNumber');
                if ($gid >= 800 && $gid < 900) {
                    # group match, valid group within gid range we are looking for
                    return 0;
                }
            }
        }
    }
    
    return 1;
}



sub
OutputBoolean {
    my ($b) = @_;
    if ($b) {
        print "Y\n";
    } else {
        print "N\n";
    }
}


sub
NetidToLogin {
    my ($netid) = @_;
    my $lookup;

    #$lookup = "/api/people/by-netid/" . $netid . "?apikey=" . $resttoken;
    #$restclient->GET($lookup);
    $lookup = "/api/people/by-netid/" . $netid ;
    $restclient->GET($lookup, \%restheader);

    my $response = $restclient->responseContent();

    # can check if person is known to CSDB
    my $json_result = decode_json $response;
    my $cslogin = "";
    if ($json_result->{'status'} != 404) {
        $cslogin = $json_result->{'login'};
    }
    return $cslogin;
}

sub
LoginToNetid {
    my ($cslogin) = @_;

    #print "LoginToNetID: checking user $cslogin\n";
    #my $lookup = "/api/people/" . $cslogin . "?apikey=" . $resttoken;
    #$restclient->GET($lookup);
    my $lookup = "/api/people/" . $cslogin;
    $restclient->GET($lookup, \%restheader);
    my $response = $restclient->responseContent();

    # can check if person is known to CSDB
    my $json_result = decode_json $response;
    my $netid = "";
    if ($json_result->{'status'} != 404) {
        $netid = lc($json_result->{'netid'});
    }
    return $netid;
}

sub
PrintUser {
    # print user data as we know it
    my ($username) = @_;
    my $netid;
    my $fullname = "";
    my $email;
    my $department = "";
    my $office = "";
    my $primary_number;
    my $other_email = "";
    my $secondary_number;
    my $alias = "";
    my $homedir = "";
    my $pin = "";
    my $login;
    my $firstname;
    my $lastname;
    my $result;

    if (length($username) < 1) { 
        $username = <STDIN>;
        chomp($username);
    }

    if ($username =~ /_wisc_edu$/) {
        $netid = $username;
        $netid =~ s/_wisc_edu$//;

        #my $lookup = "/api/uds/xwalk/by-netid/" . $netid . "?apikey=" . %resttoken;
        #$restclient->GET($lookup);
        #my $lookup = "/api/uds/xwalk/by-netid/" . $netid;
        # above query is passed to DoIT, and their system complains when it gets hit hard
        my $lookup = "/api/cached/xwalk/by-netid/" . $netid;
        $restclient->GET($lookup, \%restheader);
        my $response = $restclient->responseContent();
        my $json_result = decode_json $response;

        if ($json_result->{'status'} != 404) {
            $secondary_number = $json_result->{'detail'}->{'PROXIMITY_ID'};
            $primary_number = $json_result->{'detail'}->{'PHOTO_ID'};
        } else {
            # invalid NetID...we should't get here
            return 1;
        }

        #$lookup = "/api/people/by-netid/" . $netid . "?apikey=" . %resttoken;
        #$restclient->GET($lookup);
        $lookup = "/api/people/by-netid/" . $netid;
        $restclient->GET($lookup, \%restheader);
        $response = $restclient->responseContent();
        $json_result = decode_json $response;
        if ($json_result->{'status'} == 404) {
            # not a CS person
            $email = $netid . "\@wisc.edu";
        } else {
            # this is a CS/stat person
            $login = $json_result->{'login'};
            $alias = $login;
            $firstname = $json_result->{'firstname'}; 
            $lastname = $json_result->{'lastname'};
            if ($json_result->{'statperson'} eq 1 || $json_result->{'stat_student'} == 1) {
                # stat person
                $email = $login . "\@stat.wisc.edu";
                $department = "Statistics";
            } else {
                # CS person
                $email = $login . "\@cs.wisc.edu";
                $department = "Computer Sciences";
            }
            $other_email = $netid . "\@wisc.edu";
            if ($firstname eq "") {
                $fullname = $lastname;
            } else {
                $fullname = $firstname . " " . $lastname;
            }
            $homedir = "/afs/cs.wisc.edu/u/" . substr($login, 0, 1) . "/" . substr($login, 1, 1) . "/" . $login;
        }

        $result =  $username . "\t" . $fullname . "\t" . $email . "\t" . $department . "\t" . $office . "\t". $primary_number . "\t" .$other_email . "\t" . $secondary_number . "\t" . $alias . "\t" . $homedir . "\t" . $pin ."\n";
        push (@userresults, $result);
        print $username . "\t" . $fullname . "\t" . $email . "\t" . $department . "\t" . $office . "\t". $primary_number . "\t" .$other_email . "\t" . $secondary_number . "\t" . $alias . "\t" . $homedir . "\t" . $pin ."\n";
    }

    return 1;

}

1;
