/*
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
*/	
#define _CRT_SECURE_NO_DEPRECATE 
#include "cheaders.h"
#include "bpqmail.h"
#define MAIL
#include "httpconnectioninfo.h"
#ifdef WIN32
//#include "C:\Program Files (x86)\GnuWin32\include\iconv.h"
#else
#include 
#include 
#endif
static struct HTTPConnectionInfo * FindSession(char * Key);
int APIENTRY SessionControl(int stream, int command, int param);
int SetupNodeMenu(char * Buff);
VOID SetMultiStringValue(char ** values, char * Multi);
char * GetTemplateFromFile(int Version, char * FN);
VOID FormatTime(char * Time, time_t cTime);
struct MsgInfo * GetMsgFromNumber(int msgno);
BOOL CheckUserMsg(struct MsgInfo * Msg, char * Call, BOOL SYSOP);
BOOL OkToKillMessage(BOOL SYSOP, char * Call, struct MsgInfo * Msg);
int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * FileName, char * XML, char * Reply, char * RawMessage, int RawLen);
struct HTTPConnectionInfo * AllocateWebMailSession();
VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen);
void ConvertTitletoUTF8(WebMailInfo * WebMail, char * Title, char * UTF8Title, int Len);
char *stristr (char *ch1, char *ch2);
char * ReadTemplate(char * FormSet, char * DirName, char * FileName);
VOID DoStandardTemplateSubsitutions(struct HTTPConnectionInfo * Session, char * txtFile);
BOOL CheckifPacket(char * Via);
int GetHTMLFormSet(char * FormSet);
void ProcessFormInput(struct HTTPConnectionInfo * Session, char * input, char * Reply, int * RLen, int InputLen);
char * WebFindPart(char ** Msg, char * Boundary, int * PartLen, char * End);
struct HTTPConnectionInfo * FindWMSession(char * Key);
int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Session, char * Alert);
char * BuildFormMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, 	char * Keys[1000], char * Values[1000], int NumKeys);
char * FindXMLVariable(WebMailInfo * WebMail, char * Var);
int ReplyToFormsMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Reply, BOOL Reenter);
BOOL ParsetxtTemplate(struct HTTPConnectionInfo * Session, struct HtmlFormDir * Dir, char * FN, BOOL isReply);
VOID UpdateFormAction(char * Template, char * Key);
BOOL APIENTRY GetAPRSLatLon(double * PLat,  double * PLon);
BOOL APIENTRY GetAPRSLatLonString(char * PLat,  char * PLon);
void FreeWebMailFields(WebMailInfo * WebMail);
VOID BuildXMLAttachment(struct HTTPConnectionInfo * Session, char * Keys[1000], char * Values[1000], int NumKeys);
VOID SaveTemplateMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
VOID DownloadAttachments(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest);
VOID getAttachmentList(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest);
char * BuildB2Header(WebMailInfo * WebMail, struct MsgInfo * Msg, char ** ToCalls, int Calls);
VOID FormatTime2(char * Time, time_t cTime);
VOID ProcessSelectResponse(struct HTTPConnectionInfo * Session, char * URLParams);
VOID ProcessAskResponse(struct HTTPConnectionInfo * Session, char * URLParams);
char * CheckFile(struct HtmlFormDir * Dir, char * FN);
VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL);
VOID SendTemplateSelectScreen(struct HTTPConnectionInfo * Session, char *URLParams, int InputLen);
BOOL isAMPRMsg(char * Addr);
char * doXMLTransparency(char * string);
Dll BOOL APIENTRY APISendAPRSMessage(char * Text, char * ToCall);
void SendMessageReadEvent(char * Call, struct MsgInfo * Msg);
void SendNewMessageEvent(char * call, struct MsgInfo * Msg);
void MQTTMessageEvent(void* message);
extern char NodeTail[];
extern char BBSName[10];
extern char LTFROMString[2048];
extern char LTTOString[2048];
extern char LTATString[2048];
 UCHAR BPQDirectory[260];
int LineCount = 35;					// Lines per page on message list
// Forms 
struct HtmlFormDir ** HtmlFormDirs = NULL;
int FormDirCount = 0;
struct HtmlForm
{
	char * FileName;
	BOOL HasInitial;
	BOOL HasViewer;
	BOOL HasReply;
	BOOL HasReplyViewer;
};
struct HtmlFormDir
{
	char * FormSet;
	char * DirName;
	struct HtmlForm ** Forms;
	int FormCount;
	struct HtmlFormDir ** Dirs;		// Nested Directories
	int DirCount;
};
char FormDirList[4][MAX_PATH] = {"Standard_Templates", "Standard Templates", "Local_Templates"};
static char PassError[] = "Sorry, User or Password is invalid - please try again
";
static char BusyError[] = "Sorry, No sessions available - please try later
";
extern char MailSignon[];
char WebMailSignon[] = "BPQ32 Mail Server Access "
	"BPQ32 Mail Server %s Access "
	"Please enter Callsign and Password to access WebMail "
	"";
static char MsgInputPage[] = "Webmail Interface - Message Input Form "
	"";
static char CheckFormMsgPage[] = "Webmail Forms Interface - Check Message "
	""
	" ";
