Мне надо, чтобы мой сервер отправлял регистрационные коды пользователям. Использую такую разработку
#include <string>
#include <sstream>
#include <iostream>
#include <ctime>
#include <algorithm>
#include <random>
#include <curl/curl.h>
class EmailAddress
{
public:
EmailAddress(const char *email)
: email_{email}
{
}
EmailAddress(const std::string &email)
: email_{email}
{
}
EmailAddress(const std::string &email, const std::string &displayName)
: email_{email.empty() ? "" : "<"+email+">"},
name_{"\"" + displayName + "\""}
{
}
std::string domain() const
{
return email_.substr(email_.find('@') + 1);
}
explicit operator const char *() const
{
return email_.c_str();
}
friend std::ostream &operator<<(std::ostream &out, const EmailAddress &email)
{
return out << email.name_ << " " << email.email_;
}
private:
std::string email_;
std::string name_;
};
typedef std::vector<EmailAddress> EmailAddresses;
std::ostream &operator<<(std::ostream &out, const EmailAddresses &emailAddresses);
class Email
{
public:
Email(const EmailAddress &from,
const EmailAddress &to,
const std::string &subject,
const std::string &body,
const EmailAddresses &cc = EmailAddresses())
: from_{from}
, to_{to}
, cc_{cc.empty() ? EmailAddresses(1, to) : cc}
, subject_{subject}
, body_{body}
{
}
CURLcode send(const std::string &url,
const std::string &userName,
const std::string &password);
private:
struct StringData {
std::string msg;
size_t bytesLeft;
StringData(std::string &&m) : msg{m}, bytesLeft{msg.size()} {}
StringData(std::string &m) = delete;
};
static std::string dateTimeNow_();
static size_t payloadSource_(void *ptr, size_t size, size_t nmemb, void *userp);
std::string generateMessageId_() const;
std::string setPayloadText_();
EmailAddress from_, to_;
EmailAddresses cc_;
std::string subject_, body_;
};
CURLcode Email::send(const std::string &url,
const std::string &userName,
const std::string &password)
{
CURLcode ret = CURLE_OK;
struct curl_slist *recipients = NULL;
CURL *curl = curl_easy_init();
StringData textData { setPayloadText_() };
if (curl) {
std::ostringstream cc;
cc << cc_;
curl_easy_setopt(curl, CURLOPT_USERNAME, userName.c_str());
curl_easy_setopt(curl, CURLOPT_PASSWORD, password.c_str());
curl_easy_setopt(curl, CURLOPT_URL, url .c_str());
curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
//curl_easy_setopt(curl, CURLOPT_CAINFO, "/path/to/certificate.pem");
curl_easy_setopt(curl, CURLOPT_MAIL_FROM, (const char *)from_);
recipients = curl_slist_append(recipients, (const char *)to_);
recipients = curl_slist_append(recipients, cc.str().c_str());
curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, payloadSource_);
curl_easy_setopt(curl, CURLOPT_READDATA, &textData);
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
ret = curl_easy_perform(curl);
if (ret != CURLE_OK) {
std::cerr << "curl_easy_perform() failed: "
<< curl_easy_strerror(ret)
<< std::endl;
}
curl_slist_free_all(recipients);
curl_easy_cleanup(curl);
}
return ret;
}
std::string Email::dateTimeNow_()
{
const int RFC5322_TIME_LEN = 32;
std::string ret;
ret.resize(RFC5322_TIME_LEN);
time_t tt;
#ifdef _MSC_VER
time(&tt);
tm *t = localtime(&tt);
#else
tm tv, *t = &tv;
tt = time(&tt);
localtime_r(&tt, t);
#endif
strftime(&ret[0], RFC5322_TIME_LEN, "%a, %d %b %Y %H:%M:%S %z", t);
return ret;
}
inline std::string Email::generateMessageId_() const
{
const size_t MESSAGE_ID_LEN = 37;
tm t;
time_t tt;
time(&tt);
#ifdef _MSC_VER
gmtime_s(&t, &tt);
#else
gmtime_r(&tt, &t);
#endif
std::string ret;
ret.resize(MESSAGE_ID_LEN);
size_t dateLen = std::strftime(&ret[0], MESSAGE_ID_LEN, "%Y%m%d%H%M%S", &t);
static const std::string alphaNum {
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz" };
std::mt19937 gen;
std::uniform_int_distribution<> distr(0, alphaNum.length() - 1);
std::generate_n(ret.begin() + dateLen,
MESSAGE_ID_LEN - dateLen,
[&]() { return alphaNum[distr(gen)]; });
return ret;
}
size_t Email::payloadSource_(void *ptr, size_t size, size_t nmemb, void *userp)
{
StringData *text = reinterpret_cast<StringData *>(userp);
if ((size == 0) || (nmemb == 0) || ((size * nmemb) < 1) || (text->bytesLeft == 0)) {
return 0;
}
if ((nmemb * size) >= text->msg.size()) {
text->bytesLeft = 0;
return text->msg.copy(reinterpret_cast<char *>(ptr), text->msg.size());
}
return 0;
}
std::string Email::setPayloadText_()
{
std::string ret = "Date: " + dateTimeNow_() + "\r\n";
std::ostringstream oss;
oss << "To: " << to_ << "\r\n"
"From: " << from_ << "\r\n";
if (cc_.size() > 1) {
oss << "Cc: " << cc_ << "\r\n";
}
ret += oss.str();
ret +=
"Message-ID: <" + generateMessageId_() + "@" + from_.domain() + ">\r\n"
"Subject: " + subject_ + "\r\n"
"\r\n" +
body_ + "\r\n"
"\r\n";
return ret;
}
std::ostream &operator<<(std::ostream &out, const EmailAddresses &emailAddresses)
{
if (!emailAddresses.empty()) {
auto it = emailAddresses.begin();
out << *it;
while (++it != emailAddresses.end()) {
out << "," << *it;
}
}
return out;
}
Если я отправляю с mail.ru тогда всё работает хорошо, например:
Email email({ "...@mail.ru", "Name" },
"...@mail.ru",
"Subject",
"Body" );
email.send ( "smtp://smtp.mail.ru:25",
"...",
"Password" );
Но если я отправляю с gmail
Email email({ "...@gmail.com", "Name" },
"...@gmail.com",
"Subj",
"Body");
email.send( "smtp://smtp.gmail.com:465",
"...@gmail.com",
"Password");
я получаю ошибку
* Rebuilt URL to: smtp://smtp.gmail.com:587/ * Hostname was NOT found in DNS cache * Trying 173.194.221.109... * Connected to smtp.gmail.com (173.194.221.109) port 587 (#0) < 220 smtp.gmail.com ESMTP 125sm1125430ljj.26 - gsmtp
EHLO DNPC
< 250-smtp.gmail.com at your service, [xx.xx.xxx.xx] < 250-SIZE 35882577 < 250-8BITMIME < 250-STARTTLS < 250-ENHANCEDSTATUSCODES < 250-PIPELINING < 250-CHUNKING < 250 SMTPUTF8
STARTTLS
< 220 2.0.0 Ready to start TLS * found 174 certificates in /etc/ssl/certs/ca-certificates.crt * server certificate verification OK * common name: smtp.gmail.com (matched) * server certificate expiration date OK * server certificate activation date OK * certificate public key: RSA * certificate version: #3 * subject: C=US,ST=California,L=Mountain View,O=Google Inc,CN=smtp.gmail.com * start date: Thu, 08 Dec 2016 10:53:19 GMT
* expire date: Thu, 02 Mar 2017 10:18:00 GMT
* issuer: C=US,O=Google Inc,CN=Google Internet Authority G2 * compression: NULL * cipher: AES-128-GCM * MAC: AEAD
EHLO DNPC
< 250-smtp.gmail.com at your service, [xx.xx.xxx.xx] < 250-SIZE 35882577 < 250-8BITMIME < 250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH < 250-ENHANCEDSTATUSCODES < 250-PIPELINING < 250-CHUNKING < 250 SMTPUTF8
AUTH LOGIN
< 334 VXNlcm5hbWU6
Z2lnaWN1Y3UwODhAZ21haWwuY29t
< 334 UGFzc3dvcmQ6
ZGt0Y2Voamxia2ZjbXRranhyZg==
< 235 2.7.0 Accepted
MAIL FROM:<...@gmail.com>
< 250 2.1.0 OK 125sm1125430ljj.26 - gsmtp
RCPT TO:<...@gmail.com>
< 250 2.1.5 OK 125sm1125430ljj.26 - gsmtp
RCPT TO:< ...@gmail.com>
< 553-5.1.2 The recipient address < ...@gmail.com> is not a valid RFC-5321 < 553 5.1.2 address. 125sm1125430ljj.26 - gsmtp * RCPT failed: 553
QUIT
< 221 2.0.0 closing connection 125sm1125430ljj.26 - gsmtp * Closing connection 0 curl_easy_perform() failed: Failed sending data to the peer
Что мне надо поправить, чтобы отправка почты заработала?