Приглашаем посетить
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);
}