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];
int file;
long i,j,k,n;
node_t node;
if(useron.rest&FLAG('C')) {
bputs(text[R_Chat]);
if(channel<1 || channel>cfg.total_chans)
channel=1;
if(!chan_access(channel-1))
return;
if(useron.misc&(RIP) ||!(useron.misc&EXPERT))
menu("multchat");
bputs(text[WelcomeToMultiChat]);
if(getnodedat(cfg.node_num,&thisnode,true)==0) {
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)) {
sprintf(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)
continue;
getnodedat(i,&node,0);
if(node.action!=NODE_MCHT || node.status!=NODE_INUSE)
continue;
if(node.aux && (node.aux&0xff)!=channel)
continue;
printnodedat(i,&node);
preusrs=usrs;
if(gurubuf)
bprintf(text[NodeInMultiChatLocally]
,cfg.sys_nodes+1,cfg.guru[cfg.chan[channel-1]->guru]->name,channel);
bputs(text[YoureOnTheAir]);
done=0;
while(online && !done) {
checkline();
gettimeleft();
action=NODE_MCHT;
qusrs=usrs=0;
for(i=1;i<=cfg.sys_nodes;i++) {
if(i==cfg.node_num)
continue;
getnodedat(i,&node,0);
if(node.action!=NODE_MCHT
|| (node.aux && channel && (node.aux&0xff)!=channel))
continue;
if(node.status==NODE_QUIET)
qusr[qusrs++]=(char)i;
else if(node.status==NODE_INUSE)
if(preusrs>usrs) {
if(!usrs && channel && cfg.chan[channel-1]->misc&CHAN_GURU
&& cfg.chan[channel-1]->guru<cfg.total_gurus)
bprintf(text[NodeJoinedMultiChat]
,cfg.sys_nodes+1,cfg.guru[cfg.chan[channel-1]->guru]->name
,channel);
outchar(BEL);
for(i=0;i<preusrs;i++) {
for(j=0;j<usrs;j++)
if(preusr[i]==usr[j])
break;
if(j==usrs) {
getnodedat(preusr[i],&node,0);
if(node.misc&NODE_ANON)
sprintf(str,"%.80s",text[UNKNOWN_USER]);
else
username(&cfg,node.useron,str);
bprintf(text[NodeLeftMultiChat]
,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)
bprintf(text[NodeLeftMultiChat]
,cfg.sys_nodes+1,cfg.guru[cfg.chan[channel-1]->guru]->name
,channel);
outchar(BEL);
for(i=0;i<usrs;i++) {
for(j=0;j<preusrs;j++)
if(usr[i]==preusr[j])
break;
if(j==preusrs) {
getnodedat(usr[i],&node,0);
if(node.misc&NODE_ANON)
sprintf(str,"%.80s",text[UNKNOWN_USER]);
else
username(&cfg,node.useron,str);
bprintf(text[NodeJoinedMultiChat]
,usr[i],str,channel);
}
}
preusrs=usrs;
for(i=0;i<usrs;i++)
preusr[i]=usr[i];
attr(cfg.color[clr_multichat]);
if((ch=inkey(K_NONE,250))!=0 || wordwrap[0]) {
if(ch=='/') {
bputs(text[MultiChatCommandPrompt]);
strcpy(str,"ACELWQ?*");
if(SYSOP)
SAFECAT(str,"0");
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
i=getkeys(str,cfg.total_chans);
if(i&0x80000000L) { /* change channel */
savch=(char)(i&~0x80000000L);
if(savch==channel)
continue;
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)
continue;
getnodedat(i,&node,0);
if(node.action!=NODE_MCHT
|| node.status!=NODE_INUSE)
continue;
if(node.aux && (node.aux&0xff)!=savch)
continue;
printnodedat(i,&node);
if(node.aux&0x1f00) { /* password */
bprintf(text[PasswordProtected]
,node.misc&NODE_ANON
? text[UNKNOWN_USER]
: username(&cfg,node.useron,tmp));
if(!getstr(str,8,K_UPPER|K_ALPHA|K_LINE))
break;
if(strcmp(str,unpackchatpass(tmp,&node)))
break;
bputs(text[CorrectPassword]);
}
if(i<=cfg.sys_nodes) { /* failed password */
bputs(text[WrongPassword]);
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
)) {
sprintf(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) {
,(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)
bprintf(text[NodeInMultiChatLocally]
,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])) {
bputs(text[PasswordPrompt]);
if(getstr(str,8,K_UPPER|K_ALPHA|K_LINE)) {
getnodedat(cfg.node_num,&thisnode,true);
packchatpass(str,&thisnode);
getnodedat(cfg.node_num,&thisnode,true);
thisnode.aux=channel;
}
getnodedat(cfg.node_num,&thisnode,true);
putnodedat(cfg.node_num,&thisnode);
bputs(text[YoureOnTheAir]);
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,0);
if(node.action!=NODE_MCHT
|| node.status!=NODE_INUSE)
continue;
printnodedat(i,&node);
if(getnodedat(cfg.node_num,&thisnode,true)==0) {
thisnode.aux=channel=0;
putnodedat(cfg.node_num,&thisnode);
}
break;
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;
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);
break;
case 'L': /* list nodes */
CRLF;
for(i=1;i<=cfg.sys_nodes && i<=cfg.sys_lastnode;i++) {
getnodedat(i,&node,0);
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;
sprintf(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");
ungetkey(ch);
j=0;
pgraph[0]=0;
while(j<5) {
if(!getstr(line,66,K_WRAP|K_MSG|K_CHAT))
break;
if(j) {
sprintf(str,text[ChatLineFmt]
,thisnode.misc&NODE_ANON
? text[AnonUserChatHandle]
: useron.handle
,cfg.node_num,':',nulstr);
SAFECAT(pgraph,tmp);
SAFECAT(pgraph,line);
SAFECAT(pgraph,crlf);
if(channel && useron.chat&CHAT_ACTION) {
for(i=0;i<cfg.total_chatacts;i++) {
if(cfg.chatact[i]->actset
!=cfg.chan[channel-1]->actset)
continue;
sprintf(str,"%s ",cfg.chatact[i]->cmd);
if(!strnicmp(str,pgraph,strlen(str)))
break;
sprintf(str,"%.*s"
,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,0);
if(usrs==1) /* no need to search */
break;
if(n) {
if(usr[j]==n)
break;
username(&cfg,node.useron,str);
if(!strnicmp(str,p,strlen(str)))
break;
getuserstr(&cfg, node.useron, USER_HANDLE, str, sizeof(str));
if(!usrs
&& cfg.chan[channel-1]->guru<cfg.total_gurus)
strcpy(str
,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]);
else
username(&cfg,node.useron,str);
/* Display on same node */
bprintf(cfg.chatact[i]->out
,thisnode.misc&NODE_ANON
? text[UNKNOWN_USER] : useron.alias
,str);
CRLF;
if(usrs && j<usrs) {
/* Display to dest user */
sprintf(buf,cfg.chatact[i]->out
,thisnode.misc&NODE_ANON
? text[UNKNOWN_USER] : useron.alias
,"you");
SAFECAT(buf,crlf);
putnmsg(usr[j],buf);
/* Display to all other users */
sprintf(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)
continue;
getnodedat(usr[i],&node,0);
putnmsg(usr[i],buf);
for(i=0;i<qusrs;i++) {
getnodedat(qusr[i],&node,0);
putnmsg(qusr[i],buf);
sprintf(buf,text[ChatLineFmt]
,thisnode.misc&NODE_ANON
? text[AnonUserChatHandle]
: useron.handle
,cfg.node_num,':',pgraph);
if(useron.chat&CHAT_ECHO)
bputs(buf);
for(i=0;i<usrs;i++) {
getnodedat(usr[i],&node,0);
putnmsg(usr[i],buf);
for(i=0;i<qusrs;i++) {
getnodedat(qusr[i],&node,0);
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(gurubuf != NULL)
free(gurubuf);
/****************************************************************************/
/****************************************************************************/
bool sbbs_t::guru_page(void)
{
char path[MAX_PATH+1];
char* gurubuf;
int file;
long i;
if(useron.rest&FLAG('C')) {
bputs(text[R_Chat]);
if(!cfg.total_gurus) {
bprintf(text[SysopIsNotAvailable],"The Guru");
if(cfg.total_gurus==1 && chk_ar(cfg.guru[0]->ar,&useron,&client))
i=0;
else {
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)
}
sprintf(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);
gurubuf[length]=0;
return(true);
}
/****************************************************************************/
/* The chat section */
/****************************************************************************/
void sbbs_t::chatsection()
{
exec_bin(cfg.chatsec_mod, &main_csi);
/****************************************************************************/
/****************************************************************************/
if(useron.rest&FLAG('C')) {
bputs(text[R_Chat]);
return(false);
if(sysop_available(&cfg)
|| (cfg.sys_chat_ar[0] && chk_ar(cfg.sys_chat_ar,&useron,&client))
if(!(sys_status&SS_SYSPAGE)) {
logline("C", "paged sysop for chat");

Rob Swindell
committed
SAFEPRINTF2(str, "%s paged you to chat from node %u", useron.alias, cfg.node_num);
notify(&cfg, 1, str, NULL);
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);
break;
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);
outchar('.');
CRLF;
else {
sys_status^=SS_SYSPAGE;
bprintf(text[SysopPageIsNow]

Rob Swindell
committed
,sys_status&SS_SYSPAGE ? text[On] : text[Off]);
nosound();
if(!(sys_status&SS_SYSPAGE))
remove(syspage_semfile);
return(true);
}
bprintf(text[SysopIsNotAvailable],cfg.sys_op);
return(false);
}
/****************************************************************************/
/* 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]);
return(false);
if(!(useron.exempt&FLAG('J')) && cfg.chan[cnum]->cost>user_available_credits(&useron)) {
bputs(text[NotEnoughCredits]);
return(false);
/****************************************************************************/
/* 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 outpath[MAX_PATH+1];
char inpath[MAX_PATH+1];
int in,out,i,n,echo=1,x,y,activity,remote_activity;
int local_y=1,remote_y=1;
time_t last_nodechk=0;

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

Rob Swindell
committed
if(SYSOP && getnodedat(n, &node, true) == 0) {
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));
return;
}
if(node.action!=NODE_PAGE) {
bprintf(text[PagingUser]
,node.misc&NODE_ANON ? text[UNKNOWN_USER] : username(&cfg,node.useron,tmp)
,node.misc&NODE_ANON ? 0 : node.useron);
sprintf(str,text[NodePChatPageMsg]
,cfg.node_num,thisnode.misc&NODE_ANON
? text[UNKNOWN_USER] : useron.alias);
putnmsg(n,str);

Rob Swindell
committed
sprintf(str,"paged %s on node %d to private chat"
,username(&cfg,node.useron,tmp),n);
logline("C",str);
}
if(getnodedat(cfg.node_num,&thisnode,true)==0) {
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,0);
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;
action=NODE_PAGE;
checkline();
gettimeleft();
sync();
inkey(K_NONE,500);
}
gettimeleft();
if(getnodedat(cfg.node_num,&thisnode,true)==0) {
thisnode.action=action=NODE_PCHT;
thisnode.aux=n;

Rob Swindell
committed
thisnode.misc&=~ (NODE_LCHAT|NODE_FCHAT);
putnodedat(cfg.node_num,&thisnode);
}

Rob Swindell
committed
if(!online || (!forced && (sys_status&SS_ABORT)))

Rob Swindell
committed
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))
return;
}
if(((sys_status&SS_USERON && useron.chat&CHAT_SPLITP) || !(sys_status&SS_USERON))
&& term_supports(ANSI) && rows>=24 && cols>=80)
else
sys_status&=~SS_SPLITP;
/*
if(!(useron.misc&EXPERT))

rswindell
committed
menu("privchat");
*/
if(!(sys_status&SS_SPLITP)) {

Rob Swindell
committed
if(forced)
bprintf(text[SysopIsHere],cfg.sys_op);
else
bputs(text[WelcomeToPrivateChat]);
}
sprintf(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;
}

Rob Swindell
committed
if(forced && n == 0)
sprintf(inpath,"%slchat.dab",cfg.node_dir);
sprintf(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;
errormsg(WHERE,ERR_ALLOC,nulstr,PCHAT_LEN);
return;
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)==0) {
thisnode.misc&=~NODE_RPCHT; /* Clear "reset pchat flag" */
putnodedat(cfg.node_num,&thisnode);
}

Rob Swindell
committed
if(n) { // not local
if(getnodedat(n,&node,true)==0) {
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,0);
if(!(node.misc&NODE_RPCHT))
break;
getnodedat(cfg.node_num,&thisnode,0);
if(thisnode.misc&NODE_RPCHT)
break;
checkline();
gettimeleft();
SLEEP(500);
if(sys_status&SS_SPLITP) {
lncntr=0;
CLS;
ansi_save();
ansi_gotoxy(1,13);

Rob Swindell
committed
bprintf(forced ? local_sep : sep
,thisnode.misc&NODE_MSGW ? 'T':' '
,thisnode.misc&NODE_NMSG ? 'M':' ');
ansi_gotoxy(1,14);
local_y=14;

Rob Swindell
committed
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) {
localbuf[localline][localchar]=0;
}
else if(ch==TAB) {
if(echo)
outchar(' ');
localbuf[localline][localchar]=' ';
localchar++;
while(localchar<78 && localchar%8) {
if(echo)
localbuf[localline][localchar++]=' ';
}
if(sys_status&SS_SPLITP) {
CLS;
attr(cfg.color[clr_chatremote]);
remotebuf[remoteline][remotechar]=0;
for(i=0;i<=remoteline;i++) {
bputs(remotebuf[i]);
if(i!=remoteline)
bputs(crlf);
}
remote_y=1+remoteline;
bputs("\1i_\1n"); /* Fake cursor */
ansi_save();
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':' ');
ansi_gotoxy(1,14);
attr(cfg.color[clr_chatlocal]);
localbuf[localline][localchar]=0;
for(i=0;i<=localline;i++) {
bputs(localbuf[i]);
if(i!=localline)
bputs(crlf);
}
local_y=15+localline;
}
else if(ch>=' ' || ch==CR) {
if(ch!=CR) {
if(echo)
outchar(ch);
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':' '
,thisnode.misc&NODE_NMSG ? 'M':' ');
attr(cfg.color[clr_chatlocal]);
for(x=13,y=0;x<rows;x++,y++) {

rswindell
committed
comprintf("\x1b[%d;1H\x1b[K",x+1);
bprintf("%s\r\n",localbuf[y]);
ansi_gotoxy(1,local_y=(15+localline));
localline=0;
else {
if(localline>=4)
for(i=0;i<4;i++)
memcpy(localbuf[i],localbuf[i+1],81);
else
localline++;
if(echo) {
CRLF;
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;
}
wr = write(out,&ch,1);