/*
Copyright 2001-2018 John Wiseman G8BPQ

This file is part of LinBPQ/BPQ32.

LinBPQ/BPQ32 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 3 of the License, or
(at your option) any later version.

LinBPQ/BPQ32 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 LinBPQ/BPQ32.  If not, see http://www.gnu.org/licenses
*/	

// Mail and Chat Server for BPQ32 Packet Switch
//
//	TCP access module - POP and SMTP

#include "bpqmail.h"

VOID ReleaseSock(SOCKET sock);
void Base64EncodeAndSend(SocketConn * sockptr, UCHAR * Msg, int Len);

#define MaxSockets 64

SocketConn * Sockets = NULL;

int CurrentConnections;

int CurrentSockets=0;

#define MAX_PENDING_CONNECTS 4

#define VERSION_MAJOR         2
#define VERSION_MINOR         0

SOCKADDR_IN local_sin;  /* Local socket - internet style */

PSOCKADDR_IN psin;

SOCKET smtpsock, pop3sock;

char szBuff[80];

int SMTPInPort;
int POP3InPort;

BOOL RemoteEmail;			// Set to listen on INADDR_ANY rather than LOCALHOST

BOOL ISP_Gateway_Enabled;

char MyDomain[50];			// Mail domain for BBS<>Internet Mapping

char ISPSMTPName[50];
char ISPEHLOName[50] = "";

int ISPSMTPPort;

char ISPPOP3Name[50];
int ISPPOP3Port;

char ISPAccountName[50];
char ISPAccountPass[50];
char EncryptedISPAccountPass[100];
int EncryptedPassLen;

BOOL SMTPAuthNeeded;

BOOL GMailMode = FALSE;
char GMailName[50];

int POP3Timer=9999;							// Run on startup
int ISPPOP3Interval;

BOOL SMTPMsgCreated=FALSE;					// Set to cause SMTP client to send messages to ISP
BOOL SMTPActive=FALSE;						// SO we don't try every 10 secs!

char mycd64[256];
static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const char cd64[]="|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq";

char sockTypes[6][12] = {"Undefined", "SMTPServer", "POP3Server", "SMTPClient", "POP3Client", "NNTPServer"};

void decodeblock( unsigned char in[4], unsigned char out[3] );
VOID FormatTime(char * Time, time_t cTime);
static int Socket_Accept(SOCKET SocketId);

int SendSock(SocketConn * sockptr, char * msg)
{
	int len = (int)strlen(msg), sent;
	char * newmsg = malloc(len+10);

 	WriteLogLine(NULL, '>',msg,  len, LOG_TCP);

	strcpy(newmsg, msg);

	strcat(newmsg, "\r\n");

	len+=2;

	if (sockptr->SendBuffer)
	{
		// Already queued, so add to end

		if ((sockptr->SendSize + len) > sockptr->SendBufferSize)
		{
			sockptr->SendBufferSize += (10000 + len);
			sockptr->SendBuffer = realloc(sockptr->SendBuffer, sockptr->SendBufferSize);
		}

		memcpy(&sockptr->SendBuffer[sockptr->SendSize], newmsg, len);
		sockptr->SendSize += len;
		free (newmsg);
		return len;
	}
	
	sent = send(sockptr->socket, newmsg, len, 0);
		
	if (sent < len)
	{
		int error, remains;

		// Not all could be sent - queue rest

		if (sent == SOCKET_ERROR)
		{
			error = WSAGetLastError();
			if (error == WSAEWOULDBLOCK)
				sent=0;

			//	What else??
		}

		remains = len - sent;

		sockptr->SendBufferSize += (10000 + remains);
		sockptr->SendBuffer = malloc(sockptr->SendBufferSize);

		memcpy(sockptr->SendBuffer, &newmsg[sent], remains);

		sockptr->SendSize = remains;
		sockptr->SendPtr = 0;

	}

	free (newmsg);

	return sent;
}

VOID __cdecl sockprintf(SocketConn * sockptr, const char * format, ...)
{
	// printf to a socket

	char buff[1000];
	va_list(arglist);
	
	va_start(arglist, format);
	vsprintf(buff, format, arglist);

	SendSock(sockptr, buff);
}

extern int SMTPMsgs;

fd_set ListenSet;
SOCKET ListenMax = 0;

extern SOCKET nntpsock;

int NNTP_Read(SocketConn * sockptr, SOCKET sock);

VOID SetupListenSet()
{
	// Set up master set of fd's for checking for incoming calls

	fd_set * readfd = &ListenSet;
	SOCKET sock;

	FD_ZERO(readfd);

	sock = nntpsock;	
	if (sock)
	{
		FD_SET(sock, readfd);
		if (sock > ListenMax)
			ListenMax = sock;
	}

	sock = smtpsock;
	if (sock)
	{
		FD_SET(sock, readfd);
		if (sock > ListenMax)
			ListenMax = sock;
	}
		
	sock = pop3sock;

	if (sock)
	{
		FD_SET(sock, readfd);
		if (sock > ListenMax)
			ListenMax = sock;
	}
}

VOID Socket_Connected(SocketConn * sockptr, int error)
{
	SOCKET sock = sockptr->socket;

	if (error)
	{
		Logprintf(LOG_TCP, NULL, '|', "Connect Failed");

		if (sockptr->Type == SMTPClient)
			SMTPActive = FALSE;

		ReleaseSock(sock);

		return;
	}
	
	sockptr->State = WaitingForGreeting;
	
	if (sockptr->Type == NNTPServer)
		SendSock(sockptr, "200 BPQMail NNTP Server ready");	

	else if (sockptr->Type == SMTPServer)
		SendSock(sockptr, "220 BPQMail SMTP Server ready");
	
	else if (sockptr->Type == POP3SLAVE)
	{
		SendSock(sockptr, "+OK POP3 server ready");
		sockptr->State = GettingUser;
	}	
}

VOID TCPFastTimer()
{
	//	we now poll for incoming connections and data

	fd_set readfd, writefd, exceptfd;
	struct timeval timeout;
	int retval;
	SocketConn * sockptr = Sockets;
	SOCKET sock;
	int Active = 0;
	SOCKET maxsock;

	timeout.tv_sec = 0;
	timeout.tv_usec = 0;				// poll

	if (ListenMax)
	{	
		memcpy(&readfd, &ListenSet, sizeof(fd_set));

		retval = select((int)ListenMax + 1, &readfd, NULL, NULL, &timeout);

		if (retval == -1)
		{
			retval = 0;
			perror("Listen select");
		}

		if (retval)
		{
			sock = pop3sock;
			if (sock)
				if (FD_ISSET(sock, &readfd))
					Socket_Accept(sock);

			sock = smtpsock;
			if (sock)
				if (FD_ISSET(sock, &readfd))
					Socket_Accept(sock);
		
			sock = nntpsock;
			if (sock)
				if (FD_ISSET(sock, &readfd))
					NNTP_Accept(sock);
		}
	}

	// look for data on any active sockets

	maxsock = 0;

	FD_ZERO(&readfd);
	FD_ZERO(&writefd);
	FD_ZERO(&exceptfd);
	sockptr=Sockets;
		
	while (sockptr)
	{		
		sockptr->Timeout++;

		if (sockptr->Timeout > 1200)		// 2 mins
		{
			Logprintf(LOG_TCP, NULL, '|', "Timeout on Socket = %d", sockptr->socket);
			shutdown(sockptr->socket, 0);
			closesocket(sockptr->socket);
			ReleaseSock(sockptr->socket);

			return;					// We've messed with chain
		}
		
		if (sockptr->State & Connecting)
		{
			// look for complete or failed

			FD_SET(sockptr->socket, &writefd);
			FD_SET(sockptr->socket, &exceptfd);
		}
		else
			FD_SET(sockptr->socket, &readfd);

		Active++;

		if (sockptr->socket > maxsock)
			maxsock = sockptr->socket;
		
		sockptr = sockptr->Next;
	}

	if (Active == 0)
		return;

	retval = select((int)maxsock + 1, &readfd, &writefd, &exceptfd, &timeout);

	if (retval == -1)
	{
		perror("select");
	
		// we need to do something or the error will recur. 
		// As there are unlikely to be a lot of open tcp connections perhaps
		// simplest is to close all

		sockptr = Sockets;
		
		while (sockptr)
		{		
			Debugprintf("MAILTCP Select Failed Active %s Socket", sockTypes[sockptr->Type]);
			shutdown(sockptr->socket, 0);
			closesocket(sockptr->socket);
			ReleaseSock(sockptr->socket);

			sockptr = Sockets;		// We've messed with chain
		}
	}
	else
	{
		if (retval)
		{
			sockptr = Sockets;

			// see who has data

			while (sockptr)
			{		
				sock = sockptr->socket;
			
				if (FD_ISSET(sock, &readfd))
				{
					sockptr->Timeout = 0;

					if (sockptr->Type == NNTPServer)
					{
						if (NNTP_Read(sockptr, sock) == 0)
							break;						// We've messed with the chain
					}
					else
					{
						if (DataSocket_Read(sockptr, sock) == 0)
							break;						// We've messed with the chain
					}
				}
				if (FD_ISSET(sockptr->socket, &writefd))
					Socket_Connected(sockptr, 0);

				if (FD_ISSET(sockptr->socket, &exceptfd))
				{
					Socket_Connected(sockptr, 1);
					return;
				}
				sockptr = sockptr->Next;
			}			
		}
	}
}

VOID TCPTimer()
{
	POP3Timer+=10;

//	Debugprintf("POP3 Debug Timer = %d Interval = %d Port %d Enabled %d",
//		POP3Timer, ISPPOP3Interval, ISPPOP3Port, ISP_Gateway_Enabled);

	if (POP3Timer > ISPPOP3Interval)			// 5 mins
	{
		POP3Timer=0;

		if ((ISPSMTPPort && ISP_Gateway_Enabled))
			SendtoISP();
		
		if (ISPPOP3Port  && ISP_Gateway_Enabled)
		{
//			Debugprintf("Calling POP3 Connect");
			POP3Connect(ISPPOP3Name, ISPPOP3Port);
		}

		if (SMTPMsgs && ISPSMTPPort && ISP_Gateway_Enabled)
			SendtoISP();
	}
	else
	{
		if (SMTPMsgCreated && ISPSMTPPort && ISP_Gateway_Enabled)
			SendtoISP();
	}
}
BOOL InitialiseTCP()
{
	int			  Error;              // catches return value of WSAStartup
#ifdef	WIN32
	WORD          VersionRequested;   // passed to WSAStartup
    WSADATA       WsaData;            // receives data from WSAStartup
#endif
	int i,j;


	for (i=0;i<64; i++)
	{
		j=cb64[i];
		mycd64[j]=i;
	}

#ifdef WIN32
	
	VersionRequested = MAKEWORD(VERSION_MAJOR, VERSION_MINOR);

	Error = WSAStartup(VersionRequested, &WsaData);
    
	if (Error)
	{
#ifndef LINBPQ
		MessageBox(NULL,
            "Could not find high enough version of WinSock",
            "BPQMailChat", MB_OK | MB_ICONSTOP | MB_SETFOREGROUND);
#else
		printf("Could not find high enough version of WinSock\n");
#endif
		return FALSE;
	}

#endif

//	Create listening sockets


	if (SMTPInPort)
		smtpsock = CreateListeningSocket(SMTPInPort);

	if (POP3InPort)
		pop3sock = CreateListeningSocket(POP3InPort);

	if (ISP_Gateway_Enabled)
	{
		// See if using GMail

		char * ptr = strchr(ISPAccountName, '@');

		if (ptr)
		{
			if (_stricmp(&ptr[1], "gmail.com") == 0 || _stricmp(&ptr[1], "googlemail.com") == 0)
			{
				strcpy(GMailName, ISPAccountName);
				strlop(GMailName, '@');
				GMailMode = TRUE;
				SMTPAuthNeeded = TRUE;
			}
		}
	}

	return TRUE;

}


SOCKET CreateListeningSocket(int Port)
{
	SOCKET sock;
	unsigned int param = 1;
	
	sock = socket( AF_INET, SOCK_STREAM, 0);

    if (sock == INVALID_SOCKET)
	{
        sprintf(szBuff, "socket() failed error %d", WSAGetLastError());
#ifdef LINBPQ
		perror(szBuff);
#else
		MessageBox(MainWnd, szBuff, "BPQMailChat", MB_OK);
#endif
		return FALSE;
	}

	setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, (char *)&param,4);

	psin=&local_sin;

	psin->sin_family = AF_INET;
	psin->sin_addr.s_addr = htonl(RemoteEmail ? INADDR_ANY  : INADDR_LOOPBACK);	// Local Host Olny
	
	psin->sin_port = htons(Port);        /* Convert to network ordering */

    if (bind( sock, (struct sockaddr FAR *) &local_sin, sizeof(local_sin)) == SOCKET_ERROR)
	{
         sprintf(szBuff, "bind(%d) failed Error %d", Port, WSAGetLastError());
#ifdef LINBPQ
		perror(szBuff);
#else
		MessageBox(MainWnd, szBuff, "BPQMailChat", MB_OK);
#endif
         closesocket( sock );
		 return FALSE;
	}

    if (listen( sock, MAX_PENDING_CONNECTS ) < 0)
	{
		sprintf(szBuff, "listen(%d) failed Error %d", Port, WSAGetLastError());
#ifdef LINBPQ
		perror(szBuff);
#else
		MessageBox(MainWnd, szBuff, "BPQMailChat", MB_OK);
#endif
		closesocket( sock );
		return FALSE;
	}

	ioctl(sock, FIONBIO, &param);
	return sock;
}

