//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000-2018 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
//
// This program is free software.  The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
//
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please see the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
//
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
#include "stdtripwire.h"
#include "core/debug.h"
#include "mailmessage.h"
#include <locale.h>
#include "core/stringutil.h"
#include "core/timeconvert.h"
#include "tw/systeminfo.h"
#include "core/file.h"
#include "core/ntmbs.h"


///////////////////////////////////////////////////////////////////////////////
cMailMessage::cMailMessage()
{
}


///////////////////////////////////////////////////////////////////////////////
cMailMessage::~cMailMessage()
{
}


///////////////////////////////////////////////////////////////////////////////
void cMailMessage::SetBody(const TSTRING& strBody)
{
    mstrBody = strBody;
}


///////////////////////////////////////////////////////////////////////////////
void cMailMessage::SetFrom(const TSTRING& strFrom)
{
    mstrFrom = strFrom;
}


///////////////////////////////////////////////////////////////////////////////
void cMailMessage::SetFromName(const TSTRING& strFromName)
{
    mstrFromName = strFromName;
}


///////////////////////////////////////////////////////////////////////////////
void cMailMessage::SetSubject(const TSTRING& strSubject)
{
    mstrSubject = strSubject;
}


///////////////////////////////////////////////////////////////////////////////
void cMailMessage::AddRecipient(const TSTRING& strRecipient)
{
    mvstrRecipients.push_back(strRecipient);
}


///////////////////////////////////////////////////////////////////////////////
void cMailMessage::AttachFile(const TSTRING& strFullPath)
{
    mvstrAttachments.push_back(strFullPath);
}


///////////////////////////////////////////////////////////////////////////////
bool cMailMessage::Send()
{
    // this is a pure virtual method. You should never end up here.
    ASSERT(false);
    return false;
}


///////////////////////////////////////////////////////////////////////////////
//
// Returns true if enough things have been set so that a useful email message can be sent
//
bool cMailMessage::Ready()
{
    // You must specify 'from', 'to', the subject
    if (mstrFrom.length() == 0 || mvstrRecipients.size() == 0 || mstrSubject.length() == 0)
    {
        return false;
    }

    // make sure we can at least send something...
    if (mstrBody.empty())
    {
        mstrBody = _T("\r\n");
    }

    return true;
}

bool cMailMessage::GetAttachmentsAsString(std::string& s)
{
    s.erase();
    bool allOK = true;
    for (std::vector<TSTRING>::const_iterator at = mvstrAttachments.begin(); at != mvstrAttachments.end(); ++at)
    {
        s += "\r\n";

        cFile file;
        try
        {
            file.Open(at->c_str(), cFile::OPEN_READ);


            // Simply stream the file into the socket.
            // This will append the file as additional text.
            const cFile::File_t cBufSize = 2048;
            char                buf[cBufSize];
            cFile::File_t       bytes;

            while ((bytes = file.Read(buf, cBufSize)) != 0)
            {
                ASSERT(bytes > 0 && bytes <= cBufSize); // ensures typecast below
                ASSERT((std::string::size_type)bytes < std::string().max_size());

                s += std::string(buf, (size_t)bytes);
            }

            file.Close();
        }
        catch (eFile&)
        {
            // TODO: There is no method of reporting detailed information on
            // errors if they occur.  Perhaps someday there will be. At the moment
            // it does not seem necessary as the only attached files that will
            // be sent are temporary files that tripwire has just created. Thus
            // the likelyhood of failure is low.
            file.Close();
            allOK = false;
        }
    }

    // Send a new line to be sure the file ends properly
    // Failure to do this could result in the "<CRLF>.<CRLF>" begin acknowledged as
    // the end of the email message.
    s += "\r\n";

    return allOK;
}

