chat.cpp 51.3 KB
Newer Older
1
/* Synchronet real-time chat functions */
2
// vi: tabstop=4
3

4
/* $Id: chat.cpp,v 1.84 2020/08/15 21:58:14 rswindell Exp $ */
5 6 7 8 9

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
10
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
 *																			*
 * 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										*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * 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"};

46 47
/****************************************************************************/
/****************************************************************************/
rswindell's avatar
rswindell committed
48
void sbbs_t::multinodechat(int channel)
49
{
rswindell's avatar
rswindell committed
50 51
	char	line[256],str[256],ch,done
			,usrs,preusrs,qusrs,*gurubuf=NULL,savch,*p
52 53
			,pgraph[400],buf[400]
			,usr[MAX_NODES],preusr[MAX_NODES],qusr[MAX_NODES];
54
	char	guru_lastanswer[512];
55
	char 	tmp[512];
56 57 58 59
	int 	file;
	long	i,j,k,n;
	node_t 	node;

60 61
	if(useron.rest&FLAG('C')) {
		bputs(text[R_Chat]);
62
		return;
63 64
	}

rswindell's avatar
rswindell committed
65 66 67 68 69
	if(channel<1 || channel>cfg.total_chans)
		channel=1;

	if(!chan_access(channel-1))
		return;
70
	if(useron.misc&(RIP|WIP|HTML) ||!(useron.misc&EXPERT))
rswindell's avatar
rswindell committed
71 72
		menu("multchat");
	bputs(text[WelcomeToMultiChat]);
73
	if(getnodedat(cfg.node_num,&thisnode,true)==0) {
74
		thisnode.aux=channel;
75 76
		putnodedat(cfg.node_num,&thisnode);
	}
rswindell's avatar
rswindell committed
77 78
	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
rswindell's avatar
rswindell committed
79
		&& chk_ar(cfg.guru[cfg.chan[channel-1]->guru]->ar,&useron,&client)) {
rswindell's avatar
rswindell committed
80 81 82
		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);
83
			return;
84
		}
85
		if((gurubuf=(char *)malloc((size_t)filelength(file)+1))==NULL) {
rswindell's avatar
rswindell committed
86
			close(file);
87
			errormsg(WHERE,ERR_ALLOC,str,(size_t)filelength(file)+1);
88
			return;
89
		}
90
		read(file,gurubuf,(size_t)filelength(file));
rswindell's avatar
rswindell committed
91
		gurubuf[filelength(file)]=0;
92
		close(file);
93
	}
rswindell's avatar
rswindell committed
94 95 96 97 98 99 100 101 102 103
	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);
deuce's avatar
deuce committed
104
		preusr[usrs]=(char)i;
105
		usr[usrs++]=(char)i;
106
	}
rswindell's avatar
rswindell committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
	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)
128
				usr[usrs++]=(char)i;
129
		}
rswindell's avatar
rswindell committed
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
		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]
148 149 150
						,preusr[i],str,channel);
				}
			}
151
		}
rswindell's avatar
rswindell committed
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
		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]
170 171 172
						,usr[i],str,channel);
				}
			}
173
		}
rswindell's avatar
rswindell committed
174 175 176 177 178 179
		preusrs=usrs;
		for(i=0;i<usrs;i++)
			preusr[i]=usr[i];
		attr(cfg.color[clr_multichat]);
		SYNC;
		sys_status&=~SS_ABORT;
180
		if((ch=inkey(K_NONE,250))!=0 || wordwrap[0]) {
rswindell's avatar
rswindell committed
181 182 183 184
			if(ch=='/') {
				bputs(text[MultiChatCommandPrompt]);
				strcpy(str,"ACELWQ?*");
				if(SYSOP)
185
					SAFECAT(str,"0");
rswindell's avatar
rswindell committed
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
				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;
216 217
							bputs(text[CorrectPassword]);
						}
deuce's avatar
deuce committed
218
						preusr[usrs]=(char)i;
219
						usr[usrs++]=(char)i;
220
					}
rswindell's avatar
rswindell committed
221 222
					if(i<=cfg.sys_nodes) {	/* failed password */
						bputs(text[WrongPassword]);
223
						continue;
224
					}