extern char * WebMailTemplate;
extern char * WebMailMsgTemplate;
extern char * jsTemplate;
static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
char *longday[] = {"Sunday", "Monday", "Tusday", "Wednesday", "Thusday", "Friday", "Saturday"};
static struct HTTPConnectionInfo * WebSessionList = NULL;	// active WebMail sessions
#ifdef LINBPQ
UCHAR * GetBPQDirectory();
#endif
void UndoTransparency(char * input);
#ifndef LINBPQ
void UndoTransparency(char * input)
{
	char * ptr1, * ptr2;
	char c;
	int hex;
	if (input == NULL)
		return;
	ptr1 = ptr2 = input;
	// Convert any %xx constructs
	while (1)
	{
		c = *(ptr1++);
		if (c == 0)
			break;
		if (c == '%')
		{
			c = *(ptr1++);
			if(isdigit(c))
				hex = (c - '0') << 4;
			else
				hex = (tolower(c) - 'a' + 10) << 4;
			c = *(ptr1++);
			if(isdigit(c))
				hex += (c - '0');
			else
				hex += (tolower(c) - 'a' + 10);
			*(ptr2++) = hex;
		}
		else if (c == '+')
			*(ptr2++) = 32;
		else
			*(ptr2++) = c;
	}
	*ptr2 = 0;
}
#endif
void ReleaseWebMailStruct(WebMailInfo * WebMail)
{
	// release any malloc'ed resources
	if (WebMail == NULL)
		return;
	FreeWebMailFields(WebMail);
	free(WebMail);
	return;
}
VOID FreeWebMailMallocs()
{
	// called when closing. Not really needed, but simplifies tracking down real memory leaks
	struct HTTPConnectionInfo * Session, * SaveNext;
	int i;
	Session = WebSessionList;
	while (Session)
	{
		SaveNext = Session->Next;
			
		// Release amy malloc'ed resouces
			
		ReleaseWebMailStruct(Session->WebMail);
		free(Session);
		Session = SaveNext;
	}
	for (i = 0; i < FormDirCount; i++)
	{
		struct HtmlFormDir * Dir = HtmlFormDirs[i];
		int j;
		for (j = 0; j < Dir->FormCount; j++)
		{
			free(Dir->Forms[j]->FileName);
			free(Dir->Forms[j]);
		}
		if (Dir->DirCount)
		{
			struct HtmlFormDir * SubDir;
			int k, l;
			for (l = 0; l < Dir->DirCount; l++)
			{
				SubDir = Dir->Dirs[l];
		
				for (k = 0; k < Dir->Dirs[l]->FormCount; k++)
				{
					free(SubDir->Forms[k]->FileName);
					free(SubDir->Forms[k]);
				}
				free(SubDir->DirName);
				free(SubDir->Forms);
				free(SubDir->FormSet);
				free(Dir->Dirs[l]);
			}
		}
		free(Dir->DirName);
		free(Dir->Forms);
		free(Dir->FormSet);
		free(Dir);
	}
	free(HtmlFormDirs);
	return;
}
char * initMultipartUnpack(char ** Input)
{
	// Check if Multipart and return Boundary. Update Input to first part
	// look through header for Content-Type line, and if multipart
	// find boundary string.
	char * ptr, * ptr2;
	char Boundary[128];
	BOOL Multipart = FALSE;
	ptr = *Input;
	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
	
		if (_memicmp(ptr, "Content-Type: ", 14) == 0)
		{
			char Line[256] = "";
			char * ptr3;
			size_t len = ptr2-ptr-14;
			if (len >255)
				return NULL;
			memcpy(Line, &ptr[14], len);
			if (_memicmp(Line, "Multipart/", 10) == 0)
			{
				ptr3 = stristr(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;
					break;
				}
				else
					return NULL;						// Can't do anything without a boundary ??
			}
		}
		ptr = ptr2;
		ptr++;
	}
	// Find First part - there is a boundary before it
	ptr = strstr(ptr2, Boundary);
	// Next should be crlf then part
	ptr = strstr(ptr, "\r\n");
	if (ptr)
		ptr += 2;		// Over CRLF
	*Input = ptr;		// Return first part or NULL	
	return _strdup(Boundary);
}
BOOL unpackPart(char * Boundary, char ** Input, char ** Name, char ** Value, int * ValLen, char * End)
{
	// Format seems to be
/*
		------WebKitFormBoundaryABJaEbBWB5SuAHmq
		Content-Disposition: form-data; name="Subj"
		subj
		------WebKitFormBoundaryABJaEbBWB5SuAHmq
		Content-Disposition: form-data; name="myFile[]"; filename="exiftool.txt"
		Content-Type: text/plain
		c:\exiftool "-filenameTo = _strdup(Value);
	else if (strcmp(Name, "CC") == 0)
		WebMail->CC = _strdup(Value);
	else if (strcmp(Name, "Subj") == 0)
		WebMail->Subject = _strdup(Value);
	else if (strcmp(Name, "Type") == 0)
		WebMail->Type = Value[0];
	else if (strcmp(Name, "BID") == 0)
		WebMail->BID = _strdup(Value);
	else if (strcmp(Name, "Msg") == 0)
		WebMail->Body = _strdup(Value);
	else if (_memicmp(Name, "myFile[]", 8) == 0)
	{
		// Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain
		char * fn = strstr(Name, "filename=");
		char * endfn;
		if (fn)
		{
			fn += 10;
			endfn = strchr(fn, '"');
			if (endfn)
			{
				*endfn = 0;
				if (strlen(fn))
				{
					WebMail->FileName[WebMail->Files] = _strdup(fn);
					WebMail->FileBody[WebMail->Files] = malloc(ValLength);
					memcpy(WebMail->FileBody[WebMail->Files], Value, ValLength);
					WebMail->FileLen[WebMail->Files++] = ValLength;
				}
			}
		}
	}
	else if (_memicmp(Name, "myFile2[]", 8) == 0)
	{
		// Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain
		char * fn = strstr(Name, "filename=");
		char * endfn;
		if (fn)
		{
			fn += 10;
			endfn = strchr(fn, '"');
			if (endfn)
			{
				*endfn = 0;
				if (strlen(fn))
				{
					WebMail->Header = malloc(ValLength + 1);
					memcpy(WebMail->Header, Value, ValLength + 1);
					WebMail->HeaderLen = RemoveLF(WebMail->Header, ValLength);
				}
			}
		}
	}
	else if (_memicmp(Name, "myFile3[]", 8) == 0)
	{
		// Get File Name from param string - myFile[]"; filename="exiftool.txt" \r\nContent-Type: text/plain
		char * fn = strstr(Name, "filename=");
		char * endfn;
		if (fn)
		{
			fn += 10;
			endfn = strchr(fn, '"');
			if (endfn)
			{
				*endfn = 0;
				if (strlen(fn))
				{
					WebMail->Footer = malloc(ValLength + 1);
					memcpy(WebMail->Footer, Value, ValLength + 1);
					WebMail->FooterLen = RemoveLF(WebMail->Footer, ValLength);
				}
			}
		}
	}
	return TRUE;
}
struct HTTPConnectionInfo * AllocateWebMailSession()
{
	int KeyVal;
	struct HTTPConnectionInfo * Session, * SaveNext;
	time_t NOW = time(NULL);
	//	First see if any session records havent been used for a while
	Session = WebSessionList;
	while (Session)
	{
		if (NOW - Session->WebMailLastUsed > 1200)	// 20 Mins
		{
			SaveNext = Session->Next;
			
			// Release amy malloc'ed resouces
			
			ReleaseWebMailStruct(Session->WebMail);
			memset(Session, 0, sizeof(struct HTTPConnectionInfo));
	
			Session->Next = SaveNext;
			goto UseThis;
		}
		Session = Session->Next;
	}
	
	Session = zalloc(sizeof(struct HTTPConnectionInfo));
	
	if (Session == NULL)
		return NULL;
	if (WebSessionList)
		Session->Next = WebSessionList;
	WebSessionList = Session;
UseThis:
	Session->WebMail = zalloc(sizeof(WebMailInfo));
	KeyVal = ((rand() % 100) + 1);
	KeyVal *= (int)time(NULL);
	sprintf(Session->Key, "%c%08X", 'W', KeyVal);
	return Session;
}
struct HTTPConnectionInfo * FindWMSession(char * Key)
{
	struct HTTPConnectionInfo * Session = WebSessionList;
	while (Session)
	{
		if (strcmp(Session->Key, Key) == 0)
		{
			Session->WebMailLastUsed = time(NULL);
			return Session;
		}
		Session = Session->Next;
	}
	return NULL;
}
// Build list of available forms
VOID ProcessFormDir(char * FormSet, char * DirName, struct HtmlFormDir *** xxx, int * DirCount)
{
	struct HtmlFormDir * FormDir;
	struct HtmlFormDir ** FormDirs = *xxx;
	struct HtmlForm * Form;
	char Search[MAX_PATH];
	int count = *DirCount;
#ifdef WIN32
	HANDLE hFind = INVALID_HANDLE_VALUE;
	WIN32_FIND_DATA ffd;
#else
	DIR *dir;
	struct dirent *entry;
	char name[256];
#endif
	FormDir = zalloc(sizeof (struct HtmlFormDir));
	FormDir->DirName = _strdup(DirName);
	FormDir->FormSet = _strdup(FormSet);
	FormDirs=realloc(FormDirs, (count + 1) * sizeof(void *));
	FormDirs[count++] = FormDir;
	
	*DirCount = count;
	*xxx = FormDirs;
	// Scan Directory for .txt files
	sprintf(Search, "%s/%s/%s/*", GetBPQDirectory(), FormSet, DirName);
	// Find the first file in the directory.
#ifdef WIN32
	hFind = FindFirstFile(Search, &ffd);
	if (INVALID_HANDLE_VALUE == hFind) 
		return;
	do
	{
		if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			char Dir[MAX_PATH];
			if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0)
                continue;
			// Recurse in subdir
			sprintf(Dir, "%s/%s", DirName, ffd.cFileName);
			ProcessFormDir(FormSet, Dir, &FormDir->Dirs, &FormDir->DirCount);
			continue;
		}
	
		// Add to list
		Form = zalloc(sizeof (struct HtmlForm));
		Form->FileName = _strdup(ffd.cFileName);
		FormDir->Forms=realloc(FormDir->Forms, (FormDir->FormCount + 1) * sizeof(void *));
		FormDir->Forms[FormDir->FormCount++] = Form;       
	}
	while (FindNextFile(hFind, &ffd) != 0);
 
	FindClose(hFind);
#else
	sprintf(Search, "%s/%s/%s", GetBPQDirectory(), FormSet, DirName);
	if (!(dir = opendir(Search)))
	{
		Debugprintf("%s %d %d", "cant open forms dir", errno, dir);
        return ;
	}
    while ((entry = readdir(dir)) != NULL)
	{
        if (entry->d_type == DT_DIR)
		{
			char Dir[MAX_PATH];
			if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
                continue;
			// Recurse in subdir
			sprintf(Dir, "%s/%s", DirName, entry->d_name);
			ProcessFormDir(FormSet, Dir, &FormDir->Dirs, &FormDir->DirCount);
			continue;
		}
		// Add to list
		Form = zalloc(sizeof (struct HtmlForm));
		Form->FileName = _strdup(entry->d_name);
		FormDir->Forms=realloc(FormDir->Forms, (FormDir->FormCount + 1) * sizeof(void *));
		FormDir->Forms[FormDir->FormCount++] = Form;
    }
    closedir(dir);
#endif
	return;
}
int GetHTMLForms()
{
	int n = 0;
	while (FormDirList[n][0])
		GetHTMLFormSet(FormDirList[n++]);
	return 0;
}
int GetHTMLFormSet(char * FormSet)
{
	int i;
#ifdef WIN32
	WIN32_FIND_DATA ffd;
	char szDir[MAX_PATH];
	HANDLE hFind = INVALID_HANDLE_VALUE;
	DWORD dwError=0;
   	sprintf(szDir, "%s/%s/*", BPQDirectory, FormSet);
	// Find the first file in the directory.
	hFind = FindFirstFile(szDir, &ffd);
	if (INVALID_HANDLE_VALUE == hFind) 
	{
		// Accept either 
		return 0;
	}
	// Scan all directories looking for file
	
	do
	{
		if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0)
                continue;
			// Add to Directory List
			ProcessFormDir(FormSet, ffd.cFileName, &HtmlFormDirs, &FormDirCount);
		}
	}
	
	while (FindNextFile(hFind, &ffd) != 0);
	FindClose(hFind);
#else
	DIR *dir;
	struct dirent *entry;
	char name[256];
	
   	sprintf(name, "%s/%s", BPQDirectory, FormSet);
	if (!(dir = opendir(name)))
	{
		Debugprintf("cant open forms dir %s %d %d", name, errno, dir);
	}
	else
	{
		while ((entry = readdir(dir)) != NULL)
		{
			if (entry->d_type == DT_DIR)
			{
				if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
					continue;
				// Add to Directory List
				ProcessFormDir(FormSet, entry->d_name, &HtmlFormDirs, &FormDirCount);
			}
		}
		closedir(dir);
	}
#endif
	// List for testing
	return 0;
	Debugprintf("%d form dirs", FormDirCount);
	for (i = 0; i < FormDirCount; i++)
	{
		struct HtmlFormDir * Dir = HtmlFormDirs[i];
		int j;
		Debugprintf("%3d %s", Dir->FormCount, Dir->DirName);
		for (j = 0; j < Dir->FormCount; j++)
			Debugprintf("   %s", Dir->Forms[j]->FileName);
		if (Dir->DirCount)
		{
			int k, l;
			for (l = 0; l < Dir->DirCount; l++)
			{
				Debugprintf("Subdir %3d %s", Dir->Dirs[l]->DirCount, Dir->Dirs[l]->DirName);
				for (k = 0; k < Dir->Dirs[l]->FormCount; k++)
					Debugprintf("       %s", Dir->Dirs[l]->Forms[k]->FileName);
			}
		}
	}
	return 0;
}
static int compare(const void *arg1, const void *arg2)
{
   // Compare Calls. Fortunately call is at start of stuct
   return _stricmp(*(char**)arg1 , *(char**)arg2);
}
int SendWebMailHeader(char * Reply, char * Key, struct HTTPConnectionInfo * Session)
{
	return SendWebMailHeaderEx(Reply, Key, Session, NULL);
}
int SendWebMailHeaderEx(char * Reply, char * Key, struct HTTPConnectionInfo * Session, char * Alert)
{
 	// Ex includes an alert string to be sent before message
	
	struct UserInfo * User = Session->User;
	char Messages[245000];
	int m;
	struct MsgInfo * Msg;
	char * ptr = Messages;
	int n = NumberofMessages; //LineCount;
	char Via[64];
	int Count = 0;
	Messages[0] = 0;
	if (Alert && Alert[0])
		ptr += sprintf(Messages, "", Alert, Key); 
	ptr += sprintf(ptr, "%s", "     #  Date  XX   Len To      @       From    Subject\r\n\r\n");
	for (m = LatestMsg; m >= 1; m--)
	{
		if (ptr > &Messages[244000])
			break;						// protect buffer
		Msg = GetMsgFromNumber(m);
		if (Msg == 0 || Msg->type == 0 || Msg->status == 0)
			continue;					// Protect against corrupt messages
		
		if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP))
		{
			char UTF8Title[4096];
			char  * EncodedTitle;
			
			// List if it is the right type and in the page range we want
			if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) 
				continue;
			// All Types or right Type. Check Mine Flag
			if (Session->WebMailMine)
			{
				// Only list if to or from me
				if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0)
					continue;
			}
			if (Session->WebMailMyTX)
			{
				// Only list if to or from me
				if (strcmp(User->Call, Msg->from) != 0)
					continue;
			}
			if (Session->WebMailMyRX)
			{
				// Only list if to or from me
				if (strcmp(User->Call, Msg->to)!= 0)
					continue;
			}
			if (Count++ < Session->WebMailSkip)
				continue;
			strcpy(Via, Msg->via);
			strlop(Via, '.');
			// make sure title is HTML safe (no < > etc) and UTF 8 encoded
			EncodedTitle = doXMLTransparency(Msg->title);
			memset(UTF8Title, 0, 4096);		// In case convert fails part way through
			ConvertTitletoUTF8(Session->WebMail, EncodedTitle, UTF8Title, 4095);
			free(EncodedTitle);
			
			ptr += sprintf(ptr, "%6d  %s %c%c %5d %-8s%-8s%-8s%s\r\n",
				Key, Msg->number, Msg->number,
				FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type,
				Msg->status, Msg->length, Msg->to, Via,
				Msg->from, UTF8Title);
			n--;
			if (n == 0)
				break;
		}
	}
	if (WebMailTemplate == NULL)
		WebMailTemplate = GetTemplateFromFile(6, "WebMailPage.txt");
	return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Key, Key, Key, Messages);
}
int ViewWebMailMessage(struct HTTPConnectionInfo * Session, char * Reply, int Number, BOOL DisplayHTML)
{
	char * Key = Session->Key;
	struct UserInfo * User = Session->User;
	WebMailInfo * WebMail = Session->WebMail;
	char * DisplayStyle;
	char Message[200000] = "";
	struct MsgInfo * Msg;
	char * ptr = Message;
	char * MsgBytes, * Save;
	int msgLen;
	char FullTo[100];
	char UTF8Title[4096];
	int Index;
	char * crcrptr;
	char DownLoad[256] = "";
	DisplayStyle = "textarea";			// Prevents interpretation of html and xml
	Msg = GetMsgFromNumber(Number);
	if (Msg == NULL)
	{
		ptr += sprintf(ptr, "Message %d not found\r\n", Number);
		return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Message);
	}
	// New Display so free any old values
	FreeWebMailFields(WebMail);	
	WebMail->CurrentMessageIndex = Number;
	if (!CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP))
	{
		ptr += sprintf(ptr, "Message %d not for you\r", Number);
		return sprintf(Reply, WebMailTemplate, BBSName, User->Call, Key, Key, Key, Key, Key, Key, Key, Message);
	}
	if (_stricmp(Msg->to, "RMS") == 0)
		sprintf(FullTo, "RMS:%s", Msg->via);
	else
		if (Msg->to[0] == 0)
			sprintf(FullTo, "smtp:%s", Msg->via);
		else
			strcpy(FullTo, Msg->to);
	// make sure title is UTF 8 encoded
	memset(UTF8Title, 0, 4096);		// In case convert fails part way through
	ConvertTitletoUTF8(Session->WebMail, Msg->title, UTF8Title, 4095);
	// if a B2 message diplay B2 Header instead of a locally generated one
	if ((Msg->B2Flags & B2Msg) == 0)
	{
		ptr += sprintf(ptr, "From: %s%s\nTo: %s\nType/Status: %c%c\nDate/Time: %s\nBid: %s\nTitle: %s\n\n",
			Msg->from, Msg->emailfrom, FullTo, Msg->type, Msg->status, FormatDateAndTime((time_t)Msg->datecreated, FALSE), Msg->bid, UTF8Title);
	}
	MsgBytes = Save = ReadMessageFile(Number);
	msgLen = Msg->length;
	if (Msg->type == 'P')
		Index = PMSG;
	else if (Msg->type == 'B')
		Index = BMSG;
	else 
		Index = TMSG;
	if (MsgBytes)
	{
		if (Msg->B2Flags & B2Msg)
		{
			char * ptr1;
			// if message has attachments, display them if plain text
			if (Msg->B2Flags & Attachments)
			{
				int BodyLen, NewLen;
				int i;
				char *ptr2, *attptr;	
				sprintf(DownLoad, "Save Attachments WebMail  \r\n"
			"\r\n"
			"\r\n"
			" %s Webmail Interface - User %s - Message List \r\n"
			"\r\n"
			"Waiting for data...
\r\n"
			"\r\n";
		sprintf(Page, WebSockPage, Key, Key ,BBSName, Session->User->Call, Key, Key, Key, Key, Key, Key, Key, Key, Key, Key);
		*RLen = sprintf(Reply, "%s", Page);
		return;
	}
	
	if (memcmp(NodeURL, "/WebMail/QuoteOriginal/", 15) == 0)
	{
		// Reply to Message
		int n, len;
		struct MsgInfo * Msg;
		char Message[100] = "";
		char Title[100];
		char * MsgBytes, * Save, * NewBytes;
		char * ptr;
		char * ptr1, * ptr2;
		char * EncodedTitle;
		n = Session->WebMail->CurrentMessageIndex;
	
		Msg = GetMsgFromNumber(n);
		if (Msg == NULL)
		{
			sprintf(Message, "Message %d not found", n);
			*RLen = sprintf(Reply, "%s", Message);
			return;
		}
		Session->WebMail->Msg = Msg;
		if (stristr(Msg->title, "Re:") == 0)
			sprintf(Title, "Re:%s", Msg->title);
		else
			sprintf(Title, "%s", Msg->title);
		MsgBytes = Save = ReadMessageFile(n);
		ptr = NewBytes = malloc((Msg->length * 2) + 256);
		// Copy a line at a time with "> " in front of each
		ptr += sprintf(ptr, "%s", "\r\n\r\n\r\n\r\n\r\nOriginal Message\r\n\r\n> ");
		ptr1 = ptr2 = MsgBytes;
		len  = (int)strlen(MsgBytes);
		while (len-- > 0)
		{
			*ptr++ = *ptr1;
	
			if (*(ptr1) == '\n')
			{
				*ptr++ = '>';
				*ptr++ = ' ';
			}
		
			ptr1++;
		}
		*ptr++ = 0;
		EncodedTitle = doXMLTransparency(Msg->title);
		*RLen = sprintf(Reply, MsgInputPage, Key, Msg->from, "", EncodedTitle , NewBytes);
		free(EncodedTitle);
		free(MsgBytes);
		free(NewBytes);
		return;
	}
	if (memcmp(NodeURL, "/WebMail/Reply/", 15) == 0)
	{
		// Reply to Message
		int n = atoi(&NodeURL[15]);
		struct MsgInfo * Msg;
		char Message[100] = "";
		char Title[100];
		char * EncodedTitle;
				
		// Quote Original
		
		char Button[] = 
			"      "
			""
			"Select Required Template from %s"
			"No Page Selected";
			
		struct HtmlFormDir * Dir;
		int i;
		int len;
		SubDir = strlop(&NodeURL[17], ':');
		DirNo = atoi(&NodeURL[17]);
		if (DirNo == -1)
		{
			// User has gone back, then selected "No Folder Selected"
			// For now just stop crash
			DirNo = 0;
		}
		if (SubDir)
			SubDirNo = atoi(SubDir);
		Dir = HtmlFormDirs[DirNo];
		if (SubDir)
			len = sprintf(popup, popuphddr, Key, Dir->Dirs[SubDirNo]->DirName);
		else
			len = sprintf(popup, popuphddr, Key, Dir->DirName);
		if (SubDir)
		{
			for (i = 0; i < Dir->Dirs[SubDirNo]->FormCount; i++)
			{		
				char * Name = Dir->Dirs[SubDirNo]->Forms[i]->FileName;
				// We only send if there is a .txt file
				if (_stricmp(&Name[strlen(Name) - 4], ".txt") == 0)
					len += sprintf(&popup[len], "  %s", DirNo, SubDirNo, i, Name);
			}
		}
		else
		{
			for (i = 0; i < Dir->FormCount; i++)
			{
				char * Name = Dir->Forms[i]->FileName;
				// We only send if there is a .txt file
				if (_stricmp(&Name[strlen(Name) - 4], ".txt") == 0)
					len += sprintf(&popup[len], "  %s", DirNo, i, Name);
			}
		}
		len += sprintf(&popup[len], "  
");
		*RLen = sprintf(Reply, "%s", popup);
		return;
	}
	
	if (_stricmp(NodeURL, "/WebMail/DL") == 0)
	{
		getAttachmentList(Session, Reply, RLen, URLParams);
		return;
	}
	if (_stricmp(NodeURL, "/WebMail/GetDownLoad") == 0)
	{
		DownloadAttachments(Session, Reply, RLen, URLParams);
		return;
	}
	if (_stricmp(NodeURL, "/WebMail/DoSelect") == 0)
	{
		// User has selected item from Template  field
		ProcessSelectResponse(Session, URLParams);
		return;
	}
	// Unrecognised message - reset session
	*RLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
}
VOID SendTemplateSelectScreen(struct HTTPConnectionInfo * Session, char *Params, int InputLen)
{
	// Save any supplied message fields and Send HTML Template dropdown list
	char popuphddr[] = 
			
		""
		""
		""
		" Select Required Template Folder from List
"
		""
		"Standard Templates "
		"Local Templates "
		" "
		"";
	char NewGroup [] =
		" "
		"";
	char popup[10000];
	struct HtmlFormDir * Dir;
	char * LastGroup;
	char * Input = strstr(Params, "\r\n\r\n"); // To end of HTML header
	int i;
	int MsgLen = 0;
	char * Boundary;
	int len;
				
	WebMailInfo * WebMail = Session->WebMail;
	Input = Params;
	Boundary = initMultipartUnpack(&Input);
	if (Boundary == NULL)
		return;	// Can't work without one
	// Input points to start of part. Normally preceeded by \r\n which is Boundary Terminator. If preceeded by -- we have used last part
	while(Input && Input[-1] != '-')
	{
		char * Name, * Value;
		int ValLen;
		
		if (unpackPart(Boundary, &Input, &Name, &Value, &ValLen, Params + InputLen) == FALSE)
		{
//			ReportCorrupt(WebMail);
			free(Boundary);
			return;
		}
		SaveInputValue(WebMail, Name, Value, ValLen);
	}
	strlop(WebMail->BID, ' ');
	if (strlen(WebMail->BID) > 12)
		WebMail->BID[12] = 0;
	UndoTransparency(WebMail->To);
	UndoTransparency(WebMail->CC);
	UndoTransparency(WebMail->BID);
	UndoTransparency(WebMail->Subject);
	UndoTransparency(WebMail->Body);
	// Save values from message when Template requested (npt sure if we need these!
	WebMail->OrigTo = _strdup(WebMail->To);
	WebMail->OrigSubject = _strdup(WebMail->Subject);
	WebMail->OrigType = WebMail->Type;
	WebMail->OrigBID = _strdup(WebMail->BID);
	WebMail->OrigBody = _strdup(WebMail->Body);
	// Also to active fields in case not changed by form
	len = sprintf(popup, popuphddr, Session->Key);
	LastGroup = HtmlFormDirs[0]->FormSet;		// Save so we know when changes
	for (i = 0; i < FormDirCount; i++)
	{
		int n;
			
		Dir = HtmlFormDirs[i];
		if (strcmp(LastGroup, Dir->FormSet) != 0)
		{
			LastGroup = Dir->FormSet;
			len += sprintf(&popup[len], "%s", NewGroup);
		}
		len += sprintf(&popup[len], " %s", i, Dir->DirName);
			
		// Recurse any Subdirs
			
		n = 0;
		while (n < Dir->DirCount)
		{
			len += sprintf(&popup[len], "  %s", i, n, Dir->Dirs[n]->DirName);
			n++;
		}
	}
	len += sprintf(&popup[len], "%   
    
");
	*WebMail->RLen = sprintf(WebMail->Reply, "%s", popup);
	free(Boundary);
	return;
}
static char WinlinkAddr[] = "WINLINK.ORG";
VOID SaveNewMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest, int InputLen)
{
	int i, ReplyLen = 0;
	struct MsgInfo * Msg;
	FILE * hFile;
	int Template=0;
	char * via = NULL;
	BIDRec * BIDRec;
	char MsgFile[MAX_PATH];
	size_t WriteLen=0;
	char * HDest;
	char * HDestCopy;
	char * HDestRest;
	char * Vptr = NULL;
	char * FileList = NULL;
	char Prompt[256] = "Message Saved";
	char OrigTo[256];
	WebMailInfo * WebMail = Session->WebMail;
	struct UserInfo * user;
	CIRCUIT  conn;
	// So we can have attachments input is now Content-Type: multipart/form-data;
	char * Input; 
	size_t MsgLen = 0;
	char * Boundary;		
	strcpy(conn.Callsign, Session->User->Call);
	Input = MsgPtr;
	Boundary = initMultipartUnpack(&Input);
	if (Boundary == NULL)
		return;	// Can't work without one
	// Input points to start of part. Normally preceeded by \r\n which is Boundary Terminator. If preceeded by -- we have used last part
	while(Input && Input[-1] != '-')
	{
		char * Name, * Value;
		int ValLen;
		if (unpackPart(Boundary, &Input, &Name, &Value, &ValLen, MsgPtr + InputLen) == FALSE)
		{
			//			ReportCorrupt(WebMail);
			free(Boundary);
			return;
		}
		if (SaveInputValue(WebMail, Name, Value, ValLen) == FALSE)
		{
			*RLen = sprintf(Reply, "", Session->Key);
			return;
		}
	}
	if (WebMail->txtFileName)
	{
		// Processing Form Input
		SaveTemplateMessage(Session, MsgPtr, Reply, RLen, Rest);
		// Prevent re-entry
		free(WebMail->txtFileName);
		WebMail->txtFileName = NULL;
		return;
	}
	// If we aren't using a template then all the information is in the WebMail fields, as we haven't been here before.
	strlop(WebMail->BID, ' ');
	if (strlen(WebMail->BID) > 12)
		WebMail->BID[12] = 0;
	UndoTransparency(WebMail->BID);
	UndoTransparency(WebMail->To);
	UndoTransparency(WebMail->Subject);
	UndoTransparency(WebMail->Body);
	MsgLen = strlen(WebMail->Body);
	// We will need to mess about with To field. Create a copy so the original can go in B2 header if we use one
	if (WebMail->To[0] == 0)
	{
		*RLen = sprintf(Reply, "%s", "");
		FreeWebMailFields(WebMail);		// We will reprocess message and attachments so reinitialise
		return;
	}
	if (strlen(WebMail->To) > 255)
	{
		*RLen = sprintf(Reply, "%s", "");
		FreeWebMailFields(WebMail);		// We will reprocess message and attachments so reinitialise
		return;
	}
	HDest = _strdup(WebMail->To);
	if (strlen(WebMail->BID))
	{		
		if (LookupBID(WebMail->BID))
		{
			// Duplicate bid
			*RLen = sprintf(Reply, "%s", "");
			FreeWebMailFields(WebMail);		// We will reprocess message and attachments so reinitialise
			return;
		}
	}
	if (WebMail->Type == 'B')
	{
		if (RefuseBulls)
		{
			*RLen = sprintf(Reply, "%s", "");
			FreeWebMailFields(WebMail);		// We will reprocess message and attachments so reinitialise
			return;
		}
	}
	// ?? Can we just loop though the rest of the code to allow multiple dests ??
	HDestCopy = HDest;
	while (HDest && HDest[0])
	{
		HDestRest = strlop(HDest, ';');
		Msg = AllocateMsgRecord();
		// Set number here so they remain in sequence
		Msg->number = ++LatestMsg;
		MsgnotoMsg[Msg->number] = Msg;
		strcpy(Msg->from, Session->User->Call);
		if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0)
		{
			Vptr=&HDest[4];
			strcpy(Msg->to, "RMS");
		}
		else if (_memicmp(HDest, "smtp:", 5) == 0)
		{
			if (ISP_Gateway_Enabled)
			{
				Vptr=&HDest[5];
				Msg->to[0] = 0;
			}
		}
		else if (strchr(HDest, '@'))
		{
			strcpy(OrigTo, HDest);
			Vptr = strlop(HDest, '@');
			if (Vptr)
			{
				// If looks like a valid email address, treat as such
				if (strlen(HDest) > 6 || !CheckifPacket(Vptr))
				{
					// Assume Email address
					Vptr = OrigTo;
					if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route
						strcpy(Msg->to, "RMS");
					else if (ISP_Gateway_Enabled)
						Msg->to[0] = 0;
					else if (isAMPRMsg(OrigTo))
						strcpy(Msg->to, "RMS");		// Routing will redirect it
					else
					{		
						*RLen = sprintf(Reply, "%s", "");
						FreeWebMailFields(WebMail);		// We will reprocess message and attachments so reinitialise
						return;
					}
				}
				else
					strcpy(Msg->to, _strupr(HDest));
			}
		}
		else
		{
			// No @
			if (strlen(HDest) > 6)
				HDest[6] = 0;
			strcpy(Msg->to, _strupr(HDest));
		}
		if (SendBBStoSYSOPCall)
			if (_stricmp(HDest, BBSName) == 0)
				strcpy(Msg->to, SYSOPCall);
		if (Vptr)
		{
			if (strlen(Vptr) > 40)
				Vptr[40] = 0;
			strcpy(Msg->via, _strupr(Vptr));
		}
		else
		{
			// No via. If not local user try to add BBS 
			struct UserInfo * ToUser = LookupCall(Msg->to);
			if (ToUser)
			{
				// Local User. If Home BBS is specified, use it
				if (ToUser->flags & F_RMSREDIRECT)
				{
					// sent to Winlink
				
					strcpy(Msg->via, WinlinkAddr);
					sprintf(Prompt, "Redirecting to winlink.org\r");
				}
				else if (ToUser->HomeBBS[0])
				{
					strcpy(Msg->via, ToUser->HomeBBS);
					sprintf(Prompt, "%s added from HomeBBS. Message Saved", Msg->via);
				}
			}
			else
			{
				// Not local user - Check WP
				WPRecP WP = LookupWP(Msg->to);
				if (WP)
				{
					strcpy(Msg->via, WP->first_homebbs);
					sprintf(Prompt, "%s added from WP", Msg->via);
				}
			}
		}
		if (strlen(WebMail->Subject) > 60)
			WebMail->Subject[60] = 0;
		strcpy(Msg->title, WebMail->Subject);
		Msg->type = WebMail->Type;
		if (Session->User->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);
		}
		else
			Msg->status = 'N';
		if (strlen(WebMail->BID) == 0)
			sprintf(Msg->bid, "%d_%s", LatestMsg, BBSName);
		else
			strcpy(Msg->bid, WebMail->BID);
		Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);
		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->length = (int)MsgLen + WebMail->HeaderLen + WebMail->FooterLen;
		sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number);
		//	BuildFormMessage(Session, Msg);
		if (WebMail->Files)
		{
			// Send as B2
			char * B2Header = BuildB2Header(WebMail, Msg, NULL, 0);
			hFile = fopen(MsgFile, "wb");
			if (hFile)
			{
				WriteLen = fwrite(B2Header, 1, strlen(B2Header), hFile); 
				WriteLen += fwrite(WebMail->Header, 1, WebMail->HeaderLen, hFile); 
				WriteLen += fwrite(WebMail->Body, 1, MsgLen, hFile); 
				WriteLen += fwrite(WebMail->Footer, 1, WebMail->FooterLen, hFile); 
				WriteLen += fwrite("\r\n", 1, 2, hFile); 
				for (i = 0; i < WebMail->Files; i++)
				{
					WriteLen += fwrite(WebMail->FileBody[i], 1, WebMail->FileLen[i], hFile); 
					WriteLen += fwrite("\r\n", 1, 2, hFile); 
				}
				fclose(hFile);
				free(B2Header);
				Msg->length = (int)WriteLen;
			}
		}
		else
		{
			hFile = fopen(MsgFile, "wb");
			if (hFile)
			{
				WriteLen += fwrite(WebMail->Header, 1, WebMail->HeaderLen, hFile); 
				WriteLen += fwrite(WebMail->Body, 1, MsgLen, hFile); 
				WriteLen += fwrite(WebMail->Footer, 1, WebMail->FooterLen, hFile); 
				fclose(hFile);
			}
		}
		MatchMessagetoBBSList(Msg, &conn);
		BuildNNTPList(Msg);				// Build NNTP Groups list
		if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0)
			Msg->status = '$';				// Has forwarding
		if (EnableUI)
			SendMsgUI(Msg);	
		user = LookupCall(Msg->to);
		// If Event Notifications enabled report a new message event
		SendNewMessageEvent(user->Call, Msg);
#ifndef NOMQTT
		if (MQTT)
			MQTTMessageEvent(Msg);
#endif
		if (user && (user->flags & F_APRSMFOR))
		{
			char APRS[128];
			char Call[16];
			int SSID = user->flags >> 28;
			if (SSID)
				sprintf(Call, "%s-%d", Msg->to, SSID);
			else
				strcpy(Call, Msg->to);
			sprintf(APRS, "New BBS Message %s From %s", Msg->title, Msg->from);
			APISendAPRSMessage(APRS, Call);
		}
		HDest = HDestRest;
	}
	*RLen = SendWebMailHeaderEx(Reply, Session->Key, Session, Prompt);
	SaveMessageDatabase();
	SaveBIDDatabase();
	FreeWebMailFields(WebMail);
	free(HDestCopy);
	return;
}
// RMS Express Forms Support
char * GetHTMLViewerTemplate(char * FN)
{
	int i, j, k, l;
	// Seach list of forms for base file (without .html)
	for (i = 0; i < FormDirCount; i++)
	{
		struct HtmlFormDir * Dir = HtmlFormDirs[i];
		for (j = 0; j < Dir->FormCount; j++)
		{
			if (strcmp(FN, Dir->Forms[j]->FileName) == 0)
			{
				return CheckFile(Dir, FN);
			}
		}
		if (Dir->DirCount)
		{
			for (l = 0; l < Dir->DirCount; l++)
			{
				struct HtmlFormDir * SDir = Dir->Dirs[l];
				if (SDir->DirCount)
				{
					struct HtmlFormDir * SSDir = SDir->Dirs[0];
					int x = 1;
				}
				for (k = 0; k < SDir->FormCount; k++)
				{
					if (_stricmp(FN, SDir->Forms[k]->FileName) == 0)
					{
						return CheckFile(SDir, SDir->Forms[k]->FileName);
					}
				}
				if (SDir->DirCount)
				{
					struct HtmlFormDir * SSDir = SDir->Dirs[0];
					int x = 1;
				}
			}
		}
	}
	return NULL;
}
VOID GetReply(struct HTTPConnectionInfo * Session, char * NodeURL)
{
}
VOID GetPage(struct HTTPConnectionInfo * Session, char * NodeURL)
{
		// Read the HTML Template file and do any needed substitutions
	
	WebMailInfo * WebMail = Session->WebMail;
	KeyValues * txtKey = WebMail->txtKeys;
	int DirNo;
	char * ptr;
	int FileNo = 0;
	char * SubDir;
	int SubDirNo;
	int i;
	struct HtmlFormDir * Dir;
	char * Template;
	char * inptr;
	char FormDir[MAX_PATH] = "";
	char FN[MAX_PATH] = "";
	char * InputName = NULL;	// HTML to input message
	char * ReplyName = NULL;
	char * To = NULL;
	char * CC = NULL;
	char * BID = NULL;
	char Type = 0;
	char * Subject = NULL;
	char * MsgBody = NULL;
	char * varptr;
	char * endptr;
	size_t varlen, vallen = 0;
	char val[256]="";			// replacement text
	char var[100] = "\"";
	char * MsgBytes;
	char Submit[64];
	if (NodeURL == NULL)
	{
		//rentry after processing  or 
			
		goto reEnter;
	}
	DirNo = atoi(&NodeURL[17]);
	ptr = strchr(&NodeURL[17], ',');
	Dir = HtmlFormDirs[DirNo];
	SubDir = strlop(&NodeURL[17], ':');
	if (SubDir)
	{
		SubDirNo = atoi(SubDir);
		Dir = Dir->Dirs[SubDirNo];
	}
	sprintf(Submit, "/Webmail/Submit?%s", Session->Key);
	if (ptr)
		FileNo = atoi(ptr + 1);
	// First we read the .txt. then get name of input .html from it
	if (WebMail->txtFile)
		free(WebMail->txtFile);
	WebMail->txtFile = NULL;
	if (WebMail->txtFileName)
		free(WebMail->txtFileName);
	WebMail->txtFileName = _strdup(Dir->Forms[FileNo]->FileName);
	// Read the file here to simplify reentry if we do  or  substitutions
	// if Dir not specified search all for Filename
	if (Dir == NULL)
	{
		for (i = 0; i < FormDirCount; i++)
		{
			int n;
			
			Dir = HtmlFormDirs[i];
			MsgBytes = CheckFile(Dir, WebMail->txtFileName);
			if (MsgBytes)
				goto gotFile;
			
			// Recurse any Subdirs
			
			n = 0;
			while (n < Dir->DirCount)
			{
				MsgBytes = CheckFile(Dir->Dirs[n], FN);
				if (MsgBytes)
				{
					Dir = Dir->Dirs[n];
					goto gotFile;
				}
				n++;
			}
		}
		return;
	}
	else
		MsgBytes = CheckFile(Dir, WebMail->txtFileName);
gotFile:
	WebMail->Dir = Dir;
	if (WebMail->txtFile)
		free(WebMail->txtFile);
	WebMail->txtFile = MsgBytes;
reEnter:
	if (ParsetxtTemplate(Session, Dir, WebMail->txtFileName, FALSE) == FALSE)
	{
		// Template has  or  tags and value has been requested
			
		return;
	}
	if (WebMail->InputHTMLName == NULL)
	{
		// This is a plain text template without HTML 
		if (To == NULL &&WebMail->To && WebMail->To[0])
			To = WebMail->To;
		else
			To = "";
		if (CC == NULL && WebMail->CC && WebMail->CC[0])
			CC = WebMail->CC;
		else
			CC = "";
	
		if (Subject == NULL && WebMail->Subject && WebMail->Subject[0])
			Subject = WebMail->Subject;
		else
			Subject = "";
		if (BID == NULL && WebMail->BID && WebMail->BID[0])
			BID = WebMail->BID;
		else
			BID = "";
		if (Type == 0 && WebMail->Type) 
			Type = WebMail->Type;
		else
			Type = 'P';
		if (MsgBody == NULL)
			MsgBody = "";
		if (MsgBody[0] == 0 && WebMail->Body && WebMail->Body[0])
			MsgBody = WebMail->Body;
		*WebMail->RLen = sprintf(WebMail->Reply, CheckFormMsgPage, Session->Key, To, CC, Subject,
			(Type =='P') ? "Selected" : "", (Type =='B') ? "Selected" : "", (Type =='T') ? "Selected" : "",  BID, MsgBody);
		return;
	}
		Template = CheckFile(Dir, WebMail->InputHTMLName);
		if (Template == NULL)
		{
			*WebMail->RLen  = sprintf(WebMail->Reply, "%s", "HTML Template not found");
			return;
		}
		// I've going to update the template in situ, as I can't see a better way
		// of making sure all occurances of variables in any order are substituted.
		// The space allocated to Template is twice the size of the file
		// to allow for insertions
		// First find the Form Action string and replace with our URL. It should have
		// action="http://{FormServer}:{FormPort}" but some forms have localhost:8001 instead
		// Also remove the OnSubmit if it contains the standard popup about having to close browser
	
		UpdateFormAction(Template, Session->Key);
		// Search for "{var }" strings in form and replace with
		// corresponding variable
		// we run through the Template once for each variable
		while (txtKey->Key)
		{
			char Key[256] = "{";
		
			strcpy(&Key[1], &txtKey->Key[1]);
		
			ptr = strchr(Key, '>');
			if (ptr)
				*ptr = '}';
			inptr = Template;
			varptr = stristr(inptr, Key);
			while (varptr)
			{
				// Move the remaining message up/down the buffer to make space for substitution
				varlen = strlen(Key);
				if (txtKey->Value)
					vallen = strlen(txtKey->Value);
				else vallen = 0;
				endptr = varptr + varlen;
		
				memmove(varptr + vallen, endptr, strlen(endptr) + 1);		// copy null on end
				memcpy(varptr, txtKey->Value, vallen);
				inptr = endptr + 1;
		
				varptr = stristr(inptr, Key);
			}
			txtKey++;
		}
		// Remove  from end as we add it on later
		ptr = stristr(Template, "");
		
		if (ptr)
			*ptr = 0;
		
		*WebMail->RLen = sprintf(WebMail->Reply, "%s", Template);
		free(Template);	
		return;
}
char * xxReadTemplate(char * FormSet, char * DirName, char *FileName)
{
	int FileSize;
	char * MsgBytes;
	char MsgFile[265];
	size_t ReadLen;
	struct stat STAT;
	FILE * hFile;
#ifndef WIN32
	// Need to do case insensitive file search
	DIR *dir;
	struct dirent *entry;
	char name[256];
	sprintf(name, "%s/%s/%s", BPQDirectory, FormSet, DirName);
	if (!(dir = opendir(name)))
	{
		Debugprintf("cant open forms dir %s %d %d", name, errno, dir);
        return 0;
	}
    while ((entry = readdir(dir)) != NULL)
	{
        if (entry->d_type == DT_DIR)
                continue;
	
		if (stristr(entry->d_name, FileName))
		{
			sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), FormSet, DirName, entry->d_name);
		    closedir(dir);
			break;
		}
	}
    closedir(dir);
#else
	sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), FormSet, DirName, FileName);
#endif
	if (stat(MsgFile, &STAT) != -1)
	{
		hFile = fopen(MsgFile, "rb");
	
		if (hFile == 0)
		{
			MsgBytes = _strdup("File is missing");
			return MsgBytes;
		}
		FileSize = STAT.st_size;
		MsgBytes = malloc(FileSize * 2);		// Allow plenty of room for template substitution
		ReadLen = fread(MsgBytes, 1, FileSize, hFile); 
		MsgBytes[FileSize] = 0;
		fclose(hFile);
		return MsgBytes;
	}
	
	return NULL;
}
int	ReturnRawMessage(struct UserInfo * User, struct MsgInfo * Msg, char * Key, char * Reply, char * RawMessage, int len, char * ErrorString)
{
	char * ErrorMsg = malloc(len + 100);
	char * ptr;
	char DownLoad[256];
	sprintf(DownLoad, "Save Attachments ");
	while (ptr1)
	{
		ptr2 = strchr(++ptr1, '>');
		if (ptr2 == NULL)
			goto quit;
		*ptr2++ = 0;
		ptr3 = strstr(ptr2, "");	// end of value string
		if (ptr3 == NULL)
			goto quit;
		*ptr3++ = 0;
		XMLKeys->Key = _strdup(ptr1);
		XMLKeys->Value = _strdup(ptr2);
		XMLKeys++;
		ptr1 = strchr(ptr3, '<');
		if (_memicmp(ptr1, "", 2) == 0)
		{
			// end of a parameter block. Find start of next block
			ptr1 = strchr(++ptr1, '<');
			ptr1 = strchr(++ptr1, '<');		// Skip start of next block
		}
	}
quit:
	free(XML);
	return TRUE;
}
/*
?xml version="1.0"?>
 
 1,0 
 6.0.16.38 Debug Build 
 20181022105202 
 G8BPQ 
 Alaska_ARES_ICS213_Initial_Viewer.html 
 Alaska_ARES_ICS213_SendReply.txt 
  
 g8bpq 
 G8BPQ 
 false 
 false 
 false 
 false 
 G8BPQ-1 
 Routine 
 Here 
 11:51 
 2018-10-22 
 Test 
 John 
 John 
 Test 
 2018-10-22 11:51 
 Me 
 Me 
 Submit 
 
 
*/
char HTMLNotFoundMsg[] = " *** HTML Template for message not found - displaying raw content ***\r\n\r\n%s";
char VarNotFoundMsg[] = " *** Variable {%s} not found in message - displaying raw content ***\r\n\r\n%s";
char BadXMLMsg[] = " *** XML for Variable {%s} invalid - displaying raw content ***\r\n\r\n%s";
char BadTemplateMsg[] = " *** Template near \"%s\" invalid - displaying raw content ***\r\n\r\n%s";
int DisplayWebForm(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * FileName, char * XML, char * Reply, char * RawMessage, int RawLen)
{
	WebMailInfo * WebMail = Session->WebMail;
	struct UserInfo * User = Session->User;
	char * Key = Session->Key;
	int Len = 0;
	char * Form;
	char * SaveReply = Reply;
	char FN[MAX_PATH] = "";
	char * formptr, * varptr, * xmlptr;
	char * ptr = NULL, * endptr, * xmlend;
	size_t varlen, xmllen;
	char var[100] = "<";
	KeyValues * KeyValue; 
	if (ParseXML(WebMail, XML))
		ptr = FindXMLVariable(WebMail, "display_form");
	if (ptr == NULL)		// ?? No Display Form Specified??
	{
		// Not found - just display as normal message
		return ReturnRawMessage(User, Msg, Key, Reply, RawMessage, (int)(XML - RawMessage), HTMLNotFoundMsg);
	}
	strcpy(FN, ptr);
	Form = GetHTMLViewerTemplate(FN);
	if (Form == NULL)
	{
		// Not found - just display as normal message
		return ReturnRawMessage(User, Msg, Key, Reply, RawMessage, (int)(XML - RawMessage), HTMLNotFoundMsg);
	}
	formptr = Form;
	// Search for {var xxx} strings in form and replace with
	// corresponding variable in xml
	// Don't know why, but {MsgOriginalBody} is sent instead of {var MsgOriginalBody}
	varptr = stristr(Form, "{MsgOriginalBody}");
	{
		char * temp, * tempsave;
		char * xvar, * varsave, * ptr;
		if (varptr)
		{
			varptr++;
		
			endptr = strchr(varptr, '}');
			varlen = endptr - varptr;
			if (endptr == NULL || varlen > 99)
			{
				// corrupt template - display raw message
	
				char Err[256];
				varptr[20] = 0;
		
				sprintf(Err, BadTemplateMsg, varptr - 5, "%s");
				return ReturnRawMessage(User, Msg, Key, SaveReply, RawMessage, (int)(XML - RawMessage), Err);
			}
	
			memcpy(var + 1, varptr, varlen);
			var[++varlen] = '>';
			var[++varlen] = 0;
			xmlptr = stristr(XML, var);
			if (xmlptr)
			{
				xmlptr += varlen;
			
				xmlend = strstr(xmlptr, "");
			
				if (xmlend == NULL)
				{
					// Bad XML - return error message
					char Err[256];
					// remove <> from var as it confuses html
					var[strlen(var) -1] = 0;
			
					sprintf(Err, BadXMLMsg, var + 1, "%s");
					return ReturnRawMessage(User, Msg, Key, SaveReply, RawMessage, (int)(XML - RawMessage), Err);
				}
				xmllen = xmlend - xmlptr;
			}
			else
			{	
				// Variable not found - return error message
				char Err[256];
				// remove <> from var as it confuses html
				var[strlen(var) -1] = 0;
			
				sprintf(Err, VarNotFoundMsg, var + 1, "%s");
				return ReturnRawMessage(User, Msg, Key, SaveReply, RawMessage, (int)(XML - RawMessage), Err);
			}
			// Ok, we have the position of the variable and the substitution text.
			// Copy message up to variable to Result, then copy value
			// We create a copy so we can rescan later.
			// We also need to replace CR or CRLF with  %s Webmail Interface - User %s - Message %d "
				"", BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key,  Key, Msg->number, Key, Key, Key);
			strcat(temp, ptr);
			strcpy(SaveReply, temp);
			free(temp);
		}
	}
	if (Form)
		free(Form);
	return (int)strlen(SaveReply);
}
char * BuildB2Header(WebMailInfo * WebMail, struct MsgInfo * Msg, char ** ToCalls, int Calls)
{
	// Create B2 Header
	
	char * NewMsg = malloc(100000);
	char * SaveMsg = NewMsg;
	char DateString[80];
	struct tm * tm;
	int n;
	char Type[16] = "Private";
	// Get Type
	
	if (Msg->type == 'B')
		strcpy(Type, "Bulletin");
	else if (Msg->type == 'T')
		strcpy(Type, "Traffic");
	tm = gmtime((time_t *)&Msg->datecreated);	
	
	sprintf(DateString, "%04d/%02d/%02d %02d:%02d",
		tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
	NewMsg += sprintf(NewMsg,
		"MID: %s\r\n"
		"Date: %s\r\n"
		"Type: %s\r\n"
		"From: %s\r\n",
			Msg->bid, DateString, Type, Msg->from);
	if (ToCalls)
	{
		int i;
		for (i = 0; i < Calls; i++)
			NewMsg += sprintf(NewMsg, "To: %s\r\n",	ToCalls[i]);
	}
	else
	{
		NewMsg += sprintf(NewMsg, "To: %s\r\n",
			WebMail->To);
	}
	if (WebMail->CC && WebMail->CC[0])
		NewMsg += sprintf(NewMsg, "CC: %s\r\n", WebMail->CC);
	NewMsg += sprintf(NewMsg,
		"Subject: %s\r\n"
		"Mbo: %s\r\n",
		Msg->title, BBSName);
	NewMsg += sprintf(NewMsg, "Body: %d\r\n", (int)strlen(WebMail->Body) + WebMail->HeaderLen + WebMail->FooterLen);
	Msg->B2Flags = B2Msg;
	if (WebMail->XML)
	{
		Msg->B2Flags |= Attachments;
		NewMsg += sprintf(NewMsg, "File: %d %s\r\n",
			WebMail->XMLLen, WebMail->XMLName);
	}
	for (n = 0; n < WebMail->Files; n++)
	{
		Msg->B2Flags |= Attachments;
		NewMsg += sprintf(NewMsg, "File: %d %s\r\n",
			WebMail->FileLen[n], WebMail->FileName[n]);
	}
	NewMsg += sprintf(NewMsg, "\r\n");		// Blank Line to end header
	return SaveMsg;
}
VOID WriteOneRecipient(struct MsgInfo * Msg, WebMailInfo * WebMail, int MsgLen, char ** ToCalls, int Calls, char * BID)
{
	FILE * hFile;
	char * via = NULL;
	BIDRec * BIDRec;
	char MsgFile[MAX_PATH];
	size_t WriteLen=0;
	char * B2Header;
	if (strlen(WebMail->Subject) > 60)
		WebMail->Subject[60] = 0;
	strcpy(Msg->title, WebMail->Subject);
	if (strlen(BID) == 0)
		sprintf_s(BID, 32, "%d_%s", LatestMsg, BBSName);
	strcpy(Msg->bid, BID);
	Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);
	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->length = MsgLen;
	sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number);
	
	// We write a B2 Header, Body and XML attachment if present
	B2Header = BuildB2Header(WebMail, Msg, ToCalls, Calls);
	hFile = fopen(MsgFile, "wb");
	
	if (hFile)
	{
		int i;
		
		WriteLen = fwrite(B2Header, 1, strlen(B2Header), hFile); 
		WriteLen += fwrite(WebMail->Body, 1, Msg->length, hFile); 
		WriteLen += fwrite("\r\n", 1, 2, hFile); 
		if (WebMail->XML)
		{
			WriteLen += fwrite(WebMail->XML, 1, WebMail->XMLLen, hFile); 
			WriteLen += fwrite("\r\n", 1, 2, hFile); 
		}
		// Do any attachments
		for (i = 0; i < WebMail->Files; i++)
		{
			WriteLen += fwrite(WebMail->FileBody[i], 1, WebMail->FileLen[i], hFile); 
			WriteLen += fwrite("\r\n", 1, 2, hFile); 
		}
		fclose(hFile);
	}
	free(B2Header);
	Msg->length = (int)WriteLen;
	MatchMessagetoBBSList(Msg, 0);
	if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0)
		Msg->status = '$';				// Has forwarding
	BuildNNTPList(Msg);				// Build NNTP Groups list
