完美支持中文及附件的基于libcur邮件客户端封装类
发表于:2024-11-24 作者:热门IT资讯网编辑
编辑最后更新 2024年11月24日,最近做了一个小项目需要在windows c++ 环境下,发送邮件在网上查了很多,大部分编译不成功,少部分中文乱码参考了 libcurl实例及,网友的源码,努力了两天终于得到了比较圆满的结果从 http
最近做了一个小项目需要在windows c++ 环境下,发送邮件
在网上查了很多,大部分编译不成功,少部分中文乱码
参考了 libcurl实例及,网友的源码,努力了两天终于得到了比较圆满的结果
从 https://github.com/honeyligo/curlsmtp.git 上修改
话不多说上源码
调用方式
std::vector to = { "[email protected]" }; std::vector cc = { "[email protected]" }; std::vector 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("[email protected]"); mail->set_subject("相信相当数量的人都已经在准备吐槽了"); mail->set_message(aa); mail->set_to(to); // mail->set_attach(attach); mail->send_mail(); Sleep(5);
#ifndef __CURL_SMTP_H__#define __CURL_SMTP_H__#include #include #include #include #include "ustd_string.h"#define SMTP_SERVER "smtp.126.com"#define SMTP_PORT "25"class CurlSmtp { struct WriteThis { int pos; int counter; std::vector 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& to); void set_secret(const std::vector& secret); void set_cc(const std::vector& cc); void set_attach(const std::vector& 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 send_buffer_; std::string from_; std::vector to_; std::vector cc_; std::vector secret_; std::vector 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::string>> attachment_; CURL *curl_ = nullptr; struct curl_slist* rcpt_list_; struct WriteThis pooh_; std::unordered_map typeMap_;};#endif // !__CURL_SMTP_H__
#include "stdafx.h"//#include #include "curlsmtp.h"#include #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 * 10size_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& to) { to_.resize(to.size()); to_.assign(to.begin(), to.end());}void CurlSmtp::set_secret(const std::vector& secret) { secret_.resize(secret.size()); secret_.assign(secret.begin(), secret.end());}void CurlSmtp::set_cc(const std::vector& cc) { cc_.resize(cc.size()); cc_.assign(cc.begin(), cc.end());}void CurlSmtp::set_attach(const std::vector& attach) { attach_.resize(attach.size()); attach_.assign(attach.begin(), attach.end());}void CurlSmtp::set_subject(const std::string& subject) { std::vector 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 filedata; char c = file.get(); for (; file.good(); c = file.get()) { if (file.bad()) break; filedata.push_back(c); } std::vector 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::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; }}
#ifndef __USTD_STRING_H__#define __USTD_STRING_H__#include #include #include #include #include #include #include namespace ustd{namespace string{static std::string sprintf(const char *format, ...);static size_t base64encode(const char *data, const int &len, std::vector &dest);static size_t base64decode(const char *data, const int &len, std::vector &dest);static size_t split(const std::string &src, const std::string &delim, std::vector &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 &args_map, const std::string &delim = "&");std::string url_base64encode(const std::string &url){ std::string url_text(url); std::vector 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 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 &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 &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 &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(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 &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 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