rswindell's avatar
rswindell committed
225
					if(gurubuf) {
deuce's avatar
deuce committed
226
						free(gurubuf);
227
						gurubuf=NULL;
228
					}
rswindell's avatar
rswindell committed
229 230
					if(cfg.chan[savch-1]->misc&CHAN_GURU
						&& cfg.chan[savch-1]->guru<cfg.total_gurus
rswindell's avatar
rswindell committed
231
						&& chk_ar(cfg.guru[cfg.chan[savch-1]->guru]->ar,&useron,&client
rswindell's avatar
rswindell committed
232 233 234 235 236
						)) {
						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);
237
							break;
238
						}
239
						if((gurubuf=(char *)malloc((size_t)filelength(file)+1))==NULL) {
rswindell's avatar
rswindell committed
240 241
							close(file);
							errormsg(WHERE,ERR_ALLOC,str
242
								,(size_t)filelength(file)+1);
243
							break;
244
						}
245
						read(file,gurubuf,(size_t)filelength(file));
rswindell's avatar
rswindell committed
246
						gurubuf[filelength(file)]=0;
247
						close(file);
248
					}
rswindell's avatar
rswindell committed
249 250 251 252 253 254 255 256 257 258 259
					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)) {
260
							getnodedat(cfg.node_num,&thisnode,true);
rswindell's avatar
rswindell committed
261
							thisnode.aux=channel;
262
							packchatpass(str,&thisnode);
263
						}
rswindell's avatar
rswindell committed
264
						else {
265
							getnodedat(cfg.node_num,&thisnode,true);
266 267
							thisnode.aux=channel;
						}
268
					}
rswindell's avatar
rswindell committed
269
					else {
270
						getnodedat(cfg.node_num,&thisnode,true);
271
						thisnode.aux=channel;
272
					}
rswindell's avatar
rswindell committed
273 274 275 276
					putnodedat(cfg.node_num,&thisnode);
					bputs(text[YoureOnTheAir]);
					if(cfg.chan[channel-1]->cost
						&& !(useron.exempt&FLAG('J')))
277
						subtract_cdt(&cfg,&useron,cfg.chan[channel-1]->cost);
278
				}
rswindell's avatar
rswindell committed
279 280 281 282 283 284 285 286 287 288 289 290 291
				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);
deuce's avatar
deuce committed
292
							preusr[usrs]=(char)i;
293
							usr[usrs++]=(char)i;
294
						}
rswindell's avatar
rswindell committed
295
						preusrs=usrs;
296 297 298 299
						if(getnodedat(cfg.node_num,&thisnode,true)==0) {
							thisnode.aux=channel=0;
							putnodedat(cfg.node_num,&thisnode);
						}
rswindell's avatar
rswindell committed
300 301 302 303 304 305 306 307 308 309 310
						break;
					case 'A':   /* Action commands */
						useron.chat^=CHAT_ACTION;
						bprintf("\r\nAction commands are now %s\r\n"
							,useron.chat&CHAT_ACTION
							? text[ON]:text[OFF]);
						putuserrec(&cfg,useron.number,U_CHAT,8
							,ultoa(useron.chat,str,16));
						break;
					case 'C':   /* List of action commands */
						CRLF;
311
						for(i=0;channel && i<cfg.total_chatacts;i++) {
rswindell's avatar
rswindell committed
312 313 314 315 316 317
							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)) {
318
								CRLF;
319
							}
rswindell's avatar
rswindell committed
320
							else
321
								bputs(" ");
322
						}
rswindell's avatar
rswindell committed
323 324 325 326 327 328 329 330 331 332 333 334 335 336
						CRLF;
						break;
					case 'E':   /* Toggle echo */
						useron.chat^=CHAT_ECHO;
						bprintf(text[EchoIsNow]
							,useron.chat&CHAT_ECHO
							? text[ON]:text[OFF]);
						putuserrec(&cfg,useron.number,U_CHAT,8
							,ultoa(useron.chat,str,16));
						break;
					case 'L':	/* list nodes */
						CRLF;
						for(i=1;i<=cfg.sys_nodes && i<=cfg.sys_lastnode;i++) {
							getnodedat(i,&node,0);
337
							printnodedat(i,&node);
338
						}
rswindell's avatar
rswindell committed
339 340 341 342 343 344 345 346 347 348 349
						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]);
