#!/usr/bin/perl use warnings; use strict; use Email::Stuffer; use Email::Sender::Transport::SMTP::Persistent; use File::Basename; use Encode; use Encode::Locale; use File::Slurp; use threads; use Thread::Queue; use Getopt::Long; use Pod::Usage; use Encode::MIME::Header; use Time::HiRes qw(gettimeofday tv_interval); use Data::Dumper; use open ':encoding(locale)'; @ARGV = map { decode(locale => $_, 1) } @ARGV; # #### # configurable settings # #### my $transport = Email::Sender::Transport::SMTP::Persistent->new({ host => 'localhost', port => 25, #debug => 1, }); my $thread_count = 64; # #### # end config # #### my %opts = (); Getopt::Long::Configure ("bundling"); pod2usage(-verbose => 0) if (@ARGV== 0); GetOptions(\%opts, "help|h", "to=s", "from=s", "subject=s", "content-html=s", "content-txt=s", "debug") or pod2usage(2); pod2usage(0) if $opts{help}; sub trim { my $string = shift; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } my $subject = $opts{subject}; my $from = $opts{from}; my @addrlist; my $content_html; my $content_txt; if ($opts{to} =~ m/.+\@.+\..+/) { push @addrlist, $opts{to}; } else { if (-r $opts{to}) { open LIST, "<", $opts{to} or die; for () { my $to = trim($_); next if ($to eq ""); push @addrlist, $to; } close LIST; } else { print STDERR "Error: can't read recipient file: $!\n"; } } if (0+@addrlist < $thread_count) { $thread_count = 0+@addrlist; } die "Error: subject not set\n" unless ($opts{subject}); die "Error: from not set\n" unless ($opts{from}); die "Error: content-txt not set\n" unless ($opts{"content-txt"}); die "Error: can't read content-txt: $!\n" unless (-r $opts{"content-txt"}); die "Error: can't read content-html: $!\n" if ($opts{"content-html"} and not -r $opts{"content-html"}); if ($opts{"content-html"}) { $content_html = read_file($opts{"content-html"}, binmode => ':utf8'); } $content_txt = read_file($opts{"content-txt"}, binmode => ':utf8'); print "Building email ... " if $opts{debug}; # create mail # To will be filled when sending my $msg = Email::Stuffer->new(); $msg->from($from); $msg->subject(trim($subject)); $msg->text_body($content_txt); $msg->html_body($content_html); $msg->header("Precedence", "bulk"); #$msg->header("List-Unsubscribe", ""); $msg->transport($transport); print "done\n" if $opts{debug}; my $q = Thread::Queue->new(); print "creating worker threads\n" if $opts{debug}; for (my $i = 0; $i < $thread_count; $i++) { my $thr = threads->new(sub { while (my $to = $q->dequeue()) { $msg->to($to); $msg->send(); print "sent mail in thread".threads->self->tid()."\n" if $opts{debug}; } $transport->disconnect; }); } print "Sending\n"; my $send_time = [gettimeofday]; for (@addrlist) { $q->enqueue($_); } $q->end(); # wait for everyone before exiting foreach my $thr (threads->list) { if ($thr->tid && !threads::equal($thr, threads->self)) { print "waiting for thread ".$thr->tid()." to finish\n" if $opts{debug}; $thr->join; } } print "done after ".tv_interval($send_time)." seconds\n"; __END__ =head1 NAME mailer.pl - send lots of mails =head1 SYNOPSIS mailer.pl options ... Options: --help, -h short help message --to recipient(s) --from from --subject subject --content-html body of the mail (html part) (optional) --content-txt body of the mail (text part) --debug output extra debug information =cut