cvs: php4 /pear Mail.php /pear/Mail sendmail.php smtp.php /pear/Net SMTP.php

From: Date: Wed, 25 Oct 2000 17:16:09 +0000
Subject: cvs: php4 /pear Mail.php /pear/Mail sendmail.php smtp.php /pear/Net SMTP.php
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-2252@lists.php.net to get a copy of this message
chagenbu Wed Oct 25 10:16:09 2000 EDT Added files: /php4/pear Mail.php /php4/pear/Mail sendmail.php smtp.php /php4/pear/Net SMTP.php Log: add Mail:: interface and supporting classes.

Index: php4/pear/Mail.php +++ php4/pear/Mail.php <?php // // +----------------------------------------------------------------------+ // | PHP version 4.0 | // +----------------------------------------------------------------------+ // | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | // +----------------------------------------------------------------------+ // | This source file is subject to version 2.02 of the PHP license, | // | that is bundled with this package in the file LICENSE, and is | // | available at through the world-wide-web at | // | http://www.php.net/license/2_02.txt. | // | If you did not receive a copy of the PHP license and are unable to | // | obtain it through the world-wide-web, please send a note to | // | license@php.net so we can mail you a copy immediately. | // +----------------------------------------------------------------------+ // | Authors: Chuck Hagenbuch <chuck@horde.org> | // +----------------------------------------------------------------------+ require_once 'PEAR.php'; /** * PEAR's Mail:: interface. Defines the interface for implementing * mailers under the PEAR hierarchy, and provides supporting functions * useful in multiple mailer backends. */ class Mail extends PEAR { /** * Provides an interface for generating Mail:: objects of various * types * * @param string The kind of Mail:: object to instantiate. * @param array The parameters to pass to the Mail:: object. * @access public */ function factory($mailer_type, $params = array()) { $mailer_type = strtolower($mailer_type); $classfile = PEAR_INSTALL_DIR . '/Mail/' . $mailer_type . '.php'; if (@is_readable($classfile)) { include_once $classfile; $class = 'Mail_' . $mailer_type; return new $class($params); } else { return new PEAR_Error('unable to find classfile: ' . $classfile); } } /** * Implements Mail::send() function using php's built-in mail() * command. * * @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 array The array of headers to send with the mail, 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'. * * @param string The full text of the message body, including any * Mime parts, etc. * * @return mixed Returns true on success, or a PEAR_Error * containing a descriptive error message on * failure. * @access public */ function send($recipients, $headers, $body) { // if we're passed an array of recipients, implode it. if (is_array($recipients)) { $recipients = implode(', ', $recipients); } // get the Subject out of the headers array so that we can // pass it as a seperate argument to mail(). $subject = ''; if (isset($headers['Subject'])) { $subject = $headers['Subject']; unset($headers['Subject']); } // flatten the headers out. list(,$text_headers) = Mail::prepareHeaders($headers); return mail($recipients, $subject, $body, $text_headers); } /** * Take an array of mail headers and return a string containing * text usable in sending a message. * * @param array 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 protected */ function prepareHeaders($headers) { // Look out for the From: value to use along the way. $text_headers = ''; // text representation of headers $from = null; foreach ($headers as $key => $val) { if ($key == 'From') { $from_arr = imap_rfc822_parse_adrlist($val, 'localhost'); $from = $from_arr[0]->mailbox . '@' . $from_arr[0]->host; if (strstr($from, ' ')) { // Reject outright envelope From addresses with spaces. return false; } $text_headers .= $key . ': ' . $val . "\n"; } else { $text_headers .= $key . ': ' . $val . "\n"; } } return array($from, $text_headers); } /** * 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 protected */ function parseRecipients($recipients) { // if we're passed an array, assume addresses are valid and // implode them before parsing. if (is_array($recipients)) { $recipients = implode(', ', $recipients); } // Parse recipients, leaving out all personal info. This is // for smtp recipients, etc. All relevant personal information // should already be in the headers. $addresses = imap_rfc822_parse_adrlist($recipients, 'localhost'); $recipients = array(); if (is_array($addresses)) { foreach ($addresses as $ob) { $recipients[] = $ob->mailbox . '@' . $ob->host . ' '; } } return $recipients; } } ?> Index: php4/pear/Mail/sendmail.php +++ php4/pear/Mail/sendmail.php <?php // // +----------------------------------------------------------------------+ // | PHP version 4.0 | // +----------------------------------------------------------------------+ // | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | // +----------------------------------------------------------------------+ // | This source file is subject to version 2.02 of the PHP license, | // | that is bundled with this package in the file LICENSE, and is | // | available at through the world-wide-web at | // | http://www.php.net/license/2_02.txt. | // | If you did not receive a copy of the PHP license and are unable to | // | obtain it through the world-wide-web, please send a note to | // | license@php.net so we can mail you a copy immediately. | // +----------------------------------------------------------------------+ // | Authors: Chuck Hagenbuch <chuck@horde.org> | // +----------------------------------------------------------------------+ require_once 'Mail.php'; /** * Sendmail implementation of the PEAR Mail:: interface. */ class Mail_sendmail extends Mail { /** * The location of the sendmail binary on the filesystem. * @var string */ var $sendmail_path = '/usr/sbin/sendmail'; /** * Constructor. * * Instantiates a new Mail_sendmail:: object based on the parameters * passed in. It looks for the following parameters: * sendmail_path The location of the sendmail binary on the * filesystem. Defaults to '/usr/sbin/sendmail'. * * If a parameter is present in the $params array, it replaces the * default. * * @param array Hash containing any parameters different from the * defaults. */ function Mail_sendmail($params) { if (isset($params['sendmail_path'])) $this->sendmail_path = $params['sendmail_path']; } /** * Implements Mail::send() function using the sendmail * command-line binary. * * @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 array The array of headers to send with the mail, 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'. * * @param string The full text of the message body, including any * Mime parts, etc. * * @return mixed Returns true on success, or a PEAR_Error * containing a descriptive error message on * failure. * @access public */ function send($recipients, $headers, $body) { $recipients = escapeShellCmd(implode(' ', $this->parseRecipients($recipients))); list($from, $text_headers) = $this->prepareHeaders($headers); if (!isset($from)) { return new PEAR_Error('No from address given'); } // Finally, send the mail. $result = 0; if (@is_executable($this->sendmail_path)) { $from = '"' . escapeShellCmd($from) . '"'; $mail = popen($this->sendmail_path . " -i -f$from -- $recipients", 'w'); fputs($mail, $text_headers); fputs($mail, "\n"); // newline to end the headers section fputs($mail, $body); $result = pclose($mail) >> 8 & 0xFF; // need to shift the pclose result to get the exit code } else { return new PEAR_Error('sendmail [' . $this->sendmail_path . '] not executable'); } // Return. if ($result != 0) { return new PEAR_Error('sendmail returned error code ' . $result); } return true; } } ?> Index: php4/pear/Mail/smtp.php +++ php4/pear/Mail/smtp.php <?php // // +----------------------------------------------------------------------+ // | PHP version 4.0 | // +----------------------------------------------------------------------+ // | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | // +----------------------------------------------------------------------+ // | This source file is subject to version 2.02 of the PHP license, | // | that is bundled with this package in the file LICENSE, and is | // | available at through the world-wide-web at | // | http://www.php.net/license/2_02.txt. | // | If you did not receive a copy of the PHP license and are unable to | // | obtain it through the world-wide-web, please send a note to | // | license@php.net so we can mail you a copy immediately. | // +----------------------------------------------------------------------+ // | Authors: Chuck Hagenbuch <chuck@horde.org> | // +----------------------------------------------------------------------+ require_once 'Mail.php'; /** * SMTP implementation of the PEAR Mail:: interface. Requires the PEAR * Net_SMTP:: class. */ class Mail_smtp extends Mail { /** * The SMTP host to connect to. * @var string */ var $host = 'localhost'; /** * The port the SMTP server is on. * @var int */ var $port = 25; /** * Whether or not to attempt to authenticate to the SMTP server. * @var boolean */ var $auth = false; /** * The username to use if the SMTP server requires authentication. * @var string */ var $username = ''; /** * The password to use if the SMTP server requires authentication. * @var string */ var $password = ''; /** * Constructor. * * Instantiates a new Mail_smtp:: object based on the parameters * passed in. It looks for the following parameters: * host The server to connect to. Defaults to localhost. * port The port to connect to. Defaults to 25. * auth Whether or not to use SMTP auth. Defaults to false. * username The username to use for SMTP auth. No default. * password The password to use for SMTP auth. No default. * * If a parameter is present in the $params array, it replaces the * default. * * @param array Hash containing any parameters different from the * defaults. */ function Mail_smtp($params) { if (isset($params['host'])) $this->host = $params['host']; if (isset($params['port'])) $this->host = $params['port']; if (isset($params['auth'])) $this->host = $params['auth']; if (isset($params['username'])) $this->host = $params['username']; if (isset($params['password'])) $this->host = $params['password']; } /** * Implements Mail::send() function using SMTP. * * @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 array The array of headers to send with the mail, 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'. * * @param string The full text of the message body, including any * Mime parts, etc. * * @return mixed Returns true on success, or a PEAR_Error * containing a descriptive error message on * failure. * @access public */ function send($recipients, $headers, $body) { include_once 'Net/SMTP.php'; if (!($smtp = new Net_SMTP($this->host, $this->port))) { return new PEAR_Error('unable to instantiate Net_SMTP object'); } if (!$smtp->connect()) { return new PEAR_Error('unable to connect to smtp server ' . $this->host . ':' . $this->port); } if ($this->auth) { if (!$smtp->auth($this->username, $this->password)) { return new PEAR_Error('unable to authenticate to smtp server'); } if (!$smtp->identifySender()) { return new PEAR_Error('unable to identify smtp server'); } } list($from, $text_headers) = $this->prepareHeaders($headers); if (!isset($from)) { return new PEAR_Error('No from address given'); } if (!($smtp->mailFrom($from))) { return new PEAR_Error('unable to set sender to [' . $from . ']'); } $recipients = $this->parseRecipients($recipients); foreach($recipients as $recipient) { if (!$smtp->rcptTo($recipient)) { return new PEAR_Error('unable to add recipient [' . $recipient . ']'); } } if (!$smtp->data($text_headers . "\n" . $body)) { return new PEAR_Error('unable to send data'); } $smtp->disconnect(); return true; } } ?> Index: php4/pear/Net/SMTP.php +++ php4/pear/Net/SMTP.php <?php // // +----------------------------------------------------------------------+ // | PHP version 4.0 | // +----------------------------------------------------------------------+ // | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | // +----------------------------------------------------------------------+ // | This source file is subject to version 2.02 of the PHP license, | // | that is bundled with this package in the file LICENSE, and is | // | available at through the world-wide-web at | // | http://www.php.net/license/2_02.txt. | // | If you did not receive a copy of the PHP license and are unable to | // | obtain it through the world-wide-web, please send a note to | // | license@php.net so we can mail you a copy immediately. | // +----------------------------------------------------------------------+ // | Authors: Chuck Hagenbuch <chuck@horde.org> | // +----------------------------------------------------------------------+ require_once 'PEAR.php'; /** * Provides an implementation of the SMTP protocol using PEAR's * Net_Socket:: class. */ class Net_SMTP extends PEAR { /** * The server to connect to. * @var string */ var $host = 'localhost'; /** * The port to connect to. * @var int */ var $port = 25; /** * The value to give when sending EHLO or HELO. * @var string */ var $localhost = 'localhost'; /** * The socket resource being used to connect to the SMTP server. * @var resource */ var $socket; /** * The most recent reply code * @var int */ var $code; /** * Stores detected features of the SMTP server. * @var array */ var $esmtp; /** * Constructor * * Instantiates a new Net_SMTP object, overriding any defaults * with parameters that are passed in. * * @param string The server to connect to. * @param int The port to connect to. * @param string The value to give when sending EHLO or HELO. */ function Net_SMTP($host = null, $port = null, $localhost = null) { if (isset($host)) $this->host = $host; if (isset($port)) $this->port = $port; if (isset($localhost)) $this->localhost = $localhost; } /** * Attempt to connect to the SMTP server. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public */ function connect() { include_once 'Net/Socket.php'; if (PEAR::isError($this->socket = new Net_Socket())) { return new PEAR_Error('unable to create a socket object'); } if (PEAR::isError($this->socket->connect($this->host, $this->port))) { return new PEAR_Error('unable to open socket'); } if (PEAR::isError($this->validateResponse('220'))) { return new PEAR_Error('smtp server not 220 ready'); } if (!$this->identifySender()) { return new PEAR_Error('unable to identify smtp server'); } return true; } /** * Attempt to disconnect from the SMTP server. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public */ function disconnect() { if (PEAR::isError($this->socket->write("QUIT\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!$this->validateResponse('221')) { return new PEAR_Error('221 Bye not received'); } if (PEAR::isError($this->socket->disconnect())) { return new PEAR_Error('socket disconnect failed'); } return true; } /** * Attempt to do SMTP authentication. * * @param string The userid to authenticate as. * @param string The password to authenticate with. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public */ function auth($uid, $pwd) { /* Note: not currently checking if AUTH LOGIN is allowed */ /* Note: only allows one authentication mechanism */ if (!isset($this->esmtp['AUTH'])) { return new PEAR_Error('auth not supported'); } if (PEAR::isError($this->socket->write("AUTH LOGIN\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!$this->validateResponse('334')) { return new PEAR_Error('AUTH LOGIN not recognized'); } if (PEAR::isError($this->socket->write(base64_encode($uid) . "\n"))) { return new PEAR_Error('write to socket failed'); } if (!$this->validateResponse('334')) { return new PEAR_Error('354 not received'); } if (PEAR::isError($this->socket->write(base64_encode($pwd) . "\n"))) { return new PEAR_Error('write to socket failed'); } if (!$this->validateResponse('235')) { return new PEAR_Error('235 not received'); } return true; } /** * Send the HELO command. * * @param string The domain name to say we are. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public */ function helo($domain) { if (PEAR::isError($this->socket->write("HELO $domain\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); } return true; } /** * Send the MAIL FROM: command. * * @param string The sender (reverse path) to set. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public */ function mailFrom($reverse_path) { if (PEAR::isError($this->socket->write("MAIL FROM:<$reverse_path>\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); } return true; } /** * Send the RCPT TO: command. * * @param string The recipient (forward path) to add. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public */ function rcptTo($forward_path) { /* Note: 251 is also a valid response code */ if (PEAR::isError($this->socket->write("RCPT TO:<$forward_path>\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); } return true; } /** * Send the DATA command. * * @param string The message body to send. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public */ function data($data) { $data = preg_replace("/([^\r]{1})\n/", "\\1\r\n", $data); $data = preg_replace("/\n\n/", "\n\r\n", $data); $data = preg_replace("/^(\..*)/", ".\\1", $data); if (PEAR::isError($this->socket->write("DATA\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!($this->validateResponse('354'))) { return new PEAR_Error('354 not received'); } if (PEAR::isError($this->socket->write($data . "\r\n.\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); } return true; } /** * Send the SEND FROM: command. * * @param string The reverse path to send. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public */ function send_from($reverse_path) { if (PEAR::isError($this->socket->write("SEND FROM:<$reverse_path>\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); } return true; } /** * Send the SOML FROM: command. * * @param string The reverse path to send. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public */ function soml_from($reverse_path) { if (PEAR::isError($this->socket->write("SOML FROM:<$reverse_path>\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); } return true; } /** * Send the SAML FROM: command. * * @param string The reverse path to send. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public */ function saml_from($reverse_path) { if (PEAR::isError($this->socket->write("SAML FROM:<$reverse_path>\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); } return true; } /** * Send the RSET command. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public */ function rset() { if (PEAR::isError($this->socket->write("RSET\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); } return true; } /** * Send the VRFY command. * * @param string The string to verify * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public */ function vrfy($string) { /* Note: 251 is also a valid response code */ if (PEAR::isError($this->socket->write("VRFY $string\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); } return true; } /** * Send the NOOP command. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access public */ function noop() { if (PEAR::isError($this->socket->write("NOOP\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!($this->validateResponse('250'))) { return new PEAR_Error('250 OK not received'); } return true; } /** * Attempt to send the EHLO command and obtain a list of ESMTP * extensions available, and failing that just send HELO. * * @return mixed Returns a PEAR_Error with an error message on any * kind of failure, or true on success. * @access private */ function identifySender() { if (PEAR::isError($this->socket->write("EHLO $this->localhost\r\n"))) { return new PEAR_Error('write to socket failed'); } $extensions = array(); if (!($this->validateAndParseResponse('250', $extensions))) { if (PEAR::isError($this->socket->write("HELO $this->localhost\r\n"))) { return new PEAR_Error('write to socket failed'); } if (!($this->validateResponse('250'))) { return new PEAR_Error('HELO not accepted', $this->code); } return true; } for ($i = 0; $i < count($extensions); $i++) { $verb = strtok($extensions[$i], ' '); $arguments = substr($extensions[$i], strlen($verb) + 1, strlen($extensions[$i]) - strlen($verb) - 2); $this->esmtp[$verb] = $arguments; } return true; } /** * Read a response from the server and see if the response code * matches what we are expecting. * * @param int The response code we are expecting. * * @return boolean True if we get what we expect, false otherwise. * @access private */ function validateResponse($code) { while ($response = $this->socket->readLine()) { $reply_code = strtok($response, ' '); if (!(strcmp($code, $reply_code))) { $this->code = $reply_code; return true; } else { $reply_code = strtok($response, '-'); if (strcmp($code, $reply_code)) { $this->code = $reply_code; return false; } } } return false; } /** * Read a response from the server and see if the response code * matches what we are expecting. Also save the rest of the * response in the array passed by reference as the second * argument. * * @param int The response code we are expecting. * @param array An array to dump the rest of the response into. * * @return boolean True if we get what we expect, false otherwise. * @access private */ function validateAndParseResponse($code, &$arguments) { $arguments = array(); while ($response = $this->socket->readLine()) { $reply_code = strtok($response, ' '); if (!(strcmp($code, $reply_code))) { $arguments[] = substr($response, strlen($code) + 1, strlen($response) - strlen($code) - 1); $this->code = $reply_code; return true; } else { $reply_code = strtok($response, '-'); if (strcmp($code, $reply_code)) { $this->code = $reply_code; return false; } } $arguments[] = substr($response, strlen($code) + 1, strlen($response) - strlen($code) - 1); } return false; } } ?>
« previous php.cvs (#2252) next »