【发布时间】:2016-07-22 19:01:25
【问题描述】:
背景
我一直在构建一个使用 libcurl 发送电子邮件的小型测试程序。最终目标是将其实现为大型软件的远程通知系统。我主要使用this 和this 作为指南。
问题
程序一直运行到回调函数。调试器显示curl_callback 正在正确接收“字符串”TESTMESSAGE.email。然后再调用curl_callback 两次(总共三个)并挂起。
据我了解,curl 使用回调函数遍历“字符串”,读取每个字符。根据我在调试器中看到的情况,情况似乎并非如此。
我认为问题在于我如何将消息传递给 curl,但我对 curl 的操作方式没有足够的了解来验证这一点。对于准备和传递信息的最佳方式,我看过的许多示例和论坛似乎都相互矛盾。
注意:使用curl_global_init()是因为这里开发的功能会被实现成多线程程序。
来源
头文件.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];
来源.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, "\0");
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 元素以正确终止上传。工作源如下。
头文件.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;
来源.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);
}
【问题讨论】:
-
您是否故意忽略错误?并且不要强制转换
malloc()的返回值。如果必须强制转换,则说明您使用了错误的编程语言或错误的分配函数。另外,请在那里解释+12anf+10。通常你不应该包含空终止符以便你写malloc(some_length + 1),我对你的代码感到困惑。此外,不要只是malloc()并认为它有效。请始终检查NULL,你永远不知道它什么时候会发生,如果它发生了,你的代码完全不受此类故障的保护。 -
并且不要像这样使用
strcat(),你有所有的长度,使用memcpy(),因为像这样的strcat()会非常低效。特别是strcat(payload, "\0"),其中有两个'\0',因为""的大小实际上是1和长度0,你认为将""传递给strlen()是定义的行为吗?如果是这样,您认为strlen()如何确定它的长度并返回0? -
哦,还有一件事可能会给您带来更多问题:您在标头中声明了一个全局变量
payload,并在sendEmail()中声明了另一个变量,也称为payload。这肯定会引起混乱。 -
@iharob:我省略了错误检查,因为它使代码膨胀,here's 完整源代码。感谢您指出the importance of not casting with malloc。