C/C++, libCurl

Send email with attachment using Gmail, C and libcurl – Part 3

Requirements
* A Gmail account (Use a dedicated account! Do not use your personal one!)
* Turn on “Access for less secure apps” under the security settings of the account. less secure apps
* You may also have to enable IMAP in the account settings.

The following code snippets is from Post-recon project. This project is a work in progress.

Part 2 is here.

You can check Github for the full source code, here I will just point out the most interesting parts.

CURLOPT_READDATA
Custom pointer passed to the read callback.

struct upload_status {
    char *data;
    int dataLeft;
};

Email header

{...}
 
#define AttachmentEmailHeaderLines 17
static const char *emailWithAttachmentHeader[] = {
    "Date: %s\r\n",
    "To: %s (%s)\r\n",
    "From: %s (%s)\r\n",
    "Message-ID: <%s>\r\n",
    "Subject: %s\r\n",
    "MIME-Version: 1.0\r\n",
    "Content-Type: multipart/mixed; boundary=%s\r\n\r\n",
    "--%s\r\n",
    "Content-type: text/plain; charset=UTF-8\r\n",
    "Content-Transfer-Encoding: 7bit\r\n\r\n",
    "%s",                       
    "\r\n--%s\r\n",                 
    "Content-Type: application/octet-stream; name=\"%s\"\r\n",
    "Content-Transfer-Encoding: base64\r\n",
    "Content-Disposition: attachment; filename=\"%s\"\r\n\r\n",
    "%s",                               
    "\r\n--%s--\r\n",
};
 
{...}

CURLOPT_READFUNCTION
Read callback for data uploads.

{...}
 
//libcurl email callback
static size_t _read_function_callback(void *ptr, size_t size, size_t nmemb, void *userp)
{
    struct upload_status *upload_ctx = (struct upload_status *)userp;
    int dataLen = 0;
 
    if ((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
        return 0;
    }
 
    if (upload_ctx->dataLeft) {
        if (upload_ctx->dataLeft > SIZE) {
            dataLen = SIZE;
        }
        else {
            dataLen = upload_ctx->dataLeft;
        }
 
        memcpy(ptr, upload_ctx->data, dataLen);
        upload_ctx->data += dataLen;
        upload_ctx->dataLeft -= dataLen;
 
        return dataLen;
    }
 
    return 0;
}
 
{...}

Attach file
* Load file(attachment) into memory.
* Convert file to a base64 encoded string.
* Append base64 data to email.

{...}
 
static int buildAttachmentData(const char *format, const char *filepath, char **result)
{
    if (format == NULL || filepath == NULL) return -1;
 
    int _size = 0;
    int dataSize = 0;
    unsigned char *data = 0;
    char *base64Data = 0;
    int base64DataSize = 0;
 
    //read file
    if ((dataSize = Common::LoadFileIntoMemory(filepath, &data)) == -1) {
        return -1;
    }
 
    //convert file to base64 string
    if ((base64DataSize = Common::Base64Encode(data, dataSize, &base64Data)) == -1) {
        Common::hFree(data);
        return -1;
    }
 
    Common::hFree(data);
    _size = strlen(format) + base64DataSize;
 
    if ((*result = (char*)Common::hAlloc((_size + 1) * sizeof(char))) == NULL) {
        Common::hFree(base64Data);
        return -1;
    }
 
    if (Common::FormatString(*result, _size + 1, format, base64Data) == -1) {
        Common::hFree(base64Data);
        return -1;
    }
 
    Common::hFree(base64Data);
 
    return _size;
}
 
{...}