结合MIME C++ library与CURL发送带附件的邮件

       使用CURL发送不带附件的例子,在CURL的官方范例simplesmtp.cpp和smtp_tls.cpp中,认真看看,使用不难。而发送附件的例子则没有了,需要了解MIME协议,将邮件按照MIME编码,在CURLOPT_READFUNCTION发送出去即可。直接用字符串拼凑MIME有点麻烦,可以结合MIME C++ library完成这项工作。MIME C++ library的下载地址http://www.codesink.org/mimetic_mime_library.html。

#include "stdafx.h"
#include <string>
#include <iostream>
#include <mimetic/mimetic.h>
#include <sstream>
#include <iterator>
#include <stdio.h>
#include <fstream>

int _tmain(int argc, _TCHAR* argv[])
{

	mimetic::MimeEntity me;
	me.header().from("me <me@domain.com>");
	me.header().to("you <you@domain.com>");
	me.header().subject("my first mimetic msg");
	me.body().assign("hello there!");
	std::cout << me <<std::endl;
        return 0;
}

显示如下:

From: me <me@domain.com>   //From:简称<邮箱地址>
To: you <you@domain.com>    //To:简称<邮箱地址>
Subject: my first mimetic msg //邮件标题


hello there!  //正文内容

这是一个最简单的MIME 流了。如果是带附件的 那么:

#include "stdafx.h"
#include <string>
#include <iostream>
#include <mimetic/mimetic.h>
#include <sstream>
#include <iterator>
#include <stdio.h>
#include <fstream>

int _tmain(int argc, _TCHAR* argv[])
{

	mimetic::MultipartMixed head;
	//写件人
	head.header().from("<me@sina.com.cn>");
	//收件人
	head.header().to("<you@sina.com.cn>");
	//抄送
	head.header().cc("<he@sina.com.cn>;<she@sina.com.cn>;");
	//标题
	head.header().subject("my first mimetic msg");
	//MIME协议版本
	head.header().push_back(mimetic::Field("Mime-Version","1.0"));
	//正文,由于是MultipartMixed,这个内容是会被忽略的
	head.body().assign("hello there!");

	//创建一个新的块,在这里是附件
	mimetic::MimeEntity* pMe = new mimetic::MimeEntity;
	//下面的代码就是开始设置这一段邮件内容的头了

	//记住这里千万不能写成
	//pMe->header().push_back(mimetic::Field("Content-Type","pplication/octet-stream"));
	//pMe->header().push_back(mimetic::Field("name","readme.txt"));
	pMe->header().push_back(mimetic::Field("Content-Type: application/octet-stream; name=readme.txt"));
	//这里和上一句需要注意的地方一样,这里说明了这个块是一个附件
	pMe->header().push_back(mimetic::Field("Content-Disposition : attachment; filename=readme.txt"));
	//说明这个块是以BASE64编码的
	pMe->header().push_back(mimetic::Field("Content-Transfer-Encoding","base64"));

	//设置完头以后,加载具体内容
	FILE *pfile = fopen(".\\readme.txt","rb");
	char buffer[4096];
	uint32_t totalreadbytes = 0;
	while (!feof(pfile))
	{
		//读文件
		uint32_t readbytes = fread(buffer, 1, 4028, pfile);
		if (ferror(pfile) || readbytes == 0)
		{
			break;
		}
		totalreadbytes += readbytes;
		mimetic::Base64::Encoder b64;
		std::stringstream temp;
		std::ostreambuf_iterator<char> out(temp);
		//转为BASE64编码,目标存放至std::stringstream中
		mimetic::code(buffer,  buffer + readbytes, b64, out);
		std::string str = temp.str();
		std::cout<<str;
		//将转换后的内容放入块中,不要贪方便写成
		//pMe->load(temp.str().begin(), temp.str().end(), mimetic::imNone);
		//会崩溃的
		pMe->load(str.begin(), str.end(), mimetic::imNone);
	}

	std::cout<<head;
	return 0;
}
显示的MIME流如下:
Content-Type: multipart/mixed;
boundary="----lrsJRnnv8qrhVhSN0z5KGo2eQwzHwv_LA_MGCzpaJN87zTe1=_2_"
From: me@sina.com.cn
To: you@sina.com.cn
CC: <he@sina.com.cn>;<she@sina.com.cn>;
Subject: my first mimetic msg
Mime-Version: 1.0