std::string cMailMessage::Create822Header()
{
    std::ostringstream ss;
    std::string        strToList;
    for (std::vector<TSTRING>::size_type i = 0; i < mvstrRecipients.size(); i++)
    {
        if (strToList.length() > 0)
            strToList += ", ";

        strToList += cStringUtil::TstrToStr(mvstrRecipients[i]);
    }

    ss << cMailMessageUtil::FormatAddressHeader("MIME-Version", "1.0");

    TSTRING strDate;
    if (cMailMessageUtil::ReadDate(strDate))
        ss << cMailMessageUtil::FormatAddressHeader("Date", cStringUtil::TstrToStr(strDate));

    std::string fromAddress;
    if (!mstrFromName.empty())
    {
        fromAddress = "\"";
        fromAddress += cStringUtil::TstrToStr(mstrFromName);
        fromAddress += "\" <";
        fromAddress += cStringUtil::TstrToStr(mstrFrom);
        fromAddress += ">";
    }
    else
    {
        fromAddress = cStringUtil::TstrToStr(mstrFrom);
    }

    ss << cMailMessageUtil::FormatAddressHeader("From", fromAddress);
    ss << cMailMessageUtil::FormatAddressHeader("To", strToList);
    ss << cMailMessageUtil::FormatNonAddressHeader("Subject", cStringUtil::TstrToStr(mstrSubject));
    ss << cMailMessageUtil::FormatAddressHeader("Content-Type", "text/plain");

    return ss.str();
}

//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// cMailMessageUtil
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// gets the date in the RFC822 compliant form "Tue, 16 Mar 1999 10:53:17 -0800 (PST)"
// TODO:BAM -- make windows and unix use same function
bool cMailMessageUtil::ReadDate(TSTRING& strDateBuf)
{
    bool fGotDate = false;

#if HAVE_STRFTIME

    TCHAR      szDate[1024];
    time_t     current_time = time(NULL);
    struct tm* tm           = localtime(&current_time);

    const TCHAR* szFormat = _T("%a, %d %b %Y %H:%M:%S %z");

    size_t numChars = _tcsftime(szDate, countof(szDate), szFormat, tm);

    if (numChars != 0)
    {
        strDateBuf = szDate;
        fGotDate   = true;
    }

#else

    int64 now  = cSystemInfo::GetExeStartTime();
    strDateBuf = cTimeUtil::GetRFC822Date(cTimeUtil::TimeToDateGMT(now));
    fGotDate   = true;

#endif // HAVE_STRFTIME

    return fGotDate;
}


static const char* util_Get_IANA_CharSet()
{
    const char* pCP = setlocale(LC_CTYPE, NULL);

    if (pCP && pCP[0])
    {
        std::string s = pCP;
        // TODO:BAM -- change this to ctype::tolower when all compilers
        // have a proper locale impl. or better yet move to core/twlocale.cpp
        std::transform(s.begin(), s.end(), s.begin(), tolower);
        if (std::string::npos != s.find("jp") || std::string::npos != s.find("jap"))
        {
            return "ISO-2022-JP";
        }
    }

    // default
    return "US-ASCII";
}


static bool NeedsEncoding(char ch)
{
    return ((ch < 33) || (ch > 60 && ch < 62) || (ch > 126));
}

static std::string EncodeChar(char ch)
{
    std::ostringstream ss;

    ss.imbue(std::locale::classic());
    ss.fill('0');
    ss.setf(std::ios_base::hex, std::ios_base::basefield);
    ss.width(2);

    ss << (unsigned int)(unsigned char)ch;

    ASSERT(ss.str().length() == 2);

    // Make sure the hex is uppercase
    std::string s = ss.str();
    std::transform(s.begin(), s.end(), s.begin(), toupper);

    return s;
}


/* static */
iMimeEncoding* iMimeEncoding::GetInstance()
{
#define TSS_USE_QUOTED_PRINTABLE_MIME

    static iMimeEncoding* pME =

#if defined(TSS_USE_BASE64_MIME)
        new cBase64Encoding;
#elif defined(TSS_USE_QUOTED_PRINTABLE_MIME)
        new cQuotedPrintableEncoding;
#else
#    error what is iMimeEncoding?
#endif

    return pME;
}

