Question

I have a simple SMTP mail client. I can send an email using this app and my yahoo mail account. But when I'm going to send an email using my gmail account connection to Google's SMTP server fails! Here is my SMTP class:

Smtp::Smtp( const QString &user, const QString &pass, const QString &host, int port, int timeout )
{
    socket = new QSslSocket(this);

    connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));
    connect(socket, SIGNAL(connected()), this, SLOT(connected() ) );
    connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), this,SLOT(errorReceived(QAbstractSocket::SocketError)));
    connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(stateChanged(QAbstractSocket::SocketState)));
    connect(socket, SIGNAL(disconnected()), this,SLOT(disconnected()));


    this->user = user;
    this->pass = pass;

    this->host = host;
    this->port = port;
    this->timeout = timeout;


}

void Smtp::sendMail(const QString &from, const QString &to, const QString &subject, const QString &body)
{
   // qDebug() << subject<<" ::: "<<body;
    message = "To: " + to + "\n";
    message.append(QString("From: " + from + "\n"));
    message.append("Subject: " + subject + "\n");
    message.append(body);

    message.replace( QString::fromLatin1( "\n" ), QString::fromLatin1( "\r\n" ) );
    message.replace( QString::fromLatin1( "\r\n.\r\n" ),
    QString::fromLatin1( "\r\n..\r\n" ) );
    //qDebug()<<message;
    this->from = from;
    rcpt = to;
    state = Init;
    socket->connectToHostEncrypted(host, port); //"smtp.gmail.com" and 465 for gmail TLS
    if (!socket->waitForConnected(timeout)) {
         qDebug() << socket->errorString();
     }

    t = new QTextStream( socket );
    t->setCodec("UTF-8");
}

Smtp::~Smtp()
{
    delete t;
    delete socket;
}
void Smtp::stateChanged(QAbstractSocket::SocketState socketState)
{

    qDebug() <<"stateChanged " << socketState;
}

void Smtp::errorReceived(QAbstractSocket::SocketError socketError)
{
    qDebug() << "error " <<socketError;
}

void Smtp::disconnected()
{

    qDebug() <<"disconneted";
    qDebug() << "error "  << socket->errorString();
}

void Smtp::connected()
{
    qDebug() << "Connected ";
}