350 351
							break; 
						}
rswindell's avatar
rswindell committed
352 353 354 355 356 357 358 359 360 361

						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);
362
						SAFECAT(buf,crlf);
rswindell's avatar
rswindell committed
363 364
						if(useron.chat&CHAT_ECHO)
							bputs(buf);
365
						putnmsg(&cfg,j,buf);
rswindell's avatar
rswindell committed
366 367 368 369 370
						break;
					case 'Q':	/* quit */
						done=1;
						break;
					case '*':
371
						if(!menu("chan", P_NOERROR)) {
rswindell's avatar
rswindell committed
372 373 374 375
							bputs(text[ChatChanLstHdr]);
							bputs(text[ChatChanLstTitles]);
							if(cfg.total_chans>=10) {
								bputs("     ");
376
								bputs(text[ChatChanLstTitles]);
377
							}
rswindell's avatar
rswindell committed
378 379 380 381
							CRLF;
							bputs(text[ChatChanLstUnderline]);
							if(cfg.total_chans>=10) {
								bputs("     ");
382
								bputs(text[ChatChanLstUnderline]);
383
							}
rswindell's avatar
rswindell committed
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
							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
401 402
											,cfg.chan[k]->cost);
									}
403
								}
404
								CRLF;
405
							}
406
							CRLF;
407
						}
rswindell's avatar
rswindell committed
408 409 410
						break;
					case '?':	/* menu */
						menu("multchat");
411 412
						break;
				}
413
			} else {
rswindell's avatar
rswindell committed
414 415 416 417 418 419 420 421 422 423 424 425
				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);
deuce's avatar
deuce committed
426
						sprintf(tmp,"%*s",(int)bstrlen(str),nulstr);
427
						SAFECAT(pgraph,tmp);
428
					}
429 430
					SAFECAT(pgraph,line);
					SAFECAT(pgraph,crlf);
rswindell's avatar
rswindell committed
431 432
					if(!wordwrap[0])
						break;
433
					j++;
434
				}
rswindell's avatar
rswindell committed
435
				if(pgraph[0]) {
436
					if(channel && useron.chat&CHAT_ACTION) {
rswindell's avatar
rswindell committed
437 438 439 440 441 442 443 444 445 446 447
						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))
448
								break;
449
						}
rswindell's avatar
rswindell committed
450 451 452 453 454 455 456 457 458 459 460

						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;
461
									continue;
462
								}
rswindell's avatar
rswindell committed
463 464 465 466 467 468
								username(&cfg,node.useron,str);
								if(!strnicmp(str,p,strlen(str)))
									break;
								getuserrec(&cfg,node.useron,U_HANDLE
									,LEN_HANDLE,str);
								if(!strnicmp(str,p,strlen(str)))
469
									break;
470
							}
rswindell's avatar
rswindell committed
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
							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");
495
								SAFECAT(buf,crlf);
496
								putnmsg(&cfg,usr[j],buf);
497
							}
rswindell's avatar
rswindell committed
498 499 500 501 502 503 504


							/* Display to all other users */
							sprintf(buf,cfg.chatact[i]->out
								,thisnode.misc&NODE_ANON
								? text[UNKNOWN_USER] : useron.alias
								,str);
505
							SAFECAT(buf,crlf);
rswindell's avatar
rswindell committed
506 507 508 509 510

							for(i=0;i<usrs;i++) {
								if(i==j)
									continue;
								getnodedat(usr[i],&node,0);
511
								putnmsg(&cfg,usr[i],buf);
512
							}
rswindell's avatar
rswindell committed
513 514
							for(i=0;i<qusrs;i++) {
								getnodedat(qusr[i],&node,0);
515
								putnmsg(&cfg,qusr[i],buf);
516
							}
517 518
							continue;
						}
519
					}
rswindell's avatar
rswindell committed
520 521 522 523 524 525 526 527 528 529

					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);
530
						putnmsg(&cfg,usr[i],buf);
531
					}
rswindell's avatar
rswindell committed
532 533
					for(i=0;i<qusrs;i++) {
						getnodedat(qusr[i],&node,0);
534
						putnmsg(&cfg,qusr[i],buf);
535
					}
rswindell's avatar
rswindell committed
536 537
					if(!usrs && channel && gurubuf
						&& cfg.chan[channel-1]->misc&CHAN_GURU)
538
						guruchat(pgraph,gurubuf,cfg.chan[channel-1]->guru,guru_lastanswer);
539 540
				}
			}
541
		}
rswindell's avatar
rswindell committed
542
		if(sys_status&SS_ABORT)
543
			break;
544
	}
rswindell's avatar
rswindell committed
545
	lncntr=0;
546 547
	if(gurubuf != NULL)
		free(gurubuf);
rswindell's avatar
rswindell committed
548 549
}

550 551
/****************************************************************************/
/****************************************************************************/
rswindell's avatar
rswindell committed
552 553 554 555 556 557 558
bool sbbs_t::guru_page(void)
{
	char	path[MAX_PATH+1];
	char*	gurubuf;
	int 	file;
	long	i;

559 560
	if(useron.rest&FLAG('C')) {
		bputs(text[R_Chat]);
561
		return(false);
562 563
	}

rswindell's avatar
rswindell committed
564 565
	if(!cfg.total_gurus) {
		bprintf(text[SysopIsNotAvailable],"The Guru");
566
		return(false);
rswindell's avatar
rswindell committed
567
	}
rswindell's avatar
rswindell committed
568
	if(cfg.total_gurus==1 && chk_ar(cfg.guru[0]->ar,&useron,&client))
rswindell's avatar
rswindell committed
569 570 571 572 573 574
		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)
575
			return(false);
rswindell's avatar
rswindell committed
576 577 578 579
	}
	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);
580
		return(false);
rswindell's avatar
rswindell committed
581
	}
582 583
	if((gurubuf=(char *)malloc((size_t)filelength(file)+1))==NULL) {
		errormsg(WHERE,ERR_ALLOC,path,(size_t)filelength(file)+1);
584
		close(file);
585
		return(false);
rswindell's avatar
rswindell committed
586
	}
587
	read(file,gurubuf,(size_t)filelength(file));
rswindell's avatar
rswindell committed
588 589 590
	gurubuf[filelength(file)]=0;
	close(file);
	localguru(gurubuf,i);
deuce's avatar
deuce committed
591
	free(gurubuf);
rswindell's avatar
rswindell committed
592 593 594 595 596 597 598 599 600 601
	return(true);
}

/****************************************************************************/
/* The chat section                                                         */
/****************************************************************************/
void sbbs_t::chatsection()
{
	char	str[256],ch,no_rip_menu;

602 603
	if(useron.rest&FLAG('C')) {
		bputs(text[R_Chat]);
604
		return;
605 606
	}

607
	action=NODE_CHAT;
608
	if(useron.misc&(RIP|WIP|HTML) || !(useron.misc&EXPERT))
609
		menu("chat");
610 611 612 613 614
	ASYNC;
	bputs(text[ChatPrompt]);
	while(online) {
		no_rip_menu=0;
		ch=(char)getkeys("ACDJPQST?\r",0);
615
		if(ch>' ')
616 617 618 619 620
			logch(ch,0);
		switch(ch) {
			case 'S':
				useron.chat^=CHAT_SPLITP;
				putuserrec(&cfg,useron.number,U_CHAT,8
621
					,ultoa(useron.chat,str,16));
622 623 624 625 626 627 628
				bprintf("\r\nPrivate split-screen chat is now: %s\r\n"
					,useron.chat&CHAT_SPLITP ? text[ON]:text[OFF]);
				break;
			case 'A':
				CRLF;
				useron.chat^=CHAT_NOACT;
				putuserrec(&cfg,useron.number,U_CHAT,8
629
					,ultoa(useron.chat,str,16));
630 631 632 633
				if(getnodedat(cfg.node_num,&thisnode,true)==0) {
					thisnode.misc^=NODE_AOFF;
					printnodedat(cfg.node_num,&thisnode);
				}
634 635 636 637 638 639 640
				putnodedat(cfg.node_num,&thisnode);
				no_rip_menu=true;
				break;
			case 'D':
				CRLF;
				useron.chat^=CHAT_NOPAGE;
				putuserrec(&cfg,useron.number,U_CHAT,8
641
					,ultoa(useron.chat,str,16));
642 643 644 645
				if(getnodedat(cfg.node_num,&thisnode,true)==0) {
					thisnode.misc^=NODE_POFF;
					printnodedat(cfg.node_num,&thisnode);
				}
646 647 648 649
				putnodedat(cfg.node_num,&thisnode);
				no_rip_menu=true;
				break;
			case 'J':
rswindell's avatar
rswindell committed
650
				multinodechat();
651 652 653 654 655 656
				break;
			case 'P':   /* private node-to-node chat */
				privchat();
				break;
			case 'C':
				no_rip_menu=1;
rswindell's avatar
rswindell committed
657 658
				if(sysop_page())
					break;
659 660
				if(cfg.total_gurus && chk_ar(cfg.guru[0]->ar,&useron,&client) && text[ChatWithGuruInsteadQ][0]) {
					SAFEPRINTF(str,text[ChatWithGuruInsteadQ],cfg.guru[0]->name);
661
					if(!yesno(str))
662 663
						break; 
				}
664 665
				else
					break;
rswindell's avatar
rswindell committed
666
				/* FALL-THROUGH */
667
			case 'T':
rswindell's avatar
rswindell committed
668
				guru_page();
669 670 671 672
				no_rip_menu=1;
				break;
			case '?':
				if(useron.misc&EXPERT)
673
					menu("chat");
674 675 676
				break;
			default:	/* 'Q' or <CR> */
				lncntr=0;
rswindell's avatar
rswindell committed
677
//				if(gurubuf)
deuce's avatar
deuce committed
678
//					free(gurubuf);
679 680
				return; 
		}
681
		action=NODE_CHAT;
682
		if(!(useron.misc&EXPERT) || useron.misc&(WIP|HTML)
683
			|| (useron.misc&RIP && !no_rip_menu)) {
684 685
			menu("chat"); 
		}
686
		ASYNC;
687 688
		bputs(text[ChatPrompt]); 
	}
rswindell's avatar
rswindell committed
689
//	if(gurubuf)
deuce's avatar
deuce committed
690
//		free(gurubuf);
691 692
}

693 694
/****************************************************************************/
/****************************************************************************/
rswindell's avatar
rswindell committed
695
bool sbbs_t::sysop_page(void)
696
{
697 698
	char	str[256];
	int		i;
699

700 701 702 703 704
	if(useron.rest&FLAG('C')) {
		bputs(text[R_Chat]);
		return(false); 
	}

705
	if(sysop_available(&cfg)
rswindell's avatar
rswindell committed
706
		|| (cfg.sys_chat_ar[0] && chk_ar(cfg.sys_chat_ar,&useron,&client))
rswindell's avatar
rswindell committed
707 708
		|| useron.exempt&FLAG('C')) {

709 710 711
		logline("C", "paged sysop for chat");
		sprintf(str, "%s paged you to chat", useron.alias);
		notify(&cfg, 1, str, NULL);
712

713
		ftouch(syspage_semfile);
rswindell's avatar
rswindell committed
714
		for(i=0;i<cfg.total_pages;i++)
rswindell's avatar
rswindell committed
715
			if(chk_ar(cfg.page[i]->ar,&useron,&client))
rswindell's avatar
rswindell committed
716 717 718
				break;
		if(i<cfg.total_pages) {
			bprintf(text[PagingGuru],cfg.sys_op);
719 720 721 722 723 724 725 726
			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); 
727
		}
rswindell's avatar
rswindell committed
728 729 730 731 732
		else if(cfg.sys_misc&SM_SHRTPAGE) {
			bprintf(text[PagingGuru],cfg.sys_op);
			for(i=0;i<10 && !lkbrd(1);i++) {
				sbbs_beep(1000,200);
				mswait(200);
733 734 735 736
				outchar('.'); 
			}
			CRLF; 
		}
rswindell's avatar
rswindell committed
737 738 739 740 741 742
		else {
			sys_status^=SS_SYSPAGE;
			bprintf(text[SysopPageIsNow]
				,sys_status&SS_SYSPAGE ? text[ON] : text[OFF]);
			nosound();	
		}
743 744
		if(!(sys_status&SS_SYSPAGE))
			remove(syspage_semfile);
rswindell's avatar
rswindell committed
745 746 747 748 749 750 751

		return(true);
	}

	bprintf(text[SysopIsNotAvailable],cfg.sys_op);

	return(false);