// TODO:BAM -- line breaks
// TODO:BAM -- ToEncoding( const std::string& sIn, int maxLineLen = -1, int maxLen = -1 )
std::string cQuotedPrintableEncoding::Encode(const std::string& sIn, int maxLineLen, int maxLen)
{
    static const size_t _CHAR_AS_HEX_LEN  = 2;
    static const size_t _ENCODED_CHAR_LEN = _CHAR_AS_HEX_LEN + sizeof('=');

    std::string sOut;
    sOut.resize(sIn.size() * _ENCODED_CHAR_LEN); // optimize.  one char can turn into 3

    std::string::const_iterator at;
    std::string::size_type      i       = 0;
    std::string::size_type      lineLen = 0;
    for (at = sIn.begin(); at != sIn.end(); ++at, lineLen += _ENCODED_CHAR_LEN)
    {
        if (NeedsEncoding(*at))
        {
            ASSERT((unsigned char)*at <= 0xFF);

            std::string sTmp = EncodeChar(*at);
            ASSERT(sTmp.length() == _CHAR_AS_HEX_LEN);

            sOut[i++] = '=';

            std::copy(&sTmp[0], &sTmp[_CHAR_AS_HEX_LEN], &sOut[i]);
            ASSERT(_CHAR_AS_HEX_LEN > 0);
            i += _CHAR_AS_HEX_LEN;
        }
        else
        {
            sOut[i++] = *at;
        }

        //
        // if string is too long, just quit
        //
        if (maxLen != -1 && i >= maxLen - _ENCODED_CHAR_LEN)
        {
            break;
        }

        //
        // check line len
        //
        if (maxLineLen != -1 && lineLen >= maxLineLen - _ENCODED_CHAR_LEN)
        {
            // append EOL
            sOut[i++] = '=';
            sOut[i++] = '\r';
            sOut[i++] = '\n';
            lineLen   = 0;
        }
    }

    sOut.resize(i);

    return sOut;
}

std::string cMailMessageUtil::CreateEncodedText(const std::string& text)
{
    cDebug d("cMailMessageUtil::CreateEncodedText");

    d.TraceDebug("came in as: %s\n", text.c_str());

    std::string s;


    s += "=?";
    s += util_Get_IANA_CharSet();

    s += "?";
    s += iMimeEncoding::GetInstance()->GetEncodedWordIdentifier();
    s += "?";
    s += iMimeEncoding::GetInstance()->Encode(text, -1, _MAX_RFC822_HEADER_LEN);
    s += "?=";

    ASSERT(s.length() < _MAX_RFC822_LINE_LEN - _EOL_LEN);

    d.TraceDebug("came out as: %s\n", s.c_str());

    return s;
}

// TODO:BAM -- note that address headers can only have usascii chars
// TODO:BAM -- lines can only be 76 chars long -- enforce that!
std::string cMailMessageUtil::FormatAddressHeader(const std::string& name, const std::string& addr)
{
    cDebug d("cMailMessageUtil::FormatAddressHeader");

    d.TraceDebug("name: %s, value: %s\n", name.c_str(), addr.c_str());

    std::string s;

    s += name;
    s += ": ";
    s += addr;
    s += "\r\n";

    d.TraceDebug("came out as: %s\n", s.c_str());

    return s;
}

// TODO:BAM -- lines can only be 76 chars long -- enforce that!
std::string cMailMessageUtil::FormatNonAddressHeader(const std::string& name, const std::string& valC)
{
    cDebug d("cMailMessageUtil::FormatNonAddressHeader");

    d.TraceDebug("name: %s, value: %s\n", name.c_str(), valC.c_str());

    std::string val = valC;
    ASSERT(val != "To");
    ASSERT(val != "From");
    ASSERT(val != "Sender");
    ASSERT(val != "cc");
    ASSERT(val != "bcc");
    // ...

    std::string s;

    s += name;
    s += ": ";

    if (HasNonAsciiChars(val))
        s += CreateEncodedText(val);
    else
        s += val;

    s += "\r\n";


    d.TraceDebug("came out as: %s\n", s.c_str());

    ASSERT(s.length() < _MAX_RFC822_LINE_LEN - _EOL_LEN);

    return s;
}


