1. 程式人生 > >libcurl實現smtp傳送支援附件

libcurl實現smtp傳送支援附件

1、執行環境vs2015

2、libcurl版本:curl-7.51.0

3、拼接MIME協議

4、附上程式碼

//smtp.h
#pragma once

#include <string>
#include <vector>

#define SKIP_PEER_VERIFICATION
#define SKIP_HOSTNAME_VERIFICATION


class Smtp {
public:
        //構造smtp物件設定郵件編碼
	Smtp(const std::string& charset="utf-8");//
        //設定stmp伺服器、使用者名稱、密碼、埠(埠其實不用指定,libcurl預設25,但如果是smtps則預設是465)
	void SetSmtpServer(const std::string &username, const std::string& password, const std::string& servername, const std::string &port="25");
	//傳送者姓名,可以不用
        void SetSendName(const std::string& sendname);
        //傳送者郵箱
	void SetSendMail(const std::string& sendmail);
       //新增收件人
	void AddRecvMail(const std::string& recvmail);
       //設定主題
	void SetSubject(const std::string &subject);
       //設定正文內容
	void SetBodyContent(const std::string &content);
        //新增附件
	void AddAttachment(const std::string &filename);
       //傳送郵件
	bool SendMail();
private:
        //回撥函式,將MIME協議的拼接的字串由libcurl發出
	static  size_t payload_source(void *ptr, size_t size, size_t nmemb, void *stream);
       //建立郵件MIME內容
	void CreatMessage();
       //獲取檔案型別
	int getFileType(std::string const& stype);
       //設定檔名
	void SetFileName(const std::string& FileName);
       //設定檔案的contenttype
	void SetContentType(std::string const& stype);
       //得到檔名
	void GetFileName(const std::string& file, std::string& filename);
      //得到檔案型別
	void GetFileType(const std::string& file, std::string& stype);
	
private:
	std::string m_strCharset; //郵件編碼
	std::string m_strSubject; //郵件主題
	std::string m_strContent; //郵件內容
	std::string m_strFileName; //檔名
	std::string m_strMessage;// 整個MIME協議字串
	std::string m_strUserName;//使用者名稱
	std::string m_strPassword;//密碼
	std::string m_strServerName;//smtp伺服器
	std::string m_strPort;//埠
	std::string m_strSendName;//傳送者姓名
	std::string m_strSendMail;//傳送者郵箱
	std::string m_strContentType;//附件contenttype
	std::string m_strFileContent;//附件內容

	std::vector<std::string> m_vRecvMail; //收件人容器
	std::vector<std::string> m_vAttachMent;//附件容器
	

};


//smtp.cpp
#include "smtp.h"
#include "base64.h"
#include <curl/curl.h>
#include <iostream>
#include <sstream>
#include <fstream>

Smtp::Smtp(const std::string & charset)
{
	m_strCharset = charset;
	m_vRecvMail.clear();
}

void Smtp::SetSmtpServer(const std::string & username, const std::string &password, const std::string & servername, const std::string & port)
{
	m_strUserName = username;
	m_strPassword = password;
	m_strServerName = servername;
	m_strPort = port;
}

void Smtp::SetSendName(const std::string & sendname)
{
	std::string strTemp = "";
	strTemp += "=?";
	strTemp += m_strCharset;
	strTemp += "?B?";
	strTemp += base64_encode((unsigned char *)sendname.c_str(), sendname.size());
	strTemp += "?=";
	m_strSendName = strTemp;
}

void Smtp::SetSendMail(const std::string & sendmail)
{
	m_strSendMail = sendmail;
}

void Smtp::AddRecvMail(const std::string & recvmail)
{
	m_vRecvMail.push_back(recvmail);
}

void Smtp::SetSubject(const std::string & subject)
{
	std::string strTemp = "";
	strTemp = "Subject: ";
	strTemp += "=?";
	strTemp += m_strCharset;
	strTemp += "?B?";
	strTemp += base64_encode((unsigned char *)subject.c_str(), subject.size());
	strTemp += "?=";
	m_strSubject = strTemp;
}

