Приглашаем посетить
Хлебников (hlebnikov.lit-info.ru)

Broadcaster

#!/usr/local/bin/perl

$YES = 1;

############################################################################
#                                                                          #
# Broadcaster                       Version 1.6                            #
# Written by Matthew Wright         mattw@worldwidemart.com                #
# Designed by Craig A. Patchett     craig@patchett.com                     #
# Created 08/31/96                  Last Modified 3/18/97                   #
#                                                                          #
# Copyright 1997 Craig Patchett & Matthew Wright.  All Rights Reserved.    #
# This program is part of The CGI/Perl Cookbook from John Wiley & Sons.    #
# License to use this program or install it on a server (in original or    #
# modified form) is granted only to those who have purchased a copy of The #
# CGI/Perl Cookbook. (This notice must remain as part of the source code.) #
#                                                                          #
############################################################################


############################################################################
# Define overridable configuration constants                               #
############################################################################

# $DATE_FORMAT holds the &format_date() format for the current date and time

$DATE_FORMAT = '<wday>, <mon> <0d>, <year> at <mh>:<0n>:<0s>';

# $BODY_BGCOLOR is the default background color for the pages (in HTML
# format)

$BODY_BGCOLOR = '#FFFFFF';

# $PERMISSIONS is the type of permissions you want to give to the log file
# the program creates. They are expressed in standard UNIX octal format 
# (i.e. 0660). If your server is running on a system that doesn't support 
# file permissions then you should set $PERMISSIONS to 0.

$PERMISSIONS = 0666;

# $ERROR_PAGE is the full path to the HTML file that contains the template 
# for an error message. This message should contain 
# '<<ERROR_TITLE>>', '<<ERROR_NAME>>', and '<<ERROR_MSG>>' (without quotes) 
# wherever you want the page title, error name, and error message to appear.

$ERROR_PAGE = '/web/user/error_page.html';

# $DEFAULT_DATA_DELIMITER is the default field separator that will be used
# if not overridden by the visitor in the first form

$DEFAULT_DATA_DELIMITER = 'tab';

# $REQUIRE_DIR is the directory in which all of your required files are
# placed.  On most systems, if you leave the required files in the same
# directory as the CGI program, you can leave this variable blank.  
# Otherwise, if you move the required files to another directory, specify
# the full path here.

$REQUIRE_DIR = 'require';


############################################################################
# Get required subroutines which need to be accessed.                      #
############################################################################

# Push $REQUIRE_DIR onto the @INC array for include file directories
# and list required files.

push(@INC, $REQUIRE_DIR) if $REQUIRE_DIR;

require 'base64.pl';
require 'chkemail.pl';
require 'error.pl';
require 'formdate.pl';
require 'locksubs.pl';
require 'parsform.pl';
require 'sendmail.pl';
require 'template.pl';
require 'uuencode.pl';


############################################################################
# Parse form, pull in the configuration file if one exists                 #
############################################################################

if (!(&parse_form)) {
    &error($Error_Message, '', '', 1);
}
if ($FORM{'config'} && eval(require $FORM{'config'})) { $config_file = $YES }


############################################################################
# Define non-overridable configuration constants                           #
############################################################################

# $BASE_DIR is the full path to the directory that serves as the root for 
# the files a user can access through Broadcaster. All file paths specified
# by the user must be contained in this directory. If you do not include
# this variable then the user can access any file in your system. (Note
# that the path must end with a directory delimiter.)

$BASE_DIR = '/web/user/broadcaster/';

# $LOCK_DIR is the full path to the directory where all lock files will be 
# placed. (Note that it should include a directory delimiter at the end.)
# For security reasons you may want to have this be a directory outside the 
# web space, i.e. "/tmp/"

$LOCK_DIR = '/tmp/';

# $BULK_LIMIT is maximum number of addresses that a single message can
# be sent to when you are using the bulk email option.

$BULK_LIMIT = 25;

# $SMTP_SERVER is the domain name of the SMTP server you will be using 
# to send email. If you're not sure about this, ask your service provider.

$SMTP_SERVER = 'smtp.domain.com';

# $MAX_WAIT is the max number of seconds the lock program will wait 
# before overiding the lock file.

$MAX_WAIT = 5;


############################################################################
# Finish intializing                                                       #
############################################################################

$WEB_SERVER = $ENV{'SERVER_NAME'};
$PROGRAM_URL = "http://$ENV{'SERVER_NAME'}$ENV{'SCRIPT_NAME'}$ENV{'PATH_INFO'}";
$DATE = &format_date(time, $DATE_FORMAT);

print "HTTP/1.0 200 OK\n";
print "Content-type: text/html\n\n";


############################################################################
# Do security check on filenames (turn into relative path name, remove     #
# any references to parent directories)                                    #
############################################################################

$PAGE_HEADER_FILE =~ s#(^$BASE_DIR)|(^[/\:])|(\.\.([/\:]|$))##g;
$PAGE_FOOTER_FILE =~  s#(^$BASE_DIR)|(^[/\:])|(\.\.([/\:]|$))##g;
$DEFAULT_DATA_FILE =~ s#(^$BASE_DIR)|(^[/\:])|(\.\.([/\:]|$))##g;
$DEFAULT_MESSAGE_FILE =~ s#(^$BASE_DIR)|(^[/\:])|(\.\.([/\:]|$))##g;
$DEFAULT_LOG_FILE =~ s#(^$BASE_DIR)|(^[/\:])|(\.\.([/\:]|$))##g;
if ($FORM{'PASS'}) {
    $FORM{'data_file'} =~ s#(^$BASE_DIR)|(^[/\:])|(\.\.([/\:]|$))##g;
    $FORM{'message_file'} =~ s#(^$BASE_DIR)|(^[/\:])|(\.\.([/\:]|$))##g;
    $FORM{'log_file'} =~ s#(^$BASE_DIR)|(^[/\:])|(\.\.([/\:]|$))##g;
}

