首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >libCURL:上传数据时崩溃(Qt,SMTP)

libCURL:上传数据时崩溃(Qt,SMTP)
EN

Stack Overflow用户
提问于 2019-10-22 09:49:54
回答 1查看 130关注 0票数 0

我正在尝试使用libcurl (7.66)实现一个非常专业的电子邮件客户端来发送电子邮件。

遗憾的是,无论我怎么尝试,它总是在调用curl_easy_perform(curl)时崩溃。

目前在process_mail回调中,我已经注释掉了所有内容;只返回0。我相当舒尔,这应该是足够的,至少不会崩溃的应用程序…

真正的目标是将邮件作为字符串存储在内存中,然后将其复制到流中。

我真的很感谢任何形式的帮助。因为这个问题,我有一个相当大的延迟。

这是头文件:

代码语言:javascript
复制
#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

以及实现的相关部分:

代码语言:javascript
复制
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;
}

最后是一些控制台输出的摘录:

代码语言:javascript
复制
*  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.
EN

回答 1

Stack Overflow用户

发布于 2019-10-22 10:06:14

代码语言:javascript
复制
QByteArray array = this->message.toLocal8Bit();

这将创建一个本地QByteArray对象,它是generateMessage()方法中的一个本地变量。当generateMessage()返回时,这个对象和其中的所有东西当然会像其他函数局部变量一样被销毁。这就是C++中局部函数变量的工作方式。

代码语言:javascript
复制
this->message_char = array.data();
this->message_size = sizeof(this->message_char);

这会在message_char中保存一个指向array内部缓冲区的指针。这也会将sizeof(this->message_char)保存在message_size中。因为message_char是一个const char *,所以无论sizeof(const char *)有多大,它总是会将sizeof(const char *)保存在message_size中,通常是4或8(取决于您的操作系统)。这在这一点上已经是一个明显的失败,但不是导致所示代码崩溃的最终失败。

最终的失败是,正如我前面所解释的,只要这个函数一返回--也就是现在(因为这是这个函数中的最后一条语句) -- array和它的所有内容就会立即被销毁。前面提到的指向array内部缓冲区的指针将变成一个悬空指针,指向不再存在的对象的内部内容,并且任何进一步尝试取消对该指针的引用都会导致未定义的行为和可能的崩溃。

这是导致崩溃的明显原因:不正确地使用指向已销毁对象的悬空指针。此外,sizeof的不当使用也是一个重要因素(如果您的消息的实际大小恰好小于操作系统上指针的大小)。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58496113

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档