/*
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);
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)
{
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
Debugprintf("Recurse %s/%s/%s", FormSet, DirName, entry->d_name);
continue;
}
// see if initial html
// if (stristr(entry->d_name, "initial.html"))
{
// 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);
return 0;
}
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 (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, 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 | ", Key, Msg->number);
WebMail->Files = 0;
ptr1 = MsgBytes;
// ptr += sprintf(ptr, "Message has Attachments\r\n\r\n");
while(*ptr1 != 13)
{
ptr2 = strchr(ptr1, 10); // Find CR
if (memcmp(ptr1, "Body: ", 6) == 0)
{
BodyLen = atoi(&ptr1[6]);
}
if (memcmp(ptr1, "File: ", 6) == 0)
{
char * ptr3 = strchr(&ptr1[6], ' '); // Find Space
*(ptr2 - 1) = 0;
WebMail->FileLen[WebMail->Files] = atoi(&ptr1[6]);
WebMail->FileName[WebMail->Files++] = _strdup(&ptr3[1]);
*(ptr2 - 1) = ' '; // put space back
}
ptr1 = ptr2;
ptr1++;
}
ptr1 += 2; // Over Blank Line and Separator
// ptr1 is pointing to body. Save for possible reply
WebMail->Body = malloc(BodyLen + 2);
memcpy(WebMail->Body, ptr1, BodyLen);
WebMail->Body[BodyLen] = 0;
*(ptr1 + BodyLen) = 0;
ptr += sprintf(ptr, "%s", MsgBytes); // B2 Header and Body
ptr1 += BodyLen + 2; // to first file
// Save pointers to file
attptr = ptr1;
for (i = 0; i < WebMail->Files; i++)
{
WebMail->FileBody[i] = malloc(WebMail->FileLen[i]);
memcpy(WebMail->FileBody[i], attptr, WebMail->FileLen[i]);
attptr += (WebMail->FileLen[i] + 2);
}
// if first (only??) attachment is XML and filename
// starts "RMS_Express_Form" process as HTML Form
if (DisplayHTML && _memicmp(ptr1, "FileName[0], "RMS_Express_Form_", 16) == 0)
{
int Len = DisplayWebForm(Session, Msg, WebMail->FileName[0], ptr1, Reply, MsgBytes, BodyLen + 32); // 32 for added "has attachments"
free(MsgBytes);
// Flag as read
if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0)))
{
if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D'))
{
if (Msg->status != 'Y')
{
Msg->status = 'Y';
Msg->datechanged=time(NULL);
SaveMessageDatabase();
}
}
}
return Len;
}
for (i = 0; i < WebMail->Files; i++)
{
int n;
char * p = ptr1;
char c;
// Check if message is probably binary
int BinCount = 0;
NewLen = WebMail->FileLen[i];
for (n = 0; n < NewLen; n++)
{
c = *p;
if (c==0 || (c & 128))
BinCount++;
p++;
}
if (BinCount > NewLen/10)
{
// File is probably Binary
ptr += sprintf(ptr, "\rAttachment %s is a binary file\r", WebMail->FileName[i]);
}
else
{
*(ptr1 + NewLen) = 0;
ptr += sprintf(ptr, "\rAttachment %s\r\r", WebMail->FileName[i]);
RemoveLF(ptr1, NewLen + 1); // Removes LF after CR but not on its own
ptr += sprintf(ptr, "%s\r\r", ptr1);
User->Total.MsgsSent[Index] ++;
User->Total.BytesForwardedOut[Index] += NewLen;
}
ptr1 += WebMail->FileLen[i];
ptr1 +=2; // Over separator
}
free(Save);
ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from);
RemoveLF(Message, (int)strlen(Message) + 1); // Removes LF after CR but not on its own
if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0)))
{
if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D'))
{
if (Msg->status != 'Y')
{
Msg->status = 'Y';
Msg->datechanged=time(NULL);
SaveMessageDatabase();
}
}
}
return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle);
}
// Remove B2 Headers (up to the File: Line)
// ptr1 = strstr(MsgBytes, "Body:");
// if (ptr1)
// MsgBytes = ptr1;
}
// Body may have cr cr lf which causes double space
crcrptr = strstr(MsgBytes, "\r\r\n");
while (crcrptr)
{
*crcrptr = ' ';
crcrptr = strstr(crcrptr, "\r\r\n");
}
// Remove lf chars
msgLen = RemoveLF(MsgBytes, msgLen);
User->Total.MsgsSent[Index] ++;
// User->Total.BytesForwardedOut[Index] += Length;
// if body not UTF-8, convert it
if (WebIsUTF8(MsgBytes, msgLen) == FALSE)
{
// With Windows it is simple - convert using current codepage
// I think the only reliable way is to convert to unicode and back
size_t origlen = msgLen + 1;
UCHAR * BufferB = malloc(2 * origlen);
#ifdef WIN32
WCHAR * BufferW = malloc(2 * origlen);
int wlen;
int len = (int)origlen;
wlen = MultiByteToWideChar(CP_ACP, 0, MsgBytes, len, BufferW, (int)(origlen * 2));
len = WideCharToMultiByte(CP_UTF8, 0, BufferW, wlen, BufferB, (int)(origlen * 2), NULL, NULL);
free(Save);
Save = MsgBytes = BufferB;
free(BufferW);
msgLen = len - 1; // exclude NULL
#else
size_t left = 2 * msgLen;
size_t len = msgLen;
int ret;
UCHAR * BufferBP = BufferB;
char * orig = MsgBytes;
MsgBytes[msgLen] = 0;
iconv_t * icu = Session->WebMail->iconv_toUTF8;
if (icu == NULL)
icu = Session->WebMail->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252");
if (icu == (iconv_t) -1)
{
Session->WebMail->iconv_toUTF8 = NULL;
strcpy(BufferB, MsgBytes);
}
else
{
iconv(icu, NULL, NULL, NULL, NULL); // Reset State Machine
ret = iconv(icu, &MsgBytes, &len, (char ** __restrict__)&BufferBP, &left);
}
free(Save);
Save = MsgBytes = BufferB;
msgLen = strlen(MsgBytes);
#endif
}
// ptr += sprintf(ptr, "%s", MsgBytes);
memcpy(ptr, MsgBytes, msgLen);
ptr += msgLen;
ptr[0] = 0;
free(Save);
ptr += sprintf(ptr, "\r\r[End of Message #%d from %s]\r", Number, Msg->from);
if ((_stricmp(Msg->to, User->Call) == 0) || ((User->flags & F_SYSOP) && (_stricmp(Msg->to, "SYSOP") == 0)))
{
if ((Msg->status != 'K') && (Msg->status != 'H') && (Msg->status != 'F') && (Msg->status != 'D'))
{
if (Msg->status != 'Y')
{
Msg->status = 'Y';
Msg->datechanged=time(NULL);
SaveMessageDatabase();
}
}
}
}
else
{
ptr += sprintf(ptr, "File for Message %d not found\r", Number);
}
if (DisplayHTML && stristr(Message, "html>"))
DisplayStyle = "div"; // Use div so HTML and XML are interpreted
return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, DisplayStyle, Message, DisplayStyle);
}
int KillWebMailMessage(char * Reply, char * Key, struct UserInfo * User, int Number)
{
struct MsgInfo * Msg;
char Message[100] = "";
Msg = GetMsgFromNumber(Number);
if (Msg == NULL)
{
sprintf(Message, "Message %d not found", Number);
goto returnit;
}
if (OkToKillMessage(User->flags & F_SYSOP, User->Call, Msg))
{
FlagAsKilled(Msg, TRUE);
sprintf(Message, "Message #%d Killed\r", Number);
goto returnit;
}
sprintf(Message, "Not your message\r");
returnit:
return sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, "", Key, Key, Key, "div", Message, "div");
}
void freeKeys(KeyValues * Keys)
{
while (Keys->Key)
{
free(Keys->Key);
free(Keys->Value);
Keys++;
}
}
void FreeWebMailFields(WebMailInfo * WebMail)
{
// release any malloc'ed resources
int i;
char * SaveReply;
int * SaveRlen;
if (WebMail == NULL)
return;
if (WebMail->txtFile)
free(WebMail->txtFile);
if (WebMail->txtFileName)
free(WebMail->txtFileName);
if (WebMail->InputHTMLName)
free(WebMail->InputHTMLName);
if (WebMail->DisplayHTMLName)
free(WebMail->DisplayHTMLName);
if (WebMail->ReplyHTMLName)
free(WebMail->ReplyHTMLName);
if (WebMail->To)
free(WebMail->To);
if (WebMail->CC)
free(WebMail->CC);
if (WebMail->Subject)
free(WebMail->Subject);
if (WebMail->BID)
free(WebMail->BID);
if (WebMail->Body)
free(WebMail->Body);
if (WebMail->XML)
free(WebMail->XML);
if (WebMail->XMLName)
free(WebMail->XMLName);
if (WebMail->OrigTo)
free(WebMail->OrigTo);
if (WebMail->OrigSubject)
free(WebMail->OrigSubject);
if (WebMail->OrigBID)
free(WebMail->OrigBID);
if (WebMail->OrigBody)
free(WebMail->OrigBody);
freeKeys(WebMail->txtKeys);
freeKeys(WebMail->XMLKeys);
for (i = 0; i < WebMail->Files; i++)
{
free(WebMail->FileBody[i]);
free(WebMail->FileName[i]);
}
if (WebMail->Header)
free(WebMail->Header);
if (WebMail->Footer)
free(WebMail->Footer);
SaveReply = WebMail->Reply;
SaveRlen = WebMail->RLen;
#ifndef WIN32
if (WebMail->iconv_toUTF8)
iconv_close(WebMail->iconv_toUTF8);
#endif
memset(WebMail, 0, sizeof(WebMailInfo));
WebMail->Reply = SaveReply;
WebMail->RLen = SaveRlen;
return;
}
void ProcessWebMailMessage(struct HTTPConnectionInfo * Session, char * Key, BOOL LOCAL, char * Method, char * NodeURL, char * input, char * Reply, int * RLen, int InputLen)
{
char * URLParams = strlop(Key, '&');
int ReplyLen;
char Appl = 'M';
// Webmail doesn't use the normal Mail Key.
// webscript.js doesn't need a key
if (_stricmp(NodeURL, "/WebMail/webscript.js") == 0)
{
if (jsTemplate)
free(jsTemplate);
jsTemplate = GetTemplateFromFile(2, "webscript.js");
ReplyLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n"
"Cache-Control: max-age=60\r\nContent-Type: text/javascript\r\n\r\n%s", (int)strlen(jsTemplate), jsTemplate);
*RLen = ReplyLen;
return;
}
// Neither do js or file downloads
if (_memicmp(NodeURL, "/WebMail/WMFile/", 16) == 0)
{
int FileSize;
char * MsgBytes;
char MsgFile[512];
FILE * hFile;
size_t ReadLen;
char TimeString[64];
char FileTimeString[64];
struct stat STAT;
char * FN = &NodeURL[16];
char * fileBit = FN;
char * ext;
UndoTransparency(FN);
ext = strchr(FN, '.');
sprintf(MsgFile, "%s/%s", BPQDirectory, FN);
while (strchr(fileBit, '/'))
fileBit = strlop(fileBit, '/');
if (stat(MsgFile, &STAT) == -1)
{
*RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n");
return;
}
hFile = fopen(MsgFile, "rb");
if (hFile == 0)
{
*RLen = sprintf(Reply, "HTTP/1.1 404 Not Found\r\nContent-Length: 16\r\n\r\nPage not found\r\n");
return;
}
FileSize = STAT.st_size;
MsgBytes = malloc(FileSize + 1);
ReadLen = fread(MsgBytes, 1, FileSize, hFile);
fclose(hFile);
FormatTime2(FileTimeString, STAT.st_ctime);
FormatTime2(TimeString, time(NULL));
if (_stricmp(ext, ".htm") == 0 || _stricmp(ext, ".html") == 0 || _stricmp(ext, ".css") == 0 || _stricmp(ext, ".js") == 0)
{
*RLen = sprintf(Reply, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n"
"Content-Type: text/css\r\n"
"Date: %s\r\n"
"Last-Modified: %s\r\n"
"\r\n%s", FileSize, TimeString, FileTimeString, MsgBytes);
}
else
{
*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%s", FileSize, fileBit, TimeString, FileTimeString, MsgBytes);
}
free (MsgBytes);
return;
}
Session = NULL;
if (Key && Key[0])
Session = FindWMSession(Key);
if (Session == NULL)
{
// Lost Session
if (LOCAL)
{
Session = AllocateWebMailSession();
Key = Session->Key;
if (SYSOPCall[0])
Session->User = LookupCall(SYSOPCall);
else
Session->User = LookupCall(BBSName);
if (Session->User)
{
strcpy(NodeURL, "/WebMail/WebMail");
Session->WebMailSkip = 0;
Session->WebMailLastUsed = time(NULL);
}
}
else
{
// Send Login Page unless Signon request
if (_stricmp(NodeURL, "/WebMail/Signon") != 0 || strcmp(Method, "POST") != 0)
{
ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
*RLen = ReplyLen;
return;
}
}
}
if (strcmp(Method, "POST") == 0)
{
if (_stricmp(NodeURL, "/WebMail/Signon") == 0)
{
char * msg = strstr(input, "\r\n\r\n"); // End of headers
char * user, * password, * Key;
char Msg[128];
int n;
if (msg)
{
struct UserInfo * User;
if (strstr(msg, "Cancel=Cancel"))
{
*RLen = sprintf(Reply, "");
return;
}
// Webmail Gets Here with a dummy Session
Session = AllocateWebMailSession();
Session->WebMail->Reply = Reply;
Session->WebMail->RLen = RLen;
Key = Session->Key;
user = strtok_s(&msg[9], "&", &Key);
password = strtok_s(NULL, "=", &Key);
password = Key;
Session->User = User = LookupCall(user);
if (User)
{
// Check Password
if (password[0] && strcmp(User->pass, password) == 0)
{
// send Message Index
Session->WebMailLastUsed = time(NULL);
Session->WebMailSkip = 0;
Session->WebMailMine = FALSE;
if (WebMailTemplate)
{
free(WebMailTemplate);
WebMailTemplate = NULL;
}
if (User->flags & F_Excluded)
{
n = sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s Rejected by Exclude Flag", _strupr(user));
WriteLogLine(NULL, '|',Msg, n, LOG_BBS);
ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
*RLen = ReplyLen;
return;
}
*RLen = SendWebMailHeader(Reply, Session->Key, Session);
n=sprintf_s(Msg, sizeof(Msg), "Webmail Connect from %s", _strupr(user));
WriteLogLine(NULL, '|',Msg, n, LOG_BBS);
return;
}
}
// Bad User or Pass
ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
*RLen = ReplyLen;
return;
}
}
Session->WebMail->Reply = Reply;
Session->WebMail->RLen = RLen;
if (_stricmp(NodeURL, "/WebMail/EMSave") == 0)
{
// Save New Message
SaveNewMessage(Session, input, Reply, RLen, Key, InputLen);
return;
}
if (_stricmp(NodeURL, "/WebMail/Submit") == 0)
{
// Get the POST data from the page and place in message
char * param = strstr(input, "\r\n\r\n"); // End of headers
WebMailInfo * WebMail = Session->WebMail;
if (WebMail == NULL)
return; // Can't proceed if we have no info on form
ProcessFormInput(Session, input, Reply, RLen, InputLen);
return;
}
if (_stricmp(NodeURL, "/WebMail/FormMsgSave") == 0)
{
// Save New Message
SaveTemplateMessage(Session, input, Reply, RLen, Key);
return;
}
if (_stricmp(NodeURL, "/WebMail/GetTemplates") == 0)
{
SendTemplateSelectScreen(Session, input, InputLen);
return;
}
// End of POST section
}
if (_stricmp(NodeURL, "/WebMail/WMLogout") == 0)
{
Session->Key[0] = 0;
Session->WebMailLastUsed = 0;
ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
*RLen = ReplyLen;
return;
}
if ((_stricmp(NodeURL, "/WebMail/MailEntry") == 0) ||
(_stricmp(NodeURL, "/WebMail") == 0) ||
(_stricmp(NodeURL, "/WebMail/") == 0))
{
// Entry from Menu if signed in, continue. If not and Localhost
// signin as sysop.
if (Session->User == NULL)
{
// Not yet signed in
if (LOCAL)
{
// Webmail Gets Here with a dummy Session
Session = AllocateWebMailSession();
Session->WebMail->Reply = Reply;
Session->WebMail->RLen = RLen;
Key = Session->Key;
if (SYSOPCall[0])
Session->User = LookupCall(SYSOPCall);
else
Session->User = LookupCall(BBSName);
if (Session->User)
{
strcpy(NodeURL, "/WebMail/WebMail");
Session->WebMailSkip = 0;
Session->WebMailLastUsed = time(NULL);
}
}
else
{
// Send Login Page
ReplyLen = sprintf(Reply, WebMailSignon, BBSName, BBSName);
*RLen = ReplyLen;
return;
}
}
}
Session->WebMail->Reply = Reply;
Session->WebMail->RLen = RLen;
if (_stricmp(NodeURL, "/WebMail/WebMail") == 0)
{
if (WebMailTemplate)
{
free(WebMailTemplate);
WebMailTemplate = NULL;
}
Session->WebMailSkip = 0;
Session->WebMailMine = FALSE;
*RLen = SendWebMailHeader(Reply, Session->Key, Session);
return;
}
if (_stricmp(NodeURL, "/WebMail/WMAll") == 0)
{
Session->WebMailSkip = 0;
Session->WebMailTypes[0] = 0;
Session->WebMailMine = FALSE;
*RLen = SendWebMailHeader(Reply, Session->Key, Session);
return;
}
if (_stricmp(NodeURL, "/WebMail/WMB") == 0)
{
Session->WebMailSkip = 0;
strcpy(Session->WebMailTypes, "B");
Session->WebMailMine = FALSE;
*RLen = SendWebMailHeader(Reply, Session->Key, Session);
return;
}
if (_stricmp(NodeURL, "/WebMail/WMP") == 0)
{
Session->WebMailSkip = 0;
strcpy(Session->WebMailTypes, "P");
Session->WebMailMine = FALSE;
*RLen = SendWebMailHeader(Reply, Session->Key, Session);
return;
}
if (_stricmp(NodeURL, "/WebMail/WMT") == 0)
{
Session->WebMailSkip = 0;
strcpy(Session->WebMailTypes, "T");
Session->WebMailMine = FALSE;
*RLen = SendWebMailHeader(Reply, Session->Key, Session);
return;
}
if (_stricmp(NodeURL, "/WebMail/WMMine") == 0)
{
Session->WebMailSkip = 0;
Session->WebMailTypes[0] = 0;
Session->WebMailMine = TRUE;
*RLen = SendWebMailHeader(Reply, Session->Key, Session);
return;
}
if (_stricmp(NodeURL, "/WebMail/WMSame") == 0)
{
*RLen = SendWebMailHeader(Reply, Session->Key, Session);
return;
}
if (_stricmp(NodeURL, "/WebMail/WMAuto") == 0)
{
// Auto Refresh Version of index page. Uses Web Sockets
char Page[4096];
char WebSockPage[] =
"\r\n"
" \r\n"
" \r\n"
" \r\n"
" \r\n"
"\r\n"
"\r\n"
"\r\n"
"WebMail \r\n"
"\r\n"
"\r\n"
" %s Webmail Interface - User %s - Message List
\r\n"
"\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);
*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[] =
" "
"";
char Temp[1024];
char ReplyAddr[128];
Msg = GetMsgFromNumber(n);
if (Msg == NULL)
{
sprintf(Message, "Message %d not found", n);
*RLen = sprintf(Reply, "%s", Message);
return;
}
Session->WebMail->Msg = Msg;
// See if the message was displayed in an HTML form with a reply template
*RLen = ReplyToFormsMessage(Session, Msg, Reply, FALSE);
// If couldn't build reply form use normal text reply
if (*RLen)
return;
sprintf(Temp, Button, Key);
if (stristr(Msg->title, "Re:") == 0)
sprintf(Title, "Re:%s", Msg->title);
else
sprintf(Title, "%s", Msg->title);
strcpy(ReplyAddr, Msg->from);
strcat(ReplyAddr, Msg->emailfrom);
EncodedTitle = doXMLTransparency(Msg->title);
*RLen = sprintf(Reply, MsgInputPage, Key, Msg->from, Temp, EncodedTitle , "");
free(EncodedTitle);
return;
}
if (strcmp(NodeURL, "/WebMail/WM") == 0)
{
// Read Message
int n = 0;
if (URLParams)
n = atoi(URLParams);
if (WebMailMsgTemplate)
free(WebMailMsgTemplate);
WebMailMsgTemplate = GetTemplateFromFile(5, "WebMailMsg.txt");
*RLen = ViewWebMailMessage(Session, Reply, n, TRUE);
return;
}
if (strcmp(NodeURL, "/WebMail/WMPrev") == 0)
{
// Read Previous Message
int m;
struct MsgInfo * Msg;
struct UserInfo * User = Session->User;
for (m = Session->WebMail->CurrentMessageIndex - 1; m >= 1; m--)
{
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))
{
// Display 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;
}
*RLen = ViewWebMailMessage(Session, Reply, m, TRUE);
return;
}
}
// No More
*RLen = sprintf(Reply, "", Session->Key);
return;
}
if (strcmp(NodeURL, "/WebMail/WMNext") == 0)
{
// Read Previous Message
int m;
struct MsgInfo * Msg;
struct UserInfo * User = Session->User;
for (m = Session->WebMail->CurrentMessageIndex + 1; m <= LatestMsg; m++)
{
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))
{
// Display 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;
}
*RLen = ViewWebMailMessage(Session, Reply, m, TRUE);
return;
}
}
// No More
*RLen = sprintf(Reply, "", Session->Key);
return;
}
if (strcmp(NodeURL, "/WebMail/DisplayText") == 0)
{
// Read Message
int n = 0;
if (URLParams)
n = atoi(URLParams);
if (WebMailMsgTemplate)
free(WebMailMsgTemplate);
WebMailMsgTemplate = GetTemplateFromFile(5, "WebMailMsg.txt");
*RLen = ViewWebMailMessage(Session, Reply, n, FALSE);
return;
}
if (memcmp(NodeURL, "/WebMail/WMDel/", 15) == 0)
{
// Kill Message
int n = atoi(&NodeURL[15]);
*RLen = KillWebMailMessage(Reply, Session->Key, Session->User, n);
return;
}
if (_stricmp(NodeURL, "/WebMail/NewMsg") == 0)
{
// Add HTML Template Button if we have any HTML Form
char Button[] =
" "
"";
char Temp[1024];
FreeWebMailFields(Session->WebMail); // Tidy up for new message
sprintf(Temp, Button, Key);
if (FormDirCount == 0)
*RLen = sprintf(Reply, MsgInputPage, Key, "", "", "", "");
else
*RLen = sprintf(Reply, MsgInputPage, Key, "", Temp, "", "");
return;
}
if (_memicmp(NodeURL, "/WebMail/GetPage/", 17) == 0)
{
// Read and Parse Template File
GetPage(Session, NodeURL);
return;
}
if (_memicmp(NodeURL, "/WebMail/GetList/", 17) == 0)
{
// Send Select Template Popup
char * SubDir;
int DirNo = 0;
int SubDirNo = 0;
char popup[10000];
char popuphddr[] =
""
""
""
"Select Required Template from %s
"
"
", popup);
*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
", popup);
*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 (EnableUI)
SendMsgUI(Msg);
user = LookupCall(Msg->to);
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++)
{
for (k = 0; k < Dir->Dirs[l]->FormCount; k++)
{
if (strcmp(FN, Dir->Dirs[l]->Forms[k]->FileName) == 0)
{
return CheckFile(Dir, Dir->Dirs[l]->Forms[k]->FileName);
}
}
}
}
}
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 | ", Key, Msg->number);
RawMessage[strlen(RawMessage)] = '.'; // We null terminated file name
RawMessage[strlen(RawMessage)] = ' '; // We null terminated file name Len = XML - RawMessage;
RawMessage[len] = 0;
// Body seems to have cr cr lf which causes double space
ptr = strstr(RawMessage, "\r\r");
while (ptr)
{
*ptr = ' ';
ptr = strstr(ptr, "\r\r");
}
sprintf(ErrorMsg, ErrorString, RawMessage);
len = sprintf(Reply, WebMailMsgTemplate, BBSName, User->Call, Msg->number, Msg->number, Key, Msg->number, Key, DownLoad, Key, Key, Key, "textarea", ErrorMsg, "textarea");
free(ErrorMsg);
return len;
}
char * FindXMLVariable(WebMailInfo * WebMail, char * Var)
{
KeyValues * XMLKey = &WebMail->XMLKeys[0];
while (XMLKey->Key)
{
if (_stricmp(Var, XMLKey->Key) == 0)
{
return XMLKey->Value;
}
XMLKey++;
}
return NULL;
}
BOOL ParseXML(WebMailInfo * WebMail, char * XMLOrig)
{
char * XML = _strdup(XMLOrig); // Get copy so we can mess about with it
char * ptr1, * ptr2, * ptr3;
KeyValues * XMLKeys = &WebMail->XMLKeys[0];
// Extract Fields (stuff between < and >. Ignore Whitespace between fields
ptr1 = strstr(XML, "");
while (ptr1)
{
ptr2 = strchr(++ptr1, '>');
if (ptr2 == NULL)
goto quit;
*ptr2++ = 0;
ptr3 = strchr(ptr2, '<'); // end of value string
if (ptr3 == NULL)
goto quit;
*ptr3++ = 0;
XMLKeys->Key = _strdup(ptr1);
XMLKeys->Value = _strdup(ptr2);
XMLKeys++;
ptr1 = strchr(ptr3, '<');
}
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
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
xvar = varsave = malloc(xmllen * 2);
ptr = xmlptr;
while(ptr < xmlend)
{
while (*ptr == '\n')
ptr++;
if (*ptr == '\r')
{
*ptr++;
strcpy(xvar, "
");
xvar += 4;
}
else
*(xvar++) = *(ptr++);
}
*xvar = 0;
temp = tempsave = malloc(strlen(Form) + strlen(XML));
memcpy(temp, formptr, varptr - formptr - 1); // omit "{"
temp += (varptr - formptr - 1);
strcpy(temp, varsave);
temp += strlen(varsave);
free(varsave);
formptr = endptr + 1;
strcpy(temp, formptr);
strcpy(Form, tempsave);
free(tempsave);
}
}
formptr = Form;
varptr = stristr(Form, "{var ");
while (varptr)
{
varptr+= 5;
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, varptr, varlen);
var[varlen] = 0;
KeyValue = &WebMail->XMLKeys[0];
while (KeyValue->Key)
{
if (_stricmp(var, KeyValue->Key) == 0)
{
xmllen = strlen(KeyValue->Value);
// Ok, we have the position of the variable and the substitution text.
// Copy message up to variable to Result, then copy value
memcpy(Reply, formptr, varptr - formptr - 5); // omit "{var "
Reply += (varptr - formptr - 5);
strcpy(Reply, KeyValue->Value);
Reply += xmllen;
break;
}
if (KeyValue->Key == NULL)
{
// Not found in XML
char Err[256];
sprintf(Err, VarNotFoundMsg, var, "%s");
return ReturnRawMessage(User, Msg, Key, SaveReply, RawMessage, (int)(XML - RawMessage), Err);
}
KeyValue++;
}
formptr = endptr + 1;
varptr = stristr(endptr, "{var ");
}
// copy remaining
// Remove as we add it later
ptr = strstr(formptr, "");
if (ptr)
*ptr = 0;
strcpy(Reply, formptr);
// Add Webmail header between and form data
ptr = stristr(SaveReply, "');
if (ptr)
{
char * temp = malloc(strlen(SaveReply) + 1000);
size_t len = ++ptr - SaveReply;
memcpy(temp, SaveReply, len);
sprintf(&temp[len],
""
" %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);
BuildNNTPList(Msg); // Build NNTP Groups list
}
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);
BuildNNTPList(Msg); // Build NNTP Groups list
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, "