Zend Framework

Adding Authentication to Zend_Mail_Transport_Smtp?

Details

  • Type: Improvement Improvement
  • Status: Resolved Resolved
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: 0.1.5
  • Fix Version/s: 0.8.0
  • Component/s: Zend_Mail
  • Labels:
    None

Description

Granted I am sorta a php newbie... I need Authenication in the smtp transport. I played around with the smtp file and came up with this that I use now.
Is there any problem doing this?

$tr = new Zend_Mail_Transport_Smtp('mail.host.com', 25, '127.0.0.1', 'username', 'passwd');

then in the Zend/Mail/Transport/Smtp.php

 
    /**
     * Constructor.
     *
     * @param string $host
     * @param int $port
     * @param string $myName  (for use with HELO)
     * @param string $username
     * @param string $passwd
     */
    public function __construct($host = '127.0.0.1', $port=25, $myName='127.0.0.1', $username=null, $passwd=null)
    {
        $this->_host = $host;
        $this->_port = $port;
        $this->_myName = $myName;
        ////
        $this->_username = $username;
        $this->_passwd = $passwd;
        ////
    }


    /**
     * Connect to the server with the parameters given
     * in the constructor and send "HELO". The connection
     * is immediately closed if an error occurs.
     *
     * @throws Zend_Mail_Transport_Exception
     */
    public function connect()
    {
        $errno  = null;
        $errstr = null;

        // open connection
        $fp = stream_socket_client('tcp://'.$this->_host.':'.$this->_port, $errno, $errstr, self::CONNECTION_TIMEOUT);

        if ($fp===false) {
            if ($errno==0) {
                $msg = 'Could not open socket';
            } else {
                $msg = $errstr;
            }
            throw new Zend_Mail_Transport_Exception($msg);
        }

        $this->_con = $fp;

        try {
            $res = stream_set_timeout($this->_con, self::COMMUNICATION_TIMEOUT );
            if ($res === false) {
                throw new Zend_Mail_Transport_Exception('Could not set Stream Timeout');
            }

            /**
             * Now the connection is open. Wait for the welcome message:
             *   welcome message has error code 220
             */
            $this->_expect(220);
            $this->helo($this->_myName);
            
            ///////
            if (isset($this->_username)) $this->authenticate($this->_username, $this->_passwd);
            ///////
            
        } catch (Zend_Mail_Transport_Exception $e) {
            fclose($fp);
            throw $e;
        }
    }

    
    
    
    /**
     * Sends AUTH to the server and validates the response. If valid, username is sent to the
     * server and validates the response. If valid, password is sent to the server and validates
     * the response.
     *
     * @param string $username
     * @param string $passwd
     * @throws Zend_Mail_Transport_Exception
     */
    public function authenticate($username, $passwd)
    {
        $this->_send('AUTH LOGIN');
        $this->_expect(334);
        
        
        $this->_send(base64_encode($username));
        $this->_expect(334);
        
        $this->_send(base64_encode($passwd));
        $this->_expect(235);

    }

Activity

Hide
Bill Karwin added a comment -

Scheduling for 0.7.0 release.

Show
Bill Karwin added a comment - Scheduling for 0.7.0 release.
Hide
Justin Hendrickson added a comment -

I submitted this on the mailing list and they asked I put this over here. Here's my Smtp Auth library.

require_once 'Zend/Mail/Transport/Smtp.php';

class Zend_Mail_Transport_Smtp_Auth extends Zend_Mail_Transport_Smtp {
 
    /**#@+
     * Authentication types
     * @var string
     */
    const LOGIN = 'LOGIN';
    const PLAIN = 'PLAIN';
    /**#@-*/
 
    /**
     * @param string $username
     * @param string $password
     * @param string $method
     */
    protected function authenticate($username, $password, $method = self::PLAIN) {
        switch($method) {
            case self::LOGIN:
                $this->authenticateLogin($username, $password);
            break;
 
            case self::PLAIN:
                $this->authenticatePlain($username, $password);
            break;
        }
    }
 
    /**
     * @param  string $username
     * @param  string $password
     * @throws Zend_Mail_Transport_Exception
     */
    protected function authenticateLogin($username, $password) {
        $this->_send('AUTH LOGIN');
 
        try {
            $this->_expect(334);
        } catch(Zend_Mail_Transport_Exception $e) {
            if(substr($e->getMessage(), 0, 3) == 503) {
                return;
            }
            throw $e;
        }
 
        $this->_send(base64_encode($username));
        $this->_expect(334);
        $this->_send(base64_encode($password));
        $this->_expect(235);
    }
 
    /**
     * @param  string $username
     * @param  string $password
     * @throws Zend_Mail_Transport_Exception
     */
    protected function authenticatePlain($username, $password) {
        $this->_send('AUTH PLAIN');
 
        try {
            $this->_expect(334);
        } catch(Zend_Mail_Transport_Exception $e) {
            if(substr($e->getMessage(), 0, 3) == 503) {
                return;
            }
            throw $e;
        }
 
        $this->_send(base64_encode(chr(0) . $username . chr(0) . $password));
        $this->_expect(235);
    }
 
}

Use of the authentication methods is not directly tied into the connection process, but if it is so desired, you could also add this:

class Zend_Mail_Transport_Smtp_Auth extends Zend_Mail_Transport_Smtp {
    private $_username;
    private $_password;
    private $_method;
    public function __construct($host = '127.0.0.1', $port=25, $myName='127.0.0.1', $username=null, $password=null, $method=null) {
        parent::__construct($host, $port, $myName);
        $this->_username = $username;
        $this->_password = $password;
        $this->_method = $method;
    }
    public function connect() {
        parent::connect();
        if($username) {
            $this->authenticate($this->_username, $this->_password, $this->_method);
        }
    }
}
Show
Justin Hendrickson added a comment - I submitted this on the mailing list and they asked I put this over here. Here's my Smtp Auth library.
require_once 'Zend/Mail/Transport/Smtp.php';

