/* 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; #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

" "
" "" "" "
User
Password
" "

"; char ChatPage[] = "%s's Chat Server" "

BPQ32 Chat Node %s

" "

" "" "" "" "
StatusConfigurationNode Menu
"; static char LostSession[] = "" "

" "Sorry, Session had been lost

    " "
"; 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; 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, 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], "%sOpen   RTT %d", link->call, link->RTT); else Len += sprintf(&Links[Len], "%sOpen", link->call); else if (link->flags & (p_linked | p_linkini)) Len += sprintf(&Links[Len], "%sConnecting", link->call); else if (link->flags & p_linkfailed) Len += sprintf(&Links[Len], "%sConnect failed", link->call); else Len += sprintf(&Links[Len], "%sIdle", 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