static int Socket_Accept(SOCKET SocketId)
{
	int addrlen;
	SocketConn * sockptr;
	SOCKET sock;
	unsigned int param = 1;

	addrlen=sizeof(struct sockaddr);

	//   Allocate a Socket entry

	sockptr = malloc(sizeof(SocketConn));
	memset(sockptr, 0, sizeof (SocketConn));

	sockptr->Next = Sockets;
	Sockets = sockptr;

	sock = accept(SocketId, (struct sockaddr *)&sockptr->sin, &addrlen);

	if (sock == INVALID_SOCKET)
	{
		Logprintf(LOG_TCP, NULL, '|', " accept() failed Error %d", WSAGetLastError());

		// get rid of socket record

		Sockets = sockptr->Next;
		free(sockptr);
		return FALSE;
	}

	ioctl(sock, FIONBIO, &param);


	sockptr->socket = sock;

	if (SocketId == pop3sock)
	{
		sockptr->Type = POP3SLAVE;
		SendSock(sockptr, "+OK POP3 server ready");
		sockptr->State = GettingUser;
		Logprintf(LOG_TCP, NULL, '|', "Incoming POP3 Connect Socket = %d", sock);
	}	
	else
	{
		sockptr->Type = SMTPServer;
		sockptr->State = WaitingForGreeting;
		SendSock(sockptr, "220 BPQMail SMTP Server ready");
		Logprintf(LOG_TCP, NULL, '|', "Incoming SMTP Connect Socket = %d", sock);
	}	

	return 0;
}


VOID ReleaseSock(SOCKET sock)
{
	// remove and free the socket record

	SocketConn * sockptr, * lastptr;

	sockptr=Sockets;
	lastptr=NULL;
		
	while (sockptr)
	{
		if (sockptr->socket == sock)
		{
			if (lastptr)
				lastptr->Next=sockptr->Next;
			else
				Sockets=sockptr->Next;

			if (sockptr->POP3User)
				sockptr->POP3User->POP3Locked = FALSE;

			if (sockptr->State == WaitingForGreeting)
			{
				Logprintf(LOG_TCP, NULL, '|', "Premature Close on Socket %d", sock);
	
				if (sockptr->Type == SMTPClient)
					SMTPActive = FALSE;	
			}
			else
				Logprintf(LOG_TCP, NULL, '|', "Socket %d Closed", sock);


			free(sockptr);
			return;
		}
		else
		{
			lastptr=sockptr;
			sockptr=sockptr->Next;
		}
	}
}

/*
int Socket_Data(int sock, int error, int eventcode)
{
	SocketConn * sockptr;

	//	Find Connection Record

	sockptr=Sockets;
		
	while (sockptr)
	{
		if (sockptr->socket == sock)
		{
			switch (eventcode)
			{
				case FD_READ:

					return DataSocket_Read(sockptr,sock);

				case FD_WRITE:

					// Either Just connected, or flow contorl cleared

					if (sockptr->SendBuffer)
						// Data Queued
						SendFromQueue(sockptr);
					else
					{
						if (sockptr->Type == SMTPServer)
							SendSock(sockptr, "220 BPQMail SMTP Server ready");
						else
						{
							if (sockptr->Type == POP3SLAVE)
							{
								SendSock(sockptr, "+OK POP3 server ready");
								sockptr->State = GettingUser;
							}
						}
					}
					return 0;

				case FD_OOB:

					return 0;

				case FD_ACCEPT:

					return 0;

				case FD_CONNECT:

					return 0;

				case FD_CLOSE:

					closesocket(sock);
					ReleaseSock(sock);
					return 0;
				}
			return 0;
		}
		else
			sockptr=sockptr->Next;
	}

	return 0;
}
*/
int DataSocket_Read(SocketConn * sockptr, SOCKET sock)
{
	int InputLen, MsgLen;
	char * ptr, * ptr2;
	char Buffer[2000];

	// May have several messages per packet, or message split over packets

	if (sockptr->InputLen > 1000)	// Shouldnt have lines longer  than this in text mode
	{
		sockptr->InputLen=0;
	}
				
	InputLen=recv(sock, &sockptr->TCPBuffer[sockptr->InputLen], 1000, 0);

	if (InputLen <= 0)
	{
		int x = WSAGetLastError();

		closesocket(sock);
		ReleaseSock(sock);

		return 0;					// Does this mean closed?
	}

	sockptr->InputLen += InputLen;

loop:
	
	ptr = memchr(sockptr->TCPBuffer, '\n', sockptr->InputLen);

	if (ptr)	//  CR in buffer
	{
		ptr2 = &sockptr->TCPBuffer[sockptr->InputLen];
		ptr++;				// Assume LF Follows CR

		if (ptr == ptr2)
		{
			// Usual Case - single meg in buffer
	
			if (sockptr->Type == SMTPServer)
				ProcessSMTPServerMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen);
			else
			if (sockptr->Type == POP3SLAVE)
				ProcessPOP3ServerMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen);
			else
			if (sockptr->Type == SMTPClient)
				ProcessSMTPClientMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen);
			else
			if (sockptr->Type == POP3Client)
				ProcessPOP3ClientMessage(sockptr, sockptr->TCPBuffer, sockptr->InputLen);

			sockptr->InputLen=0;
		
		}
		else
		{
			// buffer contains more that 1 message

			MsgLen = sockptr->InputLen - (int)(ptr2-ptr);

			memcpy(Buffer, sockptr->TCPBuffer, MsgLen);


			if (sockptr->Type == SMTPServer)
				ProcessSMTPServerMessage(sockptr, Buffer, MsgLen);
			else
			if (sockptr->Type == POP3SLAVE)
				ProcessPOP3ServerMessage(sockptr, Buffer, MsgLen);
			else
			if (sockptr->Type == SMTPClient)
				ProcessSMTPClientMessage(sockptr, Buffer, MsgLen);
			else
			if (sockptr->Type == POP3Client)
				ProcessPOP3ClientMessage(sockptr, Buffer, MsgLen);


			memmove(sockptr->TCPBuffer, ptr, sockptr->InputLen-MsgLen);

			sockptr->InputLen -= MsgLen;

			goto loop;

		}
	}
	return TRUE;
}

char * FindPart(char ** Msg, char * Boundary, int * PartLen)
{
	char * ptr = *Msg, * ptr2;
	char * Msgptr = *Msg;
	int BLen = (int)strlen(Boundary);
	char * Part;

	while(*ptr)				// Just in case we run off end
	{
		ptr2 = strchr(ptr, 10);	// Find LF

		if (ptr2 == NULL) return NULL;

		if (*ptr == '-' && *(ptr+1) == '-')
		{
			if (memcmp(&ptr[2], Boundary, BLen) == 0)
			{
				// Found Boundary

				int Partlen = (int)(ptr - Msgptr);
				Part = malloc(Partlen + 1);
				memcpy(Part, Msgptr, Partlen);
				Part[Partlen] = 0;

				*Msg = ++ptr2;
		
				*PartLen = Partlen;

				return Part; 
			}
		}

		ptr = ++ptr2;
	}
	return NULL;
}





