#!/usr/bin/perl -w

# Usage: sendit.k12.pl <txtmsg> <email-addresses> <name-for-processed<-list> <delay>
#        and it expects a file called smtppwd.txt in a location specified
#        below. The <delay> between msgs is in seconds, and the delay used is
#        100-200% of that delay, averaging 150% of the seconds value.
# Example:  sendit.pl advisormsg.txt advisors.contacts.csv adv.mailed17Jun 4
#
# David Harris, Version of 14 Oct 2005 pm 4:00

# ------------------------------------------------ 
## Exception handler from
## http://perl.apache.org/docs/general/perl_reference/perl_reference.html#The_My__Exception_class_in_its_entirety
#  package My::Exception;
#
#  use vars qw/@ISA @EXPORT $AUTOLOAD/;
#  use Exporter;
#  @ISA = 'Exporter';
#  @EXPORT = qw/die/;
#
#  sub die (@);
#
#  sub import {
#      my $pkg = shift;
#      # allow "use My::Exception 'die';" to mean import locally only
#      $pkg->export('CORE::GLOBAL', 'die') unless @_;
#      Exporter::import($pkg,@_);
#  }
#    
#  sub die (@) {
#      if (!ref($_[0])) {
#          CORE::die My::Exception->UnCaught(text => join('', @_));
#      }
#      CORE::die $_[0];
#  }
#
#  {
#      package My::Exception::UnCaught;
#      use overload '""' => sub { shift->{text} } ; 
#  }
#
#  sub AUTOLOAD {
#      no strict 'refs', 'subs';
#      if ($AUTOLOAD =~ /.*::([A-Z]\w+)$/) {
#          my $exception = $1;
#          *{$AUTOLOAD} = 
#              sub {
#                  shift;
#                  my ($package, $filename, $line) = caller;
#                  push @_, caller => {
#                                  package => $package,
#                                  filename => $filename,
#                                  line => $line,
#                                      };
#                  bless { @_ }, "My::Exception::$exception"; 
#              };
#          goto &{$AUTOLOAD};
#      }
#      else {
#          CORE::die "No such exception class: $AUTOLOAD\n";
#      }
#  }
#
#  1;

# ---------------------- End of Exception code ----------

use Net::SMTP;

use Authen::SASL qw(Perl);

# Read SMTP servername|account|passwd|delay|debugFlag from an outside file that
# can be changed to use different outgoing SMTP systems, perhaps on different
# friends' machines.  The file name and location can be customized here:
open(SMTPPWD, 'C:/htdocs/smtppwd.txt') or die "Couldn't open smtppwd.txt";
chomp($lineread = <SMTPPWD>);     # Reads just first line.
close(SMTPPWD);
# Breaks into 5 substrings at |'s:
($servername, $smtpusername, $passwd, $debugFlag)
        = split /\|/, $lineread, 4;
# 0 for $debugFlag sets NO debugging.

# Check the number of arguments on the command line:
if ($#ARGV+1 != 4) {
  die "sendit.k12.pl needs txtmsg file, emails file, successes file,
  and delayseconds, as arguments";
  };
  
# Check existence of the textfile and email listing file:
(open TXTMSG, "<$ARGV[0]") or die "sendit.k12.pl textmsg file not found.\n";
(open EMAILS, "<$ARGV[1]") or die "sendit.k12.pl emails file not found.\n";
# And file to report successful sendings:
(open ATTEMPTS, ">$ARGV[2]") or die "sendit.k12.pl: successes/out file not opened.\n";
# And delay-time base number (up to 50% of it will be randomly added to it):
# Thus a value of 48 sec. will average 1 msg/minute, 8 => 8 to 12 sec, averaging
#  10 sec, etc.
$delayseconds = "$ARGV[3]";


#Read the internal documentation from the TXTMSG template file, and throw
# the internal documentation away:
THRUDOCS: while ($lineread = <TXTMSG>) {
  last THRUDOCS if ($lineread =~ /^----------------------+$/);
}

# Read in the lines that give the substitution target fields,
# like TTTTT=1 and FFFFF=2 etc. (which tell which strings are targets to
# be substituted in the text message farther down in the TXTMSG file)
# followed by an '=' sign and the number of the field (column) to
# substitute from each line of the EMAILS file.

# IF there isn't at least one line of such substitutions, then there will
# not be any variation in the emails sent to, so don't use this program but
# just send an ordinary email to a single (set of) persons.

$linenum = 0;
while ($lineread = <TXTMSG>) {
  chomp $lineread;
  if ($lineread =~ /\w+=\d+/) {
     ($substit[$linenum], $subsfield[$linenum]) = split /=/, $lineread, 2;
     $linenum++;
  } elsif ($lineread =~ /^M+$/) {
     last;
  }
    else { die "MMMMMM...MMMMM line not found.\n";}
}


$msglines = 0;
# Read in the substitutable lines from the textfile opened above:
while ($lineread = <TXTMSG>) {
# The mail stuff wraps the lines if needed.
$messagearray[$msglines] = $lineread;
$msglines++;    # The array grows and this index is used below.
};
close(TXTMSG);