------lrsJRnnv8qrhVhSN0z5KGo2eQwzHwv_LA_MGCzpaJN87zTe1=_2_
Content-Type: application/octet-stream; name="readme.txt"
Content-Disposition : attachment; filename=readme.txt
Content-Transfer-Encoding: base64


PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09DQogICAgQ09OU09MRSBBUFBMSUNBVElPTiA6IHRlc3RfbWltZXRpYyBQ
cm9qZWN0IE92ZXJ2aWV3DQo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09
PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0NCg0KQXBwV2l6YXJkIGhhcyBjcmVhdGVk
IHRoaXMgdGVzdF9taW1ldGljIGFwcGxpY2F0aW9uIGZvciB5b3UuDQoNClRoaXMgZmlsZSBjb250
YWlucyBhIHN1bW1hcnkgb2Ygd2hhdCB5b3Ugd2lsbCBmaW5kIGluIGVhY2ggb2YgdGhlIGZpbGVz
IHRoYXQNCm1ha2UgdXAgeW91ciB0ZXN0X21pbWV0aWMgYXBwbGljYXRpb24uDQoNCg0KdGVzdF9t
aW1ldGljLnZjeHByb2oNCiAgICBUaGlzIGlzIHRoZSBtYWluIHByb2plY3QgZmlsZSBmb3IgVkMr
KyBwcm9qZWN0cyBnZW5lcmF0ZWQgdXNpbmcgYW4gQXBwbGljYXRpb24gV2l6YXJkLg0KICAgIEl0
IGNvbnRhaW5zIGluZm9ybWF0aW9uIGFib3V0IHRoZSB2ZXJzaW9uIG9mIFZpc3VhbCBDKysgdGhh
dCBnZW5lcmF0ZWQgdGhlIGZpbGUsIGFuZA0KICAgIGluZm9ybWF0aW9uIGFib3V0IHRoZSBwbGF0
Zm9ybXMsIGNvbmZpZ3VyYXRpb25zLCBhbmQgcHJvamVjdCBmZWF0dXJlcyBzZWxlY3RlZCB3aXRo
IHRoZQ0KICAgIEFwcGxpY2F0aW9uIFdpemFyZC4NCg0KdGVzdF9taW1ldGljLnZjeHByb2ouZmls
dGVycw0KICAgIFRoaXMgaXMgdGhlIGZpbHRlcnMgZmlsZSBmb3IgVkMrKyBwcm9qZWN0cyBnZW5l
cmF0ZWQgdXNpbmcgYW4gQXBwbGljYXRpb24gV2l6YXJkLiANCiAgICBJdCBjb250YWlucyBpbmZv
cm1hdGlvbiBhYm91dCB0aGUgYXNzb2NpYXRpb24gYmV0d2VlbiB0aGUgZmlsZXMgaW4geW91ciBw
cm9qZWN0IA0KICAgIGFuZCB0aGUgZmlsdGVycy4gVGhpcyBhc3NvY2lhdGlvbiBpcyB1c2VkIGlu
IHRoZSBJREUgdG8gc2hvdyBncm91cGluZyBvZiBmaWxlcyB3aXRoDQogICAgc2ltaWxhciBleHRl
bnNpb25zIHVuZGVyIGEgc3BlY2lmaWMgbm9kZSAoZm9yIGUuZy4gIi5jcHAiIGZpbGVzIGFyZSBh
c3NvY2lhdGVkIHdpdGggdGhlDQogICAgIlNvdXJjZSBGaWxlcyIgZmlsdGVyKS4NCg0KdGVzdF9t
aW1ldGljLmNwcA0KICAgIFRoaXMgaXMgdGhlIG1haW4gYXBwbGljYXRpb24gc291cmNlIGZpbGUu
DQoNCi8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8v
Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vDQpPdGhlciBzdGFuZGFyZCBmaWxlczoNCg0KU3RkQWZ4
LmgsIFN0ZEFmeC5jcHANCiAgICBUaGVzZSBmaWxlcyBhcmUgdXNlZCB0byBidWlsZCBhIHByZWNv
bXBpbGVkIGhlYWRlciAoUENIKSBmaWxlDQogICAgbmFtZWQgdGVzdF9taW1ldGljLnBjaCBhbmQg
YSBwcmVjb21waWxlZCB0eXBlcyBmaWxlIG5hbWVkIFN0ZEFmeC5vYmouDQoNCi8vLy8vLy8vLy8v
Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8v
Ly8vLy8vLy8vDQpPdGhlciBub3RlczoNCg0KQXBwV2l6YXJkIHVzZXMgIlRPRE86IiBjb21tZW50
cyB0byBpbmRpY2F0ZSBwYXJ0cyBvZiB0aGUgc291cmNlIGNvZGUgeW91DQpzaG91bGQgYWRkIHRv
IG9yIGN1c3RvbWl6ZS4NCg0KLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8v
Ly8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8vLy8NCg==
------lrsJRnnv8qrhVhSN0z5KGo2eQwzHwv_LA_MGCzpaJN87zTe1=_2_--