BOOL CheckforMIME(SocketConn * sockptr, char * Msg, char ** Body, int * MsgLen)	// Will reformat message if necessary. 
{
	int i;
	char * ptr, * ptr2, * ptr3, * ptr4;
	char Boundary[1000];
	BOOL Multipart = FALSE;
	BOOL ALT = FALSE;
	int Partlen;
	char * Save;
	BOOL Base64 = FALSE;
	BOOL QuotedP = FALSE;
	
	char FileName[100][250] = {""};
	int FileLen[100];
	char * FileBody[100];
	char * MallocSave[100];
	UCHAR * NewMsg;

	int Files = 0;

	ptr = Msg;

	while(*ptr != 13)
	{
		ptr2 = strchr(ptr, 10);	// Find CR

		while(ptr2[1] == ' ' || ptr2[1] == 9)		// Whitespace - continuation line
		{
			ptr2 = strchr(&ptr2[1], 10);	// Find CR
		}

//		Content-Type: multipart/mixed;
//	boundary="----=_NextPart_000_025B_01CAA004.84449180"
//		7.2.2 The Multipart/mixed (primary) subtype
//		7.2.3 The Multipart/alternative subtype


		if (_memicmp(ptr, "Content-Type: ", 14) == 0)
		{
			char Line[1000] = "";
			char lcLine[1000] = "";

			char * ptr3;

			memcpy(Line, &ptr[14], ptr2-ptr-14);
			memcpy(lcLine, &ptr[14], ptr2-ptr-14);
			_strlwr(lcLine);

			if (_memicmp(Line, "Multipart/", 10) == 0)
			{
				Multipart = TRUE;

				if (_memicmp(&Line[10], "alternative", 11) == 0)
				{
					ALT = TRUE;
				}

				ptr3 = strstr(Line, "boundary");

				if (ptr3)
				{
					ptr3+=9;

					if ((*ptr3) == '"')
						ptr3++;

					strcpy(Boundary, ptr3);
					ptr3 = strchr(Boundary, '"');
					if (ptr3) *ptr3 = 0;
					ptr3 = strchr(Boundary, 13);			// CR
					if (ptr3) *ptr3 = 0;

				}
				else
					return FALSE;						// Can't do anything without a boundary ??
			}

		}

		else if (_memicmp(ptr, "Content-Transfer-Encoding:", 26) == 0)
		{
			if (strstr(&ptr[26], "base64"))
				Base64 = TRUE;
			else
			if (strstr(&ptr[26], "quoted-printable"))
				QuotedP = TRUE;
		}


		ptr = ptr2;
		ptr++;

	}

	if (Multipart == FALSE)
	{
		// We only have one part, but it could have an odd encoding

		if (Base64)
		{
			int i = 0, Len = *MsgLen, NewLen;
			char * ptr2;
			char * End;

			ptr = ptr2 = *Body;
			End = ptr + Len;

			while (ptr < End)
			{
				while (*ptr < 33)
					{ptr++;}

				*ptr2++ = *ptr++;
			}

			*ptr2 = 0;

			ptr = *Body;
			Len = (int)(ptr2 - ptr - 1);

			ptr2 = ptr;

			while (Len > 0)
			{
				decodeblock(ptr, ptr2);
				ptr += 4;
				ptr2 += 3;
				Len -= 4;
			}

			NewLen = (int)(ptr2 - *Body);

			if (*(ptr-1) == '=')
				NewLen--;

			if (*(ptr-2) == '=')
				NewLen--;

			*MsgLen = NewLen;
		}
		else if (QuotedP)
		{
			int i = 0, Len = *MsgLen;
			char * ptr2;
			char * End;

			ptr = ptr2 =*Body;

			End = ptr + Len;

			while (ptr < End)
			{
				if ((*ptr) == '=')
				{
					char c = *(++ptr);
					char d;

					c = c - 48;
					if (c < 0)
					{
						// = CRLF as a soft break

						ptr += 2;
						continue;
					}

					if (c > 9)
						c -= 7;
					d  = *(++ptr);
					d = d - 48;
					if (d > 9)
						d -= 7;

					*(ptr2) = c << 4 | d;
					ptr2++;	
					ptr++;
				}
				else
				{
					*ptr2++ = *ptr++;
				}
			}
			*ptr2 = 0;

			*MsgLen = (int)(ptr2 - *Body);

		}

		return FALSE;
	}
	// FindPart Returns Next Part of Message, Updates Input Pointer
	// Skip to first Boundary (over the non MIME Alt Part)

	ptr = FindPart(Body, Boundary, &Partlen);

	if (ptr == NULL)
		return FALSE;			// Couldn't find separator

	free(ptr);
	
	if (ALT)
	{
		// Assume HTML and Plain Text Versions of the same single body.

		ptr = FindPart(Body, Boundary, &Partlen);	

		Save = ptr;		// For free();

		// Should be the First (Least desireable part, but the bit we want, as we are only interested in plain text)

		// Skip any headers
	
		while(*ptr != 13)
		{
			if (_memicmp(ptr, "Content-Transfer-Encoding:", 26) == 0)
			{
				if (strstr(&ptr[26], "base64"))
					Base64 = TRUE;
				else
				if (strstr(&ptr[26], "quoted-printable"))
					QuotedP = TRUE;
			}

			ptr2 = strchr(ptr, 10);	// Find CR
					
			while(ptr2[1] == ' ' || ptr2[1] == 9)		// Whitespace - continuation line
			{
				ptr2 = strchr(&ptr2[1], 10);	// Find CR
			}

			ptr = ++ptr2;
		}

		ptr += 2;		// Skip rerminating line

		// Should now have a plain text body to return;

		// But could be an odd encoding

		if (Base64)
		{
			int i = 0, Len = (int)strlen(ptr), NewLen;
			char * ptr2;
			char * End;
			char * Save = ptr;

			ptr2 = ptr;
			End = ptr + Len;

			while (ptr < End)
			{
				while (*ptr < 33)
					{ptr++;}

				*ptr2++ = *ptr++;
			}

			*ptr2 = 0;

			ptr = Save;
			Len = (int)(ptr2 - ptr - 1);

			ptr2 = *Body;

			while (Len > 0)
			{
				decodeblock(ptr, ptr2);
				ptr += 4;
				ptr2 += 3;
				Len -= 4;
			}

			NewLen = (int)(ptr2 - *Body);

			if (*(ptr-1) == '=')
				NewLen--;

			if (*(ptr-2) == '=')
				NewLen--;

			*MsgLen = NewLen;
		}
		else if (QuotedP)
		{
			int i = 0, Len = (int)strlen(ptr);
			char * ptr2;
			char * End;
			char * Save = ptr;

			ptr2 = *Body;

			End = ptr + Len;

			while (ptr < End)
			{
				if ((*ptr) == '=')
				{
					char c = *(++ptr);
					char d;

					c = c - 48;
					if (c < 0)
					{
						// = CRLF as a soft break

						ptr += 2;
						continue;
					}

					if (c > 9)
						c -= 7;
					d  = *(++ptr);
					d = d - 48;
					if (d > 9)
						d -= 7;

					*(ptr2) = c << 4 | d;
					ptr2++;	
					ptr++;
				}
				else
				{
					*ptr2++ = *ptr++;
				}
			}
			*ptr2 = 0;

			*MsgLen = (int)(ptr2 - *Body);
		}
		else
		{
			strcpy(*Body, ptr);
			*MsgLen = (int)strlen(ptr);
		}
		free(Save);
	
		return FALSE;
	}

	// Assume Multipart/Mixed - Message with attachments

	ptr = FindPart(Body, Boundary, &Partlen);

	if (ptr == NULL)
		return FALSE;			// Couldn't find separator

	while (ptr)
	{
		BOOL Base64 = FALSE;
		BOOL QuotedP = FALSE;

		MallocSave[Files] = ptr;		// For free();

		// Should be the First (Least desireable part, but the bit we want, as we are only interested in plain text)

		// Process headers - looking for Content-Disposition: attachment;

		// The first could also be a Content-Type: multipart/alternative; - if so, feed back to mime handler
	
		while(*ptr != 13)
		{
			char lcLine[1000] = "";

			ptr2 = strchr(ptr, 10);	// Find CR

			if (ptr2 == 0)
				return FALSE;
					
			while(ptr2[1] == ' ' || ptr2[1] == 9)		// Whitespace - continuation line
			{
				ptr2 = strchr(&ptr2[1], 10);	// Find CR
			}

			memcpy(lcLine, ptr, ptr2-ptr-1);
			_strlwr(lcLine);

			ptr = lcLine;

			if (_memicmp(ptr, "Content-Type: Multipart/alternative", 30) == 0)
			{
				// Feed Back
				int MsgLen;
				char * Text = malloc(Partlen+1);

				memcpy(Text, MallocSave[Files], Partlen);

				free(MallocSave[Files]);
				MallocSave[Files] = Text;


				CheckforMIME(sockptr, Text, &Text, &MsgLen);

				FileName[Files][0] = 0;				
				FileBody[Files] = Text;


				FileLen[Files++] = MsgLen;

				goto NextPart;

			}
			else if (_memicmp(ptr, "Content-Disposition: ", 21) == 0)
			{
				ptr3 = strstr(&ptr[21], "filename");
				
				if (ptr3)
				{
					ptr3 += 9;
					if (*ptr3 == '"') ptr3++;
					ptr4 = strchr(ptr3, '"');
					if (ptr4) *ptr4 = 0;

					strcpy(FileName[Files], ptr3);
				}
			}

			else if (_memicmp(ptr, "Content-Transfer-Encoding:", 26) == 0)
			{
				if (strstr(&ptr[26], "base64"))
					Base64 = TRUE;
				else
				if (strstr(&ptr[26], "quoted-printable"))
					QuotedP = TRUE;
			}

			ptr = ++ptr2;
		}

		ptr += 2;

		// Should now have file or plain text. If file is Base64 encoded, decode it.

		FileBody[Files] = ptr;
		FileLen[Files] = (int)(Partlen - 2 - (ptr - MallocSave[Files]));

		if (Base64)
		{
			int i = 0, Len = FileLen[Files], NewLen;
			char * ptr2 = ptr;
			char * End;

			End = ptr + FileLen[Files];

			while (ptr < End)
			{
				while (*ptr < 33)
					{ptr++;}

				*ptr2++ = *ptr++;
			}
			*ptr2 = 0;

			ptr = FileBody[Files];
			Len = (int)(ptr2 - ptr - 1);

			ptr2 = ptr;

			while (Len > 0)
			{
				decodeblock(ptr, ptr2);
				ptr += 4;
				ptr2 += 3;
				Len -= 4;
			}

			NewLen = (int)(ptr2 - FileBody[Files]);

			if (*(ptr-1) == '=')
				NewLen--;

			if (*(ptr-2) == '=')
				NewLen--;
	
			FileLen[Files] = NewLen;
		}
		else if (QuotedP)
		{
			int i = 0, Len = FileLen[Files], NewLen;
			char * ptr2 = ptr;
			char * End;

			End = ptr + FileLen[Files];

			while (ptr < End)
			{
				if ((*ptr) == '=')
				{
					char c = *(++ptr);
					char d;

					c = c - 48;
					if (c < 0)
					{
						// = CRLF as a soft break

						ptr += 2;
						continue;
					}

					if (c > 9)
						c -= 7;
					d  = *(++ptr);
					d = d - 48;
					if (d > 9)
						d -= 7;

					*(ptr2) = c << 4 | d;
					ptr2++;	
					ptr++;
				}
				else
				{
					*ptr2++ = *ptr++;
				}
			}
			*ptr2 = 0;

			NewLen = (int)(ptr2 - FileBody[Files]);

			FileLen[Files] = NewLen;
		}
		
		Files++;

	NextPart:
		ptr = FindPart(Body, Boundary, &Partlen);
	}

	// Now have all the parts - build a B2 Message. Leave the first part of header for later,
	// as we may have multiple recipients. Start with the Body: Line.

	// We need to add the first part of header later, so start message part way down buffer.
	// Make sure buffer is big enough.

	if ((sockptr->MailSize + 2000) > sockptr->MailBufferSize)
	{
		sockptr->MailBufferSize += 2000;
		sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize);
	
		if (sockptr->MailBuffer == NULL)
		{
			CriticalErrorHandler("Failed to extend Message Buffer");
			shutdown(sockptr->socket, 0);
			return FALSE;
		}
	}


	NewMsg = sockptr->MailBuffer + 1000;

	NewMsg += sprintf(NewMsg, "Body: %d\r\n", FileLen[0]);

	for (i = 1; i < Files; i++)
	{
		NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[i], FileName[i]);
	}

	NewMsg += sprintf(NewMsg, "\r\n");

	for (i = 0; i < Files; i++)
	{
		memcpy(NewMsg, FileBody[i], FileLen[i]);
		NewMsg += FileLen[i];
		free(MallocSave[i]);
		NewMsg += sprintf(NewMsg, "\r\n");
	}

	*MsgLen = (int)(NewMsg - (sockptr->MailBuffer + 1000));
	*Body = sockptr->MailBuffer + 1000;

	return TRUE;		// B2 Message
}


VOID ProcessSMTPServerMessage(SocketConn * sockptr, char * Buffer, int Len)
{
	SOCKET sock;
	int i;
	time_t Date = 0;

	sock=sockptr->socket;

	WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP);

	if (sockptr->Flags == GETTINGMESSAGE)
	{
		if(memcmp(Buffer, ".\r\n", 3) == 0)
		{
			char * ptr1, * ptr2;
			int linelen, MsgLen;
			char Msgtitle[62];
			BOOL B2Flag;
			int ToLen = 0;
			char * ToString;
			char * Via;
			
			// Scan headers for a Subject: or Date: Line (Headers end at blank line)

			ptr1 = sockptr->MailBuffer;
		Loop:
			ptr2 = strchr(ptr1, '\r');

			if (ptr2 == NULL)
			{
				SendSock(sockptr, "500 Eh");
				return;
			}

			linelen = (int)(ptr2 - ptr1);

			if (_memicmp(ptr1, "Subject:", 8) == 0)
			{
				if (linelen > 68) linelen = 68;
				memcpy(Msgtitle, &ptr1[9], linelen-9);
				Msgtitle[linelen-9]=0;
			}

			if (_memicmp(ptr1, "Date:", 5) == 0)
			{
				struct tm rtime;
				char * Context;
				char seps[] = " ,\t\r";
				char Offset[10] = "";
				int i, HH, MM;
				char Copy[500]="";

				// Copy message, so original isn't messed up by strtok
				
				memcpy(Copy, ptr1, linelen);

				ptr1 = Copy;

				memset(&rtime, 0, sizeof(struct tm));

				// Date: Tue, 9 Jun 2009 20:54:55 +0100

				ptr1 = strtok_s(&ptr1[5], seps, &Context);	// Skip Day
				ptr1 = strtok_s(NULL, seps, &Context);		// Day

				rtime.tm_mday = atoi(ptr1);

				ptr1 = strtok_s(NULL, seps, &Context);		// Month

				for (i=0; i < 12; i++)
				{
					if (strcmp(month[i], ptr1) == 0)
					{
						rtime.tm_mon = i;
						break;
					}
				}
		
				sscanf(Context, "%04d %02d:%02d:%02d%s",
					&rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset);

				rtime.tm_year -= 1900;

				Date = mktime(&rtime) - (time_t)_MYTIMEZONE;
	
				if (Date == (time_t)-1)
					Date = 0;
				else
				{
					if ((Offset[0] == '+') || (Offset[0] == '-'))
					{
						MM = atoi(&Offset[3]);
						Offset[3] = 0;
						HH = atoi(&Offset[1]);
						MM = MM + (60 * HH);

						if (Offset[0] == '+')
							Date -= (60*MM);
						else
							Date += (60*MM);

					}
				}
			}

			ptr1 = ptr2 + 2;		// Skip crlf
			
			if (linelen)			// Not Null line
			{
				goto Loop;
			}

			ptr2 = ptr1;
			ptr1 = sockptr->MailBuffer;

			MsgLen = (int)(sockptr->MailSize - (ptr2 - ptr1));

			// We Just want the from call, not the full address.
			
			TidyString(sockptr->MailFrom);
			
			// Examine Message to look for html formatting and attachments.

			B2Flag = CheckforMIME(sockptr, sockptr->MailBuffer, &ptr2, &MsgLen);	// Will reformat message if necessary. 

			// If any recipients are via RMS, create one message for them, and separate messages for all others

			ToString = zalloc(sockptr->Recipients * 100);
	
			for (i=0; i < sockptr->Recipients; i++)
			{
				char Addr[256];					// Need copy, as we may change it then decide it isn't for RMS

				strcpy(Addr, sockptr->RecpTo[i]);
				Debugprintf("To Addr %s", Addr);

				TidyString(Addr);
				Debugprintf("To Addr after Tidy %s", Addr);

				if ((_memicmp (Addr, "RMS:", 4) == 0) |(_memicmp (Addr, "RMS/", 4) == 0))
				{
					// Add to B2 Message for RMS
										
					_strlwr(Addr);
					
					Via = strlop(&Addr[4], '@');
				
					if (Via && _stricmp(Via, "winlink.org") == 0)
					{
						if (CheckifLocalRMSUser(Addr)) // if local RMS - Leave Here
							continue;
						
						ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, &Addr[4]);
						*sockptr->RecpTo[i] = 0;		// So we dont create individual one later
						continue;
					}

					ToLen = sprintf(ToString, "%sTo: %s@%s\r\n", ToString, &Addr[4], Via);
					*sockptr->RecpTo[i] = 0;			// So we dont create individual one later
					continue;
				}

				_strupr(Addr);
				Debugprintf("To Addr after strupr %s", Addr);

				Via = strlop(Addr, '@');
				Debugprintf("Via %s", Via);

				if (Via && _stricmp(Via, "winlink.org") == 0)
				{
					if (CheckifLocalRMSUser(Addr)) // if local RMS - Leave Here
						continue;
					
					ToLen = sprintf(ToString, "%sTo: %s\r\n", ToString, Addr);
					*sockptr->RecpTo[i] = 0;		// So we dont create individual one later

					continue;
				}
			}

			if (ToLen)						// Have some RMS Addresses
			{
				char B2Hddr[1000];
				int B2HddrLen;
				char DateString[80];
				char * NewBody;
				struct tm * tm;
				struct MsgInfo * Msg;
				BIDRec * BIDRec;

				Msg = AllocateMsgRecord();
		
				// Set number here so they remain in sequence
		
				Msg->number = ++LatestMsg;
				MsgnotoMsg[Msg->number] = Msg;
				Msg->length = MsgLen;

				sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName);

				Msg->type = 'P';
				Msg->status = 'N';
				strcpy(Msg->to, "RMS");
				strlop(sockptr->MailFrom, '@');
				if (strlen(sockptr->MailFrom) > 6) sockptr->MailFrom[6]=0;
				strcpy(Msg->from, sockptr->MailFrom);
				strcpy(Msg->title, Msgtitle);

				BIDRec = AllocateBIDRecord();

				strcpy(BIDRec->BID, Msg->bid);
				BIDRec->mode = Msg->type;
				BIDRec->u.msgno = LOWORD(Msg->number);
				BIDRec->u.timestamp = LOWORD(time(NULL)/86400);

				Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);

				if (Date)
					Msg->datecreated = Date;

				tm = gmtime(&Date);	
	
				sprintf(DateString, "%04d/%02d/%02d %02d:%02d",
					tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);

				if (B2Flag)				// Message has attachments, so Body: line is present
				{
					Msg->B2Flags = B2Msg | Attachments;
		
					B2HddrLen = sprintf(B2Hddr,
						"MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\n%sSubject: %s\r\nMbo: %s\r\n",
						Msg->bid, DateString, "Private", Msg->from, ToString, Msg->title, BBSName);
				}
				else
				{
					Msg->B2Flags = B2Msg;
					B2HddrLen = sprintf(B2Hddr,
						"MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\n%sSubject: %s\r\nMbo: %s\r\nBody: %d\r\n\r\n",
						Msg->bid, DateString, "Private", Msg->from, ToString, Msg->title, BBSName, Msg->length);

				}
			
				NewBody = ptr2 - B2HddrLen;

				memcpy(NewBody, B2Hddr, B2HddrLen);

				Msg->length += B2HddrLen;

				free(ToString);
	
				// Set up forwarding bitmap

				MatchMessagetoBBSList(Msg, 0);

				CreateSMTPMessageFile(NewBody, Msg);
			}
			
			for (i=0; i < sockptr->Recipients; i++)
			{
				if (*sockptr->RecpTo[i])			// not already sent to RMS?
					CreateSMTPMessage(sockptr, i, Msgtitle, Date, ptr2, MsgLen, B2Flag);
				else
					free(sockptr->RecpTo[i]);
			}

			free(sockptr->RecpTo);
			sockptr->RecpTo = NULL;
			free(sockptr->MailFrom);
			free(sockptr->MailBuffer);

			sockptr->MailBufferSize=0;
			sockptr->MailBuffer=0;
			sockptr->MailSize = 0;
	
			sockptr->Flags = 0;
			sockptr->Recipients = 0;

			SendSock(sockptr, "250 Ok");
			return;
		}

		if ((sockptr->MailSize + Len) > sockptr->MailBufferSize)
		{
			sockptr->MailBufferSize += 10000;
			sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize);
	
			if (sockptr->MailBuffer == NULL)
			{
				CriticalErrorHandler("Failed to extend Message Buffer");
				shutdown(sock, 0);
				return;
			}
		}

		memcpy(&sockptr->MailBuffer[sockptr->MailSize], Buffer, Len);
		sockptr->MailSize += Len;

		return;
	}

	if (sockptr->State == GettingUser)
	{
		char Out[30];
		
		Buffer[Len-2]=0;

		decodeblock(Buffer, Out);
		decodeblock(&Buffer[4], &Out[3]);
		decodeblock(&Buffer[8], &Out[6]);
		decodeblock(&Buffer[12], &Out[9]);

		if (strlen(Out) > 10) Out[10] = 0;

		strcpy(sockptr->CallSign, Out);
		
		sockptr->State = GettingPass;
		SendSock(sockptr, "334 UGFzc3dvcmQ6");
		return;
	}

	if (sockptr->State == GettingPass)
	{
		struct UserInfo * user = NULL;
		char Out[30];

		Buffer[Len-2]=0;

		decodeblock(Buffer, Out);
		decodeblock(&Buffer[4], &Out[3]);
		decodeblock(&Buffer[8], &Out[6]);
		decodeblock(&Buffer[12], &Out[9]);
		decodeblock(&Buffer[16], &Out[12]);
		decodeblock(&Buffer[20], &Out[15]);

		user = LookupCall(sockptr->CallSign);

		if (user)
		{
			if (strcmp(user->pass, Out) == 0)
			{
				sockptr->State = Authenticated;
				SendSock(sockptr, "235 2.0.0 OK Authenticated"); //535 authorization failed
				return;
			}
		}

		SendSock(sockptr, "535 authorization failed");
		sockptr->State = 0;
		return;
	}



/*AUTH LOGIN

334 VXNlcm5hbWU6
a4msl9ux
334 UGFzc3dvcmQ6
ZvVx9G1hcg==
235 2.0.0 OK Authenticated
*/


	if(memcmp(Buffer, "AUTH LOGIN", 10) == 0)
	{
		sockptr->State = GettingUser;
		SendSock(sockptr, "334 VXNlcm5hbWU6");
		return;
	}

	if(memcmp(Buffer, "EHLO",4) == 0)
	{
		SendSock(sockptr, "250-BPQ Mail Server");
		SendSock(sockptr, "250 AUTH LOGIN");

		//250-8BITMIME

		return;
	}

	if(memcmp(Buffer, "AUTH LOGIN", 10) == 0)
	{
		sockptr->State = GettingUser;
		SendSock(sockptr, "334 VXNlcm5hbWU6");
		return;
	}


	if(memcmp(Buffer, "HELO",4) == 0)
	{
		SendSock(sockptr, "250 Ok");
		return;
	}
	
	if(_memicmp(Buffer, "MAIL FROM:", 10) == 0)
	{
		if (sockptr->State != Authenticated)
		{
			// Accept if from 44/8 and ends in ampr.org

			if (_memicmp(&Buffer[Len - 11], "ampr.org", 8) == 0 &&
				(sockptr->sin.sin_addr.s_addr & 0xff) == 44)	
			{
			}
			else
			{
				SendSock(sockptr, "530 Authentication required");
				return;
			}
		}
		
		sockptr->MailFrom = zalloc(Len);
		memcpy(sockptr->MailFrom, &Buffer[10], Len-12);
			
		SendSock(sockptr, "250 Ok");

		return;
	}

	if(_memicmp(Buffer, "RCPT TO:", 8) == 0)
	{
		if (sockptr->State != Authenticated)
		{
			// Accept if from 44/8 and ends in ampr.org



			if (_memicmp(&Buffer[Len - 11], "ampr.org", 8) == 0 &&
				(sockptr->sin.sin_addr.s_addr & 0xff) == 44)	
			{
			}
			else
			{
				SendSock(sockptr, "530 Authentication required");
				return;
			}
		}

		sockptr->RecpTo=realloc(sockptr->RecpTo, (sockptr->Recipients+1) * sizeof(void *));
		sockptr->RecpTo[sockptr->Recipients] = zalloc(Len);

		memcpy(sockptr->RecpTo[sockptr->Recipients++], &Buffer[8], Len-10);
			
		SendSock(sockptr, "250 Ok");
		return;
	}

	if(memcmp(Buffer, "DATA\r\n", 6) == 0)
	{
		sockptr->MailBuffer=malloc(10000);
		sockptr->MailBufferSize=10000;

		if (sockptr->MailBuffer == NULL)
		{
			CriticalErrorHandler("Failed to create SMTP Message Buffer");
			SendSock(sockptr, "250 Failed");
			shutdown(sock, 0);

			return;
		}
	
		sockptr->Flags |= GETTINGMESSAGE;

		SendSock(sockptr, "354 End data with <CR><LF>.<CR><LF>");
		return;
	}

	if(memcmp(Buffer, "QUIT\r\n", 6) == 0)
	{
		SendSock(sockptr, "221 OK");
		Sleep(500);
		shutdown(sock, 0);
		return;
	}

	if(memcmp(Buffer, "RSET\r\n", 6) == 0)
	{
		SendSock(sockptr, "250 Ok");

		// This cancelled AUTH which I think is wrong
		//sockptr->State = 0;

		if (sockptr->State != Authenticated)
			sockptr->State = 0;	

		sockptr->Recipients = 0;

		return;
	}

	return;
}


int CreateSMTPMessage(SocketConn * sockptr, int i, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen, BOOL B2Flag)
{
	struct MsgInfo * Msg;
	BIDRec * BIDRec;
	char * To;
	char * via;

	// Allocate a message Record slot

	Msg = AllocateMsgRecord();
		
	// Set number here so they remain in sequence
		
	Msg->number = ++LatestMsg;
	MsgnotoMsg[Msg->number] = Msg;
	Msg->length = MsgLen;

	sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName);

	Msg->type = 'P';
	Msg->status = 'N';

	BIDRec = AllocateBIDRecord();

	strcpy(BIDRec->BID, Msg->bid);
	BIDRec->mode = Msg->type;
	BIDRec->u.msgno = LOWORD(Msg->number);
	BIDRec->u.timestamp = LOWORD(time(NULL)/86400);

	Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);

	if (Date)
		Msg->datecreated = Date;

	To = sockptr->RecpTo[i];

	Debugprintf("To %s", To);

	TidyString(To);

	Debugprintf("To after tidy %s", To);

	if (_memicmp(To, "bull/", 5) == 0)
	{
		Msg->type = 'B';
		memmove(To, &To[5], strlen(&To[4]));
	}

	if ((_memicmp(To, "nts/", 4) == 0) ||(_memicmp(To, "nts:", 4) == 0) ||
		(_memicmp(To, "nts.", 4) == 0))
	{
		Msg->type = 'T';
		memmove(To, &To[4], strlen(&To[3]));
	}

	if (_memicmp(To, "rms:", 4) == 0)
	{
		via = _strlwr(strlop(To, ':'));
	}
	else if (_memicmp(To, "rms/", 4) == 0)
	{
		via = _strlwr(strlop(To, '/'));
	}
	else if (_memicmp(To, "rms.", 4) == 0)
	{
		via = _strlwr(strlop(To, '.'));
	}
	else if (_memicmp(To, "smtp:", 5) == 0)
	{
		via = _strlwr(strlop(To, ':'));
		To[0] = 0;
	}
	else if (_memicmp(To, "smtp/", 5) == 0)
	{
		via = _strlwr(strlop(To, '/'));
		To[0] = 0;
	}
	else
	{
		via = strlop(To, '@');
	}

	Debugprintf("via %s", via);

	if (via)
	{
		int toLen;
		
		if (strlen(via) > 40) via[40] = 0;

		strcpy(Msg->via, via);		// Save before messing with it

		// if ending in AMPR.ORG send via ISP if we have enabled forwarding AMPR

		toLen = (int)strlen(via);

		if (_memicmp(&via[toLen - 8], "ampr.org", 8) == 0)
		{
			// if our domain keep here.
				
			// if not, and SendAMPRDirect set, set as ISP,
			// else set as RMS			
				
			if (_stricmp(via, AMPRDomain) == 0)
			{
				// Our Message- dont forward
			}
			else
			{
				// AMPR but not us

				if (SendAMPRDirect)
				{
					sprintf(Msg->via,"%s@%s", To, via);
					strcpy(To, "AMPR");
				}
				else
				{
					sprintf(Msg->via,"%s@%s", To, via);
					strcpy(To, "RMS");
				}
			}
		}
		else
		{	
			strlop(via, '.');			// Get first part of address

			if (_stricmp(via, BBSName) == 0)
			{
				// sent via us - clear the name

				Msg->via[0] = 0;
			}
		}
	}

 	if (strlen(To) > 6) To[6]=0;

	strcpy(Msg->to, To);

	if (strchr(sockptr->MailFrom, '@'))
	{
		char * FromHA = strlop(sockptr->MailFrom, '@');
		Msg->emailfrom[0] = '@';
		strcpy(&Msg->emailfrom[1], FromHA);
	}
					
	if (strlen(sockptr->MailFrom) > 6) sockptr->MailFrom[6]=0;

	strcpy(Msg->from, sockptr->MailFrom);

	strcpy(Msg->title, MsgTitle);

	if(Msg->to[0] == 0)
		SMTPMsgCreated=TRUE;

	// If NTS message (TO is numeric and AT is NTSxx or NTSxx.NTS - Outlook won't accept x@y)

	if (isdigits(Msg->to) && memcmp(Msg->via, "NTS", 3) == 0)
	{
		if (Msg->via[5] == 0 || strcmp(&Msg->via[5], ".NTS") == 0)
		{
			Msg->type = 'T';
			Msg->via[5] = 0;
		}
	}

	Debugprintf("Msg->Via %s", Msg->via);

	if (B2Flag)
	{
		char B2Hddr[1000];
		int B2HddrLen;
		char B2To[80];
		char * NewBody;
		char DateString[80];
		char * TypeString;
		struct tm * tm;

		tm = gmtime(&Date);	
	
		sprintf(DateString, "%04d/%02d/%02d %02d:%02d",
			tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);


		if (strcmp(Msg->to, "RMS") == 0)		// Address is in via
		strcpy(B2To, Msg->via);
	else
		if (Msg->via[0])
			sprintf(B2To, "%s@%s", Msg->to, Msg->via);
		else
			strcpy(B2To, Msg->to);

		
		Msg->B2Flags = B2Msg | Attachments;

		if (Msg->type == 'P')
			TypeString = "Private" ;
		else if (Msg->type == 'B')
			TypeString = "Bulletin";
		else if (Msg->type == 'T')
			TypeString = "Traffic";

		B2HddrLen = sprintf(B2Hddr,
			"MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n",
			Msg->bid, DateString, TypeString,
			Msg->from, B2To, Msg->title, BBSName);

		NewBody = MsgBody - B2HddrLen;

		memcpy(NewBody, B2Hddr, B2HddrLen);

		Msg->length += B2HddrLen;

		free(To);
	
		// Set up forwarding bitmap

		MatchMessagetoBBSList(Msg, 0);

		if (Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0)
			Msg->status = '$';				// Has forwarding

		return CreateSMTPMessageFile(NewBody, Msg);

	}

	free(To);

	// Set up forwarding bitmap

	MatchMessagetoBBSList(Msg, 0);

	if (Msg->type == 'B' && memcmp( Msg->fbbs, zeros, NBMASK) != 0)
		Msg->status = '$';				// Has forwarding

	return CreateSMTPMessageFile(MsgBody, Msg);
		
}


