<?php
//
// +--------------------------------------------------------------------------+
// | |
// | XS PHP Library Generic Classes Library |
// | |
// | Copyright (c) 2001-2002 XSPHPLib Group. |
// | |
// +--------------------------------------------------------------------------+
// | |
// | Distributed under the terms of the GNU Lesser General Public License as |
// | published by the Free Software Foundation version 2.1 |
// | See the GNU Lesser General Public License for more details. You should |
// | have received a copy of the GNU Lesser General Public License along with |
// | this package; if not, write to the Free Software Foundation, Inc., |
// | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
// | |
// +--------------------------------------------------------------------------+
// | |
// | Authors: Robert Bala <hide@address.com> |
// | Hans Zaunere <hide@address.com> |
// | |
// +--------------------------------------------------------------------------+
//
// $Id: smtp.inc.php,v 1.2 2003/01/25 23:25:02 rbala Exp $
/**
* SMTP access class.
*
* SMTP class is a wrapper to the SMTP protocol, as specified by RFC 821.
* All mandatory SMTP commands are available, as are a few optional SMTP
* commands. It can be used to construct SMTP based web mail program, seding
* new mail from the web, or even to authenticate secured areas of the web site.
*
* @author Robert Bala <hide@address.com>
* @author Hans Zaunere <hide@address.com>
* @access public
* @package net
* @version $Id: smtp.inc.php,v 1.2 2003/01/25 23:25:02 rbala Exp $
*/
class SMTP extends Object {
/**
* IP address or host name.
* @access private
* @var string
*/
var $_host;
/**
* TCP port number.
* @access private
* @var int
*/
var $_port;
/**
* Stores detected features of the SMTP server.
* @access private
* @var array
*/
var $_esmtp;
/**
* The banner returned by the SMTP server.
* @access private
* @var int
*/
var $_banner;
/**
* The last response status returned by the SMTP server.
* @access private
* @var int
*/
var $_status;
/**
* The socket resource being used to connect to the SMTP server.
* @access private
* @var resource
*/
var $_socket;
/**
* SMTP class constructor.
*
* Creates the new instance of SMTP class and sets up basic properties.
*
* @access public
* @param string $host IP address or host name, defaults to "".
* @param int $port TCP port number, defaults to 25.
* @return void
*/
function SMTP($host='', $port=25) {
Object::Object();
$this->_host = $host;
$this->_port = $port;
$this->_esmtp = array();
$this->_banner = '';
$this->_status = '';
$this->_socket = null;
}
/**
* Gets the IP address or host name.
*
* Returns IP address or host name.
*
* @access public
* @return string
*/
function getHost() {
return $this->_host;
}
/**
* Gets the TCP port number.
*
* Returns TCP port number.
*
* @access public
* @return int
*/
function getPort() {
return $this->_port;
}
/**
* Gets the banner returned by the SMTP server.
*
* If called when the connection is already established, it returns the
* banner returned by the SMTP server otherwise it returns empty string.
*
* @access public
* @return string
*/
function getBanner() {
return $this->_banner;
}
/**
* Gets the last response message returned by the POP3 server.
*
* If called when the connection is already established, it returns the
* last response smessage returned by the POP3 server otherwise it returns
* empty string.
*
* @access public
* @return string
*/
function getStatus() {
return $this->_status;
}
/**
* Sets the IP address or host name.
*
* Returns True on success or a error object if the connection is
* already established.
*
* @access public
* @return mixed
*/
function setHost($host) {
if (!$this->isOpened()) {
$this->_host = $host;
} else {
return new Error('sethost(): SMTP connection already established.');
}
return true;
}
/**
* Sets the TCP port number.
*
* Returns True on success or a error object if the connection is
* already established.
*
* @access public
* @return mixed
*/
function setPort($port) {
if (!$this->isOpened()) {
$this->_port = $port;
} else {
return new Error('setport(): SMTP connection already established.');
}
return true;
}
/**
* Attempt to open connection to the SMTP server.
*
* If called when the connection is already established, it returns error
* object otherwise it returns true on success or a error object with an
* error message on any kind of failure.
*
* @access public
* @return mixed
*/
function open() {
if (!$this->isOpened()) {
$socket = new Socket($this->_host, $this->_port, 1, 512);
if (object_isError($socket)) {
return new Error('open(): SMTP unable to open socket.');
}
if (object_isError($socket->open())) {
return new Error('open(): SMTP unable to open socket.');
}
$this->_status = $socket->readLine();
if (smtp_extractCode($this->_status) != '220') {
return new Error('open(): SMTP server not 220 ready.');
}
$this->_banner = smtp_extractInfo($this->_status);
if (object_isError($socket->writeLine('EHLO ' . $this->_host))) {
return new Error('open(): SMTP write to socket failed.');
}
$this->_status = $socket->readLine();
if (smtp_extractCode($this->_status) == '250') {
do {
if ($reply = $socket->readLine()) {
if (strpos($reply, '-')) {
$token = strtok($reply, '-');
} else {
$token = strtok($reply, ' ');
}
$reply = substr($reply, strlen($token) + 1, strlen($reply) - strlen($token) - 1);
$token = strtok($reply, ' ');
$reply = substr($reply, strlen($token) + 1, strlen($reply) - strlen($token) - 2);
$this->_esmtp[$token] = $reply;
}
$status = $socket->status();
} while ($status['unread_bytes'] > 0);
} else {
if (object_isError($socket->writeLine('HELO ' . $this->_host))) {
return new Error('open(): SMTP write to socket failed.');
}
$this->_status = $socket->readLine();
if (smtp_extractCode($this->_status) != '250') {
return new Error('open(): SMTP HELO not accepted.');
}
}
$this->_socket =& $socket;
} else {
return new Error('open(): SMTP socket connection already established.');
}
return true;
}
/**
* Attempt to close connection from the SMTP server.
*
* If called when the connection is not established, it returns error
* object otherwise it returns true on success or a error object with an
* error message on any kind of failure.
*
* @access public
* @return mixed
*/
function close() {
if ($this->isOpened()) {
if (object_isError($this->_socket->writeLine('QUIT'))) {
return new Error('close(): SMTP write to socket failed');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '221') {
return new Error('close(): SMTP 221 Bye not received.');
}
if (object_isError($this->_socket->close())) {
return new Error('close(): SMTP unable to close socket.');
}
$this->_esmtp = array();
} else {
return new Error('close(): SMTP socket connection not established.');
}
return true;
}
/**
* Send the EHLO command and obtain a list of available ESMTP extensions.
*
* If called when the connection is not established, it returns error
* object otherwise it returns true on success or a error object with an
* error message on any kind of failure.
*
* @access public
* @param string $domain the domain name to say we are.
* @return mixed
*/
function ehlo($domain) {
if ($this->isOpened()) {
$this->_esmtp[] = array();
if (object_isError($this->_socket->writeLine('EHLO ' . $domain))) {
return new Error('ehlo(): SMTP write to socket failed.');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '250') {
return new Error('ehlo(): SMTP 250 OK not received.');
}
do {
if ($reply = $this->_socket->readLine()) {
if (strpos($reply, '-')) {
$token = strtok($reply, '-');
} else {
$token = strtok($reply, ' ');
}
$reply = substr($reply, strlen($token) + 1, strlen($reply) - strlen($token) - 1);
$token = strtok($reply, ' ');
$reply = substr($reply, strlen($token) + 1, strlen($reply) - strlen($token) - 2);
$this->_esmtp[$token] = $reply;
}
$status = $this->_socket->status();
} while ($status['unread_bytes'] > 0);
} else {
return new Error('ehlo(): SMTP socket connection not established.');
}
return true;
}
/**
* Send the HELO command.
*
* If called when the connection is not established, it returns error
* object otherwise it returns true on success or a error object with an
* error message on any kind of failure.
*
* @access public
* @param string $domain the domain name to say we are.
* @return mixed
*/
function helo($domain) {
if ($this->isOpened()) {
if (object_isError($this->_socket->writeLine('HELO ' . $domain))) {
return new Error('helo(): SMTP write to socket failed.');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '250') {
return new Error('helo(): SMTP 250 OK not received.');
}
} else {
return new Error('helo(): SMTP socket connection not established.');
}
return true;
}
/**
* Send the VRFY command.
*
* If called when the connection is not established, it returns error
* object otherwise it returns true on success or a error object with an
* error message on any kind of failure.
*
* @access public
* @param string $string the string to verify.
* @return mixed
*/
function vrfy($string) {
if ($this->isOpened()) {
if (object_isError($this->_socket->writeLine('VRFY ' . $string))) {
return new Error('vrfy(): SMTP write to socket failed.');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '250') {
return new Error('vrfy(): SMTP 250 OK not received.');
}
} else {
return new Error('vrfy(): SMTP socket connection not established.');
}
return true;
}
function expn($string) {
if ($this->isOpened()) {
if (object_isError($this->_socket->writeLine('EXPN ' . $string))) {
return new Error('expn(): SMTP write to socket failed.');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '250') {
return new Error('expn(): SMTP 250 OK not received.');
}
} else {
return new Error('expn(): SMTP socket connection not established.');
}
return true;
}
/**
* Attempt to do SMTP authentication.
*
* Attempt to do user authentication if this feature is supported by SMTP
* server. If called when the connection is not established, it returns
* error object otherwise it returns true on success or a error object
* with an error message on any kind of failure.
*
* @access public
* @param string $user the username to login.
* @param string $pass the password to login.
* @return mixed
*/
function auth($user, $pass) {
if (isset($this->_esmtp['AUTH'])) {
if (object_isError($this->_socket->writeLine('AUTH LOGIN'))) {
return new Error('auth(): SMTP write to socket failed');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '334') {
return new Error('auth(): SMTP AUTH LOGIN not recognize.');
}
if (object_isError($this->_socket->write(base64_encode($user) . "\n"))) {
return new Error('auth(): SMTP write to socket failed');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '334') {
return new Error('auth(): SMTP 334 not received.');
}
if (object_isError($this->_socket->write(base64_encode($pass) . "\n"))) {
return new Error('auth(): SMTP write to socket failed');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '235') {
return new Error('auth(): SMTP 235 not received.');
}
} else {
return new Error('auth(): SMTP auth not supported.');
}
return true;
}
/**
* Send the RSET command.
*
* If called when the connection is not established, it returns error
* object otherwise it returns true on success or a error object with an
* error message on any kind of failure.
*
* @access public
* @return mixed
*/
function rset() {
if ($this->isOpened()) {
if (object_isError($this->_socket->writeLine('RSET'))) {
return new Error('rset(): SMTP write to socket failed.');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '250') {
return new Error('rset(): SMTP 250 OK not received.');
}
} else {
return new Error('rset(): SMTP socket connection not established.');
}
return true;
}
/**
* Send the NOOP command.
*
* If called when the connection is not established, it returns error
* object otherwise it returns true on success or a error object with an
* error message on any kind of failure.
*
* @access public
* @return mixed
*/
function noop() {
if ($this->isOpened()) {
if (object_isError($this->_socket->writeLine('NOOP'))) {
return new Error('noop(): SMTP write to socket failed.');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '250') {
return new Error('noop(): SMTP 250 OK not received.');
}
} else {
return new Error('noop(): SMTP socket connection not established.');
}
return true;
}
/**
* Send the MAIL FROM command.
*
* If called when the connection is not established, it returns error
* object otherwise it returns true on success or a error object with an
* error message on any kind of failure.
*
* @access public
* @param string $addr the sender (reverse path) to set.
* @return mixed
*/
function mail($addr) {
if ($this->isOpened()) {
if (object_isError($this->_socket->writeLine('MAIL FROM:' . smtp_extractAddr($addr)))) {
return new Error('mail(): SMTP write to socket failed.');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '250') {
return new Error('mail(): SMTP 250 OK not received.');
}
} else {
return new Error('mail(): SMTP socket connection not established.');
}
return true;
}
/**
* Send the SEND FROM command.
*
* If called when the connection is not established, it returns error
* object otherwise it returns true on success or a error object with an
* error message on any kind of failure.
*
* @access public
* @param string $addr the sender (reverse path) to send.
* @return mixed
*/
function send($addr) {
if ($this->isOpened()) {
if (object_isError($this->_socket->writeLine('SEND FROM:' . smtp_extractAddr($addr)))) {
return new Error('send(): SMTP write to socket failed.');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '250') {
return new Error('send(): SMTP 250 OK not received.');
}
} else {
return new Error('send(): SMTP socket connection not established.');
}
return true;
}
/**
* Send the SOML FROM command.
*
* If called when the connection is not established, it returns error
* object otherwise it returns true on success or a error object with an
* error message on any kind of failure.
*
* @access public
* @param string $addr the reverse path to send.
* @return mixed
*/
function soml($addr) {
if ($this->isOpened()) {
if (object_isError($this->_socket->writeLine('SOML FROM:' . smtp_extractAddr($addr)))) {
return new Error('soml(): SMTP write to socket failed.');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '250') {
return new Error('soml(): SMTP 250 OK not received.');
}
} else {
return new Error('soml(): SMTP socket connection not established.');
}
return true;
}
/**
* Send the SAML FROM command.
*
* If called when the connection is not established, it returns error
* object otherwise it returns true on success or a error object with an
* error message on any kind of failure.
*
* @access public
* @param string $addr the reverse path to send.
* @return mixed
*/
function saml($addr) {
if ($this->isOpened()) {
if (object_isError($this->_socket->writeLine('SAML FROM:' . smtp_extractAddr($addr)))) {
return new Error('saml(): SMTP write to socket failed.');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '250') {
return new Error('saml(): SMTP 250 OK not received.');
}
} else {
return new Error('saml(): SMTP socket connection not established.');
}
return true;
}
/**
* Send the RCPT TO command.
*
* If called when the connection is not established, it returns error
* object otherwise it returns true on success or a error object with an
* error message on any kind of failure.
*
* @access public
* @param string $addr recipient (forward path) to add.
* @return mixed
*/
function rcpt($addr) {
if ($this->isOpened()) {
if (object_isError($this->_socket->writeLine('RCPT TO:' . smtp_extractAddr($addr)))) {
return new Error('rcpt(): SMTP write to socket failed.');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '250') {
return new Error('rcpt(): SMTP 250 OK not received.');
}
} else {
return new Error('rcpt(): SMTP socket connection not established.');
}
return true;
}
/**
* Send the DATA command.
*
* If called when the connection is not established, it returns error
* object otherwise it returns true on success or a error object with an
* error message on any kind of failure.
*
* @access public
* @param string $data the message body to send.
* @return mixed
*/
function data($data) {
if ($this->isOpened()) {
if (object_isError($this->_socket->writeLine('DATA'))) {
return new Error('data(): SMTP write to socket failed.');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '354') {
return new Error('data(): SMTP 354 not received.');
}
$data = preg_replace("/\n\n/", "\n\r\n", $data);
$data = preg_replace("/^(\..*)/", ".\\1", $data);
$data = preg_replace("/([^\r]{1})\n/", "\\1\r\n", $data);
if (object_isError($this->_socket->writeLine($data . "\r\n."))) {
return new Error('data(): SMTP write to socket failed.');
}
$this->_status = $this->_socket->readLine();
if (smtp_extractCode($this->_status) != '250') {
return new Error('data(): SMTP 250 OK not received.');
}
} else {
return new Error('data(): SMTP socket connection not established.');
}
return true;
}
/**
* Finds whether the socket connection is opened.
*
* Returns true if the connection is established, false otherwise.
*
* @access public
* @return boolean
*/
function isOpened() {
return (isset($this->_socket) && $this->_socket->isOpened());
}
}
/**
* Extracts response code returned from SMTP server.
*
* Returns response code returned from SMTP server on success, Null
* otherwise.
*
* @author Hans Zaunere <hide@address.com>
* @access private
* @param string $string the response returned from SMTP server.
* @return mixed
*/
function smtp_extractCode( $string ) {
if( is_numeric(substr($string,0,3)) )
return (int) substr($string,0,3);
return NULL;
}
/**
* Extracts response message returned from SMTP server.
*
* Returns response message returned from SMTP server on success, empty string
* otherwise.
*
* @author Robert Bala <hide@address.com>
* @access private
* @param string $string the response returned from SMTP server.
* @return string
*/
function smtp_extractInfo($string) {
$indent = smtp_extractCode($string);
if ($indent == $string) {
return '';
}
return substr($string, strlen($indent) + 1, strlen($string) - strlen($indent) - 1);
}
/**
* Extracts email address.
*
* Returns valid email address from the string on success, empty string
* otherwise.
*
* @author Robert Bala <hide@address.com>
* @access public
* @param string $string the string from which email is extracted.
* @return string
*/
function smtp_extractAddr($string) {
if (preg_match("/([^<)\s]+@\S+\.[^>(\s]+)/", $string, $match)) {
return '<' . $match[0] . '>';
}
return '';
}
?>