/*
UserinfoEx plugin for Miranda IM

Copyright:
� 2006-2010 DeathAxe, Yasnovidyashii, Merlin, K. Romanov, Kreol

This program is free software; you can redistribute it and/or
modify it under 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.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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.
*/

//Not including CRLFs
//NOTE: For BASE64 and UUENCODE, this actually
//represents the amount of unencoded characters
//per line
#define TSSMTPMAX_QP_LINE_LENGTH			 76
#define TSSMTPMAX_BASE64_LINE_LENGTH	 57
#define TSSMTPMAX_UUENCODE_LINE_LENGTH 45

//=======================================================================
// Quoted Printable encode/decode
// compliant with RFC 2045
//=======================================================================
//
#define TSSMTPQPENCODE_DOT 1
#define TSSMTPQPENCODE_TRAILING_SOFT 2

inline INT_PTR QPEncodeGetRequiredLength(INT_PTR nSrcLen)
{
	INT_PTR nRet = 3*((3*nSrcLen)/(TSSMTPMAX_QP_LINE_LENGTH-8));
	nRet += 3*nSrcLen;
	nRet += 3;
	return nRet;
}

inline INT_PTR QPDecodeGetRequiredLength(INT_PTR nSrcLen)
{
	return nSrcLen;
}

inline BOOL QPEncode(BYTE* pbSrcData, INT_PTR nSrcLen, LPSTR szDest, INT_PTR* pnDestLen, BYTE *bEncoded, DWORD dwFlags = 0)
{
	//The hexadecimal character set
	static const CHAR s_chHexChars[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
											'A', 'B', 'C', 'D', 'E', 'F'};
	INT_PTR nRead = 0, nWritten = 0, nLineLen = 0;
	CHAR ch;
	BYTE bChanged = FALSE;


	if (!pbSrcData || !szDest || !pnDestLen) {
		return FALSE;
	}

	while (nRead < nSrcLen) {
		ch = *pbSrcData++;
		nRead++;
		if (nLineLen == 0 && ch == '.' && (dwFlags & TSSMTPQPENCODE_DOT)) {
			*szDest++ = '.';
			nWritten++;
			nLineLen++;
			bChanged = TRUE;
		}
		if ((ch > 32 && ch < 61) || (ch > 61 && ch < 127)) {
			*szDest++ = ch;
			nWritten++;
			nLineLen++;
		}
		else
		if ((ch == ' ' || ch == '\t') && (nLineLen < (TSSMTPMAX_QP_LINE_LENGTH - 12))) {
			*szDest++ = ch;
			nWritten++;
			nLineLen++;
		}
		else {
			*szDest++ = '=';
			*szDest++ = s_chHexChars[(ch >> 4) & 0x0F];
			*szDest++ = s_chHexChars[ch & 0x0F];
			nWritten += 3;
			nLineLen += 3;
			bChanged = TRUE;
		}
		if (nLineLen >= (TSSMTPMAX_QP_LINE_LENGTH - 11)) {
			*szDest++ = '=';
			*szDest++ = '\r';
			*szDest++ = '\n';
			nLineLen = 0;
			nWritten += 3;
			bChanged = TRUE;
		}
	}
	if (dwFlags & TSSMTPQPENCODE_TRAILING_SOFT) {
		*szDest++ = '=';
		*szDest++ = '\r';
		*szDest++ = '\n';
		nWritten += 3;
		bChanged = TRUE;
	}
	*pnDestLen = nWritten;
	if (bEncoded) *bEncoded = bChanged;
	return TRUE;
}


inline BOOL QPDecode(BYTE* pbSrcData, INT_PTR nSrcLen, LPSTR szDest, INT_PTR* pnDestLen, DWORD dwFlags = 0)
{
	if (!pbSrcData || !szDest || !pnDestLen)
	{
		return FALSE;
	}

	INT_PTR nRead = 0, nWritten = 0, nLineLen = -1;
	char ch;
	while (nRead <= nSrcLen)
	{
		ch = *pbSrcData++;
		nRead++;
		nLineLen++;
		if (ch == '=')
		{
			//if the next character is a digit or a character, convert
			if (nRead < nSrcLen && (isdigit(*pbSrcData) || isalpha(*pbSrcData)))
			{
				char szBuf[5];
				szBuf[0] = *pbSrcData++;
				szBuf[1] = *pbSrcData++;
				szBuf[2] = '\0';
				char* tmp = '\0';
				*szDest++ = (BYTE)strtoul(szBuf, &tmp, 16);
				nWritten++;
				nRead += 2;
				continue;
			}
			//if the next character is a carriage return or line break, eat it
			if (nRead < nSrcLen && *pbSrcData == '\r' && (nRead+1 < nSrcLen) && *(pbSrcData+1)=='\n')
			{
				pbSrcData++;
				nRead++;
				nLineLen = -1;
				continue;
			}
			return FALSE;
		}
		if (ch == '\r' || ch == '\n')
		{
			nLineLen = -1;
			continue;
		}
		if ((dwFlags & TSSMTPQPENCODE_DOT) && ch == '.' && nLineLen == 0)
		{
			continue;
		}
		*szDest++ = ch;
		nWritten++;
	}

	*pnDestLen = nWritten-1;
	return TRUE;
}