4552 lines
96 KiB
C
4552 lines
96 KiB
C
/*
|
||
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
|
||
|
||
#pragma data_seg("_BPQDATA")
|
||
|
||
#define LIBCONFIG_STATIC
|
||
#include "libconfig.h"
|
||
|
||
|
||
#ifdef LINBPQ
|
||
#include "CHeaders.h"
|
||
#endif
|
||
|
||
#include "bpqchat.h"
|
||
|
||
#ifndef WIN32
|
||
|
||
iconv_t link_toUTF8 = NULL;
|
||
|
||
|
||
BOOL RunEventProgram(char * Program, char * Param);
|
||
|
||
#endif
|
||
|
||
BOOL ProcessChatConnectScript(ChatCIRCUIT * conn, char * Buffer, int len);
|
||
VOID ChatClearQueue(ChatCIRCUIT * conn);
|
||
VOID ChatFlush(ChatCIRCUIT * conn);
|
||
VOID APIENTRY SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen);
|
||
unsigned short int compute_crc(unsigned char *buf,int len);
|
||
char * ReadInfoFile(char * File);
|
||
void ChatWriteLogLine(ChatCIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags);
|
||
extern struct SEM ChatSemaphore;
|
||
UCHAR * APIENTRY GetLogDirectory();
|
||
char * APIENTRY GetBPQDirectory();
|
||
VOID WriteMiniDump();
|
||
|
||
extern SOCKADDR_IN Chatreportdest;
|
||
|
||
char OurNode[10];
|
||
char OurAlias[10];
|
||
|
||
#define MaxSockets 64
|
||
|
||
int MaxChatStreams=0;
|
||
ChatCIRCUIT ChatConnections[MaxSockets+1];
|
||
|
||
ULONG ChatApplMask;
|
||
|
||
int NumberofChatStreams=0;
|
||
|
||
char ChatSignoffMsg[100];
|
||
|
||
char OtherNodesList[1000];
|
||
char ChatWelcomeMsg[1000];
|
||
|
||
char Position[81] = "";
|
||
char PopupText[260] = "";
|
||
int PopupMode = 0;
|
||
int chatPaclen = 236;
|
||
|
||
int reportChatEvents = 0;
|
||
|
||
char RtKnown[MAX_PATH] = "RTKnown.txt";
|
||
char RtUsr[MAX_PATH] = "STUsers.txt";
|
||
char RtUsrTemp[MAX_PATH] = "STUsers.tmp";
|
||
|
||
int AXIPPort = 0;
|
||
|
||
ChatCIRCUIT *circuit_hd = NULL; // This is a chain of RT circuits. There may be others
|
||
|
||
CHATNODE *node_hd = NULL; // Nodes
|
||
|
||
LINK *link_hd = NULL; // Nodes we link to
|
||
TOPIC *topic_hd = NULL;
|
||
|
||
USER *user_hd = NULL;
|
||
|
||
KNOWNNODE * known_hd = NULL;
|
||
|
||
int ChatTmr = 0;
|
||
|
||
BOOL NeedStatus = FALSE;
|
||
|
||
|
||
char Verstring[80];
|
||
|
||
static void node_dec(CHATNODE *node);
|
||
static KNOWNNODE *knownnode_add(char *call);
|
||
VOID SendChatLinkStatus();
|
||
char * lookupuser(char * call);
|
||
VOID ChatSendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user);
|
||
|
||
static int AutoColours[20] = {0, 4, 9, 11, 13, 16, 17, 42, 45, 50, 61, 64, 66, 72, 81, 84, 85, 86, 87, 89};
|
||
|
||
#define MaxSockets 64
|
||
|
||
extern struct SEM OutputSEM;
|
||
|
||
int NeedINFO = 1; // Send INFO Msg after 10 Secs
|
||
time_t RunningConnectScript = 0;
|
||
|
||
//#undef free
|
||
//#define free(p)
|
||
|
||
|
||
struct HistoryRec * History = NULL;
|
||
int HistoryCount = 0;
|
||
|
||
|
||
typedef int (WINAPI FAR *FARPROCX)();
|
||
extern FARPROCX pRunEventProgram;
|
||
|
||
int AddtoHistory(struct user_t * user, char * text)
|
||
{
|
||
struct HistoryRec * Rec;
|
||
struct HistoryRec * ptr;
|
||
int n = 1;
|
||
char buf[2048];
|
||
char Stamp[16];
|
||
struct tm * tm;
|
||
time_t Now = time(NULL);
|
||
|
||
// Don't want to grow indefinitely and fill memory. We only allow display up to 24 hours back, so if first record is older that that
|
||
// remove and reuse it
|
||
|
||
if (History && History->Time < Now - 86400)
|
||
{
|
||
Rec = History;
|
||
History = Rec->next; // Remove from front of chain
|
||
}
|
||
else
|
||
Rec = malloc(sizeof (struct HistoryRec));
|
||
|
||
memset(Rec, 0, sizeof (struct HistoryRec));
|
||
|
||
tm = gmtime(&Now);
|
||
|
||
if (strlen(text) + strlen(user->name) + strlen(user->call) > 2000)
|
||
return 0; // Shouldn't be that long, but protect buffer
|
||
|
||
sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min);
|
||
sprintf(buf, "%s%-6.6s %s %c %s\r", Stamp, user->call, user->name, ':', text);
|
||
|
||
Rec->Time = Now;
|
||
|
||
Rec->Topic = _strdup(user->topic->name);
|
||
Rec->Message = _strdup(buf);
|
||
|
||
if (History == NULL)
|
||
History = Rec;
|
||
|
||
else
|
||
{
|
||
ptr = History;
|
||
|
||
while (ptr && ptr->next)
|
||
{
|
||
n++;
|
||
ptr = ptr->next;
|
||
}
|
||
|
||
n++;
|
||
ptr->next = Rec;
|
||
}
|
||
|
||
return n;
|
||
}
|
||
|
||
|
||
|
||
int ChatIsUTF8(unsigned char *ptr, int len)
|
||
{
|
||
int n;
|
||
unsigned char * cpt = ptr;
|
||
|
||
// This is simpler than the Term version, as it only handles complete lines of text, so cant get split sequences
|
||
|
||
cpt--;
|
||
|
||
for (n = 0; n < len; n++)
|
||
{
|
||
cpt++;
|
||
|
||
if (*cpt < 128)
|
||
continue;
|
||
|
||
if ((*cpt & 0xF8) == 0xF0)
|
||
{ // start of 4-byte sequence
|
||
if (((*(cpt + 1) & 0xC0) == 0x80)
|
||
&& ((*(cpt + 2) & 0xC0) == 0x80)
|
||
&& ((*(cpt + 3) & 0xC0) == 0x80))
|
||
{
|
||
cpt += 3;
|
||
n += 3;
|
||
continue;
|
||
}
|
||
return FALSE;
|
||
}
|
||
else if ((*cpt & 0xF0) == 0xE0)
|
||
{ // start of 3-byte sequence
|
||
if (((*(cpt + 1) & 0xC0) == 0x80)
|
||
&& ((*(cpt + 2) & 0xC0) == 0x80))
|
||
{
|
||
cpt += 2;
|
||
n += 2;
|
||
continue;
|
||
}
|
||
return FALSE;
|
||
}
|
||
else if ((*cpt & 0xE0) == 0xC0)
|
||
{ // start of 2-byte sequence
|
||
if ((*(cpt + 1) & 0xC0) == 0x80)
|
||
{
|
||
cpt++;
|
||
n++;
|
||
continue;
|
||
}
|
||
return FALSE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
#ifndef LINBPQ
|
||
|
||
char * strlop(char * buf, char delim)
|
||
{
|
||
// Terminate buf at delim, and return rest of string
|
||
|
||
char * ptr = strchr(buf, delim);
|
||
|
||
if (ptr == NULL) return NULL;
|
||
|
||
*(ptr)++=0;
|
||
|
||
return ptr;
|
||
}
|
||
|
||
|
||
VOID * _zalloc(size_t len)
|
||
{
|
||
// ?? malloc and clear
|
||
|
||
void * ptr;
|
||
|
||
ptr=malloc(len);
|
||
memset(ptr, 0, len);
|
||
|
||
return ptr;
|
||
}
|
||
|
||
|
||
VOID * _zalloc_dbg(int len, int type, char * file, int line)
|
||
{
|
||
// ?? malloc and clear
|
||
|
||
void * ptr;
|
||
|
||
ptr=_malloc_dbg(len, type, file, line);
|
||
|
||
if (ptr == NULL)
|
||
CriticalErrorHandler("malloc failed");
|
||
|
||
memset(ptr, 0, len);
|
||
|
||
return ptr;
|
||
}
|
||
|
||
#endif
|
||
|
||
VOID __cdecl nprintf(ChatCIRCUIT * conn, const char * format, ...)
|
||
|
||
{
|
||
// seems to be printf to a socket
|
||
|
||
char buff[65536];
|
||
va_list(arglist);
|
||
|
||
va_start(arglist, format);
|
||
vsnprintf(buff, sizeof(buff), format, arglist);
|
||
|
||
nputs(conn, buff);
|
||
}
|
||
|
||
|
||
VOID nputc(ChatCIRCUIT * conn, char chr)
|
||
{
|
||
// Seems to send chr to socket
|
||
|
||
ChatWriteLogLine(conn, '>',&chr, 1, LOG_CHAT);
|
||
ChatQueueMsg(conn, &chr, 1);
|
||
}
|
||
|
||
VOID nputs(ChatCIRCUIT * conn, char * buf)
|
||
{
|
||
// Seems to send buf to socket
|
||
|
||
ChatQueueMsg(conn, buf, (int)strlen(buf));
|
||
|
||
if (*buf == 0x1b)
|
||
buf += 2; // Colour Escape
|
||
|
||
ChatWriteLogLine(conn, '>',buf, (int)strlen(buf), LOG_CHAT);
|
||
}
|
||
|
||
int ChatQueueMsg(ChatCIRCUIT * conn, char * msg, int len)
|
||
{
|
||
// Add Message to queue for this connection
|
||
|
||
if (conn->rtcflags & p_linked)
|
||
conn->u.link->lastMsgReceived = time(NULL);
|
||
|
||
// UCHAR * OutputQueue; // Messages to user
|
||
// int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message
|
||
// int OutputGetPointer; // Next byte to send. When Getpointer = Queue Length all is sent - free the buffer and start again.
|
||
|
||
// Create or extend buffer
|
||
|
||
GetSemaphore(&OutputSEM, 0);
|
||
|
||
while (conn->OutputQueueLength + len > conn->OutputQueueSize)
|
||
{
|
||
// Extend Queue
|
||
|
||
conn->OutputQueueSize += 4096;
|
||
conn->OutputQueue = realloc(conn->OutputQueue, conn->OutputQueueSize);
|
||
}
|
||
|
||
memcpy(&conn->OutputQueue[conn->OutputQueueLength], msg, len);
|
||
conn->OutputQueueLength += len;
|
||
|
||
FreeSemaphore(&OutputSEM);
|
||
|
||
return len;
|
||
}
|
||
|
||
VOID ChatSendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user)
|
||
{
|
||
if (!rtloginu (conn, TRUE))
|
||
{
|
||
// Already connected - close
|
||
|
||
ChatFlush(conn);
|
||
Sleep(1000);
|
||
Disconnect(conn->BPQStream);
|
||
}
|
||
return;
|
||
|
||
}
|
||
|
||
VOID ChatExpandAndSendMessage(ChatCIRCUIT * conn, char * Msg, int LOG)
|
||
{
|
||
char NewMessage[10000];
|
||
char * OldP = Msg;
|
||
char * NewP = NewMessage;
|
||
char * ptr, * pptr;
|
||
int len;
|
||
char Dollar[] = "$";
|
||
char CR[] = "\r";
|
||
int Msgs = 0, Unread = 0;
|
||
|
||
|
||
ptr = strchr(OldP, '$');
|
||
|
||
while (ptr)
|
||
{
|
||
len = (int)(ptr - OldP); // Chars before $
|
||
memcpy(NewP, OldP, len);
|
||
NewP += len;
|
||
|
||
switch (*++ptr)
|
||
{
|
||
case 'I': // First name of the connected user.
|
||
|
||
pptr = conn->UserPointer->Name;
|
||
break;
|
||
|
||
|
||
case 'U': // Callsign of the connected user.
|
||
|
||
pptr = conn->UserPointer->Call;
|
||
break;
|
||
|
||
case 'W': // Inserts a carriage return.
|
||
|
||
pptr = CR;
|
||
break;
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
pptr = Dollar; // Just Copy $
|
||
}
|
||
|
||
len = (int)strlen(pptr);
|
||
memcpy(NewP, pptr, len);
|
||
NewP += len;
|
||
|
||
OldP = ++ptr;
|
||
ptr = strchr(OldP, '$');
|
||
}
|
||
|
||
strcpy(NewP, OldP);
|
||
|
||
len = RemoveLF(NewMessage, (int)strlen(NewMessage));
|
||
|
||
ChatWriteLogLine(conn, '>', NewMessage, len, LOG);
|
||
ChatQueueMsg(conn, NewMessage, len);
|
||
}
|
||
|
||
|
||
|
||
void chat_link_out (LINK *link)
|
||
{
|
||
int n, p;
|
||
ChatCIRCUIT * conn;
|
||
char Msg[80];
|
||
|
||
for (n = NumberofChatStreams-1; n >= 0 ; n--)
|
||
{
|
||
conn = &ChatConnections[n];
|
||
|
||
if (conn->Active == FALSE)
|
||
{
|
||
p = conn->BPQStream;
|
||
memset(conn, 0, sizeof(ChatCIRCUIT)); // Clear everything
|
||
conn->BPQStream = p;
|
||
|
||
conn->Active = TRUE;
|
||
circuit_new(conn, p_linkini);
|
||
conn->u.link = link;
|
||
conn->Flags = CHATMODE | CHATLINK;
|
||
|
||
n=sprintf_s(Msg, sizeof(Msg), "Connecting to Chat Node %s", conn->u.link->alias);
|
||
|
||
strcpy(conn->Callsign, conn->u.link->alias);
|
||
|
||
ChatWriteLogLine(conn, '|',Msg, n, LOG_CHAT);
|
||
|
||
link->ScriptIndex = -1;
|
||
RunningConnectScript = time(NULL);
|
||
link->MoreLines = TRUE;
|
||
link->scriptRunning = TRUE;
|
||
link->RTLSent = 0;
|
||
|
||
ConnectUsingAppl(conn->BPQStream, ChatApplMask);
|
||
|
||
// Connected Event will trigger connect to remote system
|
||
|
||
return;
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
|
||
VOID saywhat(ChatCIRCUIT *circuit)
|
||
{
|
||
nputs(circuit, "Invalid Command\r");
|
||
}
|
||
|
||
VOID saydone(ChatCIRCUIT *circuit)
|
||
{
|
||
nputs(circuit, "Ok\r");
|
||
}
|
||
|
||
VOID strnew(char ** new, char *f1)
|
||
{
|
||
// seems to allocate a new string, and copy the old one to it
|
||
// how is this different to strdup??
|
||
|
||
*new = _strdup(f1);
|
||
}
|
||
|
||
#define sl_ins_hd(link, hd) \
|
||
if (hd == NULL)\
|
||
hd=link;\
|
||
else\
|
||
{\
|
||
link->next=hd->next;\
|
||
hd->next=link;\
|
||
}
|
||
|
||
BOOL matchi(char * p1, char * p2)
|
||
{
|
||
// Return TRUE is strings match
|
||
|
||
if (_stricmp(p1, p2))
|
||
return FALSE;
|
||
else
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
VOID ProcessChatLine(ChatCIRCUIT * conn, struct UserInfo * user, char* OrigBuffer, int len)
|
||
{
|
||
ChatCIRCUIT *c;
|
||
char * Buffer = OrigBuffer;
|
||
WCHAR BufferW[65536];
|
||
UCHAR BufferB[65536];
|
||
|
||
// Sanity Check
|
||
|
||
if (len > 32768)
|
||
return;
|
||
|
||
// Convert to UTF8 if not already in UTF-8
|
||
|
||
if (len == 73 && memcmp(&OrigBuffer[40], " ", 20) == 0)
|
||
{
|
||
// Chat Signon Message. If Topic is present, switch to it
|
||
|
||
char * Context;
|
||
char * Appl;
|
||
char * topic;
|
||
|
||
Appl = strtok_s(OrigBuffer, " ,\r", &Context);
|
||
topic = strtok_s(NULL, " ,\r", &Context);
|
||
|
||
if (topic == NULL)
|
||
return; // Just Chat
|
||
|
||
// Have a Topic
|
||
|
||
if (conn->Flags & GETTINGUSER)
|
||
{
|
||
// Need to log in before switching topic, so Give a dummy name here
|
||
|
||
conn->Flags &= ~GETTINGUSER;
|
||
strcpy(user->Name, "?_name");
|
||
ChatSendWelcomeMsg(conn->BPQStream, conn, user);
|
||
}
|
||
|
||
OrigBuffer[40] = 0;
|
||
sprintf(&OrigBuffer[40],"/t %s\r", topic);
|
||
strcpy(OrigBuffer, &OrigBuffer[40]);
|
||
len = (int)strlen(OrigBuffer);
|
||
}
|
||
else
|
||
{
|
||
// Normal input
|
||
|
||
if (conn->Flags & GETTINGUSER)
|
||
{
|
||
// Check not getting *RTL in response to Name prompt
|
||
|
||
if (memcmp(Buffer, "*RTL", 4) == 0)
|
||
{
|
||
// Other end thinks this is a node-node link
|
||
|
||
Logprintf(LOG_CHAT, conn, '!', "Station %s trying to start Node Protocol, but not defined as a Node",
|
||
conn->Callsign);
|
||
|
||
knownnode_add(conn->Callsign); // So it won't happen again
|
||
|
||
Disconnect(conn->BPQStream);
|
||
return;
|
||
}
|
||
|
||
conn->Flags &= ~GETTINGUSER;
|
||
memcpy(user->Name, Buffer, len-1);
|
||
ChatSendWelcomeMsg(conn->BPQStream, conn, user);
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (ChatIsUTF8(OrigBuffer, len) == FALSE)
|
||
{
|
||
// With Windows it is simple - convert using current codepage
|
||
// I think the only reliable way is to convert to unicode and back
|
||
|
||
#ifdef WIN32
|
||
|
||
int wlen;
|
||
|
||
wlen = MultiByteToWideChar(CP_ACP, 0, Buffer, len, BufferW, 65536);
|
||
len = WideCharToMultiByte(CP_UTF8, 0, BufferW, wlen, BufferB, 63336, NULL, NULL);
|
||
Buffer = BufferB;
|
||
|
||
#else
|
||
size_t left = 65536;
|
||
size_t clen = len;
|
||
|
||
UCHAR * BufferBP = BufferB;
|
||
struct user_t * icu = conn->u.user;
|
||
|
||
if (conn->rtcflags & p_user)
|
||
{
|
||
if (icu->iconv_toUTF8 == NULL)
|
||
{
|
||
icu->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", icu->Codepage);
|
||
|
||
if (icu->iconv_toUTF8 == (iconv_t)-1)
|
||
icu->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252");
|
||
}
|
||
|
||
iconv(icu->iconv_toUTF8, NULL, NULL, NULL, NULL); // Reset State Machine
|
||
iconv(icu->iconv_toUTF8, &Buffer, &clen, (char ** __restrict__)&BufferBP, &left);
|
||
}
|
||
else
|
||
{
|
||
if (link_toUTF8 == NULL)
|
||
link_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252");
|
||
|
||
iconv(link_toUTF8, NULL, NULL, NULL, NULL); // Reset State Machine
|
||
iconv(link_toUTF8, &Buffer, &clen, (char ** __restrict__)&BufferBP, &left);
|
||
}
|
||
len = 65536 - left;
|
||
Buffer = BufferB;
|
||
|
||
#endif
|
||
|
||
}
|
||
ChatWriteLogLine(conn, '<',Buffer, len, LOG_CHAT);
|
||
|
||
|
||
Buffer[len] = 0;
|
||
|
||
strlop(Buffer, '\r');
|
||
|
||
if (conn->rtcflags == p_linkwait)
|
||
{
|
||
//waiting for *RTL
|
||
|
||
if (memcmp(Buffer, "*RTL", 4) == 0)
|
||
{
|
||
// Node - Node Connect
|
||
|
||
if (rtloginl (conn, conn->Callsign))
|
||
{
|
||
// Accepted
|
||
|
||
conn->Flags |= CHATLINK;
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
// Connection refused. rtlogin1 has sent error message and closed link
|
||
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (Buffer[0] == '[' && Buffer[len-2] == ']') // SID
|
||
return;
|
||
|
||
nprintf(conn, "Unexpected Message on Chat Node-Node Link - Disconnecting\r");
|
||
ChatFlush(conn);
|
||
Sleep(500);
|
||
conn->rtcflags = p_nil;
|
||
|
||
Disconnect(conn->BPQStream);
|
||
return;
|
||
}
|
||
|
||
if (conn->Flags & CHATLINK)
|
||
{
|
||
#ifndef LINBPQ
|
||
|
||
struct _EXCEPTION_POINTERS exinfo;
|
||
|
||
__try
|
||
{
|
||
chkctl(conn, Buffer, len);
|
||
}
|
||
|
||
#define EXCEPTMSG "Process Chat Line"
|
||
#include "StdExcept.c"
|
||
|
||
Debugprintf("CHAT *** Was procesing Chat Node Message %s", Buffer);
|
||
Disconnect(conn->BPQStream);
|
||
CheckProgramErrors();
|
||
}
|
||
#else
|
||
chkctl(conn, Buffer, len);
|
||
#endif
|
||
return;
|
||
}
|
||
|
||
if(conn->u.user == NULL)
|
||
{
|
||
// A node link, but not activated yet, or a chat console which has dosconnected
|
||
|
||
if (conn->BPQStream != -2)
|
||
return;
|
||
|
||
// Log console user in
|
||
|
||
if (rtloginu (conn, TRUE))
|
||
conn->Flags |= CHATMODE;
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
if ((len <6) && (memcmp(Buffer, "*RTL", 4) == 0))
|
||
{
|
||
// Other end thinks this is a node-node link
|
||
|
||
Logprintf(LOG_CHAT, conn, '!', "Station %s trying to start Node Protocol, but not defined as a Node",
|
||
conn->Callsign);
|
||
|
||
knownnode_add(conn->Callsign); // So it won't happen again
|
||
|
||
Disconnect(conn->BPQStream);
|
||
return;
|
||
}
|
||
|
||
if (Buffer[0] == '/')
|
||
{
|
||
// Process Command
|
||
|
||
int cmdLen = 0;
|
||
char * param = strchr(&Buffer[1], ' ');
|
||
|
||
if (param)
|
||
cmdLen = param - &Buffer[1];
|
||
else
|
||
cmdLen = strlen(&Buffer[1]);
|
||
|
||
if (_memicmp(&Buffer[1], "Bye", 1) == 0)
|
||
{
|
||
SendUnbuffered(conn->BPQStream, ChatSignoffMsg, (int)strlen(ChatSignoffMsg));
|
||
|
||
if (conn->BPQStream < 0)
|
||
{
|
||
logout(conn);
|
||
conn->Flags = 0;
|
||
if (conn->BPQStream == -2)
|
||
CloseConsole(conn->BPQStream);
|
||
}
|
||
else
|
||
ReturntoNode(conn->BPQStream);
|
||
|
||
return;
|
||
}
|
||
|
||
if (_memicmp(&Buffer[1], "Quit", 4) == 0)
|
||
{
|
||
SendUnbuffered(conn->BPQStream, ChatSignoffMsg, (int)strlen(ChatSignoffMsg));
|
||
|
||
if (conn->BPQStream < 0)
|
||
{
|
||
logout(conn);
|
||
conn->Flags = 0;
|
||
if (conn->BPQStream == -2)
|
||
CloseConsole(conn->BPQStream);
|
||
}
|
||
|
||
else
|
||
{
|
||
Sleep(1000);
|
||
Disconnect(conn->BPQStream);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (cmdLen > 1 && _memicmp(&Buffer[1], "History", cmdLen) == 0) // Accept Hi but not H
|
||
{
|
||
// Param is number of minutes to go back (max 24 hours)
|
||
|
||
struct HistoryRec * ptr = History;
|
||
int interval = 0;
|
||
time_t start;
|
||
int n = HistoryCount;
|
||
|
||
if (param)
|
||
interval = atoi(param);
|
||
|
||
if (interval < 1)
|
||
{
|
||
nprintf(conn, "Format is /history n, where n is history time in minutes\r");
|
||
conn->u.user->lastsendtime = time(NULL);
|
||
return;
|
||
}
|
||
|
||
if (interval > 1440)
|
||
{
|
||
nprintf(conn, "History is only held for 24 Hours (1440 Minutes)\r");
|
||
interval = 1440; // Limit to 1 day
|
||
}
|
||
|
||
start = time(NULL) - (interval * 60);
|
||
|
||
// Find first record to send
|
||
|
||
while (ptr)
|
||
{
|
||
if (ptr->Time > start)
|
||
break;
|
||
n--;
|
||
ptr = ptr->next;
|
||
}
|
||
|
||
// n is records found
|
||
|
||
while (ptr)
|
||
{
|
||
nprintf(conn, ptr->Message);
|
||
ptr = ptr->next;
|
||
}
|
||
|
||
conn->u.user->lastsendtime = time(NULL);
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
if (_memicmp(&Buffer[1], "Keepalive", 4) == 0)
|
||
{
|
||
conn->u.user->rtflags ^= u_keepalive;
|
||
upduser(conn->u.user);
|
||
nprintf(conn, "Keepalive is %s\r", (conn->u.user->rtflags & u_keepalive) ? "Enabled" : "Disabled");
|
||
conn->u.user->lastsendtime = time(NULL);
|
||
return;
|
||
}
|
||
if (_memicmp(&Buffer[1], "AUTOCHARSET", 4) == 0)
|
||
{
|
||
conn->u.user->rtflags ^= u_auto;
|
||
upduser(conn->u.user);
|
||
nprintf(conn, "Automatic Character set selection is %s\r", (conn->u.user->rtflags & u_auto) ? "Enabled" : "Disabled");
|
||
conn->u.user->lastsendtime = time(NULL);
|
||
return;
|
||
}
|
||
if (_memicmp(&Buffer[1], "UTF-8", 3) == 0)
|
||
{
|
||
conn->u.user->rtflags ^= u_noUTF8;
|
||
upduser(conn->u.user);
|
||
nprintf(conn, "Character set is %s\r", (conn->u.user->rtflags & u_noUTF8) ? "8 Bit" : "UTF-8");
|
||
conn->u.user->lastsendtime = time(NULL);
|
||
return;
|
||
}
|
||
|
||
if ((_memicmp(&Buffer[1], "CodePage", 3) == 0) || (_memicmp(&Buffer[1], "CP", 2) == 0))
|
||
{
|
||
char * Context;
|
||
char * CP = strtok_s(&Buffer[1], " ,\r", &Context);
|
||
#ifndef WIN32
|
||
iconv_t temp = NULL;
|
||
#else
|
||
int temp = 0;
|
||
WCHAR TempW[10];
|
||
#endif
|
||
CP = strtok_s(NULL, " ,\r", &Context);
|
||
|
||
if (CP == NULL || CP[0] == 0)
|
||
{
|
||
#ifndef WIN32
|
||
if (conn->u.user->Codepage[0])
|
||
nprintf(conn, "Codepage is %s\r", conn->u.user->Codepage);
|
||
#else
|
||
if (conn->u.user->Codepage)
|
||
nprintf(conn, "Codepage is %d\r", conn->u.user->Codepage);
|
||
#endif
|
||
else
|
||
nprintf(conn, "Codepage is not set\r");
|
||
|
||
return;
|
||
}
|
||
_strupr(CP);
|
||
|
||
#ifndef WIN32
|
||
|
||
// Validate Code Page by trying to open an iconv descriptor
|
||
|
||
temp = iconv_open("UTF-8", CP);
|
||
|
||
if (temp == (iconv_t)-1)
|
||
{
|
||
nprintf(conn, "Invalid Codepage %s\r", CP);
|
||
return;
|
||
}
|
||
|
||
iconv_close(conn->u.user->iconv_toUTF8);
|
||
iconv_close(conn->u.user->iconv_fromUTF8);
|
||
|
||
conn->u.user->iconv_toUTF8 = temp;
|
||
conn->u.user->iconv_fromUTF8 = iconv_open(CP, "UTF-8");
|
||
|
||
strcpy(conn->u.user->Codepage, CP);
|
||
nprintf(conn, "Codepage set to %s\r", conn->u.user->Codepage);
|
||
#else
|
||
if (CP[0] == 'C')
|
||
CP +=2;
|
||
|
||
// Validate by trying ot use it
|
||
|
||
temp = atoi(CP);
|
||
|
||
if (MultiByteToWideChar(temp, 0, "\r", 2, TempW, 10) == 0)
|
||
{
|
||
int err = GetLastError();
|
||
|
||
if (err == ERROR_INVALID_PARAMETER)
|
||
{
|
||
nprintf(conn, "Invalid Codepage %d\r", temp);
|
||
return;
|
||
}
|
||
}
|
||
|
||
conn->u.user->Codepage = temp;
|
||
nprintf(conn, "Codepage set to %d\r", conn->u.user->Codepage);
|
||
#endif
|
||
upduser(conn->u.user);
|
||
|
||
return;
|
||
}
|
||
|
||
if (_memicmp(&Buffer[1], "Shownames", 4) == 0)
|
||
{
|
||
conn->u.user->rtflags ^= u_shownames;
|
||
upduser(conn->u.user);
|
||
nprintf(conn, "Shownames is %s\r", (conn->u.user->rtflags & u_shownames) ? "Enabled" : "Disabled");
|
||
conn->u.user->lastsendtime = time(NULL);
|
||
return;
|
||
}
|
||
|
||
if (_memicmp(&Buffer[1], "Time", 4) == 0)
|
||
{
|
||
conn->u.user->rtflags ^= u_showtime;
|
||
upduser(conn->u.user);
|
||
nprintf(conn, "Show Time is %s\r", (conn->u.user->rtflags & u_showtime) ? "Enabled" : "Disabled");
|
||
conn->u.user->lastsendtime = time(NULL);
|
||
return;
|
||
}
|
||
|
||
if (_memicmp(&Buffer[1], "colours", 4) == 0)
|
||
{
|
||
int i =0;
|
||
|
||
while (i < 100)
|
||
{
|
||
nprintf(conn, "\x1b%c%02d XXXXX\r", i + 10, i);
|
||
i++;
|
||
if (i == 3)
|
||
i++;
|
||
}
|
||
return;
|
||
}
|
||
|
||
rt_cmd(conn, Buffer);
|
||
|
||
return;
|
||
}
|
||
|
||
// Send message to all other connected users on same channel
|
||
|
||
text_tellu(conn->u.user, Buffer, NULL, o_topic); // To local users.
|
||
|
||
HistoryCount = AddtoHistory(conn->u.user, Buffer);
|
||
|
||
conn->u.user->lastrealmsgtime = conn->u.user->lastmsgtime = time(NULL);
|
||
|
||
// Send to Linked nodes
|
||
|
||
for (c = circuit_hd; c; c = c->next)
|
||
{
|
||
if ((c->rtcflags & p_linked) && c->refcnt && ct_find(c, conn->u.user->topic))
|
||
nprintf(c, "%c%c%s %s %s\r", FORMAT, id_data, OurNode, conn->u.user->call, Buffer);
|
||
}
|
||
}
|
||
|
||
void upduser(USER *user)
|
||
{
|
||
FILE *in, *out;
|
||
char *c;
|
||
char Buffer[2048];
|
||
char *buf = Buffer;
|
||
|
||
in = fopen(RtUsr, "r");
|
||
|
||
if (!(in))
|
||
{
|
||
in = fopen(RtUsr, "w");
|
||
if (in)
|
||
fclose(in);
|
||
in = fopen(RtUsr, "r");
|
||
}
|
||
|
||
if (!(in)) return;
|
||
|
||
out = fopen(RtUsrTemp, "w");
|
||
|
||
if (!(out)) return;
|
||
|
||
while(fgets(buf, 128, in))
|
||
{
|
||
if (strstr(buf, "*RTL")) // Tidy user database
|
||
continue;
|
||
|
||
c = strchr(buf, ' ');
|
||
if (c) *c = '\0';
|
||
if (!matchi(buf, user->call))
|
||
{
|
||
if (c) *c = ' ';
|
||
fputs(buf, out);
|
||
}
|
||
}
|
||
|
||
#ifndef WIN32
|
||
fprintf(out, "%s %d %s %s<>%d<>%s\n", user->call, user->rtflags, user->name, user->qth, user->Colour, user->Codepage);
|
||
#else
|
||
fprintf(out, "%s %d %s %s<>%d<>%d\n", user->call, user->rtflags, user->name, user->qth, user->Colour, user->Codepage);
|
||
#endif
|
||
fclose(in);
|
||
fclose(out);
|
||
|
||
remove(RtUsr);
|
||
rename(RtUsrTemp, RtUsr);
|
||
}
|
||
|
||
char * lookupuser(char * call)
|
||
{
|
||
FILE *in;
|
||
char *flags;
|
||
char Buffer[2048];
|
||
char *buf = Buffer;
|
||
char * name;
|
||
|
||
in = fopen(RtUsr, "r");
|
||
|
||
if (in)
|
||
{
|
||
while(fgets(buf, 128, in))
|
||
{
|
||
strlop(buf, '\n');
|
||
|
||
flags = strlop(buf, ' ');
|
||
if (!matchi(buf, call)) continue;
|
||
if (!flags) break;
|
||
|
||
fclose(in);
|
||
name = strlop(flags, ' ');
|
||
strlop(name, ' ');
|
||
return _strdup(name);
|
||
}
|
||
fclose(in);
|
||
}
|
||
|
||
return NULL;
|
||
}
|
||
|
||
|
||
|
||
void rduser(USER *user)
|
||
{
|
||
FILE *in;
|
||
char *name, *flags, *qth;
|
||
char Buffer[2048];
|
||
char *buf = Buffer;
|
||
char * ptr;
|
||
|
||
user->name = _strdup("?_name");
|
||
user->qth = _strdup("?_qth");
|
||
|
||
in = fopen(RtUsr, "r");
|
||
|
||
if (in)
|
||
{
|
||
while(fgets(buf, 128, in))
|
||
{
|
||
strlop(buf, '\n');
|
||
|
||
flags = strlop(buf, ' ');
|
||
if (!matchi(buf, user->call)) continue;
|
||
if (!flags) break;
|
||
|
||
name = strlop(flags, ' ');
|
||
user->rtflags = atoi(flags);
|
||
|
||
qth = strlop(name, ' ');
|
||
strnew(&user->name, name);
|
||
|
||
if (!qth) break;
|
||
|
||
// Colour Code may follow QTH, and Code Page may follow Colour
|
||
|
||
ptr = strchr(qth, '<EFBFBD>');
|
||
if (ptr)
|
||
{
|
||
*ptr++ = 0;
|
||
user->Colour = atoi(ptr);
|
||
|
||
ptr = strchr(ptr, '<EFBFBD>');
|
||
|
||
if (ptr)
|
||
{
|
||
*ptr++ = 0;
|
||
#ifndef WIN32
|
||
strcpy(user->Codepage, ptr);
|
||
#else
|
||
user->Codepage = atoi(ptr);
|
||
#endif
|
||
}
|
||
}
|
||
|
||
strnew(&user->qth, qth);
|
||
break;
|
||
}
|
||
fclose(in);
|
||
|
||
#ifndef WIN32
|
||
|
||
// Open an iconv decriptor for each conversion
|
||
|
||
if (user->Codepage[0])
|
||
user->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", user->Codepage);
|
||
else
|
||
user->iconv_toUTF8 = (iconv_t)-1;
|
||
|
||
if (user->iconv_toUTF8 == (iconv_t)-1)
|
||
user->iconv_toUTF8 = iconv_open("UTF-8//IGNORE", "CP1252");
|
||
|
||
|
||
if (user->Codepage[0])
|
||
user->iconv_fromUTF8 = iconv_open(user->Codepage, "UTF-8");
|
||
else
|
||
user->iconv_fromUTF8 = (iconv_t)-1;
|
||
|
||
if (user->iconv_fromUTF8 == (iconv_t)-1)
|
||
user->iconv_fromUTF8 = iconv_open("CP1252//IGNORE", "UTF-8");
|
||
#endif
|
||
}
|
||
}
|
||
|
||
|
||
void ReportBadJoin(char * ncall, char *ucall)
|
||
{
|
||
Logprintf(LOG_CHAT, NULL, '!', "User %s Join from Node %s but already connected", ucall, ncall);
|
||
}
|
||
|
||
void ReportBadLeave(char * ncall, char * ucall)
|
||
{
|
||
Logprintf(LOG_CHAT, NULL, '!', "Node %s reporting Node %s as a leaving user", ncall, ucall);
|
||
}
|
||
|
||
|
||
struct DUPINFO DupInfo[MAXDUPS];
|
||
|
||
static BOOL CheckforDups(ChatCIRCUIT * circuit, char * Call, char * Msg)
|
||
{
|
||
// Primitive duplicate suppression - see if same call and text reeived in last few secons
|
||
|
||
time_t Now = time(NULL);
|
||
time_t DupCheck = Now - DUPSECONDS;
|
||
int i, saveindex = -1;
|
||
|
||
for (i = 0; i < MAXDUPS; i++)
|
||
{
|
||
if (DupInfo[i].DupTime < DupCheck)
|
||
{
|
||
// too old - use first if we need to save it
|
||
|
||
if (saveindex == -1)
|
||
{
|
||
saveindex = i;
|
||
}
|
||
continue;
|
||
}
|
||
|
||
if ((strcmp(Call, DupInfo[i].DupUser) == 0) && (memcmp(Msg, DupInfo[i].DupText, strlen(DupInfo[i].DupText)) == 0))
|
||
{
|
||
// Duplicate, so discard, but save time
|
||
|
||
DupInfo[i].DupTime = Now;
|
||
Logprintf(LOG_CHAT, circuit, '?', "Duplicate Message From %s %s suppressed", Call, Msg);
|
||
|
||
return TRUE; // Duplicate
|
||
}
|
||
|
||
}
|
||
|
||
// Not in list
|
||
|
||
if (saveindex == -1) // List is full
|
||
saveindex = MAXDUPS - 1; // Stick on end
|
||
|
||
DupInfo[saveindex].DupTime = Now;
|
||
strcpy(DupInfo[saveindex].DupUser, Call);
|
||
|
||
if (strlen(Msg) > 99)
|
||
{
|
||
memcpy(DupInfo[saveindex].DupText, Msg, 99);
|
||
DupInfo[saveindex].DupText[99] = 0;
|
||
}
|
||
else
|
||
strcpy(DupInfo[saveindex].DupText, Msg);
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
void chkctl(ChatCIRCUIT *ckt_from, char * Buffer, int Len)
|
||
{
|
||
CHATNODE * node, *ln;
|
||
ChatCIRCUIT * ckt_to;
|
||
USER * user, * su;
|
||
time_t Now = time(NULL);
|
||
LINK * Link = ckt_from->u.link;
|
||
|
||
char * ncall, * ucall, * f1, * f2, * buf;
|
||
int i;
|
||
|
||
if (Buffer[FORMAT_O] != FORMAT) return; // Not a control message.
|
||
|
||
// Check for corruption
|
||
|
||
for (i = 1; i < (Len - 1); i++)
|
||
{
|
||
if (Buffer[i] < 32)
|
||
{
|
||
if (Buffer[i] == 9)
|
||
{
|
||
Buffer[i] = 32;
|
||
continue;
|
||
}
|
||
Debugprintf("Corrupt Chat Link Messages %s", Buffer);
|
||
return;
|
||
}
|
||
}
|
||
|
||
buf = _strdup(Buffer + DATA_O);
|
||
|
||
// FORMAT and TYPE bytes are followed by node and user callsigns.
|
||
|
||
ncall = buf;
|
||
ucall = strlop(buf, ' ');
|
||
if (!ucall) { free(buf); return; } // Not a control message.
|
||
|
||
// There may be at least one field after the node and user callsigns.
|
||
// Node leave (id_unlink) has no F1.
|
||
|
||
f1 = strlop(ucall, ' ');
|
||
strlop(ucall, 9); // some have tabs ??
|
||
|
||
// If the frame came from an unknown node ignore it.
|
||
// If the frame came from us ignore it (loop breaking).
|
||
|
||
node = node_find(ncall);
|
||
if (!node || matchi(ncall, OurNode)) { free(buf); return; }
|
||
|
||
if (ckt_from->rtcflags & p_linked)
|
||
ckt_from->u.link->lastMsgReceived = Now;
|
||
|
||
switch(Buffer[TYPE_O])
|
||
{
|
||
// Data from user ucall at node ncall.
|
||
|
||
case id_data :
|
||
|
||
// Check for dups
|
||
|
||
if (CheckforDups(ckt_from, ucall, f1))
|
||
break;
|
||
|
||
user = user_find(ucall, ncall);
|
||
|
||
if (!user)
|
||
break;
|
||
|
||
user->lastrealmsgtime = user->lastmsgtime = time(NULL);
|
||
|
||
text_tellu(user, f1, NULL, o_topic);
|
||
HistoryCount = AddtoHistory(user, f1);
|
||
|
||
for (ckt_to = circuit_hd; ckt_to; ckt_to = ckt_to->next)
|
||
{
|
||
if ((ckt_to->rtcflags & p_linked) && ckt_to->refcnt &&
|
||
!cn_find(ckt_to, node) && ct_find(ckt_to, user->topic))
|
||
nprintf(ckt_to, "%s\r", Buffer);
|
||
}
|
||
break;
|
||
|
||
// User ucall at node ncall changed their Name/QTH info.
|
||
|
||
case id_user :
|
||
|
||
user = user_find(ucall, ncall);
|
||
if (!user) break;
|
||
f2 = strlop(f1, ' ');
|
||
if (!f2) break;
|
||
|
||
if ((strcmp(user->name, f1) == 0) && (strcmp(user->qth, f2) == 0)) // No Change?
|
||
break;
|
||
|
||
echo(ckt_from, node, Buffer); // Relay to other nodes.
|
||
strnew(&user->name, f1);
|
||
strnew(&user->qth, f2);
|
||
upduser(user);
|
||
break;
|
||
|
||
// User ucall logged into node ncall.
|
||
|
||
case id_join :
|
||
|
||
user = user_find(ucall, ncall);
|
||
|
||
if (user)
|
||
{
|
||
// Already Here
|
||
|
||
// If last join was less the 5 secs ago don't report - probably a "Join/Leave Storm"
|
||
|
||
if (time(NULL) - user->timeconnected > 5)
|
||
ReportBadJoin(ncall, ucall);
|
||
|
||
//if (strcmp(user->node->call, OurNode) == 0)
|
||
//{
|
||
// Locally connected, and at another node
|
||
//}
|
||
|
||
user->timeconnected = time(NULL);
|
||
break; // We have this user as an active Node
|
||
}
|
||
|
||
// update join time
|
||
|
||
echo(ckt_from, node, Buffer); // Relay to other nodes.
|
||
f2 = strlop(f1, ' ');
|
||
if (!f2) break;
|
||
user = user_join(ckt_from, ucall, ncall, NULL, FALSE);
|
||
if (!user) break;
|
||
ckt_from->refcnt++;
|
||
text_tellu_Joined(user);
|
||
strnew(&user->name, f1);
|
||
strnew(&user->qth, f2);
|
||
upduser(user);
|
||
// makelinks(); // Bring up our links if not already up
|
||
|
||
break;
|
||
|
||
// User ucall logged out of node ncall.
|
||
|
||
case id_leave :
|
||
|
||
user = user_find(ucall, ncall);
|
||
if (!user)
|
||
{
|
||
Debugprintf("CHAT: Leave for %s from %s when not on list", ucall, ncall);
|
||
break;
|
||
}
|
||
|
||
// if connected for for less than 3 seconds ignore. May give stuck nodes but should stop "Join/Leave Storm"
|
||
// we can't just silently leave as next join will propagate
|
||
|
||
if (time(NULL) - user->timeconnected < 3)
|
||
break;
|
||
|
||
echo(ckt_from, node, Buffer); // Relay to other nodes.
|
||
|
||
f2 = strlop(f1, ' ');
|
||
if (!f2) break;
|
||
|
||
text_tellu(user, rtleave, NULL, o_all);
|
||
ckt_from->refcnt--;
|
||
strnew(&user->name, f1);
|
||
strnew(&user->qth, f2);
|
||
upduser(user);
|
||
user_leave(user);
|
||
|
||
cn_dec(ckt_from, node);
|
||
node_dec(node);
|
||
|
||
break;
|
||
|
||
// Node ncall lost its link to node ucall, alias f1.
|
||
|
||
case id_unlink :
|
||
|
||
// Only relay to other nodes if we had node. Could get loop otherwise.
|
||
// ?? This could possibly cause stuck nodes
|
||
|
||
ln = node_find(ucall);
|
||
|
||
// if connected for for less than 3 seconds ignore. May give stuck nodes but should stop "Join/Leave Storm"
|
||
// we can't just silently leave as next join will propagate
|
||
|
||
if (ln)
|
||
{
|
||
if (time(NULL) - ln->timeconnected < 3)
|
||
break;
|
||
|
||
// is it on this circuit?
|
||
|
||
if (cn_find(ckt_from, ln))
|
||
{
|
||
cn_dec(ckt_from, ln);
|
||
node_dec(ln);
|
||
echo(ckt_from, node, Buffer); // Relay to other nodes if we had node. COuld get loop if
|
||
}
|
||
else
|
||
{
|
||
Debugprintf("CHAT: node %s unlink for %s when not on this link", ncall, ucall);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debugprintf("CHAT: node %s unlink for %s when not on list", ncall, ucall);
|
||
}
|
||
|
||
break;
|
||
|
||
// Node ncall acquired a link to node ucall, alias f1.
|
||
// If we are not linked, is no problem, don't link.
|
||
// If we are linked, is a loop, do what? (Try ignore!)
|
||
|
||
case id_link :
|
||
|
||
ln = node_find(ucall);
|
||
|
||
if (!ln && !matchi(ncall, OurNode))
|
||
{
|
||
f2 = strlop(f1, ' ');
|
||
cn_inc(ckt_from, ucall, f1, f2);
|
||
echo(ckt_from, node, Buffer); // Relay to other nodes.
|
||
}
|
||
else
|
||
{
|
||
// If last join was less the 5 secs ago don't report - probably a "Join/Leave Storm"
|
||
|
||
if (time(NULL) - ln->timeconnected > 5)
|
||
Debugprintf("CHAT: node %s link for %s when already on list", ncall, ucall);
|
||
|
||
// update join time
|
||
|
||
ln->timeconnected = time(NULL);
|
||
break;
|
||
}
|
||
|
||
break;
|
||
|
||
// User ucall at node ncall sent f2 to user f1.
|
||
|
||
case id_send :
|
||
user = user_find(ucall, ncall);
|
||
if (!user) break;
|
||
f2 = strlop(f1, ' ');
|
||
if (!f2) break;
|
||
su = user_find(f1, NULL);
|
||
if (!su) break;
|
||
|
||
if (su->circuit->rtcflags & p_user)
|
||
text_tellu(user, f2, f1, o_one);
|
||
else
|
||
echo(ckt_from, node, Buffer); // Relay to other nodes.
|
||
break;
|
||
|
||
// User ucall at node ncall changed topic.
|
||
|
||
case id_topic :
|
||
user = user_find(ucall, ncall);
|
||
if (user)
|
||
{
|
||
if (_stricmp(user->topic->name, f1) != 0)
|
||
{
|
||
echo(ckt_from, node, Buffer); // Relay to other nodes.
|
||
topic_chg(user, f1);
|
||
}
|
||
}
|
||
break;
|
||
|
||
|
||
case id_keepalive :
|
||
|
||
ln = node_find(ncall);
|
||
if (ln)
|
||
{
|
||
if (ln->Version == NULL)
|
||
if (f1)
|
||
ln->Version = _strdup(f1);
|
||
}
|
||
|
||
nprintf(ckt_from, "%c%c%s %s\r", FORMAT, id_pollresp, OurNode, Link->call);
|
||
break;
|
||
|
||
case id_poll:
|
||
|
||
// Send Poll Response
|
||
|
||
Link->supportsPolls = Now;
|
||
nprintf(ckt_from, "%c%c%s %s\r", FORMAT, id_pollresp, OurNode, Link->call);
|
||
break;
|
||
|
||
case id_pollresp:
|
||
|
||
Link->supportsPolls = Now;
|
||
Link->RTT = Now - Link->timePollSent;
|
||
Link->timePollSent = 0; // Cancel Timeout
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
free(buf);
|
||
}
|
||
|
||
// Tell another node about nodes known by this node.
|
||
// Do not tell it about this node, the other node knows who it
|
||
// linked to (or who linked to it).
|
||
// Tell another node about users known by this node.
|
||
// Done at incoming or outgoing link establishment.
|
||
|
||
void state_tell(ChatCIRCUIT *circuit, char * Version)
|
||
{
|
||
CHATNODE *node;
|
||
USER *user;
|
||
|
||
node = cn_inc(circuit, circuit->u.link->call, circuit->u.link->alias, Version);
|
||
node_tell(node, id_link); // Tell other nodes about this new link
|
||
|
||
// Tell the node that just linked here about nodes known on other links.
|
||
|
||
for (node = node_hd; node; node = node->next)
|
||
{
|
||
if (!matchi(node->call, OurNode))
|
||
node_xmit(node, id_link, circuit);
|
||
}
|
||
|
||
// Tell the node that just linked here about known users, and their topics.
|
||
|
||
for (user = user_hd; user; user = user->next)
|
||
{
|
||
user_xmit(user, id_join, circuit);
|
||
topic_xmit(user, circuit);
|
||
}
|
||
}
|
||
|
||
static void circuit_free(ChatCIRCUIT *circuit)
|
||
{
|
||
ChatCIRCUIT *c, *cp;
|
||
CN *ncn;
|
||
CHATNODE *nn;
|
||
TOPIC *tn;
|
||
|
||
cp = NULL;
|
||
|
||
for (c = circuit_hd; c; cp = c, c = c->next)
|
||
{
|
||
if (c == circuit)
|
||
{
|
||
if (cp) cp->next = c->next; else circuit_hd = c->next;
|
||
|
||
while (c->hnode)
|
||
{
|
||
ncn = c->hnode->next;
|
||
free(c->hnode);
|
||
c->hnode = ncn;
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (circuit_hd) return;
|
||
|
||
// RT has gone inactive. Clean up.
|
||
|
||
while (node_hd)
|
||
{
|
||
nn = node_hd->next;
|
||
free(node_hd->alias);
|
||
free(node_hd->call);
|
||
free(node_hd);
|
||
node_hd = nn;
|
||
}
|
||
|
||
while (topic_hd)
|
||
{
|
||
tn = topic_hd->next;
|
||
free(topic_hd->name);
|
||
free(topic_hd);
|
||
topic_hd = tn;
|
||
}
|
||
}
|
||
|
||
|
||
// Find a node in the node list.
|
||
|
||
CHATNODE *node_find(char *call)
|
||
{
|
||
CHATNODE *node;
|
||
|
||
for (node = node_hd; node; node = node->next)
|
||
{
|
||
//if (node->refcnt && matchi(node->call, call)) I don't think this is right!!!
|
||
if (matchi(node->call, call))
|
||
break;
|
||
}
|
||
|
||
return node;
|
||
}
|
||
|
||
// Add a reference to a node.
|
||
|
||
static CHATNODE *node_inc(char *call, char *alias, char * Version)
|
||
{
|
||
CHATNODE *node;
|
||
|
||
node = node_find(call);
|
||
|
||
if (!node)
|
||
{
|
||
knownnode_add(call);
|
||
|
||
node = zalloc(sizeof(CHATNODE));
|
||
sl_ins_hd(node, node_hd);
|
||
node->call = _strdup(call);
|
||
node->alias = _strdup(alias);
|
||
if (Version)
|
||
node->Version = _strdup(Version);
|
||
|
||
node->timeconnected = time(NULL);
|
||
|
||
// Debugprintf("New Node Rec Created at %x for %s %s", node, node->call, node->alias);
|
||
}
|
||
|
||
node->refcnt++;
|
||
return node;
|
||
}
|
||
|
||
// Remove a reference to a node.
|
||
|
||
static void node_dec(CHATNODE *node)
|
||
{
|
||
CHATNODE *t, *tp;
|
||
USER *user;
|
||
|
||
ChatCIRCUIT *circuit;
|
||
CN *cn;
|
||
|
||
if (--node->refcnt) return; // Other references.
|
||
|
||
// Remove the node from the node list.
|
||
|
||
tp = NULL;
|
||
|
||
// Make sure there aren't any user or circuit records pointing to it
|
||
|
||
for (user = user_hd; user; user = user->next)
|
||
{
|
||
if (user->node == node)
|
||
{
|
||
Debugprintf("Trying to remove node %s that is linked from user %s", node->call, user->call);
|
||
node->refcnt++;
|
||
}
|
||
}
|
||
|
||
for (circuit = circuit_hd; circuit; circuit = circuit->next)
|
||
{
|
||
if (circuit->rtcflags & p_linked)
|
||
{
|
||
for (cn = circuit->hnode; cn; cn = cn->next)
|
||
{
|
||
if (cn->node == node)
|
||
{
|
||
Debugprintf("Trying to remove node %s that is linked from circuit %s", node->call, circuit->Callsign);
|
||
node->refcnt++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (node->refcnt) return; // Now have other references.
|
||
|
||
for (t = node_hd; t; tp = t, t = t->next)
|
||
{
|
||
if (t == node)
|
||
{
|
||
if (tp) tp->next = t->next; else node_hd = t->next;
|
||
free(t->alias);
|
||
t->alias = NULL;
|
||
free(t->call);
|
||
t->call = NULL;
|
||
free(t);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// User joins a topic.
|
||
|
||
static TOPIC *topic_join(ChatCIRCUIT *circuit, char *s)
|
||
{
|
||
CT *ct;
|
||
TOPIC *topic;
|
||
|
||
// Look for an existing topic.
|
||
|
||
for (topic = topic_hd; topic; topic = topic->next)
|
||
{
|
||
if (matchi(topic->name, s))
|
||
break;
|
||
}
|
||
|
||
// Create a new topic, if needed.
|
||
|
||
if (!topic)
|
||
{
|
||
topic = zalloc(sizeof(TOPIC));
|
||
sl_ins_hd(topic, topic_hd);
|
||
topic->name = _strdup(s);
|
||
}
|
||
|
||
topic->refcnt++; // One more user in this topic.
|
||
|
||
Logprintf(LOG_CHAT, circuit, '?', "topic_join complete user %s topic %s addr %x ref %d",
|
||
circuit->u.user->call, topic->name, topic, topic->refcnt);
|
||
|
||
|
||
// Add the circuit / topic association.
|
||
|
||
for (ct = circuit->topic; ct; ct = ct->next)
|
||
{
|
||
if (ct->topic == topic)
|
||
{
|
||
ct->refcnt++;
|
||
return topic;
|
||
}
|
||
}
|
||
|
||
ct = zalloc(sizeof(CT));
|
||
sl_ins_hd(ct, circuit->topic);
|
||
ct->topic = topic;
|
||
ct->refcnt = 1;
|
||
return topic;
|
||
}
|
||
|
||
// User leaves a topic.
|
||
|
||
static void topic_leave(ChatCIRCUIT *circuit, TOPIC *topic)
|
||
{
|
||
CT *ct, *ctp;
|
||
TOPIC *t, *tp;
|
||
|
||
Logprintf(LOG_CHAT, circuit, '?', "topic_leave user %s topic %s addr %x ref %d",
|
||
circuit->u.user->call, topic->name, topic, topic->refcnt);
|
||
|
||
topic->refcnt--;
|
||
|
||
ctp = NULL;
|
||
|
||
for (ct = circuit->topic; ct; ctp = ct, ct = ct->next)
|
||
{
|
||
if (ct->topic == topic)
|
||
{
|
||
if (!--ct->refcnt)
|
||
{
|
||
if (ctp) ctp->next = ct->next; else circuit->topic = ct->next;
|
||
free(ct);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
tp = NULL;
|
||
|
||
for (t = topic_hd; t; tp = t, t = t->next)
|
||
{
|
||
if (!t->refcnt && (t == topic))
|
||
{
|
||
if (tp) tp->next = t->next; else topic_hd = t->next;
|
||
free(t->name);
|
||
free(t);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Find a circuit/topic association.
|
||
|
||
int ct_find(ChatCIRCUIT *circuit, TOPIC *topic)
|
||
{
|
||
CT *ct;
|
||
|
||
for (ct = circuit->topic; ct; ct = ct->next)
|
||
{
|
||
if (ct->topic == topic)
|
||
return ct->refcnt;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
// Nodes reached from each circuit. Used only if the circuit is a link.
|
||
|
||
// Remove a circuit/node association.
|
||
|
||
static void cn_dec(ChatCIRCUIT *circuit, CHATNODE *node)
|
||
{
|
||
CN *c, *cp;
|
||
|
||
// Debugprintf("CHAT: Remove c/n %s ", node->call);
|
||
|
||
cp = NULL;
|
||
|
||
for (c = circuit->hnode; c; cp = c, c = c->next)
|
||
{
|
||
if (c->node == node)
|
||
{
|
||
// CN * cn;
|
||
// int len;
|
||
// char line[1000]="";
|
||
|
||
if (--c->refcnt)
|
||
{
|
||
// Debugprintf("CHAT: Remove c/n Node %s still in use refcount %d", node->call, c->refcnt);
|
||
return; // Still in use
|
||
}
|
||
|
||
if (cp)
|
||
cp->next = c->next;
|
||
else
|
||
circuit->hnode = c->next;
|
||
|
||
free(c);
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (c == NULL)
|
||
{
|
||
CN * cn;
|
||
int len;
|
||
char line[1000]="";
|
||
|
||
// not found??
|
||
|
||
Debugprintf("CHAT: !! Remove c/n Node %s addr %x not found cn chain follows", node->call, node);
|
||
|
||
line[0] = 0;
|
||
|
||
for (cn = circuit->hnode; cn; cn = cn->next)
|
||
{
|
||
if (cn->node && cn->node->call)
|
||
{
|
||
#ifndef LINBPQ
|
||
__try
|
||
{
|
||
#endif
|
||
len = sprintf(line, "%s %p %s", line, cn->node, cn->node->alias);
|
||
if (len > 80)
|
||
{
|
||
Debugprintf("%s", line);
|
||
len = sprintf(line, " ");
|
||
}
|
||
#ifndef LINBPQ
|
||
}
|
||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||
{len = sprintf("%s *PE* Corrupt Rec %x %x ", line, cn, cn->node);}
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
len = sprintf("%s Corrupt Rec %x %x ", line, cn, cn->node);
|
||
}
|
||
}
|
||
Debugprintf("%s", line);
|
||
|
||
}
|
||
|
||
|
||
}
|
||
|
||
// Add a circuit/node association.
|
||
|
||
static CHATNODE *cn_inc(ChatCIRCUIT *circuit, char *call, char *alias, char * Version)
|
||
{
|
||
CHATNODE *node;
|
||
CN *cn;
|
||
|
||
node = node_inc(call, alias, Version);
|
||
|
||
for (cn = circuit->hnode; cn; cn = cn->next)
|
||
{
|
||
if (cn->node == node)
|
||
{
|
||
cn->refcnt++;
|
||
// Debugprintf("cn_inc cn Refcount for %s->%s incremented to %d - adding Call %s",
|
||
// circuit->Callsign, node->call, cn->refcnt, call);
|
||
|
||
return node;
|
||
}
|
||
}
|
||
|
||
cn = zalloc(sizeof(CN));
|
||
sl_ins_hd(cn, circuit->hnode);
|
||
cn->node = node;
|
||
cn->refcnt = 1;
|
||
|
||
// Debugprintf("cn_inc New cn for %s->%s - adding Call %s",
|
||
// circuit->Callsign, node->call, call);
|
||
|
||
return node;
|
||
}
|
||
|
||
// Find a circuit/node association.
|
||
|
||
static int cn_find(ChatCIRCUIT *circuit, CHATNODE *node)
|
||
{
|
||
CN *cn;
|
||
|
||
for (cn = circuit->hnode; cn; cn = cn->next)
|
||
{
|
||
if (cn->node == node)
|
||
return cn->refcnt;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
// From a local user to a specific user at another node.
|
||
|
||
static void text_xmit(USER *user, USER *to, char *text)
|
||
{
|
||
nprintf(to->circuit, "%c%c%s %s %s %s\r",
|
||
FORMAT, id_send, OurNode, user->call, to->call, text);
|
||
}
|
||
|
||
void put_text(ChatCIRCUIT * circuit, USER * user, UCHAR * buf)
|
||
{
|
||
UCHAR BufferB[4096];
|
||
|
||
// Text is UTF-8 internally. If user doen't want UTF-8. convert to Node's locale
|
||
|
||
if (circuit->u.user->rtflags & u_noUTF8)
|
||
{
|
||
#ifdef WIN32
|
||
char * Buffer = buf;
|
||
WCHAR BufferW[4096];
|
||
int wlen, blen;
|
||
BOOL DefaultUsed = FALSE;
|
||
char Subst = '?';
|
||
|
||
wlen = MultiByteToWideChar(CP_UTF8, 0, buf, (int)strlen(buf) + 1, BufferW, 4096);
|
||
blen = WideCharToMultiByte(circuit->u.user->Codepage, 0, BufferW, wlen, BufferB + 2, 4096, &Subst, &DefaultUsed);
|
||
|
||
if (blen == 0) // Probably means invalid code page
|
||
blen = WideCharToMultiByte(CP_ACP, 0, BufferW, wlen, BufferB + 2, 4096, &Subst, &DefaultUsed);
|
||
|
||
buf = BufferB + 2;
|
||
BufferB[blen + 2] = 0;
|
||
#else
|
||
|
||
size_t left = 4096;
|
||
UCHAR * BufferBP = BufferB;
|
||
size_t len = strlen(buf) + 1;
|
||
struct user_t * icu = circuit->u.user;
|
||
|
||
if (icu->iconv_fromUTF8 == NULL)
|
||
{
|
||
icu->iconv_fromUTF8 = iconv_open(icu->Codepage, "UTF-8");
|
||
|
||
if (icu->iconv_fromUTF8 == (iconv_t)-1)
|
||
icu->iconv_fromUTF8 = iconv_open("CP1252//IGNORE", "UTF-8");
|
||
}
|
||
|
||
iconv(icu->iconv_fromUTF8, NULL, NULL, NULL, NULL); // Reset State Machine
|
||
iconv(icu->iconv_fromUTF8, (char ** __restrict__)&buf, &len, (char ** __restrict__)&BufferBP, &left);
|
||
|
||
len = 4096 - left;
|
||
buf = BufferB;
|
||
|
||
#endif
|
||
|
||
}
|
||
|
||
|
||
if (circuit->u.user->rtflags & u_colour) // Use Colour
|
||
{
|
||
// Put a colour header on message
|
||
|
||
*(--buf) = user->Colour;
|
||
*(--buf) = 0x1b;
|
||
nputs(circuit, buf);
|
||
buf +=2;
|
||
}
|
||
else
|
||
nputs(circuit, buf);
|
||
|
||
|
||
|
||
circuit->u.user->lastsendtime = time(NULL);
|
||
}
|
||
|
||
void text_tellu(USER *user, char *text, char *to, int who)
|
||
{
|
||
ChatCIRCUIT *circuit;
|
||
UCHAR Buffer[2048];
|
||
UCHAR *buf = &Buffer[4];
|
||
char * Time;
|
||
struct tm * tm;
|
||
char Stamp[20];
|
||
time_t T;
|
||
|
||
T = time(NULL);
|
||
tm = gmtime(&T);
|
||
|
||
sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min);
|
||
|
||
// Send it to all connected users in the same topic.
|
||
// Echo to originator if requested.
|
||
|
||
for (circuit = circuit_hd; circuit; circuit = circuit->next)
|
||
{
|
||
if (!(circuit->rtcflags & p_user)) continue; // Circuit is a link.
|
||
|
||
if ((circuit->u.user == user) && !(user->rtflags & u_echo)) continue;
|
||
|
||
if (circuit->u.user->rtflags & u_showtime)
|
||
Time = Stamp;
|
||
else
|
||
Time = "";
|
||
|
||
if (circuit->u.user->rtflags & u_shownames)
|
||
sprintf(buf, "%s%-6.6s %s %c %s\r", Time, user->call, user->name, (who == o_one) ? '>' : ':', text);
|
||
else
|
||
sprintf(buf, "%s%-6.6s %c %s\r", Time, user->call, (who == o_one) ? '>' : ':', text);
|
||
|
||
|
||
switch(who)
|
||
{
|
||
case o_topic :
|
||
if (circuit->u.user->topic == user->topic)
|
||
put_text(circuit, user, buf); // Send adding Colour if wanted
|
||
|
||
break;
|
||
|
||
case o_all:
|
||
|
||
put_text(circuit, user, buf); // Send adding Colour if wanted
|
||
|
||
break;
|
||
|
||
case o_one :
|
||
if (matchi(circuit->u.user->call, to))
|
||
put_text(circuit, user, buf); // Send adding Colour if wanted
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
extern int FlashOnConnect;
|
||
|
||
void text_tellu_Joined(USER * user)
|
||
{
|
||
ChatCIRCUIT *circuit;
|
||
UCHAR Buffer[200];
|
||
UCHAR *buf = &Buffer[4];
|
||
char * Time;
|
||
struct tm * tm;
|
||
char Stamp[20];
|
||
time_t T;
|
||
char prog[256] = "";
|
||
|
||
T = time(NULL);
|
||
tm = gmtime(&T);
|
||
|
||
sprintf(Stamp,"%02d:%02d ", tm->tm_hour, tm->tm_min);
|
||
|
||
sprintf(buf, "%s%-6.6s : %s *** Joined Chat, Topic %s", Stamp, user->call, user->name, user->topic->name);
|
||
|
||
if (reportChatEvents)
|
||
{
|
||
|
||
#ifdef WIN32
|
||
if (pRunEventProgram)
|
||
pRunEventProgram("ChatNewUser.exe", user->call);
|
||
#else
|
||
sprintf(prog, "%s/%s", BPQDirectory, "ChatNewUser");
|
||
RunEventProgram(prog, user->call);
|
||
#endif
|
||
}
|
||
|
||
// Send it to all connected users in the same topic.
|
||
// Echo to originator if requested.
|
||
|
||
for (circuit = circuit_hd; circuit; circuit = circuit->next)
|
||
{
|
||
if (!(circuit->rtcflags & p_user)) continue; // Circuit is a link.
|
||
if ((circuit->u.user == user) && !(user->rtflags & u_echo)) continue;
|
||
|
||
if (circuit->u.user->rtflags & u_showtime)
|
||
Time = Stamp;
|
||
else
|
||
Time = "";
|
||
|
||
sprintf(buf, "%s%-6.6s : %s *** Joined Chat, Topic %s", Time, user->call, user->name, user->topic->name);
|
||
|
||
put_text(circuit, user, buf); // Send adding Colour if wanted
|
||
|
||
if (circuit->u.user->rtflags & u_bells)
|
||
if (circuit->BPQStream < 0) // Console
|
||
{
|
||
#ifndef LINBPQ
|
||
if (FlashOnConnect) FlashWindow(ConsHeader[1]->hConsole, TRUE);
|
||
#endif
|
||
nputc(circuit, 7);
|
||
// PlaySound ("BPQCHAT_USER_LOGIN", NULL, SND_ALIAS | SND_APPLICATION | SND_ASYNC);
|
||
}
|
||
else
|
||
nputc(circuit, 7);
|
||
|
||
nputc(circuit, 13);
|
||
}
|
||
}
|
||
// Tell one link circuit about a local user change of topic.
|
||
|
||
static void topic_xmit(USER *user, ChatCIRCUIT *circuit)
|
||
{
|
||
nprintf(circuit, "%c%c%s %s %s\r",
|
||
FORMAT, id_topic, OurNode, user->call, user->topic->name);
|
||
}
|
||
|
||
// Tell another node about one known node on a link add or drop
|
||
// if that node is from some other link.
|
||
|
||
static void node_xmit(CHATNODE *node, char kind, ChatCIRCUIT *circuit)
|
||
{
|
||
#ifndef LINBPQ
|
||
struct _EXCEPTION_POINTERS exinfo;
|
||
|
||
__try
|
||
{
|
||
#endif
|
||
if (!cn_find(circuit, node))
|
||
if (node->Version && (kind == id_link))
|
||
nprintf(circuit, "%c%c%s %s %s %s\r", FORMAT, kind, OurNode, node->call, node->alias, node->Version);
|
||
else
|
||
nprintf(circuit, "%c%c%s %s %s\r", FORMAT, kind, OurNode, node->call, node->alias);
|
||
|
||
#ifndef LINBPQ
|
||
}
|
||
|
||
#define EXCEPTMSG "node_xmit"
|
||
#include "StdExcept.c"
|
||
|
||
Debugprintf("Corrupt Rec %x %x %x", node, node->call, node->alias);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
// Tell all other nodes about one node known by this node.
|
||
|
||
static void node_tell(CHATNODE *node, char kind)
|
||
{
|
||
ChatCIRCUIT *circuit;
|
||
|
||
for (circuit = circuit_hd; circuit; circuit = circuit->next)
|
||
{
|
||
if (circuit->rtcflags & p_linked)
|
||
node_xmit(node, kind, circuit);
|
||
}
|
||
}
|
||
|
||
// Tell another node about a user login/logout at this node.
|
||
|
||
static void user_xmit(USER *user, char kind, ChatCIRCUIT *circuit)
|
||
{
|
||
CHATNODE *node;
|
||
|
||
node = user->node;
|
||
|
||
if (!cn_find(circuit, node))
|
||
nprintf(circuit, "%c%c%s %s %s %s\r", FORMAT, kind, node->call, user->call, user->name, user->qth);
|
||
}
|
||
|
||
// Tell all other nodes about a user login/logout at this node.
|
||
|
||
static void user_tell(USER *user, char kind)
|
||
{
|
||
ChatCIRCUIT *circuit;
|
||
|
||
for (circuit = circuit_hd; circuit; circuit = circuit->next)
|
||
{
|
||
if (circuit->rtcflags & p_linked)
|
||
user_xmit(user, kind, circuit);
|
||
}
|
||
}
|
||
|
||
// Find the user record for call@node. Node can be NULL, meaning any node
|
||
|
||
USER *user_find(char *call, char * node)
|
||
{
|
||
USER *user;
|
||
|
||
for (user = user_hd; user; user = user->next)
|
||
{
|
||
if (node)
|
||
{
|
||
if (matchi(user->call, call) && matchi(user->node->call, node))
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
if (matchi(user->call, call))
|
||
break;
|
||
}
|
||
}
|
||
|
||
return user;
|
||
}
|
||
|
||
static void user_leave(USER *user)
|
||
{
|
||
USER *t, *tp;
|
||
|
||
topic_leave(user->circuit, user->topic);
|
||
|
||
tp = NULL;
|
||
|
||
for (t = user_hd; t; tp = t, t = t->next)
|
||
{
|
||
if (t == user)
|
||
{
|
||
if (tp) tp->next = t->next; else user_hd = t->next;
|
||
|
||
free(t->name);
|
||
free(t->call);
|
||
free(t->qth);
|
||
#ifndef WIN32
|
||
if (t->iconv_fromUTF8)
|
||
iconv_close(t->iconv_fromUTF8);
|
||
if (t->iconv_toUTF8)
|
||
iconv_close(t->iconv_toUTF8);
|
||
#endif
|
||
free(t);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (user_hd == NULL)
|
||
ChatTmr = 59; // If no users, disconnect links after 10-20 secs
|
||
}
|
||
|
||
// User changed to a different topic.
|
||
|
||
static BOOL topic_chg(USER *user, char *s)
|
||
{
|
||
char buf[128];
|
||
|
||
if (_stricmp(user->topic->name, s) == 0) return FALSE; // Not Changed
|
||
|
||
sprintf(buf, "*** Left Topic: %s", user->topic->name);
|
||
text_tellu(user, buf, NULL, o_topic); // Tell everyone in the old topic.
|
||
topic_leave(user->circuit, user->topic);
|
||
user->topic = topic_join(user->circuit, s);
|
||
sprintf(buf, "*** Joined Topic: %s", user->topic->name);
|
||
text_tellu(user, buf, NULL, o_topic); // Tell everyone in the new topic.
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
// Create a user record for this user.
|
||
|
||
static USER *user_join(ChatCIRCUIT *circuit, char *ucall, char *ncall, char *nalias, BOOL Local)
|
||
{
|
||
CHATNODE *node;
|
||
USER *user;
|
||
|
||
if (Local)
|
||
{
|
||
node = cn_inc(circuit, ncall, nalias, Verstring);
|
||
}
|
||
else
|
||
node = cn_inc(circuit, ncall, nalias, NULL);
|
||
|
||
// Is this user already logged in at this node?
|
||
|
||
for (user = user_hd; user; user = user->next)
|
||
{
|
||
if (matchi(user->call, ucall) && (user->node == node))
|
||
return user;
|
||
}
|
||
|
||
// User is not logged in, create a user record for them.
|
||
|
||
user = zalloc(sizeof(USER));
|
||
sl_ins_hd(user, user_hd);
|
||
user->circuit = circuit;
|
||
user->call = _strdup(ucall);
|
||
_strupr(user->call);
|
||
user->node = node;
|
||
rduser(user);
|
||
|
||
if (user->Colour == 0 || user->Colour == 11) // None or default
|
||
{
|
||
// Allocate Random
|
||
int sum = 0, i;
|
||
|
||
for (i = 0; i < 9; i++)
|
||
sum += user->call[i];
|
||
sum %= 20;
|
||
|
||
user->Colour = AutoColours[sum] + 10; // Best 20 colours
|
||
}
|
||
|
||
if (circuit->rtcflags & p_user)
|
||
circuit->u.user = user;
|
||
|
||
user->timeconnected = user->lastrealmsgtime = user->lastmsgtime = time(NULL);
|
||
|
||
user->topic = topic_join(circuit, deftopic);
|
||
return user;
|
||
}
|
||
|
||
// Link went away. We dropped it, or the other node dropped it.
|
||
// Drop nodes and users connected from this link.
|
||
// Tell other (still connected) links what was dropped.
|
||
|
||
void link_drop(ChatCIRCUIT *circuit)
|
||
{
|
||
USER *user, *usernext;
|
||
CN *cn;
|
||
|
||
// So we don't try and send anything on this circuit.
|
||
|
||
if (circuit->u.link)
|
||
if (circuit->rtcflags == p_linkini)
|
||
Debugprintf("Chat link %s Link Setup Failed", circuit->u.link->call);
|
||
|
||
if (circuit->rtcflags == p_linkini)
|
||
circuit->u.link->flags = p_linkfailed;
|
||
else
|
||
circuit->u.link->flags = 0;
|
||
|
||
circuit->rtcflags = p_nil;
|
||
|
||
// Users connected on the dropped link are no longer connected.
|
||
|
||
for (user = user_hd; user; user = usernext)
|
||
{
|
||
usernext = user->next; // Save next pointer in case entry is free'd
|
||
|
||
if (user->circuit == circuit)
|
||
{
|
||
CHATNODE *node;
|
||
|
||
node = user->node;
|
||
|
||
text_tellu(user, rtleave, NULL, o_all);
|
||
user_tell(user, id_leave);
|
||
user_leave(user);
|
||
|
||
circuit->refcnt--;
|
||
if (node)
|
||
node_dec(node);
|
||
}
|
||
}
|
||
|
||
// Any node known from the dropped link is no longer known.
|
||
|
||
for (cn = circuit->hnode; cn; cn = cn->next)
|
||
{
|
||
node_tell(cn->node, id_unlink);
|
||
node_dec(cn->node);
|
||
}
|
||
|
||
// The circuit is no longer used.
|
||
|
||
circuit_free(circuit);
|
||
NeedStatus = TRUE;
|
||
}
|
||
|
||
// Handle an incoming control frame from a linked RT system.
|
||
|
||
static void echo(ChatCIRCUIT *fc, CHATNODE *node, char * Buffer)
|
||
{
|
||
ChatCIRCUIT *tc;
|
||
|
||
for (tc = circuit_hd; tc; tc = tc->next)
|
||
{
|
||
if ((tc != fc) && (tc->rtcflags & p_linked) && !cn_find(tc, node))
|
||
nprintf(tc, "%s\r", Buffer);
|
||
}
|
||
}
|
||
|
||
char ** SeparateConnectScript(char * MultiString)
|
||
{
|
||
char * ptr1 = MultiString;
|
||
char ** Value;
|
||
int Count = 0;
|
||
char * ptr;
|
||
|
||
// Convert to string array
|
||
|
||
Value = zalloc(sizeof(void *)); // always NULL entry on end even if no values
|
||
Value[0] = NULL;
|
||
|
||
ptr = MultiString;
|
||
|
||
while (ptr && strlen(ptr))
|
||
{
|
||
ptr1 = strchr(ptr, '|');
|
||
|
||
if (ptr1)
|
||
*(ptr1++) = 0;
|
||
|
||
if (strlen(ptr))
|
||
{
|
||
Value = realloc(Value, (Count + 2) * sizeof(void *));
|
||
Value[Count++] = _strdup(ptr);
|
||
}
|
||
ptr = ptr1;
|
||
}
|
||
|
||
Value[Count] = NULL;
|
||
return Value;
|
||
}
|
||
|
||
// Add an entry to list of link partners
|
||
|
||
int rtlink (char * Call)
|
||
{
|
||
LINK *link, *temp;
|
||
char *c;
|
||
char * script;
|
||
|
||
_strupr(Call);
|
||
script = strlop(Call, '|');
|
||
|
||
c = strlop(Call, ':');
|
||
if (!c) return FALSE;
|
||
|
||
link = zalloc(sizeof(LINK));
|
||
|
||
link->alias = _strdup(Call);
|
||
link->call = _strdup(c);
|
||
|
||
if (script)
|
||
{
|
||
link->ConnectScript = SeparateConnectScript(script);
|
||
link->Lines = 0;
|
||
while (link->ConnectScript[++link->Lines]);
|
||
}
|
||
else
|
||
{
|
||
// Create Script with one entry to call partner direct;
|
||
|
||
link->ConnectScript = zalloc(sizeof(void *) * 2); // always NULL entry on end
|
||
link->ConnectScript[0] = malloc(32);
|
||
sprintf(link->ConnectScript[0], "C %s", c);
|
||
link->Lines = 1;
|
||
}
|
||
|
||
if (link_hd == NULL)
|
||
link_hd = link;
|
||
else
|
||
{
|
||
temp = link_hd;
|
||
while(temp->next)
|
||
temp = temp->next;
|
||
|
||
temp->next = link;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
VOID removelinks()
|
||
{
|
||
LINK *link, *nextlink;
|
||
|
||
for (link = link_hd; link; link = nextlink)
|
||
{
|
||
nextlink = link->next;
|
||
|
||
if (link->ConnectScript)
|
||
{
|
||
int n = 0;
|
||
while(link->ConnectScript[n])
|
||
free(link->ConnectScript[n++]);
|
||
|
||
free(link->ConnectScript);
|
||
}
|
||
|
||
free(link->alias);
|
||
link->alias = 0;
|
||
free(link->call);
|
||
link->call = 0;
|
||
free(link);
|
||
link = 0;
|
||
}
|
||
link_hd = NULL;
|
||
}
|
||
VOID removeknown()
|
||
{
|
||
// Save Known Nodes list and free struct
|
||
|
||
KNOWNNODE *node, *nextnode;
|
||
FILE *out;
|
||
|
||
out = fopen(RtKnown, "w");
|
||
|
||
if (!out)
|
||
return;
|
||
|
||
for (node = known_hd; node; node = nextnode)
|
||
{
|
||
fprintf(out, "%s %u\n", node->call, (unsigned int)node->LastHeard);
|
||
|
||
nextnode = node->next;
|
||
free(node->call);
|
||
free(node);
|
||
}
|
||
known_hd = NULL;
|
||
|
||
fclose(out);
|
||
}
|
||
|
||
VOID LoadKnown()
|
||
{
|
||
// Reload Known Nodes list
|
||
|
||
FILE *in;
|
||
char buf[128];
|
||
char * ptr;
|
||
|
||
in = fopen(RtKnown, "r");
|
||
|
||
if (in == NULL)
|
||
return;
|
||
|
||
while(fgets(buf, 128, in))
|
||
{
|
||
ptr = strchr(buf, ' ');
|
||
if (ptr)
|
||
{
|
||
*(ptr) = 0;
|
||
knownnode_add(buf);
|
||
}
|
||
}
|
||
|
||
fclose(in);
|
||
}
|
||
|
||
// We don't allocate memory for circuit, but we do chain it
|
||
|
||
ChatCIRCUIT *circuit_new(ChatCIRCUIT *circuit, int flags)
|
||
{
|
||
// Make sure circuit isn't already on list
|
||
|
||
ChatCIRCUIT *c;
|
||
|
||
circuit->rtcflags = flags;
|
||
circuit->next = NULL;
|
||
|
||
for (c = circuit_hd; c; c = c->next)
|
||
{
|
||
if (c == circuit)
|
||
{
|
||
Debugprintf("CHAT: Attempting to add Circuit when already on list");
|
||
return circuit;
|
||
}
|
||
}
|
||
|
||
sl_ins_hd(circuit, circuit_hd);
|
||
|
||
return circuit;
|
||
}
|
||
|
||
// Handle an incoming link. We should only get here if we think the station is a node.
|
||
|
||
int rtloginl (ChatCIRCUIT *conn, char * call)
|
||
{
|
||
LINK * link;
|
||
|
||
if (node_find(call))
|
||
{
|
||
Logprintf(LOG_CHAT, conn, '|', "Refusing link from %s to %s to prevent a loop", conn->Callsign, OurNode);
|
||
|
||
nprintf(conn, "Refusing link from %s to %s to prevent a loop.\n", conn->Callsign, OurNode);
|
||
ChatFlush(conn);
|
||
Sleep(500);
|
||
conn->rtcflags = p_nil;
|
||
Disconnect(conn->BPQStream);
|
||
return FALSE; // Already linked.
|
||
}
|
||
|
||
for (link = link_hd; link; link = link->next)
|
||
{
|
||
if (matchi(call, link->call))
|
||
break;
|
||
}
|
||
|
||
if (!link)
|
||
{
|
||
// We don't link with this system. Shouldn't happen, as we checked earlier
|
||
|
||
nprintf(conn, "Node %s does not have %s defined as a node to link to - closing.\r",
|
||
OurNode, conn->Callsign);
|
||
ChatFlush(conn);
|
||
Sleep(500);
|
||
conn->rtcflags = p_nil;
|
||
Disconnect(conn->BPQStream);
|
||
return FALSE;
|
||
}
|
||
|
||
if (link->flags & (p_linked | p_linkini))
|
||
{
|
||
// Already Linked. Used to Disconnect, but that can cause sync errors
|
||
// Try closing old link and keeping new
|
||
|
||
ChatCIRCUIT *c;
|
||
int len;
|
||
char Msg[80];
|
||
|
||
for (c = circuit_hd; c; c = c->next)
|
||
{
|
||
if (c->u.link == link)
|
||
{
|
||
len=sprintf_s(Msg, sizeof(Msg), "Chat Node %s Connect when Connected - Old Connection Closed", call);
|
||
ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT);
|
||
|
||
c->Active = FALSE; // So we don't try to clear circuit again
|
||
Disconnect(c->BPQStream);
|
||
link_drop(c);
|
||
RefreshMainWindow();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Accept the link request.
|
||
|
||
circuit_new(conn, p_linked);
|
||
conn->u.link = link;
|
||
nputs(conn, "OK\r");
|
||
link->flags = p_linked;
|
||
link->delay = 0; // Dont delay first restart
|
||
state_tell(conn, NULL);
|
||
conn->u.link->timePollSent = time(NULL); // Keepalive is a poll
|
||
nprintf(conn, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, conn->u.link->call, Verstring);
|
||
|
||
NeedStatus = TRUE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
// User connected to chat, or did chat command from BBS
|
||
|
||
int rtloginu (ChatCIRCUIT *circuit, BOOL Local)
|
||
{
|
||
USER *user;
|
||
|
||
// Is this user already logged in to RT somewhere else?
|
||
|
||
user = user_find(circuit->UserPointer->Call, NULL);
|
||
|
||
if (user)
|
||
{
|
||
// if connected at this node, kill old connection and allow new login
|
||
|
||
if (user->node == node_find(OurNode))
|
||
{
|
||
nputs(circuit, "*** Already connected at this node - old session will be closed.\r");
|
||
|
||
if (user->circuit->BPQStream < 0)
|
||
{
|
||
CloseConsole(user->circuit->BPQStream);
|
||
}
|
||
else
|
||
{
|
||
Disconnect(user->circuit->BPQStream);
|
||
}
|
||
}
|
||
else
|
||
nputs(circuit, "*** Already connected at another node.\r");
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
// Create the user entry.
|
||
|
||
circuit_new(circuit, p_user);
|
||
|
||
user = user_join(circuit, circuit->UserPointer->Call, OurNode, OurAlias, Local);
|
||
circuit->u.user = user;
|
||
|
||
if (strcmp(user->name, "?_name") == 0)
|
||
{
|
||
user->name = _strdup(circuit->UserPointer->Name);
|
||
}
|
||
upduser(user);
|
||
|
||
ChatExpandAndSendMessage(circuit, ChatWelcomeMsg, LOG_CHAT);
|
||
text_tellu_Joined(user);
|
||
user_tell(user, id_join);
|
||
show_users(circuit);
|
||
user->lastsendtime = time(NULL);
|
||
// makelinks();
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
void logout(ChatCIRCUIT *circuit)
|
||
{
|
||
USER *user;
|
||
CHATNODE *node;
|
||
|
||
circuit->rtcflags = p_nil;
|
||
user = circuit->u.user;
|
||
|
||
if (user) // May not have logged in if already conencted
|
||
{
|
||
node = user->node;
|
||
|
||
user_tell(user, id_leave);
|
||
text_tellu(user, rtleave, NULL, o_all);
|
||
user_leave(user);
|
||
|
||
// order changed so node_dec can check if a node that is about the be deleted has eny users
|
||
|
||
if (node)
|
||
{
|
||
cn_dec(circuit, node);
|
||
node_dec(node);
|
||
}
|
||
|
||
circuit->u.user = NULL;
|
||
}
|
||
|
||
circuit_free(circuit);
|
||
}
|
||
|
||
void show_users(ChatCIRCUIT *circuit)
|
||
{
|
||
USER *user;
|
||
char * Alias;
|
||
char * Topic;
|
||
|
||
int i = 0;
|
||
|
||
// First count them
|
||
|
||
for (user = user_hd; user; user = user->next)
|
||
{
|
||
i++;
|
||
}
|
||
|
||
nprintf(circuit, "%d Station(s) connected:\r", i);
|
||
|
||
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;
|
||
|
||
#ifndef LINBPQ
|
||
__try
|
||
{
|
||
#endif
|
||
if (circuit->u.user->rtflags & u_colour) // Use Colour
|
||
nprintf(circuit, "\x1b%c%-6.6s at %-9.9s %s, %s [%s] Idle for %d seconds\r",
|
||
user->Colour, user->call, Alias, user->name, user->qth, Topic, time(NULL) - user->lastrealmsgtime);
|
||
else
|
||
nprintf(circuit, "%-6.6s at %-9.9s %s, %s [%s] Idle for %d seconds\r",
|
||
user->call, Alias, user->name, user->qth, Topic, time(NULL) - user->lastrealmsgtime);
|
||
#ifndef LINBPQ
|
||
}
|
||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||
{
|
||
Debugprintf("MAILCHAT *** Program Error in show_users");
|
||
CheckProgramErrors();
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
|
||
|
||
static void show_nodes(ChatCIRCUIT *circuit)
|
||
{
|
||
CHATNODE *node;
|
||
|
||
nputs(circuit, "Known Nodes:\r");
|
||
|
||
for (node = node_hd; node; node = node->next)
|
||
{
|
||
if (node->refcnt)
|
||
if (node->Version)
|
||
nprintf(circuit, "%s:%s %s %u\r", node->alias, node->call, node->Version, node->refcnt);
|
||
else
|
||
nprintf(circuit, "%s:%s %s %u\r", node->alias, node->call, "Not Known", node->refcnt);
|
||
}
|
||
}
|
||
|
||
// /P Command: List circuits and remote RT on them.
|
||
|
||
#define xxx "\r "
|
||
|
||
static void show_circuits(ChatCIRCUIT *conn, char Flag)
|
||
{
|
||
ChatCIRCUIT *circuit;
|
||
CHATNODE *node;
|
||
LINK *link;
|
||
char line[1000];
|
||
int len;
|
||
CN *cn;
|
||
|
||
int i = 0;
|
||
|
||
// First count them
|
||
|
||
for (node = node_hd; node; node = node->next)
|
||
{
|
||
i++;
|
||
}
|
||
|
||
nprintf(conn, "%d Node(s)\r", i);
|
||
|
||
if (Flag == 'c')
|
||
sprintf(line, "Here %-6.6s <-", OurNode);
|
||
else
|
||
sprintf(line, "Here %-6.6s <-", OurAlias);
|
||
|
||
for (node = node_hd; node; node = node->next) if (node->refcnt)
|
||
{
|
||
if (Flag == 'c')
|
||
len = sprintf(line, "%s %s", line, node->call);
|
||
else
|
||
len = sprintf(line, "%s %s", line, node->alias);
|
||
if (len > 80)
|
||
{
|
||
nprintf(conn, "%s\r", line);
|
||
len = sprintf(line, " ");
|
||
}
|
||
}
|
||
|
||
nprintf(conn, "%s\r", line);
|
||
|
||
for (circuit = circuit_hd; circuit; circuit = circuit->next)
|
||
{
|
||
if (circuit->rtcflags & p_linked)
|
||
{
|
||
if (Flag == 'c')
|
||
len = sprintf(line, "Nodes via %-6.6s(%d) -", circuit->u.link->call, circuit->refcnt);
|
||
else
|
||
len = sprintf(line, "Nodes via %-6.6s(%d) -", circuit->u.link->alias, circuit->refcnt);
|
||
|
||
#ifndef LINBPQ
|
||
__try{
|
||
for (cn = circuit->hnode; cn; cn = cn->next)
|
||
{
|
||
if (cn->node && cn->node->alias)
|
||
{
|
||
__try
|
||
{
|
||
if (Flag == 'c')
|
||
len = sprintf(line, "%s %s", line, cn->node->call);
|
||
else
|
||
len = sprintf(line, "%s %s", line, cn->node->alias);
|
||
if (len > 80)
|
||
{
|
||
nprintf(conn, "%s\r", line);
|
||
len = sprintf(line, " ");
|
||
}
|
||
}
|
||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||
{len = sprintf(line, "%s *PE* Corrupt Rec %x %x", line, cn, cn->node);}
|
||
}
|
||
else
|
||
len = sprintf(line, "%s Corrupt Rec %x %x ", line, cn, cn->node);
|
||
}
|
||
}
|
||
__except(EXCEPTION_EXECUTE_HANDLER)
|
||
{len = sprintf(line, "%s *PE* Corrupt Rec %x %x ", line, cn, cn->node);}
|
||
#else
|
||
for (cn = circuit->hnode; cn; cn = cn->next)
|
||
{
|
||
if (cn->node && cn->node->alias)
|
||
{
|
||
if (Flag == 'c')
|
||
len = sprintf(line, "%s %s", line, cn->node->call);
|
||
else
|
||
len = sprintf(line, "%s %s", line, cn->node->alias);
|
||
if (len > 80)
|
||
{
|
||
nprintf(conn, "%s\r", line);
|
||
len = sprintf(line, " ");
|
||
}
|
||
}
|
||
else
|
||
len = sprintf(line, "%s Corrupt Rec %p %p ", line, cn, cn->node);
|
||
}
|
||
#endif
|
||
nprintf(conn, "%s\r", line);
|
||
|
||
}
|
||
else if (circuit->rtcflags & p_user)
|
||
nprintf(conn, "User %-6.6s\r", circuit->u.user->call);
|
||
else if (circuit->rtcflags & p_linkini)
|
||
{
|
||
if (circuit->u.link)
|
||
{ if (Flag == 'c')
|
||
nprintf(conn, "Link %-6.6s (setup)\r", circuit->u.link->call);
|
||
else
|
||
nprintf(conn, "Link %-6.6s (setup)\r", circuit->u.link->alias);
|
||
|
||
}
|
||
else
|
||
nprintf(conn, "Link ?? (setup)\r");
|
||
}
|
||
}
|
||
|
||
nprintf(conn, "Links Defined:\r");
|
||
|
||
for (link = link_hd; link; link = link->next)
|
||
{
|
||
if (link->flags & p_linked )
|
||
if (link->supportsPolls)
|
||
nprintf(conn, " %-10.10s Open RTT %d\r", link->call, link->RTT);
|
||
else
|
||
nprintf(conn, " %-10.10s Open\r", link->call);
|
||
else if (link->flags & (p_linked | p_linkini))
|
||
nprintf(conn, " %-10.10s Connecting\r", link->call);
|
||
else if (link->flags & p_linkfailed)
|
||
nprintf(conn, " %-10.10s Connect failed\r", link->call);
|
||
else
|
||
nprintf(conn, " %-10.10s Idle\r", link->call);
|
||
}
|
||
}
|
||
|
||
// /T Command: List topics and users in them.
|
||
|
||
static void show_topics(ChatCIRCUIT *conn)
|
||
{
|
||
TOPIC *topic;
|
||
USER *user;
|
||
|
||
nputs(conn, "Active Topics are:\r");
|
||
|
||
for (topic = topic_hd; topic; topic = topic->next)
|
||
{
|
||
nprintf(conn, "%s\r", topic->name);
|
||
|
||
if (topic->refcnt)
|
||
{
|
||
nputs(conn, " ");
|
||
for (user = user_hd; user; user = user->next)
|
||
{
|
||
if (user->topic == topic)
|
||
nprintf(conn, " %s", user->call);
|
||
}
|
||
nputc(conn, '\r');
|
||
}
|
||
}
|
||
}
|
||
|
||
static void show_users_in_topic(ChatCIRCUIT *conn)
|
||
{
|
||
TOPIC *topic;
|
||
USER *user;
|
||
|
||
nputs(conn, "Users in Topic:\r");
|
||
|
||
topic = conn->u.user->topic;
|
||
{
|
||
if (topic->refcnt)
|
||
{
|
||
for (user = user_hd; user; user = user->next)
|
||
{
|
||
if (user->topic == topic)
|
||
nprintf(conn, "%s ", user->call);
|
||
}
|
||
nputc(conn, '\r');
|
||
}
|
||
}
|
||
}
|
||
|
||
// Do a user command.
|
||
|
||
int rt_cmd(ChatCIRCUIT *circuit, char * Buffer)
|
||
{
|
||
ChatCIRCUIT *c;
|
||
USER *user, *su;
|
||
char *f1, *f2;
|
||
|
||
user = circuit->u.user;
|
||
|
||
// user->lastsendtime = time(NULL);
|
||
|
||
switch(tolower(Buffer[1]))
|
||
{
|
||
case 'a' :
|
||
user->rtflags ^= u_bells;
|
||
upduser(user);
|
||
nprintf(circuit, "Alert %s\r", (user->rtflags & u_bells) ? "Enabled" : "Disabled");
|
||
return TRUE;
|
||
|
||
case 'b' : return FALSE;
|
||
|
||
case 'c' :
|
||
user->rtflags ^= u_colour;
|
||
upduser(user);
|
||
nprintf(circuit, "Colour Mode %s\r", (user->rtflags & u_colour) ? "Enabled" : "Disabled");
|
||
return TRUE;
|
||
|
||
case 'e' :
|
||
user->rtflags ^= u_echo;
|
||
upduser(user);
|
||
nprintf(circuit, "Echo %s\r", (user->rtflags & u_echo) ? "Enabled" : "Disabled");
|
||
return TRUE;
|
||
|
||
case 'f' : makelinks(); return TRUE;
|
||
|
||
case 'h' :
|
||
case '?' :
|
||
{
|
||
char * Save;
|
||
char * MsgBytes = Save = ReadInfoFile("chathelp.txt");
|
||
|
||
if (MsgBytes)
|
||
{
|
||
int Length;
|
||
|
||
// Remove lf chars
|
||
|
||
Length = RemoveLF(MsgBytes, (int)strlen(MsgBytes));
|
||
|
||
ChatQueueMsg(circuit, MsgBytes, Length);
|
||
free(Save);
|
||
}
|
||
else
|
||
{
|
||
nputs(circuit, "Commands can be in upper or lower case.\r");
|
||
nputs(circuit, "/U - Show Users.\r/N - Enter your Name.\r/Q - Enter your QTH.\r/T - Show Topics.\r");
|
||
nputs(circuit, "/T Name - Join Topic or Create new Topic. Topic Names are not case sensitive\r/P - Show Ports and Links.\r");
|
||
nprintf(circuit, "/A - Toggle Alert on user join - %s.\r",
|
||
(user->rtflags & u_bells) ? "Enabled" : "Disabled");
|
||
nprintf(circuit, "/C - Toggle Colour Mode on or off (only works on Console or BPQTerm/TermTCP/QtTermTCP - %s.\r",
|
||
(user->rtflags & u_colour) ? "Enabled" : "Disabled");
|
||
nputs(circuit, "/Codepage CPnnnn - Set Codepage to use if UTF-8 is disabled.\r");
|
||
nprintf(circuit, "/E - Toggle Echo - %s .\r",
|
||
(user->rtflags & u_echo) ? "Enabled" : "Disabled");
|
||
nprintf(circuit, "/Keepalive - Toggle sending Keepalive messages every 10 minutes - %s.\r",
|
||
(user->rtflags & u_keepalive) ? "Enabled" : "Disabled");
|
||
nprintf(circuit, "/ShowNames - Toggle displaying name as well as call on each message - %s\r",
|
||
(user->rtflags & u_shownames) ? "Enabled" : "Disabled");
|
||
nprintf(circuit, "/Auto - Toggle Automatic character set selection - %s.\r",
|
||
(user->rtflags & u_auto) ? "Enabled" : "Disabled");
|
||
nprintf(circuit, "/UTF-8 - Character set Selection - %s.\r",
|
||
(user->rtflags & u_noUTF8) ? "8 Bit" : "UTF-8");
|
||
nprintf(circuit, "/Time - Toggle displaying timestamp on each message - %s.\r",
|
||
(user->rtflags & u_showtime) ? "Enabled" : "Disabled");
|
||
nputs(circuit, "/S CALL Text - Send Text to that station only.\r");
|
||
nputs(circuit, "/F - Force all links to be made.\r/K - Show Known nodes.\r");
|
||
nputs(circuit, "/B - Leave Chat and return to node.\r/QUIT - Leave Chat and disconnect from node.\r");
|
||
nputs(circuit, "/History nn - Display chat messages received in last nn minutes.\r");
|
||
}
|
||
}
|
||
return TRUE;
|
||
|
||
case 'k' : show_nodes(circuit); return TRUE;
|
||
|
||
case 'n' :
|
||
|
||
f1 = &Buffer[2];
|
||
|
||
while ((*f1 != 0) && (*f1 == ' '))
|
||
f1++;
|
||
|
||
if (*f1 == 0)
|
||
{
|
||
nprintf(circuit, "Name is %s\r", user->name);
|
||
return TRUE;
|
||
}
|
||
|
||
strnew(&user->name, f1);
|
||
nprintf(circuit, "Name set to %s\r", user->name);
|
||
upduser(user);
|
||
user_tell(user, id_user);
|
||
return TRUE;
|
||
|
||
case 'p' : show_circuits(circuit, Buffer[3]); return TRUE;
|
||
|
||
case 'q' :
|
||
|
||
f1 = &Buffer[2];
|
||
|
||
while ((*f1 != 0) && (*f1 == ' '))
|
||
f1++;
|
||
|
||
if (*f1 == 0)
|
||
{
|
||
nprintf(circuit, "QTH is %s\r", user->qth);
|
||
return TRUE;
|
||
}
|
||
|
||
strnew(&user->qth, f1);
|
||
|
||
nprintf(circuit, "QTH set to %s\r", user->qth);
|
||
upduser(user);
|
||
user_tell(user, id_user);
|
||
return TRUE;
|
||
|
||
case 's' :
|
||
strcat(Buffer, "\r");
|
||
f1 = strlop(Buffer, ' '); // To.
|
||
if (!f1) break;
|
||
f2 = strlop(f1, ' '); // Text to send.
|
||
if (!f2) break;
|
||
_strupr(f1);
|
||
su = user_find(f1, NULL);
|
||
|
||
if (!su)
|
||
{
|
||
nputs(circuit, "*** That user is not logged in.\r");
|
||
return TRUE;
|
||
}
|
||
|
||
// Send to the desired user only.
|
||
|
||
if (su->circuit->rtcflags & p_user)
|
||
text_tellu(user, f2, f1, o_one);
|
||
else
|
||
text_xmit(user, su, f2);
|
||
|
||
return TRUE;
|
||
|
||
case 't' :
|
||
f1 = strlop(Buffer, ' ');
|
||
if (f1)
|
||
{
|
||
if (topic_chg(user, f1))
|
||
{
|
||
nprintf(circuit, "Switched to Topic %s\r", user->topic->name);
|
||
show_users_in_topic(circuit);
|
||
|
||
// Tell all link circuits about the change of topic.
|
||
|
||
for (c = circuit_hd; c; c = c->next)
|
||
{
|
||
if (c->rtcflags & p_linked)
|
||
topic_xmit(user, c);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Already in topic
|
||
|
||
nprintf(circuit, "You were already in Topic %s\r", user->topic->name);
|
||
}
|
||
}
|
||
else
|
||
show_topics(circuit);
|
||
return TRUE;
|
||
|
||
case 'u' : show_users(circuit); return TRUE;
|
||
|
||
default : break;
|
||
}
|
||
|
||
saywhat(circuit);
|
||
return TRUE;
|
||
}
|
||
|
||
void makelinks(void)
|
||
{
|
||
LINK *link;
|
||
ChatCIRCUIT *circuit;
|
||
|
||
// Make the links. Called every 10 seconds
|
||
|
||
// Make sure previous link has completed or failed
|
||
|
||
if (RunningConnectScript)
|
||
{
|
||
// Make sure Connect Script isn't taking too long
|
||
|
||
if (time(NULL) - RunningConnectScript < 30)
|
||
return;
|
||
|
||
// Running too long - close it
|
||
|
||
for (circuit = circuit_hd; circuit; circuit = circuit->next)
|
||
{
|
||
// Find the link
|
||
|
||
if (circuit->rtcflags & (p_linkini))
|
||
{
|
||
link = circuit->u.link;
|
||
link->flags = p_linkfailed;
|
||
RunningConnectScript = 0;
|
||
link->scriptRunning = 0; // so it doesn't get reentered
|
||
Logprintf(LOG_CHAT, circuit, '|', "Connect to %s timed out", circuit->Callsign);
|
||
|
||
Disconnect(circuit->BPQStream);
|
||
}
|
||
}
|
||
RunningConnectScript = 0;
|
||
}
|
||
|
||
for (link = link_hd; link; link = link->next)
|
||
{
|
||
// Is this link already established?
|
||
|
||
if (link->flags & (p_linked | p_linkini))
|
||
continue;
|
||
|
||
// Already linked through some other node?
|
||
// If so, making this link would create a loop.
|
||
|
||
if (node_find(link->call))
|
||
continue;
|
||
|
||
// Fire up the process to handle this link.
|
||
|
||
if (link->delay == 0)
|
||
{
|
||
link->flags = p_linkini;
|
||
link->delay = 12; // 2 mins
|
||
chat_link_out(link);
|
||
return; // One at a time
|
||
}
|
||
else
|
||
link->delay--;
|
||
}
|
||
}
|
||
|
||
VOID node_close()
|
||
{
|
||
// Close all Node-Node Links
|
||
|
||
ChatCIRCUIT *circuit;
|
||
|
||
for (circuit = circuit_hd; circuit; circuit = circuit->next)
|
||
{
|
||
if (circuit->rtcflags & (p_linked | p_linkini | p_linkwait))
|
||
Disconnect(circuit->BPQStream);
|
||
}
|
||
}
|
||
|
||
// Send Keepalives to all connected nodes
|
||
|
||
static void node_keepalive()
|
||
{
|
||
ChatCIRCUIT *circuit;
|
||
|
||
NeedStatus = TRUE; // Send Report to Monitor
|
||
|
||
if (user_hd) // Any Users?
|
||
{
|
||
for (circuit = circuit_hd; circuit; circuit = circuit->next)
|
||
{
|
||
if (circuit->rtcflags & p_linked && circuit->u.link)
|
||
{
|
||
nprintf(circuit, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, circuit->u.link->call, Verstring);
|
||
circuit->u.link->timePollSent = time(NULL); // Also acts as poll
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// No users. Close links
|
||
|
||
node_close();
|
||
}
|
||
}
|
||
|
||
VOID ChatTimer()
|
||
{
|
||
// Entered every 10 seconds
|
||
|
||
int i = 0;
|
||
ChatCIRCUIT *c;
|
||
|
||
#ifndef LINBPQ
|
||
int len;
|
||
CHATNODE *node;
|
||
TOPIC *topic;
|
||
char Msg[256];
|
||
#endif
|
||
USER *user;
|
||
time_t NOW = time(NULL);
|
||
|
||
GetSemaphore(&ChatSemaphore, 0);
|
||
|
||
if (NeedStatus)
|
||
{
|
||
NeedStatus = FALSE;
|
||
SendChatLinkStatus();
|
||
}
|
||
|
||
#ifndef LINBPQ
|
||
|
||
ClearDebugWindow();
|
||
|
||
WritetoDebugWindow("Chat Nodes\r\n", 12);
|
||
|
||
for (node = node_hd; node; node = node->next)
|
||
{
|
||
len = sprintf_s(Msg, sizeof(Msg), "%s Version %s Count %d\r\n",
|
||
node->call, node->Version, node->refcnt);
|
||
WritetoDebugWindow(Msg, len);
|
||
|
||
i++;
|
||
}
|
||
|
||
SetDlgItemInt(hWnd, IDC_NODES, i, FALSE);
|
||
|
||
WritetoDebugWindow("Chat Links\r\n", 12);
|
||
|
||
i = 0;
|
||
for (c = circuit_hd; c; c = c->next)
|
||
{
|
||
if (c->rtcflags & p_linked)
|
||
{
|
||
char buff[1000];
|
||
int ptr;
|
||
CT * ct;
|
||
ptr = sprintf_s(buff, sizeof(buff), "%s Topics: ", c->u.user->call);
|
||
|
||
if (c->topic)
|
||
{
|
||
for (ct = c->topic; ct; ct = ct->next)
|
||
{
|
||
ptr+= sprintf_s(&buff[ptr], sizeof(buff) - ptr, "%s ", ct->topic->name);
|
||
}
|
||
}
|
||
WritetoDebugWindow(buff, ptr);
|
||
WritetoDebugWindow("\r\n", 2);
|
||
|
||
i++;
|
||
}
|
||
}
|
||
|
||
SetDlgItemInt(hWnd, IDC_LINKS, i, FALSE);
|
||
|
||
WritetoDebugWindow("Chat Topics\r\n", 12);
|
||
|
||
i = 0;
|
||
for (topic = topic_hd; topic; topic = topic->next)
|
||
{
|
||
len = sprintf_s(Msg, sizeof(Msg), "%s %d\r\n", topic->name, topic->refcnt);
|
||
WritetoDebugWindow(Msg, len);
|
||
i++;
|
||
}
|
||
|
||
WritetoDebugWindow("Chat Users\r\n", 12);
|
||
|
||
i = 0;
|
||
for (user = user_hd; user; user = user->next)
|
||
{
|
||
len = sprintf_s(Msg, sizeof(Msg), "%s Topic %s\r\n", user->call,
|
||
(user->topic) ? user->topic->name : "** Missing Topic **");
|
||
WritetoDebugWindow(Msg, len);
|
||
i++;
|
||
|
||
if (user->circuit && user->circuit->rtcflags & p_user) // Local User
|
||
{
|
||
time_t Idle = NOW - user->lastmsgtime;
|
||
|
||
if (Idle > 7200)
|
||
{
|
||
nprintf(user->circuit, "*** Disconnected - Idle time exceeded\r");
|
||
Sleep(1000);
|
||
|
||
if (user->circuit->BPQStream < 0)
|
||
{
|
||
CloseConsole(user->circuit->BPQStream);
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
Disconnect(user->circuit->BPQStream);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ((user->rtflags & u_keepalive) && (NOW - user->lastsendtime) > 600)
|
||
{
|
||
nprintf(user->circuit, "Chat Keepalive\r");
|
||
user->lastsendtime = NOW;
|
||
}
|
||
}
|
||
}
|
||
|
||
SetDlgItemInt(hWnd, IDC_USERS, i, FALSE);
|
||
|
||
#else
|
||
|
||
for (user = user_hd; user; user = user->next)
|
||
{
|
||
if (user->circuit && user->circuit->rtcflags & p_user) // Local User
|
||
{
|
||
if ((NOW - user->lastmsgtime) > 7200)
|
||
{
|
||
nprintf(user->circuit, "*** Disconnected - Idle time exceeded\r");
|
||
Sleep(1000);
|
||
|
||
if (user->circuit->BPQStream < 0)
|
||
{
|
||
CloseConsole(user->circuit->BPQStream);
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
Disconnect(user->circuit->BPQStream);
|
||
break;
|
||
}
|
||
}
|
||
|
||
if ((user->rtflags & u_keepalive) && (NOW - user->lastsendtime) > 600)
|
||
{
|
||
nprintf(user->circuit, "Chat Keepalive\r");
|
||
user->lastsendtime = NOW;
|
||
}
|
||
}
|
||
}
|
||
|
||
#endif
|
||
|
||
// if no message on a Node-Node link, send poll
|
||
|
||
for (c = circuit_hd; c; c = c->next)
|
||
{
|
||
if (c->rtcflags & p_linked && c->u.link)
|
||
{
|
||
time_t Now = time(NULL);
|
||
LINK * Link = c->u.link;
|
||
|
||
if (Now - Link->lastMsgReceived > 60)
|
||
{
|
||
// if we have a poll outstanding for ? 30 secs close link
|
||
// but check other end can handle polls
|
||
|
||
if (Link->supportsPolls && Link->timePollSent && Now - Link->timePollSent > 30)
|
||
{
|
||
Logprintf(LOG_CHAT, c, '|', "%s No Poll Response for %d Secs - Dropping Link",
|
||
c->Callsign, Now - Link->timePollSent);
|
||
|
||
Disconnect(c->BPQStream);
|
||
continue;
|
||
}
|
||
|
||
Link->timePollSent = Now;
|
||
nprintf(c, "%c%c%s %s\r", FORMAT, id_poll, OurNode, Link->call);
|
||
}
|
||
}
|
||
}
|
||
|
||
ChatTmr++;
|
||
|
||
if (user_hd) // Any Users?
|
||
makelinks();
|
||
|
||
if (ChatTmr > 60) // 10 Mins
|
||
{
|
||
ChatTmr = 1;
|
||
node_keepalive();
|
||
}
|
||
|
||
FreeSemaphore(&ChatSemaphore);
|
||
|
||
if (NeedINFO)
|
||
{
|
||
NeedINFO--;
|
||
|
||
if (NeedINFO == 0)
|
||
{
|
||
// Send INFO to Chatmap
|
||
|
||
char Msg[500];
|
||
int len;
|
||
|
||
NeedINFO = 360; // Send Every Hour
|
||
|
||
if (Position[0])
|
||
{
|
||
len = sprintf(Msg, "INFO %s|%s|%d|\r", Position, PopupText, PopupMode);
|
||
|
||
if (len < 256)
|
||
Send_MON_Datagram(Msg, len);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
VOID FreeChatMemory()
|
||
{
|
||
removelinks();
|
||
removeknown();
|
||
}
|
||
|
||
// Find a call in the known node list.
|
||
|
||
KNOWNNODE *knownnode_find(char *call)
|
||
{
|
||
KNOWNNODE *node;
|
||
|
||
for (node = known_hd; node; node = node->next)
|
||
{
|
||
if (matchi(node->call, call))
|
||
break;
|
||
}
|
||
|
||
return node;
|
||
}
|
||
|
||
// Add a known node.
|
||
|
||
static KNOWNNODE *knownnode_add(char *call)
|
||
{
|
||
KNOWNNODE *node;
|
||
|
||
node = knownnode_find(call);
|
||
|
||
if (!node)
|
||
{
|
||
node = zalloc(sizeof(KNOWNNODE));
|
||
sl_ins_hd(node, known_hd);
|
||
node->call = _strdup(call);
|
||
}
|
||
|
||
node->LastHeard = time(NULL);
|
||
return node;
|
||
}
|
||
|
||
static char UIDEST[10] = "DUMMY";
|
||
static char AXDEST[7];
|
||
static char ChatMYCALL[7];
|
||
|
||
#pragma pack(1)
|
||
|
||
|
||
typedef struct _MESSAGEX
|
||
{
|
||
// BASIC LINK LEVEL MESSAGE BUFFER LAYOUT
|
||
|
||
struct _MESSAGE * CHAIN;
|
||
|
||
UCHAR PORT;
|
||
USHORT LENGTH;
|
||
|
||
UCHAR DEST[7];
|
||
UCHAR ORIGIN[7];
|
||
|
||
// MAY BE UP TO 56 BYTES OF DIGIS
|
||
|
||
UCHAR CTL;
|
||
UCHAR PID;
|
||
UCHAR DATA[256];
|
||
|
||
}MESSAGEX, *PMESSAGEX;
|
||
|
||
#pragma pack()
|
||
|
||
SOCKET ChatReportSocket = 0;
|
||
|
||
|
||
VOID SetupChat()
|
||
{
|
||
u_long param=1;
|
||
BOOL bcopt=TRUE;
|
||
|
||
ConvToAX25(OurNode, ChatMYCALL);
|
||
ConvToAX25(UIDEST, AXDEST);
|
||
|
||
sprintf(Verstring, "%d.%d.%d.%d", Ver[0], Ver[1], Ver[2], Ver[3]);
|
||
|
||
LoadKnown();
|
||
|
||
ChatReportSocket = socket(AF_INET,SOCK_DGRAM,0);
|
||
|
||
if (ChatReportSocket == INVALID_SOCKET)
|
||
{
|
||
Debugprintf("Failed to create Chat Reporting socket");
|
||
ChatReportSocket = 0;
|
||
return;
|
||
}
|
||
|
||
ioctlsocket (ChatReportSocket, FIONBIO, ¶m);
|
||
setsockopt (ChatReportSocket, SOL_SOCKET, SO_BROADCAST, (const char FAR *)&bcopt,4);
|
||
}
|
||
|
||
|
||
VOID Send_MON_Datagram(UCHAR * Msg, DWORD Len)
|
||
{
|
||
MESSAGEX AXMSG;
|
||
PMESSAGEX AXPTR = &AXMSG;
|
||
|
||
if (Len > 256)
|
||
{
|
||
Debugprintf("Send_MON_Datagram Error Msg = %s Len = %d", Msg, Len);
|
||
return;
|
||
}
|
||
|
||
// ConvToAX25("GM4OAS-5", ChatMYCALL);
|
||
|
||
// Block includes the Msg Header (7 bytes), Len Does not!
|
||
|
||
memcpy(AXPTR->DEST, AXDEST, 7);
|
||
memcpy(AXPTR->ORIGIN, ChatMYCALL, 7);
|
||
AXPTR->DEST[6] &= 0x7e; // Clear End of Call
|
||
AXPTR->DEST[6] |= 0x80; // set Command Bit
|
||
|
||
AXPTR->ORIGIN[6] |= 1; // Set End of Call
|
||
AXPTR->CTL = 3; //UI
|
||
AXPTR->PID = 0xf0;
|
||
memcpy(AXPTR->DATA, Msg, Len);
|
||
|
||
SendChatReport(ChatReportSocket, (char *)&AXMSG.DEST, Len + 16);
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
VOID SendChatLinkStatus()
|
||
{
|
||
char Msg[256] = {0};
|
||
LINK * link;
|
||
int len = 0;
|
||
ChatCIRCUIT *circuit;
|
||
|
||
if (ChatApplNum == 0)
|
||
return;
|
||
|
||
// if (AXIPPort == 0)
|
||
// return;
|
||
|
||
if (ChatMYCALL[0] == 0)
|
||
return;
|
||
|
||
for (link = link_hd; link; link = link->next)
|
||
{
|
||
if (link->flags & p_linked)
|
||
{
|
||
// Verify connection
|
||
|
||
for (circuit = circuit_hd; circuit; circuit = circuit->next)
|
||
{
|
||
if (strcmp(circuit->Callsign, link->alias) == 0)
|
||
{
|
||
if (circuit->Active == 0)
|
||
{
|
||
// BPQ Session is dead - Simulate a Disconnect
|
||
|
||
circuit->Active = TRUE; // So disconnect will work
|
||
Disconnected(circuit->BPQStream);
|
||
NeedStatus = TRUE; // Reenter
|
||
return; // Link Chain has changed
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (circuit == 0)
|
||
{
|
||
// No BPQ Session - is the only answer to restart the node?
|
||
|
||
// Logprintf(LOG_DEBUGx, NULL, '!', "Stuck Chat Sesion Detected");
|
||
// Logprintf(LOG_DEBUGx, NULL, '!', "Chat is a mess - forcing a restart");
|
||
// ProgramErrors = 26;
|
||
// CheckProgramErrors();
|
||
}
|
||
}
|
||
|
||
len = sprintf(Msg, "%s%s %c ", Msg, link->call, '0' + link->flags);
|
||
|
||
if (len > 240)
|
||
break;
|
||
}
|
||
Msg[len++] = '\r';
|
||
|
||
Send_MON_Datagram(Msg, len);
|
||
}
|
||
|
||
VOID ClearChatLinkStatus()
|
||
{
|
||
LINK * link;
|
||
|
||
for (link = link_hd; link; link = link->next)
|
||
{
|
||
link->flags = 0;
|
||
}
|
||
}
|
||
|
||
BOOL ProcessChatConnectScript(ChatCIRCUIT * conn, char * Buffer, int len)
|
||
{
|
||
LINK * link = conn->u.link;
|
||
char ** Scripts;
|
||
|
||
ChatWriteLogLine(conn, '<', Buffer, len-1, LOG_CHAT);
|
||
|
||
Buffer[len] = 0;
|
||
_strupr(Buffer);
|
||
|
||
Scripts = link->ConnectScript;
|
||
|
||
if (strstr(Buffer, "BUSY") || strstr(Buffer, "FAILURE") ||
|
||
(strstr(Buffer, "DOWNLINK") && strstr(Buffer, "ATTEMPTING") == 0) ||
|
||
strstr(Buffer, "SORRY") || strstr(Buffer, "INVALID") || strstr(Buffer, "RETRIED") ||
|
||
strstr(Buffer, "NO CONNECTION TO") || strstr(Buffer, "ERROR - ") ||
|
||
strstr(Buffer, "UNABLE TO CONNECT") || strstr(Buffer, "DISCONNECTED") ||
|
||
strstr(Buffer, "FAILED TO CONNECT") || strstr(Buffer, "REJECTED"))
|
||
{
|
||
// Connect Failed
|
||
|
||
link->flags = p_linkfailed;
|
||
RunningConnectScript = 0;
|
||
link->scriptRunning = 0; // so it doesn't get reentered
|
||
Disconnect(conn->BPQStream);
|
||
return FALSE;
|
||
}
|
||
|
||
// The pointer is only updated when we get the connect, so we can tell when the last line is acked
|
||
// The first entry is always from Connected event, so don't have to worry about testing entry -1 below
|
||
|
||
|
||
if (link->RTLSent)
|
||
{
|
||
RunningConnectScript = 0;
|
||
link->scriptRunning = 0;
|
||
link->RTLSent = 0;
|
||
|
||
if (memcmp(Buffer, "OK", 2) == 0)
|
||
{
|
||
// Reply to *RTL
|
||
|
||
// Make sure node isn't known. There is a window here that could cause a loop
|
||
|
||
if (node_find(conn->u.link->call))
|
||
{
|
||
Logprintf(LOG_CHAT, conn, '|', "Dropping link with %s to prevent a loop", conn->Callsign);
|
||
Disconnect(conn->BPQStream);
|
||
return FALSE;
|
||
}
|
||
|
||
conn->u.link->flags = p_linked;
|
||
conn->rtcflags = p_linked;
|
||
state_tell(conn, conn->FBBReplyChars);
|
||
NeedStatus = TRUE;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
// Some other response to *RTL - disconnect
|
||
|
||
Logprintf(LOG_CHAT, conn, '|', "Unexpected Response %s to *RTL - Dropping link", Buffer);
|
||
Disconnect(conn->BPQStream);
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
if (strstr(Buffer, " CONNECTED") || strstr(Buffer, "PACLEN") || strstr(Buffer, "IDLETIME") ||
|
||
strstr(Buffer, "OK") || strstr(Buffer, "###LINK MADE") || strstr(Buffer, "VIRTUAL CIRCUIT ESTABLISHED"))
|
||
{
|
||
char * Cmd;
|
||
|
||
LoopBack:
|
||
|
||
Cmd = Scripts[++link->ScriptIndex];
|
||
|
||
// Only Check until script is finished
|
||
|
||
if (Cmd == 0 || link->ScriptIndex >= link->Lines)
|
||
{
|
||
link->MoreLines = FALSE;
|
||
return TRUE;
|
||
}
|
||
|
||
if (Cmd && (strcmp(Cmd, " ") == 0 || Cmd[0] == ';' || Cmd[0] == '#'))
|
||
goto LoopBack; // Blank line
|
||
|
||
// Replace \ with # so can send commands starting with #
|
||
|
||
if (Cmd[0] == '\\')
|
||
{
|
||
Cmd[0] = '#';
|
||
nprintf(conn, "%s\r", Cmd);
|
||
Cmd[0] = '\\'; // Put \ back in script
|
||
}
|
||
else
|
||
nprintf(conn, "%s\r", Cmd);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
if (memcmp(Buffer, "[BPQCHATSERVER-", 15) == 0)
|
||
{
|
||
char * ptr = strchr(Buffer, ']');
|
||
if (ptr)
|
||
{
|
||
*ptr = 0;
|
||
strcpy(conn->FBBReplyChars, &Buffer[15]);
|
||
}
|
||
else
|
||
conn->FBBReplyChars[0] = 0;
|
||
|
||
// Connected - Send *RTL
|
||
|
||
nputs(conn, "*RTL\r"); // Log in to the remote RT system.
|
||
|
||
conn->u.link->timePollSent = time(NULL); // Keepalive is a poll
|
||
nprintf(conn, "%c%c%s %s %s\r", FORMAT, id_keepalive, OurNode, conn->u.link->call, Verstring);
|
||
link->RTLSent = 1;
|
||
conn->u.link->lastMsgSent = time(NULL);
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
// Anthing else could be ctext. etc. Ignore
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
#ifdef LINBPQ
|
||
|
||
// LINCHAT specific code
|
||
|
||
extern struct SEM OutputSEM;
|
||
|
||
static config_t cfg;
|
||
static config_setting_t * group;
|
||
|
||
extern char pgm[256];
|
||
|
||
char ChatSYSOPCall[50] = "";
|
||
|
||
VOID ChatSendWelcomeMsg(int Stream, ChatCIRCUIT * conn, struct UserInfo * user);
|
||
|
||
|
||
int ChatConnected(int Stream)
|
||
{
|
||
int n;
|
||
ChatCIRCUIT * conn;
|
||
struct UserInfo * user = NULL;
|
||
char callsign[10];
|
||
int port, paclen, maxframe, l4window;
|
||
char ConnectedMsg[] = "*** CONNECTED ";
|
||
char Msg[100];
|
||
LINK *link;
|
||
KNOWNNODE *node;
|
||
|
||
for (n = 0; n < NumberofChatStreams; n++)
|
||
{
|
||
conn = &ChatConnections[n];
|
||
|
||
if (Stream == conn->BPQStream)
|
||
{
|
||
if (conn->Active)
|
||
{
|
||
// Probably an outgoing connect
|
||
|
||
if (conn->rtcflags == p_linkini)
|
||
{
|
||
conn->paclen = chatPaclen;
|
||
|
||
// Run first line of connect script
|
||
|
||
ProcessChatConnectScript(conn, ConnectedMsg, 15);
|
||
// nprintf(conn, "c %s\r", conn->u.link->call);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
memset(conn, 0, sizeof(ChatCIRCUIT)); // Clear everything
|
||
conn->Active = TRUE;
|
||
conn->BPQStream = Stream;
|
||
|
||
conn->Secure_Session = GetConnectionInfo(Stream, callsign,
|
||
&port, &conn->SessType, &paclen, &maxframe, &l4window);
|
||
|
||
if (paclen == 0)
|
||
paclen = 256;
|
||
|
||
if (paclen > chatPaclen)
|
||
paclen = chatPaclen;
|
||
|
||
conn->paclen = paclen;
|
||
|
||
strlop(callsign, ' '); // Remove trailing spaces
|
||
|
||
memcpy(conn->Callsign, callsign, 10);
|
||
|
||
strlop(callsign, '-'); // Remove any SSID
|
||
|
||
user = zalloc(sizeof(struct UserInfo));
|
||
|
||
strcpy(user->Call, callsign);
|
||
|
||
conn->UserPointer = user;
|
||
|
||
n=sprintf_s(Msg, sizeof(Msg), "Incoming Connect from %s", user->Call);
|
||
|
||
// Send SID and Prompt
|
||
|
||
ChatWriteLogLine(conn, '|',Msg, n, LOG_CHAT);
|
||
conn->Flags |= CHATMODE;
|
||
|
||
nprintf(conn, ChatSID, Ver[0], Ver[1], Ver[2], Ver[3]);
|
||
|
||
// See if from a defined node
|
||
|
||
for (link = link_hd; link; link = link->next)
|
||
{
|
||
if (matchi(conn->Callsign, link->call))
|
||
{
|
||
conn->rtcflags = p_linkwait;
|
||
return 0; // Wait for *RTL
|
||
}
|
||
}
|
||
|
||
// See if from a previously known node
|
||
|
||
node = knownnode_find(conn->Callsign);
|
||
|
||
if (node)
|
||
{
|
||
// A node is trying to link, but we don't have it defined - close
|
||
|
||
Logprintf(LOG_CHAT, conn, '!', "Node %s connected, but is not defined as a Node - closing",
|
||
conn->Callsign);
|
||
|
||
nprintf(conn, "Node %s does not have %s defined as a node to link to - closing.\r",
|
||
OurNode, conn->Callsign);
|
||
|
||
ChatFlush(conn);
|
||
|
||
Sleep(500);
|
||
conn->rtcflags = p_nil;
|
||
|
||
Disconnect(conn->BPQStream);
|
||
|
||
return 0;
|
||
}
|
||
|
||
if (user->Name[0] == 0)
|
||
{
|
||
char * Name = lookupuser(user->Call);
|
||
|
||
if (Name)
|
||
{
|
||
if (strlen(Name) > 17)
|
||
Name[17] = 0;
|
||
|
||
strcpy(user->Name, Name);
|
||
free(Name);
|
||
}
|
||
else
|
||
{
|
||
conn->Flags |= GETTINGUSER;
|
||
nputs(conn, NewUserPrompt);
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
ChatSendWelcomeMsg(Stream, conn, user);
|
||
RefreshMainWindow();
|
||
ChatFlush(conn);
|
||
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int ChatDisconnected (ChatCIRCUIT * conn)
|
||
{
|
||
struct UserInfo * user = NULL;
|
||
int Stream = conn->BPQStream;
|
||
char Msg[255];
|
||
int len;
|
||
|
||
if (conn->Active == FALSE)
|
||
return 0;
|
||
|
||
ChatClearQueue(conn);
|
||
|
||
conn->Active = FALSE;
|
||
|
||
if (conn->Flags & CHATMODE)
|
||
{
|
||
if (conn->Flags & CHATLINK && conn->u.link)
|
||
{
|
||
// if running connect script, clear script active
|
||
|
||
if (conn->u.link->flags & p_linkini)
|
||
{
|
||
RunningConnectScript = 0;
|
||
conn->u.link->scriptRunning = 0;
|
||
}
|
||
|
||
len=sprintf_s(Msg, sizeof(Msg), "Chat Node %s Disconnected", conn->u.link->call);
|
||
ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT);
|
||
link_drop(conn);
|
||
|
||
}
|
||
else
|
||
{
|
||
len=sprintf_s(Msg, sizeof(Msg), "Chat User %s Disconnected", conn->Callsign);
|
||
ChatWriteLogLine(conn, '|',Msg, len, LOG_CHAT);
|
||
|
||
logout(conn);
|
||
|
||
}
|
||
|
||
conn->Flags = 0;
|
||
conn->u.link = NULL;
|
||
conn->UserPointer = NULL;
|
||
return 0;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int ChatDoReceivedData(ChatCIRCUIT * conn)
|
||
{
|
||
int count, InputLen;
|
||
UINT MsgLen;
|
||
int Stream = conn->BPQStream;
|
||
struct UserInfo * user;
|
||
char * ptr, * ptr2;
|
||
char Buffer[10000];
|
||
|
||
|
||
// May have several messages per packet, or message split over packets
|
||
|
||
if (conn->InputLen + 1000 > 10000) // Shouldnt have lines longer than this in text mode
|
||
conn->InputLen = 0; // discard
|
||
|
||
GetMsg(Stream, &conn->InputBuffer[conn->InputLen], &InputLen, &count);
|
||
|
||
if (InputLen == 0) return 0;
|
||
|
||
conn->Watchdog = 900; // 15 Minutes
|
||
conn->InputLen += InputLen;
|
||
|
||
loop:
|
||
|
||
if (conn->InputLen == 1 && conn->InputBuffer[0] == 0) // Single Null
|
||
{
|
||
conn->InputLen = 0;
|
||
|
||
if (conn->u.user->circuit && conn->u.user->circuit->rtcflags & p_user) // Local User
|
||
conn->u.user->lastmsgtime = time(NULL);
|
||
|
||
return 0;
|
||
}
|
||
|
||
ptr = memchr(conn->InputBuffer, '\r', conn->InputLen);
|
||
|
||
if (ptr) // CR in buffer
|
||
{
|
||
user = conn->UserPointer;
|
||
|
||
ptr2 = &conn->InputBuffer[conn->InputLen];
|
||
|
||
if (++ptr == ptr2)
|
||
{
|
||
// Usual Case - single meg in buffer
|
||
|
||
if (conn->rtcflags == p_linkini) // Chat Connect
|
||
ProcessChatConnectScript(conn, conn->InputBuffer, conn->InputLen);
|
||
else
|
||
ProcessChatLine(conn, user, conn->InputBuffer, conn->InputLen);
|
||
conn->InputLen=0;
|
||
}
|
||
else
|
||
{
|
||
// buffer contains more that 1 message
|
||
|
||
MsgLen = conn->InputLen - (int)(ptr2-ptr);
|
||
|
||
memcpy(Buffer, conn->InputBuffer, MsgLen);
|
||
|
||
if (conn->rtcflags == p_linkini)
|
||
ProcessChatConnectScript(conn, Buffer, MsgLen);
|
||
else
|
||
ProcessChatLine(conn, user, Buffer, MsgLen);
|
||
|
||
if (*ptr == 0 || *ptr == '\n')
|
||
{
|
||
/// CR LF or CR Null
|
||
|
||
ptr++;
|
||
conn->InputLen--;
|
||
}
|
||
|
||
memmove(conn->InputBuffer, ptr, conn->InputLen-MsgLen);
|
||
conn->InputLen -= MsgLen;
|
||
|
||
goto loop;
|
||
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
|
||
int ChatPollStreams()
|
||
{
|
||
int state,change;
|
||
ChatCIRCUIT * conn;
|
||
int n;
|
||
struct UserInfo * user = NULL;
|
||
char ConnectedMsg[] = "*** CONNECTED ";
|
||
|
||
for (n = 0; n < NumberofChatStreams; n++)
|
||
{
|
||
conn = &ChatConnections[n];
|
||
|
||
SessionState(conn->BPQStream, &state, &change);
|
||
|
||
if (change == 1)
|
||
{
|
||
if (state == 1) // Connected
|
||
{
|
||
GetSemaphore(&ConSemaphore, 0);
|
||
ChatConnected(conn->BPQStream);
|
||
FreeSemaphore(&ConSemaphore);
|
||
}
|
||
else
|
||
{
|
||
GetSemaphore(&ConSemaphore, 0);
|
||
ChatDisconnected(conn);
|
||
FreeSemaphore(&ConSemaphore);
|
||
}
|
||
}
|
||
|
||
ChatDoReceivedData(conn);
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
|
||
BOOL GetChatConfig(char * ConfigName)
|
||
{
|
||
config_init(&cfg);
|
||
|
||
/* Read the file. If there is an error, report it and exit. */
|
||
|
||
if(! config_read_file(&cfg, ConfigName))
|
||
{
|
||
fprintf(stderr, "%d - %s\n",
|
||
config_error_line(&cfg), config_error_text(&cfg));
|
||
config_destroy(&cfg);
|
||
return(EXIT_FAILURE);
|
||
}
|
||
|
||
group = config_lookup (&cfg, "Chat");
|
||
|
||
if (group == NULL)
|
||
return EXIT_FAILURE;
|
||
|
||
ChatApplNum = GetIntValue(group, "ApplNum");
|
||
MaxChatStreams = GetIntValue(group, "MaxStreams");
|
||
reportChatEvents = GetIntValue(group, "reportChatEvents");
|
||
chatPaclen = GetIntValue(group, "chatPaclen");
|
||
GetStringValue(group, "OtherChatNodes", OtherNodesList);
|
||
GetStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg);
|
||
GetStringValue(group, "MapPosition", Position);
|
||
GetStringValue(group, "MapPopup", PopupText);
|
||
PopupMode = GetIntValue(group, "PopupMode");
|
||
|
||
if (chatPaclen == 0)
|
||
chatPaclen = 236;
|
||
|
||
if (chatPaclen < 60)
|
||
chatPaclen = 60;
|
||
|
||
|
||
return EXIT_SUCCESS;
|
||
}
|
||
|
||
VOID SaveChatConfigFile(char * ConfigName)
|
||
{
|
||
config_setting_t *root, *group;
|
||
|
||
// Get rid of old config before saving
|
||
|
||
config_init(&cfg);
|
||
|
||
root = config_root_setting(&cfg);
|
||
|
||
group = config_setting_add(root, "Chat", CONFIG_TYPE_GROUP);
|
||
|
||
SaveIntValue(group, "ApplNum", ChatApplNum);
|
||
SaveIntValue(group, "MaxStreams", MaxChatStreams);
|
||
SaveIntValue(group, "reportChatEvents", reportChatEvents);
|
||
SaveIntValue(group, "chatPaclen", chatPaclen);
|
||
SaveStringValue(group, "OtherChatNodes", OtherNodesList);
|
||
SaveStringValue(group, "ChatWelcomeMsg", ChatWelcomeMsg);
|
||
|
||
SaveStringValue(group, "MapPosition", Position);
|
||
SaveStringValue(group, "MapPopup", PopupText);
|
||
SaveIntValue(group, "PopupMode", PopupMode);
|
||
|
||
if(! config_write_file(&cfg, ConfigName))
|
||
{
|
||
fprintf(stderr, "Error while writing file.\n");
|
||
config_destroy(&cfg);
|
||
return;
|
||
}
|
||
config_destroy(&cfg);
|
||
}
|
||
|
||
BOOL ChatInit()
|
||
{
|
||
char * ptr1 = GetApplCall(ChatApplNum);
|
||
char * ptr2;
|
||
char * Context;
|
||
int i;
|
||
ChatCIRCUIT * conn;
|
||
|
||
|
||
if (*ptr1 < 0x21)
|
||
{
|
||
printf("No APPLCALL for Chat APPL\n");
|
||
return FALSE;
|
||
}
|
||
|
||
memcpy(OurNode, ptr1, 10);
|
||
strlop(OurNode, ' ');
|
||
|
||
ptr1 = GetApplAlias(ChatApplNum);
|
||
memcpy(OurAlias, ptr1,10);
|
||
strlop(OurAlias, ' ');
|
||
|
||
if (ChatSYSOPCall[0] == 0)
|
||
{
|
||
strcpy(ChatSYSOPCall, OurNode);
|
||
strlop(ChatSYSOPCall, '-');
|
||
}
|
||
|
||
sprintf(ChatSignoffMsg, "73 de %s\r", ChatSYSOPCall);
|
||
|
||
if (ChatWelcomeMsg[0] == 0)
|
||
sprintf(ChatWelcomeMsg, "%s's Chat Server.$WType /h for command summary.$WBringing up links to other nodes.$W"
|
||
"This may take a minute or two.$WThe /p command shows what nodes are linked.$W", ChatSYSOPCall);
|
||
|
||
ChatApplMask = 1<<(ChatApplNum-1);
|
||
|
||
// Set up other nodes list. rtlink messes with the string so pass copy
|
||
|
||
// On first run config will have spaces not newlines
|
||
|
||
if (strchr(OtherNodesList, '\r')) // Has connect script entries
|
||
{
|
||
ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), "\r\n", &Context);
|
||
|
||
while (ptr1 && ptr1[0])
|
||
{
|
||
rtlink(ptr1);
|
||
ptr1 = strtok_s(NULL, "\r\n", &Context);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ptr2 = ptr1 = strtok_s(_strdup(OtherNodesList), " ,\r", &Context);
|
||
|
||
while (ptr1)
|
||
{
|
||
rtlink(ptr1);
|
||
ptr1 = strtok_s(NULL, " ,\r", &Context);
|
||
}
|
||
}
|
||
|
||
free(ptr2);
|
||
|
||
SetupChat();
|
||
|
||
// Allocate Streams
|
||
|
||
strcpy(pgm, "CHAT");
|
||
|
||
for (i = 0; i < MaxChatStreams; i++)
|
||
{
|
||
conn = &ChatConnections[i];
|
||
conn->BPQStream = FindFreeStream();
|
||
|
||
if (conn->BPQStream == 255) break;
|
||
|
||
NumberofChatStreams++;
|
||
|
||
SetAppl(conn->BPQStream, 3, ChatApplMask);
|
||
Disconnect(conn->BPQStream);
|
||
}
|
||
|
||
strcpy(pgm, "LINBPQ");
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
#endif
|
||
|
||
void ChatFlush(ChatCIRCUIT * conn)
|
||
{
|
||
int tosend, len, sent;
|
||
|
||
// Try to send data to user. May be stopped by user paging or node flow control
|
||
|
||
// UCHAR * OutputQueue; // Messages to user
|
||
// int OutputQueueLength; // Total Malloc'ed size. Also Put Pointer for next Message
|
||
// int OutputGetPointer; // Next byte to send. When Getpointer = Quele Length all is sent - free the buffer and start again.
|
||
|
||
// BOOL Paging; // Set if user wants paging
|
||
// int LinesSent; // Count when paging
|
||
// int PageLen; // Lines per page
|
||
|
||
|
||
tosend = conn->OutputQueueLength - conn->OutputGetPointer;
|
||
|
||
sent = 0;
|
||
|
||
while (tosend > 0)
|
||
{
|
||
if (TXCount(conn->BPQStream) > 4)
|
||
return; // Busy
|
||
|
||
if (tosend <= conn->paclen)
|
||
len = tosend;
|
||
else
|
||
len=conn->paclen;
|
||
|
||
GetSemaphore(&OutputSEM, 0);
|
||
|
||
SendUnbuffered(conn->BPQStream, &conn->OutputQueue[conn->OutputGetPointer], len);
|
||
|
||
conn->OutputGetPointer += len;
|
||
|
||
FreeSemaphore(&OutputSEM);
|
||
|
||
tosend -= len;
|
||
sent++;
|
||
|
||
if (sent > 4)
|
||
return;
|
||
}
|
||
|
||
// All Sent. Free buffers and reset pointers
|
||
|
||
ChatClearQueue(conn);
|
||
}
|
||
|
||
VOID ChatClearQueue(ChatCIRCUIT * conn)
|
||
{
|
||
GetSemaphore(&OutputSEM, 0);
|
||
|
||
conn->OutputGetPointer = 0;
|
||
conn->OutputQueueLength = 0;
|
||
|
||
FreeSemaphore(&OutputSEM);
|
||
}
|
||
|
||
#ifdef LINBPQ
|
||
void ChatTrytoSend()
|
||
{
|
||
// call Flush on any connected streams with queued data
|
||
|
||
ChatCIRCUIT * conn;
|
||
|
||
int n;
|
||
|
||
for (n = 0; n < NumberofChatStreams; n++)
|
||
{
|
||
conn = &ChatConnections[n];
|
||
|
||
if (conn->Active == TRUE)
|
||
ChatFlush(conn);
|
||
}
|
||
}
|
||
|
||
VOID CloseChat()
|
||
{
|
||
int BPQStream, n;
|
||
|
||
for (n = 0; n < NumberofChatStreams; n++)
|
||
{
|
||
BPQStream = ChatConnections[n].BPQStream;
|
||
|
||
if (BPQStream)
|
||
{
|
||
SetAppl(BPQStream, 0, 0);
|
||
Disconnect(BPQStream);
|
||
DeallocateStream(BPQStream);
|
||
}
|
||
}
|
||
|
||
ClearChatLinkStatus();
|
||
SendChatLinkStatus();
|
||
Sleep(1000); // A bit of time for links to close
|
||
SendChatLinkStatus(); // Send again to reduce chance of being missed
|
||
FreeChatMemory();
|
||
}
|
||
|
||
VOID SendChatReport(SOCKET ChatReportSocket, char * buff, int txlen)
|
||
{
|
||
unsigned short int crc = compute_crc(buff, txlen);
|
||
|
||
crc ^= 0xffff;
|
||
|
||
buff[txlen++] = (crc&0xff);
|
||
buff[txlen++] = (crc>>8);
|
||
|
||
sendto(ChatReportSocket, buff, txlen, 0, (LPSOCKADDR)&Chatreportdest, sizeof(Chatreportdest));
|
||
|
||
}
|
||
|
||
#endif
|
||
|
||
|
||
#ifndef WIN32
|
||
#define INVALID_HANDLE_VALUE (void *)-1
|
||
#endif
|
||
|
||
static FILE * LogHandle[4] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
|
||
|
||
static time_t LastLogTime[4] = {0, 0, 0, 0};
|
||
|
||
static char FilesNames[4][100] = {"", "", "", ""};
|
||
|
||
static char * Logs[4] = {"BBS", "CHAT", "TCP", "DEBUG"};
|
||
|
||
|
||
BOOL ChatOpenLogfile(int Flags)
|
||
{
|
||
UCHAR FN[MAX_PATH];
|
||
time_t LT;
|
||
struct tm * tm;
|
||
|
||
LT = time(NULL);
|
||
tm = gmtime(<);
|
||
|
||
sprintf(FN,"%s/logs/log_%02d%02d%02d_%s.txt", GetLogDirectory(), tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, Logs[Flags]);
|
||
|
||
LogHandle[Flags] = fopen(FN, "ab");
|
||
|
||
#ifndef WIN32
|
||
|
||
if (strcmp(FN, &FilesNames[Flags][0]))
|
||
{
|
||
UCHAR SYMLINK[MAX_PATH];
|
||
|
||
sprintf(SYMLINK,"%s/logLatest_%s.txt", GetBPQDirectory(), Logs[Flags]);
|
||
unlink(SYMLINK);
|
||
strcpy(&FilesNames[Flags][0], FN);
|
||
symlink(FN, SYMLINK);
|
||
}
|
||
|
||
#endif
|
||
|
||
return (LogHandle[Flags] != NULL);
|
||
}
|
||
|
||
void ChatWriteLogLine(ChatCIRCUIT * conn, int Flag, char * Msg, int MsgLen, int Flags)
|
||
{
|
||
char CRLF[2] = {0x0d,0x0a};
|
||
struct tm * tm;
|
||
char Stamp[20];
|
||
time_t T;
|
||
|
||
#ifndef LINBPQ
|
||
|
||
if (hMonitor)
|
||
{
|
||
if (Flags == LOG_CHAT)
|
||
{
|
||
WritetoMonitorWindow((char *)&Flag, 1);
|
||
|
||
if (conn && conn->Callsign[0])
|
||
{
|
||
char call[20];
|
||
sprintf(call, "%s ", conn->Callsign);
|
||
WritetoMonitorWindow(call, 10);
|
||
}
|
||
else
|
||
WritetoMonitorWindow(" ", 10);
|
||
|
||
WritetoMonitorWindow(Msg, MsgLen);
|
||
if (Msg[MsgLen-1] != '\r')
|
||
WritetoMonitorWindow(CRLF , 1);
|
||
}
|
||
else if (Flags == LOG_DEBUGx)
|
||
{
|
||
WritetoMonitorWindow((char *)&Flag, 1);
|
||
WritetoMonitorWindow(Msg, MsgLen);
|
||
WritetoMonitorWindow(CRLF , 1);
|
||
}
|
||
|
||
}
|
||
|
||
#endif
|
||
|
||
if (Flags == LOG_CHAT && !LogCHAT)
|
||
return;
|
||
|
||
if (LogHandle[Flags] == INVALID_HANDLE_VALUE) ChatOpenLogfile(Flags);
|
||
|
||
if (LogHandle[Flags] == INVALID_HANDLE_VALUE) return;
|
||
|
||
T = time(NULL);
|
||
tm = gmtime(&T);
|
||
|
||
sprintf(Stamp,"%02d%02d%02d %02d:%02d:%02d %c",
|
||
tm->tm_year-100, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, Flag);
|
||
|
||
fwrite(Stamp, 1, strlen(Stamp), LogHandle[Flags]);
|
||
|
||
if (conn && conn->Callsign[0])
|
||
{
|
||
char call[20];
|
||
sprintf(call, "%s ", conn->Callsign);
|
||
fwrite(call, 1, 10, LogHandle[Flags]);
|
||
}
|
||
else
|
||
fwrite(" ", 1, 10, LogHandle[Flags]);
|
||
|
||
fwrite(Msg, 1, MsgLen, LogHandle[Flags]);
|
||
|
||
if (Flags == LOG_CHAT && Msg[MsgLen-1] == '\r')
|
||
fwrite(&CRLF[1], 1, 1, LogHandle[Flags]);
|
||
else
|
||
fwrite(CRLF, 1, 2, LogHandle[Flags]);
|
||
|
||
fclose(LogHandle[Flags]);
|
||
LogHandle[Flags] = INVALID_HANDLE_VALUE;
|
||
}
|
||
|
||
|