Home
About
Blog
Media Gallery

Sending mail with libcurl


class CurlMailer
{
	private:
	curl_slist *recipients = 0;
	std::string MailUploadPackage = "";
	
	// Innstillinger.
	const char* MailFrom = "x Services <x@x.x>";
	const char* MailAuth = "x@x.x:xxx";
	const char* MailSMTP = "x.x.x";
	
	// Bygger en HTML melding.
	// Gidder ikke multipart...
	// Tilpasser evt. til text/plain ved behov.
	void BuildMessage()
	{
		MailUploadPackage = 
		u8"To: "+ To +"\r\n"
		u8"Cc: "+ Cc +"\r\n"
		u8"From: x Services <x@x.x>\r\n"
		u8"Subject: Testdetaljer fra batteritest.\r\n"
		u8"MIME-Version: 1.0\r\n"
		u8"Content-type: text/html; charset=UTF-8\r\n"
		u8"\r\n" + // Tom linje markerer deling mellom headers og body (RFC5322).
		Message;
	}

	struct DataPackage {
		size_t BytesUploaded = 0;
		const char *UploadPackage = 0;
	};

	// CURLOPT_READFUNCTION.
	static size_t readdata_callback(char *buf, size_t size, size_t nitems, DataPackage *data)
	{
		if (data->BytesUploaded == strlen(data->UploadPackage))
			return 0;

		size_t NumBytesToTransfer = size * nitems;

		// Sjekk at antall ikke overstiger det som er igjen.
		size_t RemainingBytes = strlen(data->UploadPackage) - data->BytesUploaded;
		if (NumBytesToTransfer > RemainingBytes)
			NumBytesToTransfer = RemainingBytes;

		// Kopier data til buffer ved å opprette en peker til det som er igjen.
		memcpy(buf, &data->UploadPackage[data->BytesUploaded], NumBytesToTransfer);

		// Registrer hvor mange bytes som er lastet opp, til neste runde.
		data->BytesUploaded = NumBytesToTransfer;

		return NumBytesToTransfer;
	}

	public:
	std::string To, Cc, Message;

	// Bruk format mail@mail.com eller Navn <mail@mail.com>.
	void AddRecipient(const char* MailAddress)
	{
		recipients = curl_slist_append(recipients, MailAddress);
	}

	void SendMail()
    {
		CURL *curl = curl_easy_init();
		CURLcode res = CURLE_OK;
		
		if (curl == 0)
			GetError(L"curl_easy_init() feilet.");
		
		// Fortsett hvis curl kunne initiere.
		curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
		curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
		curl_easy_setopt(curl, CURLOPT_MAIL_FROM, MailFrom);
		curl_easy_setopt(curl, CURLOPT_USERPWD, MailAuth);
		curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);

		std::string MailURL = "smtps://";
		MailURL.append(MailSMTP);
		curl_easy_setopt(curl, CURLOPT_URL, MailURL.c_str());

		BuildMessage();
		DataPackage bc;
		bc.BytesUploaded = 0;
		bc.UploadPackage = MailUploadPackage.c_str();
		curl_easy_setopt(curl, CURLOPT_READFUNCTION, readdata_callback);
		curl_easy_setopt(curl, CURLOPT_READDATA, &bc);
		curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
		curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

		// https://curl.se/libcurl/c/libcurl-errors.html
		res = curl_easy_perform(curl);
		if (res != CURLE_OK)
			MessageBoxA(
				0, 
				curl_easy_strerror(res), 
				std::to_string(res).c_str(), 
				MB_OK | MB_ICONINFORMATION
			);
		else
			MessageBoxW(
				0, 
				L"Testdetaljer sendt til x.", 
				L"Rapport", 
				MB_OK | MB_ICONINFORMATION
			);
				
		curl_slist_free_all(recipients);
		curl_easy_cleanup(curl);
    }
};


Example use:
CurlMailer cm;
cm.To = "x@x.x";
cm.Cc = "";
cm.AddRecipient("x@x.x");
cm.Message = "...";
cm.SendMail();


Todo, add attachments. I'll be rolling my own base64 multipart formatting. Curl has a kind of support for it, but I found it quirky and annoying.

Original Post: Sep 22nd, '22 12:23 CEST.
Updated: Sep 24th, '22 20:33 CEST.

Tags: C/C++