From 968bdc7c90f718560b84eb3b7e8e1d02cf4270f3 Mon Sep 17 00:00:00 2001 From: thomascube Date: Fri, 30 Sep 2005 22:04:50 +0000 Subject: Improved SMTP script for sending mails. Now using the PEAR::Net_SMTP class --- program/include/rcube_smtp.inc | 324 +++++++++++++++++++++++++++++++++++++ program/lib/Mail/mime.php | 2 +- program/lib/smtp.inc | 351 ---------------------------------------- program/steps/mail/sendmail.inc | 37 +++-- 4 files changed, 343 insertions(+), 371 deletions(-) create mode 100644 program/include/rcube_smtp.inc delete mode 100644 program/lib/smtp.inc (limited to 'program') diff --git a/program/include/rcube_smtp.inc b/program/include/rcube_smtp.inc new file mode 100644 index 000000000..9f1932703 --- /dev/null +++ b/program/include/rcube_smtp.inc @@ -0,0 +1,324 @@ + | + +-----------------------------------------------------------------------+ + + $Id$ + +*/ + + +// include required PEAR classes +require_once('Net/SMTP.php'); + + +// define headers delimiter +define('SMTP_MIME_CRLF', "\r\n"); + +$SMTP_CONN = null; + +/** + * Function for sending mail using SMTP. + * + * @param string Sender e-Mail address + * + * @param mixed Either a comma-seperated list of recipients + * (RFC822 compliant), or an array of recipients, + * each RFC822 valid. This may contain recipients not + * specified in the headers, for Bcc:, resending + * messages, etc. + * + * @param mixed The message headers to send with the mail + * Either as an associative array or a finally + * formatted string + * + * @param string The full text of the message body, including any Mime parts, etc. + * + * @return bool Returns TRUE on success, or FALSE on error + * @access public + */ +function smtp_mail($from, $recipients, $headers, $body) + { + global $SMTP_CONN, $CONFIG, $SMTP_ERROR; + $smtp_timeout = null; + $smtp_port = is_numeric($CONFIG['smtp_port']) ? $CONFIG['smtp_port'] : 25; + + // create Net_SMTP object and connect to server + if (!is_object($smtp_conn)) + { + $SMTP_CONN = new Net_SMTP($CONFIG['smtp_server'], $smtp_port, 'localhost'); + + // set debugging + if ($CONFIG['debug_level'] & 8) + $SMTP_CONN->setDebug(TRUE); + + + // try to connect to server and exit on failure + if (PEAR::isError($SMTP_CONN->connect($smtp_timeout))) + { + $SMTP_CONN = null; + $SMTP_ERROR .= "Connection failed\n"; + return FALSE; + } + + + // attempt to authenticate to the SMTP server + if ($CONFIG['smtp_user'] && $CONFIG['smtp_pass']) + { + if (PEAR::isError($SMTP_CONN->auth($CONFIG['smtp_user'], $CONFIG['smtp_pass']))) + { + smtp_reset(); + $SMTP_ERROR .= "authentication failure\n"; + return FALSE; + } + } + } + + + // prepare message headers as string + if (is_array($headers)) + { + $headerElements = smtp_prepare_headers($headers); + if (!$headerElements) + { + smtp_reset(); + return FALSE; + } + + list($from, $text_headers) = $headerElements; + } + else if (is_string($headers)) + $text_headers = $headers; + else + { + smtp_reset(); + $SMTP_ERROR .= "Invalid message headers\n"; + return FALSE; + } + + // exit if no from address is given + if (!isset($from)) + { + smtp_reset(); + $SMTP_ERROR .= "No From address has been provided\n"; + return FALSE; + } + + + // set From: address + if (PEAR::isError($SMTP_CONN->mailFrom($from))) + { + smtp_reset(); + $SMTP_ERROR .= "Failed to set sender '$from'\n"; + return FALSE; + } + + + // prepare list of recipients + $recipients = smtp_parse_rfc822($recipients); + if (PEAR::isError($recipients)) + { + smtp_reset(); + return FALSE; + } + + + // set mail recipients + foreach ($recipients as $recipient) + { + if (PEAR::isError($SMTP_CONN->rcptTo($recipient))) + { + smtp_reset(); + $SMTP_ERROR .= "Failed to add recipient '$recipient'\n"; + return FALSE; + } + } + + + // Send the message's headers and the body as SMTP data. + if (PEAR::isError($SMTP_CONN->data("$text_headers\r\n$body"))) + { + smtp_reset(); + $SMTP_ERROR .= "Failed to send data\n"; + return FALSE; + } + + + return TRUE; + } + + + +/** + * Reset the global SMTP connection + * @access public + */ +function smtp_reset() + { + global $SMTP_CONN; + + if (is_object($SMTP_CONN)) + { + $SMTP_CONN->rset(); + smtp_disconnect(); + } + } + + + +/** + * Disconnect the global SMTP connection and destroy object + * @access public + */ +function smtp_disconnect() + { + global $SMTP_CONN; + + if (is_object($SMTP_CONN)) + { + $SMTP_CONN->disconnect(); + $SMTP_CONN = null; + } + } + + +/** + * Take an array of mail headers and return a string containing + * text usable in sending a message. + * + * @param array $headers The array of headers to prepare, in an associative + * array, where the array key is the header name (ie, + * 'Subject'), and the array value is the header + * value (ie, 'test'). The header produced from those + * values would be 'Subject: test'. + * + * @return mixed Returns false if it encounters a bad address, + * otherwise returns an array containing two + * elements: Any From: address found in the headers, + * and the plain text version of the headers. + * @access private + */ +function smtp_prepare_headers($headers) + { + $lines = array(); + $from = null; + + foreach ($headers as $key => $value) + { + if (strcasecmp($key, 'From') === 0) + { + $addresses = smtp_parse_rfc822($value); + + if (is_array($addresses)) + $from = $addresses[0]; + + // Reject envelope From: addresses with spaces. + if (strstr($from, ' ')) + return FALSE; + + + $lines[] = $key . ': ' . $value; + } + else if (strcasecmp($key, 'Received') === 0) + { + $received = array(); + if (is_array($value)) + { + foreach ($value as $line) + $received[] = $key . ': ' . $line; + } + else + { + $received[] = $key . ': ' . $value; + } + + // Put Received: headers at the top. Spam detectors often + // flag messages with Received: headers after the Subject: + // as spam. + $lines = array_merge($received, $lines); + } + + else + { + // If $value is an array (i.e., a list of addresses), convert + // it to a comma-delimited string of its elements (addresses). + if (is_array($value)) + $value = implode(', ', $value); + + $lines[] = $key . ': ' . $value; + } + } + + return array($from, join(SMTP_MIME_CRLF, $lines) . SMTP_MIME_CRLF); + } + + + +/** + * Take a set of recipients and parse them, returning an array of + * bare addresses (forward paths) that can be passed to sendmail + * or an smtp server with the rcpt to: command. + * + * @param mixed Either a comma-seperated list of recipients + * (RFC822 compliant), or an array of recipients, + * each RFC822 valid. + * + * @return array An array of forward paths (bare addresses). + * @access private + */ +function smtp_parse_rfc822($recipients) + { + // if we're passed an array, assume addresses are valid and implode them before parsing. + if (is_array($recipients)) + $recipients = implode(', ', $recipients); + + $addresses = array(); + $recipients = smtp_explode_quoted_str(",", $recipients); + + reset($recipients); + while (list($k, $recipient) = each($recipients)) + { + $a = explode(" ", $recipient); + while (list($k2, $word) = each($a)) + { + if ((strpos($word, "@") > 0) && (strpos($word, "\"")===false)) + { + $word = ereg_replace('^<|>$', '', trim($word)); + if (in_array($word, $addresses)===false) + array_push($addresses, $word); + } + } + } + return $addresses; + } + + +function smtp_explode_quoted_str($delimiter, $string) + { + $quotes=explode("\"", $string); + while ( list($key, $val) = each($quotes)) + if (($key % 2) == 1) + $quotes[$key] = str_replace($delimiter, "_!@!_", $quotes[$key]); + $string=implode("\"", $quotes); + + $result=explode($delimiter, $string); + while (list($key, $val) = each($result)) + $result[$key] = str_replace("_!@!_", $delimiter, $result[$key]); + + return $result; + } + + +?> diff --git a/program/lib/Mail/mime.php b/program/lib/Mail/mime.php index 48bd5f5c0..6b3a992b0 100644 --- a/program/lib/Mail/mime.php +++ b/program/lib/Mail/mime.php @@ -119,7 +119,7 @@ class Mail_mime $this->_build_params = array( 'text_encoding' => '7bit', 'html_encoding' => 'quoted-printable', - 'header_encoding' => 'quoted-printable', + 'head_encoding' => 'quoted-printable', '7bit_wrap' => 998, 'html_charset' => 'ISO-8859-1', 'text_charset' => 'ISO-8859-1', diff --git a/program/lib/smtp.inc b/program/lib/smtp.inc deleted file mode 100644 index ebff9d263..000000000 --- a/program/lib/smtp.inc +++ /dev/null @@ -1,351 +0,0 @@ - -// -// This file is part of IlohaMail. -// IlohaMail is free software released under the GPL -// license. See enclosed file COPYING for details, -// or see http://www.fsf.org/copyleft/gpl.html -// -///////////////////////////////////////////////////////// - -/******************************************************** - - AUTHOR: Ryo Chijiiwa - FILE: include/smtp.php - PURPOSE: - Provide SMTP functionality using pure PHP. - PRE-CONDITIONS: - The functions here require a SMTP server configured to allow relaying. - POST-CONDITIONS: - The following global variables are returned: - $smtp_errornum - error number, 0 if successful - $smtp_error - error message(s) - $SMTP_TYPE - Optional - COMMENTS: - The optional $smtp_message_file can be used for sending extremely large - messages. Storing large messages in a variable will consume whatever - amount of memory required, which may be more than is available if dealing - with messages with large attachments. By storing them in a file, it becomes - possible to read/send bits at a time, drastically reducing memory useage. - - This library only provides bare-bones SMTP functionality. It is up to the - parent code to form valid RFC822 (MIME) messages. - -********************************************************/ - - //set some global constants - if (strcasecmp($SMTP_TYPE, "courier")==0){ - $SMTP_REPLY_DELIM = "-"; - $SMTP_DATA_REPLY = 250; - }else{ - $SMTP_REPLY_DELIM = " "; - $SMTP_DATA_REPLY = 354; - } - - /* fgets replacement that's multi-line aware */ - function smtp_get_response($fp, $len){ - $end = false; - do{ - $line = chop(fgets($fp, 5120)); - // echo "SMTP:".$line."
\n"; flush(); - if ((strlen($line)==3) || ($line[3]==' ')) $end = true; - }while(!$end); - - return $line; - } - - - function smtp_check_reply($reply){ - global $smtp_error; - global $SMTP_REPLY_DELIM; - - $a = explode($SMTP_REPLY_DELIM, chop($reply)); - - if (count($a) >= 1){ - - if ($a[0]==250||$a[0]==354) return true; - else{ - $smtp_error .= $reply."\n"; - } - }else{ - $smtp_error .= "Invalid SMTP response line: $reply\n"; - } - - return false; - } - - - function smtp_split($str){ - $result = array(); - $pos = strpos($str, " "); - if ($pos===false){ - $result[0] = $str; - }else{ - $result[0] = substr($str, 0, $pos); - $result[1] = substr($str, $pos+1); - } - - return $result; - } - - - function smtp_ehlo(&$conn, $host){ - $result = ""; - fputs($conn, "EHLO $host\r\n"); - //echo "Sent: EHLO $host\n"; flush(); - do{ - $line = fgets($conn, 2048); - //echo "Got: $line"; flush(); - $a = explode(" ", $line); - if ($a[0]=="250-AUTH") $result .= substr($line, 9); - }while(!is_numeric($a[0])); - - if ($a[0]==250) return $result; - else return $a[0]; - - } - - - function smtp_auth_login(&$conn, $user, $pass){ - $auth["username"] = base64_encode($user); - $auth["password"] = base64_encode($pass); - - fputs($conn, "AUTH LOGIN\r\n"); - - //echo "Sent: AUTH LOGIN\n"; flush(); - - //get first line - $line = smtp_get_response($conn, 1024); - //echo "AUTH_LOGIN << $line"; flush(); - $parts = smtp_split($line); - //valid reply? - if (($parts[0]!=334) || (empty($parts[1]))) return false; - //send data - $prompt = eregi_replace("[^a-z]", "", strtolower(base64_decode($parts[1]))); - fputs($conn, $auth[$prompt]."\r\n"); - //echo "AUT_LOGIN >> ".$auth[$prompt]."\n"; flush(); - - //get second line - $line = smtp_get_response($conn, 1024); - //echo "AUTH_LOGIN << $line"; flush(); - $parts = smtp_split($line); - //valid reply? - if (($parts[0]!=334) || (empty($parts[1]))) return false; - $prompt = eregi_replace("[^a-z]", "", strtolower(base64_decode($parts[1]))); - fputs($conn, $auth[$prompt]."\r\n"); - //echo "AUT_LOGIN >> ".$auth[$prompt]."\n"; flush(); - - $line = smtp_get_response($conn, 1024); - //echo "AUTH_LOGIN << $line"; flush(); - $parts = smtp_split($line); - return ($parts[0]==235); - } - - - function smtp_connect($host, $port, $user, $pass){ - global $smtp_errornum; - global $smtp_error; - - //auth user? - global $SMTP_USER, $SMTP_PASSWORD; - if ((!empty($SMTP_USER)) && (!empty($SMTP_PASSWORD))){ - $user = $SMTP_USER; - $pass = $SMTP_PASSWORD; - } - - // echo "User: $user
\n"; - - //figure out auth mode - global $AUTH_MODE; - $auth_mode = $AUTH_MODE["smtp"]; - if (empty($auth_mode)) $auth_mode = "none"; - if (empty($user) || empty($pass)) $auth_mode = "none"; - - // echo "authmode: $auth_mode
\n"; flush(); - - //initialize defaults - if (empty($host)) $host = "localhost"; - if (empty($port)) $port = 25; - - // echo "Connecting to $host:$port
\n"; flush(); - - //connect to SMTP server - $conn = fsockopen($host, $port); - - if (!$conn){ - //echo "fsockopen failed\n"; - $smtp_error = "Couldn't connect to $host:$port
\n"; - return false; - } - - //read greeting - $greeting = smtp_get_response($conn, 1024); - - // echo "Connected: $greeting
\n"; flush(); - - if (($auth_mode=="check") || ($auth_mode=="auth")){ - // echo "Trying EHLO
\n"; flush(); - $auth_modes = smtp_ehlo($conn, $_SERVER["SERVER_NAME"]); - // echo "smtp_ehlo returned: $auth_modes
\n"; flush(); - if ($auth_modes===false){ - $smtp_error = "EHLO failed\n"; - $conn = false; - }else if (stristr($auth_modes, "LOGIN")!==false){ - echo "trying AUTH LOGIN\n"; flush(); - if (!smtp_auth_login($conn, $user, $pass)){ - //echo "CONN: AUTH_LOGIN failed\n"; flush(); - $conn = false; - } - //echo "Conn after LOGIN: $conn
\n"; flush(); - } - }else{ - fputs($conn, "HELO ".$_SERVER["SERVER_NAME"]."\r\n"); - $line = smtp_get_response($conn, 1024); - if (!smtp_check_reply($line)){ - $conn = false; - $smtp_error .= $line."\n"; - } - // echo "after HELO: $conn
\n"; flush(); - } - - return $conn; - } - - - function smtp_close($conn){ - fclose($conn); - } - - - function smtp_mail($conn, $from, $recipients, $message, $is_file){ - global $smtp_errornum; - global $smtp_error; - global $SMTP_DATA_REPLY; - - //check recipients and sender addresses - if ((count($recipients)==0) || (!is_array($recipients))){ - $smtp_errornum = -1; - $smtp_error .= "Recipients list is empty\n"; - return false; - } - if (empty($from)){ - $smtp_errornum = -2; - $smtp_error .= "From address unspecified\n"; - return false; - } - - if (!$conn){ - $smtp_errornum = -3; - $smtp_error .= "Invalid connection\n"; - } - - if (!ereg("^<", $from)) $from = "<".$from; - if (!ereg(">$", $from)) $from = $from.">"; - - //send MAIL FROM command - $command = "MAIL FROM: $from\r\n"; - // echo nl2br(htmlspecialchars($command)); - fputs($conn, $command); - - if (smtp_check_reply(smtp_get_response($conn, 1024))){ - //send RCPT TO commands, count valid recipients - $num_recipients = 0; - while ( list($k, $recipient) = each($recipients) ){ - $command = "RCPT TO: $recipient\r\n"; - fputs($conn, $command); - $reply = smtp_check_reply(smtp_get_response($conn, 1024)); - if ($reply) $num_recipients++; - else $smtp_error .= $reply."\n"; - } - - //error out if no valid recipiets - if ($num_recipients == 0){ - $smtp_errornum = -1; - $smtp_error .= "No valid recipients\n"; - return false; - } - - //send DATA command - fputs($conn, "DATA\r\n"); - $reply = chop(smtp_get_response($conn, 1024)); - $a = explode(" ", $reply); - - //error out if DATA command ill received - if ($a[0]!=$SMTP_DATA_REPLY){ - $smtp_errornum = -4; - $smtp_error .= $reply; - return false; - } - //send data - if ($is_file){ - //if message file, open file - $fp = false; - if (file_exists(realpath($message))) $fp = fopen($message, "rb"); - if (!$fp) - { - $smtp_errornum = -4; - $smtp_error .= "Invlid message file\n"; - return false; - } - - //send file - while(!feof($fp)){ - $buffer = chop(fgets($fp, 4096), "\r\n"); - fputs($conn, $buffer."\r\n"); - } - fclose($fp); - fputs($conn, "\r\n.\r\n"); - - return smtp_check_reply(smtp_get_response($conn, 1024)); - }else{ - //else, send message - $message = str_replace("\r\n", "\n", $message); - $message = str_replace("\n", "\r\n", $message); - $message = str_replace("\r\n.\r\n", "\r\n..\r\n", $message); - fputs($conn, $message); - fputs($conn, "\r\n.\r\n"); - - return smtp_check_reply(smtp_get_response($conn, 1024)); - } - } - - return false; - } - - - function smtp_ExplodeQuotedString($delimiter, $string){ - $quotes=explode("\"", $string); - while ( list($key, $val) = each($quotes)) - if (($key % 2) == 1) - $quotes[$key] = str_replace($delimiter, "_!@!_", $quotes[$key]); - $string=implode("\"", $quotes); - - $result=explode($delimiter, $string); - while ( list($key, $val) = each($result) ) - $result[$key] = str_replace("_!@!_", $delimiter, $result[$key]); - - return $result; - } - - - function smtp_expand($str){ - $addresses = array(); - $recipients = smtp_ExplodeQuotedString(",", $str); - reset($recipients); - while ( list($k, $recipient) = each($recipients) ){ - $a = explode(" ", $recipient); - while ( list($k2, $word) = each($a) ){ - if ((strpos($word, "@") > 0) && (strpos($word, "\"")===false)){ - if (!ereg("^<", $word)) $word = "<".$word; - if (!ereg(">$", $word)) $word = $word.">"; - if (in_array($word, $addresses)===false) array_push($addresses, $word); - } - } - } - return $addresses; - } -?> \ No newline at end of file diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index e33593139..2172accc8 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -21,7 +21,8 @@ */ -require_once('lib/smtp.inc'); +//require_once('lib/smtp.inc'); +require_once('include/rcube_smtp.inc'); require_once('Mail/mime.php'); @@ -155,30 +156,28 @@ $msg_subject = $headers['Subject']; // send thru SMTP server using cusotm SMTP library if ($CONFIG['smtp_server']) { - // connect to SMTP server - $smtp_conn = smtp_connect($CONFIG['smtp_server'], '25', $CONFIG['smtp_user'], $CONFIG['smtp_pass']); - - if ($smtp_conn) - { - // generate list of recipients - $recipients = $mailto.', '.$headers['Cc'].', '.$headers['Bcc']; - $a_recipients = smtp_expand($recipients); - - // generate message headers - $header_str = $MAIL_MIME->txtHeaders($headers); - - // send message - $sent = smtp_mail($smtp_conn, $from, $a_recipients, $header_str."\r\n".$msg_body, FALSE); - } - + // generate list of recipients + $a_recipients = array($mailto); + + if (strlen($headers['Cc'])) + $a_recipients[] = $headers['Cc']; + if (strlen($headers['Bcc'])) + $a_recipients[] = $headers['Bcc']; + + // generate message headers + $header_str = $MAIL_MIME->txtHeaders($headers); + + // send message + $sent = smtp_mail($from, $a_recipients, $header_str, $msg_body); + // log error - if (!$smtp_conn || !$sent) + if (!$sent) { raise_error(array('code' => 800, 'type' => 'smtp', 'line' => __LINE__, 'file' => __FILE__, - 'message' => "Connection failed: $smtp_error"), TRUE, FALSE); + 'message' => "SMTP error: $SMTP_ERROR"), TRUE, FALSE); } } -- cgit v1.2.3