2016-12-14 29 views
2

我需要从我的服务器向用户发送注册码。我为此开发了一些代码。我无法从Gmail发送电子邮件

#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 { 
     "" 
     "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服务发送电子邮件,它的效果很好。但是,如果使用gmail.com服务,我得到一个错误:

  • Rebuilt URL to: smtp://smtp.gmail.com:465/
  • Trying 64.233.165.109...
  • Connected to smtp.gmail.com (64.233.165.109) port 465 (#0)
  • response reading failed
  • Closing connection 0 curl_easy_perform() failed: Failure when receiving data from the peer
Email email({ "[email protected]", "Name" }, 
       "[email protected]", 
       "Subj", 
       "Body"); 

email.send( "smtp://smtp.gmail.com:465", 
       "[email protected]", 
       "Password"); 

我做了什么错?

+0

您是否已将SMTP中继打开为Gmail? – TheValyreanGroup

+1

你有密码吗?由于安全性,您无法使用正常密码。 –

+1

上次我做了这个,我不得不拒绝一些G-mail的内部安全(他们最近改变了怎么做,所以我不能给出一个很好的答案),并且@ Raindrop7建议,必须打开编译SSL支持。使用OpenSSL,但必须做一些默认的构建脚本的hackery,以使cURL和OpenSSL在使用mingw的Windows上很好地运行。 – user4581301

回答

2

我认为你错过了服务器的Gmail的认证,这里是传出邮件的一些配置:

发送邮件(SMTP)服务器

smtp.gmail.com 
Requires SSL: Yes 
Requires TLS: Yes (if available) 
Requires Authentication: Yes 
Port for SSL: 465 
Port for TLS/STARTTLS: 587 

也转到您的帐户,并启用IMAP和POP并保存更改。

+0

对于587:curl_easy_perform()失败:发送数据失败 – Ufx

+0

@Ufx cURL吐出来,或者可以配置为吐出一些相当详细的诊断信息。你使用的是什么开发环境和操作系统? – user4581301

+0

@ user4581301现在我正在使用VS 2015和Windows 7 – Ufx