在这里值得一说的是Content-Type: multipart/mixed;
boundary="----lrsJRnnv8qrhVhSN0z5KGo2eQwzHwv_LA_MGCzpaJN87zTe1=_2_"这里说明了邮件有多块,以----lrsJRnnv8qrhVhSN0z5KGo2eQwzHwv_LA_MGCzpaJN87zTe1=_2_为分割标识符。如果想给邮件中添加新的一个块,比如说,除了附件还需要个正文那么需要再new mimetic::MimeEntity* pMe

	//添加一段正文
	mimetic::MimeEntity* pMe2 = new mimetic::MimeEntity;
	//说明块是一段文字,文字使用GB2312字符集
	pMe2->header().push_back(mimetic::Field("Content-Type: text/plain; charset=GB2312"));
	pMe2->header().push_back(mimetic::Field("Content-Transfer-Encoding","base64"));
	//把这一块加入到整个邮件中
	head.body().parts().push_back(pMe2);
到此为止所需要的流已经生成了,剩下的就是在CURL中发送出去。
// smtp_ex01.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"


/***************************************************************************
*                                  _   _ ____  _
*  Project                     ___| | | |  _ \| |
*                             / __| | | | |_) | |
*                            | (__| |_| |  _ <| |___
*                             \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/ 
#include <stdio.h>
#include <mimetic/mimetic.h>
#include <curl/curl.h>
#include <string.h>
#include <iostream>
#include <sstream>
#include <stdio.h>
/* This is a simple example showing how to send mail using libcurl's SMTP
* capabilities. It builds on the simplesmtp.c example, adding some
* authentication and transport security.
*/ 

#define FROM    "<me@test.com.cn>"
#define TO      "<you@test.com.cn>"
#define CC      "<he@test.com.cn>"

#define  BUFFERR_SIZE 4096

struct UserData
{
	std::stringstream ss;
	size_t total;
	UserData()
		:total(0)
	{

	}
};


static size_t my_read(void *ptr, size_t size, size_t nmemb, void *userp)
{
	struct UserData * pstream = static_cast<struct UserData *>(userp);
	//assert(pstream);
	if (pstream->ss.eof())
	{
		//也是因为移动到末尾时tellg()返回的数据总是不对
		return 0;
	}
	size_t before = pstream->ss.tellg();
	pstream->ss.read((char*)ptr, size*nmemb);
	size_t after = pstream->ss.tellg();
	if (pstream->ss.eof())
	{
		//对std::stringstream不熟悉,不知道为什么移动到末尾后
		//tellg()返回的数据总是不对
		std::cout<<(pstream->total - before)<<std::endl;
		return pstream->total - before;
	}
	std::cout<<(after - before)<<std::endl;
	return after - before;
}



