完美支持中文及附件的基于libcur邮件客户端封装类

最近做了一个小项目需要在windows c++ 环境下,发送邮件
在网上查了很多,大部分编译不成功,少部分中文乱码
参考了 libcurl实例及,网友的源码,努力了两天终于得到了比较圆满的结果
从 https://github.com/honeyligo/curlsmtp.git 上修改
话不多说上源码
调用方式

    std::vector<std::string> to = {
        "43982653@qq.com"
    };
    std::vector<std::string> cc = {
        "fj1981@126.com"
    };
    std::vector<std::string> attach = {
        "C://Users//xxx//Downloads//Compressed//curlsmtp-master//curlsmtp.cpp"
    };
    auto l =L"相信相当数量的人都已经在准备吐槽了,只要看过《编程珠玑》的人都知道这道题的答案和其中极为简单的道理。不过别着急骂街,不管你信不信,这道笔试题我拿到的答案好多都长这样:";
    auto aa = CStdStr::W2UTF(l);
    CurlSmtp* mail = new CurlSmtp("fj1981"
                                  , "*****"
                                  , "smtp.126.com"
                                  , "25");
    mail->set_from("fj1981@126.com");
    mail->set_subject("相信相当数量的人都已经在准备吐槽了");
    mail->set_message(aa);
    mail->set_to(to);
   // mail->set_attach(attach);
    mail->send_mail();
    Sleep(5);

<ustd_string.h>

#ifndef __CURL_SMTP_H__
#define __CURL_SMTP_H__

#include <vector>
#include <string.h>
#include <unordered_map>
#include <curl/curl.h>
#include "ustd_string.h"

#define SMTP_SERVER             "smtp.126.com"
#define SMTP_PORT               "25"

class CurlSmtp {
    struct WriteThis {
        int pos;
        int counter;
        std::vector<std::string> data;
    };

  public:
    CurlSmtp(const std::string& user,
             const std::string& password,
             const std::string& server = SMTP_SERVER,
             const std::string& port = SMTP_PORT);

    ~CurlSmtp();

    void set_from(const std::string& from);
    void set_to(const std::vector<std::string>& to);
    void set_secret(const std::vector<std::string>& secret);
    void set_cc(const std::vector<std::string>& cc);
    void set_attach(const std::vector<std::string>& attach);
    void set_subject(const std::string& subject);
    void set_message(const std::string& message);
    void set_server(const std::string& server);
    void set_port(const std::string& port);
    void set_user(const std::string& user);
    void set_password(const std::string& password);
    bool send_mail();
    std::string last_error() const;
  private:
    void make_send_message();
    bool attach(const std::string& filename);
    void set_receiver_list();
    void set_curl_option();
    void clear();
    static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userp);

    std::string get_boundary();

  private:
    std::vector<std::string> send_buffer_;
    std::string from_;
    std::vector<std::string> to_;
    std::vector<std::string> cc_;
    std::vector<std::string> secret_;
    std::vector<std::string> attach_;
    std::string server_;
    std::string port_;
    std::string subject_;
    std::string message_;
    std::string user_;
    std::string password_;
    std::string last_error_;
    std::vector<std::pair<std::vector<char>, std::string>> attachment_;

    CURL *curl_ = nullptr;
    struct curl_slist* rcpt_list_;
    struct WriteThis pooh_;

    std::unordered_map<std::string, std::string> typeMap_;
};

#endif // !__CURL_SMTP_H__

<curlsmtp.cpp>

