/*
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
*/
#include "bpqchat.h"
extern char OurNode[10];
// Flags Equates
#define GETTINGUSER 1
#define GETTINGBBS 2
#define CHATMODE 4
#define GETTINGTITLE 8
#define GETTINGMESSAGE 16
#define CHATLINK 32 // Link to another Chat Node
#define SENDTITLE 64
#define SENDBODY 128
#define WAITPROMPT 256 // Waiting for prompt after message
extern char PassError[];
extern char BusyError[];
extern int chatPaclen;
extern char NodeTail[];
extern BOOL APRSApplConnected;
extern char ChatConfigName[250];
extern char OtherNodesList[1000];
extern char ChatWelcomeMsg[1000];
extern USER *user_hd;
extern LINK *link_hd;
extern UCHAR BPQDirectory[260];
#define MaxSockets 64
extern ChatCIRCUIT ChatConnections[MaxSockets+1];
extern int NumberofChatStreams;
extern int SMTPMsgs;
extern int ChatApplNum;
extern int MaxChatStreams;
extern char Position[81];
extern char PopupText[251];
extern int PopupMode;
extern int reportChatEvents;
#include "httpconnectioninfo.h"
static struct HTTPConnectionInfo * SessionList; // active bbs config sessions
static struct HTTPConnectionInfo * AllocateSession(char Appl);
static struct HTTPConnectionInfo * FindSession(char * Key);
VOID ProcessUserUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
VOID ProcessMsgFwdUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
VOID SendConfigPage(char * Reply, int * ReplyLen, char * Key);
VOID ProcessConfUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
VOID ProcessUIUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
VOID SendUserSelectPage(char * Reply, int * ReplyLen, char * Key);
VOID SendFWDSelectPage(char * Reply, int * ReplyLen, char * Key);
int EncryptPass(char * Pass, char * Encrypt);
VOID ProcessFWDUpdate(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
VOID SendStatusPage(char * Reply, int * ReplyLen, char * Key);
VOID SendChatStatusPage(char * Reply, int * ReplyLen, char * Key);
VOID SendUIPage(char * Reply, int * ReplyLen, char * Key);
static VOID GetParam(char * input, char * key, char * value);
BOOL GetConfig(char * ConfigName);
VOID ProcessChatDisUser(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
int APIENTRY SessionControl(int stream, int command, int param);
int APIENTRY GetNumberofPorts();
int APIENTRY GetPortNumber(int portslot);
UCHAR * APIENTRY GetPortDescription(int portslot, char * Desc);
struct PORTCONTROL * APIENTRY GetPortTableEntryFromSlot(int portslot);
VOID SendHouseKeeping(char * Reply, int * ReplyLen, char * Key);
VOID SendWelcomePage(char * Reply, int * ReplyLen, char * Key);
VOID SaveWelcome(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key);
VOID GetMallocedParam(char * input, char * key, char ** value);
VOID SaveMessageText(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
VOID SaveHousekeeping(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key);
VOID SaveWP(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key);
int SetupNodeMenu(char * Buff);
VOID SendFwdSelectPage(char * Reply, int * ReplyLen, char * Key);
VOID SendFwdDetails(struct HTTPConnectionInfo * Session, char * Reply, int * ReplyLen, char * Key);
VOID SendFwdMainPage(char * Reply, int * ReplyLen, char * Key);
VOID SaveFwdCommon(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
VOID SaveFwdDetails(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest);
char ** SeparateMultiString(char * MultiString);
VOID SendChatConfigPage(char * Reply, int * ReplyLen, char * Key);
VOID SaveChatInfo(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key);
int rtlink (char * Call);
char * APIENTRY GetBPQDirectory();
VOID SaveChatConfigFile(char * Config);
BOOL GetChatConfig(char * Config);
char * GetTemplateFromFile(int Version, char * FN);
static char UNC[] = "";
static char CHKD[] = "checked=checked ";
static char sel[] = "selected";
char ChatSignon[] = "
BPQ32 Chat Server Access"
"BPQ32 Chat Server %s Access
"
"Please enter Callsign and Password to access the Chat Server
"
"";
char ChatPage[] = "%s's Chat Server"
"BPQ32 Chat Node %s
"
"
";
static char LostSession[] = ""
"";
char * ChatConfigTemplate = NULL;
char * ChatStatusTemplate = NULL;
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 SendChatHeader(char * Reply, char * Key)
{
return sprintf(Reply, ChatPage, OurNode, OurNode, Key, Key);
}
void ProcessChatHTTPMessage(struct HTTPConnectionInfo * Session, char * Method, char * URL, char * input, char * Reply, int * RLen)
{
char * Conxtext = 0, * NodeURL;
int ReplyLen;
char * Key;
char Appl = 'M';
NodeURL = strtok_s(URL, "?", &Conxtext);
Key = Session->Key;
if (strcmp(Method, "POST") == 0)
{
if (_stricmp(NodeURL, "/Chat/Header") == 0)
{
*RLen = SendChatHeader(Reply, Session->Key);
return;
}
if (_stricmp(NodeURL, "/Chat/ChatConfig") == 0)
{
if (ChatConfigTemplate)
free(ChatConfigTemplate);
ChatConfigTemplate = GetTemplateFromFile(2, "ChatConfig.txt");
NodeURL[strlen(NodeURL)] = ' '; // Undo strtok
SaveChatInfo(Session, input, Reply, RLen, Key);
return ;
}
if (_stricmp(NodeURL, "/Chat/ChatDisSession") == 0)
{
ProcessChatDisUser(Session, input, Reply, RLen, Key);
return ;
}
// End of POST section
}
if ((_stricmp(NodeURL, "/chat/Chat.html") == 0) || (_stricmp(NodeURL, "/chat/Header") == 0))
{
*RLen = SendChatHeader(Reply, Session->Key);
return;
}
if ((_stricmp(NodeURL, "/Chat/ChatStatus") == 0) || (_stricmp(NodeURL, "/Chat/ChatDisSession") == 0))
{
if (ChatStatusTemplate)
free(ChatStatusTemplate);
ChatStatusTemplate = GetTemplateFromFile(1, "ChatStatus.txt");
SendChatStatusPage(Reply, RLen, Key);
return;
}
if (_stricmp(NodeURL, "/Chat/ChatConf") == 0)
{
if (ChatConfigTemplate)
free(ChatConfigTemplate);
ChatConfigTemplate = GetTemplateFromFile(2, "ChatConfig.txt");
SendChatConfigPage(Reply, RLen, Key);
return;
}
ReplyLen = sprintf(Reply, ChatSignon, OurNode, OurNode);
*RLen = ReplyLen;
}
static VOID GetParam(char * input, char * key, char * value)
{
char * ptr = strstr(input, key);
char Param[2048];
char * ptr1, * ptr2;
char c;
if (ptr)
{
ptr2 = strchr(ptr, '&');
if (ptr2) *ptr2 = 0;
strcpy(Param, ptr + strlen(key));
if (ptr2) *ptr2 = '&'; // Restore string
// Undo any % transparency
ptr1 = Param;
ptr2 = Param;
c = *(ptr1++);
while (c)
{
if (c == '%')
{
int n;
int m = *(ptr1++) - '0';
if (m > 9) m = m - 7;
n = *(ptr1++) - '0';
if (n > 9) n = n - 7;
*(ptr2++) = m * 16 + n;
}
else if (c == '+')
*(ptr2++) = ' ';
else
*(ptr2++) = c;
c = *(ptr1++);
}
*(ptr2++) = 0;
strcpy(value, Param);
}
}
static VOID GetCheckBox(char * input, char * key, int * value)
{
char * ptr = strstr(input, key);
if (ptr)
*value = 1;
else
*value = 0;
}
VOID SaveChatInfo(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Key)
{
int ReplyLen = 0;
char * input;
struct UserInfo * USER = NULL;
char Temp[80];
char Nodes[10000] = "";
char * ptr1, * ptr2;
input = strstr(MsgPtr, "\r\n\r\n"); // End of headers
if (input)
{
if (strstr(input, "Cancel=Cancel"))
{
*RLen = SendChatHeader(Reply, Session->Key);
return;
}
GetParam(input, "ApplNum=", Temp);
ChatApplNum = atoi(Temp);
GetParam(input, "Streams=", Temp);
MaxChatStreams = atoi(Temp);
GetParam(input, "Paclen=", Temp);
chatPaclen = atoi(Temp);
if (chatPaclen < 60)
chatPaclen = 60;
GetCheckBox(input, "Events=", &reportChatEvents);
GetParam(input, "nodes=", Nodes);
ptr1 = Nodes;
ptr2 = OtherNodesList;
// Now we just save with crlf in place
strcpy(OtherNodesList, Nodes);
/*
while (*ptr1)
{
if ((*ptr1) == 13)
{
*(ptr2++) = ' ';
ptr1 += 2;
}
else
*(ptr2++) = *(ptr1++);
}
*ptr2 = 0;
*/
GetParam(input, "Posn=", Position);
GetParam(input, "MapText=", PopupText);
GetParam(input, "welcome=", ChatWelcomeMsg);
// Replace cr lf in string with $W
ptr1 = ChatWelcomeMsg;
scan2:
ptr1 = strstr(ptr1, "\r\n");
if (ptr1)
{
*(ptr1++)='$'; // put in cr
*(ptr1++)='W'; // put in lf
goto scan2;
}
GetCheckBox(input, "PopType=Click", &PopupMode);
if (strstr(input, "Restart=Restart+Links"))
{
char * ptr1, * ptr2, * Context;
node_close();
Sleep(2);
// Dont call removelinks - they may still be attached to a circuit. Just clear header
link_hd = NULL;
// Set up other nodes list. rtlink messes with the string so pass copy
ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), "\r\n", &Context);
while (ptr1)
{
rtlink(ptr1);
ptr1 = strtok_s(NULL, "\r\n", &Context);
}
free(ptr2);
if (user_hd) // Any Users?
makelinks(); // Bring up links
}
if (strstr(input, "UpdateMap=Update+Map"))
{
char Msg[500];
int len;
len = sprintf(Msg, "INFO %s|%s|%d|\r", Position, PopupText, PopupMode);
if (len < 256)
Send_MON_Datagram(Msg, len);
}
SaveChatConfigFile(ChatConfigName);
GetChatConfig(ChatConfigName);
}
SendChatConfigPage(Reply, RLen, Key);
return;
}
VOID ProcessChatDisUser(struct HTTPConnectionInfo * Session, char * MsgPtr, char * Reply, int * RLen, char * Rest)
{
char * input;
char * ptr;
input = strstr(MsgPtr, "\r\n\r\n"); // End of headers
if (input)
{
ptr = strstr(input, "Stream=");
if (ptr)
{
int Stream = atoi(ptr + 7);
SessionControl(Stream, 2, 0);
}
}
SendChatStatusPage(Reply, RLen, Rest);
}
VOID SendChatConfigPage(char * Reply, int * ReplyLen, char * Key)
{
int Len;
char Nodes[10000];
char Text[1000];
char * ptr1, * ptr2;
// Replace spaces in Node List with CR/LF
ptr1 = OtherNodesList;
ptr2 = Nodes;
if (strchr(OtherNodesList, 13)) // New format maybe with connect scripts
{
// OtherNodesList alresdy has crlf
strcpy(Nodes, OtherNodesList);
}
else
{
while (*ptr1)
{
if ((*ptr1) == ' ')
{
*(ptr2++) = 13;
*(ptr2++) = 10;
ptr1++ ;
}
else
*(ptr2++) = *(ptr1++);
}
*ptr2 = 0;
}
// Replace " in Text with "
ptr1 = PopupText;
ptr2 = Text;
while (*ptr1)
{
if ((*ptr1) == '"')
{
*(ptr2++) = '&';
*(ptr2++) = 'q';
*(ptr2++) = 'u';
*(ptr2++) = 'o';
*(ptr2++) = 't';
*(ptr2++) = ';';
ptr1++ ;
}
else
*(ptr2++) = *(ptr1++);
}
*ptr2 = 0;
// Replace $W in Welcome Message with cr lf
ptr2 = ptr1 = _strdup(ChatWelcomeMsg);
scan:
ptr1 = strstr(ptr1, "$W");
if (ptr1)
{
*(ptr1++)=13; // put in cr
*(ptr1++)=10; // put in lf
goto scan;
}
Len = sprintf(Reply, ChatConfigTemplate,
OurNode, Key, Key, Key,
ChatApplNum, MaxChatStreams,
(reportChatEvents) ? CHKD : UNC,
Nodes, chatPaclen, Position,
(PopupMode) ? UNC : CHKD,
(PopupMode) ? CHKD : UNC, Text, ptr2);
free(ptr2);
*ReplyLen = Len;
}
VOID SendChatStatusPage(char * Reply, int * ReplyLen, char * Key)
{
int Len = 0;
USER *user;
char * Alias;
char * Topic;
LINK *link;
char Streams[65536];
char Users[65536];
char Links[65536];
ChatCIRCUIT * conn;
int i = 0, n;
Users[0] = 0;
for (user = user_hd; user; user = user->next)
{
if ((user->node == 0) || (user->node->alias == 0))
Alias = "(Corrupt Alias)";
else
Alias = user->node->alias;
if ((user->topic == 0) || (user->topic->name == 0))
Topic = "(Corrupt Topic)";
else
Topic = user->topic->name;
Len += sprintf(&Users[Len], "%s | %s | %s | %s | %d | %s |
",
user->call, Alias, user->name, Topic, (int)(time(NULL) - user->lastrealmsgtime), user->qth);
}
Links[0] = 0;
Len = 0;
for (link = link_hd; link; link = link->next)
{
if (link->flags & p_linked )
if (link->supportsPolls)
Len += sprintf(&Links[Len], "%s | Open   RTT %d |
", link->call, link->RTT);
else
Len += sprintf(&Links[Len], "%s | Open |
", link->call);
else if (link->flags & (p_linked | p_linkini))
Len += sprintf(&Links[Len], "%s | Connecting |
", link->call);
else if (link->flags & p_linkfailed)
Len += sprintf(&Links[Len], "%s | Connect failed |
", link->call);
else
Len += sprintf(&Links[Len], "%s | Idle |
", link->call);
}
Len = 0;
Streams[0] = 0;
for (n = 0; n < NumberofChatStreams; n++)
{
conn=&ChatConnections[n];
i = conn->BPQStream;
if (!conn->Active)
{
Len += sprintf(&Streams[Len], "Idle | | | | |
", i, i);
}
else
{
if (conn->Flags & CHATLINK)
{
if (conn->BPQStream > 64 || conn->u.link == 0)
Len += sprintf(&Streams[Len], "** Corrupt ChatLink ** | "
" | | | |
", i, i);
else
Len += sprintf(&Streams[Len], ""
"%s | %s | %d | %s | %d |
",
i, i, "Chat Link", conn->u.link->alias, conn->BPQStream,
"", conn->OutputQueueLength - conn->OutputGetPointer);
}
else
if ((conn->Flags & CHATMODE) && conn->topic)
{
Len += sprintf(&Streams[Len], "%s | %s | %d | %s | %d |
",
i, i, conn->u.user->name, conn->u.user->call, conn->BPQStream,
conn->topic->topic->name, conn->OutputQueueLength - conn->OutputGetPointer);
}
else
{
if (conn->UserPointer == 0)
Len += sprintf(&Streams[Len], "Logging in");
else
{
Len += sprintf(&Streams[Len], "%s | %s | %d | %s | %d |
",
i, i, conn->UserPointer->Name, conn->UserPointer->Call, conn->BPQStream,
"CHAT", conn->OutputQueueLength - conn->OutputGetPointer);
}
}
}
}
Len = sprintf(Reply, ChatStatusTemplate, OurNode, OurNode, Key, Key, Key, Streams, Users, Links);
*ReplyLen = Len;
}
static struct HTTPConnectionInfo * AllocateSession(char Appl)
{
int KeyVal;
struct HTTPConnectionInfo * Session = zalloc(sizeof(struct HTTPConnectionInfo));
if (Session == NULL)
return NULL;
KeyVal = (int)time(NULL);
sprintf(Session->Key, "%c%012X", Appl, KeyVal);
if (SessionList)
Session->Next = SessionList;
SessionList = Session;
return Session;
}
static struct HTTPConnectionInfo * FindSession(char * Key)
{
struct HTTPConnectionInfo * Session = SessionList;
while (Session)
{
if (strcmp(Session->Key, Key) == 0)
return Session;
Session = Session->Next;
}
return NULL;
}
#ifdef WIN32
static char PipeFileName[] = "\\\\.\\pipe\\BPQChatWebPipe";
static DWORD WINAPI InstanceThread(LPVOID lpvParam)
// This routine is a thread processing function to read from and reply to a client
// via the open pipe connection passed from the main loop. Note this allows
// the main loop to continue executing, potentially creating more threads of
// of this procedure to run concurrently, depending on the number of incoming
// client connections.
{
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
BOOL fSuccess = FALSE;
HANDLE hPipe = NULL;
char Buffer[4096];
char OutBuffer[100000];
char * MsgPtr;
int InputLen = 0;
int OutputLen = 0;
struct HTTPConnectionInfo Session;
char URL[4096];
char * Context, * Method;
int n;
char * ptr;
// Debugprintf("InstanceThread created, receiving and processing messages.");
// The thread's parameter is a handle to a pipe object instance.
hPipe = (HANDLE) lpvParam;
// Read client requests from the pipe. This simplistic code only allows messages
// up to BUFSIZE characters in length.
n = ReadFile(hPipe, &Session, sizeof (struct HTTPConnectionInfo), &n, NULL);
fSuccess = ReadFile(hPipe, Buffer, 4096, &InputLen, NULL);
if (!fSuccess || InputLen == 0)
{
if (GetLastError() == ERROR_BROKEN_PIPE)
Debugprintf("InstanceThread: client disconnected.", GetLastError());
else
Debugprintf("InstanceThread ReadFile failed, GLE=%d.", GetLastError());
}
else
{
Buffer[InputLen] = 0;
MsgPtr = &Buffer[0];
strcpy(URL, MsgPtr);
ptr = strstr(URL, " HTTP");
if (ptr)
*ptr = 0;
Method = strtok_s(URL, " ", &Context);
ProcessChatHTTPMessage(&Session, Method, Context, MsgPtr, OutBuffer, &OutputLen);
WriteFile(hPipe, &Session, sizeof (struct HTTPConnectionInfo), &n, NULL);
WriteFile(hPipe, OutBuffer, OutputLen, &cbWritten, NULL);
FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
}
return 1;
}
static DWORD WINAPI PipeThreadProc(LPVOID lpvParam)
{
BOOL fConnected = FALSE;
DWORD dwThreadId = 0;
HANDLE hPipe = INVALID_HANDLE_VALUE, hThread = NULL;
// The main loop creates an instance of the named pipe and
// then waits for a client to connect to it. When the client
// connects, a thread is created to handle communications
// with that client, and this loop is free to wait for the
// next client connect request. It is an infinite loop.
for (;;)
{
hPipe = CreateNamedPipe(
PipeFileName, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_BYTE | // message type pipe
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
4096, // output buffer size
4096, // input buffer size
0, // client time-out
NULL); // default security attribute
if (hPipe == INVALID_HANDLE_VALUE)
{
Debugprintf("CreateNamedPipe failed, GLE=%d.\n", GetLastError());
return -1;
}
// Wait for the client to connect; if it succeeds,
// the function returns a nonzero value. If the function
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
fConnected = ConnectNamedPipe(hPipe, NULL) ?
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
if (fConnected)
{
// Create a thread for this client.
hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
InstanceThread, // thread proc
(LPVOID) hPipe, // thread parameter
0, // not suspended
&dwThreadId); // returns thread ID
if (hThread == NULL)
{
Debugprintf("CreateThread failed, GLE=%d.\n", GetLastError());
return -1;
}
else CloseHandle(hThread);
}
else
// The client could not connect, so close the pipe.
CloseHandle(hPipe);
}
return 0;
}
BOOL CreateChatPipeThread()
{
DWORD ThreadId;
CreateThread(NULL, 0, PipeThreadProc, 0, 0, &ThreadId);
return TRUE;
}
static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
static char *dat[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static VOID FormatTime(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);
}
#endif