Приглашаем посетить
Sends an email message
############################################################################
# #
# send_email() Version 1.5 #
# Written by Craig Patchett craig@patchett.com #
# and Matthew Wright mattw@worldwidemart.com #
# Created 10/4/96 Last Modified 3/31/97 #
# #
# Copyright 1997 Craig Patchett & Matthew Wright. All Rights Reserved. #
# This subroutine 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.) #
# #
# Function: Sends an email message and optional attached files via #
# a direct connection to an SMTP server. #
# #
# Usage: &send_email($subject, $from, $to[, $cc, $bcc, $body, #
# $files, $encoding]); #
# #
# Variables: $subject -- String containing subject of message. #
# Example 'Buy the CGI/Perl Cookbook!' #
# $from -- String containing email address of person #
# sending message. An associated name can #
# follow the address if placed in parentheses. #
# Example 'me@home.com (My Name)' #
# $to -- String containing email addresses to send #
# message to. Multiple addresses should be #
# separated by commas. Associated names #
# can follow each address if placed in #
# parentheses. #
# Example 'him@place.com (Name),her@place.com' #
# $cc -- String containing email addresses to send #
# copies of the message to. Same format as $to.#
# $bcc -- String containing email addresses to send #
# blind copies of the message to (i.e., nobody #
# else receiving the message will know that #
# copies were sent to these addresses). Same #
# format as $to. #
# $body -- Full path to file containing the body of the #
# message or text containing body of message #
# (if $body doesn't begin with a directory #
# delimiter and contains at least one space #
# then the subroutine assumes it contains #
# message text). #
# Example '/home/user/body.txt' #
# Example 'This is message text.' #
# $files -- String containing a list of full paths, #
# separated by commas, to files to be attached #
# to the message. #
# Example '/home/user/file1, /home/user/file2' #
# $encoding -- String containing a list of encoding types, #
# separated by commas, to match the list of #
# files in $file. Valid types are: text, #
# uuencode, base64 #
# Example 'text, base64' #
# #
# Returns: 0 if successful #
# 1 if error creating socket and connecting to server #
# 2, @bad_addresses if addresses in *to, *cc, or *bcc were #
# rejected by the server. (Note: Just because addresses #
# were not rejected does not ensure they are valid.) #
# 3 if error initiating conversation with server #
# 4 if error specifying message sender #
# 5 if error specifying message recipients #
# 6 if error initiating message body transfer #
# 7 if error sending message body #
# 8 if error shutting down server #
# 9 if file couldn't be opened or found #
# 10 if uuencode error #
# 11 if base 64 encode error #
# #
# Uses Global: $Error_Message for descriptive error messages #
# $SMTP_SERVER for the name of the SMTP server #
# $WEB_SERVER for the name of the server running the #
# script #
# #
# Requires: base64.pl #
# chkemail.pl #
# error.pl #
# uuencode.pl #
# #
# Files Created: None #
# #
############################################################################
use Socket;
sub send_email {
local($subject, $from, $to, $cc, $bcc, $body, $files, $encoding) = @_;
local($i, $mime_id, $error, $name, $status, $message) = '';
local(@MIME_FILES, @MIME_TYPES, @ATTACH_FILES, @ENCODING) = ();
# Attempt to set default values if globals aren't set
if (!$WEB_SERVER) { $WEB_SERVER = $ENV{'SERVER_NAME'} }
if (!$WEB_SERVER) {
$Error_Message = "$WEB_SERVER is not set.";
return(1);
}
if (!$SMTP_SERVER) {
$SMTP_SERVER = "smtp.$WEB_SERVER";
$SMTP_SERVER =~ s/^smtp\.[^.]+\.([^.]+\.)/smtp.$1/;
}
# Split the input into arrays where needed, since values are passed
# as strings separated by commas.
local(@to) = split(/, */, $to);
local(@cc) = split(/, */, $cc);
local(@bcc) = split(/, */, $bcc);
local(@attach_files) = split(/, */, $files);
local(@encoding) = split(/, */, $encoding);
# Check to see what file encoding is being used and if necessary, set the
# mime flag and id.
for ($i = 0; $i < @attach_files; ++$i) {
if (!(-e $attach_files[$i])) {
$Error_Message = "$attach_files[$i] does not exist.";
return(9);
}
if ($encoding[$i] eq 'base64') {
push(@MIME_FILES, $attach_files[$i]);
push(@MIME_TYPES, $encoding[$i]);
}
else {
push(@ATTACH_FILES, $attach_files[$i]);
push(@ENCODING, $encoding[$i]);
}
}
if (@MIME_FILES) {
push(@ATTACH_FILES, @MIME_FILES);
push(@ENCODING, @MIME_TYPES);
$mime_id = 'CGI_Perl_Cookbook_-' . time;
}
# SMTP commands end in CRLF (\015\012)
local($CRLF) = "\015\012";
# Set up other variables
local($SMTP_SERVER_PORT) = 25;
local($AF_INET) = ($] > 5 ? AF_INET : 2);
local($SOCK_STREAM) = ($] > 5 ? SOCK_STREAM : 1);
local(@bad_addresses) = ();
$, = ', ';
$" = ', ';
# Translate hostnames to corresponding addresses and pack
local($local_address) = (gethostbyname($WEB_SERVER))[4];
local($local_socket_address) = pack('S n a4 x8', $AF_INET, 0, $local_address);
local($server_address) = (gethostbyname($SMTP_SERVER))[4];
local($server_socket_address) = pack('S n a4 x8', $AF_INET, $SMTP_SERVER_PORT, $server_address);
# Translate protocol name to corresponding number
local($protocol) = (getprotobyname('tcp'))[2];
# Make the socket filehandle
if (!socket(SMTP, $AF_INET, $SOCK_STREAM, $protocol)) {
$Error_Message = "Could not make socket filehandle ($!).";
return(1);
}
# Give the socket an address
bind(SMTP, $local_socket_address);
# Connect to the server
if (!(connect(SMTP, $server_socket_address))) {
$Error_Message = "Could not connect to server ($!).";
return(1);
}
# Set the socket to be line buffered
local($old_selected) = select(SMTP);
$| = 1;
select($old_selected);
# Set regex to handle multiple line strings
$* = 1;
# Read first response from server (wait for .75 seconds first)
select(undef, undef, undef, .75);
sysread(SMTP, $_, 1024);
# Initiate a conversation with the server
print SMTP "HELO $WEB_SERVER$CRLF";
sysread(SMTP, $_, 1024);
while (/(^|(\r?\n))[^0-9]*((\d\d\d).*)$/g) { $status = $4; $message = $3}
if ($status != 250) { $Error_Message = $message; return(3) }
# Tell the server where we're sending from
print SMTP "MAIL FROM:<$from>$CRLF";
sysread(SMTP, $_, 1024);
if (!/[^0-9]*250/) { $Error_Message = $_; return(4) }
# Tell the server where we're sending to
local($good_addresses) = 0;
foreach $address (@to, @cc, @bcc) {
if ($address) {
# Make sure address is enclosed in <>
$address =~ /(\(.*\))/;
$name = $1 ? "$1 " : '';
$address =~ /([^<)\s]+@\S+\.[^>(\s]+)/;
$address = "<$1>";
# Hand it to the server
print SMTP "RCPT TO:$address$CRLF";
sysread(SMTP, $_, 1024);
/[^0-9]*(\d\d\d)/;
if ($1 ne '250') { push(@bad_addresses, $name$address, $_) }
else { ++$good_addresses }
}
}
if (!$good_addresses) {
$Error_Message = $_;
return(5, @bad_addresses)
}
# Give the server the message header
print SMTP "DATA$CRLF";
sysread(SMTP, $_, 1024);
if (!/[^0-9]*354/) { $Error_Message = $_; return(6) }
print SMTP "To: @to$CRLF";
print SMTP "From: $from$CRLF";
print SMTP "CC: @cc$CRLF" if $cc;
print SMTP "Subject: $subject$CRLF";
# If there are mime files to attach, we need special headers.
if ($mime_id) {
print SMTP "x-sender: $from$CRLF";
print SMTP "x-mailer: CGI/Perl Cookbook$CRLF";
print SMTP "Mime-Version: 1.0$CRLF";
print SMTP "Content-Type: multipart/mixed; boundary=\"$mime_id\"$CRLF$CRLF";
print SMTP "--$mime_id$CRLF";
print SMTP "Content-Type: text/plain; charset=\"US-ASCII\"$CRLF$CRLF";
}
else { print SMTP $CRLF }
# Output the message body.
if ($body) {
if (!($body =~ /^[\\\/:]/) && ($body =~ /\s/)) { print SMTP $body }
elsif (-e $body && -T $body) { &parse_template($body, *SMTP) }
}
print SMTP $CRLF;
# Attach each file.
for ($i = 0; $i < @ATTACH_FILES; ++$i) {
$attach_file = $ATTACH_FILES[$i];
$encoding = $ENCODING[$i];
# Split the filename by directories. / for unix, \ for dos, : for mac
$attach_file =~ /[\\\/:]([^\\\/:]+)$/g;
$filename = $1;
# Attach text file.
if ($encoding eq 'text' && -e $attach_file) {
if (!(open(TEXT, $attach_file))) {
$Error_Message = "Can't open text file $attach_file ($!).";
return(9);
}
print SMTP "Attachment:\t$filename$CRLF";
print SMTP "Encoding:\tNone$CRLF$CRLF";
# Attach the text file, converting any lines with a single period
while (<TEXT>) { s/^\.([\n\r\f]+)/..$1/; print SMTP }
close(TEXT);
print SMTP "\n\n";
}
# Attach uuencoded file.
elsif ($encoding eq 'uuencode' && -e $attach_file) {
print SMTP "Attachment:\t$filename$CRLF";
print SMTP "Encoding:\tUUEncoded$CRLF";
print SMTP "begin 600 $filename\n";
$uuencoded_data = &uuencode($attach_file, 'open->attach_file');
if (!$uuencoded_data) { return(10) }
print SMTP $uuencoded_data;
print SMTP "`\nend\n\n";
}
# Attach MIME files
elsif ($encoding eq 'base64' && -e $attach_file) {
print SMTP "--$mime_id$CRLF";
# If it is a text file, print a text content type, otherwise print
# an octet stream.
if (-T $attach_file) {
print SMTP "Content-type: text/plain; charset=US-ASCII; name=\"$filename\"$CRLF";
}
else {
print SMTP "Content-type: application/octet-stream; name=\"$filename\"$CRLF";
}
print SMTP "Content-transfer-encoding: base64$CRLF$CRLF";
# Encode the data with base 64.
$base64_encoded_data = &base64_encode_file($attach_file);
if (!$base64_encoded_data) { return(11) }
print SMTP "$base64_encoded_data$CRLF";
}
}
# Print the final mime id if necessary
if ($mime_id) { print SMTP "--$mime_id--$CRLF" }
# End the conversation
print SMTP "$CRLF.$CRLF";
sysread(SMTP, $_, 1024);
if (!/[^0-9]*250/) { $Error_Message = $_; return(7) }
# Disconnect from the server
if (!shutdown(SMTP, 2)) {
$Error_Message = "Could not shut down server ($!).";
return(8, @bad_addresses);
}
elsif (@bad_addresses) {
return(2, @bad_addresses);
}
else { return(0) }
}
1;