Email to Wiki Gateway

I, Ry4an Brase, wrote a quick little gateway script that takes emails and automatically creates/appends to the specified wiki page. When the script receives an email (on standard input) with a To: or Cc: line containing an email address of the format: wiki-PageName@example.com

The Subject:, Date:, From:, and body info are appended to PageName. It optionally supports using cookies to login to the wiki.

Many MTAs support the ability to allow arbitrary information after the user portion of an email address. In qmail any user with a .qmail-default file in their home directory will receive any email of the format theirusername-xxxxx@example.com. In postfix one need simply set the recipient_delimiter to '-' in the config file to enable this feature. In both cases one must create a system user named 'wiki'.

Sendmail, by default, uses something of the form: theiruser+xxxxx@example.com. In those cases you should change the script's regular expression from /wiki-(\w+)\@/ to /wiki\+(\w+)\@/.

You shouldn't need to create a dedicated system account; if you control the MTA an entry in the aliases file like:

wiki:               |/usr/local/bin/wikimail

works nicely. This assumes the MTA strips the delimiter before doing the lookups in the aliases file (as Exim can do). This method also doesn't need to involve procmail.

I invoke it using a procmail recipe (in ~wiki/.procmailrc) like this:

    :0c
    * ^TO.*wiki-
    | /home/wiki/wikimail.pl

I'm sure what the script does could've been done more elegantly using WikiRpc or the ?action=raw retrieval method, but this seems to work pretty well and only took an hour to write. I know this place is more about python than perl, but hey, "When all you have is a swiss army chainsaw, every..."

# Takes an email as input and appends it to a wiki page
# The inbound email should have a To: or Cc: address of the form:
#
#               wiki-PageName@example.com
#
# Be sure to update the wikiUrl definition below
#
# Ry4an Brase <ry4an at ry4an dot org>
# This script is placed into the public domain.  All rights waived.

use strict;
use LWP::UserAgent;
use HTTP::Request::Common qw(POST);
use HTTP::Cookies;

my $wikiUrl = 'http://your.hostname .com/path-to-wiki'; # UPDATE this

my $body = "";
my $page;
my $title = 'Untitled';
my $from = 'Unknown';
my $isBody = 0;

while (<>) {
    if ($isBody) {
        $body .= $_;
        next;
    }
    if (/wiki-(\w+)\@/) {
        $page = $1;
        next;
    }
    if (/^Subject:\s*(.*?)\s*$/) {
        $title = $1;
        next;
    }
    if (/^From:\s*(.*?)\s*$/) {
        $from= $1;
        next;
    }
    $isBody = 1 if (/^\s*$/);
}
unless ($page) {
    print STDERR "No email address specifying page found\n";
    exit;
}

my $ua = LWP::UserAgent->new;

# Uncomment this next line if you have created a wiki user account for the
# gateway and have created a .cookies.txt file with the moin ID for that
# account.  For detail on how that's done check out the HTTP::Cookies man page.
#
# $ua->cookie_jar(HTTP::Cookies->new('file' => "$ENV{'HOME'}/.cookies.txt"));

my $req = HTTP::Request->new(GET => "$wikiUrl/$page?action=edit");
my $res = $ua->request($req);

unless ($res->is_success) {
    print STDERR "Unable to fetch wiki page '$page': $!\n";
    exit;
}   my ($when, $oldBody);

if ($res->content =~ /<input type="hidden" name="datestamp" value="(\d+)">/s) {
    $when = $1;
} else {
    print STDERR "Unable to extract datestamp from edit page.\n";
    exit;
}

if ($res->content =~ /<textarea [^>]*>(.*)<\/textarea>/s) {
    $oldBody = $1;
    if ($oldBody eq "Describe $page here.") {
        $oldBody = "";
    }
} else {
    print STDERR "Unable to extract old body from edit page: ", $res->content;
    exit;
}

while ($oldBody =~ s/&amp;/&/g) {}
while ($oldBody =~ s/&gt;/>/g) {}
while ($oldBody =~ s/&lt;/</g) {}

$req = POST "$wikiUrl/$page", [ 'action' => 'savepage', 'datestamp' => $when,
        'button_save' => 'Save Changes', 'notify' => 1,
        'comment' => 'gatewayed from email', 'savetext' =>
        "$oldBody\n== $title ==\nPosted " . localtime()
            . " by $from\n{{{\n$body\n

\n"];# $req->content_type('application/x-www-form-urlencoded'); $res = $ua->request($req);

unless ($res->is_success) {

}

}}}

If someone using this feature reimplements this in Python and tests it, we might consider including it in moin distribution. We don't want to maintain Perl code, of course. -- ThomasWaldmann 2006-01-14 01:24:15

MoinMoin: ScriptMarket/EmailToWikiGateway (last edited 2007-10-29 19:20:35 by localhost)