if (!chdir($BASE_DIR)) {
    &error("Can't change directory to $BASE_DIR ($!).", '', '', 1);
}


############################################################################
# Process initial call and generate file selection page                    #
############################################################################

if (!$FORM{'PASS'}) {
    
    # Insert the page header if specified
    
    if ($PAGE_HEADER_FILE) {
        if (!&parse_template($PAGE_HEADER_FILE, *STDOUT)) {
            &error($Error_Message, '', '', 1);
        }
    }
    else {
        print "<HTML>\n   <HEAD>\n";
        print "      <TITLE>Broadcaster: File Selection</TITLE>\n";
        print "   </HEAD>\n   " . &print_body_tag;
    }
    print "      <FORM ACTION=\"$PROGRAM_URL\" METHOD=\"POST\">\n";
    print "         <CENTER><H1>Broadcaster: File Selection</H1></CENTER>\n";

    # Print a warning if no configuration file was specified
    
    if (!$config_file && defined($FORM{'config'})) {
        print "         <P><I><B>WARNING:</B> A valid configuration file ";
        print "has not been specified. This may affect the look of the ";
        print "pages.</I>\n";
    }

    # Print the rest of the HTML body
    
    print <<"(END HTML)";
         <P><I>Please enter the names of the files Broadcaster should use to 
         prepare and log the messages it will send, along with the other
         requested information. Click on <B>Continue</B> when done.</I>
         <BR><BR><BR>
         <P><TABLE BORDER=0 CELLPADDING=1>
            <TR>
               <TD VALIGN=BOTTOM>
                  <P><B>Data File Path:</B>
               </TD><TD>
                  <P>$BASE_DIR<INPUT TYPE=TEXT NAME="data_file" 
                  VALUE="$DEFAULT_DATA_FILE" SIZE=40 MAXLENGTH=100>
               </TD></TR>
            <TR>
               <TD VALIGN=BOTTOM>
                  <P><B>Field Separator:</B>
               </TD><TD>
                  <P><INPUT TYPE=TEXT NAME="data_delimiter" 
                  VALUE="$DEFAULT_DATA_DELIMITER" SIZE=4 MAXLENGTH=4>
                  (<I>tab</I> = tab, <I>csv</I> = comma separated w/quotes) 
               </TD></TR>
            <TR>
               <TD VALIGN=BOTTOM>
                  <P><B>Send to <BR>Invalid Records?</B>
               </TD><TD>
                  <P> 
                  <BR><INPUT TYPE=CHECKBOX NAME="send_invalid" VALUE="YES">
               </TD></TR>
            <TR>
               <TD VALIGN=BOTTOM>
                  <P><B>Message File Path:</B>
               </TD><TD VALIGN=BOTTOM>
                  <P> 
                  <P>$BASE_DIR<INPUT TYPE=TEXT NAME="message_file"
                  VALUE="$DEFAULT_MESSAGE_FILE" SIZE=40 MAXLENGTH=100>
               </TD></TR>
            <TR>
               <TD VALIGN=BOTTOM>
                  <P><B>Use Log File?</B>
               </TD><TD VALIGN=BOTTOM>
                  <P> 
                  <P><INPUT TYPE=CHECKBOX NAME="use_log" VALUE="YES"
                  CHECKED>
               </TD></TR>
            <TR>
               <TD VALIGN=BOTTOM>
                  <P><B>Log File Path:</B>
               </TD><TD VALIGN=BOTTOM>
                  <P>$BASE_DIR<INPUT TYPE=TEXT NAME="log_file" 
                  VALUE="$DEFAULT_LOG_FILE" SIZE=40 MAXLENGTH=100>
               </TD></TR>
         </TABLE>
         <INPUT TYPE=HIDDEN NAME="PASS" VALUE="2">
         <INPUT TYPE=HIDDEN NAME="config" VALUE="$FORM{'config'}">
         <BR>
         <BR>
         <INPUT TYPE=SUBMIT NAME="Submit"
         VALUE="Continue..."> <INPUT TYPE=RESET VALUE="Clear">
      </FORM>
(END HTML)
      
    # Insert the page footer if specified, otherwise finish up the page
    
    if ($PAGE_FOOTER_FILE) {
        if (!&parse_template($PAGE_FOOTER_FILE, *STDOUT)) {
            &error($Error_Message, '', '', 1);
        }
    }
    else { print "   </BODY>\n</HTML>\n" }
}


############################################################################
# Process second call and generate field selection page                    #
############################################################################