$emailnumber = 1;  # Counter of emails sent, for output near end
# Do the rest of this program once for each non-empty line in EMAILS file
while ((chomp($email = <EMAILS>)) && (length($email) > 2)) {
  # Put fields into array.
  @fields = split /\|/, $email;

  # Now @messagearray has the text with the targets embedded,
  #     @substit has the images of the targets,
  #     @subsfield has the numbers of the fields that are  used, and
  #     @fields has the fields OF THIS PARTICULAR EMAIL FILE LINE
  #        to put in place of the targets.

  # Make the 'Date:' string for this particular email
  ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
                  localtime(time-(0*3600)-3);  # Future time zone correction?
  $weekday = (Sun,Mon,Tue,Wed,Thu,Fri,Sat)[$wday];
  $monthname = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$mon];
  $yr2000 = $year+1900;
  if (length($mday) == 1) {
    $mday = '0'.$mday;
  }
  if (length($hour) == 1) {
    $hour = '0'.$hour;
  }
  if (length($min) == 1) {
    $min = '0'.$min;
  }
  if (length($sec) == 1) {
    $sec = '0'.$sec;
  }
  $datestr = "$weekday, $mday $monthname $yr2000 $hour\:$min\:$sec +0000\n";
  # Exception handler starts with eval around the block that might
  # throw an exeption:
 # eval {
    # Activate the SMTP link: [IMPLEMENTING A RETRY ABILITY]
    RETRY: for ($retries = 3; $retries >= 0; $retries--) {
      $smtpworked = 1 ;
      # $servername comes from smtppwd.txt file here.:
      if ($debugFlag eq '0') {
        $smtp = Net::SMTP->new( Host => $servername,
                                Hello => 'darwinday.sbcglobal.net',
                                Timeout => 120 );
        print STDERR "Couldn't connect to server without debugging \n" unless $smtp;
        unless ($smtp) {$smtpworked = 0} ;
      } else {
        $smtp = Net::SMTP->new( Host => $servername,
                                Hello => 'darwinday.sbcglobal.net',
                                Timeout => 120,
                                Debug => 1 );     # Non-zero says DO debugging.
      }
      unless ($smtp) {
        print STDERR " $smtp\n";
        print STDERR "Couldn't connect to server with debugging\n" ;
        $smtpworked = 0;
      };
      $smtp->auth($smtpusername,$passwd);  # Authentication!
      unless ($smtp) {
        $smtpworked = 0;
        print STDERR "AUTHENTICATION failure";
      }
      #Start of substitutable message, as stored in array by code above
      for ($j = 0; $j < $msglines; $j++) {  # Step thru array of stored lines
        $linetosend = $messagearray[$j];
        for ($i = 0; $i < $linenum; $i++) {  #For this line, make substitutions
          # Make a substitution as often as needed in 1 line:
          ($linetosend = $linetosend) =~ s/$substit[$i]/$fields[$subsfield[$i]-1]/g;
        }
        # Special calls for the first 3 lines:
        SWITCH: {
          if ($j == 0) { $smtp->mail($linetosend);
            unless ($smtp) {
              $smtpworked = 0 ;
              last SWITCH;
            }
          }
          if ($j == 1) { $smtp->recipient($linetosend);
            unless ($smtp) {
              $smtpworked = 0 ;
              last SWITCH;
            }
          }
          # Blank line then the computed date:
          if ($j == 2) { $smtp->data();
            unless ($smtp) {
              $smtpworked = 0 ;
              last SWITCH;
            }
          }
          # All further lines:
          if ($linetosend =~ m/^From: /) {
            $smtp->datasend("Date: $datestr");  # Emit computed date
            unless ($smtp) {
              $smtpworked = 0;
            }
          }
          $smtp->datasend($linetosend);
          unless ($smtp) {
            $smtpworked = 0;
          }
        } # End of SWITCH.
      }
      $smtp->dataend();
      unless ($smtp) {$smtpworked = 0} ;
      # Write the emails.txt line that was attempted to be sent, and its number:
      chomp($dateinsert = $datestr);
      print ATTEMPTS  "$dateinsert processed|$emailnumber|$email\n" ;
      print STDERR "At $dateinsert processed #$emailnumber: $email\n" ;
      sleep 5;  # A delay intended to let outgoing message be scanned by the
                #  Symantec virus scan on outgoing msgs, which is assumed
                #  to run in parallel to this process/program but starting
                #  when the message is completed.  It's an experiment.
      $smtp->quit;
      unless ($smtp) {$smtpworked = 0} ;
      $emailnumber++ ;
      # Let some seconds elapse: the specified time plus randomly to 100% more:
                        # The -5 is to match the delay before quit above.
      sleep $delayseconds + int(rand ($delayseconds * 100/100)) -5;
      if ( $smtpworked ) { # No failures, so don't repeat:
        last RETRY ;
      } else { # if there was a mistake, let loop count determine repeating.
        next RETRY ;
      }
    } # End of FOR loop.

  #};  # End of eval block ; NOTE semicolon!
  ## Exception handler: Did any method in the SMTP object fail?:
  #if ( $@ ) {
  #    warn "Exception thrown was $@ \n" ;
  #}

}  # End of processing one line of email addresses.
print STDERR "ATTEMPTS file closed and about to exit now.\n" ;
exit;