#!/usr/bin/python
#
# This script is released to the public domain.
# Example usage:
# $ imapquota.py -s imap.mail.drexel.edu xyz12@drexel.edu
# Password:
# Used 92693 of 102400 KB
# 90.5 percent

import getpass
import imaplib
import re


def parse_server(server, ssl=False):
    server_bits = server.split(':')
    assert len(server_bits) in [1,2], 'invalid server: %s' % server_bits
    server = server_bits[0]
    if len(server_bits) == 2:
        port = server_bits[1]
    elif ssl == True:
        port = 993
    else:
        port = 143
    return (server, port)

def traverse_mailboxes(imap_connection, root='""', recursive=False):
    status,value = imap_connection.list(root, '%')
    regexp = re.compile(r'\((.*)\) "(.)" (.*)')
    #status,value = ('OK', [
    #    '(\NoInferiors) "/" INBOX',
    #    '(\HasNoChildren) "/" APS',
    #    ...])
    for mailbox_line in value:
        if mailbox_line is None:
            continue
        match = regexp.match(mailbox_line)
        attributes,delimiter,mailbox = match.groups()
        # TODO: unquote mailbox (if necessary?)
        yield (attributes, delimiter, mailbox)
        children = True
        for attribute in ['\Noinferiors', '\HasNoChildren']:# RFC 2060 and 3348
            if attribute.lower() in attributes.lower():
                children = False
                break
        if not recursive or not children:
            continue
        for m in traverse_mailboxes(imap_connection, root=mailbox):
            yield m

def get_quota(imap_connection, mailbox='inbox'):
    status,value = imap_connection.getquotaroot(mailbox)
    #status,value = ('OK',
    #    [['inbox user/xyz12'], ['user/xyz12 (STORAGE 80000 102400)']])
    quota = value[1][0]
    regexp = re.compile(r'(.*) \(STORAGE ([0-9]*) ([0-9]*)\)')
    m = regexp.match(quota)
    root,used,avail = m.groups()
    return (root, int(used), int(avail))

def get_mailbox_size(imap_connection, mailbox='inbox'):
    status,value = imap_connection.select(mailbox, readonly=True)
    if status != 'OK':
        raise Exception(value)
    status,value = imap_connection.search(None, 'ALL')
    size = 0
    regexp = re.compile(r'.*RFC822\.SIZE ([0-9]*).*')
    for num in value[0].split():
        status,value = imap_connection.fetch(num, '(RFC822.SIZE)')
        #status,value = ('OK', ['231 (RFC822.SIZE 4882)'])
        match = regexp.match(value[0])
        msg_size = int(match.group(1))
        size += msg_size
    return size


if __name__ == '__main__':
    import optparse
    p = optparse.OptionParser('%prog SERVER[:port] account',
        epilog='imapquota -s imap.mail.drexel.edu xyz12@drexel.edu')
    p.add_option('-s', '--ssl', dest='ssl', default=False, action='store_true',
                 help='Connect over an SSL encrypted socket and change default port to 993.')
    p.add_option('-m', '--mailbox', dest='mailbox',
                 help='Root mailbox (e.g. INBOX).')
    p.add_option('-r', '--recursive', dest='recursive', default=False,
                 action='store_true',
                 help='List size of all mailboxes below the root given with `-m`.')
    options,args = p.parse_args()

    server,account = args
    server,port = parse_server(server, ssl=options.ssl)
    password = getpass.getpass()

    quota_roots = {}

    if options.ssl == True:
        i = imaplib.IMAP4_SSL(server, port)
    else:
        i = imaplib.IMAP4(server, port)
    i.login(account, password)
    try:
        if options.mailbox:
            if options.recursive:
                mailboxes = [
                    m for a,d,m in traverse_mailboxes(i, options.mailbox)]
                mailboxes.insert(0, options.mailbox)
            else:
                mailboxes = [options.mailbox]
        elif options.recursive:
            mailboxes = [m for a,d,m in traverse_mailboxes(i)]
        else:
            mailboxes = ['inbox']
        for mailbox in mailboxes:
            root,used,avail = get_quota(i, mailbox)
            if root not in quota_roots:
                quota_roots[root] = (used, avail)
            if options.recursive:
                size = get_mailbox_size(i, mailbox)
                print '%s used %d B' % (mailbox, size)
    finally:
        i.logout()

    for root,specs in sorted(quota_roots.iteritems()):
        used,available = specs
        print 'quota rooted at %s used %d of %d KB (%.1f percent)' % (
            root, used, avail, (float(used)/avail*100.0))