class Zend_Mail_Transport_Smtp_Auth extends Zend_Mail_Transport_Smtp {
 
    /**#@+
     * Authentication types
     * @var string
     */
    const LOGIN = 'LOGIN';
    const PLAIN = 'PLAIN';
    /**#@-*/
 
    /**
     * @param string $username
     * @param string $password
     * @param string $method
     */
    protected function authenticate($username, $password, $method = self::PLAIN) {
        switch($method) {
            case self::LOGIN:
                $this->authenticateLogin($username, $password);
            break;
 
            case self::PLAIN:
                $this->authenticatePlain($username, $password);
            break;
        }
    }
 
    /**
     * @param  string $username
     * @param  string $password
     * @throws Zend_Mail_Transport_Exception
     */
    protected function authenticateLogin($username, $password) {
        $this->_send('AUTH LOGIN');
 
        try {
            $this->_expect(334);
        } catch(Zend_Mail_Transport_Exception $e) {
            if(substr($e->getMessage(), 0, 3) == 503) {
                return;
            }
            throw $e;
        }
 
        $this->_send(base64_encode($username));
        $this->_expect(334);
        $this->_send(base64_encode($password));
        $this->_expect(235);
    }
 
    /**
     * @param  string $username
     * @param  string $password
     * @throws Zend_Mail_Transport_Exception
     */
    protected function authenticatePlain($username, $password) {
        $this->_send('AUTH PLAIN');
 
        try {
            $this->_expect(334);
        } catch(Zend_Mail_Transport_Exception $e) {
            if(substr($e->getMessage(), 0, 3) == 503) {
                return;
            }
            throw $e;
        }
 
        $this->_send(base64_encode(chr(0) . $username . chr(0) . $password));
        $this->_expect(235);
    }
 
}
Use of the authentication methods is not directly tied into the connection process, but if it is so desired, you could also add this:
class Zend_Mail_Transport_Smtp_Auth extends Zend_Mail_Transport_Smtp {
    private $_username;
    private $_password;
    private $_method;
    public function __construct($host = '127.0.0.1', $port=25, $myName='127.0.0.1', $username=null, $password=null, $method=null) {
        parent::__construct($host, $port, $myName);
        $this->_username = $username;
        $this->_password = $password;
        $this->_method = $method;
    }
    public function connect() {
        parent::connect();
        if($username) {
            $this->authenticate($this->_username, $this->_password, $this->_method);
        }
    }
}
Hide
Justin Hendrickson added a comment -
if($username) {
            $this->authenticate($this->_username, $this->_password, $this->_method);
        }

This should be:

Show
Justin Hendrickson added a comment -
if($username) {
            $this->authenticate($this->_username, $this->_password, $this->_method);
        }
This should be:
Hide
Gavin added a comment -

Thanks Justin

Show
Gavin added a comment - Thanks Justin
Hide
Simon Mundy added a comment -

Could you please try the latest checkout from trunk (r3168) and try the incubator version of Zend_Mail_Transport_Auth?

An example usage of code would be:-

require_once 'Zend/Mail/Transport/Smtp.php';

$config = array('auth' => 'plain', // defaults to FALSE,
                'username' => 'myusername', // defaults to NULL,
                'password' => 'mypass', // defaults to NULL,
                'port' => 25, // defaults from php.ini,
                'host' => 'localhost'); // for EHLO

$tr = new Zend_Mail_Transport_Smtp('mail.someone.com', $config);

require_once 'Zend/Mail.php';

$mail = new Zend_Mail();
$mail->setBodyText('This is the text of the mail.');
$mail->setFrom('your.email@someone.com', 'Some Sender');
$mail->addTo('their.email@someone.com', 'Some Recipient');
$mail->setSubject('TestSubject');
$mail->send($tr);

The new transport supports 'auth' and 'plain' as auth methods and Cram-md5 is scheduled for 0.9

Show
Simon Mundy added a comment - Could you please try the latest checkout from trunk (r3168) and try the incubator version of Zend_Mail_Transport_Auth? An example usage of code would be:-
require_once 'Zend/Mail/Transport/Smtp.php';

$config = array('auth' => 'plain', // defaults to FALSE,
                'username' => 'myusername', // defaults to NULL,
                'password' => 'mypass', // defaults to NULL,
                'port' => 25, // defaults from php.ini,
                'host' => 'localhost'); // for EHLO

$tr = new Zend_Mail_Transport_Smtp('mail.someone.com', $config);

require_once 'Zend/Mail.php';

$mail = new Zend_Mail();
$mail->setBodyText('This is the text of the mail.');
$mail->setFrom('your.email@someone.com', 'Some Sender');
$mail->addTo('their.email@someone.com', 'Some Recipient');
$mail->setSubject('TestSubject');
$mail->send($tr);
The new transport supports 'auth' and 'plain' as auth methods and Cram-md5 is scheduled for 0.9
Hide
Gavin added a comment -

Post ZF 1.0, perhaps we can consider TLS/SASL support.
However, probably less than 5% of all ZF users would use this, so don't let the idea distract anyone from more important work.

Show
Gavin added a comment - Post ZF 1.0, perhaps we can consider TLS/SASL support. However, probably less than 5% of all ZF users would use this, so don't let the idea distract anyone from more important work.
Hide
Simon Mundy added a comment -

Moved to core for 0.8

Show
Simon Mundy added a comment - Moved to core for 0.8

People

Vote (1)
Watch (2)

Dates

  • Created:
    Updated:
    Resolved: