/***************************************************************************
 *   Copyright (C) 2004 by EVER Sp. z o.o.                                 *
 *                                                                         *
 *   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.             *
 ***************************************************************************/
#include "cemail.h"

using namespace std;

// Function name	: CEmail::CEmail
// Description		: Class constructor
CEmail::CEmail()
{
	iLastError = 0;
	sEmail = 0;
	
	memset(szRecipientAddress, 0, sizeof(szRecipientAddress));
	memset(szSenderAddress, 0, sizeof(szRecipientAddress));
	memset(szSenderName, 0, sizeof(szSenderName));
	memset(szSMTPServer, 0, sizeof(szSMTPServer));
	memset(szUser, 0, sizeof(szUser));
	memset(szPassword, 0, sizeof(szPassword));
	memset(szErrorString, 0, sizeof(szErrorString));
}

// Function name	: CEmail::~CEmail
// Description		: class destructor
CEmail::~CEmail()
{
	iLastError = 0;
	*szErrorString = '\0';
	if (sEmail != 0)
		close(sEmail);
	sEmail = 0;
}

// Function name	: CEmail::OpenConnection
// Description		: Opens connection to a specified server on a spec. port
int CEmail::OpenConnection()
{
	struct hostent *lphServer;
	struct sockaddr_in sai;
	int iResult;
	char szRecBuffer[256] = "", szSendBuffer[256] = "", *szHostName;
	unsigned long dwSize;

	lphServer=gethostbyname(szSMTPServer);
	if (!lphServer)
	{
		iLastError = errno;
		return -1;
	}
	/* else
		printf ("SMTP Server address: %d.%d.%d.%d\n", 
			(unsigned char)lphServer->h_addr_list[0][0],
			(unsigned char)lphServer->h_addr_list[0][1], 
			(unsigned char)lphServer->h_addr_list[0][2],
			(unsigned char)lphServer->h_addr_list[0][3]);
	*/
	sEmail = socket(AF_INET, SOCK_STREAM, 0);
	if (sEmail == -1) {
		iLastError = errno;
		return -1;
	}
	
	sai.sin_family	= AF_INET;
	sai.sin_port	= htons(25);
	sai.sin_addr	= *((in_addr*)*lphServer->h_addr_list);

	iResult = connect(sEmail, (struct sockaddr *)&sai, sizeof(sockaddr));
	if (iResult == -1) {
		iLastError = errno;
		return -1;
	}

	if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
		return -1;	// iLastError variable contains error value

	// Check if it's valid SMTP server (shall return 220...)
	szRecBuffer[3] = '\0';		// Cut off everything after third letter of string
	if (strcmp(szRecBuffer, "220") != 0) {
		if (SendString("QUIT\r\n") != 0)
			return -1;	// iLastError variable contains error value
		return ERROR_EM_INVALID_SMTPSERVER;
	}

	if (!(szHostName=getenv("HOSTNAME"))) {
		iLastError = errno;
		return -1;
	}
	sprintf(szSendBuffer, "EHLO %s\r\n", szHostName);
	if (SendString(szSendBuffer) != 0)
		return -1;	// iLastError variable contains error value
	if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
		return -1;	// iLastError variable contains error value
	// Check if it we aren't rejected (shall return 250...)
	szRecBuffer[3] = '\0';		// Cut off everything after third letter of string to get number
	if (strcmp(szRecBuffer, "250") != 0) {
		// Let's try 'HELO'
		sprintf(szSendBuffer, "HELO %s\r\n", szHostName);
		if (SendString(szSendBuffer) != 0)
			return -1;	// iLastError variable contains error value
		if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
			return -1;	// iLastError variable contains error value
		// Check if it we aren't rejected (shall return 250...)
		szRecBuffer[3] = '\0';		// Cut off everything after third letter of string to get number
		if ( strcmp( szRecBuffer, "250" ) != 0 ) {
			if ( SendString( "QUIT\r\n" ) != 0 )
				return -1;	// iLastError variable contains error value
			return ERROR_EM_INVALID_SMTPSERVER;
		}
	}

	return 0;
}

// Function name	: CEmail::CloseConnection
// Description		: Closes connection with server
int CEmail::CloseConnection()
{
	char	szRecBuffer[256]	= "";

	if (SendString("QUIT\r\n") != 0)
		return -1;	// iLastError variable contains error value
	if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
		return -1;
	szRecBuffer[3] = '\0';
	if (strcmp(szRecBuffer, "221") != 0)
		return -1;
	return 0;
}

// Function name	: CEmail::SendMessage
// Description		: Send message on configured address
int CEmail::SendMessage(char *lpszTitle, char *lpszMessage, int iMsglen)
{
	char szBuffer[2048] = "", szRecBuffer[2048] = "", szSendBuffer[2048] = "";
	char *lpsResBuff;
	CBase64Utils b64;
	int iLength;

	if (!lpszTitle || !lpszMessage || iMsglen <= 0) {
		iLastError = -1; // Bad params
		return -1;
	}

	if (OpenConnection() == 0) {
		if (strlen(szUser) > 0) {
			// ====== AUTH =======
			sprintf(szSendBuffer, "AUTH %s\r\n", "LOGIN");
			if (SendString(szSendBuffer) != 0)
				return -1;
			if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
				return -1;
			szRecBuffer[3]='\0';
			if (strcmp(szRecBuffer, "334") != 0) {
				if (SendString("QUIT\r\n") != 0)
					return -1;
				return -2;
			}
			// LOGIN
			sprintf(szSendBuffer, "%s", szUser);
			iLength=strlen(szSendBuffer);
			lpsResBuff=b64.Encode(szSendBuffer, iLength);
			sprintf(szSendBuffer, "%s\r\n", lpsResBuff);
			free(lpsResBuff);
			if (SendString(szSendBuffer) != 0) {
				return -1;
			}
			if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
				return -1;
			szRecBuffer[3]='\0';
			if (strcmp(szRecBuffer, "334") != 0) {
				if (SendString("QUIT\r\n") != 0)
					return -1;
				return -2;
			}
			// PASSWORD
			sprintf(szSendBuffer, "%s", szPassword);
			iLength=strlen(szSendBuffer);
			lpsResBuff=b64.Encode(szSendBuffer, iLength);
			sprintf(szSendBuffer, "%s\r\n", lpsResBuff);
			free(lpsResBuff);
			if (SendString(szSendBuffer) != 0) {
				return -1;
			}
			if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
				return -1;
			szRecBuffer[3]='\0';
			if (strcmp(szRecBuffer, "235") != 0) {
				if (SendString("QUIT\r\n") != 0)
					return -1;
				return -2;
			}
		}

		// HEADER
		// ====== FROM =======
		sprintf(szSendBuffer, "MAIL FROM:<%s>\r\n", szSenderAddress);
		if (SendString(szSendBuffer) != 0)
			return -1;
		if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
			return -1;
		szRecBuffer[3] = '\0';
		if (strcmp(szRecBuffer, "250") != 0) {
			if (SendString("QUIT\r\n") != 0)
				return -1;
			return -2;
		}

		// ======= TO ========
		sprintf(szSendBuffer, "RCPT TO:<%s>\r\n", szRecipientAddress);
		if (SendString(szSendBuffer) != 0)
			return -1;
		if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
			return -1;
		szRecBuffer[3] = '\0';
		if (strcmp(szRecBuffer, "250") != 0) {
			if (SendString("QUIT\r\n") != 0)
				return -1;
			return -2;
		}

		// ====== DATA =======
		sprintf(szSendBuffer, "DATA\r\n");
		if (SendString(szSendBuffer) != 0)
			return -1;
		if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
			return -1;
		szRecBuffer[3] = '\0';
		if (strcmp(szRecBuffer, "354") != 0) {
			if (SendString("QUIT\r\n") != 0)
				return -1;
			return -2;
		} else {
			// Header
			if ( !strlen( szSenderName ) )
				sprintf(szSendBuffer, "From: <%s>\r\n", szSenderAddress);
			else
				sprintf(szSendBuffer, "From: %s<%s>\r\n", szSenderName, szSenderAddress);
			if (SendString(szSendBuffer) != 0)
				return -1;
			sprintf(szSendBuffer, "Subject: %s\r\n", lpszTitle);
			if (SendString(szSendBuffer) != 0)
				return -1;
			sprintf(szSendBuffer, "To: %s\r\n", szRecipientAddress);
			if (SendString(szSendBuffer) != 0)
				return -1;
			strcpy( szSendBuffer, 
				"Mime-version: 1.0\r\n"
				"Content-type: text/html; charset=iso-8859-2\r\n"
				"Content-transfer-encoding: 8bit\r\n"
				"Status:  O\r\n" );
			if (SendString(szSendBuffer) != 0)
				return -1;
			// Message
			sprintf(szSendBuffer, "%s", lpszMessage);
			if (SendString(szSendBuffer) != 0)
				return -1;
			// End of Message
			sprintf(szSendBuffer, "\r\n.\r\n");
			if (SendString(szSendBuffer) != 0)
				return -1;
			if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
				return -1;
			szRecBuffer[3] = '\0';
			if (strcmp(szRecBuffer, "250") != 0) {
				if (SendString("QUIT\r\n") != 0)
					return -1;
				return -2;
			}
		}

		// ====== Finish =======
		if (CloseConnection() != 0)
			return -1;
	}
	else
		return -1;
	return 0;
}

// Function name	: CEmail::SendMessageTo
// Description		: Send message on specified address
int CEmail::SendMessageTo(char *lpszRecipientAddress, char *lpszTitle, char *lpszMessage, int iMsgLen)
{
	char szBuffer[256] = "", szRecBuffer[256] = "", szSendBuffer[256] = "";

	if (!lpszRecipientAddress || !lpszTitle || !lpszMessage || iMsgLen <= 0) {
		iLastError = -1; // Bad params
		return -1;
	}

	if (OpenConnection() == 0) {
		// ====== FROM =======
		sprintf(szSendBuffer, "MAIL FROM:<%s>\r\n", szSenderAddress);
		if (SendString(szSendBuffer) != 0)
			return -1;
		if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
			return -1;
		szRecBuffer[3] = '\0';
		if (strcmp(szRecBuffer, "250") != 0) {
			if (SendString("QUIT\r\n") != 0)
				return -1;
			return -2;
		}
		// ======= TO ========
		sprintf(szSendBuffer, "RCPT TO:<%s>\r\n", lpszRecipientAddress);
		if (SendString(szSendBuffer) != 0)
			return -1;
		if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
			return -1;
		szRecBuffer[3] = '\0';
		if (strcmp(szRecBuffer, "250") != 0) {
			if (SendString("QUIT\r\n") != 0)
				return -1;
			return -2;
		}
		// ====== DATA =======
		sprintf(szSendBuffer, "DATA\r\n");
		if (SendString(szSendBuffer) != 0)
			return -1;
		if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
			return -1;
		szRecBuffer[3] = '\0';
		if (strcmp(szRecBuffer, "354") != 0) {
			if (SendString("QUIT\r\n") != 0)
				return -1;
			return -2;
		} else {
			// Header
			if ( !strlen( szSenderName ) )
				sprintf(szSendBuffer, "From: <%s>\r\n", szSenderAddress);
			else
				sprintf(szSendBuffer, "From: %s<%s>\r\n", szSenderName, szSenderAddress);
			if (SendString(szSendBuffer) != 0)
				return -1;
			sprintf(szSendBuffer, "Subject: %s\r\n", lpszTitle);
			if (SendString(szSendBuffer) != 0)
				return -1;
			sprintf(szSendBuffer, "To: %s\r\n", lpszRecipientAddress);
			if (SendString(szSendBuffer) != 0)
				return -1;
			strcpy( szSendBuffer, 
				"Mime-version: 1.0\r\n"
				"Content-type: text/html; charset=iso-8859-2\r\n"
				"Content-transfer-encoding: 8bit\r\n"
				"Status:  O\r\n" );
			if (SendString(szSendBuffer) != 0)
				return -1;
			// Message
			sprintf(szSendBuffer, "%s", lpszMessage);
			if (SendString(szSendBuffer) != 0)
				return -1;
			// End of Message
			sprintf(szSendBuffer, "\r\n.\r\n");
			if (SendString(szSendBuffer) != 0)
				return -1;
			if (ReceiveString(szRecBuffer, sizeof(szRecBuffer), 10) != 0)
				return -1;
			szRecBuffer[3] = '\0';
			if (strcmp(szRecBuffer, "250") != 0) {
				if (SendString("QUIT\r\n") != 0)
					return -1;
				return -2;
			}
		}
		// ====== Finish =======
		if (CloseConnection() != 0)
			return -1;
	} else
		return -1;
	return 0;
}