752 753 754 755 756 757 758 759
}

/****************************************************************************/
/* Returns 1 if user online has access to channel "channum"                 */
/****************************************************************************/
bool sbbs_t::chan_access(uint cnum)
{

rswindell's avatar
rswindell committed
760
	if(!cfg.total_chans || cnum>=cfg.total_chans || !chk_ar(cfg.chan[cnum]->ar,&useron,&client)) {
761
		bputs(text[CantAccessThatChannel]);
762 763
		return(false); 
	}
764 765
	if(!(useron.exempt&FLAG('J')) && cfg.chan[cnum]->cost>useron.cdt+useron.freecdt) {
		bputs(text[NotEnoughCredits]);
766 767
		return(false); 
	}
768 769 770
	return(true);
}

771 772 773
/****************************************************************************/
/* Private split-screen (or interspersed) chat with node or local sysop		*/
/****************************************************************************/
774
void sbbs_t::privchat(bool forced, int node_num)
775 776 777
{
	char	str[128],c,*p,localbuf[5][81],remotebuf[5][81]
			,localline=0,remoteline=0,localchar=0,remotechar=0
778 779 780
			,*sep=text[PrivateChatSeparator]
			,*local_sep=text[SysopChatSeparator]
			;
781
	char 	tmp[512];
782
	char	outpath[MAX_PATH+1];
783
	char	inpath[MAX_PATH+1];
784 785
	uchar	ch;
	int 	in,out,i,n,echo=1,x,y,activity,remote_activity;
786
    int		local_y=1,remote_y=1;
787
	node_t	node;
788
	time_t	last_nodechk=0;
789

790 791
	if(forced)
		n = node_num;
792
	else {
793 794 795 796 797 798

		if(useron.rest&FLAG('C')) {
			bputs(text[R_Chat]);
			return; 
		}

799 800 801 802 803
		n=getnodetopage(0,0);
		if(!n)
			return;
		if(n==cfg.node_num) {
			bputs(text[NoNeedToPageSelf]);
804 805
			return; 
		}
806 807 808
		getnodedat(n,&node,0);
		if(node.action==NODE_PCHT && node.aux!=cfg.node_num) {
			bprintf(text[NodeNAlreadyInPChat],n);
809 810
			return; 
		}
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
		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(&cfg,n,str);
				sprintf(str,"paged %s on node %d to private chat"
					,username(&cfg,node.useron,tmp),n);
				logline("C",str);
			}
833
		}
834

835 836 837 838 839
		if(getnodedat(cfg.node_num,&thisnode,true)==0) {
			thisnode.action=action=NODE_PAGE;
			thisnode.aux=n;
			putnodedat(cfg.node_num,&thisnode);
		}
840 841 842 843 844 845 846 847 848 849

		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));
850 851
					break; 
				}
852 853 854
				action=NODE_PAGE;
				checkline();
				gettimeleft();
855
				SYNC; 
856
				inkey(K_NONE,500);
857 858
			} 
		}
859 860
	}

861 862
	gettimeleft();

863 864 865
	if(getnodedat(cfg.node_num,&thisnode,true)==0) {
		thisnode.action=action=NODE_PCHT;
		thisnode.aux=n;
866
		thisnode.misc&=~ (NODE_LCHAT|NODE_FCHAT);
867 868
		putnodedat(cfg.node_num,&thisnode);
	}
869

870
	if(!online || (!forced && (sys_status&SS_ABORT)))
871 872
		return;

873
	if(forced && n == 0) {
874 875 876 877 878
		/* If an external sysop chat event handler is installed, just run that and do nothing else */
		if(user_event(EVENT_LOCAL_CHAT))
			return;
	}

879
	if(((sys_status&SS_USERON && useron.chat&CHAT_SPLITP) || !(sys_status&SS_USERON))
880
		&& term_supports(ANSI) && rows>=24 && cols>=80)
881
		sys_status|=SS_SPLITP;
882 883
	else
		sys_status&=~SS_SPLITP;
884 885
	/*
	if(!(useron.misc&EXPERT))
886
		menu("privchat");
887 888 889
	*/

	if(!(sys_status&SS_SPLITP)) {
890
		if(forced)
891 892 893 894 895
			bprintf(text[SysopIsHere],cfg.sys_op);
		else
			bputs(text[WelcomeToPrivateChat]);
	}