void Smtp::SetBodyContent(const std::string & content)
{
	m_strContent = content;
}

void Smtp::AddAttachment(const std::string & filename)
{
	m_vAttachMent.push_back(filename);
}

bool Smtp::SendMail()
{
	CreatMessage();
	bool ret = true;
	CURL *curl;
	CURLcode res = CURLE_OK;
	struct curl_slist *recipients = NULL;
	
	curl = curl_easy_init();
	if (curl) {
		/* Set username and password */										 
		curl_easy_setopt(curl, CURLOPT_USERNAME, m_strUserName.c_str());
		curl_easy_setopt(curl, CURLOPT_PASSWORD, m_strPassword.c_str());
		std::string tmp = "smtp://";
		tmp += m_strServerName;
		curl_easy_setopt(curl, CURLOPT_URL, tmp);
		/* If you want to connect to a site who isn't using a certificate that is
		* signed by one of the certs in the CA bundle you have, you can skip the
		* verification of the server's certificate. This makes the connection
		* A LOT LESS SECURE.
		*
		* If you have a CA cert for the server stored someplace else than in the
		* default bundle, then the CURLOPT_CAPATH option might come handy for
		* you. */
#ifdef SKIP_PEER_VERIFICATION
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
#endif

		/* If the site you're connecting to uses a different host name that what
		* they have mentioned in their server certificate's commonName (or
		* subjectAltName) fields, libcurl will refuse to connect. You can skip
		* this check, but this will make the connection less secure. */
#ifdef SKIP_HOSTNAME_VERIFICATION
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
#endif

		/* Note that this option isn't strictly required, omitting it will result
		* in libcurl sending the MAIL FROM command with empty sender data. All
		* autoresponses should have an empty reverse-path, and should be directed
		* to the address in the reverse-path which triggered them. Otherwise,
		* they could cause an endless loop. See RFC 5321 Section 4.5.5 for more
		* details.
		*/
		//curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM); 
		curl_easy_setopt(curl, CURLOPT_MAIL_FROM, m_strSendMail.c_str());
		/* Add two recipients, in this particular case they correspond to the
		* To: and Cc: addressees in the header, but they could be any kind of
		* recipient. */
		for (size_t i = 0; i < m_vRecvMail.size(); i++) {

			recipients = curl_slist_append(recipients, m_vRecvMail[i].c_str());
		}
		curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);

		std::stringstream stream;
		stream.str(m_strMessage);
		stream.flush();
		/* We're using a callback function to specify the payload (the headers and
		* body of the message). You could just use the CURLOPT_READDATA option to
		* specify a FILE pointer to read from. */
		curl_easy_setopt(curl, CURLOPT_READFUNCTION, &Smtp::payload_source);
		curl_easy_setopt(curl, CURLOPT_READDATA, (void *)&stream);
		curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

		/* Since the traffic will be encrypted, it is very useful to turn on debug
		* information within libcurl to see what is happening during the
		* transfer */
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

		/* Send the message */
		res = curl_easy_perform(curl);
		CURLINFO info = CURLINFO_NONE;
		curl_easy_getinfo(curl, info);
		/* Check for errors */
	
		if (res != CURLE_OK) {
          
			fprintf(stderr, "curl_easy_perform() failed: %s\n\n",
				curl_easy_strerror(res));
			ret = false;
		}
			
		/* Free the list of recipients */
		curl_slist_free_all(recipients);

		/* Always cleanup */
		curl_easy_cleanup(curl);
		
	}
	return ret;
}

size_t Smtp::payload_source(void *ptr, size_t size, size_t nmemb, void *stream)
{
	size_t num_bytes = size * nmemb;
	char* data = (char*)ptr;
	std::stringstream* strstream = (std::stringstream*)stream;

	strstream->read(data, num_bytes);

	return strstream->gcount();
}

