Skip to content

Instantly share code, notes, and snippets.

@anarchivist
Created December 24, 2009 17:52
Show Gist options
  • Select an option

  • Save anarchivist/263283 to your computer and use it in GitHub Desktop.

Select an option

Save anarchivist/263283 to your computer and use it in GitHub Desktop.
Modified mail2rss script, originally by ngerakines

mail2rss.pl is a rewritten version of another script written by Nick Gerakines.

His script was a good starting block, but Feedvalidator.org noted that the RSS it produced was invalid.

Disclaimer

I am not responsible for any ill effects on the privacy or security of you or any system on which you run this script. Don't place the RSS file in a publicly accessible web directory without realizing the implications of doing so.

Modifications

Nick's version of mail2rss.pl created four major issues for the feed validator. These were:

  1. A missing version attribute in the <rss> tag. This attribute is required for a feed to be valid, and changing the code to reflect this was very basic.

  2. An invalid <guid> tag. The GUID must be a full URL unless the isPermalink is set to be false. This was also an easy fix. I also changed the GUID to be an MD5 hash of the sender and the time().

  3. An invalid URI in the <link> tag. The original specified the <link> to be "1", and that just didn't sit right with me. The link needed to begin with an IANA-registered URI scheme, so I sifted through them for a while until I decided upon the mid: URI scheme for message IDs. I had to add another variable for procmail to pull the mid and a corresponding flag for the script. The mid had to be scrubbed for spaces and other extraneous characters before mid: was prepended. I have to admit that this approach is a bit kludgy, given that it produces a link to the message referenced by the mid: URI. These links won't be handled properly by most RSS readers, and I'd dare say that they won't be handled properly by any. Nonetheless, it's a valid reference to the message in question.

  4. An invalid <pubDate>, i.e. not in RFC 822 format. The original script just made a call to time, which returns the current epoch time. After a quick Google search I came across this code snippet, which processes the call to time() to output an RFC822-compliant time.\

In addition, I cleaned up the code here and there, but nothing worth mentioning overall. If you're really curious, run diff on the two scripts.

Usage

Copy the Perl script and procmail script to the appropriate server. The example procmail script calls bmf (Bayesian Mail Filter) first, which helps to weed out the spam. After moving identified spam to the appropriate mailbox, it reads data from the message and calls the Perl script.

#!/usr/pkg/bin/perl
# http://purl.org/net/matienzo/projects/mail2rss/
# based upon http://blog.socklabs.com/2006/03/07/email_feeds_with_procmail.html
# rewritten to create valid RSS 2.0
use strict;
use warnings;
use POSIX qw(strftime);
use Getopt::Long;
use XML::Simple;
use Digest::MD5 qw(md5 md5_hex md5_base64);
my ($subject, $from, $to, $mid) = ('', '', '', '', '');
GetOptions (
"subject=s" => \$subject,
"from=s" => \$from,
"to=s" => \$to,
"mid=s" => \$mid,
);
my %XML_Encode = ('<' => '#60', '>' => '#62', '"' => '#34', "'" => '#39' );
my $XML_Encode_Pattern = join ("|", keys %XML_Encode);
my $filename = 'mail2rss.xml';
my $ref = XMLin($filename,
KeepRoot => 1,
ForceArray => [ 'item' ],
);
my $now = time();
my $tz = strftime("%z", localtime($now));
$tz =~ s/(\d{2})(\d{2})/$1:$2/;
$mid =~ s#<##;
$mid =~ s#>##;
$mid =~ s/\s//;
$mid = xmlencode('mid:'.$mid);
my $newitem = {
'link' => $mid,
'guid' => $from.time,
'title' => 'Message from '.$from,
'pubDate' => strftime("%a, %d %b %Y %H:%M:%S %z", localtime($now)),
'description' => '<p>From:'.$from.'<br/>'.'Subject:'.$subject.'</p> ',
'category' => time,
};
push @{ $ref->{'rss'}->{'channel'}->{'item'} }, $newitem;
my $oldcount = 0;
my $xml = '';
my $rss = $ref->{'rss'}->{'channel'}->{'item'};
$xml .= "<?xml version=\"1\.0\" encoding=\"utf-8\"?>\n";
$xml .= "<rss version=\"2\.0\">\n";
$xml .= "<channel>\n";
$xml .= "<title>Recent Mail Messages</title>\n";
$xml .= "<link>http://gist.github.com/263283</link>\n";
$xml .= "<language>en</language>\n";
$xml .= "<description>This is a feed of recent messages.</description>\n";
for my $item ( @{$rss} ) {
if ( $item->{'category'} && $item->{'category'} < time - (6 * 60 * 60) ) {
$oldcount++;
next;
}
$xml .= "<item>\n";
$xml .= "<title>".xmlencode($item->{'title'})."</title>\n";
$xml .= "<guid isPermaLink=\"false\">".md5_hex($item->{'guid'})."</guid>\n";
$xml .= "<link>".$item->{'link'}."</link>\n";
$xml .= "<description>".xmlencode($item->{'description'})."\n </description>\n";
$xml .= "<pubDate>".$item->{'pubDate'}."</pubDate>\n";
$xml .= "<category>".$item->{'category'}."</category>\n";
$xml .= "</item>\n";
}
if ($oldcount > 0 ) {
$xml .= "<item>\n";
$xml .= "<title>Mail cleanup</title>\n";
$xml .= "<guid isPermaLink=\"false\">".md5_hex('Message cleanup '.time)."</guid>\n";
$xml .= "<link>mailto:root\@localhost</link>\n";
$xml .= "<description>Removed $oldcount messages from the feed that were over 6 hours old.</description>\n";
$xml .= "<pubDate>".strftime("%a, %d %b %Y %H:%M:%S %z", localtime($now))."</pubDate>\n";
$xml .= "<category>".time."</category>\n";
$xml .= "</item>\n";
}
$xml .= "</channel>\n";
$xml .= "</rss>\n";
open DAT,"> $filename";
print DAT $xml;
close DAT;
sub xmlencode {
my $data = join '', map { (defined $_) ? $_ : '' } @_;
$data =~ s/($XML_Encode_Pattern)/&$XML_Encode{$1};/go;
return $data;
}
LOGFILE=${HOME}/.procmail.log
:0fw
| bmf -p
:0:
* ^X-Spam-Status: Yes
INBOX/Junk
:0
* ^From:\/.*
{
FROM="$MATCH"
}
:0
* ^Subject:\/.*
{
SUBJECT="$MATCH"
}
:0
* ^Message-ID:\/.*
{
MID="$MATCH"
}
:0
* ? test -f mail2rss.xml
{
:0c: mail2rss.lock
| mail2rss.pl --from="$FROM" --subject="$SUBJECT" --mid="$MID"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment