Files
oddmuse/scripts/rc2mail.pl

205 lines
6.4 KiB
Perl
Raw Permalink Normal View History

#! /usr/bin/perl
# Copyright (C) 20102019 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;
2009-06-05 23:34:34 +00:00
use File::Temp;
use File::Basename;
use File::Path;
use Net::SMTP;
use Authen::SASL qw(Perl);
2009-06-05 23:34:34 +00:00
# This script can be invoked as follows:
2009-06-06 16:04:39 +00:00
# perl rc2mail.pl -r http://localhost/cgi-bin/wiki \
# -p test \
2009-06-06 16:04:39 +00:00
# -m "alex:*secret*@mail.epfarms.org" \
# -f "kensanata@gmail.com" \
# -t ~/.rc2mail
2009-06-05 23:34:34 +00:00
2009-06-06 16:04:39 +00:00
# -n Don't send email; useful if debugging the script
# -p Oddmuse administrator password
# -r Oddmuse full URL, eg. http://localhost/cgi-bin/wiki
2009-06-06 23:25:20 +00:00
# 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
2009-06-06 16:04:39 +00:00
# -m user:password@mailhost for sending email using SMTP Auth. Without this
# information, the script will send mail to localhost. The host can end
# in a port number, e.g. "kensanata:*secret*@smtp.migadu.com:587"
2009-06-06 16:04:39 +00:00
# -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
2009-06-05 23:34:34 +00:00
my %opts;
2011-10-12 16:19:40 +00:00
getopts('np:r:m:f:t:qvx', \%opts);
2009-06-06 16:04:39 +00:00
my $nomail = exists $opts{n};
2009-06-06 23:14:07 +00:00
my $verbose = exists $opts{v};
my $quiet = exists $opts{q};
my $debug = exists $opts{x};
2009-06-06 16:04:39 +00:00
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};
2009-06-05 23:34:34 +00:00
my $ua = new LWP::UserAgent;
# Fetch subscribers first because we need to verify the password
sub get_subscribers {
2009-06-06 16:04:39 +00:00
my $url = "$root?action=subscriptionlist;raw=1;pwd=$admin_password";
print "Getting $url\n" if $debug;
2009-06-05 23:34:34 +00:00
my $response = $ua->get($url);
die "Must provide an admin password with the -p option\n"
2009-06-06 16:04:39 +00:00
if $response->code == 403 and not $admin_password;
2009-06-05 23:34:34 +00:00
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;
2009-06-05 23:34:34 +00:00
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;
2009-06-05 23:34:34 +00:00
return \%data;
}
2009-06-05 23:34:34 +00:00
# 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);
}
}
2009-06-05 23:34:34 +00:00
sub get_rss {
my $url = "$root?action=rss;full=1;short=0;" . get_timestamp();
print "Getting $url\n" if $debug;
2009-06-05 23:34:34 +00:00
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;
2009-06-05 23:34:34 +00:00
return $rss;
}
2009-06-05 23:34:34 +00:00
sub send_files {
my ($rss, $subscribers) = @_;
2009-06-07 18:24:57 +00:00
my @items = @{$rss->{items}};
2009-06-06 16:04:39 +00:00
die "No items to send\n" unless @items;
my $sent = 0;
foreach my $item (@items) {
2009-06-05 23:34:34 +00:00
my $title = $item->{title};
print "Looking at $title\n" if $debug;
2009-06-05 23:34:34 +00:00
my $id = $title;
$id =~ s/ /_/g;
2009-06-06 16:04:39 +00:00
my @subscribers = @{$subscribers->{$id}};
print "Subscribers: ", join(', ', @subscribers), "\n" if $debug;
2009-06-06 16:04:39 +00:00
$sent += @subscribers;
send_file($id, $title, $item, @subscribers);
2009-06-05 23:34:34 +00:00
}
print "$sent messages sent\n" if $sent and not $quiet;
2009-06-05 23:34:34 +00:00
}
2009-06-05 23:34:34 +00:00
sub send_file {
2009-06-06 16:04:39 +00:00
my ($id, $title, $item, @subscribers) = @_;
return unless @subscribers;
2009-06-06 16:04:39 +00:00
my $fh = File::Temp->new(SUFFIX => '.html');
2012-07-19 10:53:13 +02:00
binmode($fh, ":utf8");
2009-06-06 16:04:39 +00:00
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>)
2009-06-06 16:04:39 +00:00
. 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;
2009-06-06 16:04:39 +00:00
$fh->close;
2009-06-05 23:34:34 +00:00
foreach my $subscriber (@subscribers) {
send_mail($subscriber, $title, $fh);
}
}
2009-06-05 23:34:34 +00:00
sub send_mail {
my ($subscriber, $title, $fh) = @_;
print "Skipping mail to $subscriber...\n" if $debug && $nomail;
2009-06-06 23:14:07 +00:00
return if $nomail;
2009-06-05 23:34:34 +00:00
my $mail = new MIME::Entity->build(To => $subscriber,
2009-06-06 16:04:39 +00:00
From => $from,
2009-06-05 23:34:34 +00:00
Subject => $title,
Path => $fh,
Type=> "text/html");
2009-06-06 16:04:39 +00:00
if ($host) {
print "$root\nSending $title to $subscriber using ${user}\@${host}\n" if $verbose;
my $smtp = Net::SMTP->new($host, Debug => $debug);
$smtp->starttls();
# the following requires Authen::SASL!
$smtp->auth($user, $password);
$smtp->mail($from);
if ($smtp->to($subscriber)) {
$smtp->data;
$smtp->datasend($mail->stringify);
$smtp->dataend;
2009-06-06 16:04:39 +00:00
} else {
warn "Error: ", $smtp->message();
2009-06-06 16:04:39 +00:00
}
$smtp->quit;
2009-06-05 23:34:34 +00:00
}
}
2009-06-05 23:34:34 +00:00
sub main {
2009-06-06 16:04:39 +00:00
my $rss = get_rss();
2018-06-22 13:00:51 +02:00
if (@{$rss->{items}}) {
my $subscribers = get_subscribers();
if (%{$subscribers}) {
send_files($rss, $subscribers);
}
}
2012-08-14 09:05:22 -04:00
update_timestamp();
2009-06-05 23:34:34 +00:00
}
2009-06-05 23:34:34 +00:00
main ();