#!/usr/bin/perl -w use strict; use Carp qw(croak); use POSIX qw(setsid); $|++; my $Debug = 1; my $Verbose = 1; my $NOFORK = 0; our $MAILER = "/usr/sbin/sendmail -t"; my %EXPECT = ( body => 1, subject => 1, from => 1, max => 1, rmax => 1, nofork => 1, help => 0 ); #massmail.pl -from your@host.com [-body ] [-subject ] [-rmax ] [-max <#emails#>:<#per sec#>] [-verbose] [-debug] -- my %HELP = ( '' => "\tMarks the end of arguments, the parameters following this will be considered email address dictionaries.", subject => "[file]\t (Optional) Filename containing the Subject to be sent with the emails. (This defaults to subject.txt", body => "[file]\t (Optional) HTML file to send, this defaults to body.txt", rmax => "\tHow many addresses a single email that is sent, should be sent to. (Defaults to 25)", max => ":\tHow many emails at to be sent within (Defaults to 25:0) - no delay", from => "fbi\@fbi.gov\t - this is a manatory argument." ); sub help { print "\n Available arguments:\n\n"; foreach(keys %HELP) { print " --$_ $HELP{$_}\n"; } print "\n\nExample usage: ./massmail.pl -from security\@fbi.gov -rmax 5 -max 10:1 -- emails.txt emails2.txt ... ..\n"; # } my $OPT = { body => 'body.txt', subject => 'subject.txt', max => '25:0', rmax => 25, nofork => 0, }; sub die_nicely { local $\ = "\n"; print STDERR foreach(@_); exit(1); } ARG: while((local $_ = lc $ARGV[0]) =~ /^-/) { shift; last ARG if /^--$/; s/^-{1,2}//; if($_ eq 'help') { help(); exit(0); } $OPT->{$_} = 1, next ARG unless $EXPECT{$_}; $OPT->{$_} = shift; } not scalar grep {!exists $EXPECT{$_}} keys %$OPT or die_nicely "Unknown argument suppled (--help?)"; $NOFORK = 1 if $^O =~ /mswin/i; # regardless of what is sent $Verbose = 1 unless exists $OPT->{verbose}; die_nicely "Required argument 'from' was not supplied" unless($OPT->{from}); do { print "OPT: $_\t=>\t$OPT->{$_}\n" foreach(keys %$OPT); print "ARGV: $_\n" foreach(@ARGV); print "\$Debug = $Debug\n"; print "\$Verbose = $Verbose\n"; } if $Debug; die_nicely "Invalid parameter supplied for argument 'max', should be :" unless !defined $OPT->{max} || $OPT->{max} =~ /^\d+:\d+$/; my($LIMIT, $DELAY) = split /:/, $OPT->{max}; $LIMIT ||= 25; $DELAY ||= 0; my $Body = slurp($OPT->{body}); (my $Subject = slurp($OPT->{subject})) =~ s/[\r\n]//g; sub mail { my($From, $To, $Subject, $Body, $Headers) = @_; defined $Body or croak "Required argument was not supplied"; open(MAIL, "|$MAILER") or die_nicely "Failed to open mailer, where is it?\n"; $To = join ', ', @$To if(ref $To eq 'ARRAY'); print MAIL "From: $From\n", "To: $To\n", "Subject: $Subject\n"; print MAIL foreach(@$Headers); print MAIL "Content-Type: text/html\n\n"; print MAIL $Body; close(MAIL) or warn "Mailer did not exit nicely ($!)", return; } sub slurp { local $/ = undef; open(FILE, "<${_[0]}") or die "cannot open file $_[0] ($!)"; my $contents = ; close(FILE); $contents; } sub valid { foreach(@_) { return 0 unless /^.+\@.+\.\w{2,4}$/i } 1; } sub display(\@) { my $preped = shift or croak "send(); requires an array"; print $preped . "=\n"; foreach(@$preped) { print "\t" . $_ . "\n"; } } do { local $, = "\n"; print "\n - Subject:\t(" . ((defined $Subject) ? $Subject : 'No Subject') . ") <" . $OPT->{subject} . ">", " - Body (HTML):\t" . $OPT->{body}, " - Emails:\t" . join(", ", @ARGV) . "\n"; printf("\n - Operating at an interval of %d sec%s, %d emails per.\n\n", $DELAY, ($DELAY == 1) ? '' : 's', $LIMIT); }; sub creader { my $FILE = shift; (-F($FILE)) or die_nicely "File '$FILE' does not exist, or is not ASCII"; } FORK: { unless($NOFORK) { print "Attempting to detach from the terminal.\n"; last FORK unless $^O =~ /mswin/i; defined(my $pid = fork) or die "Cannot fork into the background ($!)"; open(STDIN, '/dev/null') or die "Cannot read /dev/null: ($!)"; open(STDOUT, '>/dev/null') or die "Cannot write to /dev/null/: ($!)"; exit if $pid; setsid or die "Cannot create a new session!"; open(STDERR, '>&STDOUT') or die "Cannot duplicate STDOUT! ($!)"; } } my $start = time; foreach my $file (@ARGV) { print "Reading file: $file\n" if $Debug; open(EMAILS, "<$file") or die "Cannot open file $file\n"; my @stack; my @emails; my $count = 0; STACK: while() { chomp; next STACK if !defined($_) || $_ eq ''; $count++; push @emails, $_; unless($count % $OPT->{rmax}) { push @stack, [ @emails ] if(@emails); @emails = (); } unless(@stack % $LIMIT) { mailem(@stack); sleep(($DELAY / 2)) if(($DELAY > 0) && ($count)); @stack = (); } } if(scalar @stack) { mailem(@stack); }; print "\n- $count emails queued in " . (time() - $start) . " sec(s)\n"; close(EMAILS); } sub mailem { my $i = 0; foreach(@_) { print " - Mailing to " . join(", ", @$_) . "\n" if $Verbose; mail($OPT->{from}, $_, $Subject, $Body); } # sleep($DELAY) # if($DELAY > 0); } sleep(1), print " Done!\n"; exit(0); 1; __END__