libCURL: сбой при загрузке данных (Qt, SMTP) - PullRequest
0 голосов
/ 22 октября 2019

Я пытаюсь реализовать довольно специализированный почтовый клиент, использующий libcurl (7.66) для отправки электронной почты.

К сожалению, всегда происходит сбой при вызове curl_easy_perform (curl), независимо от того, что я пытаюсь вообще.

В настоящее время в обратном вызове process_mail я все закомментировал;просто возвращаю 0. Я довольно уверен, этого должно быть достаточно, чтобы, по крайней мере, не сломать приложение ...

Реальная цель - это получить почту в виде строки в памяти и просто скопировать ее вstream.

Я действительно ценю любую помощь ... У меня довольно большая задержка из-за этой проблемы.

Вот файл заголовка:

#ifndef MAILMESSAGE_H
#define MAILMESSAGE_H

#include <QObject>
#include <QDebug>

#include <QFile>
#include <QIODevice>
#include <QByteArray>

#include <curl/curl.h>
#include <stdio.h>

class MailMessage : public QObject
{
    Q_OBJECT
public:
    explicit MailMessage(QObject *parent = nullptr);

    void setSMTPPort(QString smtp_port);
    void setSMTPAddress(QString smtp_address);
    void setSMTPUser(QString smtp_user);
    void setSMTPPassword(QString smtp_password);

    void addTo(QString address_to);
    void setFrom(QString address_from);
    void setSubject(QString subject);

    void setAlternativeText(QString text);
    void setHTML(QString html);
    void addAttachment(QString attachment_path, QString filename);

    void generateMessage();
    // return POSIX-style: 0: ok; 1: error
    int sendMail();

    QString message;
    int message_size;
    const char* message_char;

    QString smtp_port;
    QString smtp_address;
    QString smtp_user;
    QString smtp_password;
    //QStringList addresses_to;
    QString address_to;
    QString address_from;
    QString subject;
    QString payload_text;
    QString payload_html;
    QStringList attachments_base64;

    double upload_speed;
    double upload_time;

protected:
    static size_t process_mail(void *ptr, size_t size, size_t nmemb, void *userp);

signals:

public slots:
};

#endif // MAILMESSAGE_H

И соответствующие части реализации:

void MailMessage::generateMessage()
{
    this->message = //"To: "+ this->address_to +"\r\n"
            "From: "+ this->address_from +"\r\n"
            "Subject: "+ this->subject +"\r\n"
            "Content-Type: multipart/mixed; boundary=MixedBoundary"
            "\r\n"
            "--MixedBoundary"
            "Content-Type: multipart/alternative; boundary=AlternativeBoundary"
            "\r\n"
            "--AlternativeBoundary"
            "Content-Type: text/plain; charset=\"utf-8\""
            "\r\n"+ this->payload_text +"\r\n"
            "--AlternativeBoundary"
            "Content-Type: text/html; charset=\"utf-8\""
            "\r\n"+ this->payload_html +"\r\n"
            "--AlternativeBoundary--"
            "\r\n"
            "--MixedBoundary";
    /*
            "Content-Type: application/pdf; name=\"HelloWorld.pdf\""
            "Content-Transfer-Encoding: base64"
            "Content-Disposition: attachment; filename=\"HelloWorld.pdf\"";
    */
    this->message.append("\r\n"
            "--MixedBoundary--");

    QByteArray array = this->message.toLocal8Bit();
    this->message_char = array.data();
    this->message_size = sizeof(this->message_char);
}

size_t MailMessage::process_mail(void *ptr, size_t size, size_t nmemb, void *userprocess)
{
    size_t retcode = 0;

    /*
    MailMessage *self = reinterpret_cast<MailMessage*>(userprocess);

    if (sizeof(self->message_char) == 0)
        return retcode;

    retcode = (size * nmemb >= sizeof(self->message_char)) ? sizeof(self->message_char) : size * nmemb;


    self->message_size -= retcode;
    memcpy(ptr, self->message_char, retcode);
    self->message_char += retcode;
    */

    return retcode;
}

