Files
oddmuse/scripts/rc2mail.pl
2018-10-07 18:15:30 +02:00

219 lines
6.7 KiB
Perl
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#! /usr/bin/perl
# Copyright (C) 20102018 Alex Schroeder <alex@gnu.org>
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
package OddMuse;
use Getopt::Std;
use XML::RSS;
use LWP::UserAgent;
use MIME::Entity;
use File::Temp;
use File::Basename;
use File::Path;
# This script can be invoked as follows:
# perl rc2mail.pl -r http://localhost/cgi-bin/wiki \
# -p test \
# -m "alex:*secret*@mail.epfarms.org" \
# -f "kensanata@gmail.com" \
# -t ~/.rc2mail
# -n Don't send email; useful if debugging the script
# -p Oddmuse administrator password
# -r Oddmuse full URL, eg. http://localhost/cgi-bin/wiki
# gets http://localhost/cgi-bin/wiki?action=rss;days=1;full=1;short=0
# And http://localhost/cgi-bin/wiki?action=subscriptionlist;raw=1;pwd=foo
# -m user:password@mailhost for sending email using SMTP Auth. Without this
# information, the script will send mail to localhost.
# -f email address to use as the sender.
# -t timestamp file; it's last modified date is used to determine when the
# the last run was and an appropriate URL is used. Instead of days=1 it
# will use from=n where n is the last modified date of the timestamp file.
# -q quiet (default: number of messages sent)
# -v verbose output (recipients)
# -x debug output
my %opts;
getopts('np:r:m:f:t:qvx', \%opts);
my $nomail = exists $opts{n};
my $verbose = exists $opts{v};
my $quiet = exists $opts{q};
my $debug = exists $opts{x};
my $admin_password = $opts{p};
my $root = $opts{r};
die "Must provide an url with the -r option\n" unless $root;
$opts{m} =~ /(.*?):(.*)\@(.*)/;
my ($user, $password, $host) = ($1, $2, $3);
die "Cannot parse -m " . $opts{m} . "\n" if $opts{m} && !$host;
my $from = $opts{f};
die "Must provide sender using -f\n" if !$nomail && $host && !$from;
my $ts = $opts{t};
my $ua = new LWP::UserAgent;
# Fetch subscribers first because we need to verify the password
sub get_subscribers {
my $url = "$root?action=subscriptionlist;raw=1;pwd=$admin_password";
print "Getting $url\n" if $debug;
my $response = $ua->get($url);
die "Must provide an admin password with the -p option\n"
if $response->code == 403 and not $admin_password;
die "Must provide the correct admin password with the -p option\n"
if $response->code == 403;
die $url, "\n", $response->status_line unless $response->is_success;
my %data;
foreach my $line (split(/\n/, $response->content)) {
my ($key, @entries) = split(/ +/, $line);
# print "Subscription for $key: ", join(', ', @entries), "\n";
$data{$key} = \@entries;
}
print "Found " . scalar(keys(%data)) . " subscribers\n" if $debug;
return \%data;
}
# Fetch RSS feed
sub get_timestamp {
if ($ts and -f $ts) {
return "from=" . (stat($ts))[9];
} else {
return "days=1";
}
}
sub update_timestamp {
# Only update timestamps if $ts is provided.
return unless $ts;
if (-f $ts) {
# File exists: update timestamp.
utime undef, undef, $ts;
} else {
# File does not exist: create it. File content is ignored on the
# next run!
my $dir = dirname($ts);
mkpath($dir) unless -d $dir;
open(F, ">$ts") or warn "Unable to create $ts: $!";
close(F);
}
}
sub get_rss {
my $url = "$root?action=rss;full=1;short=0;" . get_timestamp();
print "Getting $url\n" if $debug;
my $response = $ua->get($url);
die $url, $response->status_line unless $response->is_success;
my $rss = new XML::RSS;
$rss->parse($response->content);
print "Found " . @{$rss->{items}} . " items.\n" if $debug;
return $rss;
}
sub send_files {
my ($rss, $subscribers) = @_;
my @items = @{$rss->{items}};
die "No items to send\n" unless @items;
my $sent = 0;
foreach my $item (@items) {
my $title = $item->{title};
print "Looking at $title\n" if $debug;
my $id = $title;
$id =~ s/ /_/g;
my @subscribers = @{$subscribers->{$id}};
print "Subscribers: ", join(', ', @subscribers), "\n" if $debug;
$sent += @subscribers;
send_file($id, $title, $item, @subscribers);
}
print "$sent messages sent\n" if $sent and not $quiet;
}
sub send_file {
my ($id, $title, $item, @subscribers) = @_;
return unless @subscribers;
my $fh = File::Temp->new(SUFFIX => '.html');
binmode($fh, ":utf8");
warn "No content for $title\n" unless $item->{description};
my $link = $item->{link};
my $sub = "$root?action=subscriptions";
my $text = qq(<p>Visit <a href="$link">$title</a>)
. qq( or <a href="$sub">manage your subscriptions</a>.</p><hr />)
. $item->{description};
# prevent 501 Syntax error - line too long
$text =~ s/<(p|h[1-6]|[duo]l|pre|li|form|div|blockquote|hr|table|tr)>/\r\n<$1>/gi;
print $fh $text;
$fh->close;
foreach my $subscriber (@subscribers) {
send_mail($subscriber, $title, $fh);
}
}
sub send_mail {
my ($subscriber, $title, $fh) = @_;
print "Skipping mail to $subscriber...\n" if $debug && $nomail;
return if $nomail;
my $mail = new MIME::Entity->build(To => $subscriber,
From => $from,
Subject => $title,
Path => $fh,
Type=> "text/html");
if ($host) {
print "Sending $title to $subscriber using ${user}\@${host}\n" if $verbose;
eval {
require Net::SMTP::TLS;
my $smtp = Net::SMTP::TLS->new($host,
User => $user,
Password => $password);
$smtp->mail($from);
$smtp->to($subscriber);
$smtp->data;
$smtp->datasend($mail->stringify);
$smtp->dataend;
$smtp->quit;
};
if ($@) {
require Net::SMTP::SSL;
my $smtp = Net::SMTP::SSL->new($host, Port => 465);
$smtp->auth($user, $password);
$smtp->mail($from);
$smtp->to($subscriber);
$smtp->data;
$smtp->datasend($mail->stringify);
$smtp->dataend;
$smtp->quit;
}
} else {
my @recipients = $mail->smtpsend();
if (@recipients) {
print "Sent $title to ", join(', ', @recipients), "\n" unless $quiet;
} else {
print "Failed to send $title to $subscriber\n" unless $quiet;
}
}
}
sub main {
my $rss = get_rss();
if (@{$rss->{items}}) {
my $subscribers = get_subscribers();
if (%{$subscribers}) {
send_files($rss, $subscribers);
}
}
update_timestamp();
}
main ();