bool cMailMessageUtil::HasNonAsciiChars(const std::string& s)
{
    for (std::string::const_iterator at = s.begin(); at != s.end(); ++at)
    {
        if ((unsigned char)*at > (unsigned char)0x7F)
            return true;
    }

    return false;
}

// converts lone \n's to \r\n
std::string& cMailMessageUtil::LFToCRLF(std::string& sIn)
{
    cDebug d("cMailMessageUtil::LFToCRLF");

    if (sIn.empty())
        return sIn;

    std::string sOut;

    // worst case: could be just '\n's
    // in which case we'd double the length
    sOut.resize(sIn.size() * 2);

    bool lastCharCR = false;

    std::string::size_type       stringSize = 0;
    std::string::const_iterator  at;
    std::string::difference_type charSize;
    for (at = sIn.begin(), charSize = ((*at ? at + 1 : at) - at); at != sIn.end();
         at += charSize, charSize   = ((*at ? at + 1 : at) - at))
    {
        ASSERT(charSize > 0);

        if (*at == '\n')
        {
            if (!lastCharCR)
            {
                d.TraceDebug("Adding LF\n");
                sOut[stringSize++] = '\r';
            }

            lastCharCR = false;
        }
        else if (*at == '\r')
        {
            lastCharCR = true;
        }
        else
        {
            lastCharCR = false;
        }

        std::copy(at, at + charSize, &sOut[stringSize]);
        stringSize += charSize;
    }

    sOut.resize(stringSize);
    sIn = sOut;
    return sIn;
}


// #define RDIFALCO_BASE64_IMPLEMENTATION
#ifndef RDIFALCO_BASE64_IMPLEMENTATION
/*
static 
std::string 
util_Base64Encode( const byte b[3], int size )
{
    // TODO:BAM -- what about endianness?
    ASSERT( size > 0 && size <= 3 );
    static char v64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    std::string s;

    if( size >= 1 )
    {
        // encode A
        s += v64[ b[0] >> 2 ];
    }
    
    if( size >= 2 )
    {
        // encode B,C
        s += v64[ ( ( b[0] & (byte)0x3 ) << 4 ) | ( b[1] >> 4 ) ];
        
        if( size == 3 )
            s += v64[ ( ( b[1] & (byte)0xF ) << 2 ) | ( b[2] >> 6 ) ];
        else
            s += v64[ ( ( b[1] & (byte)0xF ) << 2 ) ];
    }
    
    if( size >= 3 )
    {
        // encode D
        s += v64[ b[2] & (byte)0x3F ];
    }

    // padding
    if( size == 1 )
        s += "==";
    else if( size == 2 )
        s += "=";

    return s;
}

// MS _defines_ 'min' and 'max', so I can't use std::min(). 
template< class T >
const T& MS_SUCKS_min( const T& a, const T& b )
{
    return( b < a ? b : a );
}


/// More consistent with the rest of our Convert interfaces

// NOTE: Works when we don't want to allocate anything new

const std::string::value_type*
cMailMessageUtil::ConvertBase64( 
    std::string&  sEncode,
    const byte*   pchSrc,
    size_t        nchSrc )
{
    sEncode.assign( ToBase64( pchSrc, nchSrc ) );
    return sEncode.c_str();
}


std::string 
cMailMessageUtil::ToBase64( const byte* p, size_t size )
{
    ASSERT( sizeof( uint8 ) == sizeof( byte ) ); // everything breaks otherwise
    std::string s;    

    const int MAX_WORKING_BYTES = 3;
    const int CHARS_PER_WORKING_BYTES = 4;
    const int MAX_CHARS_PER_BYTE = 2; // should be 
    ASSERT( MAX_CHARS_PER_BYTE > CHARS_PER_WORKING_BYTES/MAX_WORKING_BYTES );
    const int NERVOUS_LINE_BUFFER = 10;
    const byte* at = p;
    byte buf[ MAX_WORKING_BYTES ];
    int nbLeft;
    int nbWorking;
    int nchCurLine;
    std::string::size_type i = 0; // where we are in the output string

    s.resize( size * MAX_CHARS_PER_BYTE );


    for (  
            nchCurLine = 0, nbLeft = size, nbWorking = MS_SUCKS_min( MAX_WORKING_BYTES, nbLeft );
            nbLeft > 0;
            nchCurLine += CHARS_PER_WORKING_BYTES, at += nbWorking, nbLeft -= nbWorking, nbWorking = MS_SUCKS_min( MAX_WORKING_BYTES, nbLeft )
        )
    {
        ASSERT( nbWorking > 0 );

        // fill out the working buffer
        std::copy( at, at + nbWorking, &buf[0] );
        
        // encode from working buffer to 'enc' string
        std::string enc = util_Base64Encode( buf, nbWorking );

        // append 'enc' string to output string
        for( std::string::const_iterator at = enc.begin();
             at != enc.end();
             at++ )
        {
            s[i++] = *at;
        }

        // make sure we aren't over the max line length
        if( nchCurLine > ( _MAX_RFC822_LINE_LEN - NERVOUS_LINE_BUFFER ) ) // NERVOUS_LINE_BUFFER for a little margin of error
        {
            s[i++] = '\r';
            s[i++] = '\n';
            nchCurLine = 0;
        }
    }

    s.resize( i );
    return s;
}
*/
#else //RDIFALCO_BASE64_IMPLEMENTATION


