libcurl smtp 消息发送

libcurl smtp message sending

背景

我一直在构建一个使用 libcurl 发送电子邮件的小型测试程序。最终目标是将其实现为大型软件的远程通知系统。我主要使用 this and this 作为指南。

问题

程序一直运行到回调函数。调试器显示 curl_callback 正在正确接收 'string' TESTMESSAGE.email。然后再调用 curl_callback 两次(总共三次)并挂起。

据我了解,curl 使用回调函数遍历 'string',读取每个字符。根据我在调试器中看到的情况,情况似乎并非如此。

我认为问题在于我如何将消息传递给 curl,但我对 curl 的运作方式没有足够的了解来验证这一点。关于准备和传递消息的最佳方式,我看过的许多示例和论坛似乎都相互矛盾。

注:curl_global_init()是因为这里开发的功能要实现成多线程程序。

来源

header.h
/* Define message structures */
typedef struct emailMsg {
    const char *toEmail;
    const char *toName;
    const char *fromEmail;
    const char *fromName;
    const char *subject;
    const char *body;
    const char **email;
    size_t pos;
} emailMsg;


int sendEmail(emailMsg *data);
static size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *instream);
char *payload[6];
source.c
#include <header.h>    

int main(void){
    curl_global_init(CURL_GLOBAL_NOTHING);

    struct emailMsg TESTMESSAGE;

    /* Definitions */
    TESTMESSAGE.toEmail = "<toEmail@somedomain.com>";
    TESTMESSAGE.toName = "toName";
    TESTMESSAGE.fromEmail = "<fromEmail@somedomain.com>";
    TESTMESSAGE.fromName = "fromName";
    TESTMESSAGE.subject = "Test Message";
    TESTMESSAGE.body = "THIS IS A TEST MESSAGE.\r\n"
                    "\r\n"
                    "DISREGARD IT AND HAVE A NICE DAY!";

    sendEmail(&TESTMESSAGE);

    return(0);
}


int sendEmail(emailMsg *data){
    size_t bodySize = strlen(data->body) + 3;
    char *body = malloc(bodySize);
    snprintf(body, bodySize, "%s%s", data->body, "\r\n");

    size_t subjectSize = strlen(data->subject) + 12;
    char *subject = malloc(subjectSize);
    snprintf(subject, subjectSize, "%s%s%s", "Subject: ", data->subject, "\r\n");

    size_t receiverSize = strlen(data->toName) + strlen(data->toEmail) + 10;
    char *receiver = malloc(receiverSize);
    snprintf(receiver, receiverSize, "%s%s (%s)%s", "To: ", data->toEmail, data->toName, "\r\n");

    size_t senderSize = strlen(data->fromEmail) + strlen(data->fromName) + 12;
    char *sender = malloc(senderSize);
    snprintf(sender, senderSize, "%s%s (%s)%s", "From: ", data->fromEmail, data->fromName, "\r\n");

    size_t timeSize = 40;
    char *timeString = malloc(timeSize);
    time_t rawtime = 0;
    time(&rawtime);
    struct tm *timeinfo = localtime(&rawtime);
    strftime (timeString, timeSize, "Date: %a, %d %b %Y %T %z\r\n", timeinfo);

    /* Create payload (string) */
    size_t total_length = timeSize + receiverSize + senderSize + subjectSize + bodySize + 1;

    char *payload = (char*)malloc(total_length);
    memset(payload,0,strlen(payload));

    strcat(payload, timeString);
    strcat(payload, receiver);
    strcat(payload, sender);
    strcat(payload, subject);
    strcat(payload, body);
    strcat(payload, "[=11=]");

    data->email = &payload;

    CURL *curl;
    CURLcode res = CURLE_OK;
    struct curl_slist *recipients = NULL;

    data->pos = 0;

    curl = curl_easy_init();
    if (curl){
        curl_easy_setopt(curl, CURLOPT_URL, "smtp://somemta");

        curl_easy_setopt(curl, CURLOPT_MAIL_FROM, data->fromEmail);
        recipients = curl_slist_append(recipients, data->toEmail);
        curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);

        curl_easy_setopt(curl, CURLOPT_READFUNCTION, curl_callback);
        curl_easy_setopt(curl, CURLOPT_READDATA, data);
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
        /* useful for debugging encryped traffic */
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

        /* 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));
        };

        /* Free receipients list and cleanup */
        curl_slist_free_all(recipients);
        curl_easy_cleanup(curl);
    };

    return((int)res);
}


static size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *instream){
/*
 * Pass N bytes of data to libcurl for the transfer requested.
 * This is done 'size' by 'size'.
 *
 * Parameters:
 *  void *buffer:   Pointer to the buffer
 *  size_t size:    Size of each item
 *  size_t nmemb:   Number of items
 *  void *instream: Pointer address of data
 */
    /* Check for invalid arguments */
    if((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)){
        return(0);
    };

    emailMsg *upload = (emailMsg*)instream;
    const char *email = upload->email[upload->pos];

    if(email){
        size_t len = strlen(email);
        if(len > size * nmemb){
            return(CURL_READFUNC_ABORT);
        };
        memcpy(buffer, email, len);
        upload->pos++;

        return(len);
    };

    return(0);
}

编辑:分辨率

感谢 nephtes 帮助指出我的逻辑缺陷和程序的其他一些问题。您的解决方案帮助我意识到,通过将字符串连接成一个,我正在删除 CURL 正在寻找以正确终止上传的 NULL 元素。工作来源如下。

header.h
#define MTA "somemta"

