我正在一个C ++项目中,我正在使用libcurl通过SMTP发送电子邮件。该代码几乎适用于较小的内容,但是,对于较大的电子邮件,它会引发写访问冲突,我看不出任何原因。
以下是我使用curl函数发送邮件的方式:
curl = curl_easy_init();
//curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
if (curl)
{
if (this->useVerboseOutput)
{
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
}
curl_easy_setopt(curl, CURLOPT_URL, smtpAddress.c_str());
if (this->useTLS)
{
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
}
if (this->useAuthentication)
{
if (this->username.empty() || this->password.empty())
{
throw logic_error("SMTP username or password has not been set but authentication is enabled");
}
curl_easy_setopt(curl, CURLOPT_USERNAME, this->username.c_str());
curl_easy_setopt(curl, CURLOPT_PASSWORD, this->password.c_str());
}
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, this->fromAddress.c_str());
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
curl_easy_setopt(curl, CURLOPT_READDATA, this);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, &EmailSender::invoke_write_data);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
//Send the message
res = curl_easy_perform(curl);
下面是读取函数的回叫
size_t EmailSender::invoke_write_data(void *data, size_t size, size_t nmemb, void* pInstance)
{
return ((EmailSender*)pInstance)->payload_source(data, size, nmemb);
}
size_t EmailSender::payload_source(void *ptr, size_t size, size_t nmemb)
{
//struct upload_status *upload_ctx = (struct upload_status*)userp;
const char *data;
if ((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
return 0;
}
if (this->upload_ctx.lines_read < this->lineArray.size())
{
data = this->lineArray.at(this->upload_ctx.lines_read).c_str();
}
else
{
return 0;
}
if (data) {
size_t len = strlen(data);
memcpy(ptr, data, len);
this->upload_ctx.lines_read++;
return len;
}
return 0;
}
它在第5次调用后在this->upload_ctx.lines_read++;
行崩溃(向量lineArray中有6行,upload_ctx-> lines_read为5。
完整的错误消息是:
Exception thrown at 0x00007FFF4E8F16D7 (vcruntime140d.dll) in myapp.exe: 0xC0000005: Access violation writing location 0x00000205CC8AC000.
答案 0 :(得分:3)
根据the documentation of CURLOPT_READFUNCTION
:
简介
#include <curl/curl.h> size_t read_callback(char *buffer, size_t size, size_t nitems, void *instream); CURLcode curl_easy_setopt(CURL *handle, CURLOPT_READFUNCTION, read_callback);
说明
将指针传递给回调函数,如上面的原型所示。
libcurl会在需要读取数据以便将其发送给对等方时立即调用此回调函数,就像您要求它将数据上传或发布到服务器一样。 指针缓冲区所指向的数据区域最多应填充
size
乘以函数的nitems
个字节数。< / p>
您写道:
size_t len = strlen(data);
memcpy(ptr, data, len);
由于len
仅取决于您要发送的数据,并且由于您不检查数据是否小于size*nitems
(对您而言,nmemb
),因此您可能会写出分配的缓冲区由libcurl提供,因此调用了未定义的行为。
由于您按行工作,但libcurl按字节工作,因此您将需要重新编写应用程序以跟踪部分编写的行,或者完全删除行的概念。
答案 1 :(得分:0)
简短的回答:我认为您需要向收件人变量添加内容。
您没有提供足够的信息来检查c ++实现,因此没有太多人可以谈论它。我将您的代码稍微转换回了C并确认它可以按cURL smtp-mail.c示例中的预期运行。
soquestsmtp-mail.cpp
#include "pch.h"
enum optionuses : uint32_t
{
useVerboseOutput = 1 << 1, // 0x02
useTLS = 1 << 2, // 0x04
useAuthentication = 1 << 3, // 0x08
};
struct upload_status {
int lines_read;
};
typedef struct upload_status* pupload_status;
static size_t __cdecl invoke_write_data(void* buffer, size_t size, size_t nmemb, void* pInstance);
static size_t __cdecl payload_source(void* buffer, size_t size, size_t nmemb);
static size_t __cdecl read_callback(void* buffer, size_t size, size_t nitems, void* instream);
static const char* payload_text[] = {
"Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n",
"To: <addressee@example.net>\r\n",
"From: <sender@example.org>\r\n",
"Cc: <info@example.org>\r\n",
"Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd@"
"rfcpedant.example.org>\r\n",
"Subject: SMTP example message\r\n",
"\r\n", /* empty line to divide headers from body, see RFC5322 */
"The body of the message starts here.\r\n",
"\r\n",
"It could be a lot of lines, could be MIME encoded, whatever.\r\n",
"Check RFC5322.\r\n",
nullptr
};
extern "C" int __cdecl
wmain(_In_ int argc,_In_reads_(argc) _Pre_z_ wchar_t** argv,_In_z_ wchar_t** envp)
{
argc;argv;envp;
CURL* curl = nullptr;
int res = CURLE_OK;
uint32_t options = useVerboseOutput|useTLS|useAuthentication;
std::string smtpAddress = "smtp://mail.example.com";
std::string fromAddress = "<sender@example.org>";
std::string toAddress = "<addressee@example.net>";
std::string ccAddress = "<info@example.org>";
std::string username = "sockerconny";
std::string password = "love_mom";
struct curl_slist* recipients = nullptr;
struct upload_status upload_ctx;
upload_ctx.lines_read = 0;
curl = curl_easy_init();
//curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
if (curl)
{
if (options & useVerboseOutput)
{
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
}
curl_easy_setopt(curl, CURLOPT_URL, smtpAddress.c_str());
if (options & useTLS)
{
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
}
if (options & useAuthentication)
{
if (username.empty() || password.empty())
{
// throw std::logic_error("SMTP username or password has not been set but authentication is enabled");
}
curl_easy_setopt(curl, CURLOPT_USERNAME, username.c_str());
curl_easy_setopt(curl, CURLOPT_PASSWORD, password.c_str());
}
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, fromAddress.c_str());
recipients = curl_slist_append(recipients, toAddress.c_str());
recipients = curl_slist_append(recipients, ccAddress.c_str());
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
//Send the message
res = curl_easy_perform(curl);
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
}
}
size_t __cdecl read_callback(void* buffer, size_t size, size_t nitems, void *instream)
{
pupload_status pupload_ctx = (pupload_status)instream;
const char* data = nullptr;
if((size == 0) || (nitems == 0) || ((size*nitems) < 1)) {
return 0;
}
data = payload_text[pupload_ctx->lines_read];
if(data) {
size_t len = strlen(data);
memcpy(buffer, data, len);
pupload_ctx->lines_read++;
return len;
}
return 0;
}
pch.h
// pch.h - precompiled header with standard includes and definitions
#pragma once
#define STRICT
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#define DISABLE_WARNING_PUSH(x) \
__pragma(warning(push)); __pragma(warning(disable: x))
#define DISABLE_WARNING_POP __pragma(warning(pop))
#define _WIN32_WINNT 0x0601 // minimum Windows 7
#include <winsdkver.h>
#include <sdkddkver.h>
#ifndef WINAPI_FAMILY
#define WINAPI_FAMILY WINAPI_FAMILY_DESKTOP_APP
#endif
// disable useless MSVC warnings when compiling with -Wall
#pragma warning(disable: 4514 4710 4711)
// comment out for diagnostic messages, usually safe to ignore
#pragma warning(disable: 4625 4626 4820)
// temporary disable warnings when compiling with -Wall
DISABLE_WARNING_PUSH(4191 4350 4365 4774 4571 4640 5026 5027 5039)
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include <stdexcept>
#include <windows.h>
#include <ws2tcpip.h>
DISABLE_WARNING_POP
#define CURL_STATICLIB
#define USE_LIBSSH2
#define HAVE_LIBSSH2_H
#define USE_SCHANNEL
#define USE_WINDOWS_SSPI
#define USE_WIN32_IDN
#define WANT_IDN_PROTOTYPES
#include <curl/curl.h>