#!@l_prefix@/bin/perl
##
##  dbmail-folders -- DBMail Folders Creation Utility 
##  Copyright (c) 2007-2008 Ralf S. Engelschall <rse@engelschall.com>
##
##  See also:
##  http://www.dbmail.org/dokuwiki/doku.php?id=shared-mbox
##  http://library.mobrien.com/dbmailadministrator/DBMA_ACLs.htm
##

#   language requirements
require 5.008;
use strict;
use warnings;

#   load extensions
use Getopt::Long;
use DBI;
use DBD::Pg;
use DBIx::Simple;

#   command line argument parsing
my $opt = {
    -help     => 0,
    -hostname => ($ENV{"PGHOST"}     || "localhost"),
    -database => ($ENV{"PGDATABASE"} || "dbmail"   ),
    -username => ($ENV{"PGUSER"}     || "dbmail"   ),
    -password => ($ENV{"PGPASSWORD"} || "dbmail"   ),
};
my $p = new Getopt::Long::Parser;
$p->configure("bundling");
$p->getoptions(
    'h|help'       => \$opt->{-help},
    'H|hostname=s' => \$opt->{-hostname},
    'D|database=s' => \$opt->{-database},
    'U|username=s' => \$opt->{-username},
    'P|password=s' => \$opt->{-password},
) || die "option parsing failed";
undef $p;
if ($opt->{-help} or @ARGV < 2) {
    printf(STDERR "USAGE: dbmail-folders [-h|--help]\n");
    printf(STDERR "       [-H|--hostname <db-hostname>] [-D|--database <db-name>]\n");
    printf(STDERR "       [-U|--username <db-username>] [-P|--password <db-password>]\n");
    printf(STDERR "       <owner> <folder> [[{ro,rw}:]<user> ...]\n");
    printf(STDERR "EXAMPLES:\n");
    printf(STDERR "1. # create public folder #Public/<folder> (fully shared)\n");
    printf(STDERR "   \$ dbmail-folders __public__ <folder> anyone\n");
    printf(STDERR "2. # create personal shared folder #Users/<owner>/<folder> (fully shared)\n");
    printf(STDERR "   \$ dbmail-folders <owner> <folder> anyone\n");
    printf(STDERR "3. # create personal shared folder #Users/<owner>/<folder> (fully shared, different access)\n");
    printf(STDERR "   \$ dbmail-folders <owner> <folder> ro:anyone rw:<proxy>\n");
    printf(STDERR "4. # create personal shared folder #Users/<owner>/<folder> (partly shared)\n");
    printf(STDERR "   \$ dbmail-folders <owner> <folder> <user1> <user2> ...\n");
    printf(STDERR "5. # create personal private folder #Users/<owner>/<folder> (not shared)\n");
    printf(STDERR "   \$ dbmail-folders <owner> <folder>\n");
    exit($opt->{-help} ? 0 : 1);
}
my $owner  = shift(@ARGV);
my $folder = shift(@ARGV);
my @users  = @ARGV;

#   connect to DBMail's PostgreSQL-based backend RDBMS
my $db = DBIx::Simple->connect(
    sprintf("dbi:Pg:host=%s;dbname=%s", $opt->{-hostname}, $opt->{-database}),
    $opt->{-username}, $opt->{-password},
    { RaiseError => 1, AutoCommit => 1 }
);
die "failed to connect to database"
    if (not defined($db));

#   ensure that the folder owner exists
my ($owner_id) = $db->query(q{
    SELECT user_idnr
    FROM   dbmail_users 
    WHERE  userid = ?;
}, $owner)->flat();
if (not defined $owner_id) {
    printf(STDERR "dbmail-folders:ERROR: folder owner \"%s\" not existing\n", $owner); 
    exit(1);
}

#   check whether folder already exists
my ($mailbox_id) = $db->query(q{
    SELECT mailbox_idnr 
    FROM   dbmail_mailboxes 
    WHERE  owner_idnr = ? AND name = ?;
}, $owner_id, $folder)->flat();
if (not defined $mailbox_id) {
    #   create it if still not existing
    $db->query(q{
        INSERT
        INTO    dbmail_mailboxes 
                ( owner_idnr, name, 
                  seen_flag, answered_flag, deleted_flag, flagged_flag, recent_flag, draft_flag,
                  no_inferiors, no_select, permission )
        VALUES  ( ?, ?,
                  1, 1, 1, 1, 1, 1,
                  0, 0, 2 );
    }, $owner_id, $folder)
        or printf(STDERR "dbmail-folders:ERROR: unable to create folder \"%s/%s\": %s\n", $owner, $folder, $db->error()), exit(1);
    ($mailbox_id) = $db->query(q{
        SELECT mailbox_idnr 
        FROM   dbmail_mailboxes 
        WHERE  owner_idnr = ? AND name = ?;
    }, $owner_id, $folder)->flat();
    if (not defined $mailbox_id) {
        printf(STDERR "dbmail-folders:ERROR: unable to determine id of newly created folder\n"); 
        exit(1);
    }
}

#   optionally grant access to folder for additional users (beside folder owner)
$db->query(q{
    DELETE 
    FROM   dbmail_acl 
    WHERE  mailbox_id = ?;
}, $mailbox_id);
foreach my $user (@ARGV) {
    my $mode = "rw";
    if ($user =~ m/^(r[ow]):(.+)$/) {
        $mode = $1;
        $user = $2;
    } 
    my ($user_id) = $db->query(q{
        SELECT user_idnr
        FROM   dbmail_users 
        WHERE  userid = ?;
    }, $user)->flat();
    if (not defined $user_id) {
        printf(STDERR "dbmail-folders:WARNING: user \"%s\" not existing\n", $user); 
        next;
    }
    if ($mode eq "rw") {
        $db->query(q{
            INSERT 
            INTO   dbmail_acl 
                   ( user_id, mailbox_id, 
                     lookup_flag, read_flag, seen_flag, write_flag, insert_flag,
                     post_flag, create_flag, delete_flag, administer_flag )
            VALUES ( ?, ?,
                     1, 1, 1, 1, 1,
                     1, 1, 1, 1 );
        }, $user_id, $mailbox_id);
    }
    else {
        $db->query(q{
            INSERT 
            INTO   dbmail_acl 
                   ( user_id, mailbox_id, 
                     lookup_flag, read_flag, seen_flag, write_flag, insert_flag,
                     post_flag, create_flag, delete_flag, administer_flag )
            VALUES ( ?, ?,
                     1, 1, 0, 0, 0,
                     0, 0, 0, 0 );
        }, $user_id, $mailbox_id);
    }
}

#   make sure at least owner is subscribed to folder
my ($exists) = $db->query(q{
    SELECT 1
    FROM   dbmail_subscription 
    WHERE  user_id = ? AND mailbox_id = ?;
}, $owner_id, $mailbox_id)->flat();
if (not (defined $exists and $exists)) {
    $db->query(q{
        INSERT 
        INTO   dbmail_subscription 
               ( user_id, mailbox_id )
        VALUES ( ?, ? );
    }, $owner_id, $mailbox_id);
}

#   disconnect from DBMail's PostgreSQL-based backend RDBMS
$db->disconnect();
undef $db;

#   die gracefully
exit(0);

