Subject: | [PATCH] Support sendEmail to make it easier emailing from Win32 |
I'm trying to set up my personal (Windows) PC to run Test-Smoke and I
find that it fails at the last hurdle: it is unable to email the final
smoke report.
Windows doesn't have sendmail, mail or mailx, or even an SMTP server by
default any more (I'm running Windows 7 Home Premium; I think there are
options for installing an SMTP server on higher-spec versions of Windows
7, and on older Windows OSes, but that doesn't help me).
I considered installing the non-standard hMailServer suggested here:
http://social.technet.microsoft.com/forums/en-us/w7itproinstall/thread/7CEF5DA9-5B31-4B98-9BD9-9F7777DD7B37
but I think a simpler option would be to just use smtp.gmail.com :-)
Unfortunately, the bundled Mail::Sendmail won't work with that as-is
because Gmail requires an SSL/TLS connection (see
http://support.google.com/mail/bin/answer.py?hl=en&answer=13287), and
there is no IO::Socket::SSL bundled with Test-Smoke. The popular Windows
command-line program called "Blat" has the same problem, so supporting
that in Test-Smoke wouldn't help either.
A solution to that is to have stunnel running, providing a secure
connection to programs without it. I did get that running, but I've
finally found a simpler solution still: there is another Windows
command-line program called "sendEmail" (actually written in perl,
compiled into an executable, but with the source code available too)
which works very much like Blat but *does* support SSL/TLS and hence
works out of the box with Gmail. The download page for sendEmail is
here: http://caspian.dotconf.net/menu/Software/SendEmail/
The attached patch against Test-Smoke-1.44 adds support for this
sendEmail program, thus providing an easy option for Windows users,
without the hassle of setting up an SMTP server or building
IO::Socket::SSL or configuring stunnel.
Note that the configsmoke.pl script requests a username and password
with which to connect to the specified SMTP server when using sendEmail.
The password, if supplied, will be stored in plaintext format in the
_config file, which is not ideal. However, you can choose to leave it
empty, in which case the sendEmail program will prompt for the password
at the point of sending the email, thus avoiding the need to store the
password at all.
Subject: | sendemail.patch |
diff -ruN Test-Smoke-1.44.orig\configsmoke.pl Test-Smoke-1.44\configsmoke.pl
--- Test-Smoke-1.44.orig\configsmoke.pl Fri Aug 27 19:09:28 2010
+++ Test-Smoke-1.44\configsmoke.pl Sat Mar 10 16:15:29 2012
@@ -526,6 +526,17 @@
alt => [ ],
dft => 'localhost',
},
+ muser => {
+ msg => 'Which username should be used for the SMTP server?',
+ alt => [ ],
+ dft => '',
+ },
+ mpass => {
+ msg => 'Which password should be used for the SMTP server?' .
+ "\nLeave empty to be prompted when sending email",
+ alt => [ ],
+ dft => '',
+ },
to => {
msg => <<EOMSG,
@@ -1224,6 +1235,20 @@
$config{ $arg } = prompt( $arg );
};
+ /^sendemail$/ && do {
+ $arg = 'from';
+ $config{ $arg } = prompt( $arg );
+
+ $arg = 'mserver';
+ $config{ $arg } = prompt( $arg );
+
+ $arg = 'muser';
+ $config{ $arg } = prompt( $arg );
+
+ $arg = 'mpass';
+ $config{ $arg } = prompt( $arg );
+ };
+
/^(?:Mail::Sendmail|MIME::Lite)$/ && do {
$arg = 'from';
$opt{ $arg }{chk} = '\S+';
@@ -1677,7 +1702,7 @@
qw( force_c_locale locale defaultenv ),
# Report related
- qw( mail mail_type mserver from to ccp5p_onfail
+ qw( mail mail_type mserver muser mpass from to ccp5p_onfail
swcc cc swbcc bcc ),
# Archive reports and logfile
@@ -2171,6 +2196,8 @@
local $ENV{PATH} = "$ENV{PATH}$Config{path_sep}/usr/sbin";
$map{ $mailer } = whereis( $mailer );
}
+ $mailer = 'sendemail';
+ $map{ $mailer } = whereis( $mailer );
eval { require Mail::Sendmail };
$map{ 'Mail::Sendmail' } = $@ ? '' : 'Mail::Sendmail';
diff -ruN Test-Smoke-1.44.orig\lib\Test\Smoke\Mailer.pm Test-Smoke-1.44\lib\Test\Smoke\Mailer.pm
--- Test-Smoke-1.44.orig\lib\Test\Smoke\Mailer.pm Fri Aug 27 19:09:28 2010
+++ Test-Smoke-1.44\lib\Test\Smoke\Mailer.pm Sat Mar 10 16:12:50 2012
@@ -27,12 +27,14 @@
mail => [qw( bcc cc mailbin )],
df_mailxbin => 'mailx',
mailx => [qw( bcc cc mailxbin swcc swbcc )],
+ df_sendemailbin => 'sendemail',
+ sendemail => [qw( from bcc cc sendemailbin mserver muser mpass )],
df_sendmailbin => 'sendmail',
sendmail => [qw( from bcc cc sendmailbin )],
'Mail::Sendmail' => [qw( from bcc cc mserver )],
'MIME::Lite' => [qw( from bcc cc mserver )],
- valid_mailer => { sendmail => 1, mail => 1, mailx => 1,
+ valid_mailer => { sendmail => 1, mail => 1, mailx => 1, sendemail => 1,
'Mail::Sendmail' => 1, 'MIME::Lite' => 1, },
);
@@ -51,7 +53,7 @@
=head1 DESCRIPTION
-This little wrapper still allows you to use the B<sendmail>,
+This little wrapper still allows you to use the B<sendmail>, B<sendemail>,
B<mail> or B<mailx> programs, but prefers to use the B<Mail::Sendmail>
module (which comes with this distribution) to send the reports.
@@ -63,7 +65,7 @@
Can we provide sensible defaults for the mail stuff?
- mhowto => [Module::Name|sendmail|mail|mailx]
+ mhowto => [Module::Name|sendmail|mail|mailx|sendemail]
mserver => an SMTP server || localhost
mbin => the full path to the mail binary
mto => list of addresses (comma separated!)
@@ -101,6 +103,7 @@
/^sendmail$/ && return Test::Smoke::Mailer::Sendmail->new( %fields );
/^mailx?$/ && return Test::Smoke::Mailer::Mail_X->new( %fields );
+ /^sendemail?$/ && return Test::Smoke::Mailer::SendEmail->new( %fields );
/^Mail::Sendmail$/ &&
return Test::Smoke::Mailer::Mail_Sendmail->new( %fields );
/^MIME::Lite$/ &&
@@ -119,15 +122,15 @@
sub fetch_report {
my $self = shift;
- my $report_file = File::Spec->catfile( $self->{ddir}, $self->{rptfile} );
+ $self->{file} = File::Spec->catfile( $self->{ddir}, $self->{rptfile} );
local *REPORT;
- if ( open REPORT, "< $report_file" ) {
+ if ( open REPORT, "< $self->{file}" ) {
$self->{body} = do { local $/; <REPORT> };
close REPORT;
} else {
require Carp;
- Carp::croak( "Cannot read '$report_file': $!" );
+ Carp::croak( "Cannot read '$self->{file}': $!" );
}
my @config = parse_report_Config( $self->{body} );
@@ -336,6 +339,80 @@
$self->{error} = "Error in pipe to '$mailer': $! (" . $?>>8 . ")";
} else {
$self->{error} = "Cannot fork '$mailer': $!";
+ }
+ $self->{v} and print $self->{error} ? "not OK\n" : "OK\n";
+
+ return ! $self->{error};
+}
+
+=back
+
+=head1 Test::Smoke::Mailer::SendEmail
+
+This handles sending the message with the B<sendEmail> program.
+
+=over 4
+
+=cut
+
+package Test::Smoke::Mailer::SendEmail;
+
+@Test::Smoke::Mailer::SendEmail::ISA = qw( Test::Smoke::Mailer );
+
+=item Test::Smoke::Mailer::SendEmail->new( %args )
+
+Keys for C<%args>:
+
+ * ddir
+ * mserver
+ * muser
+ * mpass
+ * sendemailbin
+ * to
+ * from
+ * cc
+ * v
+
+=cut
+
+sub new {
+ my $proto = shift;
+ my $class = ref $proto || $proto;
+
+ return bless { @_ }, $class;
+}
+
+=item $mailer->mail( )
+
+C<mail()> sets up the commandline and body and passes it to the
+B<sendemail> program.
+
+=cut
+
+sub mail {
+ my $self = shift;
+
+ my $mailer = $self->{sendemailbin};
+
+ my $subject = $self->fetch_report();
+ my $cc = $self->_get_cc( $subject );
+
+ my $cmdline = qq|$mailer -u "$subject"|;
+ $self->{swcc} ||= '-cc', $cmdline .= qq| $self->{swcc} "$cc"| if $cc;
+ $self->{swbcc} ||= '-bcc', $cmdline .= qq| $self->{swbcc} "$self->{bcc}"|
+ if $self->{bcc};
+ $cmdline .= qq| -t "$self->{to}"|;
+ $cmdline .= qq| -f "$self->{from}"| if $self->{from};
+ $cmdline .= qq| -s "$self->{mserver}"| if $self->{mserver};
+ $cmdline .= qq| -xu "$self->{muser}"| if $self->{muser};
+ $cmdline .= qq| -xp "$self->{mpass}"| if ($self->{mpass});
+ $cmdline .= qq| -o message-file="$self->{file}"|;
+
+ $self->{v} > 1 and print "[$cmdline]\n";
+ $self->{v} and print "Sending report to $self->{to}\n";
+ system $cmdline;
+ if ($?) {
+ $self->{error} = "Error executing '$mailer': " . $?>>8;
}
$self->{v} and print $self->{error} ? "not OK\n" : "OK\n";
diff -ruN Test-Smoke-1.44.orig\mailrpt.pl Test-Smoke-1.44\mailrpt.pl
--- Test-Smoke-1.44.orig\mailrpt.pl Fri Aug 27 19:09:28 2010
+++ Test-Smoke-1.44\mailrpt.pl Sat Mar 10 15:47:20 2012
@@ -29,6 +29,8 @@
ccp5p_onfail => undef,
from => undef,
mserver => undef,
+ muser => undef,
+ mpass => undef,
v => undef,
rptfile => 'mktest.rpt',
@@ -42,7 +44,7 @@
my $defaults = Test::Smoke::Mailer->config( 'all_defaults' );
-my %valid_type = map { $_ => 1 } qw( mail mailx sendmail
+my %valid_type = map { $_ => 1 } qw( mail mailx sendmail sendemail
Mail::Sendmail MIME::Lite );
=head1 NAME
@@ -76,7 +78,8 @@
--to <emailaddresses> Comma separated list (smokers-reports@perl.org)
--cc <emailaddresses> Comma separated list
- -t | --type <type> mail mailx sendmail Mail::Sendmail [mandatory]
+ -t | --type <type> mail mailx sendmail sendemail Mail::Sendmail
+ [mandatory]
--nomail Don't send the message
--report Create a report anyway
@@ -96,6 +99,13 @@
--from <address>
+=item * B<options for> -t sendemail
+
+ --from <address>
+ --mserver <smtpserver> (localhost)
+ --muser <smtpserverusername>
+ --mpass <smtpserverpassword>
+
=item * B<options for> -t Mail::Sendmail | MIME::Lite
--from <address>
@@ -114,7 +124,7 @@
'type|t=s', 'ddir|d=s', 'to=s', 'cc=s', 'bcc=s', 'ccp5p_onfail!',
'v|verbose=i',
- 'from=s', 'mserver=s',
+ 'from=s', 'mserver=s', 'muser=s', 'mpass=s',
'help|h', 'man',