//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// RAD:10311999 -- A Different Try
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

namespace /*Unique*/
{
typedef byte byte_t;

#    ifdef TSS_MAKE_EOL_CRLF
const byte _aszEoL[] = "\r\n";
size_t _EOL_LEN = 2;

#    else
const byte _aszEoL[] = "\n";
size_t     _EOL_LEN  = 1;

#    endif

const byte_t _abBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                           "abcdefghijklmnopqrstuvwxyz"
                           "0123456789+/";

inline size_t tss_base64_amplitude() // TODO: Make a Constant
{
    /* 
        * NOTE:RAD -- Calculate Max Incoming Bytes:
        *
        *   eol_bytes = ( UINT_MAX / _MAX_RFC822_LINE_LEN ) * _EOL_LEN;
        *   max_bytes = UINT_MAX - eol_bytes;
        *   cvt_bytes = max_bytes / 4 * 3;    
        */
    return (((UINT_MAX - (UINT_MAX / _MAX_RFC822_LINE_LEN * _EOL_LEN)) / 4) * 3) + 1;
}

inline size_t tss_encode_base64_size(size_t nchSource)
{
    ASSERT(nchSource < tss_base64_amplitude()); // NOTE:

    /* NOTE: The formula is --
            + bring source bytes to an even divisible of 3 (the padding bytes)
            + number of source bytes to converted (3:4 conversion ratio)
            + ( number of lines ) * ( size of end of line string ) */

    size_t nchPadding = nchSource % 3;
    size_t nchOut = ((nchSource + nchPadding) / 3) * 4;
    size_t nchEol = (nchOut / (_MAX_RFC822_LINE_LEN - _EOL_LEN)) * _EOL_LEN;

    return nchOut + nchEol + 2;
}

void tss_encode_digit_base64(const byte_t*& pchSrc, size_t& nchSrcLeft, byte_t*& pchBuf, size_t& nchBufLeft)
{
    // NOTE:RAD -- Redundant and ugly but very fast!!

    ASSERT(nchBufLeft > 4); // One for NULL

    switch (nchSrcLeft)
    {
    case 0:
    {
        ASSERTMSG(false, "Invalid call to encode_base64_digit!");
        break;
    }
    case 1:
    {
        int ch1 = int(*pchSrc++ & 0xFF);

        *pchBuf++ = _abBase64[(ch1 & 0xFC) >> 2];
        *pchBuf++ = _abBase64[(ch1 & 0x03) << 4];
        *pchBuf++ = '=';
        *pchBuf++ = '=';

        nchSrcLeft = 0; // NOTE:
        break;
    }
    case 2:
    {
        int ch1 = int(*pchSrc++ & 0xFF);
        int ch2 = int(*pchSrc++ & 0xFF);

        *pchBuf++ = _abBase64[((ch1 & 0xFC) >> 2)];
        *pchBuf++ = _abBase64[((ch1 & 0x03) << 4) | ((ch2 & 0xF0) >> 4)];
        *pchBuf++ = _abBase64[((ch2 & 0x0F) << 2)];
        *pchBuf++ = '=';

        nchSrcLeft = 0; // NOTE:
        break;
    }
    default:
    {

        int ch1 = int(*pchSrc++ & 0xFF);
        int ch2 = int(*pchSrc++ & 0xFF);
        int ch3 = int(*pchSrc++ & 0xFF);

        *pchBuf++ = _abBase64[((ch1 & 0xFC) >> 2)];
        *pchBuf++ = _abBase64[((ch1 & 0x03) << 4) | ((ch2 & 0xF0) >> 4)];
        *pchBuf++ = _abBase64[((ch2 & 0x0F) << 2) | ((ch3 & 0xC0) >> 6)];
        *pchBuf++ = _abBase64[(ch3 & 0x3F)];

        nchSrcLeft -= 3; // NOTE: Probably greater than 3
        break;
    }
    }

    nchBufLeft -= 4; // Always the same due to padding!
};


size_t tss_encode_base64(const byte_t* pchSource, size_t nchSource, byte_t* pchBuffer, size_t nchBuffer)
{
    const size_t _ERROR = std::string::npos; // -1;


    //--Assert Preconditions

    if (pchSource == 0 || nchSource == 0)
        return _ERROR;


    //--If pchBuffer == 0, User is requesting Size

    size_t nchBuf = tss_encode_base64_size(nchSource);

    if (pchBuffer == 0) // Requesting Size
        return nchBuf;

    if (nchBuf > nchBuffer) // DOH!!
        return _ERROR;


    //--Get three characters at a time and encode them (watching linelen)

    byte_t* pchBuf = (pchBuffer + 0);
    const byte_t* pchSrc = (pchSource + 0);


    const size_t _max_linelen = (_MAX_RFC822_LINE_LEN - _EOL_LEN);
    size_t nLineLen = 0;

    for (; nchSource;)
    {
        // Like iconv: Increment pointers and decrement sizes
        tss_encode_digit_base64(pchSrc, nchSource, pchBuf, nchBuf);

        nLineLen += 4;

        if (nchSource == 0 || nLineLen > _max_linelen)
        {
            nLineLen = 0; // RESET:

            const byte_t* pchEol = &_aszEoL[0];
            while (*pchEol)
                *pchBuf++ = *pchEol++;
        }
    }

    *pchBuf = 0x00; // CAUTION: Null Terminate!!!

    return (pchBuf - pchBuffer);
}
} // namespace


const std::string::value_type*
cMailMessageUtil::ConvertBase64(std::string& sEncode, const byte_t* pchSrc, size_t nchSrc)
{
    size_t nch = tss_encode_base64(pchSrc, nchSrc, 0, 0); // Like mbstowcs

    sEncode.reserve(nch); // Make it big enough but don't resize
    sEncode.resize(0);    // String must be clean in case of exception!

    const char* pch = sEncode.c_str(); // Get Pointer (won't change)

    size_t nLength = // Action
        tss_encode_base64(pchSrc, nchSrc, (byte_t*)pch, nch);

    if (nLength == std::string::npos)
        throw std::bad_alloc();

    sEncode.resize(nLength); // Now it's okay to resize

    return pch; // So user can do stuff like "out << ConvertBase64()";
}


std::string cMailMessageUtil::ToBase64(const byte_t* pchSrc, size_t nchSrc)
{
    // NOTE: It sucks to use std::string this way! Should be
    //       passed in by reference.

    std::string sucks;
    cMailMessageUtil::ConvertBase64(sucks, pchSrc, nchSrc);
    return sucks;
}
#endif //RDIFALCO_BASE64_IMPLEMENTATION