elsif ($FORM{'PASS'} == 2) {

    # Data File must be specified and be readable
    
    if (!$FORM{'data_file'}) {
        push(@messages, "You must specify a data file.");
    }
    elsif (!(-r $FORM{'data_file'}) || !(-T $FORM{'data_file'})) {
        push(@messages, "Cannot read $FORM{'data_file'} ($!).");
    }

    # Message File must be specified and be readable
    
    if (!$FORM{'message_file'}) {
        push(@messages, "You must specify a message file.");
    }
    elsif (!(-r $FORM{'message_file'}) && (-T $FORM{'message_file'})) {
        push(@messages, "Cannot read $FORM{'message_file'} ($!).");
    }

    # If they wish to use a log file, it must be writable if it exists or
    # else we must be able to create it
    
    if ($FORM{'use_log'} eq 'YES') {
        if (!$FORM{'log_file'}) {
            push(@messages, "You did not specify a log file.");
        }
        elsif ((-e $FORM{'log_file'}) && !(-w $FORM{'log_file'})) {
            push(@messages, "Cannot write to $FORM{'log_file'} ($!).");
        }
        elsif (!(-e $FORM{'log_file'})) {
            if (open(LOG, ">$FORM{'log_file'}")) {
                chmod $PERMISSIONS, $FORM{'log_file'} if $PERMISSIONS;
                close(LOG);
            }
            else {
                push(@messages, "Could not create $FORM{'log_file'} ($!).");
                
            }
        }
    }

    if ($FORM{'data_delimiter'}) {
        if ($FORM{'data_delimiter'} =~ /tab/i) {
            $data_delimiter = "\t";
        }
        elsif ($FORM{'data_delimiter'} =~ /csv/i) {
            $data_delimiter = '';
            $csv = $YES;
        }
        else {
            $data_delimiter = $FORM{'data_delimiter'};
            $data_delimiter =~ s#([$()*+./?\^|])#\\$1#g;
        }
    }
    else {
        push(@messages, 'You must specify a data delimiter');
    }

    # Print out any accumulated messages here.

    if (@messages) { &show_messages(@messages) }

    # Read header record from data file and count the number of 
    # data lines
       
    if (!open(DATA_FILE, $FORM{'data_file'})) {
        &error("Could not open $FORM{'data_file'} ($!).", '', '', 1);
    }
    $header_record = <DATA_FILE>;
    chop($header_record);

    $num_data_records = 0;
    while (<DATA_FILE>) {
        ++$num_data_records;
    }
    close(DATA_FILE);

    # Take a guess at the number of reports they want as email is sent.

    if ($num_data_records < 100) {
        $num_status_reports = int($num_data_records / 5);
    }
    elsif ($num_data_records < 500) {
        $num_status_reports = int($num_data_records / 10);
    }
    else {
        $num_status_reports = int($num_data_records / 25);
    }

    # Use special subroutine to split CSV record
        
    if ($csv) { @data_fields = &split_csv($header_record) }

    # Use straight split for other records
    
    else { @data_fields = split(/$data_delimiter/, $header_record) }
    foreach $data_field (@data_fields) {
    
        # Check to see if it looks like the email address
        
        if (!$email_field && ($data_field =~ /e-?mail/i)) { 
            $email_field = $data_field;
        }
        $DATA_FIELD{$data_field} = $YES;
    }
        
    # Read message file into memory and generate list of field names used
    
    if (!open(MESSAGE, $FORM{'message_file'})) {
        &error("Could not open $FORM{'message_file'} ($!).", '', '', 1);
    }
    $num_msg_fields = 0;
    while ($line = <MESSAGE>) {
        while ($line =~ /<<([^>]+)>>/g) {
            if (!$MESSAGE_FIELDS{$1}) {
                ++$num_msg_fields;
                if (!$DATA_FIELD{$1}) {
                    push(@missing_data_fields, $1);
                }
                $MESSAGE_FIELDS{$1} = $YES;
            }
        }
    }
    close(MESSAGE);
    
    # Output the page header
    
    if ($PAGE_HEADER_FILE) {
        if (!&parse_template($PAGE_HEADER_FILE, *STDOUT)) {
            &error($Error_Message, '', '', 1);
        }
    }
    else {
        print "<HTML>\n   <HEAD>\n";
        print "      <TITLE>Broadcaster: Field Selection</TITLE>\n";
        print "   </HEAD>\n   " . &print_body_tag;
    }
    
    # Output the form instructions
    
    print <<"(END HTML)";
      <FORM ACTION="$PROGRAM_URL" METHOD="POST">
         <CENTER><H1>Broadcaster: Field Selection</H1></CENTER>
         <P><I>Please select the required fields below, or input values to 
         be used for each message if these values are not in the data file.
         Click on <B>Continue</B> to see a sample message using your  
         choices, or click on your browser's <B>Back</B> button if you need 
         to make corrections to the filenames or field separator.</I>
(END HTML)

    # Output a warning message if there are any undefined data fields
    
    if (@missing_data_fields) {
        local($") = "<BR>";
        print <<"(END HTML)";
         <P>
         <I><B>WARNING:</B> Undefined Data Fields were found in the message file.
         This means that the message file contained data fields with names
         that did not match any of the field names in the data file header. 
         These fields will appear blank in outgoing messages. 
         <P>
         The following is a list of the undefined fields:</I> 
         <P>
         @missing_data_fields
(END HTML)
     }

     # Start the form table and output the email_to field popup list
     
     print <<"(END HTML)";
         <BR><BR><BR>
         <P><TABLE BORDER=0 CELLPADDING=1>
            <TR>
               <TD>
                  <P><B>*Send Email To:</B>
               </TD><TD>
                  <P><SELECT NAME="email_to">
                  <OPTION>-- None --
(END HTML)
    
    foreach $data_field (@data_fields) {
        if ($email_field && ($data_field eq $email_field)) {
            print "                 <OPTION SELECTED>$data_field\n";
        }
        else {
            print "                 <OPTION>$data_field\n";
        }

	    # Set up the data fields in a string for the rest of the popups
	    
	    $field_list .= (' ' x 21) . "<OPTION>$data_field\n";
	}

    # Remove the first indent from the data field string
    
    $field_list = substr($field_list, 21);

    # Finish up the email popup and output the additional popups
    
    print <<"(END HTML)";
                  </SELECT>
               </TD><TD>
                  <P>or input value: 
                  <INPUT TYPE=TEXT NAME="email_to_text" VALUE="" SIZE=40>
               </TD><TD>
                  <P>
               </TD></TR>
            <TR>
               <TD>
                  <P><B>*Send Email From:</B>
               </TD><TD>
                  <P><SELECT NAME="email_from">
                     <OPTION>-- None --
                     $field_list
                  </SELECT>
               </TD><TD>
                  <P>or input value: 
                  <INPUT TYPE=TEXT NAME="email_from_text" VALUE="" SIZE=40>
               </TD></TR>
            <TR>
               <TD>
                  <P><B>*Message Subject:</B>
               </TD><TD>
                  <P><SELECT NAME="email_subject">
                     <OPTION>-- None --
                     $field_list
                  </SELECT>
               </TD><TD>
                  <P>or input value: 
                  <INPUT TYPE=TEXT NAME="email_subject_text" VALUE="" 
                  SIZE=40>
               </TD></TR>
            <TR>
               <TD>
                  <P><B>CC To:</B>
               </TD><TD>
                  <P><SELECT NAME="email_cc">
                     <OPTION>-- None --
                     $field_list
                  </SELECT>
               </TD><TD>
                  <P>or input value: 
                  <INPUT TYPE=TEXT NAME="email_cc_text" VALUE="" SIZE=40>
               </TD></TR>
            <TR>
               <TD>
                  <P><B>BCC To:</B>
               </TD><TD>
                  <P><SELECT NAME="email_bcc">
                     <OPTION>-- None --
                     $field_list
                  </SELECT>
               </TD><TD>
                  <P>or input value: 
                  <INPUT TYPE=TEXT NAME="email_bcc_text" VALUE="" SIZE=40>
               </TD></TR>
            <TR>
               <TD VALIGN=BOTTOM>
                  <P><B>Status Reports:</B>
               </TD><TD>
                  <P> <BR>
                  <P><INPUT TYPE=TEXT NAME="num_status_reports" 
                  VALUE="$num_status_reports" SIZE=4>
               </TD><TD> </TD></TR>
         </TABLE>
         <P>
         <I>Broadcaster has the ability to give you simple, one-line reports 
         while it processes your data file. This prevents your browser from 
         timing out if you have a lot of entries in your data file.  The 
         number above is the recommended number of reports based on the fact
         that your data file contains $num_data_records records. Feel free
         to change it.</I>
         <BR><BR><BR>
         
         <INPUT TYPE=HIDDEN NAME="PASS" VALUE="3">
         <INPUT TYPE=HIDDEN NAME="data_file" VALUE="$FORM{'data_file'}">
         <INPUT TYPE=HIDDEN NAME="data_delimiter" VALUE="$data_delimiter">
         <INPUT TYPE=HIDDEN NAME="csv" VALUE="$csv">
         <INPUT TYPE=HIDDEN NAME="message_file" VALUE="$FORM{'message_file'}">
         <INPUT TYPE=HIDDEN NAME="log_file" VALUE="$FORM{'log_file'}">
         <INPUT TYPE=HIDDEN NAME="use_log" VALUE="$FORM{'use_log'}">
         <INPUT TYPE=HIDDEN NAME="num_data_records" VALUE="$num_data_records">
         <INPUT TYPE=HIDDEN NAME="num_msg_fields" VALUE="$num_msg_fields">
         <INPUT TYPE=HIDDEN NAME="config" VALUE="$FORM{'config'}">
         <INPUT TYPE=SUBMIT NAME="Submit" VALUE="Continue..."> 
         <INPUT TYPE=RESET VALUE="Clear">
         <P> 
         <P>*Required field
      </FORM>
(END HTML)

    # Insert the page footer if specified, otherwise finish up the page
    
    if ($PAGE_FOOTER_FILE) {
        if (!&parse_template($PAGE_FOOTER_FILE, *STDOUT)) {
            &error($Error_Message, '', '', 1);
        }
    }
    else { print "   </BODY>\n</HTML>\n" }
}


############################################################################
# Process third call and generate sample message page                      #
############################################################################

elsif ($FORM{'PASS'} == 3) {

    # Make sure all the required fields were filled in
    
    if ($FORM{'email_to_text'}) { 
        if (!(&email_check($FORM{'email_to_text'}))) {
            push(@messages, '$email_to_text is an invalid email address.');
        }
        else { $email_to = $FORM{'email_to_text'} }
    }
    elsif (!($FORM{'email_to'} =~ /None/i)) {
        $email_to = $FORM{'email_to'};
        $email_to_field = $YES;
    }
    else {
        push(@messages, 'You need to specify a TO address.');
    }

    if ($FORM{'email_from_text'}) { 
        if (!(&email_check($FORM{'email_from_text'}))) {
            push(@messages, "$email_from_text is an invalid email address.");
        }
        else { $email_from = $FORM{'email_from_text'} } 
    }
    elsif (!($FORM{'email_from'} =~ /none/i)) {
        $email_from = $FORM{'email_from'};
        $email_from_field = $YES;
    }
    else { push(@messages, 'You need to specify a FROM address.') }

    if ($FORM{'email_subject_text'}) {
        $email_subject = $FORM{'email_subject_text'};
    }
    elsif (!($FORM{'email_subject'} =~ /None/i)) {
        $email_subject = $FORM{'email_subject'};
        $email_subject_field = $YES;
    }
    else { push(@messages, 'You need to specify a message subject.') }
    
    # Check the optional fields
    
    if ($FORM{'email_cc_text'}) {
        if (!(&email_check($FORM{'email_cc_text'}))) {
            push(@messages, '$email_cc_text is an invalid email address.');
        }
        else { $email_cc = $FORM{'email_cc_text'} }
    }
    elsif (!($FORM{'email_cc'} =~ /None/i)) {
        $email_cc = $FORM{'email_cc'};
        $email_cc_field = $YES;
    }

    if ($FORM{'email_bcc_text'}) {
        if (!(&email_check($FORM{'email_bcc_text'}))) {
            push(@messages, '$email_bcc_text is an invalid email address.');
        }
        else { $email_bcc = $FORM{'email_bcc_text'} }
    }
    elsif (!($FORM{'email_bcc'} =~ /None/i)) {
        $email_bcc = $FORM{'email_bcc'};
        $email_bcc_field = $YES;
    }

    # Print out any accumulated messages

    if (@messages) { &show_messages(@messages) }

    # Read first two records from data file
    
    if (!open(DATA_FILE, $FORM{'data_file'})) {
        &error("Could not open $FORM{'data_file'} ($!).", '', '', 1);
    }
    ($header_record, $sample_record) = <DATA_FILE>;
    close(DATA_FILE);
    chop($header_record);
    chop($sample_record) if $sample_record =~ /\n$/;    

    # Pull out the header and example data field values
    
    if ($FORM{'csv'}) { 
        @data_fields = &split_csv($header_record);
        @sample_fields = &split_csv($sample_record);
    }
    else {
        @data_fields = split(/$FORM{'data_delimiter'}/, $header_record);
        @sample_fields = split(/$FORM{'data_delimiter'}/, $sample_record);
    }
    
    # Match values to names
    
    for ($field = 0; $field < @data_fields; ++$field) {
        $CONFIG{$data_fields[$field]} = $sample_fields[$field];
    }
    
    # Output the page header
    
    if ($PAGE_HEADER_FILE) {
        if (!&parse_template($PAGE_HEADER_FILE, *STDOUT)) {
            &error($Error_Message, '', '', 1);
        }
    }
    else {
        print "<HTML>\n   <HEAD>\n";
        print "      <TITLE>Broadcaster: Sample Message</TITLE>\n";
        print "   </HEAD>\n" . &print_body_tag;
    }
    
    # Output the form header
    
    print "      <CENTER><H1>Broadcaster: Sample Message</H1></CENTER>\n";

    # If no form fields were specified for any of the recipient addresses
    # then print a warning. 
    
    if (!($email_to_field || $email_cc_field || $email_bcc_field)) {
        $to = "$FORM{'email_to'}, $FORM{'email_cc'}, $FORM{'email_bcc'}";
        $to =~ s/(, )|(, , )$//;
        $to =~ s/, ([^,]+)$/, and $1/;
        print <<"(END HTML)";
      <P><I><B>WARNING:</B> You have not specified a data field for
      any of the TO, CC, or BCC addresses. This means that all copies of
      the message ($FORM{'num_data_records'} total) will be sent to $to.</I>
      <P>
(END HTML)
    }
    print <<"(END HTML)";
      <P><I>Please make sure this is how you want your messages to appear
      and also indicate which records you want a message sent to.
      Click on <B>Send Messages</B> to begin sending messages or click on  
      your browser's <B>Back</B> button to make any necessary changes to 
      your configuration information.</I>
      <P> 
      <HR>
      <PRE>
(END HTML)

    # Output the message body header
    
    if ($email_to_field) {
        print "To: $CONFIG{$email_to}\n";
    }
    else {
        print "To: $email_to\n";
    }
    if ($email_from_field) {
        print "From: $CONFIG{$email_from}\n";
    }
    else {
        print "From: $email_from\n";
    }
    if ($email_cc_field) {
        print "CC: $CONFIG{$email_cc}\n";
    }
    elsif ($email_cc) {
        print "CC: $email_cc\n";
    }
    if ($email_subject_field) {
        print "Subject: $CONFIG{$email_subject}\n";
    }
    else {
        print "Subject: $email_subject\n";
    }
    print "\n";

    # Output the parsed message body
    
    &parse_template($FORM{'message_file'}, *STDOUT);
    print "</PRE>\n         <HR>\n         <BR><BR>\n";
    print qq'         <FORM ACTION="$PROGRAM_URL" METHOD="POST">';
    
    # Give them the option of sending as bulk email if there are no variable
    # tags in the message file
    
    if (!$FORM{'num_msg_fields'} && !$email_from_field) {
        print "         Send as bulk email:  \n";
        print '         <INPUT TYPE=CHECKBOX NAME="bulk" ';
        if (!$email_to_field) { print qq'VALUE="YES"><P>\n' }
        else { print qq'VALUE="NO"><P>\n' }
    }

    # Chop off any decimals they may have entered in the previous form in
    # the num_status_reports field
    
    $FORM{'num_status_reports'} = int($FORM{'num_status_reports'});
    
    # Output additional fields including the configuration data stored
    # in hidden fields

    print <<"(END HTML)";
         Send to records  
         <INPUT TYPE=TEXT NAME="start_record" SIZE=6 VALUE=1> through
         <INPUT TYPE=TEXT NAME="end_record" SIZE=6 
            VALUE="$FORM{'num_data_records'}"> ($FORM{'num_data_records'} max)
         <BR><BR><BR>
         <INPUT TYPE=HIDDEN NAME="PASS" VALUE="4">
         <INPUT TYPE=HIDDEN NAME="data_file" VALUE="$FORM{'data_file'}">
         <INPUT TYPE=HIDDEN NAME="data_delimiter" 
            VALUE="$FORM{'data_delimiter'}">
         <INPUT TYPE=HIDDEN NAME="csv" VALUE="$FORM{'csv'}">
         <INPUT TYPE=HIDDEN NAME="message_file" 
            VALUE="$FORM{'message_file'}">
         <INPUT TYPE=HIDDEN NAME="log_file" VALUE="$FORM{'log_file'}">
         <INPUT TYPE=HIDDEN NAME="use_log" VALUE="$FORM{'use_log'}">
         <INPUT TYPE=HIDDEN NAME="email_to" VALUE="$email_to">
         <INPUT TYPE=HIDDEN NAME="email_to_field" VALUE="$email_to_field">
         <INPUT TYPE=HIDDEN NAME="email_from" VALUE="$email_from">
         <INPUT TYPE=HIDDEN NAME="email_from_field" 
            VALUE="$email_from_field">
         <INPUT TYPE=HIDDEN NAME="email_subject" VALUE="$email_subject">
         <INPUT TYPE=HIDDEN NAME="email_subject_field" 
            VALUE="$email_subject_field">
         <INPUT TYPE=HIDDEN NAME="email_cc" VALUE="$email_cc">
         <INPUT TYPE=HIDDEN NAME="email_cc_field" VALUE="$email_cc_field">
         <INPUT TYPE=HIDDEN NAME="email_bcc" VALUE="$email_bcc">
         <INPUT TYPE=HIDDEN NAME="email_bcc_field" VALUE="$email_bcc_field">
         <INPUT TYPE=HIDDEN NAME="config" VALUE="$FORM{'config'}">
         <INPUT TYPE=HIDDEN NAME="num_data_records" 
            VALUE="$FORM{'num_data_records'}">
         <INPUT TYPE=HIDDEN NAME="num_status_reports" 
            VALUE="$FORM{'num_status_reports'}">
         <P><INPUT TYPE=SUBMIT NAME="Submit" VALUE="Send Messages">
         </FORM>
(END HTML)

    # Insert the page footer if specified, otherwise finish up the page
    
    if ($PAGE_FOOTER_FILE) {
        if (!&parse_template($PAGE_FOOTER_FILE, *STDOUT)) {
            &error($Error_Message, '', '', 1);
        }
    }
    else { print "   </BODY>\n</HTML>\n" }
}

############################################################################
# Process fourth call and generate email messages                          #
############################################################################

elsif ($FORM{'PASS'} == 4) {

    # Turn on autoflushing
    
    select((select(STDOUT), $| = 1)[0]);

    # Open the data file, get header data line, remove trailing newline
      
    if (!open(DATA_FILE, $FORM{'data_file'})) {
        &error("Could not open $FORM{'data_file'} ($!).", '', '', 1);
    }
    $header_record = <DATA_FILE>;
    chop($header_record) if ($header_record =~ /\n$/);
    
    # Pull the header fields out of the header data line (use local 
    # split_csv subroutine from the end of file if CSV format, else use 
    # plain split)

    if ($FORM{'csv'}) { 
        @header_fields = &split_csv($header_record);
    }
    else {
        @header_fields = split(/$FORM{'data_delimiter'}/, $header_record);
    }
    
    # Output the page header
    
    if ($PAGE_HEADER_FILE) {
        if (!&parse_template($PAGE_HEADER_FILE, *STDOUT)) {
            &error($Error_Message, '', '', 1);
        }
    }
    else {
        print "<HTML>\n   <HEAD>\n";
        print "      <TITLE>Broadcaster: Results</TITLE>\n";
        print "   </HEAD>\n   " . &print_body_tag;
    }
    
    # Calculate how often to print status reports
    
    if ($FORM{'num_status_reports'} > 0) {
        $status_frequency = $FORM{'num_data_records'} / 
                            $FORM{'num_status_reports'};
    }

    # Only print out status reports if they've been requested and we're
    # sending out more messages than the frequency
    
    $total_to_send = $FORM{'end_record'} - $FORM{'start_record'} + 1;
    if ($status_frequency && ($total_to_send > $status_frequency)) {
        print "      <CENTER><H1>Broadcaster: Status</H1></CENTER>\n";
    }

    # Process each record in the data file
    
    $email_to = $email_cc = $email_bcc = "";
    $address_count = $total_addresses = $record_num = 0;
    $invalid_emails = $duplicate_emails = $sent_count = 0;
    
    RECORD: while ($record = <DATA_FILE>) {
        
        # Initialize
        
        $to = $cc = $bcc = "";
        $invalid_to = $duplicate_to = 0;
        $invalid_cc = $duplicate_cc = 0;
        $invalid_bcc = $duplicate_bcc = 0;

        # Only process records that are within the requested range
        
        if (++$record_num < $FORM{'start_record'}) { next RECORD }
        elsif ($record_num > $FORM{'end_record'}) { last RECORD }
        chop($record) if ($record =~ /\n$/);
        
        # Pull out data fields and make sure same number as in header

        if ($FORM{'csv'}) { 
            @record_fields = &split_csv($record);
        }
        else {      
            @record_fields = split(/$FORM{'data_delimiter'}/, $record);
        }        
        if (@record_fields != @header_fields) {
            push(@invalid_records, $record_num);
            next RECORD unless $FORM{'send_invalid'} eq 'YES';
        }
        
        # Assign field values to field names
        
        for ($field = 0; $field < @record_fields; ++$field) {
            $CONFIG{$header_fields[$field]} = $record_fields[$field];
        }

        # Determine the recipient's address.

        if ($FORM{'email_to_field'}) { 
            $to = $CONFIG{$FORM{'email_to'}};
            ++$address_count;
        }
        else {
            $to = $FORM{'email_to'};
        }

        # Determine who the email message is from.

        if ($FORM{'email_from_field'}) {
            $email_from = $CONFIG{$FORM{'email_from'}};
        }
        else {
            $email_from = $FORM{'email_from'};
        }

        # Determine the email's subject.

        if ($FORM{'email_subject_field'}) {
            $email_subject = $CONFIG{$FORM{'email_subject'}};
        }
        else {
            $email_subject = $FORM{'email_subject'};
        }

        # Determine who, if anyone, to carbon copy the message to.

        if ($FORM{'email_cc_field'}) {
            $cc = $CONFIG{$FORM{'email_cc'}};
            ++$address_count;
       }
        else {
            $cc = $FORM{'email_cc'};
        }

        # Determine who, if anyone, to send blind carbon copies to.

        if ($FORM{'email_bcc_field'}) {
            $bcc = $CONFIG{$FORM{'email_bcc'}};
            ++$address_count;
        }
        else {
            $bcc = $FORM{'email_bcc'};
        }
    
        # Check that each address is valid and unique
           
        if ($to) {
            if (!&email_check($to)) {
                $invalid_email{$record_num} .= ", $to (TO)";
                ++$invalid_emails;
                --$address_count;
                $invalid_to = $YES;
            }
            else {
                if ($used_email{$to}) {
                    $duplicate_email{$record_num} .= ", $to (TO)";
                    ++$duplicate_emails;
                    --$address_count;
                    $duplicate_to = $YES;
                }
                else { $used_email{$to} = $YES }
            }
        }
        if ($cc) {
            if (!&email_check($cc)) {
                $invalid_email{$record_num} .= ", $cc (CC)";
                ++$invalid_emails;
                --$address_count;
                $invalid_cc = $YES;
            }
            else {
                if ($used_email{$cc}) {
                    $duplicate_email{$record_num} .= ", $cc (CC)";
                    ++$duplicate_emails;
                    --$address_count;
                    $duplicate_cc = $YES;
                }
                else { $used_email{$cc} = $YES }
            }
        }
        if ($bcc) {
            if (!&email_check($bcc)) {
                $invalid_email{$record_num} .= ", $bcc (BCC)";
                ++$invalid_emails;
                --$address_count;
                $invalid_bcc = $YES;
            }
            else {
                if ($used_email{$bcc}) {
                    $duplicate_email{$record_num} .= ", $bcc (BCC)";
                    ++$duplicate_emails;
                    --$address_count;
                    $duplicate_bcc = $YES;
                }
                else { $used_email{$bcc} = $YES }
            }
        }
        if ($invalid_email{$record_num}) {
            $invalid_email{$record_num} =~ s/^, //;
        }
        if ($duplicate_email{$record_num}) {
            $duplicate_email{$record_num} =~ s/^, //;
        }
         
        # If the addresses are okay, add them to the appropriate strings, 
        # otherwise skip to the next message 
        
        if (!$invalid_to && !$duplicate_to) { $email_to .= "$to," }
        elsif (!$FORM{'bulk'}) { next RECORD }
        else { --$sent_count }
        if (!$invalid_cc && !$duplicate_cc) { $email_cc .= "$cc," }
        if (!$invalid_bcc && !$duplicate_bcc) { $email_bcc .= "$bcc," }
        
        # Send email message if we're not in bulk mode, if we've reached
        # the maximum number of addresses for bulk mode, or if we're on the
        # last record
        
        if (!$FORM{'bulk'} || (($address_count % $BULK_LIMIT) == 0) 
          || ($record_num == $FORM{'end_record'})) {
              
            # Strip the trailing comma from each of the address fields
              
            chop($email_to, $email_cc, $email_bcc);
              
            if (&send_email($email_subject, $email_from, $email_to,
                            $email_cc, $email_bcc, $FORM{'message_file'})) {
                &error($Error_Message, '', '', 1);
            }
            $total_addresses += $address_count;
            $address_count = 0;
            $email_to = $email_cc = $email_bcc = "";
        }
        ++$sent_count;

        # Print out the status report if appropriate
        
        $num_processed = $record_num - $FORM{'start_record'} + 1;
        if ($status_frequency && ($total_to_send > $status_frequency) 
          && ($num_processed % $status_frequency) == 0) {
            $left = $total_to_send - $num_processed;
            if ($left) {
                print "      Record number $num_processed of $total_to_send ";
                print "processed. $left records remaining.<BR>\n";
            }
        }
    }
    
    # Wrap everything up
    
    close(DATA_FILE);
    if ($status_frequency && ($total_to_send > $status_frequency)) {
        print "      <BR>\nProcessing complete.\n<BR><BR><BR>\n";
    }
    
    # Turn off autoflushing
    
    select((select(STDOUT), $| = 0)[0]);
    
    # Output the results summary header
    
    print <<"(END HTML)";
      <CENTER><H1>Broadcaster: Results</H1></CENTER>
      <P><I>Message processing is now complete. Below is a summary of 
      Broadcaster's results:</I>
      <BR><BR><BR>
      <H3>Results Summary:</H3>
      <P><TABLE BORDER=0 CELLPADDING=1>
         <TR>
            <TD>
               <P><B>Records Successfully Sent To:</B>
            </TD><TD>
               <P><B>$sent_count (out of $total_to_send)</B>
            </TD></TR>
         <TR>
            <TD>
               <P><B>Total Addresses Sent To:</B>
            </TD><TD>
               <P><B>$total_addresses</B>
            </TD></TR>
         <TR>
            <TD>
(END HTML)

    $indent = ' ' x 14;
    
    # Create link if there were invalid records.

    $invalid_records = @invalid_records;
    if ($invalid_records) {
        print "$indent <P><B><A HREF=\"#BadRecords\">Invalid Records\n";
        print "$indent in Database File:</A></B>\n";
    }
    else {
        print "$indent <P><B>Invalid Records in Database File:</B>\n";
    }
    print <<"(END HTML)";
            </TD><TD>
               <P><B>$invalid_records</B>
            </TD></TR>
         <TR>
            <TD>
(END HTML)

    # Create link if there were invalid email addresses.

    if ($invalid_emails) {
       print "$indent <P><B><A HREF=\"#InvalidEmail\">Invalid Email\n";
       print "$indent Addresses in Database File:</A></B>\n";
    }
    else {
       print "$indent <P><B>Invalid Email Addresses in Database File:</B>\n";
    }
    print <<"(END HTML)";
            </TD><TD>
               <P><B>$invalid_emails</B>
            </TD></TR>
         <TR>
            <TD>
(END HTML)

    # Create link if there were duplcated email addresses.

    if ($duplicate_emails) {
        print "$indent <P><B><A HREF=\"#DuplicateEmail\">Duplicate\n";
        print "$indent Email Addresses in Database File:</A></B>\n";
    }
    else {
        print "$indent <P><B>Duplicate Email Addresses in Database File:</B>\n";
    }
    print <<"(END HTML)";
            </TD><TD>
               <P><B>$duplicate_emails</B>
            </TD></TR>
      </TABLE>
(END HTML)

    # Print details for invalid records
    
    if ($invalid_records) {
        print <<"(END HTML)";
      <BR><BR>
      <H3><A NAME="BadRecords"></A>Invalid Records in Database File:</H3>
      <P>There were <B>$invalid_records</B> invalid records found in the 
      database file. The following is a list of record numbers for those 
      records that were invalid. To find the invalid records in your 
      database file, find the line numbers in the file that match the record 
      numbers below.
      <P>
(END HTML)

        # Let them know if the invalid records were sent
        
        if ($FORM{'send_invalid'} eq 'YES') {
            print <<"(END HTML)";
      <I>Even though the records were invalid, the messages were still sent,
      because you checked the <B>Send to Invalid Records</B> checkbox in the
      File Selection form.</I>
      <P>
(END HTML)
        }
        foreach $invalid_record (@invalid_records) {
            print "      $invalid_record<BR>\n";
        }
    }

    # Print details for invalid email addresses
    
    if ($invalid_emails) {
        print <<"(END HTML)";
      <BR><BR>
      <H3><A NAME="InvalidEmail"></A>Invalid Email Addresses in Database
      File:</H3>
      <P>There were <B>$invalid_emails</B> invalid Email addresses found in
      the data file. The following is a listing of invalid addresses and the
      corresponding record numbers:
      <P><TABLE BORDER=0 CELLPADDING=1>
(END HTML)

        foreach $record_num (sort { $a <=> $b } keys %invalid_email) {
            print "         <TR>\n";
            print "            <TD>\n";
            print "               <P ALIGN=RIGHT>$record_num:\n";
            print "            </TD><TD>\n";
            print "               <P>$invalid_email{$record_num}\n";
            print "            </TD></TR>\n";
        }
        print "      </TABLE>\n";
    }

    # Print details for duplicate email addresses
    
    if ($duplicate_emails) {
        print <<"(END HTML)";
      <BR><BR>
      <H3><A NAME="DuplicateEmail"></A>Duplicate Email Addresses in
      Database File</H3>
      <P>There were <B>$duplicate_emails</B> duplicate Email addresses 
      found in the data file. The following is a list of the duplicate 
      addresses and the corresponding record numbers where the duplicates 
      were found:
      <P><TABLE BORDER=0 CELLPADDING=1>
(END HTML)

        foreach $record_num (sort { $a <=> $b } keys %duplicate_email) {
            print "         <TR>\n";
            print "            <TD>\n";
            print "               <P ALIGN=RIGHT>$record_num:\n";
            print "            </TD><TD>\n";
            print "               <P>$duplicate_email{$record_num}\n";;
            print "            </TD></TR>\n";
        }
        print "      </TABLE>\n";
    }

    # Insert the page footer if specified, otherwise finish up the page
    
    if ($PAGE_FOOTER_FILE) {
        if (!&parse_template($PAGE_FOOTER_FILE, *STDOUT)) {
            &error($Error_Message, '', '', 1);
        }
    }
    else { print "   </BODY>\n</HTML>\n" }

    # Write status message to log file

    if ($FORM{'use_log'} eq 'YES') {
        if (&lock($FORM{'log_file'}, $LOCK_DIR, $MAX_WAIT)) {
            &error($Error_Message); 
        }
        elsif (!open(LOG, ">>$FORM{'log_file'}")) {
            &error("Could not open $FORM{'log_file'} ($!).", '', '', 1);
        }
        print LOG "Broadcaster Log For Run On: $DATE\n";
        print LOG "Message File:               $FORM{'message_file'}\n";
        print LOG "Data File:                  $FORM{'data_file'}\n";
        print LOG "Total Records:              $FORM{'num_data_records'}\n";
        print LOG "Records Processed:          $FORM{'start_record'}-$FORM{'end_record'}\n";
        print LOG "Messages Sent:              $sent_count\n";
        print LOG "Total Addresses:            $total_addresses\n";
        print LOG "Invalid Records:            $invalid_records\n";
        print LOG "Invalid Email Addresses:    $invalid_emails\n";
        print LOG "Duplicate Email Addresses:  $duplicate_emails\n\n";
        close(LOG);
        &unlock($FORM{'use_log'}, $LOCK_DIR);
    }
}


############################################################################
# Local subroutines                                                        #
############################################################################

sub print_body_tag {

    local($body_tag) = "<BODY";

    if ($BODY_BACKGROUND) {
        $body_tag .= " BACKGROUND=\"$BODY_BACKGROUND\"";
    }
    if ($BODY_BGCOLOR) {
        $body_tag .= " BGCOLOR=\"$BODY_BGCOLOR\"";
    }
    if ($BODY_TEXT) {
        $body_tag .= " TEXT=\"$BODY_TEXT\"";
    }
    if ($BODY_LINK) {
        $body_tag .= " LINK=\"$BODY_LINK\"";
    }
    if ($BODY_VLINK) {
        $body_tag .= " VLINK=\"$BODY_VLINK\"";
    }
    $body_tag .= ">\n";

    return $body_tag;
}

sub show_messages {

    # This takes a list of messages and outputs them in an HTML page
    # using show_message() to process each individual message.

    local(@messages) = @_;
    local($index) = 0;

    &show_message($messages[$index], (@messages != 1));
    for ($index = 1; $index < (@messages - 1); ++$index) {
        &show_message($messages[$index], 2);
    }
    &show_message($messages[$index], 3) if $index < @messages;

}

sub show_message {

    # This will show a message as the start of a page, the middle of a page
    # the end of a page, or as a whole page
    
    local ($message, $flag) = @_;

    # If flag is set to 0 or 1, print out the header.
    
    if ($flag < 2) {
        print "<HTML>\n";
        print "   <HEAD>\n";
        print "      <TITLE>Broadcaster Message</TITLE>\n";
        print "   </HEAD>\n";
        print &print_body_tag;
        print "      <CENTER>\n";
    }

    # Print out the message, regardless of a flag.
    
    print "      <H2>$message</H2>\n";

    # If the flag is set to 0 or 3 finish up the page and exit the program.  
    # This allows us to build up multiple messages if the flag is set to 1
    # or 2.

    if ($flag == 0 || $flag == 3) {
        print "      <P><H3>Click on your browser's Back button to return\n";
        print "      to the form</H3>\n";
        print "      </CENTER>\n";
        print "   </BODY>\n";
        print "</HTML>\n";
        exit(0);
    }
}

sub split_csv {
    
    # This will split a CSV-delimited record into its component fields
    
    local ($record) = $_[0];
    local (@fields) = ();
    
    # Separate any quote-enclosed records, process both types
    
    foreach $segment (split(/("[^"]*"),* */, $record)) {        
        if ($segment =~ /^"(.*)"$/) { push(@fields, $1) }
        else { push(@fields, split(/, */, $segment)) }
    }
    return(@fields);
}