int main(void)
{
	CURL *curl;
	CURLcode res;
 	struct curl_slist *recipients = NULL;

	mimetic::MultipartMixed head;
	head.header().from("<me@test.com.cn>");
	head.header().to("<you@test.com.cn>");
	head.header().cc("<he@test.com.cn>");
	head.header().subject("my first mimetic msg");
	head.header().push_back(mimetic::Field("Mime-Version","1.0"));
	head.header().push_back(mimetic::Field("Message-ID","<dcd7cb36-11db-487a-9f3a-e652a9458efd@rfcpedant.example.org>"));
	head.body().assign("hello there!");

	mimetic::MimeEntity* pMe = new mimetic::MimeEntity;

	pMe->header().push_back(mimetic::Field("Content-Type: application/octet-stream; name=readme.txt"));
	pMe->header().push_back(mimetic::Field("Content-Disposition : attachment; filename=readme.txt"));
	pMe->header().push_back(mimetic::Field("Content-Transfer-Encoding","base64"));
	FILE *pfile = fopen(".\\readme.txt","rb");
	char buffer[BUFFERR_SIZE];
	uint32_t totalreadbytes = 0;
	while (!feof(pfile))
	{
		uint32_t readbytes = fread(buffer, 1, BUFFERR_SIZE - 1, pfile);
		if (ferror(pfile) || readbytes == 0)
		{
			break;
		}
		totalreadbytes += readbytes;
		mimetic::Base64::Encoder b64;
		std::stringstream temp;
		std::ostreambuf_iterator<char> out(temp);
		buffer[readbytes] = '\0';
		mimetic::code(buffer,  buffer + readbytes, b64, out);
		std::string str = temp.str();
		pMe->load(str.begin(), str.end(), mimetic::imNone);
	}
	head.body().parts().push_back(pMe);
	struct UserData ud;
	ud.ss<<head;
	ud.ss.seekg(0, std::ios::end);
	ud.total = ud.ss.tellg();
	ud.ss.seekg(0, std::ios::beg);


	curl = curl_easy_init();
	if (curl) {
		/* This is the URL for your mailserver. Note the use of port 587 here,
		* instead of the normal SMTP port (25). Port 587 is commonly used for
		* secure mail submission (see RFC4403), but you should use whatever
		* matches your server configuration. */ 
		curl_easy_setopt(curl, CURLOPT_URL, "smtp://mainserver.example.net:25");//"smtp://mainserver.example.net:25");

		/* In this example, we'll start with a plain text connection, and upgrade
		* to Transport Layer Security (TLS) using the STARTTLS command. Be careful
		* of using CURLUSESSL_TRY here, because if TLS upgrade fails, the transfer
		* will continue anyway - see the security discussion in the libcurl
		* tutorial for more details. */ 
		//curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);

		/* If your server doesn't have a valid certificate, then you can disable
		* part of the Transport Layer Security protection by setting the
		* CURLOPT_SSL_VERIFYPEER and CURLOPT_SSL_VERIFYHOST options to 0 (false).
		*   curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
		*   curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
		* That is, in general, a bad idea. It is still better than sending your
		* authentication details in plain text though.
		* Instead, you should get the issuer certificate (or the host certificate
		* if the certificate is self-signed) and add it to the set of certificates
		* that are known to libcurl using CURLOPT_CAINFO and/or CURLOPT_CAPATH. See
		* docs/SSLCERTS for more information.
		*/ 
		//curl_easy_setopt(curl, CURLOPT_CAINFO, "/path/to/certificate.pem");

		/* A common reason for requiring transport security is to protect
		* authentication details (user names and passwords) from being "snooped"
		* on the network. Here is how the user name and password are provided: */ 
		curl_easy_setopt(curl, CURLOPT_USERNAME, "test@test.com");
		curl_easy_setopt(curl, CURLOPT_PASSWORD, "*****");

		/* value for envelope reverse-path */ 
		curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM);
		/* 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. */ 
		recipients = curl_slist_append(recipients, TO);
		recipients = curl_slist_append(recipients, CC);
		curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);

		/* In this case, we're using a callback function to specify the data. You
		* could just use the CURLOPT_READDATA option to specify a FILE pointer to
		* read from.
		*/ 
		curl_easy_setopt(curl, CURLOPT_READFUNCTION, my_read);
		curl_easy_setopt(curl, CURLOPT_READDATA, &ud);

// 		curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
// 		curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);

		/* 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 (including headers) */ 
		res = curl_easy_perform(curl);

		/* free the list of recipients and clean up */ 
		curl_slist_free_all(recipients);
		curl_easy_cleanup(curl);
	}
	return 0;
}
需要注意的是MIME C++ library构造邮件流的时候是将所有内容加载至内存中的,对于大的附件还得自己拼凑MIME流,不过通过对MIME C++ library学习了MIME协议这应该不是太难的问题了。


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值