#!perl

use strict;
use warnings;

use XML::RSS::Parser;
use LWP::Simple;
use Data::Dumper;
use Fcntl;
use DB_File;
use Template;
use POSIX;
use Net::SMTP;
use YAML qw{LoadFile};

die "Usage: $0 config.yaml\n" unless defined $ARGV[0];
my $conf   = $ARGV[0];
my %config = %{ LoadFile($conf) }
  or die "Error: failed to load config file $conf: $!";

my $ttk = Template->new();

my %emailed;
tie( %emailed, 'DB_File', $config{'db'}, O_RDWR | O_CREAT, 0, $DB_BTREE )
  or die "can't tie DB_File: $!";

while (1) {
    my @items = map { &get_items($_) } @{ $config{'searches'} };
    for (@items) {

        # get page - pull out and decode email address
        my $page = get( ${$_}{'url'} );
        $page =~ /Reply to: <a href="mailto:(.+?)"/;
        my $email = $1;
        $email =~ s/&#(\d+);/chr($1)/ge;
        $email =~ s/\?.+//;

        # send email
        next if $emailed{ ${$_}{'title'} };
        my %vars = (
	        'msgid'   => &msgid($config{'from'}),
            'subject' => ${$_}{'title'},
            'from'    => $config{'from'},
            'to'      => $email,
            'date'    => strftime( '%c %z', localtime ),
            'page'    => $page,
        );
        my ( $msg_seller, $msg_buyer );
        $ttk->process( $config{'message_seller'}, \%vars, \$msg_seller );
        $ttk->process( $config{'message_buyer'},  \%vars, \$msg_buyer );

        print "${$_}{'title'}\n${$_}{'url'}\n$email\n\n";

        next unless &mail( $config{'from'}, $email, $msg_seller );
        &mail( $config{'from'}, $config{'from'}, $msg_buyer );

        # record title in cache
        $emailed{ ${$_}{'title'} } = 1;
    }

    sleep( $config{'interval'} );
}

# msgid: scalar -> scalar
# given an email address, generates a Message-ID
sub msgid {
	my ($email) = @_;
	$email =~ /(\@.+)/;
	my $dom = $1;

	return &random_string(8) . "." &random_string(7) . $dom;
}

# random_string: scalar -> scalar
# generates a random string of letters and numbers of the given length
sub random {
	my @c = (0 .. 9, 'A' .. 'Z');
	my $str;
	for (0 .. shift) {
		$str .= $c[rand @c];
	}
	return $str;
}

# mail: scalar, scalar, scalar
# send the given email message to the given address using the given return address
# uses global variable $config{'mx'} as the SMTP relay
# returns 1 if success, 0 if failure
sub mail {
    my ( $from, $to, $message ) = @_;

    if ( my $smtp =
        Net::SMTP->new( $config{'mx'}, Hello => $config{'mx'}, Timeout => 30 ) )
    {
        $smtp->mail($from);
        $smtp->recipient($to);
        $smtp->data;
        $smtp->datasend($message);
        $smtp->dataend;
        $smtp->quit;
        return 1;
    }
    else {
        return 0;
    }
}

# get_items: scalar -> list
# given an RSS url, returns a list of  the item titles and URLs
sub get_items {
    my ($url) = @_;

    my @items;

    my $p    = XML::RSS::Parser->new;
    my $feed = $p->parse_uri($url);

    foreach my $i ( $feed->query('//item') ) {
        my $node_title = $i->query('title');
        my $node_link  = $i->query('link');

        my %item = (
            'title' => $node_title->text_content,
            'url'   => $node_link->text_content,
        );

        push ( @items, \%item );
    }

    return @items;
}