BOOL CreateSMTPMessageFile(char * Message, struct MsgInfo * Msg)
{
	char MsgFile[250];
	FILE * hFile;
	int WriteLen=0;
	char Mess[255];
	int len;

	struct UserInfo * ToUser = LookupCall(Msg->to);

	if (ToUser && ToUser->flags & F_HOLDMAIL)
	{
		int Length=0;
		char * MailBuffer = malloc(100);
		char Title[100];

		Msg->status = 'H';

		Length += sprintf(MailBuffer, "Message %d Held\r\n", Msg->number);
		sprintf(Title, "Message %d Held - %s", Msg->number, "User has Hold Messages flag set");
		SendMessageToSYSOP(Title, MailBuffer, Length);
	}

	sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number);
	
	hFile = fopen(MsgFile, "wb");
	
	if (hFile)
	{
		WriteLen = (int)fwrite(Message, 1, Msg->length, hFile);
		fclose(hFile);
	}

	if (WriteLen != Msg->length)
	{
		len = sprintf_s(Mess, sizeof(Mess), "Failed to create Message File\r");
		CriticalErrorHandler(Mess);

		return FALSE;
	}

	SaveMessageDatabase();
	SaveBIDDatabase();

	return TRUE;
}

int TidyString(char * Address)
{
	// Cleans up a From: or To: Address

	// May have leading or trailing spaces, or be enclosed by <>,  or have a " " part

	// From: "John Wiseman" <john.wiseman@ntlworld.com>

	char * ptr1, * ptr2;
	size_t len;

	_strupr(Address);

	ptr1 = strchr(Address, '<');

	if (ptr1)
	{
		ptr1++;
		ptr2 = strlop(ptr1, '>');
		len = (int)strlen(ptr1);
		memmove(Address, ptr1, len);
		Address[len] = 0;

		// Could have surrounding "" ""
	
		if (Address[0] == '"')
		{
			int len = (int)strlen(Address) - 1;
		
			if (Address[len] == '"')
			{
				Address[len] = 0;
				memmove(Address, &Address[1], len);
				return 0;
			}
			
			// Thunderbird can put "" round part of address "rms:john.wiseman"@cantab.net
			
			ptr2 = strchr(&Address[1], '"');

			if (ptr2)
			{
				memmove(Address, &Address[1], ptr2 - &Address[1]);
				memmove(ptr2 - 1, ptr2 + 1, strlen(ptr2 + 1) + 1);

			}


		}

		return 0;
	}

	ptr1 = Address;

	while (*ptr1 == ' ') ptr1++;

	if (*ptr1 == '"')
	{
		ptr1++;
		ptr1=strlop(ptr1, '"');
		ptr2=strlop(ptr1, ' ');
		ptr1=ptr2;
	}

	if (*ptr1 == '<') ptr1++;

	ptr2 = strlop(ptr1, '>');
	strlop(ptr1, ' ');

	len = strlen(ptr1);
	memmove(Address, ptr1, len);
	Address[len] = 0;

	return 0;
}
/*
+OK POP3 server ready
USER john.wiseman
+OK please send PASS command
PASS gb7bpq
+OK john.wiseman is welcome here
STAT
+OK 6 115834

UIDL
+OK 6 messages
1 <4A0DC6E0.5020504@hb9bza.net>
2 <gul8gb+of9r@eGroups.com>
3 <1085101c9d5d0$09b15420$16f9280a@phx.gbl>
4 <gul9ms+qkht@eGroups.com>
5 <B0139742084@email.bigvalley.net>
6 <20090516011401.53DB013804@panix1.panix.com>
.
LIST
+OK 6 messages
1 7167
2 10160
3 52898
4 4746
5 20218
6 20645
.

*/

VOID ProcessPOP3ServerMessage(SocketConn * sockptr, char * Buffer, int Len)
{
	SOCKET sock;
	int i;
	struct MsgInfo * Msg;

	sock=sockptr->socket;

	WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP);

	if(memcmp(Buffer, "CAPA",4) == 0)
	{
		SendSock(sockptr, "+OK Capability list follows");
		SendSock(sockptr, "UIDL");
		SendSock(sockptr, "TOP");
		SendSock(sockptr, "EXPIRE 30");
		SendSock(sockptr, ".");
		return;
	}  

	if(memcmp(Buffer, "AUTH",4) == 0)
	{
		SendSock(sockptr, "-ERR");
		return;
	}  
	if (sockptr->State == GettingUser)
	{
		
		Buffer[Len-2]=0;
		if (Len > 15) Buffer[15]=0;

		strcpy(sockptr->CallSign, &Buffer[5]);
		
		sockptr->State = GettingPass;
		SendSock(sockptr, "+OK please send PASS command");
		return;
	}

	if (sockptr->State == GettingPass)
	{
		struct UserInfo * user = NULL;

		Buffer[Len-2]=0;
		user = LookupCall(sockptr->CallSign);

		if (user)
		{
			if (strcmp(user->pass, &Buffer[5]) == 0)
			{
				if (user->POP3Locked)
				{
					SendSock(sockptr, "-ERR Mailbox Locked");
					sockptr->State = 0;
					return;
				}

				sockptr->State = Authenticated;
				SendSock(sockptr, "+OK Authenticated");

				sockptr->POP3User = user;
				user->POP3Locked = TRUE;

				// Get Message List

				for (i=0; i<=NumberofMessages; i++)
				{
					Msg = MsgHddrPtr[i];
					
					if ((_stricmp(Msg->to, sockptr->CallSign) == 0) ||
						((_stricmp(Msg->to, "SYSOP") == 0) && (user->flags & F_SYSOP) && (Msg->type == 'P')))
					{
						if (Msg->status != 'K' && Msg->status != 'H')
						{
							sockptr->POP3Msgs = realloc(sockptr->POP3Msgs, (sockptr->POP3MsgCount+1) * sizeof(void *));
							sockptr->POP3Msgs[sockptr->POP3MsgCount++] = MsgHddrPtr[i];
						}
					}
				}

				return;
			}
		}

		SendSock(sockptr, "-ERR Authentication failed");
		sockptr->State = 0;
		return;
	}

	if (memcmp(Buffer, "QUIT",4) == 0)
	{
		SendSock(sockptr, "+OK Finished");

		if (sockptr->POP3User)
			sockptr->POP3User->POP3Locked = FALSE;

		return;
	}

	if (memcmp(Buffer, "NOOP",4) == 0)
	{
		SendSock(sockptr, "+OK ");
		return;
	}

//	if (memcmp(Buffer, "LAST",4) == 0)
//	{
//		SendSock(sockptr, "+OK 0");
//		return;
//	}

	if (sockptr->State != Authenticated)
	{
		SendSock(sockptr, "-ERR Need Authentication");
		sockptr->State = 0;
		return;
	}

	if (memcmp(Buffer, "STAT",4) == 0)
	{
		char reply[40];
		int i, size=0;

		for (i=0; i< sockptr->POP3MsgCount; i++)
		{
			size+=sockptr->POP3Msgs[i]->length;
		}

		sprintf_s(reply, sizeof(reply), "+OK %d %d", sockptr->POP3MsgCount, size);

		SendSock(sockptr, reply);
		return;
	}

	if (memcmp(Buffer, "UIDL",4) == 0)
	{
		char reply[40];
		int i, count=0, size=0;
		int MsgNo=1;

		SendSock(sockptr, "+OK ");

		for (i=0; i< sockptr->POP3MsgCount; i++)
		{
			sprintf_s(reply, sizeof(reply), "%d %s", i+1, sockptr->POP3Msgs[i]->bid);
			SendSock(sockptr, reply);	
		}

		SendSock(sockptr, ".");
		return;
	}

	if (memcmp(Buffer, "LIST", 4) == 0)
	{
		char reply[40];
		int i, count=0, size=0;
		int MsgNo = atoi(&Buffer[4]);

		if (Buffer[4] == 13)	// CR
			MsgNo = 0;

		Debugprintf("%s %d", Buffer, MsgNo);

		if (MsgNo)
		{
			if (MsgNo > sockptr->POP3MsgCount)		
				sprintf(reply, "-ERR no such message, only %d messages in maildrop", sockptr->POP3MsgCount);
			else
				sprintf(reply, "+OK %d %d", MsgNo, sockptr->POP3Msgs[MsgNo - 1]->length);
			SendSock(sockptr, reply);
			return;
		}


		SendSock(sockptr, "+OK ");

		for (i=0; i< sockptr->POP3MsgCount; i++)
		{
			sprintf_s(reply, sizeof(reply), "%d %d", i+1, sockptr->POP3Msgs[i]->length);
			SendSock(sockptr, reply);	
		}

		SendSock(sockptr, ".");
		return;
	}

	if (memcmp(Buffer, "RETR", 4) == 0 || memcmp(Buffer, "TOP", 3) == 0)
	{
		char * ptr;		
		char Header[120];
		int i, count=0, size=0;
		int MsgNo=1;
		char * msgbytes;
		struct MsgInfo * Msg;
		char B2From[80];
		struct UserInfo * FromUser;
		char TimeString[64];
		BOOL TOP = FALSE;
		int Len;

		if (memcmp(Buffer, "TOP", 3) == 0)
			TOP = TRUE;

		ptr=strlop(Buffer, ' ');			// Get Number

		i=atoi(ptr);

		if ((i > sockptr->POP3MsgCount)  || (i == 0))
		{
			SendSock(sockptr, "-ERR no such message");
			return;
		}

		Msg = sockptr->POP3Msgs[i-1];

		msgbytes = ReadMessageFile(Msg->number);

		if (msgbytes == NULL)
		{
			SendSock(sockptr, "-ERR no such message");
			return;
		}

		SendSock(sockptr, "+OK ");

		// Build an RFC822 ish header

//Received: from [69.147.65.148] by n15.bullet.sp1.yahoo.com with NNFMP; 16 May 2009 02:30:47 -0000
//Received: from [69.147.108.192] by t11.bullet.mail.sp1.yahoo.com with NNFMP; 16 May 2009 02:30:47 -0000

		FormatTime(TimeString, (time_t)Msg->datecreated);

		sprintf_s(Header, sizeof(Header), "Date: %s", TimeString);
		SendSock(sockptr, Header);

		sprintf_s(Header, sizeof(Header), "To: %s", Msg->to);
		SendSock(sockptr, Header);
		
		sprintf_s(Header, sizeof(Header), "Message-ID: %s", Msg->bid);
		SendSock(sockptr, Header);

		if (_stricmp(Msg->from, "smtp:") == 0)
		{
			sprintf_s(Header, sizeof(Header), "From: smtp/%s", Msg->emailfrom);
			SendSock(sockptr, Header);
			sprintf_s(Header, sizeof(Header), "Replyto: smtp/%s", Msg->emailfrom);
		}
		else
		{
			if (_stricmp(Msg->from, "rms:") == 0)
			{
				sprintf_s(Header, sizeof(Header), "From: RMS/%s", Msg->emailfrom);
				SendSock(sockptr, Header);
				sprintf_s(Header, sizeof(Header), "Replyto: RMS/%s", Msg->emailfrom);
			}
			else
			{
				// If there is an adddress in Msg->emailfrom use it

				if (Msg->emailfrom[0])
				{
					strcpy(B2From, Msg->from);
					strcat(B2From, Msg->emailfrom);
				}
				else
				{
					// Packet Address. Mail client will need more than just a call to respond to
	
					strcpy(B2From, Msg->from);

					if (strcmp(Msg->from, "SMTP:") == 0)		// Address is in via
						strcpy(B2From, Msg->emailfrom);
					else
					{
						FromUser = LookupCall(Msg->from);

						if (FromUser)
						{
							if (FromUser->HomeBBS[0])
								sprintf(B2From, "%s@%s", Msg->from, FromUser->HomeBBS);
							else
								sprintf(B2From, "%s@%s", Msg->from, BBSName);
						}
						else
						{
							WPRecP WP = LookupWP(Msg->from);
							if (WP)
								sprintf(B2From, "%s@%s", Msg->from, WP->first_homebbs);
						}
					}	
				}
				sprintf_s(Header, sizeof(Header), "From: %s", B2From);	
				SendSock(sockptr, Header);
				sprintf_s(Header, sizeof(Header), "Replyto: %s", B2From);	
			}
		}	
		SendSock(sockptr, Header);
		sprintf_s(Header, sizeof(Header), "Subject: %s", Msg->title);
		SendSock(sockptr, Header);

		if ((Msg->B2Flags & Attachments) && TOP == FALSE)
		{
			// B2 Message with Attachments. Create a Mime-Encoded Multipart message

			SendMultiPartMessage(sockptr, Msg, msgbytes);
			return;
		}

		if (TOP)
		{
			// Get first i lines of message

			char * ptr1, * ptr2;

			ptr = strlop(ptr, ' ');			// Get Number of lines
			i = atoi(ptr);

			ptr1 = msgbytes;
			ptr2 = --ptr1;					// Point both to char before message
			
			while(i--)
			{
				ptr2 = strchr(++ptr1, 10);

				if (ptr2 == 0)				// No more lines
					i = 0;

				ptr1 = ptr2;
			}
			if (ptr2)
				*(ptr2 + 1) = 0;
		}

		// If message has characters above 7F convert to UFT8 if necessary and send as Base64

		Len = (int)strlen(msgbytes);

		if (Is8Bit(msgbytes, Len))
		{
			// 8 Bit. Will send as UFT8

			if (WebIsUTF8(msgbytes, Len) == FALSE)
			{
				// Must be some other coding

				int code = TrytoGuessCode(msgbytes, Len);
				UCHAR * UTF = malloc(Len * 3);

				if (code == 437)
					Len = Convert437toUTF8(msgbytes, Len, UTF);
				else if (code == 1251)
					Len = Convert1251toUTF8(msgbytes, Len, UTF);
				else
					Len = Convert1252toUTF8(msgbytes, Len, UTF);

				free(msgbytes);
				msgbytes = UTF;
			}
			
			SendSock(sockptr, "Content-Type: text/plain; charset=\"utf-8\"");
			SendSock(sockptr, "Content-Transfer-Encoding: base64");
			SendSock(sockptr, "Content-Disposition: inline");

			SendSock(sockptr, "");							// Blank line before body

			Base64EncodeAndSend(sockptr, msgbytes, Len);

		}
		else
		{
			// send as USASCII

			SendSock(sockptr, "");							// Blank line before body
			SendSock(sockptr, msgbytes);
		}

		SendSock(sockptr, "");
		SendSock(sockptr, ".");

		free(msgbytes);
		return;
	}


	if (memcmp(Buffer, "DELE",4) == 0)
	{
		char * ptr;		
		int i;
		struct MsgInfo * Msg;

		ptr=strlop(Buffer, ' ');			// Get Number

		i=atoi(ptr);

		if ((i > sockptr->POP3MsgCount)  || (i == 0))
		{
			SendSock(sockptr, "-ERR no such message");
			return;
		}

		Msg = sockptr->POP3Msgs[i-1];

		FlagAsKilled(Msg, TRUE);

		SendSock(sockptr, "+OK ");
		return;
	}


	if (memcmp(Buffer, "QUIT",4) == 0)
	{
		SendSock(sockptr, "+OK Finished");

		if (sockptr->POP3User)
			sockptr->POP3User->POP3Locked = FALSE;

		return;
	}

	SendSock(sockptr, "-ERR Unrecognised Command");

}