int MailMessage::sendMail()
{
    //qDebug() << this->message;

    CURLcode res = CURLE_OK;
    CURL *curl = curl_easy_init();
    curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
    curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
    // CURLUSESSL_ALL, CURLUSESSL_NONE, CURLUSESSL_TRY, CURLUSESSL_CONTROL
    curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
    curl_easy_setopt(curl, CURLOPT_URL, this->smtp_address.toStdString().c_str());
    curl_easy_setopt(curl, CURLOPT_USERNAME, this->smtp_user.toStdString().c_str());
    curl_easy_setopt(curl, CURLOPT_PASSWORD, this->smtp_password.toStdString().c_str());
    curl_easy_setopt(curl, CURLOPT_MAIL_FROM, this->address_from.toStdString().c_str());
    curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, this->address_to.toStdString().c_str());
    curl_easy_setopt(curl, CURLOPT_READFUNCTION, &MailMessage::process_mail);
    curl_easy_setopt(curl, CURLOPT_READDATA, this);

    /* Send the message */ 
    res = curl_easy_perform(curl);

    /* Check for errors */ 
    if(res != CURLE_OK)
    {
        fprintf(stderr, "curl_easy_perform() failed: %s\n",
                curl_easy_strerror(res));
    }
    else
    {
        curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &upload_speed);
        curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &upload_time);

        qDebug() << "Speed: "+QString::number(upload_speed) + " Time: "+QString::number(upload_time);
    }

    //curl_slist_free_all(this->address_to.toStdString().c_str());
    curl_easy_cleanup(curl);

    return 0;
}

И, наконец, некоторые выдержки из консольного вывода:

*  start date: May  8 08:01:21 2019 GMT
*  expire date: May 13 23:59:59 2021 GMT
*  subjectAltName: host "mail.gmx.net" matched cert's "mail.gmx.net"
*  issuer: C=DE; O=T-Systems International GmbH; OU=T-Systems Trust Center; ST=Nordrhein Westfalen; postalCode=57250; L=Netphen; street=Untere Industriestr. 20; CN=TeleSec ServerPass Extended Validation Class 3 CA
*  SSL certificate verify ok.
> EHLO cf-19
* SMTP 0x5572580cd700 state change from UPGRADETLS to EHLO
< 250-gmx.com Hello cf-19 [2.202.204.204]
* Curl_pp_readresp_ 55 bytes of trailing server response left
< 250-8BITMIME* Curl_pp_readresp_ 41 bytes of trailing server response left
< 250-AUTH LOGIN PLAIN
* Curl_pp_readresp_ 19 bytes of trailing server response left
< 250 SIZE 69920427
> AUTH PLAIN
* SASL 0x5572580cd780 state change from STOP to PLAIN
* SMTP 0x5572580cd700 state change from EHLO to AUTH
< 334 
> AHNhbXVlbF9iZWNrZXJAZ214LmRlADI4OTZhY2Q2
* SASL 0x5572580cd780 state change from PLAIN to FINAL
< 235 Authentication succeeded
* SASL 0x5572580cd780 state change from FINAL to STOP
* SMTP 0x5572580cd700 state change from AUTH to STOP
* STATE: PROTOCONNECT => DO handle 0x5572580d0678; line 1701 (connection #0)
* DO phase starts
> MAIL FROM:<xxxxxxx@xxx.xx>
* SMTP 0x5572580cd700 state change from STOP to MAIL
* STATE: DO => DOING handle 0x5572580d0678; line 1743 (connection #0)
< 250 Requested mail action okay, completed
03:37:55: The program has unexpectedly finished.
03:37:55: The process was ended forcefully.

1 Ответ

0 голосов
/ 22 октября 2019
QByteArray array = this->message.toLocal8Bit();

Создает локальный объект QByteArray, локальную переменную в методе generateMessage(). Когда generateMessage() вернется, этот объект и все в нем, конечно, будут уничтожены, как и любая другая локальная переменная функции. Вот как переменные локальной функции работают в C ++.

this->message_char = array.data();
this->message_size = sizeof(this->message_char);

Это сохраняет указатель на внутренний буфер array в message_char. Это также экономит sizeof(this->message_char) в message_size. Поскольку message_char - это const char *, это всегда сохранит sizeof(const char *), который обычно будет 4 или 8 (в зависимости от вашей операционной системы), в message_size, независимо от того, насколько велик array. Это уже очевидный сбой на данный момент, но не окончательный сбой, который приводит к сбою показанного кода.

Окончательный сбой заключается в том, что, как я объяснил ранее, как только эта функция вернется - что верноabout now (так как это последний оператор в этой функции) - array и все его содержимое немедленно уничтожается. Вышеупомянутый указатель на внутренний буфер array станет висящим указателем на внутреннее содержимое объекта, который больше не существует, и любые дальнейшие попытки разыменования этого указателя приводят к неопределенному поведению и вероятному сбою.

Это очевидная причина вашего сбоя: неправильное использование висячих указателей на разрушенных объектах. Кроме того, способствующим фактором является неправильное использование sizeof (если фактический размер вашего сообщения окажется меньше размера указателя в вашей операционной системе).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...