void Smtp::readyRead()
{

     qDebug() <<"readyRead";
    // SMTP is line-oriented

    QString responseLine;
    do
    {
        responseLine = socket->readLine();
        response += responseLine;
    }
    while ( socket->canReadLine() && responseLine[3] != ' ' );

    responseLine.truncate( 3 );

    qDebug() << "Server response code:" <<  responseLine;
    qDebug() << "Server response: " << response;

    if ( state == Init && responseLine == "220" )
    {
        // banner was okay, let's go on
        *t << "EHLO localhost" <<"\r\n";
        t->flush();

        state = HandShake;
    }
    //No need, because I'm using socket->startClienEncryption() which makes the SSL handshake for you
    /*else if (state == Tls && responseLine == "250")
    {
        // Trying AUTH
        qDebug() << "STarting Tls";
        *t << "STARTTLS" << "\r\n";
        t->flush();
        state = HandShake;
    }*/
    else if (state == HandShake && responseLine == "250")
    {
        socket->startClientEncryption();
        if(!socket->waitForEncrypted(timeout))
        {
            qDebug() << socket->errorString();
            state = Close;
        }


        //Send EHLO once again but now encrypted

        *t << "EHLO localhost" << "\r\n";
        t->flush();
        state = Auth;
    }
    else if (state == Auth && responseLine == "250")
    {
        // Trying AUTH
        qDebug() << "Auth";
        *t << "AUTH LOGIN" << "\r\n";
        t->flush();
        state = User;
    }
    else if (state == User && responseLine == "334")
    {
        //Trying User
        qDebug() << "Username";
        //GMAIL is using XOAUTH2 protocol, which basically means that password and username has to be sent in base64 coding
        //https://developers.google.com/gmail/xoauth2_protocol
        *t << QByteArray().append(user).toBase64()  << "\r\n";
        t->flush();

        state = Pass;
    }
    else if (state == Pass && responseLine == "334")
    {
        //Trying pass
        qDebug() << "Pass";
        *t << QByteArray().append(pass).toBase64() << "\r\n";
        t->flush();

        state = Mail;
    }
    else if ( state == Mail && responseLine == "235" )
    {
        // HELO response was okay (well, it has to be)

        //Apperantly for Google it is mandatory to have MAIL FROM and RCPT email formated the following way -> <email@gmail.com>
        qDebug() << "MAIL FROM:<" << from << ">";
        *t << "MAIL FROM:<" << from << ">\r\n";
        t->flush();
        state = Rcpt;
    }
    else if ( state == Rcpt && responseLine == "250" )
    {
        //Apperantly for Google it is mandatory to have MAIL FROM and RCPT email formated the following way -> <email@gmail.com>
        *t << "RCPT TO:<" << rcpt << ">\r\n"; //r
        t->flush();
        state = Data;
    }
    else if ( state == Data && responseLine == "250" )
    {

        *t << "DATA\r\n";
        t->flush();
        state = Body;
    }
    else if ( state == Body && responseLine == "354" )
    {

        *t << message << "\r\n.\r\n";
        t->flush();
        state = Quit;
    }
    else if ( state == Quit && responseLine == "250" )
    {

        *t << "QUIT\r\n";
        t->flush();
        // here, we just close.
        state = Close;
        emit status( tr( "Message sent" ) );
    }
    else if ( state == Close )
    {
        deleteLater();
        return;
    }
    else
    {
        // something broke.
        QMessageBox::warning( 0, tr( "Qt Simple SMTP client" ), tr( "Unexpected reply from SMTP server:\n\n" ) + response );
        state = Close;
        emit status( tr( "Failed to send message" ) );
    }
    response = "";
}

I use smtp.mail.yahoo.com as yahoo smtp server and smtp.gmail.com as gmail smtp server. I use port 465 for both.
Here is the debug output(Although I think is useless ):

stateChanged  QAbstractSocket::HostLookupState 
stateChanged  QAbstractSocket::ConnectingState 
stateChanged  QAbstractSocket::UnconnectedState 
error  QAbstractSocket::SocketTimeoutError 
stateChanged  QAbstractSocket::UnconnectedState 
"Socket operation timed out" 

I'm using Windows 7 64-bit . Qt 4.8.5 and visual studio 2008 !

Was it helpful?

Solution

Following sample works fine with GMAIL:

SmtpSsl::SmtpSsl(QObject *parent) :
    QObject(parent) ,
    smtp( new QSslSocket ) ,
    istd( new QFile ) ,
    ostd( new QFile )
{
    qDebug() << "constructing";


    // QIODevice
    QObject::connect( smtp , SIGNAL(aboutToClose()) , this , SLOT(sck_aboutToClose()) );
    QObject::connect( smtp , SIGNAL(bytesWritten(qint64)) , this , SLOT    (sck_bytesWritten(qint64)) );
    QObject::connect( smtp , SIGNAL(readChannelFinished()) , this , SLOT(sck_readChannelFinished()) );
    QObject::connect( smtp , SIGNAL(readyRead()) , this , SLOT(sck_readyRead()) );


    // QAbstractSocket
    QObject::connect( smtp , SIGNAL(connected()) , this , SLOT(sck_connected()) );
    QObject::connect( smtp , SIGNAL(disconnected()) , this , SLOT(sck_disconnected()) );
    QObject::connect( smtp , SIGNAL(error(QAbstractSocket::SocketError)) , this , SLOT(sck_error(QAbstractSocket::SocketError)) );
    QObject::connect( smtp , SIGNAL(hostFound()) , this , SLOT(sck_hostfound()) );
    QObject::connect( smtp , SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)) , this , SLOT(sck_proxyAuthRequired()) );
    QObject::connect( smtp , SIGNAL(stateChanged(QAbstractSocket::SocketState)) , this , SLOT(sck_stateChanged(QAbstractSocket::SocketState)) );


    // QSslSocket
    QObject::connect( smtp , SIGNAL(encrypted()) , this , SLOT(sck_encrypted()) );
    QObject::connect( smtp , SIGNAL(encryptedBytesWritten(qint64)) , this , SLOT(sck_encryptedBytesWritten(qint64)) );
    QObject::connect( smtp , SIGNAL(modeChanged(QSslSocket::SslMode)) , this , SLOT(sck_modeChanged(QSslSocket::SslMode)) );
    QObject::connect( smtp , SIGNAL(sslErrors(QList<QSslError>)) , this , SLOT(sck_sslErrors(QList<QSslError>)) );
    QObject::connect( smtp , SIGNAL(peerVerifyError(QSslError)) , this , SLOT(sck_peerVerifyError(QSslError)) );


    // public part
    QObject::connect( smtp , SIGNAL(encrypted()) , this , SLOT(start_session()) );
    QObject::connect( smtp , SIGNAL(disconnected()) , this , SLOT(deleteLater()) );

    smtp->setPeerVerifyMode( QSslSocket::VerifyPeer );
    smtp->connectToHostEncrypted( "smtp.gmail.com" , 465 );
    istd->open( stdin  , QIODevice::ReadOnly );
    ostd->open( stdout , QIODevice::WriteOnly );

    qDebug() << "constructed";
}