/* jer:
 * This is the original file, my mods were only to change the name/semantics on the b64decode function
 * and remove some dependencies.
 */
/*
	LibCGI base64 manipulation functions is extremly based on the work of Bob Tower,
	from its projec http://base64.sourceforge.net. The functions were a bit modicated. 
	Above is the MIT license from b64.c original code:

LICENCE:        Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.

                Permission is hereby granted, free of charge, to any person
                obtaining a copy of this software and associated
                documentation files (the "Software"), to deal in the
                Software without restriction, including without limitation
                the rights to use, copy, modify, merge, publish, distribute,
                sublicense, and/or sell copies of the Software, and to
                permit persons to whom the Software is furnished to do so,
                subject to the following conditions:

                The above copyright notice and this permission notice shall
                be included in all copies or substantial portions of the
                Software.

                THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
                KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
                WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
                PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
                OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
                OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
                OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
                SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE

*/
void encodeblock( unsigned char in[3], unsigned char out[4], int len )
{
    out[0] = cb64[ in[0] >> 2 ];
    out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
    out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
    out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
}

void decodeblock( unsigned char in[4], unsigned char out[3] )
{   
    char Block[5];
	
	Block[0]=mycd64[in[0]];
    Block[1]=mycd64[in[1]];
    Block[2]=mycd64[in[2]];
    Block[3]=mycd64[in[3]];

	out[0] = (unsigned char ) (Block[0] << 2 | Block[1] >> 4);
    out[1] = (unsigned char ) (Block[1] << 4 | Block[2] >> 2);
    out[2] = (unsigned char ) (((Block[2] << 6) & 0xc0) | Block[3]);
}

/** 
* @ingroup libcgi_string
* @{
*/

/**
* Encodes a given tring to its base64 form.
* 
* @param *str String to convert
* @return Base64 encoded String
* @see str_base64_decode
**/
char *str_base64_encode(char *str)
{
    unsigned int i = 0, j = 0, len = (int)strlen(str);
	char *tmp = str;
	char *result = (char *)zalloc((len+1) * sizeof(void *));
	
	if (!result)
		return NULL;

	while (len  > 2 )
	{
		encodeblock(&str[i], &result[j],3);
		i+=3;
		j+=4;
		len -=3;
	}
	if (len)
	{
		encodeblock(&str[i], &result[j], len);
	}

	return result;
}

SocketConn * SMTPConnect(char * Host, int Port, BOOL AMPR, struct MsgInfo * Msg, char * MsgBody)
{
	int err;
	u_long param=1;
	BOOL bcopt=TRUE;

	SocketConn * sockptr;

	SOCKADDR_IN sinx; 
	SOCKADDR_IN destaddr;
	int addrlen=sizeof(sinx);
	struct hostent * HostEnt;

	// Resolve Name if needed

	destaddr.sin_family = AF_INET; 
	destaddr.sin_port = htons(Port);

	destaddr.sin_addr.s_addr = inet_addr(Host);

	if (destaddr.sin_addr.s_addr == INADDR_NONE)
	{
		//	Resolve name to address

		 HostEnt = gethostbyname (Host);
		 
		 if (!HostEnt)
		 {
 			Logprintf(LOG_TCP, NULL, '|', "Resolve Failed for SMTP Server %s", Host);
			SMTPActive = FALSE;
			return FALSE;			// Resolve failed
		 }
		 memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4);
	}

//   Allocate a Socket entry

	sockptr=malloc(sizeof(SocketConn));
	memset(sockptr, 0, sizeof (SocketConn));

	sockptr->Next=Sockets;
	Sockets=sockptr;

	sockptr->socket=socket(AF_INET,SOCK_STREAM,0);

	if (sockptr->socket == INVALID_SOCKET)
	{
  	 	return FALSE; 
	}

	sockptr->Type = SMTPClient;
	sockptr->AMPR = AMPR;

	if (AMPR)
		strcpy(sockptr->FromDomain, AMPRDomain);
	else
		strcpy(sockptr->FromDomain, MyDomain);
	
	sockptr->SMTPMsg = Msg;
	sockptr->MailBuffer = MsgBody;

	ioctlsocket (sockptr->socket, FIONBIO, &param);
 
	setsockopt (sockptr->socket, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4);

	sinx.sin_family = AF_INET;
	sinx.sin_addr.s_addr = INADDR_ANY;
	sinx.sin_port = 0;

	if (bind(sockptr->socket, (LPSOCKADDR) &sinx, addrlen) != 0 )
	{
		//
		//	Bind Failed
		//
	
  	 	return FALSE; 
	}

	if (connect(sockptr->socket,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0)
	{
		//
		//	Connected successful
		//

		sockptr->State = WaitingForGreeting;

		return sockptr;
	}
	else
	{
		err=WSAGetLastError();

		if (err == WSAEWOULDBLOCK || err == 115 || err == 36)
		{
			//
			//	Connect in Progress
			//

			sockptr->State = Connecting;
			return sockptr;
		}
		else
		{
			//
			//	Connect failed
			//

			printf("SMTP Connect failed immediately\n");
			closesocket(sockptr->socket);
			ReleaseSock(sockptr->socket);
			return FALSE;

			return FALSE;
		}
	}
	return FALSE;
}

int TryHELO = 0;			// Not thread safe but taking the chance..

VOID ProcessSMTPClientMessage(SocketConn * sockptr, char * Buffer, int Len)
{
	SOCKET sock;

	sock=sockptr->socket;

	WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP);

	Buffer[Len] = 0;

	if (sockptr->State == WaitingForGreeting)
	{
		if (memcmp(Buffer, "220 ",4) == 0)
		{
			TryHELO = 0;

			if (sockptr->AMPR)
				sockprintf(sockptr, "EHLO %s", AMPRDomain);
			else if (ISPEHLOName[0])
				sockprintf(sockptr, "EHLO %s", ISPEHLOName);
			else
				sockprintf(sockptr, "EHLO %s", BBSName);
			
			sockptr->State = WaitingForHELOResponse;
		}
		else
		{
			SendSock(sockptr, "QUIT");
			sockptr->State = 0;
		}

		return;
	}

	if (sockptr->State == WaitingForHELOResponse)
	{
/*
	if (memcmp(Buffer, "500 ",4) == 0 && TryHELO == 0)
		{
			TryHELO = 1;

			if (sockptr->AMPR)
				sockprintf(sockptr, "HELO %s", AMPRDomain);
			else if (ISPEHLOName[0])
				sockprintf(sockptr, "HELO %s", ISPEHLOName);
			else
				sockprintf(sockptr, "HELO %s", BBSName);

			return;
		}
*/
		if (memcmp(Buffer, "250-",4) == 0)
			return;

		if (memcmp(Buffer, "250 ",4) == 0)
		{
			if (SMTPAuthNeeded && sockptr->AMPR == FALSE)
			{
				sockprintf(sockptr, "AUTH LOGIN");
				sockptr->State = WaitingForAUTHResponse;
			}
			else
			{
				sockprintf(sockptr, "MAIL FROM: <%s@%s>", sockptr->SMTPMsg->from, sockptr->FromDomain);
				sockptr->State = WaitingForFROMResponse;
			}
		}
		else
		{
			SendSock(sockptr, "QUIT");
			sockptr->State = 0;
			SMTPActive = FALSE;

		}

		return;
	}

	if (sockptr->State == WaitingForAUTHResponse)
	{
		if (memcmp(Buffer, "334 VXN", 7) == 0)
		{
			char * Msg = str_base64_encode(ISPAccountName);
			SendSock(sockptr, Msg);
			free(Msg);
			return;
		}
		else if (memcmp(Buffer, "334 UGF", 7) == 0)
		{
			char * Msg = str_base64_encode(ISPAccountPass);
			SendSock(sockptr, Msg);
			free(Msg);
			return;
		}
		else if (memcmp(Buffer, "235 ", 4) == 0)
		{
			sockprintf(sockptr, "MAIL FROM: <%s@%s>", sockptr->SMTPMsg->from, sockptr->FromDomain);
//			sockprintf(sockptr, "MAIL FROM: <%s@%s.%s>", sockptr->SMTPMsg->from, BBSName, HRoute);
			sockptr->State = WaitingForFROMResponse;
		}

		else
		{
			SendSock(sockptr, "QUIT");
			sockptr->State = 0;
			SMTPActive = FALSE;
		}

		return;

	}


	if (sockptr->State == WaitingForFROMResponse)
	{
		if (memcmp(Buffer, "250 ",4) == 0)
		{
			sockprintf(sockptr, "RCPT TO: <%s>", sockptr->SMTPMsg->via);
			sockptr->State = WaitingForTOResponse;
		}
		else
		{
			sockptr->SMTPMsg->status = 'H';			// Hold for review
			SendSock(sockptr, "QUIT");
			sockptr->State = 0;
			SMTPActive = FALSE;
		}

		return;
	}

	if (sockptr->State == WaitingForTOResponse)
	{
		if (memcmp(Buffer, "250 ",4) == 0)
		{
			SendSock(sockptr, "DATA");
			sockptr->State = WaitingForDATAResponse;
		}
		else
		{
			sockptr->SMTPMsg->status = 'H';			// Hold for review
			SendSock(sockptr, "QUIT");
			sockptr->State = 0;
			SMTPActive = FALSE;
		}

		return;
	}

	if (sockptr->State == WaitingForDATAResponse)
	{
		int Len;
		UCHAR * UTF;
		
		if (memcmp(Buffer, "354 ",4) == 0)
		{
			sockprintf(sockptr, "To: %s", sockptr->SMTPMsg->via);
			sockprintf(sockptr, "From: %s <%s@%s>", sockptr->SMTPMsg->from, sockptr->SMTPMsg->from, sockptr->FromDomain);
			sockprintf(sockptr, "Sender: %s@%s", sockptr->SMTPMsg->from, sockptr->FromDomain);
			if (GMailMode && sockptr->AMPR == FALSE)
				sockprintf(sockptr, "Reply-To: %s+%s@%s", GMailName, sockptr->SMTPMsg->from, sockptr->FromDomain);
			else
				sockprintf(sockptr, "Reply-To: %s@%s", sockptr->SMTPMsg->from, sockptr->FromDomain);

			sockprintf(sockptr, "Subject: %s", sockptr->SMTPMsg->title);
			
			sockptr->State = WaitingForBodyResponse;

			if (sockptr->SMTPMsg->B2Flags & Attachments)
			{
				// B2 Message with Attachments. Create a Mime-Encoded Multipart message

				SendMultiPartMessage(sockptr, sockptr->SMTPMsg, sockptr->MailBuffer);
				return;
			}

			// If message has characters above 7F convert to UFT8 if necessary and send as Base64


			Len = (int)strlen(sockptr->MailBuffer);

			if (Is8Bit(sockptr->MailBuffer, Len))
			{
				// 8 Bit. Will send as UFT8

				SendSock(sockptr, "Content-Type: text/plain; charset=\"utf-8\"");
				SendSock(sockptr, "Content-Transfer-Encoding: base64");
				SendSock(sockptr, "Content-Disposition: inline");

				SendSock(sockptr, "");							// Blank line before body

				if (WebIsUTF8(sockptr->MailBuffer, Len) == FALSE)
				{
					// Must be some other coding

					int code = TrytoGuessCode(sockptr->MailBuffer, Len);
					UTF = malloc(Len * 3);

					if (code == 437)
						Len = Convert437toUTF8(sockptr->MailBuffer, Len, UTF);
					else if (code == 1251)
						Len = Convert1251toUTF8(sockptr->MailBuffer, Len, UTF);
					else
						Len = Convert1252toUTF8(sockptr->MailBuffer, Len, UTF);	// Default
			
					Base64EncodeAndSend(sockptr, UTF, Len);
					free(UTF);
				}
				else
					Base64EncodeAndSend(sockptr, sockptr->MailBuffer, Len);

			}
			else
			{
				// send as USASCII

				SendSock(sockptr, "");							// Blank line before body
				SendSock(sockptr, sockptr->MailBuffer);
			}

			SendSock(sockptr, ".");

		}
		else
		{
			SendSock(sockptr, "QUIT");
			sockptr->State = 0;
			SMTPActive = FALSE;
		}

		return;
	}

	if (sockptr->State == WaitingForBodyResponse)
	{
		struct MsgInfo * Msg = sockptr->SMTPMsg;

		if (memcmp(Buffer, "250 ",  4) == 0)
		{
			// if AMPR, clear forwarding bitmap

			if (sockptr->AMPR)
			{
				// Mark mail as sent, and look for more

				struct UserInfo * bbs = sockptr->bbs;
	
				clear_fwd_bit(Msg->fbbs, bbs->BBSNumber);
				set_fwd_bit(Msg->forw, bbs->BBSNumber);

				//  Only mark as forwarded if sent to all BBSs that should have it
			
				if (memcmp(Msg->fbbs, zeros, NBMASK) == 0)
				{
					Msg->status = 'F';			// Mark as forwarded
					Msg->datechanged=time(NULL);
				}
				
				bbs->ForwardingInfo->MsgCount--;
				bbs->ForwardingInfo->Forwarding = 0;

				// See if any more

				if (bbs->ForwardingInfo->MsgCount)
					bbs->ForwardingInfo->FwdTimer = bbs->ForwardingInfo->FwdInterval; // Reschdul send
			
			}
			else
			{
				Msg->status = 'F';
				SMTPActive = FALSE;
				SMTPMsgCreated=TRUE;					// See if any more
			}
		}

		SendSock(sockptr, "QUIT");
		sockptr->State = 0;

		SMTPActive = FALSE;

		SMTPMsgCreated=TRUE;					// See if any more

		return;
	}
}	