#ifndef NOMQTT
	if (MQTT)
		MQTTMessageEvent(Msg);
#endif
}
VOID SaveTemplateMessage(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest)
{
	int ReplyLen = 0;
	struct MsgInfo * Msg;
	char * ptr, *input, *context;
	size_t MsgLen;
	char Type;
	char * via = NULL;
	char BID[32];
	size_t WriteLen=0;
	char * Body = NULL;
	char * To = NULL;
	char * CC = NULL;
	char * HDest = NULL;
	char * Title = NULL;
	char * Vptr = NULL;
	char * Context;
	char Prompt[256] = "Message Saved";
	char OrigTo[256];
	WebMailInfo * WebMail = Session->WebMail;
	BOOL SendMsg = FALSE;
	BOOL SendReply = FALSE;
	char * RMSTo[1000] = {NULL};		// Winlink addressees
	char * PKTT0[1000] = {NULL};		// Packet addressees
	__int32 Recipients = 0;
	__int32 RMSMsgs = 0, BBSMsgs = 0;
	input = strstr(MsgPtr, "\r\n\r\n");	// End of headers
	if (input == NULL)
		return;
	
	if (strstr(input, "Cancel=Cancel"))
	{
		*RLen = sprintf(Reply, "", Session->Key);
		return;
	}
	if (WebMail->txtFileName == NULL)
	{
		// No template, so user must have used back button
		*RLen = sprintf(Reply, "", Session->Key);
		return;
	}
	ptr = strtok_s(input + 4, "&", &Context);
	while (ptr)
	{
		char * val = strlop(ptr, '=');
		if (strcmp(ptr, "To") == 0)
			HDest = To = val;
		else if (strcmp(ptr, "CC") == 0)
			CC = val;
		else if (strcmp(ptr, "Subj") == 0)
			Title = val;
		else if (strcmp(ptr, "Type") == 0)
			Type = val[0];
		else if (strcmp(ptr, "BID") == 0)
			strcpy(BID, val);
		else if (strcmp(ptr, "Msg") == 0)
			Body = _strdup(val);
		ptr = strtok_s(NULL, "&", &Context);
	}
	strlop(BID, ' ');
	if (strlen(BID) > 12)
		BID[12] = 0;
	UndoTransparency(To);
	UndoTransparency(CC);
	UndoTransparency(BID);
	UndoTransparency(HDest);
	UndoTransparency(Title);
	UndoTransparency(Body);
	MsgLen = strlen(Body);
	// The user could have changed any of the input fields.
	if (To && To[0])
	{
		free (WebMail->To);
		WebMail->To = _strdup(To);
	}
	if (CC && CC[0])
	{
		free (WebMail->CC);
		WebMail->CC = _strdup(CC);
	}
	if (Title && Title[0])
	{
		free (WebMail->Subject);
		WebMail->Subject = _strdup(Title);
	}
	if (Body && Body[0])
	{
		free (WebMail->Body);
		WebMail->Body = _strdup(Body);
	}
	// We will put the supplied address in the B2 header
	
	// We may need to change the HDest to make sure message
	// is delivered to Internet or Packet as requested
	if (HDest == NULL || HDest[0] == 0)
	{
		*RLen = sprintf(Reply, "%s", "");
		return;
	}
	// Multiple TO fields could be more than 255 bytes long
//	if (strlen(HDest) > 255)
//	{
//		*RLen = sprintf(Reply, "%s", "");
//		return;
//	}
	if (strlen(BID))
	{		
		if (LookupBID(BID))
		{
			// Duplicate bid
			*RLen = sprintf(Reply, "%s", "");
			return;
		}
	}
	if (Type == 'B')
	{
		if (RefuseBulls)
		{
			*RLen = sprintf(Reply, "%s", "");
			return;
		}
	}
	// We should be able to handle multiple recipients, with all Winlink addresses sent in one message and
	// multiple copies for packet addressess. For Winlink we should use multiple To@ lines in B2 Header
	ptr = strtok_s(HDest, " ;", &context);
	while (ptr && ptr[0])
	{
		int Winlink = 0;
		int AMPR = 0;
		
		char * dest = zalloc(256);
		char * via = NULL;
		strcpy(dest, ptr);
		// See if packet or Winlink
		// If Type=Winlink specified send plain call as @winlink.org 
		if (strchr(dest, '@') == 0 && WebMail->Winlink)
			strcat(dest, "@winlink.org");
		if (_memicmp(dest, "rms:", 4) == 0 || _memicmp(dest, "rms/", 4) == 0)
		{
			memcpy(dest, &dest[4], strlen(dest));
			Winlink = 1;
			RMSTo[RMSMsgs++] = dest;
			ptr = strtok_s(NULL, " ;", &context);
			continue;
		}
		else if (_memicmp(dest, "smtp:", 5) == 0)
		{
			if (ISP_Gateway_Enabled)
			{
				via = &dest[5];
				dest[0] = 0;
			}
		}
		else if (strchr(dest, '@'))
		{
			strcpy(OrigTo, dest);
			via = strlop(dest, '@');
			if (via)
			{
				// If looks like a valid email address, treat as such
				if (strlen(via) > 6 || !CheckifPacket(via))
				{
					// Assume Email address. See if to send via RMS or SMTP
					if (isAMPRMsg(OrigTo))
					{
						dest[strlen(dest)] = '@';		// Put back together
						memmove(&dest[1], dest, strlen(dest));
						dest[0] = 0;
						via = &dest[1];
						AMPR = 1;
					}
					else if (FindRMS() || strchr(via, '!')) // have RMS or source route
					{
						dest[strlen(dest)] = '@';		// Put back together
						Winlink = 1;
						RMSTo[RMSMsgs++] = dest;
						ptr = strtok_s(NULL, " ;", &context);
						continue;
					}
					else if (ISP_Gateway_Enabled)
					{
						dest[strlen(dest)] = '@';		// Put back together
						memmove(dest, &dest[1], strlen(dest));
						dest[0] = 0;
					}
					else
					{		
						*RLen = sprintf(Reply, "%s", "");
						return;
					}
				}
			}
		}
		else
		{
			// No @
			if (strlen(dest) > 6)
				dest[6] = 0;
		}
		// This isn't an RMS Message, so can queue now
		Msg = AllocateMsgRecord();
		
		// Set number here so they remain in sequence
		
		Msg->number = ++LatestMsg;
		MsgnotoMsg[Msg->number] = Msg;
		strcpy(Msg->title, WebMail->Subject);
		Msg->type = Type;
		Msg->status = 'N';
		strcpy(Msg->from, Session->User->Call);
		strcpy(Msg->to, _strupr(dest));
		
		if (SendBBStoSYSOPCall)
			if (_stricmp(dest, BBSName) == 0)
				strcpy(Msg->to, SYSOPCall);
		if (via)
		{
			if (strlen(via) > 40)
				via[40] = 0;
			strcpy(Msg->via, _strupr(via));
		}
		else
		{
			// No via. If not local user try to add BBS 
			struct UserInfo * ToUser = LookupCall(Msg->to);
			if (ToUser)
			{
				// Local User. If Home BBS is specified, use it
				if (ToUser->flags & F_RMSREDIRECT)
				{
					// sent to Winlink
				
					strcpy(Msg->via, WinlinkAddr);
					sprintf(Prompt, "Redirecting to winlink.org\r");
				}
				else if (ToUser->HomeBBS[0])
				{
					strcpy(Msg->via, ToUser->HomeBBS);
					sprintf(Prompt, "%s added from HomeBBS. Message Saved", Msg->via);
				}
			}
			else
			{
				// Not local user - Check WP
				WPRecP WP = LookupWP(Msg->to);
				if (WP)
				{
					strcpy(Msg->via, WP->first_homebbs);
					sprintf(Prompt, "%s added from WP", Msg->via);
				}
			}
		}		
		WriteOneRecipient(Msg, WebMail, MsgLen, NULL, 0, BID);
		ptr = strtok_s(NULL, " ;", &context);
		free(dest);
		BID[0] = 0;					// Can't use more than once
	}
	if (RMSMsgs)
	{
		// Write one message to all Winlink addresses
		int i;
		Msg = AllocateMsgRecord();
		
		// Set number here so they remain in sequence
		
		Msg->number = ++LatestMsg;
		MsgnotoMsg[Msg->number] = Msg;
		strcpy(Msg->title, WebMail->Subject);
		Msg->type = Type;
		Msg->status = 'N';
		strcpy(Msg->from, Session->User->Call);
		strcpy(Msg->to, "RMS");
		WriteOneRecipient(Msg, WebMail, MsgLen, RMSTo, RMSMsgs, BID);
		for (i = 0; i < RMSMsgs; i++)
			free(RMSTo[i]);
	}
	SaveMessageDatabase();
	SaveBIDDatabase();
	*RLen = SendWebMailHeaderEx(Reply, Session->Key, Session, Prompt);
	FreeWebMailFields(WebMail);			
	return;
}
VOID DoStandardTemplateSubsitutions(struct HTTPConnectionInfo * Session, char * txtFile)
{
	WebMailInfo * WebMail = Session->WebMail;
	struct UserInfo * User = Session->User;
	KeyValues * txtKey = WebMail->txtKeys;
	char * inptr, * varptr, * endptr;
	int varlen, vallen;
	while (txtKey->Key != NULL)
	{
		inptr = WebMail->txtFile;
		varptr = stristr(inptr, txtKey->Key);
		while (varptr)
		{
			// Move the remaining message up/down the buffer to make space for substitution
			varlen = (int)strlen(txtKey->Key);
			if (txtKey->Value)
				vallen = (int)strlen(txtKey->Value);
			else
				vallen = 0;
			endptr = varptr + varlen;
		
			memmove(varptr + vallen, endptr, strlen(endptr) + 1);		// copy null on end
			memcpy(varptr, txtKey->Value, vallen);
			inptr = endptr + 1;
		
			varptr = stristr(inptr, txtKey->Key);
		}
		txtKey++;
	}
}
VOID BuildMessageFromHTMLInput(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Keys[1000], char * Values[1000], int NumKeys)
{
	int ReplyLen = 0;
	struct MsgInfo * Msg;
	int MsgLen;
	FILE * hFile;
	char Type = 'P';
	BIDRec * BIDRec;
	char * MailBuffer;
	char MsgFile[MAX_PATH];
	int WriteLen=0;
	char Prompt[256] = "Message Saved";
	char OrigTo[256];
	WebMailInfo * WebMail = Session->WebMail;
	char * HDest = _strdup(WebMail->To);
	char * Vptr = NULL;
	char BID[16] = "";
///	if (strlen(HDest) > 255)
///	{
//		*RLen = sprintf(Reply, "%s", "");
//		return;
//	}
	MsgLen = (int)strlen(WebMail->Body);	
	Msg = AllocateMsgRecord();
		
	// Set number here so they remain in sequence
		
	Msg->number = ++LatestMsg;
	MsgnotoMsg[Msg->number] = Msg;
	strcpy(Msg->from, Session->User->Call);
	if (_memicmp(HDest, "rms:", 4) == 0 || _memicmp(HDest, "rms/", 4) == 0)
	{
		Vptr=&HDest[4];
		strcpy(Msg->to, "RMS");
	}
	else if (_memicmp(HDest, "smtp:", 5) == 0)
	{
		if (ISP_Gateway_Enabled)
		{
			Vptr=&HDest[5];
			Msg->to[0] = 0;
		}
	}
	else if (strchr(HDest, '@'))
	{
		strcpy(OrigTo, HDest);
		Vptr = strlop(HDest, '@');
		if (Vptr)
		{
			// If looks like a valid email address, treat as such
			if (strlen(HDest) > 6 || !CheckifPacket(Vptr))
			{
				// Assume Email address
				Vptr = OrigTo;
				if (FindRMS() || strchr(Vptr, '!')) // have RMS or source route
					strcpy(Msg->to, "RMS");
				else if (ISP_Gateway_Enabled)
					Msg->to[0] = 0;
				else if (isAMPRMsg(OrigTo))
					strcpy(Msg->to, "RMS");		// Routing will redirect it
				else
				{		
					*RLen = sprintf(Reply, "%s", "");
					return;
		
				}
			}
		}
	}
	else
	{	
		if (strlen(HDest) > 6)
			HDest[6] = 0;
		
		strcpy(Msg->to, _strupr(HDest));
	}
	if (SendBBStoSYSOPCall)
		if (_stricmp(HDest, BBSName) == 0)
			strcpy(Msg->to, SYSOPCall);
	if (Vptr)
	{
		if (strlen(Vptr) > 40)
			Vptr[40] = 0;
		strcpy(Msg->via, _strupr(Vptr));
	}
	else
	{
		// No via. If not local user try to add BBS 
	
		struct UserInfo * ToUser = LookupCall(Msg->to);
		if (ToUser)
		{
			// Local User. If Home BBS is specified, use it
			if (ToUser->flags & F_RMSREDIRECT)
			{
				// sent to Winlink
				
				strcpy(Msg->via, WinlinkAddr);
				sprintf(Prompt, "Redirecting to winlink.org\r");
			}
			else if (ToUser->HomeBBS[0])
			{
				strcpy(Msg->via, ToUser->HomeBBS);
				sprintf(Prompt, "%s added from HomeBBS", Msg->via);
			}
		}
		else
		{
			// Not local user - Check WP
			
			WPRecP WP = LookupWP(Msg->to);
			if (WP)
			{
				strcpy(Msg->via, WP->first_homebbs);
				sprintf(Prompt, "%s added from WP", Msg->via);
			}
		}
	}
	if (strlen(WebMail->Subject) > 60)
		WebMail->Subject[60] = 0;
	strcpy(Msg->title, WebMail->Subject);
	Msg->type = Type;
	Msg->status = 'N';
	if (strlen(BID) == 0)
		sprintf_s(BID, sizeof(BID), "%d_%s", LatestMsg, BBSName);
	strcpy(Msg->bid, BID);
	Msg->datereceived = Msg->datechanged = Msg->datecreated = time(NULL);
	BIDRec = AllocateBIDRecord();
	strcpy(BIDRec->BID, Msg->bid);
	BIDRec->mode = Msg->type;
	BIDRec->u.msgno = LOWORD(Msg->number);
	BIDRec->u.timestamp = LOWORD(time(NULL)/86400);
	MailBuffer = malloc(MsgLen + 2000);		// Allow for a B2 Header if attachments
	Msg->length = MsgLen;
	BuildFormMessage(Session, Msg, Keys, Values, NumKeys);
	sprintf_s(MsgFile, sizeof(MsgFile), "%s/m_%06d.mes", MailDir, Msg->number);
	
	hFile = fopen(MsgFile, "wb");
	
	if (hFile)
	{
		WriteLen = (int)fwrite(WebMail->Body, 1, Msg->length, hFile);
		fclose(hFile);
	}
	
	free(WebMail->Body);
	free(HDest);
	WebMail->Body = NULL;
	MatchMessagetoBBSList(Msg, 0);
	if (Msg->status != 'H' && Msg->type == 'B' && memcmp(Msg->fbbs, zeros, NBMASK) != 0)
		Msg->status = '$';				// Has forwarding
	BuildNNTPList(Msg);				// Build NNTP Groups list
#ifndef NOMQTT
	if (MQTT)
		MQTTMessageEvent(Msg);
#endif
	SaveMessageDatabase();
	SaveBIDDatabase();
	*RLen = sprintf(Reply, "", Prompt);
	return;
}
void ProcessFormInput(struct HTTPConnectionInfo * Session, char * input, char * Reply, int * RLen, int InputLen)
{
	// If there is no display html defined place data in a normal
	// input window, else build the Message body and XML attachment and send
	// I now think it is better to put data in a normal input window
	// even if there is a display form so user can view it before submission
	WebMailInfo * WebMail = Session->WebMail;
	char * info = strstr(input, "\r\n\r\n"); // To end of HTML header
	// look through header for Content-Type line, and if multipart
	// find boundary string.
	char * ptr, * saveptr, * ptr1, * ptr2, * inptr;
	char Boundary[1000];
	BOOL Multipart = FALSE;
	int Partlen;
	char ** Body = &info;
	int i;
	char * Keys[1000];
	char * Values[1000];
	char * saveForfree[1000];
	int NumKeys = 0;
	char * varptr;
	char * endptr;
	int varlen, vallen = 0;
	char *crcrptr;
	if (WebMail->txtFile == NULL)
	{
		// No template, so user must have used back button
		*RLen = sprintf(Reply, "", Session->Key);
		return;
	}
	ptr = input;
	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;
	
				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;						// Can't do anything without a boundary ??
			}
		}
		ptr = ptr2;
		ptr++;
	}
	if (info == NULL)
		return;		// Wot!
	// Extract the Key/Value pairs from input data
	saveptr = ptr = WebFindPart(Body, Boundary, &Partlen, input + InputLen);
	if (ptr == NULL)
		return;			// Couldn't find separator
	// Now extract fields
	while (ptr)
	{
		char * endptr;
		char * val;
//		Debugprintf(ptr);
	
		// Format seems to be
		//Content-Disposition: form-data; name="FieldName"
		// crlf crlf
		// field value
		// crlf crlf
		// No, is actually
		// ------WebKitFormBoundary7XHZ1i7Jc8tOZJbw
		// Content-Disposition: form-data; name="State"
		//
		// UK
		// ------WebKitFormBoundary7XHZ1i7Jc8tOZJbw
		// ie Value is terminated by ------WebKitFormBoundary7XHZ1i7Jc8tOZJbw
		// But FindPart has returned length, so can use that
		// Be aware that Part and PartLen include the CRLF which is actually part of the Boundary string so should be removed.
		ptr = strstr(ptr, "name=");
	
		if (ptr)
		{
			endptr = strstr(ptr, "\"\r\n\r\n");		// "/r/n/r/n
			if (endptr)
			{
				*endptr = 0;
				ptr += 6;			// to start of name string
				val = endptr + 5;
				// val was Null Terminated by FindPart so can just use it. This assumes all fields are text,
				// which I think is safe enough here.
				saveptr[Partlen - 2] = 0;
				// Now have key value pair
				Keys[NumKeys] = ptr;
				Values[NumKeys] = val;
				saveForfree[NumKeys++] = saveptr;		// so we can free() when finished with it
			}
			else
				free(saveptr);
		}
		else
			free(saveptr);
		saveptr = ptr = WebFindPart(Body, Boundary, &Partlen, input + InputLen);
	}
	if (info == NULL)
		return;		// Wot!
	info += 4;
	// It looks like some standard variables can be used in User->Call;
	Keys[NumKeys] = "MsgSubject";
	Values[NumKeys++] = "";
	Keys[NumKeys] = "MsgBody";