#include "stdafx.h"
//#include <fstream>
#include "curlsmtp.h"
#include <fstream>
#define LEFT_BRACE                    "<"
#define RIGTH_BRACE                   ">"
#define ENTER                       "/r/n"
#define BOUNDARY_FLAG               "--"
#define USER_AGENT                  "User-Agent: Mail Client"
#define MIME_VER                    "MIME-Version: 1.0"
#define HEADER_CONTENT_TYPE         "Content-Type: multipart/mixed;"
#define MSG_CONTENT_TYPE            "Content-Type: text/html; charset=utf-8; format=flowed"
#define MSG_ENCODING                "Content-Transfer-Encoding: 7bit"
#define MULTI_PERFORM_HANG_TIMEOUT  60 * 1000
#define CHUNCK_SIZE                 1024 * 10
size_t CurlSmtp::read_callback(void *ptr, size_t size, size_t nmemb,
void *userp) {
struct WriteThis *pooh = (struct WriteThis *)userp;
//const char *data;
if(size * nmemb < 1)
return 0;
const std::string& str = pooh->data[pooh->counter];
if(pooh->counter < (int)pooh->data.size()) {
size_t len = str.size();
int size = len - pooh->pos;
if (len < CHUNCK_SIZE || size <= CHUNCK_SIZE) {
memcpy(ptr, str.c_str() + pooh->pos, size);
pooh->counter++; /* advance pointer */
pooh->pos = 0;
return size;
} else {
memcpy(ptr, str.c_str() + pooh->pos, CHUNCK_SIZE);
pooh->pos += CHUNCK_SIZE;
return CHUNCK_SIZE;
}
}
return 0;
}
std::string CurlSmtp::get_boundary() {
std::string boundary;
boundary.reserve(16);
const char hex[] =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
for (int i = 0; i < 16; ++i) {
int x = rand() % 62;
boundary.append(1, hex[x]);
}
return boundary;
}
CurlSmtp::CurlSmtp(const std::string& user,
const std::string& password,
const std::string& server,
const std::string& port)
: user_(user)
, password_(password)
, server_(server)
, port_(port)
, rcpt_list_(NULL)
, curl_(curl_easy_init()) {
curl_global_init(CURL_GLOBAL_DEFAULT);
typeMap_.insert(std::make_pair(".gif", "Content-Type: image/gif;"));
typeMap_.insert(std::make_pair(".jpg", "Content-Type: image/jpg;"));
typeMap_.insert(std::make_pair(".jpeg", "Content-Type: image/jpeg;"));
typeMap_.insert(std::make_pair(".png", "Content-Type: image/png;"));
typeMap_.insert(std::make_pair(".bmp", "Content-Type: image/bmp;"));
typeMap_.insert(std::make_pair(".txt", "Content-Type: plain/txt;"));
typeMap_.insert(std::make_pair(".log", "Content-Type: plain/txt;"));
typeMap_.insert(std::make_pair(".htm", "Content-Type: plain/htm;"));
typeMap_.insert(std::make_pair(".html", "Content-Type: plain/htm;"));
typeMap_.insert(std::make_pair(".exe",
"Content-Type: application/X-exectype-1;"));
}
CurlSmtp::~CurlSmtp() {
curl_easy_cleanup(curl_);
curl_global_cleanup();
}
void CurlSmtp::set_from(const std::string& from) {
from_.assign(from);
}
void CurlSmtp::set_password(const std::string& password) {
password_.assign(password);
}
void CurlSmtp::set_to(const std::vector<std::string>& to) {
to_.resize(to.size());
to_.assign(to.begin(), to.end());
}
void CurlSmtp::set_secret(const std::vector<std::string>& secret) {
secret_.resize(secret.size());
secret_.assign(secret.begin(), secret.end());
}
void CurlSmtp::set_cc(const std::vector<std::string>& cc) {
cc_.resize(cc.size());
cc_.assign(cc.begin(), cc.end());
}
void CurlSmtp::set_attach(const std::vector<std::string>& attach) {
attach_.resize(attach.size());
attach_.assign(attach.begin(), attach.end());
}
void CurlSmtp::set_subject(const std::string& subject) {
std::vector<char> outdata;
ustd::string::base64encode(&subject[0], subject.size(), outdata);
outdata.push_back(0);
std::string encode ="=?utf-8?B?";
encode += &outdata[0];
encode += "?=";
subject_= std::move(encode);
}
void CurlSmtp::set_message(const std::string& message) {
message_.assign(message);
}
void CurlSmtp::set_server(const std::string& server) {
server_.assign(server);
}
void CurlSmtp::set_port(const std::string& port) {
port_.assign(port);
}
void CurlSmtp::set_user(const std::string& user) {
user_.assign(user_);
}
bool CurlSmtp::send_mail() {
last_error_.clear();
set_receiver_list();
make_send_message();
set_curl_option();
auto  res = curl_easy_perform(curl_);
/* Check for errors */
if (res != CURLE_OK) {
char buff[MAX_PATH] = {};
sprintf(buff, "failed: %s/n",
curl_easy_strerror(res));
last_error_ = buff;
}
clear();
return res == CURLE_OK;
}
std::string CurlSmtp::last_error() const {
return last_error_;
}
bool CurlSmtp::attach(const std::string& filename) {
if (!filename.length()) // do silly checks.
return false;
std::ifstream file(filename.c_str(), std::ios::binary | std::ios::in);
if (!file)
return false;
std::vector<char> filedata;
char c = file.get();
for (; file.good(); c = file.get()) {
if (file.bad())
break;
filedata.push_back(c);
}
std::vector<char> outdata;
ustd::string::base64encode(&filedata[0], filedata.size(), outdata);
std::string fn(filename);
std::string::size_type p = fn.find_last_of('/');
if (p == std::string::npos)
p = fn.find_last_of('//');
if (p != std::string::npos) {
p += 1; // get past folder delimeter
fn = fn.substr(p, fn.length() - p);
}
attachment_.push_back(std::make_pair(outdata, fn));
return true;
}
void CurlSmtp::set_receiver_list() {
for (int i = 0; i < (int)to_.size(); i++) {
rcpt_list_ = curl_slist_append(rcpt_list_,
std::string(LEFT_BRACE + to_[i] + RIGTH_BRACE).c_str());
}
for (int i = 0; i < (int)cc_.size(); i++) {
rcpt_list_ = curl_slist_append(rcpt_list_,
std::string(LEFT_BRACE + cc_[i] + RIGTH_BRACE).c_str());
}
for (int i = 0; i < (int)secret_.size(); i++) {
rcpt_list_ = curl_slist_append(rcpt_list_,
std::string(LEFT_BRACE + secret_[i] + RIGTH_BRACE).c_str());
}
}
void CurlSmtp::set_curl_option() {
pooh_.pos = 0;
pooh_.counter = 0;
pooh_.data.resize(send_buffer_.size() + 1);
pooh_.data.insert(pooh_.data.begin(), send_buffer_.begin(), send_buffer_.end());
curl_easy_setopt(curl_, CURLOPT_URL,
std::string("smtp://" + server_ + ":" + port_).c_str());
curl_easy_setopt(curl_, CURLOPT_USERNAME, user_.c_str());
curl_easy_setopt(curl_, CURLOPT_PASSWORD, password_.c_str());
curl_easy_setopt(curl_, CURLOPT_READFUNCTION, read_callback);
curl_easy_setopt(curl_, CURLOPT_MAIL_FROM, from_.c_str());
curl_easy_setopt(curl_, CURLOPT_MAIL_RCPT, rcpt_list_);
curl_easy_setopt(curl_, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl_, CURLOPT_READDATA, &pooh_);
curl_easy_setopt(curl_, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(curl_, CURLOPT_SSLVERSION, 0L);
curl_easy_setopt(curl_, CURLOPT_SSL_SESSIONID_CACHE, 0L);
curl_easy_setopt(curl_, CURLOPT_UPLOAD, 1L);
}
void CurlSmtp::clear() {
from_.clear();
password_.clear();
to_.clear();
cc_.clear();
secret_.clear();
attach_.clear();
attachment_.clear();
subject_.clear();
message_.clear();
server_.clear();
port_.clear();
if (rcpt_list_ != NULL) {
curl_slist_free_all(rcpt_list_);
rcpt_list_ = NULL;
}
}
void CurlSmtp::make_send_message() {
send_buffer_.clear();
// from
send_buffer_.push_back("From: " LEFT_BRACE + from_ + RIGTH_BRACE);
// to
for (int i = 0; i < (int)to_.size(); ++i) {
send_buffer_.push_back("To: " LEFT_BRACE + to_[i] + RIGTH_BRACE);
}
// cc
for (int i = 0; i < (int)cc_.size(); ++i) {
send_buffer_.push_back("Cc: " LEFT_BRACE + cc_[i] + RIGTH_BRACE);
}
// subject
send_buffer_.push_back("Subject: " + subject_);
if (attach_.empty() && 0) {
// split body
send_buffer_.push_back(ENTER);
// message
send_buffer_.push_back(message_ + ENTER);
} else {
// user agent
send_buffer_.push_back(USER_AGENT);
send_buffer_.push_back(MIME_VER);
send_buffer_.push_back(HEADER_CONTENT_TYPE);
std::string boundary(get_boundary());
// set boundary
send_buffer_.push_back(" boundary=/"" + boundary + "/"" ENTER);
// first part of body, boundary header and message
send_buffer_.push_back(BOUNDARY_FLAG + boundary);
send_buffer_.push_back(MSG_CONTENT_TYPE);
send_buffer_.push_back(MSG_ENCODING);
// split body
send_buffer_.push_back(ENTER);
send_buffer_.push_back(message_ + ENTER);
send_buffer_.push_back(BOUNDARY_FLAG + boundary);
// attachment
for (int i = 0; i < (int)attach_.size(); ++i) {
attach(attach_[i]);
}
for (std::vector<std::pair<std::vector<char>, std::string>>::iterator it1 =
attachment_.begin();
it1 != attachment_.end(); ++it1) {
if (it1->second.length() > 3) {
// long enough for an extension
std::string typ(it1->second.substr(it1->second.length() - 4, 4));
if (typeMap_.count(typ) > 0) {
send_buffer_.push_back(typeMap_[typ]);
} else {
// add other types
// everything else
send_buffer_.push_back("Content-Type: application/X-other-1;");
}
} else {
// default to don't know
send_buffer_.push_back("Content-Type: application/X-other-1;");
}
send_buffer_.push_back(" name=/"" + it1->second + "/"");
send_buffer_.push_back("Content-Transfer-Encoding: base64");
send_buffer_.push_back("Content-Disposition: attachment; filename=/"" +
it1->second + "/"");
// split body
send_buffer_.push_back(ENTER);
send_buffer_.push_back(std::string(it1->first.begin(), it1->first.end()));
// terminate the message with the boundary + "--"
if ((it1 + 1) == attachment_.end())
send_buffer_.push_back(BOUNDARY_FLAG + boundary + BOUNDARY_FLAG);
else
send_buffer_.push_back(BOUNDARY_FLAG + boundary);
}
}
// add /r/n to each item
for (int i = 0; i < (int)send_buffer_.size(); ++i) {
send_buffer_[i] += ENTER;
}
}

<ustd_string.h>

#ifndef __USTD_STRING_H__
#define __USTD_STRING_H__
#include <vector>
#include <string>
#include <stdarg.h>
#include <stdlib.h>
#include <unordered_map>
#include <algorithm>
#include <cctype>
namespace ustd
{
namespace string
{
static std::string sprintf(const char *format, ...);
static size_t base64encode(const char *data, const int &len, std::vector<char> &dest);
static size_t base64decode(const char *data, const int &len, std::vector<char> &dest);
static size_t split(const std::string &src, const std::string &delim, std::vector<std::string> &dst);
static std::string ltrim(const std::string &src, const std::string &key = " ");
static std::string rtrim(const std::string &src, const std::string &key = " ");
static std::string trim(const std::string &src, const std::string &key = " ");
static int replace(std::string &base, const std::string &src, const std::string &dst = "");
static std::string url_encode(const std::string &url_text);
static std::string url_base64encode(const std::string &url);
static std::string url_decode(const std::string &url_text);
static std::string url_base64decode(const std::string &url);
static std::string tolower(const std::string &src_text);
static std::string toupper(const std::string &src_text);
static size_t args_parse(const std::string &args, std::unordered_map<std::string, std::string> &args_map, const std::string &delim = "&");
std::string url_base64encode(const std::string &url)
{
std::string url_text(url);
std::vector<char> buffer;
if (ustd::string::base64encode(url_text.c_str(), url_text.size(), buffer) > 0)
{
url_text.assign(&buffer[0], buffer.size());
ustd::string::replace(url_text, "+", "-");
ustd::string::replace(url_text, "/", "_");
return url_text;
}
return "";
}
std::string url_base64decode(const std::string &url)
{
std::string url_text(url);
ustd::string::replace(url_text, "-", "+");
ustd::string::replace(url_text, "_", "/");
std::vector<char> buffer;
if (ustd::string::base64decode(url_text.c_str(), url_text.size(), buffer) > 0)
{
return (std::string(&buffer[0], buffer.size()));
}
return "";
}
std::string sprintf(const char *format, ...)
{
char buffer[10240] = {0x00};
va_list arg_ptr;
va_start(arg_ptr, format);
vsprintf(buffer, format, arg_ptr);
va_end(arg_ptr);
return (buffer);
}
size_t base64encode(const char *data, const int &len, std::vector<char> &dest)
{
static const char encodedict[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
int div = len / 3;
int mod = len % 3;
int size = div * 4 + ((mod == 0) ? 0 : 4);
dest.clear();
dest.reserve(size);
for (int i = 0; i < div; ++i)
{
unsigned char c1 = *data++;
unsigned char c2 = *data++;
unsigned char c3 = *data++;
dest.push_back(encodedict[c1 >> 2]);
dest.push_back(encodedict[((c1 << 4) | (c2 >> 4)) & 0x3f]);
dest.push_back(encodedict[((c2 << 2) | (c3 >> 6)) & 0x3f]);
dest.push_back(encodedict[c3 & 0x3f]);
}
switch (mod)
{
case 1:
{
unsigned char c1 = *data++;
dest.push_back(encodedict[(c1 & 0xfc) >> 2]);
dest.push_back(encodedict[((c1 & 0x03) << 4)]);
dest.push_back('=');
dest.push_back('=');
break;
}
case 2:
{
unsigned char c1 = *data++;
unsigned char c2 = *data++;
dest.push_back(encodedict[(c1 & 0xfc) >> 2]);
dest.push_back(encodedict[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)]);
dest.push_back(encodedict[((c2 & 0x0f) << 2)]);
dest.push_back('=');
break;
}
default:
{
break;
}
}
return dest.size();
}
size_t base64decode(const char *data, const int &len, std::vector<char> &dest)
{
static const char decodedict[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62,        // '+'
0, 0, 0,
63,        // '/'
52, 53, 54, 55, 56, 57, 58, 59, 60, 61,                     // '0'-'9'
0, 0, 0, 0, 0, 0, 0,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,         // 'A'-'Z'
0, 0, 0, 0, 0, 0,
26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51          // 'a'-'z'
};
dest.clear();
if (len % 4 != 0)
{
return dest.size();
}
int div = len / 4;
int size = div * 3;
dest.reserve(size);
unsigned char *udata = (unsigned char *)data;
for (int i = 0; i < div; ++i)
{
int key = decodedict[*udata++] << 18;
key += decodedict[*udata++] << 12;
dest.push_back((char)((key & 0x00ff0000) >> 16));
if (*udata != '=')
{
key += decodedict[*udata++] << 6;
dest.push_back((char)((key & 0x0000ff00) >> 8));
if (*udata != '=')
{
key += decodedict[*udata++];
dest.push_back((char)(key & 0x000000ff));
}
}
}
return dest.size();
}
size_t split(const std::string &src, const std::string &delim, std::vector<std::string> &dst)
{
dst.clear();
size_t idx = 0;
size_t pos = src.find(delim, idx);
while (pos != std::string::npos)
{
dst.push_back(src.substr(idx, pos - idx));
idx = pos + delim.length();
pos = src.find(delim, idx);
}
dst.push_back(src.substr(idx));
return dst.size();
}
std::string ltrim(const std::string &src, const std::string &key)
{
size_t pos = src.find_first_not_of(key);
if (pos != std::string::npos)
{
return src.substr(pos);
}
return ("");
}
std::string rtrim(const std::string &src, const std::string &key)
{
size_t pos = src.find_last_not_of(key);
if (pos != std::string::npos)
{
return src.substr(0, pos + 1);
}
return ("");
}
std::string trim(const std::string &src, const std::string &key)
{
return ltrim(rtrim(src, key), key);
}
int replace(std::string &base, const std::string &src, const std::string &dst)
{
int count = 0;
size_t src_len = src.length();
size_t dst_len = dst.length();
size_t pos = base.find(src, 0);
while (pos != std::string::npos)
{
count += 1;
base.replace(pos, src_len, dst);
pos = base.find(src, pos + dst_len);
}
return count;
}
std::string url_encode(const std::string &url_text)
{
size_t idx = 0;
std::string encode_text;
char hex[] = "0123456789abcdef";
size_t str_size = url_text.size();
while (idx < str_size)
{
unsigned char ch = url_text[idx++];
//0-9 a-z A-Z
//- _ . ! ~ * ( ) /'
//: ; ? @ & =
if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '-' || ch == '_' || ch == '.')
{
encode_text += ch;
}
else
{
encode_text += "%";
encode_text += hex[ch / 16];
encode_text += hex[ch % 16];
}
}
return encode_text;
}
std::string url_decode(const std::string &url_text)
{
size_t idx = 0;
std::string decode_text;
size_t str_size = url_text.size();
while (idx < str_size)
{
char ch = url_text[idx++];
switch (ch)
{
case '%':
{
std::string str = url_text.substr(idx, 2);
decode_text += static_cast<char>(strtol(str.c_str(), NULL, 16));
idx += 2;
}
break;
case '+':
{
decode_text += ' ';
}
break;
default:
{
decode_text += ch;
}
break;
}
}
return decode_text;
}
std::string tolower(const std::string &src_text)
{
std::string lower_text = src_text;
transform(lower_text.begin(), lower_text.end(), lower_text.begin(), (int (*)(int))::tolower);
return lower_text;
}
std::string toupper(const std::string &src_text)
{
std::string upper_text = src_text;
transform(upper_text.begin(), upper_text.end(), upper_text.begin(), (int (*)(int))::toupper);
return upper_text;
}
size_t args_parse(const std::string &args, std::unordered_map<std::string, std::string> &args_map, const std::string &delim)
{
args_map.clear();
size_t args_count = 0;
std::string args_text = args;
size_t idx = args.find("?");
if (idx != std::string::npos)
{
args_text = args.substr(idx + 1);
}
std::vector<std::string> tokens;
if (ustd::string::split(args_text, delim, tokens) > 0)
{
for (size_t i = 0; i < tokens.size(); ++i)
{
size_t pos = tokens[i].find("=");
if (pos != std::string::npos)
{
std::string key = ustd::string::tolower(ustd::string::trim(tokens[i].substr(0, pos)));
std::string value = ustd::string::trim(tokens[i].substr(pos + 1));
if (!key.empty() && args_map.find(key) == args_map.end())
{
args_map.insert(std::make_pair(key, value));
args_count += 1;
}
}
}
}
return args_count;
}
}
}
#endif

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/190596.html

(0)
上一篇 2021年11月14日
下一篇 2021年11月14日

相关推荐

发表回复

登录后才能评论