SmtpSsl::~SmtpSsl()
{
    qDebug() << "destroying";
    delete smtp;
    qDebug() << "destroyed";
}   

void
SmtpSsl::start_session()
{
    QObject::connect( smtp , SIGNAL(readyRead()) , this , SLOT(receive()) );
}   

void    SmtpSsl::receive()
{
    ostd->write( smtp->readAll() );
    ostd->flush();

    smtp->write( istd->readLine() );
}

As concerns to your sample. I can guess that you have one of following cases:

  1. you have connectivity problems at network level. Can you connect smtp.gmail.com port 465 without SSL?
  2. I believe you have that case. You're intercepting readyRead signal and probably (this code was not shown here) read received data before you've entered encrypted state (i.e. SSL handshake data). Because of you've read handshake data QSslSocket will wait until it can read grabbed handshake data which will result in timeout.

Hope, this help.

OTHER TIPS

you should use port 587 for gmail, enable ssl,tls and auth. Here is my working solution written in Java, this code doesn't use Qt framework however but it can show you how to configure your smtp server to send email via gmail properly.

public void sendMail2(){   
        Email email = new SimpleEmail();
        try {
            DateFormat dateFormat = new SimpleDateFormat(
                                                        "yyyy/MM/dd HH:mm:ss");
            Calendar c = new GregorianCalendar();
          String authuser="email@gmail.com";String authpwd="passtoyouraccount";
            email.setSmtpPort(587);
            email.setAuthenticator(new DefaultAuthenticator(authuser,authpwd));
            email.setDebug(true);
            email.setHostName("smtp.gmail.com");
          email.getMailSession().getProperties().put("mail.smtps.auth","true");
          email.getMailSession().getProperties().put("mail.debug", "true");
          email.getMailSession().getProperties().put("mail.smtps.port","587");
            email.getMailSession().getProperties().put(
            "mail.smtps.socketFactory.port", "587");
          email.getMailSession().getProperties().put(
            "mail.smtps.socketFactory.class",
            "javax.net.ssl.SSLSocketFactory");
          email.getMailSession().getProperties().put(
            "mail.smtps.socketFactory.fallback", "false");
          email.getMailSession().getProperties().put(
            "mail.smtp.starttls.enable", "true");
          email.setFrom("me@gmail.com", "my_site");
          email.setSubject("new message");
          Date date = new Date();
          email.setMsg("from: "+userMail+"\ndate:"                  
                               +dateFormat.format(date)+"\n\n"+mailContent);
          email.addTo("addMeToo@gmail.com","ToName");
          email.send();
        } catch (EmailException e) {
            e.printStackTrace();
        }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top