//	if (WebMail->OrigBody)
//		txtKey++->Value = _strdup(WebMail->OrigBody);
//	else
		Values[NumKeys++] = "";
	Keys[NumKeys] = _strdup("MsgP2P");
	Values[NumKeys++] = _strdup("");
	Keys[NumKeys] = _strdup("MsgIsReply");
	if (WebMail->isReply)
		Values[NumKeys++] = "True";
	else
		Values[NumKeys++] = "True";
	Keys[NumKeys] = "MsgIsForward";
	Values[NumKeys++] = "False";
	Keys[NumKeys] = "MsgIsAcknowledgement";
	Values[NumKeys++] = "False";
	// Update Template with variables from the form
	
	// I've going to update the template in situ, as I can't see a better way
	// of making sure all occurances of variables in any order are substituted.
	// The space allocated to Template is twice the size of the file
	// to allow for insertions
	inptr = WebMail->txtFile;
	// Search for "" strings in form and replace with
	// corresponding variable
	// we run through the Template once for each variable
	i = 0;
	while (i < NumKeys)
	{
		char Key[256];
		
		sprintf(Key, "", Keys[i]);
		
		inptr = WebMail->txtFile;
		varptr = stristr(inptr, Key);
		while (varptr)
		{
			// Move the remaining message up/down the buffer to make space for substitution
			varlen = (int)strlen(Key);
			vallen = (int)strlen(Values[i]);
			endptr = varptr + varlen;
		
			memmove(varptr + vallen, endptr, strlen(endptr) + 1);		// copy null on end
			memcpy(varptr, Values[i], vallen);
			inptr = endptr + 1;
		
			varptr = stristr(inptr, Key);
		}
		i++;
	}
	// We need to look for To:, CC: and Subject lines, and remove any other
	// Var: lines. Use everything following Msg: as the plain text body
	
	// Find start of Message body
	ptr = WebMail->txtFile;
	ptr1 = strchr(ptr, '\r');
		
	while (ptr1)
	{
		if (_memicmp(ptr, "Msg:", 4) == 0)
		{
			// Rest is message body.  substitutions have been done
			if (WebMail->Body)
				free(WebMail->Body);
			WebMail->Body = _strdup(ptr + 4);	// Remove Msg:
			break;
		}
		// Can now terminate lines
		*ptr1++ = 0;
		while (*ptr1 == '\r' || *ptr1 == '\n')
			*ptr1++ = 0;
		if (_memicmp(ptr, "To:", 3) == 0)
		{
			if (strlen(ptr) > 5)
				WebMail->To = _strdup(&ptr[3]);
		}
		else if (_memicmp(ptr, "CC:", 3) == 0)
		{
			if (strlen(ptr) > 5)
				WebMail->CC = _strdup(&ptr[3]);
		}
		else if (_memicmp(ptr, "Subj:", 5) == 0)
		{
			if (ptr[5] == ' ')		// May have space after :
				ptr++;
			if (strlen(ptr) > 6)
				WebMail->Subject = _strdup(&ptr[5]);
		}
		else if (_memicmp(ptr, "Subject:", 8) == 0)
		{
			if (ptr[8] == ' ')
				ptr++;
			if (strlen(ptr) > 9)
				WebMail->Subject = _strdup(&ptr[8]);
		}
		ptr = ptr1;
		ptr1 = strchr(ptr, '\r');
	}
	if (WebMail->Subject == NULL)
		 WebMail->Subject = _strdup("");
	if (WebMail->To == NULL)
		 WebMail->To = _strdup("");
	if (WebMail->CC == NULL)
		 WebMail->CC = _strdup("");
	// Replace var in Subject
	if (_memicmp(WebMail->Subject, "Subject = realloc(WebMail->Subject, 512);		// Plenty of space
		i = 0;
		while (i < NumKeys)
		{
			char Key[256];
		
			sprintf(Key, "", Keys[i]);
		
			inptr = WebMail->Subject;
			varptr = stristr(inptr, Key);
			while (varptr)
			{
				// Move the remaining message up/down the buffer to make space for substitution
				varlen = (int)strlen(Key);
				vallen = (int)strlen(Values[i]);
				endptr = varptr + varlen;
		
				memmove(varptr + vallen, endptr, strlen(endptr) + 1);		// copy null on end
				memcpy(varptr, Values[i], vallen);
				inptr = endptr + 1;
		
				varptr = stristr(inptr, Key);
			}
			i++;
		}
	}
	// Build XML Attachment if Display Form is defined
	if (WebMail->DisplayHTMLName)
		BuildXMLAttachment(Session, Keys, Values, NumKeys);
	// if Reply, attach original message to Body;
	if (WebMail->isReply && WebMail->OrigBody)
	{
		char * NewBody = malloc(strlen(WebMail->Body) + strlen(WebMail->OrigBody) + 100);
		sprintf(NewBody, "%s\r\n%s", WebMail->Body, WebMail->OrigBody);
		free(WebMail->Body);
		WebMail->Body = NewBody;
	}
	// Display Message for user to check and send
	// fix any cr cr lf sequence
	
	crcrptr = strstr(WebMail->Body, "\r\r");
	while (crcrptr)
	{
		*crcrptr = ' ';
		crcrptr = strstr(crcrptr, "\r\r");
	}
	if (WebMail->BID == NULL)
		WebMail->BID = _strdup("");
	*RLen = sprintf(Reply, CheckFormMsgPage, Session->Key, WebMail->To, WebMail->CC, WebMail->Subject, "Selected", "", "", WebMail->BID, WebMail->Body);
	// Free the part strings 
	i = 0;
	while (saveForfree[i])
		free(saveForfree[i++]);
}
// XML Template Stuff
char XMLHeader [] = 
	"\r\n"
	"\r\n"
	" \r\n"
	" %s \r\n"
	" %s \r\n"
	" %s \r\n"
	" %s \r\n"
	" %s \r\n"
	" %s \r\n"
	" %s \r\n"
	"  \r\n"
	"\r\n"
	" %s \r\n"
    " %s \r\n"
    " %s \r\n"
    " %s \r\n"
    " %s \r\n"
    " %s \r\n"
    " %s \r\n"
    " %s \r\n"
    " %s \r\n";
