Russian Qt Forum
Апрель 20, 2024, 18:47 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: smtp tls gmail.com  (Прочитано 18346 раз)
Павелъ
Гость
« : Июнь 02, 2017, 20:45 »

Доброго времени суток. Понадобилось сделать рассылку сообщений на рабочую почту.

На рабочем почтовом сервере используется tls шифрование с портом 587.

Скачал готовый код для отсылки сообщений - ничего не работает (Шindows XP).

Пришёл домой, отправил спокойно с помощью готового когда сообщения с mail на yandex. (Linux)

Почтовый сервер майла работает на ssl с портов 465.

Решил для теста отправить с gmail(использует tls) на яндекс. Тоже ничего не работает. Что я делаю не так?


Вот код главной формы

Код:
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->sendBtn, SIGNAL(clicked()),this, SLOT(sendMail()));
    connect(ui->exitBtn, SIGNAL(clicked()),this, SLOT(close()));
}

void MainWindow::sendMail()
{
    Smtp* smtp = new Smtp(ui->uname->text(), ui->paswd->text(), ui->server->text(), ui->port->text().toInt());
    connect(smtp, SIGNAL(status(QString)), this, SLOT(mailSent(QString)));


    smtp->sendMail(ui->uname->text(), ui->rcpt->text() , ui->subject->text(),ui->msg->toPlainText());
}

void MainWindow::mailSent(QString status)
{
    if(status == "Message sent")
        QMessageBox::warning( 0, tr( "Qt Simple SMTP client" ), tr( "Message sent!\n\n" ) );
}

MainWindow::~MainWindow()
{
    delete ui;
}

Вот код файла smtp

Код:
/*
Copyright (c) 2013 Raivis Strogonovs

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/



#include "smtp.h"

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)
{
    message = "To: " + to + "\n";
    message.append("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" ) );
    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 );



}

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 = "";
}
Записан
Павелъ
Гость
« Ответ #1 : Июнь 02, 2017, 20:50 »

Без использования
Код:
socket->setprotocol

Дебаг выводит следующее

Код:
stateChanged  QAbstractSocket::HostLookupState
stateChanged  QAbstractSocket::ConnectingState
stateChanged  QAbstractSocket::ConnectedState
Connected
error  QAbstractSocket::SocketError(13)
stateChanged  QAbstractSocket::UnconnectedState
disconneted
error  "Error during SSL handshake: error:140770FC:SSL routines:SSL23_GET_SERVER_HELLO:unknown protocol"

Делаю вот так

Код:
socket->setProtocol(QSsl::SslV2);

Вылазит

Код:
stateChanged  QAbstractSocket::HostLookupState
stateChanged  QAbstractSocket::ConnectingState
stateChanged  QAbstractSocket::ConnectedState
error  QAbstractSocket::SocketError(21)
error  QAbstractSocket::SocketError(20)
Connected

Письмо не отправляется.

Что надо сделать, чтобы заработало?
Записан
Павелъ
Гость
« Ответ #2 : Июнь 02, 2017, 20:54 »

Отсюда качал исходники https://morf.lv/simple-tls-ssl-smtp-client-for-qt5
Записан
Apktyc
Самовар
**
Offline Offline

Сообщений: 132


Просмотр профиля
« Ответ #3 : Июнь 03, 2017, 12:25 »

В случае TLS (587 порт) соединение устанавливается незащищенным (!), после приветствия (EHLO/HELO), должно быть инициировано начало шифрования (STARTTLS), рукопожатие (SSL handshake) и после этого (при установке защищенного соединения) снова приветствуем сервер (EHLO/HELO) и отправляем письмо.

В случае SSL (465 порт / Implicit SSL) соединение сразу устанавливается защищенным, можно сразу работать. (Этот вариант судя по всему и работает).

Проверьте, как себя ведет код в районе STARTTLS, там должно вызываться QSslSocket::startClientEncryption.

P.S. Глянул ссылку, там у автора STARTTLS закомментировано, не уверен, что сервер ожидает рукопожатие без этой команды. (Во всяком случае, я ее всегда отправляю, и все smtp типа gmail, yandex, mail работают).
« Последнее редактирование: Июнь 03, 2017, 12:33 от Apktyc » Записан
Павелъ
Гость
« Ответ #4 : Июнь 05, 2017, 19:43 »

ничего умнее, чем сделать dll на Delphi и вызвать её в Qt, не придумал.
Осталось найти как отправить вложения через TIdSMTP, и будет всё очень круто.
Записан
Apktyc
Самовар
**
Offline Offline

Сообщений: 132


Просмотр профиля
« Ответ #5 : Июнь 05, 2017, 22:42 »

ничего умнее, чем сделать dll на Delphi и вызвать её в Qt, не придумал.
Осталось найти как отправить вложения через TIdSMTP, и будет всё очень круто.
Дык, TIdAttachmentFile же.
Записан
Павелъ
Гость
« Ответ #6 : Июнь 06, 2017, 18:54 »

ничего умнее, чем сделать dll на Delphi и вызвать её в Qt, не придумал.
Осталось найти как отправить вложения через TIdSMTP, и будет всё очень круто.
Дык, TIdAttachmentFile же.

вложения тоже реализовал, только с кодировками пришлось повозиться.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.053 секунд. Запросов: 23.