IMAPExpire

Being an Alpine user, I have several rules and filters in place, especially for all those countless mailing lists I'm subscribed to. Specifically, I only want the mails of, say the last 3 weeks kept in certain mail folders, but I don't need the whole archive of lkml to be stored on my disk. To do that, there's a rule in my .pinerc to implement that:

patterns-filters2=LIT:pattern="/NICK=purge_old-threads/AGE=(21,INF)/FLDTYPE=SPEC/FOLDER={localhost\/user=dummy\/tls\/novalidate-cert}INBOX.Misc.lkml,{localhost\/user=dummy\/tls\/novalidate-cert}INBOX.Misc.bugtraq,[...]" action="/FILTER=1"
This is just an excerpt but maybe you get the idea: the filter is called purge_old-threads and it deletes mails older than 21 days. So far, so good.

But the filter stanza is actually quite long and hard to maintain and only gets triggered when I actually change into the mail folder and look at its contents. Alpine doesn't do any automagic housekeeping, so when I don't read lkml for a few weeks, the mailfolder grows and the incoming flow of mails just pile up. Then, when I get around to read lkml again, the filter kicks in and has to crawl through ~20k messages and delete all the older ones, which might take a while to complete.

So, I wanted to know if there's a way to do this without these rather cryptic Alpine rule sets. During my search I came across IMAPExpire. It's a nice Perl script that uses IMAP::Client, which looks a lot like Net::IMAP::Client (and has been packaged for Debian too), except it's not - we really need IMAP::Client here and we'll try to install it from CPAN:
$ env | grep PERL
PERL5LIB=/home/dummy/.perl5/lib/perl5
PERL_MB_OPT=--install_base "/home/dummy/.perl5"
PERL_LOCAL_LIB_ROOT=/home/dummy/.perl5
PERL_MM_OPT=INSTALL_BASE=/home/dummy/.perl5

$ cpan
cpan[1]> install IMAP::Client
[...]
IMAP::Client is up to date (0.13).

cpan[4]> i IMAP::Client 
Module id = IMAP::Client
    CPAN_USERID  CONTEB (Brenden Conte <conteb@cpan.org>)
    CPAN_VERSION 0.13
    CPAN_FILE    C/CO/CONTEB/IMAP-Client-0.13.tar.gz
    UPLOAD_DATE  2006-09-28
    MANPAGE      IMAP::Client - Advanced manipulation of IMAP services w/ referral support
    INST_FILE    /home/dummy/.perl5/lib/perl5/IMAP/Client.pm
    INST_VERSION 0.13
With that in place, imapexpire.pl should work now. Don't forget the --test switch when trying this out:
$ cat > ~/.imap-pw
s3cr3tpassw0rd
^D

$ ./imapexpire.pl --test --user dummy --passfile ~/.imap-pw --age 21 \
        --debug 9 --folders INBOX.Misc.lkml
<< * OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE STARTTLS AUTH=PLAIN] Dovecot ready.
> 0001 NOOP
<< 0001 OK NOOP completed.
> 0002 LOGIN dummy s3cr3tpassw0rd
<< 0002 OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS THREAD=ORDEREDSUBJECT MULTIAPPEND URL-PARTIAL CATENATE UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS SPECIAL-USE BINARY MOVE] Logged in
> 0003 LIST "" "INBOX.Misc.lkml"
<< * LIST (\HasNoChildren) "." INBOX.Misc.lkml
<< 0003 OK List completed.
TEST  : You're running in test mode, so the deletions wont actually take place
ACTION: Delete mail which arrived before 20-Apr-2015 from: INBOX.Misc.lkml
>> 0004 SELECT "INBOX.Misc.lkml"
<< * FLAGS (\Answered \Flagged \Deleted \Seen \Draft NonJunk)
<< * OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft NonJunk \*)] Flags permitted.
<< * 36 EXISTS
<< * 14 RECENT
<< * OK [UNSEEN 1] First unseen.
<< * OK [UIDVALIDITY 1204617147] UIDs valid
<< * OK [UIDNEXT 6464] Predicted next UID
<< * OK [HIGHESTMODSEQ 855] Highest
<< 0004 OK [READ-WRITE] Select completed (0.370 secs).
>> 0005 UID SEARCH BEFORE 3-May-2015
<< * SEARCH 6428 6429 6430 6431
<< 0005 OK Search completed (0.034 secs).
Deleting 4 messages from INBOX.Misc.lkml
This should be put into a script of course, running over every mailing list folder I'm subscribed to. If we're confident enough that no real email folder will be purged (and our backups restores are working), a cronjob could be created too :-)