mkogg.py: Fix 'self.get_mp4_metadata(self, source)'
[blog.git] / posts / imapquota / imapquota.py
1 #!/usr/bin/python
2 #
3 # This script is released to the public domain.
4 # Example usage:
5 # $ imapquota.py -s imap.mail.drexel.edu xyz12@drexel.edu
6 # Password:
7 # Used 92693 of 102400 KB
8 # 90.5 percent
9
10 import getpass
11 import imaplib
12 import re
13
14
15 def parse_server(server, ssl=False):
16     server_bits = server.split(':')
17     assert len(server_bits) in [1,2], 'invalid server: %s' % server_bits
18     server = server_bits[0]
19     if len(server_bits) == 2:
20         port = server_bits[1]
21     elif ssl == True:
22         port = 993
23     else:
24         port = 143
25     return (server, port)
26
27 def traverse_mailboxes(imap_connection, root='""', recursive=False):
28     status,value = imap_connection.list(root, '%')
29     regexp = re.compile(r'\((.*)\) "(.)" (.*)')
30     #status,value = ('OK', [
31     #    '(\NoInferiors) "/" INBOX',
32     #    '(\HasNoChildren) "/" APS',
33     #    ...])
34     for mailbox_line in value:
35         if mailbox_line is None:
36             continue
37         match = regexp.match(mailbox_line)
38         attributes,delimiter,mailbox = match.groups()
39         # TODO: unquote mailbox (if necessary?)
40         yield (attributes, delimiter, mailbox)
41         children = True
42         for attribute in ['\Noinferiors', '\HasNoChildren']:# RFC 2060 and 3348
43             if attribute.lower() in attributes.lower():
44                 children = False
45                 break
46         if not recursive or not children:
47             continue
48         for m in traverse_mailboxes(imap_connection, root=mailbox):
49             yield m
50
51 def get_quota(imap_connection, mailbox='inbox'):
52     status,value = imap_connection.getquotaroot(mailbox)
53     #status,value = ('OK',
54     #    [['inbox user/xyz12'], ['user/xyz12 (STORAGE 80000 102400)']])
55     quota = value[1][0]
56     regexp = re.compile(r'(.*) \(STORAGE ([0-9]*) ([0-9]*)\)')
57     m = regexp.match(quota)
58     root,used,avail = m.groups()
59     return (root, int(used), int(avail))
60
61 def get_mailbox_size(imap_connection, mailbox='inbox'):
62     status,value = imap_connection.select(mailbox, readonly=True)
63     if status != 'OK':
64         raise Exception(value)
65     status,value = imap_connection.search(None, 'ALL')
66     size = 0
67     regexp = re.compile(r'.*RFC822\.SIZE ([0-9]*).*')
68     for num in value[0].split():
69         status,value = imap_connection.fetch(num, '(RFC822.SIZE)')
70         #status,value = ('OK', ['231 (RFC822.SIZE 4882)'])
71         match = regexp.match(value[0])
72         msg_size = int(match.group(1))
73         size += msg_size
74     return size
75
76
77 if __name__ == '__main__':
78     import optparse
79     p = optparse.OptionParser('%prog SERVER[:port] account',
80         epilog='imapquota -s imap.mail.drexel.edu xyz12@drexel.edu')
81     p.add_option('-s', '--ssl', dest='ssl', default=False, action='store_true',
82                  help='Connect over an SSL encrypted socket and change default port to 993.')
83     p.add_option('-m', '--mailbox', dest='mailbox',
84                  help='Root mailbox (e.g. INBOX).')
85     p.add_option('-r', '--recursive', dest='recursive', default=False,
86                  action='store_true',
87                  help='List size of all mailboxes below the root given with `-m`.')
88     options,args = p.parse_args()
89
90     server,account = args
91     server,port = parse_server(server, ssl=options.ssl)
92     password = getpass.getpass()
93
94     quota_roots = {}
95
96     if options.ssl == True:
97         i = imaplib.IMAP4_SSL(server, port)
98     else:
99         i = imaplib.IMAP4(server, port)
100     i.login(account, password)
101     try:
102         if options.mailbox:
103             if options.recursive:
104                 mailboxes = [
105                     m for a,d,m in traverse_mailboxes(i, options.mailbox)]
106                 mailboxes.insert(0, options.mailbox)
107             else:
108                 mailboxes = [options.mailbox]
109         elif options.recursive:
110             mailboxes = [m for a,d,m in traverse_mailboxes(i)]
111         else:
112             mailboxes = ['inbox']
113         for mailbox in mailboxes:
114             root,used,avail = get_quota(i, mailbox)
115             if root not in quota_roots:
116                 quota_roots[root] = (used, avail)
117             if options.recursive:
118                 size = get_mailbox_size(i, mailbox)
119                 print '%s used %d B' % (mailbox, size)
120     finally:
121         i.logout()
122
123     for root,specs in sorted(quota_roots.iteritems()):
124         used,available = specs
125         print 'quota rooted at %s used %d of %d KB (%.1f percent)' % (
126             root, used, avail, (float(used)/avail*100.0))