#!/usr/bin/perl
#
# Author:  Petter Reinholdtsen
# Date:    2005-12-28
# License: GNU Public License v2 or later
#
# Make it easier to report gmane messages as spam.  List all new
# messages in a given group, and let the user mark a message as spam
# with a single keypress.
#
# Configuration is in ~/.gmanespam, with content like this:
#   server news.gmane.org
#   group gmane.linux.debian.devel.initscripts-ng
#   group gmane.linux.debian.devel.java
#
# New version of this script is available from
# <URL:http://www.student.uit.no/~pere/linux/>

use warnings;
use strict;

use Net::NNTP;
use LWP::UserAgent;

my $server = "news.gmane.org";
my $spamurl = "http://spam.gmane.org/{group}:{msgnum}";

# List of groups to process
my @groups;

# Msgnum of the last visited article in the key group.
my %grouplast;

my @configfiles = ("/etc/gmanespam", $ENV{HOME}."/.gmanespam");
my $statusfile = $ENV{HOME}."/.gmanespamrc";

loadconfig();

my $nntp = Net::NNTP->new($server);

unless ($nntp) {
    print "error: Unable to connect to NNTP server '$server'.\n";
    exit 1;
}

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

loadstatus($statusfile);

for my $group (@groups) {
    process_gmane_group($group);
}

savestatus($statusfile);

$nntp->quit;

sub loadconfig {
    for my $filename (@configfiles) {
        open (CFG, "<$filename") || next;
        while (<CFG>) {
            chomp;
            s/\#.*//g;
            s/\s*$//g;
            next if (/^$/);
            $server = $1 if (m/^server (.+)$/);
            $spamurl = $1 if (m/^spamurl (.+)$/);
            push(@groups, $1) if (m/^group (.+)$/);
        }
        close(CFG);
    }
}

sub loadstatus {
    my $statusfile = shift;
    open (STATUS, "<$statusfile");
    while (<STATUS>) {
        chomp;
        my ($groupname, $lastnum) = m/^(\S+): 1-(\d+)$/;
        $grouplast{$groupname} = $lastnum;
    }
    close(STATUS);
}

sub savestatus {
    my $statusfile = shift;
    open(STATUS, ">$statusfile") || die "Unable to write to $statusfile";
    for my $groupname (sort keys %grouplast) {
        print STATUS "$groupname: 1-".$grouplast{$groupname}."\n";
    }
    close(STATUS);
}

sub process_gmane_group {
    my $group = shift;
    my ($msgcount, $msgfirst, $msglast, $groupname) = $nntp->group($group);
    my $curmsgnum = $grouplast{$groupname};

    my @spammsgs;

    print "\n$groupname: $msgfirst -> $msglast ($msgcount)\n";
    my $aktive = 1;
    while ($aktive && ++$curmsgnum <= $msglast) {
        print "\n[$curmsgnum/$msglast] ============ $groupname ===========\n";

        my $headersref = $nntp->head($curmsgnum);
        unless ($headersref) {
            print "**** Message does not exist.  Skipping.\n";
            next;
        }
        my ($spam, $subject, $from) = process_header($headersref);

        print "From:    $from\n";
        print "Subject: $subject\n";
        if ($spam) {
            print "**** Message already flagged as spam.  Ignoring.\n";
            next;
        }
        print "Non-spam/Spam/sKip/view Body/view Full/jump #/Help/Quit [N] ? ";
        my $input = <>;
        chomp $input;
        $input = "\L$input";
        if ("" eq $input || "n" eq $input) {
            # Not spam, ignore
        } elsif ("s" eq $input) {
            # Report as spam
            push(@spammsgs, $curmsgnum);
        } elsif ("q" eq $input) {
            print "Exiting\n";
            savestatus($statusfile);
            exit 0;
        } elsif ($input =~ m/^(\d+)$/) {
            $curmsgnum = $1 - 1;
            $curmsgnum = $msglast if $curmsgnum > $msglast;
            print "Jumping to message number $1\n";
            next;
	} elsif ("k" eq $input) {
	    # Skip this group and move to the next group
	    $aktive = 0;
	} elsif ("f" eq $input) {
	    # View message header and body
	    my $articleref = $nntp->article($curmsgnum);
	    list_lines($articleref);
	    $curmsgnum--;
	} elsif ("b" eq $input) {
	    # View message body
	    my $bodyref = $nntp->body($curmsgnum);
	    list_lines($bodyref);
	    $curmsgnum--;
	} elsif ("h" eq $input) {
	    # print help
        } else {
            print STDERR "error: Unhandled choice '$input'\n";
        }
    }
    if (@spammsgs) {
	print "Submit changes? [yes] ";
	my $input = <>;
	chomp $input;
	$input = "\L$input";
	if ("" eq $input || "yes" eq $input) {
	    for my $msgnum (@spammsgs) {
		report_spam($groupname, $msgnum);
	    }
	}
    }
    $grouplast{$groupname} = --$curmsgnum;
    print "Storing $curmsgnum as last read message in $groupname\n";
}

sub list_lines {
    my $arrayref = shift;
    for (@{$arrayref}) {
	print "  ", $_;
    }
}

sub process_header {
    my $headerref = shift;
    my $subject;
    my $from;
    my $spam;
    for (@{$headerref}) {
        $from = $1 if m/^From: (.*)$/i;
        $subject = $1 if m/^Subject: (.*)$/i;
        $spam = 1 if m/Xref: .*gmane.spam.detected.*$/;
    }
    return ($spam, $subject, $from)
}

sub report_spam {
    my ($groupname, $msgnum) = @_;
    my $url = $spamurl;
    $url =~ s/{group}/$groupname/g;
    $url =~ s/{msgnum}/$msgnum/g;
    my $response = $ua->get($url);
    if ($response->is_success) {
        print "Reported $groupname:$msgnum as spam.\n";
    } else {
        die $response->status_line;
    }
}