BOOL SendtoAMPR(CIRCUIT * conn)
{
	struct MsgInfo * Msg = conn->FwdMsg;
	SocketConn * sockptr;

	char * Body;
	int toLen;
	char * tocopy;
	char * Host;
	
	// Make sure message exists

	Body = ReadMessageFile(Msg->number);

	if (Body == NULL)
	{
		FlagAsKilled(Msg, TRUE);
		return FALSE;
	}
		
	toLen = (int)strlen(Msg->via);

	tocopy = _strdup(Msg->via);

	Host = strlop(tocopy, '@');

	if (Host == NULL)
	{
		Logprintf(LOG_TCP, NULL, '|', "AMPR Forward - Host Name missing from VIA %s for Msg %d", Msg->via, Msg->number);
		free(tocopy);
		return FALSE;
	}

	Logprintf(LOG_TCP, NULL, '|', "Connecting to Server %s to send Msg %d", Host, Msg->number);

	sockptr = SMTPConnect(Host, 25, TRUE, Msg, Body);
	
	free(tocopy);

	if (sockptr)
	{
		sockptr->bbs = conn->UserPointer;

		return TRUE;
	}

	return FALSE;
}

BOOL SendtoISP()
{
	// Find a message intended for the Internet and send it

	int m = NumberofMessages;
	char * Body;

	struct MsgInfo * Msg;

	if (SMTPActive)
		return FALSE;

	do
	{
		Msg=MsgHddrPtr[m];

		if ((Msg->status == 'N') && (Msg->to[0] == 0) && (Msg->from[0] != 0))
		{
			// Make sure message exists

			Body = ReadMessageFile(Msg->number);

			if (Body == NULL)
			{
				FlagAsKilled(Msg, TRUE);
				return FALSE;
			}

			Logprintf(LOG_TCP, NULL, '|', "Connecting to Server %s to send Msg %d", ISPSMTPName, Msg->number);

			SMTPMsgCreated=FALSE;		// Stop any more attempts
			SMTPConnect(ISPSMTPName, ISPSMTPPort, FALSE, Msg, Body);

			SMTPActive = TRUE;

			return TRUE;
		}

		m--;

	} while (m> 0);

	return FALSE;

}


BOOL POP3Connect(char * Host, int Port)
{
	int err;
	u_long param=1;
	BOOL bcopt=TRUE;

	SocketConn * sockptr;

	SOCKADDR_IN sinx; 
	SOCKADDR_IN destaddr;
	int addrlen=sizeof(sinx);
	struct hostent * HostEnt;

	Logprintf(LOG_TCP, NULL, '|', "Connecting to POP3 Server %s", Host);

	// Resolve Name if needed

	destaddr.sin_family = AF_INET; 
	destaddr.sin_port = htons(Port);

	destaddr.sin_addr.s_addr = inet_addr(Host);

	if (destaddr.sin_addr.s_addr == INADDR_NONE)
	{
		//	Resolve name to address

		 HostEnt = gethostbyname (Host);
		 
		 if (!HostEnt)
		 {
			Logprintf(LOG_TCP, NULL, '|', "Resolve Failed for POP3 Server %s", Host);
			return FALSE;			// Resolve failed
		 }
		 memcpy(&destaddr.sin_addr.s_addr,HostEnt->h_addr,4);
	}

//   Allocate a Socket entry

	sockptr = malloc(sizeof(SocketConn));
	memset(sockptr, 0, sizeof (SocketConn));

	sockptr->Next = Sockets;
	Sockets = sockptr;

	sockptr->socket = socket(AF_INET,SOCK_STREAM,0);

	if (sockptr->socket == INVALID_SOCKET)
	{
  	 	return FALSE; 
	}

	sockptr->Type = POP3Client;
	
	ioctlsocket (sockptr->socket, FIONBIO, &param);
 
	setsockopt (sockptr->socket, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bcopt,4);

	sinx.sin_family = AF_INET;
	sinx.sin_addr.s_addr = INADDR_ANY;
	sinx.sin_port = 0;

	if (bind(sockptr->socket, (LPSOCKADDR) &sinx, addrlen) != 0 )
	{
		//
		//	Bind Failed
		//
	
  	 	return FALSE; 
	}

	if (connect(sockptr->socket,(LPSOCKADDR) &destaddr, sizeof(destaddr)) == 0)
	{
		//
		//	Connected successful
		//

		sockptr->State = WaitingForGreeting;

		return TRUE;
	}
	else
	{
		err=WSAGetLastError();

		if (err == WSAEWOULDBLOCK || err == 115 || err == 36)
		{
			//
			//	Connect in Progressing
			//

			sockptr->State = Connecting;
			return TRUE;
		}
		else
		{
			//
			//	Connect failed
			//

			printf("Connect failed immediately %d\n", err);
			perror("POP Connect");
			closesocket(sockptr->socket);
			ReleaseSock(sockptr->socket);
			return FALSE;
		}
	}
	return FALSE;
}

VOID ProcessPOP3ClientMessage(SocketConn * sockptr, char * Buffer, int Len)
{
	SOCKET sock;
	time_t Date;
	BOOL B2Flag;

	sock=sockptr->socket;

	WriteLogLine(NULL, '<',Buffer, Len-2, LOG_TCP);

	if (sockptr->Flags == GETTINGMESSAGE)
	{
		if(memcmp(Buffer, ".\r\n", 3) == 0)
		{
			// File Message

			char * ptr1, * ptr2;
			int linelen, MsgLen;
			char MsgFrom[62], MsgTo[100], Msgtitle[62];

			// Scan headers for From: To: and Subject: Line (Headers end at blank line)

			ptr1 = sockptr->MailBuffer;
		Loop:
			ptr2 = strchr(ptr1, '\r');

			if (ptr2 == NULL)
			{
				SendSock(sockptr, "500 Eh");
				return;
			}

			linelen = (int)(ptr2 - ptr1);

			// From: "John Wiseman" <john.wiseman@ntlworld.com>
			// To: <G8BPQ@g8bpq.org.uk>
			//<To: <gm8bpq+g8bpq@googlemail.com>


			if (_memicmp(ptr1, "From:", 5) == 0)
			{
				if (linelen > 65) linelen = 65;
				memcpy(MsgFrom, &ptr1[5], linelen-5);
				MsgFrom[linelen-5]=0;
			}
			else
			if (_memicmp(ptr1, "To:", 3) == 0)
			{
				if (linelen > 99) linelen = 99;
				memcpy(MsgTo, &ptr1[4], linelen-4);
				MsgTo[linelen-4]=0;
			}
			else
			if (_memicmp(ptr1, "Subject:", 8) == 0)
			{
				if (linelen > 68) linelen = 68;
				memcpy(Msgtitle, &ptr1[9], linelen-9);
				Msgtitle[linelen-9]=0;
			}
			else
			if (_memicmp(ptr1, "Date:", 5) == 0)
			{
				struct tm rtime;
				char * Context;
				char seps[] = " ,\t\r";
				char Offset[10] = "";
				int i, HH, MM;
				char Copy[500]="";

				// Copy message, so original isn't messed up by strtok
				
				memcpy(Copy, ptr1, linelen);

				ptr1 = Copy;

				memset(&rtime, 0, sizeof(struct tm));

				// Date: Tue, 9 Jun 2009 20:54:55 +0100

				ptr1 = strtok_s(&ptr1[5], seps, &Context);	// Skip Day
				ptr1 = strtok_s(NULL, seps, &Context);		// Day

				rtime.tm_mday = atoi(ptr1);

				ptr1 = strtok_s(NULL, seps, &Context);		// Month

				for (i=0; i < 12; i++)
				{
					if (strcmp(month[i], ptr1) == 0)
					{
						rtime.tm_mon = i;
						break;
					}
				}
		
				sscanf(Context, "%04d %02d:%02d:%02d%s",
					&rtime.tm_year, &rtime.tm_hour, &rtime.tm_min, &rtime.tm_sec, Offset);

				rtime.tm_year -= 1900;

				Date = mktime(&rtime) - (time_t)_MYTIMEZONE; 
				
				if (Date == (time_t)-1)
					Date = 0;
				else
				{
					if ((Offset[0] == '+') || (Offset[0] == '-'))
					{
						MM = atoi(&Offset[3]);
						Offset[3] = 0;
						HH = atoi(&Offset[1]);
						MM = MM + (60 * HH);

						if (Offset[0] == '+')
							Date -= (60*MM);
						else
							Date += (60*MM);


					}
				}
			}

			
			if (linelen)			// Not Null line
			{
				ptr1 = ptr2 + 2;		// Skip crlf
				goto Loop;
			}

			ptr1 = sockptr->MailBuffer;

			TidyString(MsgFrom);
			_strlwr(MsgFrom);

			MsgLen = (int)(sockptr->MailSize - (ptr2 - ptr1));

			B2Flag = CheckforMIME(sockptr, sockptr->MailBuffer, &ptr2, &MsgLen);	// Will reformat message if necessary. 

			CreatePOP3Message(MsgFrom, MsgTo, Msgtitle, Date, ptr2, MsgLen, B2Flag);

			free(sockptr->MailBuffer);
			sockptr->MailBufferSize=0;
			sockptr->MailBuffer=0;
			sockptr->MailSize = 0;

			sockptr->Flags &= ~GETTINGMESSAGE;

			if (sockptr->POP3MsgCount > sockptr->POP3MsgNum++)
			{
				sockprintf(sockptr, "RETR %d", sockptr->POP3MsgNum);

				sockptr->State = WaitingForRETRResponse;
			}
			else
			{
				sockptr->POP3MsgNum = 1;
				sockprintf(sockptr, "DELE %d", sockptr->POP3MsgNum);;
				sockptr->State = WaitingForDELEResponse;
			}

			return;
		}

		if ((sockptr->MailSize + Len) > sockptr->MailBufferSize)
		{
			sockptr->MailBufferSize += 10000;
			sockptr->MailBuffer = realloc(sockptr->MailBuffer, sockptr->MailBufferSize);
	
			if (sockptr->MailBuffer == NULL)
			{
				CriticalErrorHandler("Failed to extend Message Buffer");
				shutdown(sock, 0);
				return;
			}
		}

		memcpy(&sockptr->MailBuffer[sockptr->MailSize], Buffer, Len);
		sockptr->MailSize += Len;

		return;
	}

	if (sockptr->State == WaitingForGreeting)
	{
		if (memcmp(Buffer, "+OK", 3) == 0)
		{
			sockprintf(sockptr, "USER %s", ISPAccountName);
			sockptr->State = WaitingForUSERResponse;
		}
		else
		{
			SendSock(sockptr, "QUIT");
			sockptr->State = 0;
		}

		return;
	}

	if (sockptr->State == WaitingForUSERResponse)
	{
		if (memcmp(Buffer, "+OK", 3) == 0)
		{
			sockprintf(sockptr, "PASS %s", ISPAccountPass);
			sockptr->State = WaitingForPASSResponse;
		}
		else
		{
			SendSock(sockptr, "QUIT");
			sockptr->State = WaitingForQUITResponse;
		}

		return;
	}

	if (sockptr->State == WaitingForPASSResponse)
	{
		if (memcmp(Buffer, "+OK", 3) == 0)
		{
			SendSock(sockptr, "STAT");
			sockptr->State = WaitingForSTATResponse;
		}
		else
		{
			shutdown(sock, 0);
			sockptr->State = 0;
		}

		return;
	}

	if (sockptr->State == WaitingForSTATResponse)
	{
		if (memcmp(Buffer, "+OK", 3) == 0)
		{
			int Msgs = atoi(&Buffer[3]);
			
			if (Msgs > 0)
			{
				sockptr->POP3MsgCount = Msgs;
				sockptr->POP3MsgNum = 1;
				SendSock(sockptr, "RETR 1");

				sockptr->State = WaitingForRETRResponse;

			}
			else
			{
				SendSock(sockptr, "QUIT");
				sockptr->State = WaitingForQUITResponse;
			}
		}
		else
		{
			SendSock(sockptr, "QUIT");
			sockptr->State = WaitingForQUITResponse;
		}

		return;
	}

	if (sockptr->State == WaitingForRETRResponse)
	{
		if (memcmp(Buffer, "+OK", 3) == 0)
		{
			sockptr->MailBuffer=malloc(10000);
			sockptr->MailBufferSize=10000;

			if (sockptr->MailBuffer == NULL)
			{
				CriticalErrorHandler("Failed to create POP3 Message Buffer");
				SendSock(sockptr, "QUIT");
				sockptr->State = WaitingForQUITResponse;
				shutdown(sock, 0);

				return;
			}
	
			sockptr->Flags |= GETTINGMESSAGE;
		
		}
		else
		{
			SendSock(sockptr, "QUIT");
			sockptr->State = WaitingForQUITResponse;
		}

		return;
	}
	if (sockptr->State == WaitingForDELEResponse)
	{
		if (memcmp(Buffer, "+OK", 3) == 0)
		{
			if (sockptr->POP3MsgCount > sockptr->POP3MsgNum++)
			{
				sockprintf(sockptr, "DELE %d", sockptr->POP3MsgNum);;
			}
			else
			{
				SendSock(sockptr, "QUIT");
				sockptr->Flags = WaitingForQUITResponse;
			}
		}
		else
		{
			shutdown(sock,0);
			sockptr->State = 0;
		}
		return;
	}

	if (sockptr->State == WaitingForQUITResponse)
	{
		shutdown(sock,0);
		sockptr->State = 0;
		return;
	}

	SendSock(sockptr, "QUIT");
	shutdown(sock,0);
	sockptr->State = 0;

}

