linbpq/HanksRT.c

4552 lines
96 KiB
C
Raw Blame History

/*
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, &param);
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(&LT);
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;
}