void Smtp::CreatMessage()
{
	m_strMessage = "From: ";
	m_strMessage += m_strSendMail;
	m_strMessage += "\r\nReply-To: ";
	m_strMessage += m_strSendMail;
	m_strMessage += "\r\nTo: ";
	for (size_t i = 0; i < m_vRecvMail.size(); i++)
	{
		if (i > 0) {
			m_strMessage += ",";
		}
		m_strMessage += m_vRecvMail[i];
	}
	m_strMessage += "\r\n";
	m_strMessage += m_strSubject;
	m_strMessage += "\r\nX-Mailer: The Bat! (v3.02) Professional";
	m_strMessage += "\r\nMime-Version: 1.0";
	m_strMessage += "\r\nContent-Type: multipart/mixed;";
	m_strMessage += "boundary=\"simple boundary\"";
	m_strMessage += "\r\nThis is a multi-part message in MIME format.";
	m_strMessage += "\r\n--simple boundary";
	//正文
	m_strMessage += "\r\nContent-Type: text/html;";
	m_strMessage += "charset=";
	m_strMessage += "\"";
	m_strMessage += m_strCharset;
	m_strMessage += "\"";
	m_strMessage += "\r\nContent-Transfer-Encoding: 7BIT";
	m_strMessage += "\r\n\r\n";
	m_strMessage += m_strContent;
	
	//附件
	std::string filename = "";
	std::string filetype = "";
	for (size_t i = 0; i < m_vAttachMent.size(); i++)
	{
		m_strMessage += "\r\n--simple boundary";
		GetFileName(m_vAttachMent[i], filename);
		GetFileType(m_vAttachMent[i], filetype);
		SetContentType(filetype);
		SetFileName(filename);
	
		m_strMessage += "\r\nContent-Type: ";
		m_strMessage += m_strContentType;
		m_strMessage += "\tname=";
		m_strMessage += "\"";
		m_strMessage += m_strFileName;
		m_strMessage += "\"";
		m_strMessage += "\r\nContent-Disposition:attachment;filename=";
		m_strMessage += "\"";
		m_strMessage += m_strFileName;
		m_strMessage += "\"";
		m_strMessage += "\r\nContent-Transfer-Encoding:base64";	
		m_strMessage += "\r\n\r\n";

	
		FILE *pt = NULL;
		if ((pt = fopen(m_vAttachMent[i].c_str(), "rb")) == NULL) {

			std::cerr << "開啟檔案失敗: " << m_vAttachMent[i] <<std::endl;
			continue;
		}
		fseek(pt, 0, SEEK_END);
		int len = ftell(pt);
		fseek(pt, 0, SEEK_SET);
		int rlen = 0;
		char buf[55];
		for (size_t i = 0; i < len / 54 + 1; i++)
		{
			memset(buf, 0, 55);
			rlen = fread(buf, sizeof(char), 54, pt);
			m_strMessage += base64_encode((const unsigned char*)buf, rlen);
			m_strMessage += "\r\n";
		}
		fclose(pt);
		pt = NULL;
		
	}
	m_strMessage += "\r\n--simple boundary--\r\n";
	
}


int Smtp::getFileType(std::string const & stype)
{
	if (stype == "txt")
	{
		return 0;
	}
	else if (stype == "xml")
	{
		return 1;
	}
	else if (stype == "html")
	{
		return 2;
	}
	else if (stype == "jpeg")
	{
		return 3;
	}
	else if (stype == "png")
	{
		return 4;
	}
	else if (stype == "gif")
	{
		return 5;
	}
	else if (stype == "exe")
	{
		return 6;
	}
	
	return -1;
}

void Smtp::SetFileName(const std::string & FileName)
{
	std::string EncodedFileName = "=?";
	EncodedFileName += m_strCharset;
	EncodedFileName += "?B?";//修改
	EncodedFileName += base64_encode((unsigned char *)FileName.c_str(), FileName.size());
	EncodedFileName += "?=";
	m_strFileName = EncodedFileName;
}

