usevote/UVsendmail.pm
Thomas Hochstein ac7e2c541a Initial checkin of upstream version 4.09.
Signed-off-by: Thomas Hochstein <thh@inter.net>
2010-08-16 22:16:26 +02:00

337 lines
11 KiB
Perl

# UVsendmail: functions for sending mails
# Used by uvvote.pl, uvcfv.pl
package UVsendmail;
use strict;
use UVconfig;
use UVtemplate;
use MIME::Words;
use Text::Wrap qw(wrap $columns);
# Set columns for Text::Wrap
$columns = $config{rightmargin};
use vars qw($VERSION);
# Module version
$VERSION = "0.9";
my $num = 0;
##############################################################################
# generation of acknowledge and error mails (don't sends them out yet) #
# each mail is saved in a different file and a control file containing #
# filename and envelope-to address is generated. #
# Parameters: mail address, fixed part of subject and body of mail (strings) #
##############################################################################
sub mail {
my ($addr, $subject, $text, $reference, $replyto) = @_;
# address set?
if ($addr) {
# generate mail to sender
my $template = UVtemplate->new();
$template->setKey('from' => mimeencode($config{mailfrom}));
$template->setKey('subject' => mimeencode("$config{votename} - $subject"));
$template->setKey('address' => $addr);
$template->setKey('reference' => $reference) if ($reference);
$template->setKey('reply-to' => $replyto) if ($replyto);
$template->setKey('usevote-version' => $usevote_version);
my $message = $template->processTemplate($config{'tpl_mailheader'});
$message .= "\n" . $text;
# get envelope-to addresses
my $envaddr = $addr;
$envaddr .= " $config{mailcc}" if ($config{mailcc});
my $mailfile = '';
# search for file names
do {
$num++;
$mailfile = "$config{tmpdir}/ack.$num";
} while (-e $mailfile);
# write mail in a file and append a line at the control file
open (CONTROL, ">>$config{controlfile}") or print STDERR "\n\n",
UVmessage::get("SENDMAIL_ERROPENCONTROL", (FILE => $config{controlfile})), "\n";
print CONTROL "$mailfile\t$envaddr\n";
close (CONTROL) or print STDERR "\n\n",
UVmessage::get("SENDMAIL_ERRCLOSECONTROL", (FILE => $config{controlfile})), "\n";
open (MAIL, ">$mailfile") or print STDERR "\n\n",
UVmessage::get("SENDMAIL_ERROPENMAIL", (FILE => $config{controlfile})), "\n";
print MAIL $message;
close (MAIL) or print STDERR "\n\n",
UVmessage::get("SENDMAIL_ERRCLOSEMAIL", (FILE => $config{controlfile})), "\n";
}
}
##############################################################################
# Send previously generated acknowledge or error mails. #
# Depending on configuration mails are piped to your MTA or send via SMTP. #
##############################################################################
sub send {
unless (-e $config{controlfile}) {
print "\n", UVmessage::get("SENDMAIL_NOMAILS", (FILE => $config{controlfile})),
"\n\n";
return 0;
}
open (CONTROL, "<$config{controlfile}") or die "\n\n",
UVmessage::get("SENDMAIL_ERROPENCONTROL", (FILE => $config{controlfile})), "\n";
my @mailinfo = <CONTROL>;
close (CONTROL);
print UVmessage::get("SENDMAIL_SENDING"), "\n";
if ($config{smtp}) {
# send mails via SMTP
use Net::SMTP;
my $smtp = Net::SMTP->new("$config{smtpserver}:$config{smtpport}",
Hello => $config{smtphelo});
die UVmessage::get("SENDMAIL_SMTP_CONNREFUSED") . "\n\n" unless ($smtp);
if ($config{smtpauth}) {
$smtp->auth($config{smtpuser}, $config{smtppass})
or die UVmessage::get("SENDMAIL_SMTP_CONNREFUSED") . "\n" .
$smtp->code() . ' ' . $smtp->message() . "\n";
}
my $errors = 0;
my $missingfiles = 0;
open (CONTROL, ">$config{controlfile}") or die "\n\n",
UVmessage::get("SENDMAIL_ERROPENCONTROL", (FILE => $config{controlfile})), "\n";
foreach my $mail (@mailinfo) {
chomp ($mail);
next unless $mail;
my ($file, $envelope) = split(/\t/, $mail);
my $notfound = 0;
open (MAIL, "<$file") or $notfound = 1;
if ($notfound) {
print STDERR UVmessage::get("SENDMAIL_ERRNOTFOUND") . "\n";
$missingfiles++;
next;
}
my $message = join('', <MAIL>);
close (MAIL);
next unless $message;
$smtp->reset();
$smtp->mail($config{envelopefrom});
unless ($smtp->ok()) {
print STDERR UVmessage::get("SENDMAIL_SMTP_INVRCPT", (RCPT => $envelope)),
"\n", $smtp->code(), ' ', $smtp->message(), "\n";
$errors++;
next;
}
my $onesent = 0;
my $onefail = 0;
foreach my $addr (split(/ +/, $envelope)) {
$smtp->to($addr);
if ($smtp->ok()) {
$onesent = 1;
} else {
print CONTROL ($onefail ? " " : "$file\t");
print CONTROL $addr;
print STDERR UVmessage::get("SENDMAIL_SMTP_INVRCPT", (RCPT => $envelope)),
"\n", $smtp->code(), ' ', $smtp->message(), "\n";
$errors++;
$onefail = 1;
next;
}
}
print CONTROL "\n" if ($onefail);
next unless $onesent;
$smtp->data();
if ($smtp->ok()) {
$smtp->datasend($message);
$smtp->dataend();
}
unless ($smtp->ok()) {
print STDERR UVmessage::get("SENDMAIL_SMTP_INVRCPT", (RCPT => $envelope)),
"\n", $smtp->code(), ' ', $smtp->message(), "\n";
$errors++;
next;
}
unlink ($file) unless ($onefail);
}
$smtp->quit();
close (CONTROL) or die "\n\n",
UVmessage::get("SENDMAIL_ERRCLOSECONTROL", (FILE => $config{controlfile})), "\n";
if ($errors) {
print STDERR "\n".wrap('', '', "$errors ".UVmessage::get("SENDMAIL_ERROCCURED"))."\n\n";
}
if ($missingfiles) {
print STDERR wrap('', '', "$missingfiles " .
UVmessage::get("SENDMAIL_MISSINGFILES")), "\n\n";
}
} else {
foreach my $mail (@mailinfo) {
next unless $mail;
chomp($mail);
my ($file, @rcpt) = split(/\s+/, $mail);
open (DOMAIL, ">>$config{domailfile}");
print DOMAIL "$config{mailcmd} ";
foreach my $rcpt (@rcpt) {
print DOMAIL "'$rcpt' ";
}
print DOMAIL "<$file && rm $file ; $config{sleepcmd}\n";
close (DOMAIL)
or print STDERR "\n\n", UVmessage::get("SENDMAIL_ERRCLOSEDOMAIL"), "\n";
}
chmod(0700, $config{domailfile});
system($config{domailfile});
}
opendir (DIR, $config{tmpdir});
my @files = grep (/^ack\.\d+/, readdir (DIR));
closedir (DIR);
return 0 if (@files);
unlink $config{controlfile} or print STDERR "\n\n",
UVmessage::get("SENDMAIL_ERRDELCONTROL", (FILE => $config{controlfile})), "\n";
unless ($config{smtp}) {
unlink $config{domailfile} or print STDERR "\n\n",
UVmessage::get("SENDMAIL_ERRDELCONTROL", (FILE => $config{domailfile})), "\n";
}
}
##############################################################################
# Encodes a string for use in mail headers #
# #
# Parameters: $text = string to encode. #
# Returns: $newtext = encoded string. #
##############################################################################
sub mimeencode {
my ($text) = @_;
my @words = split(/ /, $text);
my $line = '';
my @lines;
foreach my $word (@words) {
my $sameword = 0;
$word =~ s/\n//g;
my $encword;
if ($word =~ /[\x7F-\xFF]/) {
$encword = MIME::Words::encode_mimeword($word, 'Q', 'ISO-8859-1');
} elsif (length($word) > 75) {
$encword = MIME::Words::encode_mimeword($word, 'Q', 'us-ascii');
} else {
$encword = $word;
}
# no more than 75 chars per line allowed
if (length($encword) > 75) {
while ($encword) {
if ($encword =~ /(^=\?[-\w]+\?\w\?)(.{55}.*?)((=.{2}|[^=]{3}).*\?=)$/) {
addword($1 . $2 . '?=', \$line, \@lines, $sameword);
$encword = $1 . $3;
} else {
addword($encword, \$line, \@lines, $sameword);
$encword = '';
}
$sameword = 1;
}
} else {
addword($encword, \$line, \@lines, $sameword);
}
}
my $delim = (@lines) ? ' ' : '';
push(@lines, $delim . $line) if ($line);
return join('', @lines);
}
##############################################################################
# Adds a word to a MIME encoded string, inserts linefeed if necessary #
# #
# Parameters: #
# $word = word to add #
# $line = current line #
# $lines = complete text (without current line) #
# $sameword = boolean switch, indicates that this is another part of #
# the last word (for encoded words > 75 chars) #
##############################################################################
sub addword {
my ($word, $line, $lines, $sameword) = @_;
# If the passed fragment is a new word (and not another part of the
# previous): Check if it is MIME encoded
if (!$sameword && $word =~ /^(=\?[^\?]+?\?[QqBb]\?)(.+\?=[^\?]*)$/) {
# Word is encoded, save without the MIME header
# (e.g. "t=E4st?=" instead of "?iso-8859-1?q?t=E4st?=")
my $charset = $1;
my $newword = $2;
if ($$line =~ /^(=\?[^\?]+\?[QqBb]\?)(.+)\?=$/) {
# Previous word was encoded, too:
# Delete the trailing "?=" and insert an underline character (=space)
# (space between to encoded words is ignored)
if ($1 eq $charset) {
if (length($1.$2)+length($newword)>75) {
my $delim = (@$lines) ? ' ' : '';
push(@$lines, "$delim$1$2_?=\n");
$$line = $word;
} else {
$$line = $1 . $2 . '_' . $newword;
}
} else {
if (length("$$line $word")>75) {
my $delim = (@$lines) ? ' ' : '';
push(@$lines, "$delim$1$2_?=\n");
$$line = $word;
} else {
$$line = "$1$2_?= $word";
}
}
return 0;
}
}
# New word is not encoded: simply append it, but check for line length
# and add a newline if necessary
if (length($$line) > 0) {
if (length($$line) + length($word) >= 75) {
my $delim = (@$lines) ? ' ' : '';
push(@$lines, "$delim$$line\n");
$$line = $word;
} else {
$$line .= " $word";
}
} else {
# line is empty
$$line = $word;
}
}
1;