#!/usr/bin/perl -T

use strict;
use warnings;

use AppConfig;
use Net::Remctl;
use Net::Remctl::Backend;
use Stanford::Directory;
use Stanford::WorkgroupXML;
use Stanford::WorkgroupXML::Workgroup;
use Try::Tiny;


# Everything is configured via a flat file, but we need to know where it is!
my $CONFIG_PATH = '/etc/tools-cgi/sharedemail-migrate';


# Load in our configuraion
my $config = AppConfig->new({
        CASE     => 1,
        PEDANTIC => 1,
        GLOBAL   => {
            ARGCOUNT => 1,
        },
    },
    'krb5_keytab',
    'krb5_service',
    'krb5_instance',
    'ldap_server',
    'workgroup_key',
    'workgroup_cert',
    'workgroup_ca_dir',
    'workgroup_baseurl',
);
if (!$config->file($CONFIG_PATH)) {
    print "Configuration file $CONFIG_PATH could not be loaded\n";
    exit 1;
}

# dir is our connection to LDAP, which pretty much every command will need.
my $dir = Stanford::Directory->new();
$dir->set(
    ldap_server => $config->get('ldap_server'),
    mechanism   => 'GSSAPI',
    keytab      => $config->get('krb5_keytab'),
    service     => $config->get('krb5_service'),
    instance    => $config->get('krb5_instance'),
);

# Set up our remctl commands, and run the handler!
my %commands = (
    'workgroup' => {
        args_min => 1,
        args_max => 1,
        code     => \&cmd_workgroup,
    },
    'database' => {
        args_min => 1,
        args_max => 1,
        code     => \&cmd_database,
    },
);
$| = 1;
exit Net::Remctl::Backend->new({
    commands => \%commands,
})->run();

# Next up we've got the code for our invidivual commands.

# cmd_workgroup: Create and populate a workgroup, using information from the 
# directory and from the IMAP server.
# From the directory we pull the list of admins, (TBD) the description, and
# (TBD) the OrgID.  From the IMAP server we pull the ACLs for the mailbox.
sub cmd_workgroup {
    my ($list_name) = @_;
    my $workgroup_name = 'office365:' . $list_name;

    print "Searching for $list_name in LDAP\n";
    $dir->set(
        basedn => 'cn=accounts,dc=stanford,dc=edu',
        scope  => 'sub',
    );
    my @entries = $dir->ldap_query("uid=$list_name",
        'uid', 'suEmailAdmin', 'description', 'owner',
    );
    if (scalar(@entries) == 0) {
        if ($dir->error) {
            print '... Error!', $dir->error, "\n";
        }
        else {
            print "... Error!  No entries were found in LDAP.\n";
        }
        return 1;
    }
    if (scalar(@entries) > 1) {
        print '... Error!  There were ', scalar(@entries),
              ' entries returned, but we only want 1.';
        return 1;
    }
    my $entry = $entries[0];
    print "... Entry Found!\n";

    # Create the workgroup, add admins, and set properties
    print "Creating workgroup $workgroup_name ...\n";
    my $workgroup = Stanford::WorkgroupXML::Workgroup->create(
        $workgroup_name, {
            url_base => $config->get('workgroup_baseurl'),
            key      => $config->get('workgroup_key'),
            cert     => $config->get('workgroup_cert'),
            ca_dir   => $config->get('workgroup_ca_dir'),
        },
    );

    # Some accounts don't have admins, so don't let that stop us!
    if (!defined($entry->suEmailAdmin)) {
        print "WARNING: $list_name has no administrators!\n";
    }
    else {
        print 'Adding ', scalar($entry->suEmailAdmin), " administrators...\n";
    }
    $workgroup->administrators()->add_workgroup('workgroup:office365-owners');
    foreach my $admin ($entry->suEmailAdmin) {
        $workgroup->administrators()->add_member($admin);
    }

    # TODO: Pull the description from LDAP
    print "Setting other workgroup attributes...\n";
    $workgroup->privgroup(1);
    $workgroup->reusable(0);
    $workgroup->description(
      "Read/write access for the shared mailbox ${list_name}\@stanford.edu\n"
    );
    print "... Done.\n";

    # Get the IMAP ACLs
    # TODO: This is an annoying hack, because we're working with two different
    # principals right now.
    $ENV{KRB5CCNAME} = '/var/run/tools-mail.tkt';
    print "Searching for IMAP ACLs for group.${list_name}\n";
    my $acl = remctl('pobox04.stanford.edu', 0, '', 'cyrus', 'listacl',
                     "group.${list_name}");
    if ($acl->error) {
        print 'Error: ', $acl->error, "\n";
        return 1;
    }
    if ($acl->status != 0) {
        print 'Error: Remctl exited with non-zero code ', $acl->status, "\n";
        return 1;
    }

    # For each ACL, add the SUNetID to the workgroup.
    # We skip people who do not have an 'r' (read), 'w' (write), or 'a' (admin)
    # ACL on the IMAP server.
    foreach my $candidate (split(/^/m, $acl->stdout)) {
        chomp $candidate;
        my ($username, $permissions) = split(/ /, $candidate);
        if ($permissions !~ m/a|r|w/) {
            print "   Skipping '$username', who only has '$permissions' access\n";
            next;
        }
        print "   Adding SUNetID '$username' to workgroup...\n";
        try {
            $workgroup->members()->add_member($username);
        } catch {
            print "      Error adding $username to the workgroup\n";
        };
    }
    print "... Done.\n";

    print "Workgroup setup for $list_name is complete!\n";
    return 0;
}

# cmd_database: Update the Shared Email database with information on the 
# migrated account.  We use LDAP for (TBD) the description, the list of admins,
# and (TBD) the OrgID.
sub cmd_database {
    my ($list_name) = @_;

    print "Not implemented yet!\n";
    return 1;
}
