Added SILC Thread Queue API
[crypto.git] / apps / irssi / scripts / mail.pl
1 $VERSION = "2.92";
2 %IRSSI = (
3     authors     => "Timo Sirainen, Matti Hiljanen, Joost Vunderink, Bart Matthaei",
4     contact     => "tss\@iki.fi, matti\@hiljanen.com, joost\@carnique.nl, bart\@dreamflow.nl",
5     name        => "mail",
6     description => "Fully customizable mail counter statusbar item with multiple mailbox and multiple Maildir support",
7     license     => "Public Domain",
8     url         => "http://irssi.org, http://scripts.irssi.de",
9 );
10
11 # Mail counter statusbar item
12 # for irssi 0.8.1 by Timo Sirainen
13 #
14 # Maildir support added by Matti Hiljanen
15 # Multiple Maildir/mbox and customization support added by Joost Vunderink
16 # OLD mailtreatment switch added by Bart Matthaei.
17 # Improved some regexps in maildirmode by Bart Matthaei.
18 # Maildirmode regexps (hopefully) fixed for good by Matti Hiljanen. 
19 #
20 # You can add any number of mailboxes or Maildirs to watch for new mail in.
21 # Give them any name and <name>:<count> will appear in your mail
22 # statusbar item, where <count> is the number of unread messages.
23 # If only 1 mailbox/Maildir is defined, the statusbar item will have the 
24 # familiar form [Mail: <count>].
25 # If you set mail_show_message to ON, irssi will print a message in the
26 # active window whenever new mail arrives.
27 #
28 # Check /mailbox help for help.
29
30 use Irssi::TextUI;
31
32 my $maildirmode = 0; # maildir=1, file(spools)=0
33 my $old_is_not_new = 0; 
34 my $extprog;
35 my ($last_refresh_time, $refresh_tag);
36
37 # for mbox caching
38 my $last_size, $last_mtime, $last_mailcount, $last_mode;
39
40 # list of mailboxes
41 my %mailboxes = (); 
42 my %new_mails_in_box = ();
43 my $nummailboxes = 0; 
44
45 # the string to be stored in Irssi's mail_mailboxes setting
46 my $mailboxsetting = "";
47
48 sub cmd_print_help {
49   Irssi::print(
50   "MAILBOX ADD <num> <file|dir>\n".
51   "MAILBOX DEL <num>\n".
52   "MAILBOX SHOW\n\n".
53   "Statusbar item to keep track of how many (new) emails there are in ".
54   "each of your mailboxes/Maildirs.\n\n".
55   "/MAILBOX ADD <name> <file|dir>\n".
56   "    - Adds a mailbox or a Maildir to the list.\n".
57   "/MAILBOX DEL <name>\n".
58   "    - Removes mailbox or Maildir named <name> from the list.\n".
59   "/MAILBOX SHOW\n".
60   "    - Shows a list of the defined mailboxes.\n\n".
61   "Use the following commands to change the behaviour:\n\n".
62   "/SET MAILDIRMODE on|off\n".
63   "    - If maildirmode is on, the mailboxes in the list are assumed to be ".
64   "directories. Otherwise they are assumed to be spool files.\n".
65   "      Default: off.\n".
66   "/SET MAIL_OLDNOTNEW on|off\n".
67   "    - If switched on, mail marked als \"OLD\" will not be treated as new.\n".
68   "      Default: off.\n".
69   "/SET MAIL_EXT_PROGRAM <prog>\n".
70   "    - <prog> will be used to check for mail.\n".
71   "/SET MAIL_REFRESH_TIME <num>\n".
72   "    - Sets the time between checks to <num> seconds.\n      Default: 60.\n".
73   "/SET MAIL_SHOW_MESSAGE on|off\n".
74   "    - If this is on, a message will be printed in the active window ".
75   "whenever new email is received.\n      Default: off.\n".
76   "/SET MAIL_SHOW_ONLY_UNREAD on|off\n".
77   "    - If you don't want to see a mailbox if it does not contain any new ".
78   "mail, set this to on.\n      Default: on.\n" .
79   "/SET MAIL_SEPARATOR <char>\n".
80   "    - Sets the character to be printed between each mailbox.\n".
81   "      The default is a comma.\n".
82   "/SET MAIL_FORMAT <format>\n".
83   "    - Sets the format of each mailbox.\n".
84   "      Allowed variables:\n".
85   "      %%n = mailbox name\n".
86   "      %%u = number of unread mail\n".
87   "      %%r = number of read mail\n".
88   "      %%t = total amount of mail\n".
89   "      The default format is %%n:%%u/%%t.\n".
90   "\nSee also: STATUSBAR"
91   ,MSGLEVEL_CRAP);
92 }
93
94 sub mbox_count {
95   my $mailfile = shift;
96   my $unread = 0;
97   my $read = 0;
98   my $maildirmode=Irssi::settings_get_bool('maildir_mode');
99   my $old_is_not_new=Irssi::settings_get_bool('mail_oldnotnew');
100
101   if ($extprog ne "") {
102      $total = `$extprog`;
103      chomp $unread;
104   } else {
105     if (!$maildirmode) {
106       if (-f $mailfile) {
107         my @stat = stat($mailfile);
108         my $size = $stat[7];
109         my $mtime = $stat[9];
110
111         # if the file hasn't changed, get the count from cache
112         return $last_mailcount if ($last_size == $size && $last_mtime == $mtime);
113         $last_size = $size;
114         $last_mtime = $mtime;
115
116         my $f = gensym;
117         return 0 if (!open($f, $mailfile));
118
119         # count new mails only
120         my $internal_removed = 0;
121         while (<$f>) {
122           $unread++ if (/^From /);
123
124           if(!$old_is_not_new) {
125                 $unread-- if (/^Status: R/);
126           } else {
127                 $unread-- if (/^Status: [OR]/);
128           }
129
130           $read++ if (/^From /);
131
132           # Remove folder internal data, but only once
133           if (/^Subject: .*FOLDER INTERNAL DATA/) {
134             if ($internal_removed == 0) {
135               $internal_removed = 1;
136               $read--;
137               $unread--;
138             }
139           }
140         }
141         close($f);
142       }
143     } else {
144       opendir(DIR, "$mailfile/cur") or return 0;
145       while (defined(my $file = readdir(DIR))) {
146         next if $file =~ /^(.|..)$/;
147         # Maildir flags: http://cr.yp.to/proto/maildir.html
148         # My old regexps were useless if the MUA added any 
149         # non-default flags -qvr
150         # 
151         # deleted mail
152         next if $file =~ /\:.*?T.*?$/;
153             if($old_is_not_new) {
154            # when mail gets moved from new to cur it's name _always_
155            # changes from uniq to uniq:info, even when it's still not
156            # read. I assume "old mail" means mail which hasn't been read
157            # yet but it has been "acknowledged" by the user. (it's been
158            # moved to cur) -qvr
159            if ($file =~ /\:.*?$/) {
160               $read++;
161                   next;
162            }
163         } else {
164            if ($file =~ /\:.*?S.*?$/) {
165               $read++;
166                   next;
167            }
168         }
169         $unread++;
170       }
171       closedir(DIR);
172
173       opendir(DIR, "$mailfile/new") or return 0;
174       while (defined(my $file = readdir(DIR))) {
175         next if $file =~ /^(.|..)$/;
176         $unread++;
177       }
178       closedir(DIR);
179     }
180   }
181
182   if ($unread eq "" || $unread < 0) {
183     $unread = 0;
184   }
185   if ($read eq "" || $read < 0) {
186     $read = 0;
187   }
188
189   $last_mailcount = $unread;
190
191   return ($unread, $read);
192 }
193
194 # Checks for mail and sets the statusbar item to the right string.
195 # Also shows a message in the active window if that setting is set.
196 sub mail {
197   my ($item, $get_size_only) = @_;
198
199   my $result;
200   my $format = Irssi::settings_get_str('mail_format');
201   my $unread = 0;
202   my $read = 0;
203   my $total = 0;
204
205   # check all mailboxes for new email
206   foreach $name (keys(%mailboxes)) {
207     my $box = $mailboxes{$name};
208     # replace "~/" at the beginning by the user's home dir
209     $box =~ s/^~\//$ENV{'HOME'}\//;
210
211     ($unread, $read) = mbox_count($box);
212     $unread = "0" if ($unread eq "");
213     $read = "0" if ($read eq "");
214     $total = $unread + $read;
215     $total = "0" if ($total eq "");
216
217     next if (Irssi::settings_get_bool('mail_show_only_unread') && $unread == 0);
218
219     if ($total eq "") { $total = 0; }
220     if (length($result) > 0) {
221       $result .= Irssi::settings_get_str('mail_separator');
222     }
223     my $string = $format;
224     $string =~ s/%n/$name/;
225     $string =~ s/%u/$unread/;
226     $string =~ s/%r/$read/;
227     $string =~ s/%t/$total/;
228     $result .= $string;
229     
230     # Show -!- You have <num> new messages in <name>.
231     # Show this only if there are any new, unread messages.
232     if (Irssi::settings_get_bool('mail_show_message') &&
233         $unread > $new_mails_in_box{$name}) {
234       $new_mails = $unread - $new_mails_in_box{$name};
235       if ($nummailboxes == 1) {
236         Irssi::print("You have $new_mails new message" . ($new_mails != 1 ? "s." : "."), MSGLEVEL_CRAP);
237       } else {
238         Irssi::print("You have $new_mails new message" . ($new_mails != 1 ? "s " : " ") . "in $name.", MSGLEVEL_CRAP);
239       }
240     }
241
242     $new_mails_in_box{$name} = $unread;
243   }
244   
245   if (length($result) == 0) {
246     # no mail - don't print the [Mail: ] at all
247     if ($get_size_only) {
248       $item->{min_size} = $item->{max_size} = 0;
249     }
250   } else {
251     $item->default_handler($get_size_only, undef, $result, 1);
252   }
253 }
254
255 sub refresh_mail {
256   Irssi::statusbar_items_redraw('mail');
257 }
258
259 # Adds the mailboxes from a string. Only to be used during startup.
260 sub add_mailboxes {
261   my $boxstring = $_[0];
262   my @boxes = split(/,/, $boxstring);
263
264   foreach $dbox(@boxes) {
265     my $name = $dbox;
266     $name = substr($dbox, 0, index($dbox, '='));
267     my $box = $dbox;
268     $box = substr($dbox, index($dbox, '=') + 1, length($dbox));
269     addmailbox($name, $box);
270   }
271 }
272
273 sub addmailbox {
274   my ($name, $box) = @_;
275
276   if (exists($mailboxes{$name})) {
277     if ($box eq $mailboxes{$name}) {
278       Irssi::print("Mailbox $name already set to $box", MSGLEVEL_CRAP);
279     } else {
280       Irssi::print("Mailbox $name changed to $box", MSGLEVEL_CRAP);
281       $new_mails_in_box{$name} = 0;
282     }
283   } else {
284     Irssi::print("Mailbox $name added: " . $box, MSGLEVEL_CRAP);
285     $new_mails_in_box{$name} = 0;
286     $nummailboxes++;
287   }
288   $mailboxes{$name} = $box;
289 }
290
291 sub delmailbox {
292   my $name = $_[0];
293
294   if (exists($mailboxes{$name})) {
295     Irssi::print("Mailbox $name removed", MSGLEVEL_CRAP);
296     delete($mailboxes{$name});
297     delete($new_mails_in_box{$name});
298     $nummailboxes--;
299   } else {
300     Irssi::print("No such mailbox $name. Use /mailbox show to see a list.", MSGLEVEL_CRAP);
301   }
302 }
303
304 sub update_settings_string {
305   my $setting;
306
307   foreach $name (keys(%mailboxes)) {
308     $setting .= $name . "=" . $mailboxes{$name} . ",";
309   }
310
311   Irssi::settings_set_str("mail_mailboxes", $setting);
312 }
313
314 sub cmd_addmailbox {
315   my ($name, $box) = split(/ +/, $_[0]);
316
317   if ($name eq "" || $box eq "") {
318     Irssi::print("Use /mailbox add <name> <mailbox> to add a mailbox.", MSGLEVEL_CRAP);
319     return;
320   }
321
322   addmailbox($name, $box);
323   update_settings_string();
324   refresh_mail();
325 }
326
327 sub cmd_delmailbox {
328   my $name = $_[0];
329
330   if ($name eq "") {
331     Irssi::print("Use /mailbox del <name> to delete a mailbox.", MSGLEVEL_CRAP);
332     return;
333   }
334
335   delmailbox($name);
336   update_settings_string();
337   refresh_mail();
338 }
339
340 sub cmd_showmailboxes {
341   if ($nummailboxes == 0) {
342     Irssi::print("No mailboxes defined.", MSGLEVEL_CRAP);
343     return;
344   }
345   Irssi::print("Mailboxes:", MSGLEVEL_CRAP);
346   foreach $box (keys(%mailboxes)) {
347     Irssi::print("$box: " . $mailboxes{$box}, MSGLEVEL_CRAP);
348   }
349 }
350
351 sub cmd_mailboxes {
352   my ($data, $server, $item) = @_;
353   if ($data =~ m/^[(show)|(add)|(del)]/i ) {
354     Irssi::command_runsub ('mailbox', $data, $server, $item);
355   }
356   else {
357     Irssi::print("Use /mailbox (show|add|del).")
358   }
359 }
360
361 sub init_mailboxes {
362   # Add the mailboxes at startup of the script
363   my $boxes = Irssi::settings_get_str('mail_mailboxes');
364   if (length($boxes) > 0) {
365     add_mailboxes($boxes);
366   }
367 }
368
369 sub read_settings {
370   $extprog = Irssi::settings_get_str('mail_ext_program');
371   my $time = Irssi::settings_get_int('mail_refresh_time');
372   my $mode = Irssi::settings_get_bool('maildir_mode');
373   unless ($time == $last_refresh_time) {
374      $last_refresh_time = $time;
375      Irssi::timeout_remove($refresh_tag) if ($refresh_tag);
376      $refresh_tag = Irssi::timeout_add($time*1000, 'refresh_mail', undef);
377   }
378   return if ($mode == $last_mode);
379   $last_mode = $mode;
380   refresh_mail;
381 }
382
383
384 if (!$maildirmode) {
385   my $default = "1=" . $ENV{'MAIL'} . ",";
386   Irssi::settings_add_str('misc', 'mail_mailboxes', $default);
387 } else {
388   my $default = "1=~/Maildir/,";
389   Irssi::settings_add_str('misc', 'mail_mailboxes', $default);
390 }
391
392 Irssi::command_bind('mailbox show', 'cmd_showmailboxes');
393 Irssi::command_bind('mailbox add', 'cmd_addmailbox');
394 Irssi::command_bind('mailbox del', 'cmd_delmailbox');
395 Irssi::command_bind('mailbox help', 'cmd_print_help');
396 Irssi::command_bind('mailbox', 'cmd_mailboxes');
397
398 Irssi::settings_add_str('misc', 'mail_ext_program', '');
399 Irssi::settings_add_int('misc', 'mail_refresh_time', 60);
400 Irssi::settings_add_bool('misc', 'maildir_mode', "$maildirmode");
401 Irssi::settings_add_bool('misc', 'mail_oldnotnew', "$old_is_not_new");
402 Irssi::settings_add_str('misc', 'mail_separator', ",");
403 Irssi::settings_add_bool('misc', 'mail_show_message', "0");
404 Irssi::settings_add_str('misc', 'mail_format', '%n:%u/%t');
405 Irssi::settings_add_bool('misc', 'mail_show_only_unread', "1");
406
407 Irssi::statusbar_item_register('mail', '{sb Mail: $0-}', 'mail');
408
409 read_settings();
410 init_mailboxes();
411 Irssi::signal_add('setup changed', 'read_settings');
412 refresh_mail();
413
414 # EOF