/* Define message structures */
typedef struct emailMsg {
    const char *toEmail;
    const char *toName;
    const char *fromEmail;
    const char *fromName;
    const char *subject;
    const char *body;
    size_t pos;
    char *email[];
} emailMsg;

int sendEmail(emailMsg *data);
static size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *instream);
char *payload;
source.c
#include <header.h>

int main(void){
    curl_global_init(CURL_GLOBAL_NOTHING

    struct emailMsg TESTMESSAGE;

    /* Definitions */
    TESTMESSAGE.toEmail = "<toEmail@somedomain.com>";
    TESTMESSAGE.toName = "toName";
    TESTMESSAGE.fromEmail = "<fromEmail@somedomain.com>";
    TESTMESSAGE.fromName = "fromName";
    TESTMESSAGE.subject = "Test Message";
    TESTMESSAGE.body = "THIS IS A TEST MESSAGE.\r\n"
                        "\r\n"
                        "DISREGARD IT AND HAVE A NICE DAY!";

    /* Process and send message */
    sendEmail(&TESTMESSAGE);

    /* Return zero for success */
    return(0);
}


int sendEmail(emailMsg *data){
    int i;
    size_t sizes[6];

    /* Date */
    /* Calculate string size */
    sizes[0] = 39;

    /* Allocate memory (+1 for NULL) */
    data->email[0] = malloc(sizes[0] + 1);

    /* Compose string */
    time_t rawtime = 0;
    time(&rawtime);
    struct tm *timeinfo = localtime(&rawtime);
    strftime (data->email[0], sizes[0] + 1, "Date: %a, %d %b %Y %T %z\r\n", timeinfo);


    /* Receiver */
    /* Calculate string size */
    sizes[1] = strlen(data->toName) + strlen(data->toEmail) + 9;

    /* Allocate memory (+1 for NULL) */
    data->email[1] = malloc(sizes[1] + 1);

    /* Compose string */
    snprintf(data->email[1], sizes[1] + 1, "%s%s (%s)%s", "To: ", data->toEmail, data->toName, "\r\n");


    /* Sender */
    /* Calculate string size */
    sizes[2] = strlen(data->fromEmail) + strlen(data->fromName) + 11;

    /* Allocate memory (+1 for NULL) */
    data->email[2] = malloc(sizes[2] + 1);

    /* Compose string */
    snprintf(data->email[2], sizes[2] + 1, "%s%s (%s)%s", "From: ", data->fromEmail, data->fromName, "\r\n");


    /* Subject */
    /* Calculate string size */
    sizes[3] = strlen(data->subject) + 11;

    /* Allocate memory (+1 for NULL) */
    data->email[3] = malloc(sizes[3] + 1);

    /* Compose string */
    snprintf(data->email[3], sizes[3] + 1, "%s%s%s", "Subject: ", data->subject, "\r\n");


    /* Body */
    /* Calculate string size */
    sizes[4] = strlen(data->body) + 2;

    /* Allocate memory (+1 for NULL) */
    data->email[4] = malloc(sizes[4] + 1);

    /* Compose string */
    snprintf(data->email[4], sizes[4] + 1, "%s%s", data->body, "\r\n");

    /* NULL terminated */
    sizes[5] = 1;
    data->email[5] = "";

    /* Reset counter */
    data->pos = 0;

    printf("%s", data->email);

    /* CURL initialization */
    CURL *curl;
    CURLcode res = CURLE_OK;
    struct curl_slist *recipients = NULL;

    curl = curl_easy_init();
    if (curl){
        curl_easy_setopt(curl, CURLOPT_URL, MTA);

        curl_easy_setopt(curl, CURLOPT_MAIL_FROM, data->fromEmail);
        recipients = curl_slist_append(recipients, data->toEmail);
        curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);

        curl_easy_setopt(curl, CURLOPT_READFUNCTION, curl_callback);
        curl_easy_setopt(curl, CURLOPT_READDATA, data);
        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
        /* useful for debugging encryped traffic */
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

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

        /* Free receipients list and cleanup */
        curl_slist_free_all(recipients);
        curl_easy_cleanup(curl);
    };

    /* Return CURL response */
    return((int)res);
}


static size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *instream){
/*
 * Pass N bytes of data to libcurl for the transfer requested.
 * This is done 'size' by 'size'.
 *
 * Parameters:
 *  void *buffer:   Pointer to the buffer
 *  size_t size:    Size of each item
 *  size_t nmemb:   Number of items
 *  void *instream: Pointer address of data
 */
    /* Check for invalid arguments */
    if((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)){
        return(0);
    };

    /* Assign instream to local structure */
    emailMsg *upload = (emailMsg*)instream;

    /* Assign current position to local variable */
    const char *email = upload->email[upload->pos];

    /* Run until NULL reached */
    if(email){
        /* Find length of string */
        size_t len = strlen(email);

        /* len > expected message */
        if(len > size * nmemb){
            return(CURL_READFUNC_ABORT);
        };

        /* Copy to buffer */
        memcpy(buffer, email, len);

        /* Increment */
        upload->pos++;

        /* Return the number of bytes written */
        return(len);
    };

    /* Signify end of upload */
    return(0);
}

这是您之前所做的 curl_easy_perform()

char *payload = (char*)malloc(total_length);
data->email = &payload;

这是您在回调中所做的事情:

const char *email = upload->email[upload->pos]

问题是 upload->email(又名 data->email)不是数组,但您却将其视为一个数组。索引 0 没问题,但其他任何内容都会指向某个随机内存位置。

你需要做的是使 email 成为一个简单的 char* 缓冲区而不是 char**,并使用 upload->pos 作为该缓冲区的位置索引,递增其将数据复制到回调参数中提供的缓冲区时的值。