0

I'm writing a php IMAP API. The constructor connects to them Gmail server and stores the socket in a variable. When I use the same socket connection in another method, the connection is null. Why does this happen and how can I fix it?

class Imap{

    const RESPONSE_SIZE = 4096;
    const LOCAL_HOST = '127.0.0.1';
    const CRLF = "\r\n";

    //------------RESPONSE CODES---------//
    const OK = "OK";
    const BAD = "BAD";
    const NO = "NO";

    //----------------FLAGS--------------//
    const FLAG_ANSWERED = "\\Answered";
    const FLAG_FLAGGED = "\\Flagged";
    const FLAG_DRAFT = "\\Draft";
    const FLAG_DELETED = "\\Deleted";
    const FLAG_SEEN = "\\Seen";

    //------------- PRIVATE VARS ------------//
    private $_connection = NULL;
    private $_number = 0;
    private $_instruction_num;

    private $_connected = false;
    private $_authenticated = false;

    //------------- PUBLIC VARS ------------//

    public $error = array();

    function __construct($imap_server, $imap_port)
    {
        $this->connect($imap_server,$imap_port);
    }

    function __destruct()
    {
        if($this->_connected)
            $this->logout();
    }

    private function connect($imap_server, $imap_port)
    {
        if($this->_connected == false)
        {
            if($imap_server == NULL)    $imap_server = self::LOCAL_HOST;
            if($imap_port == NULL)      $imap_port = 993;

            $this->_connection = fsockopen($imap_server,$imap_port);

            if(empty($this->_connection))
            {
                throw new Exception('Connection to server could not be established');
            }
            else $this->_connected = true;      

        }

        return $this->_connected;
    }

    function login($username, $password)
    {
        echo 'connection: '.$this->_connected;
        if(!$this->_authenticated)
        {
            $instruction = $this->get_instruction_num();
            fputs($this->_connection,"$instruction LOGIN $username $password".self::CRLF);
            $response = $this->get_response($instruction);

            switch ($response['code']) {
                case self::OK:
                    $this->_authenticated = true;
                    break;

                case self::NO:
                    $this->_authenticated = false;
                    $this->error = array('error'=>'Invalid username or password.');
                    break;

                case self::BAD:
                default: 
                    $this->_authenticated = false;
                    $this->error = array('error'=>$response['response']);
                break;
            }
        }

        return $this->_authenticated;
    }

private function get_response($aInstructionNumber)
    {

        $end_of_response = false;

        if(empty($this->_connection))
            die('DEAD');//connection is null here.

        while (!$end_of_response)
        {
            $line = fgets($this->_connection,self::RESPONSE_SIZE);
            $response .= $line.'<br/>';

            if(preg_match("/$aInstructionNumber (OK|NO|BAD)/", $response,$responseCode))
                $end_of_response = true;
        }

        return array('code' => $responseCode[1],
            'response'=>$response);
    } 

Used in Mailer.php

class Mailer
{
    const imap_server = "ssl://imap.gmail.com";
    const imap_port = 993;
    const smtp_server = "ssl://smtp.gmail.com";
    const smtp_port = 465;

    private $imap = NULL;
    private $smtp = NULL;
    private $username = "";
    private $password = "";

    private $logged_in = false;

    function __construct()
    {
        try{
            $this->imap = new Imap(self::imap_server,self::imap_port);
            $this->smtp = new Smtp(self::smtp_server,self::smtp_port);
        }catch(Exception $e)
        {
            throw new Exception($e->getMessage());
        }
    }

    function login($username,$password)
    {
        if($username != NULL) $this->username = $username;
        if($password != NULL) $this->password = $password;

        //$this->imap = new Imap(self::imap_server,self::imap_port);
        if(!$this->imap->login($this->username,$this->password))
        {
            $result = array('success'=>0,'message'=>$this->imap->error());
        }
        else
        {
            $this->logged_in = true;
            $result = array('success'=>1,'message'=>'Login successful');
        }

        return $result;
    }

The code in the logging in page:

    if(!isset($_SESSION['mailer']))
{
    try{
        $mailer = new Mailer();
    }catch(Exception $e){
        die($e->getMessage());
    }

    $_SESSION['mailer'] = serialize($mailer);
}

if(isset($_POST['username']) && isset($_POST['password']))
{
    //ENCRYPT
    $username = htmlentities($_POST['username']);
    $password = htmlentities($_POST['password']);
    $s_mailer = $_SESSION['mailer'];
    $mailer = unserialize($s_mailer);

    $_login = $mailer->login($username,$password);
    if($_login['success']!=1)
    {
        $feedback = $_login['message'];
    }else
        header('Location: inbox.php');      

}
W.K.S
  • 9,787
  • 15
  • 75
  • 122

1 Answers1

0

You are storing the Mailer class in SESSION and expecting that the connection keeps alive across several requests. Note that this will not work.

Instead you could store the serialized Mailer object in the SESSION and implement __sleep and __wakeup to establish the connection again on unserialize.

hek2mgl
  • 152,036
  • 28
  • 249
  • 266
  • thanks! I've updated my code, but the unserialize method gives an error `unserialize() expects parameter 1 to be string, object given`. Sorry, I'm new to PHP – W.K.S Apr 03 '13 at 12:35
  • `Sorry, I'm new to PHP` -> No problem. Clear your session or start a new one before testing this. – hek2mgl Apr 03 '13 at 12:37
  • Ok, now it accusses $mailer of being a non-object after unserialisation: `Call to a member function login() on a non-object `. – W.K.S Apr 03 '13 at 12:40
  • Do I have to add __sleep and __wakeup methods to the imap class as well? – W.K.S Apr 03 '13 at 12:41
  • As Mailer will connect as soon as the constructor is called, I see currently no reason for that. But I'm unsure. You should test it without Imap::__sleep, ... first – hek2mgl Apr 03 '13 at 12:42
  • Unfortunately I cannot fix your whole application from remote. Especially as I don't have a GMail account. If you do it the *right way*, __sleep and __wakeup is the way to go – hek2mgl Apr 03 '13 at 13:49