#!/usr/bin/perl

# Load libraries
use Cyrus::IMAP::Admin;
use Net::LDAP qw(:all);
use Net::LDAP::Util qw(ldap_error_desc);
use Unix::Syslog qw(:macros);  # Syslog macros
use Unix::Syslog qw(:subs);    # Syslog functions

# Open syslog connection
openlog( "cyrus_ldap_quota.pl", LOG_PID|LOG_PERROR, LOG_LOCAL4 );
syslog LOG_INFO, "Begining run...";

# This script was created by Kevin J. Menard, Jr. <kmenard@wpi.edu>.
# Best use is to set this up as cron job.
# Any questions/complaints/praise/whatever, send 'em to the address
# above.   -- 08/16/2001

# Significant modifications by Nic Bernstein, <nic@onlight.com>,
# Copyright (c) 2005 for Onlight, llc, Milwaukee, WI  53202
# Wed Feb  9 14:38:43 CST 2005
#
# Added Net::LDAP for lookups, unix::Syslog for reporting

# Recommended that the files /etc/cyrus_ldap_quota_time and
# /etc/cyrus-utils.conf have root:mail ownership, and 660 permissions
# (or 660 for /etc/cyrus_ldap_quota_time and 640 for /etc/cyrus-utils.conf)
# then run this script as a cron job for the cyrus user.

# Read our configuration information
require ( '/etc/cyrus-utils.conf' );

# These shouldn't have to change
my @entries = ();
my $index = 0;
my $counter = 0;
my $old_timestamp = 0;
my $timestamp = "199412161032Z";

# Open the /etc/cyrus_ldap_quota_time file; it's a long name, but
# shouldn't interfere with existing files :)  This file contains 1 line,
# the generalized time format of the last time the script ran.  This is
# used for the search, so we only update quotas that have been modified
# since then.
{   if (-e "/etc/cyrus_ldap_quota_time")
    {   open(TIME_FILE, "/etc/cyrus_ldap_quota_time")
             or die "could not open the time file: $!\n";
     
         while(<TIME_FILE>) { $old_timestamp = $_; }
         close(TIME_FILE);
    }  

    # Now we deal with the case where the file doesn't exist, that is to
    # say the first time the script was run.

    unless ($old_timestamp == 0) { $timestamp = $old_timestamp; }

    # Now that we have that information, we can overwrite the file with
    # the new timestamp.  Maybe this overkill, but this is only a
    # temporary solution anyway.

    open(TIME_FILE, ">/etc/cyrus_ldap_quota_time")
                      or die "could not create file: $!\n";
    printf TIME_FILE "%4d%02d%02d%02d%02d%02dZ", $time[5] + 1900, $time[4] +1,
                      $time[3], $time[2], $time[1], $time[0];
    close(TIME_FILE);
}

# Create IMAP object
$imap_client = Cyrus::IMAP::Admin->new($imap_host)
        or syslog LOG_ERR, "Can't connect to IMAP server: $@";

$imap_client->send(undef, undef, "LOGIN %s %s", $imap_user, $imap_pw)
        or syslog LOG_ERR, "could not send user:pass to the server: $!";

# Create LDAP object
$ldap_client = new Net::LDAP ( $ldap_host, port => $ldap_port, )
        or syslog LOG_ERR, "can't make new LDAP object: $@";

# Bind to the LDAP server as administrator
$mesg = $ldap_client->bind ( $bind_dn, password => $bind_pw );
syslog LOG_WARN, "Failed to bind to %s as %s: %s", $ldap_host, $bind_dn,
                                    ldap_error_desc( $mesg ) if $mesg->code;

# perform search for usernames
$ldap_quota_filter = "(&$ldap_user_filter(modifyTimestamp>=$timestamp)($ldap_quota_attr=*))";
$search_result = $ldap_client->search( base   => $base_dn, scope  => 'one',
                                       filter => $ldap_quota_filter );
syslog LOG_INFO, "Username search failed for: %s",
                  ldap_error_desc( $search_result ) if $search_result->code;

# process each user in the directory
$count = 0;
while ( $entry = $search_result->shift_entry )
{   syslog LOG_INFO, "Quota for $user is $quota" if
                      $user = $entry->get_value( $ldap_user_attr ) 
                      and $quota = $entry->get_value( $ldap_quota_attr );

    $mailbox = $imap_prefix . $delimiter . $user;

    $imap_client->setquota( $mailbox, "STORAGE" => $quota )
        or syslog LOG_ERR, "could not set quota for $mailbox: $!";
    $count++;
}

# Close the LDAP connection
$ldap_client->unbind();
$ldap_client->disconnect();

# There is no IMAP LOGOUT method, so destroy the object...
Cyrus::IMAP::Admin::DESTROY ($imap_client);

# Log the end of our run
syslog LOG_INFO, "Ending run, updated $count quota records.";
1;