896
	sprintf(outpath,"%schat.dab",cfg.node_dir);
897
	if((out=sopen(outpath,O_RDWR|O_CREAT|O_BINARY,SH_DENYNO,DEFFILEMODE))==-1) {
898
		errormsg(WHERE,ERR_OPEN,outpath,O_RDWR|O_DENYNONE|O_CREAT);
899 900
		return; 
	}
901

902
	if(forced && n == 0)
903
		sprintf(inpath,"%slchat.dab",cfg.node_dir);
904
	else
905 906
		sprintf(inpath,"%schat.dab",cfg.node_path[n-1]);
	if(!fexist(inpath))		/* Wait while it's created for the first time */
907
		mswait(2000);
908
	if((in=sopen(inpath,O_RDWR|O_CREAT|O_BINARY,SH_DENYNO,DEFFILEMODE))==-1) {
909
		close(out);
910
		errormsg(WHERE,ERR_OPEN,inpath,O_RDWR|O_DENYNONE|O_CREAT);
911 912
		return; 
	}
913

deuce's avatar
deuce committed
914
	if((p=(char *)malloc(PCHAT_LEN))==NULL) {
915 916
		close(in);
		close(out);
917
		errormsg(WHERE,ERR_ALLOC,nulstr,PCHAT_LEN);
918 919
		return; 
	}
920 921 922
	memset(p,0,PCHAT_LEN);
	write(in,p,PCHAT_LEN);
	write(out,p,PCHAT_LEN);
deuce's avatar
deuce committed
923
	free(p);
924 925 926
	lseek(in,0L,SEEK_SET);
	lseek(out,0L,SEEK_SET);

927 928 929 930
	if(getnodedat(cfg.node_num,&thisnode,true)==0) {
		thisnode.misc&=~NODE_RPCHT; 		/* Clear "reset pchat flag" */
		putnodedat(cfg.node_num,&thisnode);
	}
931

932
	if(n) { // not local
933 934 935 936
		if(getnodedat(n,&node,true)==0) {
			node.misc|=NODE_RPCHT;				/* Set "reset pchat flag" */
			putnodedat(n,&node); 				/* on other node */
		}
937 938 939 940 941 942 943 944 945 946 947 948 949

											/* 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();
			SYNC;
950 951
			SLEEP(500); 
		}
952 953 954 955 956 957 958 959
	}

	action=NODE_PCHT;
	SYNC;

	if(sys_status&SS_SPLITP) {
		lncntr=0;
		CLS;
960 961
		ansi_save();
		ansi_gotoxy(1,13);
962
		remote_y=1;
963
		bprintf(forced ? local_sep : sep
964
			,thisnode.misc&NODE_MSGW ? 'T':' '
965
			,sectostr(timeleft,tmp)
966
			,thisnode.misc&NODE_NMSG ? 'M':' ');
967
		CRLF;
968 969
		local_y=14; 
	}
970

971
	while(online && (forced || !(sys_status&SS_ABORT))) {
972 973 974 975 976 977
		lncntr=0;
		if(sys_status&SS_SPLITP)
			lbuflen=0;
		action=NODE_PCHT;
		activity=0;
		remote_activity=0;
978
		if((ch=inkey(K_GETSTR,100))!=0) {
979 980 981
			activity=1;
			if(echo)
				attr(cfg.color[clr_chatlocal]);
982
			if(ch==BS || ch==DEL) {
983 984
				if(localchar) {
					if(echo)
985
						backspace();
986
					localchar--;
987 988 989
					localbuf[localline][localchar]=0; 
				} 
			}
990 991
			else if(ch==TAB) {
				if(echo)
992 993
					outchar(' ');
				localbuf[localline][localchar]=' ';
994 995 996
				localchar++;
				while(localchar<78 && localchar%8) {
					if(echo)
997
						outchar(' ');
998 999 1000
					localbuf[localline][localchar++]=' '; 
				} 
			}
1001
			else if(ch==CTRL_R) {
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012
				if(sys_status&SS_SPLITP) {
					CLS;
					attr(cfg.color[clr_chatremote]);
					remotebuf[remoteline][remotechar]=0;