/aux/cgi/rss.cgi
#!/usr/bin/perl
use CGI::Fast qw/:cgi/;
use Blerg::Database;
use URI::Escape;
use POSIX qw/strftime/;
use MIME::Base64;
use strict;
my $baseurl = Blerg::Database::BASEURL;
sub xml_escape {
local $_ = shift;
s/&/&/g;
s/</</g;
s/>/>/g;
return $_;
}
sub decode_basic_auth {
my ($q) = @_;
my ($method, $base64) = split(/\s+/, $q->http('Authorization'));
if (!defined $method) {
return;
} elsif ($method ne 'Basic') {
return;
}
my ($username, $password) = split(':', decode_base64($base64), 2);
if (!defined $username) {
return;
}
return ($username, $password);
}
sub print_401 {
print header(-type => 'text/html',
-status => '401 Unauthorized',
-WWW_Authenticate => 'Basic realm="blerg"');
print <<DOC;
<!DOCTYPE html>
<h1>401 Unauthorized</h1>
Please log in.
DOC
}
sub print_404 {
print header(-type => 'text/html',
-status => '404 Not Found');
print <<DOC;
<!DOCTYPE html>
<h1>404 Not Found</h1>
Not Found
DOC
}
sub fetch_records {
my @out;
local $_;
foreach (@_) {
my $b = Blerg::Database->open_existing($_->{author});
my $data = $b->fetch($_->{record});
if (!defined $data) {
$b->close;
next;
}
my $timestamp = $b->timestamp($_->{record});
$b->close;
push @out, {
author => $_->{author},
record => $_->{record},
data => $data,
timestamp => $timestamp,
};
}
return @out;
}
sub print_rss {
my ($type, $name, @items) = @_;
my ($title, $link);
if ($type eq 'user') {
$title = "${name}'s blërg";
$link = "${baseurl}#$name";
} elsif ($type eq 'feed') {
$title = "${name}'s stalking feed";
$link = "${baseurl}#/feed";
} elsif ($type eq 'tag' || $type eq 'ref') {
$title = $name;
my $basename = $name;
$basename =~ s/^.//;
$link = "${baseurl}#/$type/$basename";
}
print <<HEADER;
<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0">
<channel>
<title>$title</title>
<link>$link</link>
<description>Textual vomit</description>
HEADER
for my $i (@items) {
my $author = defined $i->{author} ? $i->{author} : $name;
my $data = xml_escape(qq{<a href="${baseurl}#$author"><strong>\@$author</strong></a><br> $i->{data}});
my $title = xml_escape(substr($i->{data}, 0, 27)) . "...";
my $post_time = strftime("%a, %d %b %Y %H:%M:%S %Z", localtime($i->{timestamp}));
print <<ITEM;
<item>
<title>$title</title>
<pubDate>$post_time</pubDate>
<guid>${baseurl}get/$author/$i->{record}</guid>
<link>${baseurl}#$author/$i->{record}</link>
<description>$data</description>
</item>
ITEM
}
print <<'FOOTER'
</channel>
</rss>
FOOTER
}
REQUEST:
while (my $q = new CGI::Fast) {
$q->charset('utf8');
my @path = split('/', $ENV{PATH_INFO});
shift @path;
if ($path[0] eq 'feed') {
my ($username, $password) = decode_basic_auth($q);
if (!defined $username) {
print_401;
next REQUEST;
}
if (!Blerg::Database::auth_check_password($username, $password)) {
print_401;
next REQUEST;
}
my $b = Blerg::Database->open_existing($username);
my @list = fetch_records($b->subscription_list());
$b->close;
print header(-type => 'application/rss+xml');
print_rss(feed => $username, @list);
} elsif (@path == 1) {
# Assume this is a username; redirect to /user/<username>
my $username = $path[0];
my $b = Blerg::Database->open_existing($username);
if (!defined $b) {
print_404;
next REQUEST;
}
print header(-type => 'application/rss+xml',
-charset => 'utf8',
-status => '301 Moved Permanently',
-location => "${baseurl}rss/user/$username");
# And present the content in case their client is broken
my $i = {
record => '?failed_redirect',
timestamp => time,
data => qq{Your RSS aggregator is dumb and isn't following 301 redirects. Please manually redirect it here: ${baseurl}rss/user/$username}
};
print_rss(user => $username, $i);
} elsif ($path[0] eq 'user') {
my $username = $path[1];
my $b = Blerg::Database->open_existing($username);
if (!defined $b) {
print_404;
next REQUEST;
}
print header(-type => 'application/rss+xml');
my $n = $b->record_count - 1;
my @list = reverse map {
{
record => $_,
data => $b->fetch($_),
timestamp => $b->timestamp($_),
}
} ($n > 50 ? $n - 50 : 0)..$n;
$b->close;
print_rss(user => $username, @list);
} elsif ($path[0] eq 'tag' || $path[0] eq 'ref') {
my $tag = $path[1];
my $atag;
if ($path[0] eq 'tag') {
$atag = '#' . $tag;
} else {
$atag = '@' . $tag;
}
my @list = fetch_records(Blerg::Database::tag_list($atag, 0, -1));
print header(-type => 'application/rss+xml');
print_rss($path[0] => $atag, @list);
}
}