void Smtp::SetContentType(std::string const & stype)
{
	int type = getFileType(stype);
	switch (type)
	{//
	case 0:
		m_strContentType = "plain/text;";
		break;

	case 1:
		m_strContentType = "text/xml;";
		break;

	case 2:
		m_strContentType = "text/html;";

	case 3:
		m_strContentType = "image/jpeg;";
		break;

	case 4:
		m_strContentType = "image/png;";
		break;

	case 5:
		m_strContentType = "image/gif;";
		break;

	case 6:
		m_strContentType = "application/x-msdownload;";
		break;

	default:
		m_strContentType = "application/octet-stream;";
		break;
	}
}

void Smtp::GetFileName(const std::string& file, std::string& filename)
{
	
	std::string::size_type p = file.find_last_of('/');
	if (p == std::string::npos)
		p = file.find_last_of('\\');
	if (p != std::string::npos) {
		p += 1; // get past folder delimeter
		filename = file.substr(p, file.length() - p);
	}
}

void Smtp::GetFileType(const std::string & file, std::string & stype)
{
	std::string::size_type p = file.find_last_of('.');
	if (p != std::string::npos) {
		p += 1; // get past folder delimeter
		stype = file.substr(p, file.length() - p);
	}
}
//64編碼
#include <string>

#ifndef _BASE64_H_
#define _BASE64_H_

std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);

#endif
/* 
   base64.cpp and base64.h

   Copyright (C) 2004-2008 Ren?Nyffenegger

   This source code is provided 'as-is', without any express or implied
   warranty. In no event will the author be held liable for any damages
   arising from the use of this software.

   Permission is granted to anyone to use this software for any purpose,
   including commercial applications, and to alter it and redistribute it
   freely, subject to the following restrictions:

   1. The origin of this source code must not be misrepresented; you must not
      claim that you wrote the original source code. If you use this source code
      in a product, an acknowledgment in the product documentation would be
      appreciated but is not required.

   2. Altered source versions must be plainly marked as such, and must not be
      misrepresented as being the original source code.

   3. This notice may not be removed or altered from any source distribution.

   Ren?Nyffenegger [email protected]

*/

#include "base64.h"
#include <iostream>

static const std::string base64_chars = 
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             "abcdefghijklmnopqrstuvwxyz"
             "0123456789+/";


static inline bool is_base64(unsigned char c) 
{
  return (isalnum(c) || (c == '+') || (c == '/'));
}

std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) 
{
  std::string ret;
  int i = 0, j = 0;
  unsigned char char_array_3[3], char_array_4[4];

  while (in_len--)
	{
    char_array_3[i++] = *(bytes_to_encode++);
    if (i == 3) 
		{
      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
      char_array_4[3] = char_array_3[2] & 0x3f;

      for(i = 0; (i <4) ; i++)
        ret += base64_chars[char_array_4[i]];
      i = 0;
    }
  }

  if (i)
  {
    for(j = i; j < 3; j++)
      char_array_3[j] = '\0';

    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
    char_array_4[3] = char_array_3[2] & 0x3f;

    for (j = 0; (j < i + 1); j++)
      ret += base64_chars[char_array_4[j]];

    while((i++ < 3))
      ret += '=';

  }

  return ret;

}

std::string base64_decode(std::string const& encoded_string) 
{
  int in_len = encoded_string.size();
  int i = 0, j = 0, in_ = 0;
  unsigned char char_array_4[4], char_array_3[3];
  std::string ret;

  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) 
	{
    char_array_4[i++] = encoded_string[in_]; in_++;
    if (i ==4) {
      for (i = 0; i <4; i++)
        char_array_4[i] = base64_chars.find(char_array_4[i]);

      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

      for (i = 0; (i < 3); i++)
        ret += char_array_3[i];
      i = 0;
    }
  }

  if (i) 
	{
    for (j = i; j <4; j++)
      char_array_4[j] = 0;

    for (j = 0; j <4; j++)
      char_array_4[j] = base64_chars.find(char_array_4[j]);

    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

    for (j = 0; (j < i - 1); j++) 
			ret += char_array_3[j];
  }

  return ret;
}