Newer
Older
/* Synchronet real-time chat functions */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program 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 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include "sbbs.h"
#define PCHAT_LEN 1000 /* Size of Private chat file */
const char *weekday[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"
, "Saturday"};
const char *month[] = {"January", "February", "March", "April", "May", "June"
, "July", "August", "September", "October", "November", "December"};
/****************************************************************************/
/****************************************************************************/
char line[256], str[256], ch, done
, usrs, preusrs, qusrs, *gurubuf = NULL, savch, *p
, pgraph[400], buf[400]
, usr[MAX_NODES], preusr[MAX_NODES], qusr[MAX_NODES];
char guru_lastanswer[512];
char tmp[512];
int file;
long i, j, k, n;
node_t node;
if (useron.rest & FLAG('C')) {
if (channel < 1 || channel > cfg.total_chans)
channel = 1;
if (!chan_access(channel - 1))
if (useron.misc & (RIP) || !(useron.misc & EXPERT))
menu("multchat");
bputs(text[WelcomeToMultiChat]);
if (getnodedat(cfg.node_num, &thisnode, true)) {
thisnode.aux = channel;
putnodedat(cfg.node_num, &thisnode);
}
bprintf(text[WelcomeToChannelN], channel, cfg.chan[channel - 1]->name);
if (cfg.chan[channel - 1]->misc & CHAN_GURU && cfg.chan[channel - 1]->guru < cfg.total_gurus
&& chk_ar(cfg.guru[cfg.chan[channel - 1]->guru]->ar, &useron, &client)) {
snprintf(str, sizeof str, "%s%s.dat", cfg.ctrl_dir, cfg.guru[cfg.chan[channel - 1]->guru]->code);
if ((file = nopen(str, O_RDONLY)) == -1) {
errormsg(WHERE, ERR_OPEN, str, O_RDONLY);
if ((gurubuf = (char *)malloc((size_t)filelength(file) + 1)) == NULL) {
errormsg(WHERE, ERR_ALLOC, str, (size_t)filelength(file) + 1);
if (read(file, gurubuf, (size_t)filelength(file)) < 1)
*gurubuf = '\0';
else
gurubuf[filelength(file)] = 0;
usrs = 0;
for (i = 1; i <= cfg.sys_nodes && i <= cfg.sys_lastnode; i++) {
if (i == cfg.node_num)
getnodedat(i, &node);
if (node.action != NODE_MCHT || node.status != NODE_INUSE)
if (node.aux && (node.aux & 0xff) != channel)
printnodedat(i, &node);
preusr[usrs] = (char)i;
usr[usrs++] = (char)i;
preusrs = usrs;
if (gurubuf)
, cfg.sys_nodes + 1, cfg.guru[cfg.chan[channel - 1]->guru]->name, channel);
done = 0;
while (online && !done) {
action = NODE_MCHT;
qusrs = usrs = 0;
for (i = 1; i <= cfg.sys_nodes; i++) {
if (i == cfg.node_num)
getnodedat(i, &node);
if (node.action != NODE_MCHT
|| (node.aux && channel && (node.aux & 0xff) != channel))
if (node.status == NODE_QUIET)
qusr[qusrs++] = (char)i;
else if (node.status == NODE_INUSE)
usr[usrs++] = (char)i;
if (preusrs > usrs) {
if (!usrs && channel && cfg.chan[channel - 1]->misc & CHAN_GURU
&& cfg.chan[channel - 1]->guru < cfg.total_gurus)
, cfg.sys_nodes + 1, cfg.guru[cfg.chan[channel - 1]->guru]->name
, channel);
for (i = 0; i < preusrs; i++) {
for (j = 0; j < usrs; j++)
if (preusr[i] == usr[j])
getnodedat(preusr[i], &node);
if (node.misc & NODE_ANON)
snprintf(str, sizeof str, "%.80s", text[UNKNOWN_USER]);
username(&cfg, node.useron, str);
, preusr[i], str, channel);
else if (preusrs < usrs) {
if (!preusrs && channel && cfg.chan[channel - 1]->misc & CHAN_GURU
&& cfg.chan[channel - 1]->guru < cfg.total_gurus)
, cfg.sys_nodes + 1, cfg.guru[cfg.chan[channel - 1]->guru]->name
, channel);
for (i = 0; i < usrs; i++) {
for (j = 0; j < preusrs; j++)
if (usr[i] == preusr[j])
getnodedat(usr[i], &node);
if (node.misc & NODE_ANON)
snprintf(str, sizeof str, "%.80s", text[UNKNOWN_USER]);
username(&cfg, node.useron, str);
, usr[i], str, channel);
preusrs = usrs;
for (i = 0; i < usrs; i++)
preusr[i] = usr[i];
sys_status &= ~SS_ABORT;
if ((ch = inkey(K_NONE, 250)) != 0 || wordwrap[0]) {
if (ch == '/') {
strcpy(str, "ACELWQ?*");
if (SYSOP)
SAFECAT(str, "0");
i = getkeys(str, cfg.total_chans);
if (i & 0x80000000L) { /* change channel */
savch = (char)(i & ~0x80000000L);
if (savch == channel)
if (!chan_access(savch - 1))
continue;
bprintf(text[WelcomeToChannelN]
, savch, cfg.chan[savch - 1]->name);
usrs = 0;
for (i = 1; i <= cfg.sys_nodes; i++) {
if (i == cfg.node_num)
getnodedat(i, &node);
if (node.action != NODE_MCHT
|| node.status != NODE_INUSE)
if (node.aux && (node.aux & 0xff) != savch)
printnodedat(i, &node);
if (node.aux & 0x1f00) { /* password */
, node.misc & NODE_ANON
? text[UNKNOWN_USER]
: username(&cfg, node.useron, tmp));
if (!getstr(str, 8, K_UPPER | K_ALPHA | K_LINE))
if (strcmp(str, unpackchatpass(tmp, &node)))
bputs(text[CorrectPassword]);
}
preusr[usrs] = (char)i;
usr[usrs++] = (char)i;
if (i <= cfg.sys_nodes) { /* failed password */
if (cfg.chan[savch - 1]->misc & CHAN_GURU
&& cfg.chan[savch - 1]->guru < cfg.total_gurus
&& chk_ar(cfg.guru[cfg.chan[savch - 1]->guru]->ar, &useron, &client
)) {
snprintf(str, sizeof str, "%s%s.dat", cfg.ctrl_dir
, cfg.guru[cfg.chan[savch - 1]->guru]->code);
if ((file = nopen(str, O_RDONLY)) == -1) {
errormsg(WHERE, ERR_OPEN, str, O_RDONLY);
if ((gurubuf = (char *)malloc((size_t)filelength(file) + 1)) == NULL) {
errormsg(WHERE, ERR_ALLOC, str
, (size_t)filelength(file) + 1);
if (read(file, gurubuf, (size_t)filelength(file)) < 1)
*gurubuf = '\0';
else
gurubuf[filelength(file)] = 0;
preusrs = usrs;
if (gurubuf)
, cfg.sys_nodes + 1
, cfg.guru[cfg.chan[savch - 1]->guru]->name
, savch);
channel = savch;
if (!usrs && cfg.chan[savch - 1]->misc & CHAN_PW
&& !noyes(text[PasswordProtectChanQ])) {
if (getstr(str, 8, K_UPPER | K_ALPHA | K_LINE)) {
getnodedat(cfg.node_num, &thisnode, true);
thisnode.aux = channel;
packchatpass(str, &thisnode);
getnodedat(cfg.node_num, &thisnode, true);
thisnode.aux = channel;
getnodedat(cfg.node_num, &thisnode, true);
thisnode.aux = channel;
putnodedat(cfg.node_num, &thisnode);
if (cfg.chan[channel - 1]->cost
&& !(useron.exempt & FLAG('J')))
subtract_cdt(&cfg, &useron, cfg.chan[channel - 1]->cost);
else switch (i) { /* other command */
case '0': /* Global channel */
if (!SYSOP)
break;
usrs = 0;
for (i = 1; i <= cfg.sys_nodes; i++) {
if (i == cfg.node_num)
continue;
getnodedat(i, &node);
if (node.action != NODE_MCHT
|| node.status != NODE_INUSE)
continue;
printnodedat(i, &node);
preusr[usrs] = (char)i;
usr[usrs++] = (char)i;
}
preusrs = usrs;
if (getnodedat(cfg.node_num, &thisnode, true)) {
thisnode.aux = channel = 0;
putnodedat(cfg.node_num, &thisnode);
}
case 'A': /* Action commands */
useron.chat ^= CHAT_ACTION;
bprintf("\r\nAction commands are now %s\r\n"
, useron.chat & CHAT_ACTION

Rob Swindell
committed
? text[On]:text[Off]);
putuserchat(useron.number, useron.chat);
break;
case 'C': /* List of action commands */
CRLF;
for (i = 0; channel && i < cfg.total_chatacts; i++) {
if (cfg.chatact[i]->actset
!= cfg.chan[channel - 1]->actset)
continue;
bprintf("%-*.*s", LEN_CHATACTCMD
, LEN_CHATACTCMD, cfg.chatact[i]->cmd);
if (!((i + 1) % 8)) {
CRLF;
}
else
bputs(" ");
CRLF;
break;
case 'E': /* Toggle echo */
useron.chat ^= CHAT_ECHO;
bprintf(text[EchoIsNow]
, useron.chat & CHAT_ECHO

Rob Swindell
committed
? text[On]:text[Off]);
putuserchat(useron.number, useron.chat);
case 'L': /* list nodes */
CRLF;
for (i = 1; i <= cfg.sys_nodes && i <= cfg.sys_lastnode; i++) {
getnodedat(i, &node);
printnodedat(i, &node);
}
CRLF;
break;
case 'W': /* page node(s) */
j = getnodetopage(0, 0);
if (!j)
break;
for (i = 0; i < usrs; i++)
if (usr[i] == j)
break;
if (i >= usrs) {
bputs(text[UserNotFound]);
break;
}
bputs(text[NodeMsgPrompt]);
if (!getstr(line, 66, K_LINE | K_MSG))
break;
snprintf(buf, sizeof buf, text[ChatLineFmt]
, thisnode.misc & NODE_ANON
? text[AnonUserChatHandle]
: useron.handle
, cfg.node_num, '*', line);
SAFECAT(buf, crlf);
if (useron.chat & CHAT_ECHO)
bputs(buf);
putnmsg(j, buf);
break;
case 'Q': /* quit */
done = 1;
break;
case '*':
if (!menu("chan", P_NOERROR)) {
bputs(text[ChatChanLstHdr]);
bputs(text[ChatChanLstTitles]);
if (cfg.total_chans >= 10) {
bputs(" ");
bputs(text[ChatChanLstTitles]);
}
CRLF;
bputs(text[ChatChanLstUnderline]);
if (cfg.total_chans >= 10) {
bputs(" ");
bputs(text[ChatChanLstUnderline]);
}
CRLF;
if (cfg.total_chans >= 10)
j = (cfg.total_chans / 2) + (cfg.total_chans & 1);
else
j = cfg.total_chans;
for (i = 0; i < j && !msgabort(); i++) {
bprintf(text[ChatChanLstFmt], i + 1
, cfg.chan[i]->name
, cfg.chan[i]->cost);
if (cfg.total_chans >= 10) {
k = (cfg.total_chans / 2)
+ i + (cfg.total_chans & 1);
if (k < cfg.total_chans) {
bputs(" ");
bprintf(text[ChatChanLstFmt]
, k + 1
, cfg.chan[k]->name
, cfg.chan[k]->cost);
}
break;
case '?': /* menu */
menu("multchat");
break;
}
j = 0;
pgraph[0] = 0;
while (j < 5) {
if (!getstr(line, 66, K_WRAP | K_MSG | K_CHAT))
snprintf(str, sizeof str, text[ChatLineFmt]
, thisnode.misc & NODE_ANON
? text[AnonUserChatHandle]
: useron.handle
, cfg.node_num, ':', nulstr);
snprintf(tmp, sizeof tmp, "%*s", (int)bstrlen(str), nulstr);
SAFECAT(pgraph, tmp);
SAFECAT(pgraph, line);
SAFECAT(pgraph, crlf);
if (!wordwrap[0])
if (pgraph[0]) {
if (channel && useron.chat & CHAT_ACTION) {
for (i = 0; i < cfg.total_chatacts; i++) {
if (cfg.chatact[i]->actset
!= cfg.chan[channel - 1]->actset)
snprintf(str, sizeof str, "%s ", cfg.chatact[i]->cmd);
if (!strnicmp(str, pgraph, strlen(str)))
, LEN_CHATACTCMD + 2, pgraph);
str[strlen(str) - 2] = 0;
if (!stricmp(cfg.chatact[i]->cmd, str))
if (i < cfg.total_chatacts) {
p = pgraph + strlen(str);
n = atoi(p);
for (j = 0; j < usrs; j++) {
getnodedat(usr[j], &node);
if (usrs == 1) /* no need to search */
if (n) {
if (usr[j] == n)
username(&cfg, node.useron, str);
if (!strnicmp(str, p, strlen(str)))
getuserstr(&cfg, node.useron, USER_HANDLE, str, sizeof(str));
if (!strnicmp(str, p, strlen(str)))
if (!usrs
&& cfg.chan[channel - 1]->guru < cfg.total_gurus)
, cfg.guru[cfg.chan[channel - 1]->guru]->name);
else if (j >= usrs)
strcpy(str, "everyone");
else if (node.misc & NODE_ANON)
strcpy(str, text[UNKNOWN_USER]);
username(&cfg, node.useron, str);
/* Display on same node */
bprintf(cfg.chatact[i]->out
, thisnode.misc & NODE_ANON
? text[UNKNOWN_USER] : useron.alias
, str);
if (usrs && j < usrs) {
snprintf(buf, sizeof buf, cfg.chatact[i]->out
, thisnode.misc & NODE_ANON
? text[UNKNOWN_USER] : useron.alias
, "you");
SAFECAT(buf, crlf);
putnmsg(usr[j], buf);
snprintf(buf, sizeof buf, cfg.chatact[i]->out
, thisnode.misc & NODE_ANON
? text[UNKNOWN_USER] : useron.alias
, str);
SAFECAT(buf, crlf);
for (i = 0; i < usrs; i++) {
if (i == j)
getnodedat(usr[i], &node);
for (i = 0; i < qusrs; i++) {
getnodedat(qusr[i], &node);
putnmsg(qusr[i], buf);
snprintf(buf, sizeof buf, text[ChatLineFmt]
, thisnode.misc & NODE_ANON
? text[AnonUserChatHandle]
: useron.handle
, cfg.node_num, ':', pgraph);
if (useron.chat & CHAT_ECHO)
for (i = 0; i < usrs; i++) {
getnodedat(usr[i], &node);
for (i = 0; i < qusrs; i++) {
getnodedat(qusr[i], &node);
putnmsg(qusr[i], buf);
if (!usrs && channel && gurubuf
&& cfg.chan[channel - 1]->misc & CHAN_GURU)
guruchat(pgraph, gurubuf, cfg.chan[channel - 1]->guru, guru_lastanswer);
if (sys_status & SS_ABORT)
lncntr = 0;
if (gurubuf != NULL)
free(gurubuf);
/****************************************************************************/
/****************************************************************************/
char path[MAX_PATH + 1];
char* gurubuf;
int file;
long i;
if (useron.rest & FLAG('C')) {
if (!cfg.total_gurus) {
bprintf(text[SysopIsNotAvailable], "The Guru");
if (cfg.total_gurus == 1 && chk_ar(cfg.guru[0]->ar, &useron, &client))
i = 0;
for (i = 0; i < cfg.total_gurus; i++)
uselect(1, i, nulstr, cfg.guru[i]->name, cfg.guru[i]->ar);
i = uselect(0, 0, 0, 0, 0);
if (i < 0)
snprintf(path, sizeof path, "%s%s.dat", cfg.ctrl_dir, cfg.guru[i]->code);
if ((file = nopen(path, O_RDONLY)) == -1) {
errormsg(WHERE, ERR_OPEN, path, O_RDONLY);
long length = (long)filelength(file);
if (length < 0) {
errormsg(WHERE, ERR_CHK, path, length);
close(file);
return false;
}
if ((gurubuf = (char *)malloc(length + 1)) == NULL) {
errormsg(WHERE, ERR_ALLOC, path, length + 1);
if (read(file, gurubuf, length) != length)
errormsg(WHERE, ERR_READ, path, length);
localguru(gurubuf, i);
}
/****************************************************************************/
/* The chat section */
/****************************************************************************/
void sbbs_t::chatsection()
{
exec_bin(cfg.chatsec_mod, &main_csi);
/****************************************************************************/
/****************************************************************************/
char str[256];
int i;
if (useron.rest & FLAG('C')) {
if (sysop_available(&cfg)
|| (cfg.sys_chat_ar[0] && chk_ar(cfg.sys_chat_ar, &useron, &client))
|| useron.exempt & FLAG('C')) {
if (!(sys_status & SS_SYSPAGE)) {
logline("C", "paged sysop for chat");
notify(text[SysopPageNotification]);
ftouch(syspage_semfile);
char topic[128];
SAFEPRINTF(topic, "page/node/%u", cfg.node_num);
snprintf(str, sizeof(str), "%u\t%s", useron.number, useron.alias);
mqtt_pub_timestamped_msg(mqtt, TOPIC_BBS_ACTION, topic, time(NULL), str);
for (i = 0; i < cfg.total_pages; i++)
if (chk_ar(cfg.page[i]->ar, &useron, &client))
if (i < cfg.total_pages) {
bprintf(text[PagingGuru], cfg.sys_op);
long mode = 0;
if (cfg.page[i]->misc & XTRN_STDIO)
mode |= EX_STDIO;
if (cfg.page[i]->misc & XTRN_NATIVE)
mode |= EX_NATIVE;
if (cfg.page[i]->misc & XTRN_SH)
mode |= EX_SH;
external(cmdstr(cfg.page[i]->cmd, nulstr, nulstr, NULL, mode), mode);
else if (cfg.sys_misc & SM_SHRTPAGE) {
bprintf(text[PagingGuru], cfg.sys_op);
for (i = 0; i < 10; i++) {
sbbs_beep(1000, 200);
outchar('.');
CRLF;
sys_status ^= SS_SYSPAGE;
, sys_status & SS_SYSPAGE ? text[On] : text[Off]);
nosound();
if (!(sys_status & SS_SYSPAGE))
remove(syspage_semfile);
bprintf(text[SysopIsNotAvailable], cfg.sys_op);
}
/****************************************************************************/
/* Returns 1 if user online has access to channel "channum" */
/****************************************************************************/

Rob Swindell
committed
bool sbbs_t::chan_access(int cnum)
if (!cfg.total_chans || cnum >= cfg.total_chans || !chk_ar(cfg.chan[cnum]->ar, &useron, &client)) {
bputs(text[CantAccessThatChannel]);
if (!(useron.exempt & FLAG('J')) && cfg.chan[cnum]->cost > user_available_credits(&useron)) {
bputs(text[NotEnoughCredits]);
/****************************************************************************/
/* Private split-screen (or interspersed) chat with node or local sysop */
/****************************************************************************/

Rob Swindell
committed
void sbbs_t::privchat(bool forced, int node_num)
char str[128], c, *p, localbuf[5][81], remotebuf[5][81]
, localline = 0, remoteline = 0, localchar = 0, remotechar = 0
, *sep = text[PrivateChatSeparator]
, *local_sep = text[SysopChatSeparator]
;
char tmp[512];
char outpath[MAX_PATH + 1];
char inpath[MAX_PATH + 1];
uchar ch;
int wr;
int in, out, i, n, echo = 1, x, y, activity, remote_activity;
int local_y = 1, remote_y = 1;
node_t node;
time_t last_nodechk = 0;
if (forced)

Rob Swindell
committed
n = node_num;
if (useron.rest & FLAG('C')) {
bputs(text[R_Chat]);
return;
}
n = getnodetopage(0, 0);
if (!n)
if (n == cfg.node_num) {
bputs(text[NoNeedToPageSelf]);
return;
getnodedat(n, &node);
if (node.action == NODE_PCHT && node.aux != cfg.node_num) {
bprintf(text[NodeNAlreadyInPChat], n);
return;
if (SYSOP && getnodedat(n, &node, true)) {

Rob Swindell
committed
node.misc |= NODE_FCHAT;
putnodedat(n, &node);
} else {
if ((node.action != NODE_PAGE || node.aux != cfg.node_num)
&& node.misc & NODE_POFF) {
bprintf(text[CantPageNode], node.misc & NODE_ANON
? text[UNKNOWN_USER] : username(&cfg, node.useron, tmp));

Rob Swindell
committed
return;
}
if (node.action != NODE_PAGE) {

Rob Swindell
committed
bprintf(text[PagingUser]
, node.misc & NODE_ANON ? text[UNKNOWN_USER] : username(&cfg, node.useron, tmp)
, node.misc & NODE_ANON ? 0 : node.useron);
snprintf(str, sizeof str, text[NodePChatPageMsg]
, cfg.node_num, thisnode.misc & NODE_ANON
? text[UNKNOWN_USER] : useron.alias);
putnmsg(n, str);
snprintf(str, sizeof str, "paged %s on node %d to private chat"
, username(&cfg, node.useron, tmp), n);
logline("C", str);

Rob Swindell
committed
}
if (getnodedat(cfg.node_num, &thisnode, true)) {
thisnode.action = action = NODE_PAGE;
thisnode.aux = n;
putnodedat(cfg.node_num, &thisnode);
}
if (node.action != NODE_PAGE || node.aux != cfg.node_num) {
bprintf(text[WaitingForNodeInPChat], n);
while (online && !(sys_status & SS_ABORT)) {
getnodedat(n, &node);
if ((node.action == NODE_PAGE || node.action == NODE_PCHT)
&& node.aux == cfg.node_num) {
bprintf(text[NodeJoinedPrivateChat]
, n, node.misc & NODE_ANON ? text[UNKNOWN_USER]
: username(&cfg, node.useron, tmp));
break;
checkline();
gettimeleft();
sync();
}
gettimeleft();
if (getnodedat(cfg.node_num, &thisnode, true)) {
thisnode.action = action = NODE_PCHT;
thisnode.aux = n;
thisnode.misc &= ~(NODE_LCHAT | NODE_FCHAT);
putnodedat(cfg.node_num, &thisnode);
}
if (!online || (!forced && (sys_status & SS_ABORT)))
if (forced && n == 0) {
/* If an external sysop chat event handler is installed, just run that and do nothing else */
if (user_event(EVENT_LOCAL_CHAT))
if (((sys_status & SS_USERON && useron.chat & CHAT_SPLITP) || !(sys_status & SS_USERON))
&& term_supports(ANSI) && rows >= 24 && cols >= 80)
sys_status |= SS_SPLITP;
else
sys_status &= ~SS_SPLITP;
/*
if(!(useron.misc&EXPERT))

rswindell
committed
menu("privchat");
if (!(sys_status & SS_SPLITP)) {
if (forced)
bprintf(text[SysopIsHere], cfg.sys_op);
else
bputs(text[WelcomeToPrivateChat]);
}
snprintf(outpath, sizeof outpath, "%schat.dab", cfg.node_dir);
if ((out = sopen(outpath, O_RDWR | O_CREAT | O_BINARY, SH_DENYNO, DEFFILEMODE)) == -1) {
errormsg(WHERE, ERR_OPEN, outpath, O_RDWR | O_DENYNONE | O_CREAT);
return;
}
if (forced && n == 0)
snprintf(inpath, sizeof inpath, "%slchat.dab", cfg.node_dir);
snprintf(inpath, sizeof inpath, "%schat.dab", cfg.node_path[n - 1]);
if (!fexist(inpath)) /* Wait while it's created for the first time */
if ((in = sopen(inpath, O_RDWR | O_CREAT | O_BINARY, SH_DENYNO, DEFFILEMODE)) == -1) {
errormsg(WHERE, ERR_OPEN, inpath, O_RDWR | O_DENYNONE | O_CREAT);
return;
if ((p = (char *)malloc(PCHAT_LEN)) == NULL) {
errormsg(WHERE, ERR_ALLOC, nulstr, PCHAT_LEN);
return;
memset(p, 0, PCHAT_LEN);
if (write(in, p, PCHAT_LEN) != PCHAT_LEN)
errormsg(WHERE, ERR_WRITE, inpath, PCHAT_LEN);
if (write(out, p, PCHAT_LEN) != PCHAT_LEN)
errormsg(WHERE, ERR_WRITE, outpath, PCHAT_LEN);
lseek(in, 0L, SEEK_SET);
lseek(out, 0L, SEEK_SET);
if (getnodedat(cfg.node_num, &thisnode, true)) {
thisnode.misc &= ~NODE_RPCHT; /* Clear "reset pchat flag" */
putnodedat(cfg.node_num, &thisnode);
}
if (n) { // not local
if (getnodedat(n, &node, true)) {
node.misc |= NODE_RPCHT; /* Set "reset pchat flag" */
putnodedat(n, &node); /* on other node */
}
/* Wait for other node */
/* to acknowledge and reset */
while (online && !(sys_status & SS_ABORT)) {
getnodedat(n, &node);
if (!(node.misc & NODE_RPCHT))
getnodedat(cfg.node_num, &thisnode);
if (thisnode.misc & NODE_RPCHT)
break;
checkline();
gettimeleft();
SLEEP(500);
if (sys_status & SS_SPLITP) {
lncntr = 0;
ansi_save();
ansi_gotoxy(1, 13);
remote_y = 1;

Rob Swindell
committed
bprintf(forced ? local_sep : sep
, thisnode.misc & NODE_MSGW ? 'T':' '
, sectostr(timeleft, tmp)
, thisnode.misc & NODE_NMSG ? 'M':' ');
ansi_gotoxy(1, 14);
local_y = 14;
while (online && (forced || !(sys_status & SS_ABORT))) {
lncntr = 0;
if (sys_status & SS_SPLITP)
lbuflen = 0;
action = NODE_PCHT;
activity = 0;
remote_activity = 0;
if ((ch = inkey(K_GETSTR, 100)) != 0) {
activity = 1;
if (echo)
attr(cfg.color[clr_chatlocal]);
if (ch == BS || ch == DEL) {
if (localchar) {
if (echo)
localbuf[localline][localchar] = 0;
}
else if (ch == TAB) {
if (echo)
localbuf[localline][localchar] = ' ';
while (localchar < 78 && localchar % 8) {
if (echo)
localbuf[localline][localchar++] = ' ';
}
else if (ch == CTRL_R) {
if (sys_status & SS_SPLITP) {
CLS;
attr(cfg.color[clr_chatremote]);
remotebuf[remoteline][remotechar] = 0;
for (i = 0; i <= remoteline; i++) {
bputs(remotebuf[i]);
remote_y = 1 + remoteline;
bputs("\1i_\1n"); /* Fake cursor */
ansi_save();

Rob Swindell
committed
bprintf(forced ? local_sep : sep
, thisnode.misc & NODE_MSGW ? 'T':' '
, sectostr(timeleft, tmp)
, thisnode.misc & NODE_NMSG ? 'M':' ');
ansi_gotoxy(1, 14);
attr(cfg.color[clr_chatlocal]);
localbuf[localline][localchar] = 0;
for (i = 0; i <= localline; i++) {
local_y = 15 + localline;
else if (ch >= ' ' || ch == CR) {
if (ch != CR) {
if (echo)
localbuf[localline][localchar] = ch;
if (ch == CR || (localchar > 68 && ch == ' ') || ++localchar > 78) {
lprintf(LOG_DEBUG, "chat line wrapped, localchar=%d, ch=%x", localchar, ch);
localbuf[localline][localchar] = 0;
localchar = 0;
if (sys_status & SS_SPLITP && local_y >= rows) {
ansi_gotoxy(1, 13);

Rob Swindell
committed
bprintf(forced ? local_sep : sep
, thisnode.misc & NODE_MSGW ? 'T':' '
, sectostr(timeleft, tmp)
, thisnode.misc & NODE_NMSG ? 'M':' ');
attr(cfg.color[clr_chatlocal]);
for (x = 13, y = 0; x < rows; x++, y++) {
comprintf("\x1b[%d;1H\x1b[K", x + 1);
if (y <= localline)
bprintf("%s\r\n", localbuf[y]);
ansi_gotoxy(1, local_y = (15 + localline));
localline = 0;
if (localline >= 4)
for (i = 0; i < 4; i++)
memcpy(localbuf[i], localbuf[i + 1], 81);
local_y++;
if (sys_status & SS_SPLITP)
cleartoeol();
}
}
}
} else { // illegal key
continue;
}
if (read(out, &c, 1) < 1) {
lprintf(LOG_ERR, "Error reading char from %s", outpath);
continue;
}
(void)lseek(out, -1L, SEEK_CUR);
if (!c) /* hasn't wrapped */
wr = write(out, &ch, 1);
if (!tell(out))
lseek(out, 0L, SEEK_END);
lseek(out, -1L, SEEK_CUR);
ch = 0;
wr = write(out, &ch, 1);