source: trunk/athena/bin/mitmailutils/mailquota.pl @ 24300

Revision 24300, 7.4 KB checked in by geofft, 14 years ago (diff)
In mitmailutils: * Switch from Hesiod to using *.mail.mit.edu so we can use GSSAPI authentication (Trac: #403). * Because of a Cyrus SASL bug (documented in that ticket) regarding parsing long encrypted responses, set maxssf to zero to send mail in the clear. This isn't as terrible as it sounds because mail travels in the clear on the public Internet anyway, and a bunch of client programs (Pine on Athena 9, for instance) don't use encryption, and the major use of mitmailutils is `from` and `mailquota` anyway. Patch based on one from Jonathan Reed <jdreed@mit.edu>.
Line 
1#!/usr/bin/perl -w
2
3# $Id: mailquota.pl,v 1.4 2004-07-29 19:11:52 rbasch Exp $
4
5# Display the IMAP resource usage on the user's post office server.
6
7use strict;
8use warnings FATAL => 'all';
9use Cyrus::IMAP;
10use Getopt::Std;
11
12sub usage(;$);
13sub print_quota($$$$$);
14sub send_command($);
15sub quota_callback(@);
16sub quotaroot_callback(@);
17sub capability_callback(@);
18sub close_and_errorout($);
19sub close_connection();
20sub errorout($);
21
22sub usage(;$) {
23    print STDERR "mailquota: $_[0]\n" if $_[0];
24    print STDERR <<EOF;
25Usage: mailquota [<options>] [<user>]
26  Options:
27    -h <host>     query <host> instead of default post office server
28    -m <mailbox>  query for <mailbox> (default is INBOX)
29    -n            be silent unless usage % is above the threshold
30    -u <percent>  usage % threshold (default is 90); implies -n
31    -d            turn on debugging
32EOF
33    exit 1;
34}
35
36my $need_header = 1;
37my $root_width = 16;
38my $num_width = 10;
39my $percent_width = 5;
40my $format = "%-${root_width}.${root_width}s" .
41    " %${num_width}s %${num_width}s %${percent_width}s" .
42    " %${num_width}s %${num_width}s %${percent_width}s\n";
43
44# Parse the command line arguments.
45my %opts;
46getopts('dh:m:nu:', \%opts) || usage;
47my $username = shift @ARGV || $ENV{'ATHENA_USER'} || $ENV{'USER'} || getlogin || (getpwuid($<))[0] ||
48        errorout "Cannot determine user name";
49
50usage "Too many arguments" if @ARGV > 0;
51my $debug = $opts{'d'};
52my $warn_only = $opts{'n'};
53my $usage_threshold = $opts{'u'};
54my $host = $opts{'h'} || (gethostbyname("$username.mail.mit.edu"))[0];
55errorout "Cannot find Post Office server for $username" unless $host;
56errorout "Exchange accounts are not supported yet. Try http://owa.mit.edu/." if $host =~ /EXCHANGE/;
57my $mbox = $opts{'m'} || "INBOX";
58
59# Validate the usage percentage threshold, allowing a trailing %.
60# Setting the threshold implies we should only display quotas
61# for which any resource usage is above the threshold.
62if (defined $usage_threshold) {
63    chop $usage_threshold if $usage_threshold =~ /^\d+%$/;
64    usage "-u argument must be numeric" if $usage_threshold !~ /^\d+$/;
65    $warn_only = 1;
66} else {
67    $usage_threshold = 90;
68}
69
70# Connect to the IMAP server, check for the QUOTA extension, and
71# authenticate.
72my $client = Cyrus::IMAP->new($host) ||
73    errorout "Cannot connect to IMAP server on $host";
74my $caps = '';
75$client->addcallback({-trigger => 'CAPABILITY',
76                      -callback => \&capability_callback,
77                      -rock => \$caps});
78send_command "CAPABILITY";
79$caps =~ '\bQUOTA\b' ||
80    close_and_errorout "$host does not support the IMAP QUOTA extension";
81$client->authenticate(-authz => $username, -maxssf => 0) ||
82    close_and_errorout "Cannot authenticate to $host";
83
84# Send the GETQUOTAROOT command, which returns both the QUOTA and
85# QUOTAROOT responses.  Quota information will be displayed via
86# the QUOTA callback.
87$client->addcallback({-trigger => 'QUOTA',
88                      -callback => \&quota_callback});
89$client->addcallback({-trigger => 'QUOTAROOT',
90                      -callback => \&quotaroot_callback});
91send_command "GETQUOTAROOT \"$mbox\"";
92
93# We are done talking to the IMAP server; close down the connection.
94close_connection();
95
96# Print the quota information for the given quota root and its
97# storage and message resource values.
98sub print_quota($$$$$) {
99    my ($root, $storage_used, $storage_max, $message_used, $message_max) = @_;
100    my $storage_percent;
101    my $storage_percent_out;
102    my $message_percent;
103    my $message_percent_out;
104
105    # Calculate the usage percentages, and format for output.
106    if ($storage_max) {
107        $storage_percent = ($storage_used / $storage_max) * 100;
108        $storage_percent_out = sprintf("%.0f%%", $storage_percent);
109    }
110    if ($message_max) {
111        $message_percent = ($message_used / $message_max) * 100;
112        $message_percent_out = sprintf("%.0f%%", $message_percent);
113    }
114
115    # Skip this quota if we are only displaying usages above the
116    # specified threshold.
117    return unless (!$warn_only ||
118                   (defined $storage_percent &&
119                    $storage_percent >= $usage_threshold) ||
120                   (defined $message_percent &&
121                    $message_percent >= $usage_threshold));
122
123    # Print a header if this is the first line of output.
124    if ($need_header) {
125        printf($format,
126               "Quota",
127               "KB Used", "KB Max", "KB %",
128               "# Msgs", "# Max", "# %");
129        $need_header = 0;
130    }
131    printf($format,
132           $root,
133           defined $storage_used ? $storage_used : '-',
134           defined $storage_max ? $storage_max : '-',
135           defined $storage_percent_out ? $storage_percent_out : '-',
136           defined $message_used ? $message_used : '-',
137           defined $message_max ? $message_max : '-',
138           defined $message_percent_out ? $message_percent_out : '-');
139}
140
141# Subroutine to send a command to the IMAP server, and wait for the
142# response; any defined callbacks for the response are invoked.
143# If the server response indicates failure, we error out.
144sub send_command($) {
145    print "Sending: $_[0]\n" if $debug;
146    my ($status, $text) = $client->send('', '', $_[0]);
147    print "Response: status $status, text $text\n" if $debug;
148    errorout "Premature end-of-file on IMAP connection to $host"
149        if $status eq 'EOF';
150    close_and_errorout "IMAP error for $mbox on $host: $text"
151        if $status ne 'OK';
152}
153
154# Callback subroutine to parse the QUOTA response.
155# The "-text" hash element contains the quota root name, and a list
156# of quota resource names, usages, and limits.  Recognized names are
157# STORAGE (sum of message sizes, in kilobytes) and MESSAGE (number of
158# messages).  See RFC 2087.
159sub quota_callback(@) {
160    my %cb = @_;
161    my ($root, $quotalist);
162    print "In QUOTA callback: text $cb{-text}\n" if $debug;
163    if (($root, $quotalist) = ($cb{-text} =~ /(\S*)\s+\((.*)\)/io)) {
164        my ($storage_used, $storage_max, $message_used, $message_max);
165        while ($quotalist) {
166            my ($resource, $used, $max);
167            ($resource, $used, $max, $quotalist) = split /\s/, $quotalist, 4;
168            last unless $max;
169            $resource = uc $resource;
170            if ($resource eq "STORAGE") {
171                $storage_used = $used;
172                $storage_max = $max;
173            }
174            elsif ($resource eq "MESSAGE") {
175                $message_used = $used;
176                $message_max = $max;
177            }
178        }
179        print_quota($root, $storage_used, $storage_max,
180                    $message_used, $message_max)
181            if (defined $storage_max || defined $message_max);
182    }
183}
184
185# Callback subroutine to parse the QUOTAROOT response.  The "-text"
186# hash element contains the mailbox name, and zero or more quota root
187# names.  This is currently used for debugging only.
188sub quotaroot_callback(@) {
189    my %cb = @_;
190    print "In QUOTAROOT callback: text $cb{-text}\n" if $debug;
191}
192
193# Callback subroutine to parse the CAPABILITY response.  The "-rock" hash
194# element is a reference to the string in which to store the space-separated
195# capability names.
196sub capability_callback(@) {
197    my %cb = @_;
198    print "In CAPABILITY callback: keyword $cb{-keyword}, text $cb{-text}\n"
199        if $debug;
200    ${$cb{-rock}} = $cb{-text};
201}
202
203# Close the connection to the IMAP server, and error out.
204sub close_and_errorout($) {
205    close_connection();
206    errorout $_[0];
207}
208
209# Logout from the IMAP server, and close the connection.
210sub close_connection() {
211    $client->send('', '', "LOGOUT");
212    # Set the client reference to undef, so that perl invokes the
213    # destructor, which closes the connection.  Note that if we invoke
214    # the destructor explicitly here, then perl will still invoke it
215    # again when the program exits, thus touching memory which has
216    # already been freed.
217    $client = undef;
218}
219
220sub errorout($) {
221    print STDERR "mailquota: $_[0]\n";
222    exit 1;
223}
Note: See TracBrowser for help on using the repository browser.