char XMLLine[] = " <%s>%s%s>\r\n";
char XMLTrailer[] = " \r\n \r\n";
char * doXMLTransparency(char * string)
{
	// Make sure string doesn't contain forbidden XML chars (<>"'&)
	char * newstring = malloc(5 * strlen(string) + 1);		// If len is zero still need null terminator
	char * in = string;
	char * out = newstring;
	char c;
	c = *(in++);
	while (c)
	{
		switch (c)
		{
		case '<':
			strcpy(out, "<");
			out += 4;
			break;
		case '>':
			strcpy(out, ">");
			out += 4;
			break;
		case '"':
			strcpy(out, """);
			out += 6;
			break;
		case '\'':
			strcpy(out, "'");
			out += 6;
			break;
		case '&':
			strcpy(out, "&");
			out += 5;
			break;
		default:
			*(out++) = c;
		}
		c = *(in++);
	}
	*(out++) = 0;
	return newstring;
}
 
VOID BuildXMLAttachment(struct HTTPConnectionInfo * Session, char * Keys[1000], char * Values[1000], int NumKeys)
{
	// Create XML Attachment for form
	WebMailInfo * WebMail = Session->WebMail;
	char XMLName[MAX_PATH];
	char * XMLPtr;
	char DateString[80];
	struct tm * tm;
	time_t NOW = time(NULL);
	int n;
	int TotalFileSize = 0;
		
	tm = gmtime(&NOW);	
	
	sprintf(DateString, "%04d%02d%02d%02d%02d%02d",
		tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
	strcpy(XMLName, WebMail->DisplayHTMLName);
	XMLName[strlen(XMLName) - 5] = 0;	// remove .html
	WebMail->XMLName = malloc(MAX_PATH);
	WebMail->XML = XMLPtr = malloc(100000);
	WebMail->XMLLen = 0;
	sprintf(WebMail->XMLName, "RMS_Express_Form_%s.xml", XMLName);
	XMLPtr += sprintf(XMLPtr, XMLHeader,
		"1,0", VersionString,
		DateString,
		Session->User->Call,
		"", //Grid
		WebMail->DisplayHTMLName,
		WebMail->ReplyHTMLName,
		WebMail->To,
		WebMail->CC,
		Session->User->Call,
		WebMail->OrigSubject,
		"", //	WebMail->OrigBody,
		"False",			// P2P
		WebMail->isReply ? "True": "False",
		"False",			// Forward,
		"False");			// Ack
	// create XML lines for Key/Value Pairs
	for (n = 0; n < NumKeys; n++)
	{
		if (Values[n] == NULL)
			Values[n] = _strdup("");
		XMLPtr += sprintf(XMLPtr, XMLLine, Keys[n], Values[n], Keys[n]);
	}
	if (WebMail->isReply)
	{
		if (WebMail->OrigBody)
		{
			char * goodXML = doXMLTransparency(WebMail->OrigBody);
			XMLPtr += sprintf(XMLPtr, XMLLine, "MsgOriginalBody", goodXML, "MsgOriginalBody");
		}
		else
			XMLPtr += sprintf(XMLPtr, XMLLine, "MsgOriginalBody", "", "MsgOriginalBody");
	}
		
	XMLPtr += sprintf(XMLPtr, "%s", XMLTrailer);
	WebMail->XMLLen = (int)(XMLPtr - WebMail->XML);
}
char * BuildFormMessage(struct HTTPConnectionInfo * Session, struct MsgInfo * Msg, char * Keys[1000], char * Values[1000], int NumKeys)
{
	// Create B2 message with template body and xml attachment
	char * NewMsg = malloc(100000);
	char * SaveMsg = NewMsg;
	char * XMLPtr;
	char DateString[80];
	struct tm * tm;
	char * FileName[100];
	int FileLen[100];
	char * FileBody[100];
	int n, Files = 0;
	int TotalFileSize = 0;
	char Type[16] = "Private";
	WebMailInfo * WebMail = Session->WebMail;
	// Create a B2 Message
	tm = gmtime((time_t *)&Msg->datecreated);	
	
	sprintf(DateString, "%04d%02d%02d%02d%02d%02d",
		tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
	if (WebMail->DisplayHTMLName)
	{
		char XMLName[MAX_PATH];
		
		strcpy(XMLName, WebMail->DisplayHTMLName);
		XMLName[strlen(XMLName) - 5] = 0;	// remove .html
		FileName[0] = malloc(MAX_PATH);
		FileBody[0] = malloc(100000);
		Files = 1;
		FileLen[0] = 0;
		sprintf(FileName[0], "RMS_Express_Form_%s.xml", XMLName);
		XMLPtr = FileBody[0];
		XMLPtr += sprintf(XMLPtr, XMLHeader,
			"1,0", VersionString,
			DateString,
			Session->User->Call,
			"", //Grid
			WebMail->DisplayHTMLName,
			WebMail->ReplyHTMLName,
			WebMail->OrigTo,
			"",		// CC
			Session->User->Call,
			WebMail->OrigSubject,
			WebMail->OrigBody,
			"false",		// P2P,
			"false",		//Reply
			"false",		//Forward,
			"false");			// Ack
		// create XML lines for Key/Value Pairs
		for (n = 0; n < NumKeys; n++)
		{
			if (Values[n] == NULL)
				Values[n] = _strdup("");
			XMLPtr += sprintf(XMLPtr, XMLLine, Keys[n], Values[n], Keys[n]);
		}
		XMLPtr += sprintf(XMLPtr, "%s", XMLTrailer);
		FileLen[0] = (int)(XMLPtr - FileBody[0]);
	}
	sprintf(DateString, "%04d/%02d/%02d %02d:%02d",
		tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
	
	// Get Type
	
	if (Msg->type == 'B')
		strcpy(Type, "Bulletin");
	else if (Msg->type == 'T')
		strcpy(Type, "NTS");
	// We put original To call in B2 Header
	NewMsg += sprintf(NewMsg,
		"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, WebMail->To, Msg->title, BBSName);
				
	NewMsg += sprintf(NewMsg, "Body: %d\r\n", (int)strlen(WebMail->Body));
	for (n = 0; n < Files; n++)
	{
		char * p = FileName[n], * q;
		// Remove any path
		q = strchr(p, '\\');
					
		while (q)
		{
			if (q)
				*q++ = 0;
			p = q;
			q = strchr(p, '\\');
		}
		NewMsg += sprintf(NewMsg, "File: %d %s\r\n", FileLen[n], p);
	}
	NewMsg += sprintf(NewMsg, "\r\n");
	strcpy(NewMsg, WebMail->Body);
	NewMsg += strlen(WebMail->Body);
	NewMsg += sprintf(NewMsg, "\r\n");
	for (n = 0; n < Files; n++)
	{
		memcpy(NewMsg, FileBody[n], FileLen[n]);
		NewMsg += FileLen[n];
		free(FileName[n]);
		free(FileBody[n]);
		NewMsg += sprintf(NewMsg, "\r\n");
	}
	Msg->length = (int)strlen(SaveMsg);
	Msg->B2Flags = B2Msg;
	
	if (Files)
		Msg->B2Flags |= Attachments;
	if (WebMail->Body)
		free(WebMail->Body);
	WebMail->Body = SaveMsg;
	return NULL;
}
VOID UpdateFormAction(char * Template, char * Key)
{
	char * inptr, * saveptr;
	char * varptr, * endptr;
	size_t varlen, vallen;
	char Submit[64];
	sprintf(Submit, "/Webmail/Submit?%s", Key);
	// First find the Form Action string and replace with our URL. It should have
	// action="http://{FormServer}:{FormPort}" but some forms have localhost:8001 instead
	// Also remove the OnSubmit if it contains the standard popup about having to close browser
	inptr = Template;
	saveptr = varptr = stristr(inptr, "WebMail;
	KeyValues * txtKey = WebMail->XMLKeys;
	int Len, i;
	char * inptr, * ptr;
	char * varptr, * endptr;
	int varlen, vallen;
	struct HtmlFormDir * Dir;
	char * Template = FindXMLVariable(WebMail, "reply_template");
	if (Template == NULL || Template[0] == 0)
		return 0;					// No Template
	if (Reenter)
		goto reEnter;
	WebMail->isReply = TRUE;
	if (WebMail->OrigBody)
		free(WebMail->OrigBody);
	WebMail->OrigBody = _strdup(WebMail->Body);
	
	// Add "Re: " to Subject
	ptr = WebMail->Subject;
	WebMail->Subject = malloc(strlen(Msg->title) + 10);
	sprintf(WebMail->Subject, "Re: %s", Msg->title);
	// Set To: from From:
	WebMail->To = malloc(80);
	if (Msg->emailfrom[0])
		sprintf(WebMail->To, "%s%s", Msg->from, Msg->emailfrom);
	else
		sprintf(WebMail->To, "%s", Msg->from);
	
	if (ptr)
		free(ptr);
	WebMail->txtFileName = _strdup(Template);
	// Read the .txt file
	if (WebMail->txtFile)
		free(WebMail->txtFile);
	for (i = 0; i < FormDirCount; i++)
	{
		int n;
			
		Dir = HtmlFormDirs[i];
		WebMail->txtFile = CheckFile(Dir, WebMail->txtFileName);
		if (WebMail->txtFile)
			goto gotFile;
			
		// Recurse any Subdirs
			
		n = 0;
		while (n < Dir->DirCount)
		{
			WebMail->txtFile = CheckFile(Dir->Dirs[n], WebMail->txtFileName);
			if (WebMail->txtFile)
			{
				Dir = Dir->Dirs[n];
				goto gotFile;
			}
			n++;
		}
	}
	// Template Not Found
	// Missing template
		*WebMail->RLen = sprintf(WebMail->Reply, "", WebMail->txtFileName);
		return *WebMail->RLen;
gotFile:
	WebMail->Dir = Dir;
reEnter:
	if (ParsetxtTemplate(Session, WebMail->Dir, WebMail->txtFileName, TRUE) == FALSE)
		return *WebMail->RLen;			// processing  or 
	if (WebMail->InputHTMLName == NULL)
	{
		// This is a plain text template without HTML 
/*
		if (To == NULL)
			To = "";
		if (To[0] == 0 && WebMail->To && WebMail->To[0])
			To = WebMail->To;
		if (CC == NULL)
			CC = "";
		if (CC[0] == 0 && WebMail->CC && WebMail->CC[0])
			CC = WebMail->CC;
		if (Subject == NULL)
			Subject = "";
		if (Subject[0] == 0 && WebMail->Subject && WebMail->Subject[0])
			Subject = WebMail->Subject;
	
		if (MsgBody == NULL)
			MsgBody = "";
		if (MsgBody[0] == 0 && WebMail->Body && WebMail->Body[0])
			MsgBody = WebMail->Body;
		*WebMail->RLen = sprintf(WebMail->Reply, CheckFormMsgPage, Session->Key, To, CC, Subject, MsgBody);
	*/	
		return *WebMail->RLen;
	}
	Template = CheckFile(WebMail->Dir, WebMail->InputHTMLName);
	if (Template == NULL)
	{
			// Missing HTML
		*WebMail->RLen = sprintf(WebMail->Reply, "", WebMail->InputHTMLName);
		return *WebMail->RLen;
	}
	// I've going to update the template in situ, as I can't see a better way
	// of making sure all occurances of variables in any order are substituted.
	// The space allocated to Template is twice the size of the file
	// to allow for insertions
	UpdateFormAction(Template, Session->Key);		// Update "Submit" Action
	// Search for "{var }" strings in form and replace with
	// corresponding variable from XML
	while (txtKey->Key)
	{
		char Key[256] = "{var ";
		
		strcpy(&Key[5], txtKey->Key);
		strcat(Key, "}");
		inptr = Template;
		varptr = stristr(inptr, Key);
		while (varptr)
		{
			// Move the remaining message up/down the buffer to make space for substitution
			varlen = (int)strlen(Key);
			if (txtKey->Value)
				vallen = (int)strlen(txtKey->Value);
			else vallen = 0;
			endptr = varptr + varlen;
		
			memmove(varptr + vallen, endptr, strlen(endptr) + 1);		// copy null on end
			memcpy(varptr, txtKey->Value, vallen);
			inptr = endptr + 1;
		
			varptr = stristr(inptr, Key);
		}
		txtKey++;
	}
	// Remove  from end as we add it on later
	ptr = stristr(Template, "");
		
	if (ptr)
		*ptr = 0;
		
	Len = sprintf(Reply, "%s", Template);
	free(Template);	
	return Len;
}
char * CheckFile(struct HtmlFormDir * Dir, char * FN)
{
	struct stat STAT;
	FILE * hFile;
	char MsgFile[MAX_PATH];
	char * MsgBytes;
	int ReadLen;
	int FileSize;
#ifndef WIN32
	// Need to do case insensitive file search
	DIR *dir;
	struct dirent *entry;
	char name[256];
	sprintf(name, "%s/%s/%s", BPQDirectory, Dir->FormSet, Dir->DirName);
	if (!(dir = opendir(name)))
	{
		Debugprintf("cant open forms dir %s %s %d", Dir->DirName, name, errno);
        return 0;
	}
    while ((entry = readdir(dir)) != NULL)
	{
        if (entry->d_type == DT_DIR)
                continue;
	
		if (stricmp(entry->d_name, FN) == 0)
		{
			sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), Dir->FormSet, Dir->DirName, entry->d_name);
			break;
		}
	}
    closedir(dir);
#else
	sprintf(MsgFile, "%s/%s/%s/%s", GetBPQDirectory(), Dir->FormSet, Dir->DirName, FN);
#endif
	printf("%s\n", MsgFile);
	if (stat(MsgFile, &STAT) != -1)
	{
		hFile = fopen(MsgFile, "rb");
	
		if (hFile == 0)
		{
			MsgBytes = _strdup("File is missing");
			return MsgBytes;
		}
		FileSize = STAT.st_size;
		MsgBytes = malloc(FileSize * 10);		// Allow plenty of room for template substitution
		ReadLen = (int)fread(MsgBytes, 1, FileSize, hFile);
		MsgBytes[FileSize] = 0;
		fclose(hFile);
		printf("%d %s\n", strlen(MsgBytes), MsgBytes);
		return MsgBytes;
	}
	return NULL;
}
BOOL DoSelectPrompt(struct HTTPConnectionInfo * Session, char * Select)
{
	// Send a Popup window to select value. Reply handling code will update template then reenter ParsetxtTemplate
	char popuphddr[] = 
			
		""
		""
		"%s
"
		"
"
		"";
	
	char popup[10000];
	int i, vars = 0;
	char * ptr, * ptr1;
	char * prompt;
	char * var[100];
	int len;
	WebMailInfo * WebMail = Session->WebMail;
		
	char * SelCopy = _strdup(Select + 8);		// Skip "');
	
	if (ptr)
		*ptr = 0;
	ptr = SelCopy;
	if (*ptr == '"')
	{
		// String has " " round it
		ptr++;
		ptr1 = strchr(ptr, '"');
		if (ptr1 == NULL)
			goto returnDuff;
	
		*(ptr1++) = 0;
		prompt = ptr;
	}
	else
	{
		// Normal comma terminated
		ptr1 = strchr(ptr, ',');
		if (ptr1 == NULL)
			goto returnDuff;
	
		*(ptr1++) = 0;
		prompt = ptr;
	}
	ptr = ptr1;
	while (ptr && ptr[0])
	{
		if (*ptr == '"')
		{
			// String has " " round it
			ptr++;
			ptr1 = strchr(ptr, '"');
			if (ptr1 == NULL)
				goto returnDuff;
	
			*(ptr1++) = 0;
			while(ptr1 && *ptr1 == ',')
				ptr1++;
		}
		else
		{
			// Normal comma terminated
			ptr1 = strchr(ptr, ',');
			if (ptr1)	
				*(ptr1++) = 0;
		}
		var[vars++] = ptr;
		
		ptr = ptr1;
	}
	len = sprintf(popup, popuphddr, Session->Key, prompt, vars + 1);
	for (i = 0; i < vars; i++)
	{
		char * key = strlop(var[i], '=');
		if (key == NULL)
			key = var[i];
		len += sprintf(&popup[len], " %s", key, var[i]);
	}
	len += sprintf(&popup[len], "%s   
. Update Template and re-renter ParsetxtTemplate
	WebMailInfo * WebMail = Session->WebMail;
	
	char * valptr, * varptr, * endptr;
	size_t varlen, vallen;
	char * Select = WebMail->txtFile;
	if (Select == 0)
	{
		// Missing template
		*WebMail->RLen = sprintf(WebMail->Reply, "", Session->Key);
		return;
	}
	Select = stristr(Select, "RLen = sprintf(WebMail->Reply, "", Session->Key);
		return;
	}
	varptr = Select;
	endptr = strchr(Select, '>');
	if (endptr == NULL)
	{
		*WebMail->RLen = sprintf(WebMail->Reply, "", Session->Key);
		return;
	}
	*endptr = 0;
	varlen = endptr - varptr;
	valptr = URLParams;
	// Move the remaining message up/down the buffer to make space for substitution
	vallen = strlen(valptr);
	endptr = varptr + varlen;
		
	memcpy(varptr, valptr, vallen);
	memmove(varptr + vallen, endptr + 1, strlen(endptr + 1) + 1);		// copy null on end
	if (WebMail->isReply)
		*WebMail->RLen = ReplyToFormsMessage(Session, Session->Msg, WebMail->Reply, TRUE);
	else
		GetPage(Session, NULL);
	return ;
}
BOOL ParsetxtTemplate(struct HTTPConnectionInfo * Session, struct HtmlFormDir * Dir, char * FN, BOOL isReply)
{
	WebMailInfo * WebMail = Session->WebMail;
	KeyValues * txtKey = WebMail->txtKeys;
	char * MsgBytes;
	char * txtFile;
	char * ptr, *ptr1;
	char * InputName = NULL;	// HTML to input message
	char * ReplyName = NULL;
	char * To = NULL;
	char * Subject = NULL;
	char * MsgBody = NULL;
	char * Select = NULL;
	char * Ask = NULL;
	char Date[16];
	char UDate[16];
	char DateTime[32];
	char UDateTime[32];
	char Day[16];
	char UDay[16];
	char UDTG[32];
	char Seq[16];
	char FormDir[MAX_PATH];
	double Lat;
	double Lon;
	char LatString[32], LonString[32], GPSString[32];
	BOOL GPSOK;
	
	struct tm * tm;
	time_t NOW;
	// Template is now read before entering here
	MsgBytes = WebMail->txtFile;
	// if Template uses  or  get the values
	Select = stristr(MsgBytes, "tm_year + 1900,tm->tm_mon + 1, tm->tm_mday);
	
	sprintf(DateTime, "%04d-%02d-%02d %02d:%02d:%02d",
		tm->tm_year + 1900,tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
	
	strcpy(Day, longday[tm->tm_wday]);
	tm = gmtime(&NOW);				
	sprintf(UDate, "%04d-%02d-%02dZ",
		tm->tm_year + 1900,tm->tm_mon + 1, tm->tm_mday);
	sprintf(UDateTime, "%04d-%02d-%02d %02d:%02d:%02dZ",
		tm->tm_year + 100,tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
	sprintf(UDTG, "%02d%02d%02dZ %s %04d",
		tm->tm_mday, tm->tm_hour, tm->tm_min, month[tm->tm_mon], tm->tm_year + 1900);
	strcpy(UDay, longday[tm->tm_wday]);
	sprintf(Seq, "%d", Session->User->WebSeqNo);
	sprintf(FormDir, "/WebMail/WMFile/%s/%s/", WebMail->Dir->FormSet, WebMail->Dir->DirName);	
	// Keep SeqNo at front
	
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(Seq);
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(DateTime);
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(UDateTime);
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(Date);
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(UDate);
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(&DateTime[11]);
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(&UDateTime[11]);
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(Day);
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(UDay);
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(UDTG);
	// Try to get position from APRS
	GPSOK = GetAPRSLatLon(&Lat, &Lon);
	GPSOK = GetAPRSLatLonString(&LatString[1], &LonString[1]);
	memmove(LatString, &LatString[1], 2);
	memmove(LonString, &LonString[1], 3);
	LatString[2] = '-';
	LonString[3] = '-';
	sprintf(GPSString,"%s %s", LatString, LonString);
	txtKey->Key = _strdup("");
	if (GPSOK)
		txtKey++->Value = _strdup(GPSString);
	else
		txtKey++->Value = _strdup("");
	
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(GPSString);
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(VersionString);
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(Session->User->Call);
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup("");
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup("");
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(Session->User->Call);
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup("");
	txtKey->Key = _strdup("");
//	if (WebMail->OrigBody)
//		txtKey++->Value = _strdup(WebMail->OrigBody);
//	else
		txtKey++->Value = _strdup("");
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup("");
	if (isReply)
	{
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("True");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("False");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("False");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup(WebMail->Body);
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup(WebMail->Msg->bid);
		// Get Timestamp from Message
		tm = gmtime((time_t *)&WebMail->Msg->datecreated);				
		sprintf(Date, "%02d-%02d-%02d",
			tm->tm_year - 100,tm->tm_mon + 1, tm->tm_mday);
	
		sprintf(DateTime, "%02d-%02d-%02d %02d:%02d",
			tm->tm_year - 100,tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);
	
		strcpy(Day, longday[tm->tm_wday]);
			tm = gmtime((time_t *)&WebMail->Msg->datecreated);				
		sprintf(UDate, "%02d-%02d-%02dZ",
			tm->tm_year - 100,tm->tm_mon + 1, tm->tm_mday);
		sprintf(UDateTime, "%02d-%02d-%02d %02d:%02dZ",
			tm->tm_year - 100,tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min);
		sprintf(UDTG, "%02d%02d%02dZ %s %04d",
			tm->tm_mday, tm->tm_hour, tm->tm_min, month[tm->tm_mon], tm->tm_year + 1900);
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup(UDate);
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup(UDate);
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup(&UDateTime[9]);
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup(Date);
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup(&UDateTime[9]);
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup(UDTG);
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
	}
	else
	{
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("False");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("False");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("False");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
		txtKey->Key = _strdup("");
		txtKey++->Value = _strdup("");
	}
	txtKey->Key = _strdup("");
	txtKey++->Value = _strdup(FormDir);		//Form Folder
	// Do standard Variable substitution on file
	DoStandardTemplateSubsitutions(Session, WebMail->txtFile);
	txtFile = _strdup(WebMail->txtFile);		// We chop up and modify bits of txtFile, so need copy
	// Scan template line by line extracting useful information
	ptr = txtFile;
	ptr1 = strchr(ptr, '\r');
		
	while (ptr1)
	{
		if (_memicmp(ptr, "Msg:", 4) == 0)
		{
			// Rest is message body. May need  substitutions
			if (WebMail->Body)
				free(WebMail->Body);
			WebMail->Body = _strdup(ptr + 4);
			break;
		}
		// Can now terminate lines
		*ptr1++ = 0;
		while (*ptr1 == '\r' || *ptr1 == '\n')
			*ptr1++ = 0;
		if (_memicmp(ptr, "Form:", 5) == 0)
		{
			InputName = &ptr[5];
	
			while (*InputName == ' ')		// Remove leading spaces
				InputName++;
			WebMail->InputHTMLName = _strdup(InputName);
			WebMail->DisplayHTMLName = strlop(WebMail->InputHTMLName, ',');
			if (WebMail->DisplayHTMLName)
			{
				while (*WebMail->DisplayHTMLName == ' ')		// Remove leading spaces
				WebMail->DisplayHTMLName++;
				WebMail->DisplayHTMLName = _strdup(WebMail->DisplayHTMLName);
			}
		}
		else if (_memicmp(ptr, "ReplyTemplate:",14) == 0)
		{
			char * end;
			ReplyName = &ptr[14];
	
			while (*ReplyName == ' ')
				ReplyName++;
			
			strlop(ReplyName, '\r');		// Terminate
			// Filename may have embedded spaces, so have to scan from end, not use strlop
			end = ReplyName + strlen(ReplyName) - 1;
			while (*end == ' ')
				*end-- = 0;
			WebMail->ReplyHTMLName = _strdup(ReplyName);
		}
		else if (_memicmp(ptr, "To:", 3) == 0)
		{
			if (strlen(ptr) > 5)
				WebMail->To = _strdup(&ptr[3]);
		}
		else if (_memicmp(ptr, "Subj:", 5) == 0)
		{
			if (ptr[5] == ' ')		// May have space after :
				ptr++;
			if (strlen(ptr) > 6)
				WebMail->Subject = _strdup(&ptr[5]);
		}
		else if (_memicmp(ptr, "Subject:", 8) == 0)
		{
			if (ptr[8] == ' ')
				ptr++;
			if (strlen(ptr) > 9)
				WebMail->Subject = _strdup(&ptr[8]);
		}
		else if (_memicmp(ptr, "Def:", 4) == 0)
		{
			// Def: MsgOriginalBody= 
			char * val = strlop(ptr, '=');
			if (val)
			{
				// Make Room for {} delimiters
				memmove(ptr, ptr + 1, strlen(ptr)); 
				ptr[3] = '<';
				ptr[strlen(ptr)] = '>';
				
				while (val[strlen(val) - 1] == ' ')
					val[strlen(val) - 1]  = 0;
				txtKey->Key = _strdup(&ptr[3]);
				txtKey++->Value = _strdup(val);		//Form Folder
			}
		}
		else if (_memicmp(ptr, "Type:", 5) == 0)
		{
			if (stristr(ptr, "Winlink"))
				WebMail->Winlink = TRUE;
			else if (stristr(ptr, "P2P"))
				WebMail->P2P = TRUE;
			else if (stristr(ptr, "Packet"))
				WebMail->Packet = TRUE;
		}
		else if (_memicmp(ptr, "SeqInc:", 7) == 0)
		{
			int SeqInc = 1;
			
			while (ptr[7] == ' ')
				ptr++;
			if (strlen(&ptr[7]))
				SeqInc = atoi(&ptr[7]);
	
			Session->User->WebSeqNo += SeqInc;
			sprintf(WebMail->txtKeys[0].Value, "%d", Session->User->WebSeqNo);
		}
		else if (_memicmp(ptr, "SeqSet:", 7) == 0)
		{
			int SeqSet = 0;
			
			while (ptr[7] == ' ')
				ptr++;
			if (strlen(&ptr[7]))
				SeqSet = atoi(&ptr[7]);
	
			Session->User->WebSeqNo = SeqSet;
			sprintf(WebMail->txtKeys[0].Value, "%d", Session->User->WebSeqNo);
		}
		// Attach:file1, file2, ... - If present, specifies one or more files to attach to the message.
		//Readonly: Yes | No - If Readonly is set to Yes, then the message is created by the form and it cannot be edited by the user.
		ptr = ptr1;
		ptr1 = strchr(ptr, '\r');
	}
	if (WebMail->ReplyHTMLName == NULL)
		WebMail->ReplyHTMLName = _strdup("");
	free(txtFile);
	return TRUE;
}
VOID FormatTime2(char * Time, time_t cTime)
{
	struct tm * TM;
	TM = gmtime(&cTime);
	sprintf(Time, "%s, %02d %s %3d %02d:%02d:%02d GMT", dat[TM->tm_wday], TM->tm_mday, month[TM->tm_mon],
		TM->tm_year + 1900, TM->tm_hour, TM->tm_min, TM->tm_sec);
}
VOID DownloadAttachments(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Param)
{
	WebMailInfo * WebMail = Session->WebMail;
	char TimeString[64];
	char FileTimeString[64];
	int file = atoi(Param);
	file--;			// Sent at +1 in case no downloadable attachments
	if (file == -1)
	{
		// User has gone back, then selected "No file Selected"
		// Or no files
		*RLen = sprintf(Reply, "");
		return;
	}
	FormatTime2(FileTimeString, time(NULL));
	FormatTime2(TimeString, time(NULL));
	*RLen =	sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n"
		"Content-Type: application/octet-stream\r\n"
		"Content-Disposition: attachment; filename=\"%s\"\r\n"
		"Date: %s\r\n"
		"Last-Modified: %s\r\n" 
		"\r\n", WebMail->FileLen[file], WebMail->FileName[file], TimeString, FileTimeString);
	memcpy(&Reply[*RLen], WebMail->FileBody[file], WebMail->FileLen[file]); 
	*RLen += WebMail->FileLen[file];
	return;
}
VOID getAttachmentList(struct HTTPConnectionInfo * Session, char * Reply, int * RLen, char * Rest)
{
	char popuphddr[] = 
			
		""
		""
		"");
	*RLen = sprintf(Reply, "%s", popup);
	return;
}
char * WebFindPart(char ** Msg, char * Boundary, int * PartLen, char * End)
{
	char * ptr = *Msg;
	char * Msgptr = *Msg;
	size_t BLen = strlen(Boundary);
	char * Part;
	while(ptr < End)				// Just in case we run off end
	{
		if (*ptr == '-' && *(ptr+1) == '-')
		{
			if (memcmp(&ptr[2], Boundary, BLen) == 0)
			{
				// Found Boundary
				size_t Partlen = ptr - Msgptr;
				Part = malloc(Partlen + 1);
				memcpy(Part, Msgptr, Partlen);
				Part[Partlen] = 0;
				*Msg = ptr + BLen + 4;
		
				*PartLen = (int)Partlen;
				return Part; 
			}
		}
		ptr ++;
	}
	return NULL;
}
int ProcessWebmailWebSock(char * MsgPtr, char * OutBuffer)
{
	int Len = 129;
	char * Key = strlop(MsgPtr, '&');
	struct HTTPConnectionInfo * Session;
	struct UserInfo * User;
	int m;
	struct MsgInfo * Msg;
	char * ptr = &OutBuffer[10];			// allow room for full payload length (64 bit)
	int n = NumberofMessages;
	char Via[64];
	int Count = 0;
	if (Key == 0)
		return 0;
	Session = FindWMSession(Key);
	if (Session == NULL)
		return 0;
	User = Session->User;
	// Outbuffer is 250000
//	ptr += sprintf(ptr, "\r\n"
	ptr += sprintf(ptr, "
");
	ptr += sprintf(ptr, "%s", "     #  Date  XX   Len To      @       From    Subject\r\n\r\n");
	for (m = LatestMsg; m >= 1; m--)
	{
		if (ptr > &OutBuffer[244000])
			break;						// protect buffer
		Msg = GetMsgFromNumber(m);
		if (Msg == 0 || Msg->type == 0 || Msg->status == 0)
			continue;					// Protect against corrupt messages
		
		if (Msg && CheckUserMsg(Msg, User->Call, User->flags & F_SYSOP))
		{
			char UTF8Title[4096];
			char  * EncodedTitle;
			
			// List if it is the right type and in the page range we want
			if (Session->WebMailTypes[0] && strchr(Session->WebMailTypes, Msg->type) == 0) 
				continue;
			// All Types or right Type. Check Mine Flag
			if (Session->WebMailMine)
			{
				// Only list if to or from me
				if (strcmp(User->Call, Msg->to) != 0 && strcmp(User->Call, Msg->from) != 0)
					continue;
			}
			if (Session->WebMailMyTX)
			{
				// Only list if to or from me
				if (strcmp(User->Call, Msg->from) != 0)
					continue;
			}
			if (Session->WebMailMyRX)
			{
				// Only list if to or from me
				if (strcmp(User->Call, Msg->to) != 0)
					continue;
			}
			if (Count++ < Session->WebMailSkip)
				continue;
			strcpy(Via, Msg->via);
			strlop(Via, '.');
			// make sure title is HTML safe (no < > etc) and UTF 8 encoded
			EncodedTitle = doXMLTransparency(Msg->title);
			memset(UTF8Title, 0, 4096);		// In case convert fails part way through
			ConvertTitletoUTF8(Session->WebMail, EncodedTitle, UTF8Title, 4095);
			free(EncodedTitle);
			ptr += sprintf(ptr, "%6d  %s %c%c %5d %-8s%-8s%-8s%s\r\n",
				Key, Msg->number, Msg->number,
				FormatDateAndTime((time_t)Msg->datecreated, TRUE), Msg->type,
				Msg->status, Msg->length, Msg->to, Via,
				Msg->from, UTF8Title);
			n--;
			if (n == 0)
				break;
		}
	}
	ptr += sprintf(&ptr[strlen(ptr)], "  \r\n");
	Len = ptr - &OutBuffer[10];
	OutBuffer[0] = 0x81;		// Fin, Data
	if (Len < 126)
	{
		OutBuffer[1] = Len;
		memmove(&OutBuffer[2], &OutBuffer[10], Len);
		return Len + 2;
	}
	else if (Len < 65536)
	{
		OutBuffer[1] = 126;			// Unmasked, Extended Len 16
		OutBuffer[2] = Len >> 8;
		OutBuffer[3] = Len & 0xff;
		memmove(&OutBuffer[4], &OutBuffer[10], Len);
		return Len + 4;
	}
	else
	{
		OutBuffer[1] = 127;			// Unmasked, Extended Len 64 bits
		// Len is 32 bits, so pad with zeros
		OutBuffer[2] = 0;
		OutBuffer[3] = 0;
		OutBuffer[4] = 0;
		OutBuffer[5] = 0;
		OutBuffer[6] = (Len >> 24) & 0xff;
		OutBuffer[7] = (Len >> 16) & 0xff;
		OutBuffer[8] = (Len >> 8) & 0xff;
		OutBuffer[9] = Len & 0xff;
		return Len + 10;
	}
}