int CEmail::ReceiveString(char *lpszBuffer, int iSizeBuff, int iTimeout)
{
	struct timeval tv;
	fd_set readset;
	u_long ulfArg = 0;
	int iResult;

	if (!lpszBuffer || iSizeBuff <= 0) {
		iLastError = -1;	// Bad params
		return -1;
	}

	tv.tv_sec = iTimeout;
	tv.tv_usec = 0;
	
	FD_ZERO(&readset);
	FD_SET(sEmail, &readset);

	iResult = select(sEmail+1, &readset, NULL, NULL, &tv);
	if (iResult == -1) {	// error
		iLastError = ERROR_EM_SERVER_DISCONNECTED;
		return -1;
	}
	if (iResult == 0) {					// Timeout
		iLastError = ERROR_EM_SERVER_TIMEOUT;
		return -1;
	}
	iResult = ioctl(sEmail, FIONREAD, &ulfArg);
	if (iResult == -1) {
		iLastError = errno;
		return -1;
	}

	if (ulfArg > (unsigned int)iSizeBuff)
		ulfArg = (unsigned int)iSizeBuff;

	iResult = recv(sEmail, lpszBuffer, ulfArg, 0);
	if (iResult == -1) {
		iLastError = errno;
		return -1;
	} else 
		if (ulfArg > sizeof(szErrorString))
			strncpy(szErrorString, lpszBuffer, sizeof(szErrorString));
		else
			strcpy(szErrorString, lpszBuffer);
	return 0;
}

int CEmail::ReceiveData(LPBYTE lpbBuffer, int iSizeBuff, int iTimeout)
{
	return 0;
}

int CEmail::SendString(char *lpszString)
{
	int iResult;

	if (!lpszString || !sEmail)
		return -1;

	iResult = send(sEmail, lpszString, (int)strlen(lpszString), 0);
	if (iResult == -1) {
		iLastError = errno;
		return -1;
	}
	return 0;
}

// Function name	: CEmail::Init
// Description		: Initialize all parameters
int CEmail::Init(char *lpszSMTPServer, char *lpszSender, char *lpszUser, char *lpszPassword)
{
	if (!lpszSMTPServer || !lpszSender || !lpszUser || !lpszPassword) {
		iLastError = -1;	// Bad params
		return -1;
	}

	strcpy(szSMTPServer, lpszSMTPServer);
	strcpy(szSenderAddress, lpszSender);
	strcpy(szUser, lpszUser);
	strcpy(szPassword, lpszPassword);

	iLastError = 0;

	return 0;
}

int CEmail::SetAuthorization(char *lpszUser, char *lpszPassword)
{
	strcpy(szUser, lpszUser);
	strcpy(szPassword, lpszPassword);
	return 0;
}

int CEmail::SetRecipient(char *lpszRecipientAddress)
{
	strcpy(szRecipientAddress, lpszRecipientAddress);
	return 0;
}

int CEmail::SetSMTPServer(char *lpszSMTPServer)
{
	strcpy(szSMTPServer, lpszSMTPServer);
	return 0;
}

int CEmail::SetSender(char *lpszSenderAddress, char *lpszSenderName)
{
	if ( lpszSenderAddress != NULL )
		strcpy( szSenderAddress, lpszSenderAddress );

	if ( lpszSenderName != NULL )
		strcpy( szSenderName, lpszSenderName );

	return 0;
}

int CEmail::GetLastErrorValue()
{
	return iLastError;
}

int CEmail::GetLastErrorString(char *lpszBuffer, int iSize)
{
	if (!lpszBuffer || iSize <= 0) {
		iLastError = -1;
		return -1;
	}
	if (iSize < sizeof(szErrorString))
		strncpy(lpszBuffer, szErrorString, iSize);
	else
		strcpy(lpszBuffer, szErrorString);

	return 0;
}