static char Winlink[] = "WINLINK.ORG";

int CreatePOP3Message(char * From, char * To, char * MsgTitle, time_t Date, char * MsgBody, int MsgLen, BOOL B2Flag)
{
	struct MsgInfo * Msg;
	BIDRec * BIDRec;

	// Allocate a message Record slot

	Msg = AllocateMsgRecord();
		
	// Set number here so they remain in sequence
		
	Msg->number = ++LatestMsg;
	MsgnotoMsg[Msg->number] = Msg;
	Msg->length = MsgLen;


	sprintf_s(Msg->bid, sizeof(Msg->bid), "%d_%s", LatestMsg, BBSName);

	Msg->type = 'P';
	Msg->status = 'N';
	Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);

	if (Date)
		Msg->datecreated = Date;

	BIDRec = AllocateBIDRecord();

	strcpy(BIDRec->BID, Msg->bid);
	BIDRec->mode = Msg->type;
	BIDRec->u.msgno = LOWORD(Msg->number);
	BIDRec->u.timestamp = LOWORD(time(NULL)/86400);


	TidyString(To);
	strlop(To, '@');

	// Could have surrounding "" ""

	if (To[0] == '"')
	{
		int len = (int)strlen(To) - 1;
		
		if (To[len] == '"')
		{
			To[len] = 0;
			memmove(To, &To[1], len);
		}
	}

	if (GMailMode)
	{
		// + separates our address and the target user

		char * GMailto;

		GMailto = strlop(To,'+');
		
		if (GMailto)
		{
			char * GmailVia = NULL;
		
			strcpy(To, GMailto);
			GmailVia = strlop(To, '|');

			if (GmailVia)
				strcpy(Msg->via, GmailVia);
		}
		else
		{
			// Someone has sent to the GMAIL account without a +. 
			// This should go to the BBS Call

			strcpy(To, BBSName);
		}
	}

	if ((_memicmp(To, "bull/", 5) == 0) || (_memicmp(To, "bull.", 5) == 0)
		|| (_memicmp(To, "bull:", 5) == 0))
	{
		Msg->type = 'B';
		memmove(To, &To[5], strlen(&To[4]));
	}

	if ((_memicmp(To, "nts/", 4) == 0) || (_memicmp(To, "nts.", 4) == 0)
		|| (_memicmp(To, "nts:", 4) == 0))
	{
		Msg->type = 'T';
		memmove(To, &To[4], strlen(&To[3]));
	}

	if (Msg->type == 'P' && Msg->via[0] == 0)
	{
		// No via - add one from HomeBBS or WP

		struct UserInfo * ToUser = LookupCall(To);

		if (ToUser)
		{
			// Local User. If Home BBS is specified, use it

			if (ToUser->flags & F_RMSREDIRECT)
			{
				// sent to Winlink
				
				strcpy(Msg->via, Winlink);
			}
			else if (ToUser->HomeBBS[0])
				strcpy(Msg->via, ToUser->HomeBBS);
		}
		else
		{
			WPRec * WP = LookupWP(To);

			if (WP)
				strcpy(Msg->via, WP->first_homebbs);
		}
	}

/*	if (_memicmp(To, "rms:", 4) == 0)
	{
		via = _strlwr(strlop(To, ':'));
	}
	else if (_memicmp(To, "rms/", 4) == 0)
	{
		via = _strlwr(strlop(To, '/'));
	}
	else if (_memicmp(To, "rms.", 4) == 0)
	{
		via = _strlwr(strlop(To, '.'));
	}
*/	


	if (strlen(To) > 6) To[6]=0;

	strcpy(Msg->to, To);
	strcpy(Msg->from, "smtp:");
	strcpy(Msg->emailfrom, From);
	strcpy(Msg->title, MsgTitle);

	if(Msg->to[0] == 0)
		SMTPMsgCreated=TRUE;

	if (B2Flag)
	{
		char B2Hddr[1000];
		int B2HddrLen;
		char B2To[80];
		char * NewBody;
		char DateString[80];
		struct tm * tm;
		char Type[16] = "Private";
					
		// Get Type
	
		if (Msg->type == 'B')
			strcpy(Type, "Bulletin");
		else if (Msg->type == 'T')
			strcpy(Type, "Traffic");


		tm = gmtime(&Date);	
	
		sprintf(DateString, "%04d/%02d/%02d %02d:%02d",
			tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);


		if (strcmp(Msg->to, "RMS") == 0)		// Address is in via
		strcpy(B2To, Msg->via);
	else
		if (Msg->via[0])
			sprintf(B2To, "%s@%s", Msg->to, Msg->via);
		else
			strcpy(B2To, Msg->to);

		
		Msg->B2Flags = B2Msg | Attachments;

		B2HddrLen = sprintf(B2Hddr,
			"MID: %s\r\nDate: %s\r\nType: %s\r\nFrom: %s\r\nTo: %s\r\nSubject: %s\r\nMbo: %s\r\n",
			Msg->bid, DateString, Type,
			Msg->from, B2To, Msg->title, BBSName);

		NewBody = MsgBody - B2HddrLen;

		memcpy(NewBody, B2Hddr, B2HddrLen);

		Msg->length += B2HddrLen;

		// Set up forwarding bitmap

		MatchMessagetoBBSList(Msg, 0);

		return CreateSMTPMessageFile(NewBody, Msg);
	}

	// Set up forwarding bitmap

	MatchMessagetoBBSList(Msg, 0);

	return CreateSMTPMessageFile(MsgBody, Msg);

}

VOID base64_encode(char *str, char * result, int len)
{
    unsigned int i = 0, j = 0;
	char *tmp = str;
	

	while (len  > 2 )
	{
		encodeblock(&str[i], &result[j],3);
		i+=3;
		j+=4;
		len -=3;
	}
	if (len)
	{
		encodeblock(&str[i], &result[j], len);
	}

	return;
}

void Base64EncodeAndSend(SocketConn * sockptr, UCHAR * Msg, int Len)
{
	char Base64Line[80];
	int i = Len;
	int j = 0;

	Base64Line[76] = 13;
	Base64Line[77] = 10;
	Base64Line[78] = 0;

	// Need to encode in 57 byte chunks to give 76 char lines.

	while(i > 57)
	{
		base64_encode(&Msg[j], Base64Line, 57);
		SendSock(sockptr, Base64Line);
	
		j += 57;
		i -= 57;
	}

	memset(Base64Line, 0, 79);

	base64_encode(&Msg[j], Base64Line, i);
	SendSock(sockptr, Base64Line);
	SendSock(sockptr, "");
}

VOID SendMultiPartMessage(SocketConn * sockptr, struct MsgInfo * Msg, UCHAR * msgbytes)
{
	char * ptr;		
	char Header[120];
	char Separator[33]="";
	char FileName[100][250] = {""};
	int FileLen[100];
	int Files = 0;
	int BodyLen;
	int i;

	CreateOneTimePassword(&Separator[0], "Key", 0); 
	CreateOneTimePassword(&Separator[16], "Key", 1); 

	SendSock(sockptr, "MIME-Version: 1.0");

	sprintf_s(Header, sizeof(Header), "Content-Type: multipart/mixed; boundary=\"%s\"", Separator);
	SendSock(sockptr, Header);

	SendSock(sockptr, "");							// Blank line before body

// Get Part Sizes and Filenames

	ptr = msgbytes;

	while(*ptr != 13)
	{
		char * ptr2 = strchr(ptr, 10);	// Find CR

		if (memcmp(ptr, "Body: ", 6) == 0)
		{
			BodyLen = atoi(&ptr[6]);
		}

		if (memcmp(ptr, "File: ", 6) == 0)
		{
			char * ptr1 = strchr(&ptr[6], ' ');	// Find Space

			FileLen[Files] = atoi(&ptr[6]);

			memcpy(FileName[Files++], &ptr1[1], (ptr2-ptr1 - 2));
		}
				
		ptr = ptr2;
		ptr++;
	}

	ptr += 2;			// Over Blank Line 

	// Write the none-Mime Part

	SendSock(sockptr, "This is a multi-part message in MIME format.");
	SendSock(sockptr, "");

	// Write the Body as the first part.

	sprintf_s(Header, sizeof(Header), "--%s", Separator);
	SendSock(sockptr, Header);
	SendSock(sockptr, "Content-Type: text/plain");
	SendSock(sockptr, "");

	ptr[BodyLen] = 0;

	SendSock(sockptr, ptr);

	ptr += BodyLen;		// to first file
	ptr += 2;			// Over Blank Line

	// Write Each Attachment

	for (i = 0; i < Files; i++)
	{
		sprintf_s(Header, sizeof(Header), "--%s", Separator);
		SendSock(sockptr, Header);
//		Content-Type: image/png; name="UserParams.png"
		SendSock(sockptr, "Content-Transfer-Encoding: base64");

		sprintf_s(Header, sizeof(Header), "Content-Disposition: attachment; filename=\"%s\"", FileName[i]);
		SendSock(sockptr, Header);

		SendSock(sockptr, "");

		// base64 encode and send file	

		Base64EncodeAndSend(sockptr, ptr, FileLen[i]);

		ptr += FileLen[i];
		ptr +=2;				// Over separator
	}
	
	sprintf_s(Header, sizeof(Header), "--%s--", Separator);
	SendSock(sockptr, Header);

	SendSock(sockptr, "");
	SendSock(sockptr, ".");

	free(msgbytes);

	return;
}

BOOL SendAMPRSMTP(CIRCUIT * conn)
{
	struct UserInfo * bbs = conn->UserPointer;

	while (FindMessagestoForward(conn))
	{
		if (SendtoAMPR(conn))
		{
			bbs->ForwardingInfo->Forwarding = TRUE;
			return TRUE;
		}
	}

	bbs->ForwardingInfo->Forwarding = FALSE;
	return FALSE;
}

