I've used Eudora for the Mac for nearly a decade. After all this time, it's still the best mailer I've found. For the last few years, it has had a fairly robust filtering feature that made it easy to deal with the volume of mail I receive. But I've passed a certain point. I now have so many filter rules that it's hard to keep them organized. And I've started to read my mail from multiple locations using IMAP. It's a pain to keep my Eudora Filters synchronized on my Mac at home and my Mac at work. I also use other IMAP mail readers when I need to check my mail and I'm not at home or at work.
Enter procmail . Procmail runs on my mail server and makes it "easy" for me to write filter rules to categorize my mail and to organize these rules in various ways. For example, I put all of the rules relating to mailinglists in one file, and all of the rules relating to individual correspondents in another file, each sorted alphabetically.
The drawback? Eudora has a feature which allows it to open a mailbox window whenever it filters mail into that mailbox. Without that feature, it's hard to notice that you have even received new mail, and annoying to have to open the mailbox and read the mail. Eudora could use IMAP subscriptions or periodic checks to do the same thing when a different program puts mail into a mailbox, but the folks at Qualcomm haven't seen fit to include that feature yet. Maybe if more people make a feature request for it, they'll include it in a future version...
Loath to give up this feature, I recently realized that I could get a close approximation of it with some programming glue. Procmail creates a log that notes when it puts mail into a mailbox. I have a server process sitting on my mailserver and watching that log. When a client connects, the server informs it of all of the new messages that have arrived since the last time a client checked in and where they've been filtered to. The client then calls an AppleScript that sends a message to Eudora, telling it to open the appropriate mailboxes.
#!/usr/bin/perl # change these two lines $server_port = 1234; $identification_phrase = 'insert phrase here'; # redirect error messages to a logfile open(STDERR, ">>/tmp/logserver.log"); # code stolen liberally from Perl Cookbook use IO::Socket; $logfile = $ARGV[0]; open(LOG, "<$logfile"); # read to the current end of the log while(<LOG>) { } $server = IO::Socket::INET->new(LocalPort => $server_port, Type => SOCK_STREAM, Reuse => 1, Listen => 10 ) # or SOMAXCONN or exit; # "Couldn't be a tcp server on port $server_port : $@\n"; while (($client,$client_address) = $server->accept()) { ($client_port, $client_ip) = unpack_sockaddr_in($client_address); @decimal_address = unpack("C4", $client_ip); $ip_number = join(".", @decimal_address); warn "connection from $ip_number"; while(<$client>) { last if /$identification_phrase/; } warn "connection from $ip_number gave pass phrase"; $lines = 0; while(<LOG>) { $lines++; print $client $_; } warn "returned $lines lines to $ip_number"; close($client); } close($server);
#!/usr/bin/perl # make these match the server $remote_host = 'my.mail.server' $remote_port = 1234; $identification_phrase = 'insert phrase here'; $open_mailbox_binary = '/Users/aneel/bin/open_mailbox.pl'; # redirect error messages to a logfile open(STDERR,">>/tmp/logclient.log"); # make sure Eudora is running unless ($running = `ps auxwww | grep Eudora | grep -v grep`) { warn "Eudora NOT running at " . localtime() . "\n"; exit; } # code stolen liberally from Perl Cookbook use IO::Socket; $socket = IO::Socket::INET->new(PeerAddr => $remote_host, PeerPort => $remote_port, Proto => "tcp", Type => SOCK_STREAM) or die "Couldn't connect to $remote_host:$remote_port : $@\n"; print $socket "$identification_phrase\n"; while (<$socket>) { #warn $_; chomp; if (m/^\s*From\s*(.*)/) { $from = substr($1, 0, 60); } elsif (m/^\s*Subject:\s*(.*)/) { $subject = substr($1, 0, 60); } else { $folder = ""; if (m{^\s*Folder: /var/mail/}) { $folder = 'Inbox'; } elsif (m{^\s*Folder:\s+(.*/)?([^/]*[^\s/])\s+\d+}) { $folder = $2; } if ($folder) { # From turns out to be useless, in this day of mailman # lists. Everything gets listed as coming from list-admin # or some such nonsense #$folders{$folder} .= " From: $from\n Subject: $subject\n"; $folders{$folder} .= " $subject\n"; } } } if (%folders) { warn "\n" . localtime() . "\n"; foreach (sort keys %folders) { warn "$_\n$folders{$_}"; } foreach (sort keys %folders) { if ($_ ne 'spam') { system($open_mailbox_binary, $_); } } }
It would be cool if this were just an AppleScript, but it turns out that the way to call AppleScripts from the command line is somewhat broken. From the osascript manpage:
BUGS osascript does not yet provide any way to pass arguments to the script.So I've constructed a perl script that makes a custom AppleScript and calls it:
#!/usr/bin/perl open(STDERR, ">>/tmp/logclient.log"); foreach $mailbox (@ARGV) { $script = <<SCRIPT; on findMailboxNamed(parentFolder, target) tell application "Eudora" repeat with x from 1 to count every mailbox of parentFolder if (name of mailbox x of parentFolder) is equal to target then return (a reference to mailbox x of parentFolder) end if end repeat repeat with x from 1 to count every mail folder of parentFolder set subFind to my findMailboxNamed((a reference to mail folder x of parentFolder), target) if subFind is not false then return subFind end if end repeat end tell return false end findMailboxNamed tell application "Eudora" set M to my findMailboxNamed((a reference to mail folder "ÇDominantÈ"), "$mailbox") close M open M end tell SCRIPT @script = split("\n", $script); @script = map { "-e '$_'"} @script; system('/usr/bin/osascript ' . join(' ', @script)); }I'm sure my AppleScript idiom sucks. This is the only AppleScript I've ever written, and the process of writing it was fairly painful.
The server runs on the mailserver that procmail runs on. I have it in my crontab so that, if it dies it gets restarted. Here's the cron entry:
0 * * * * /home/nazareth/mac/logserver.pl /home/nazareth/procmail/log
The client runs on all of the Macs that I run Eudora on. I have it in my crontab to run every 5 minutes. Here's the cron entry:
*/5 * * * * /Users/aneel/bin/logclient.pl
The mailbox opener gets called by the client.