Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

getnode.cpp 17 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/* getnode.cpp */

/* Synchronet node information retrieval functions */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html		*
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"
#include "cmdshell.h"

/****************************************************************************/
/* Reads the data for node number 'number' into the structure 'node'        */
/* from NODE.DAB															*/
/* if lockit is non-zero, locks this node's record. putnodedat() unlocks it */
/****************************************************************************/
46
int sbbs_t::getnodedat(uint number, node_t *node, bool lockit)
47
{
48
	char	str[MAX_PATH+1];
49 50
	int		rd=sizeof(node_t);
	int		count;
51

52
	if(node==NULL || number<1)
53
		return(-1);
54 55

	if(number>cfg.sys_nodes) {
56
		errormsg(WHERE,ERR_CHK,"node number",number);
57
		return(-1); 
58
	}
59

60 61
	if(node!=&thisnode)
		memset(node,0,sizeof(node_t));
62
	sprintf(str,"%snode.dab",cfg.ctrl_dir);
63 64 65
	if(nodefile==-1) {
		if((nodefile=nopen(str,O_RDWR|O_DENYNONE))==-1) {
			errormsg(WHERE,ERR_OPEN,str,O_RDWR|O_DENYNONE);
66
			return(errno); 
67 68
		}
	}
69 70
	else
		utime(str,NULL);		/* NFS fix... utime() forces a cache refresh */
71

72
	number--;	/* make zero based */
73 74
	for(count=0;count<LOOP_NODEDAB;count++) {
		if(count)
75
			mswait(100);
76
		if(lockit && lock(nodefile,(long)number*sizeof(node_t),sizeof(node_t))!=0) {
77
			unlock(nodefile,(long)number*sizeof(node_t),sizeof(node_t));
78
			continue;
79
		}
80
		lseek(nodefile,(long)number*sizeof(node_t),SEEK_SET);
81 82 83 84
		rd=read(nodefile,node,sizeof(node_t));
		if(!lockit || rd!=sizeof(node_t))
			unlock(nodefile,(long)number*sizeof(node_t),sizeof(node_t));
		if(rd==sizeof(node_t))
85
			break;
86
	}
87
	if(!lockit && cfg.node_misc&NM_CLOSENODEDAB) {
88 89 90 91
		close(nodefile);
		nodefile=-1;
	}

92 93
	if(count==LOOP_NODEDAB) {
		errormsg(WHERE,rd==sizeof(node_t) ? ERR_LOCK : ERR_READ,"node.dab",number+1);
94 95
		if(nodefile!=-1)
			close(nodefile);
96
		nodefile=-1;
97 98 99
		return(-2);
	}
	if(count>(LOOP_NODEDAB/2)) {
100 101
		sprintf(str,"NODE.DAB (node %d) COLLISION - Count: %d"
			,number+1, count);
102
		logline(LOG_WARNING,"!!",str); 
103
	}
104 105

	return(0);
106 107 108 109 110 111 112 113 114 115 116
}

/****************************************************************************/
/* Synchronizes all the nodes knowledge of the other nodes' actions, mode,  */
/* status and other flags.                                                  */
/* Assumes that getnodedat(node_num,&thisnode,0) was called right before it */
/* is called.  																*/
/****************************************************************************/
void sbbs_t::nodesync()
{
	char	str[256],today[32];
rswindell's avatar
rswindell committed
117
	int		atr=curatr;
118

rswindell's avatar
rswindell committed
119
	if(nodesync_inside || !online) 
120
		return;
121 122 123
	nodesync_inside=1;

	if(thisnode.action!=action) {
124 125 126 127
		if(getnodedat(cfg.node_num,&thisnode,true)==0) {
			thisnode.action=action;
			putnodedat(cfg.node_num,&thisnode); 
		}
128
	}
129 130 131 132

	criterrs=thisnode.errors;

	if(sys_status&SS_USERON) {
133 134

		if(thisnode.status==NODE_WFC) {
135
			lprintf(LOG_ERR, "Node %d NODE STATUS FIXUP", cfg.node_num);
136 137 138 139
			if(getnodedat(cfg.node_num,&thisnode,true)==0) {
				thisnode.status=NODE_INUSE;
				putnodedat(cfg.node_num,&thisnode); 
			}
140 141
		}

142 143
		if(!(sys_status&SS_NEWDAY)) {
			now=time(NULL);
144 145
			unixtodstr(&cfg,(time32_t)logontime,str);
			unixtodstr(&cfg,(time32_t)now,today);
146 147
			if(strcmp(str,today)) { /* New day, clear "today" user vars */
				sys_status|=SS_NEWDAY;	// So we don't keep doing this over&over
148
				resetdailyuserdat(&cfg, &useron,/* write: */true);
149 150
			} 
		}
151 152
		if(thisnode.misc&NODE_UDAT && !(useron.rest&FLAG('G'))) {   /* not guest */
			getuserdat(&cfg, &useron);
153 154 155 156
			if(getnodedat(cfg.node_num,&thisnode,true)==0) {
				thisnode.misc&=~NODE_UDAT;
				putnodedat(cfg.node_num,&thisnode); 
			}
157
		}
158
		if(!(sys_status&SS_MOFF)) {
159 160 161 162 163
			if(thisnode.misc&NODE_MSGW)
				getsmsg(useron.number); 	/* getsmsg clears MSGW flag */
			if(thisnode.misc&NODE_NMSG)
				getnmsg();					/* getnmsg clears NMSG flag */
		}
164
	}
165 166 167 168 169 170

	if(cfg.sync_mod[0])
		exec_bin(cfg.sync_mod,&main_csi);

	if(thisnode.misc&NODE_INTR) {
		bputs(text[NodeLocked]);
171
		logline(LOG_NOTICE,nulstr,"Interrupted");
172 173
		hangup();
		nodesync_inside=0;
174 175
		return; 
	}
176 177 178 179 180 181 182 183 184 185

	if(thisnode.misc&NODE_LCHAT) { // pulled into local chat with sysop
		SAVELINE;
		privchat(true);
		RESTORELINE;
	}
		
	if(sys_status&SS_USERON && memcmp(&nodesync_user,&useron,sizeof(user_t))) {
		getusrdirs();
		getusrsubs();
186 187
		memcpy(&nodesync_user,&useron,sizeof(nodesync_user)); 
	}
188 189 190 191

	if(sys_status&SS_USERON && online && (timeleft/60)<(5-timeleft_warn)
		&& !SYSOP) {
		timeleft_warn=5-(timeleft/60);
192 193
		if(!(sys_status&SS_MOFF)) {
			attr(LIGHTGRAY);
194 195 196
			bprintf(text[OnlyXminutesLeft]
				,((ushort)timeleft/60)+1,(timeleft/60) ? "s" : nulstr); 
		}
197
	}
198 199 200 201 202 203 204 205

	attr(atr);	/* replace original attributes */
	nodesync_inside=0;
}

/****************************************************************************/
/* Prints short messages waiting for this node, if any...                   */
/****************************************************************************/
206
int sbbs_t::getnmsg()
207
{
deuce's avatar
deuce committed
208
	char	str[MAX_PATH+1], *buf;
209 210
	int		file;
	long	length;
211

212 213 214 215
	if(getnodedat(cfg.node_num,&thisnode,true)==0) {
		thisnode.misc&=~NODE_NMSG;          /* clear the NMSG flag */
		putnodedat(cfg.node_num,&thisnode);
	}
216

217
	sprintf(str,"%smsgs/n%3.3u.msg",cfg.data_dir,cfg.node_num);
218
	if(flength(str)<1L)
219
		return(0);
220 221 222 223
	if((file=nopen(str,O_RDWR))==-1) {
		/**
			errormsg(WHERE,ERR_OPEN,str,O_RDWR);
		**/
224
		return(errno); 
225
	}
226
	length=(long)filelength(file);
227 228
	if(!length) {
		close(file);
229 230
		return(0); 
	}
deuce's avatar
deuce committed
231
	if((buf=(char *)malloc(length+1))==NULL) {
232 233
		close(file);
		errormsg(WHERE,ERR_ALLOC,str,length+1);
234 235
		return(-1); 
	}
236 237
	if(lread(file,buf,length)!=length) {
		close(file);
deuce's avatar
deuce committed
238
		free(buf);
239
		errormsg(WHERE,ERR_READ,str,length);
240 241
		return(errno); 
	}
242 243 244 245 246 247
	chsize(file,0L);
	close(file);
	buf[length]=0;

	if(thisnode.action==NODE_MAIN || thisnode.action==NODE_XFER
		|| sys_status&SS_IN_CTRLP) {
248 249
		CRLF; 
	}
250
	putmsg(buf,P_NOATCODES);
deuce's avatar
deuce committed
251
	free(buf);
252 253

	return(0);
254 255 256 257 258
}

/****************************************************************************/
/* 'ext' must be at least 128 bytes!                                        */
/****************************************************************************/
259
int sbbs_t::getnodeext(uint number, char *ext)
260
{
261
    char	str[MAX_PATH+1];
262
    int		rd,count;
263 264 265

	if(!number || number>cfg.sys_nodes) {
		errormsg(WHERE,ERR_CHK,"node number",number);
266
		return(-1); 
267
	}
268 269 270 271 272

	sprintf(str,"%snode.exb",cfg.ctrl_dir);
	if((node_ext=nopen(str,O_RDONLY|O_DENYNONE))==-1) {
		memset(ext,0,128);
		errormsg(WHERE,ERR_OPEN,str,O_RDONLY|O_DENYNONE);
273
		return(errno); 
274 275
	}

276
	number--;   /* make zero based */
277 278
	for(count=0;count<LOOP_NODEDAB;count++) {
		if(count)
279
			mswait(100);
280
		if(lock(node_ext,(long)number*128L,128)!=0) 
281
			continue; 
282
		lseek(node_ext,(long)number*128L,SEEK_SET);
283 284 285
		rd=read(node_ext,ext,128);
		unlock(node_ext,(long)number*128L,128);
		if(rd==128)
286
			break;
287
	}
288 289 290
	close(node_ext);
	node_ext=-1;

291 292 293 294 295 296

	if(count==LOOP_NODEDAB) {
		errormsg(WHERE,ERR_READ,"node.exb",number+1);
		return(-2);
	}
	if(count>(LOOP_NODEDAB/2)) {
297 298
		sprintf(str,"NODE.EXB (node %d) COLLISION - Count: %d"
			,number+1, count);
299 300
		logline("!!",str); 
	}
301 302

	return(0);
303 304 305 306 307 308 309
}


/****************************************************************************/
/* Prints short messages waiting for 'usernumber', if any...                */
/* then deletes them.                                                       */
/****************************************************************************/
310
int sbbs_t::getsmsg(int usernumber)
311
{
deuce's avatar
deuce committed
312
	char	str[MAX_PATH+1], *buf;
313 314
    int		file;
    long	length;
315 316 317 318 319 320 321 322 323 324 325 326
	node_t	node;
	int		i;

	for(i=1;i<=cfg.sys_nodes;i++) {	/* clear msg waiting flag */
		if(getnodedat(i,&node,true)==0) {
			if(node.useron==usernumber
					&& (node.status==NODE_INUSE || node.status==NODE_QUIET)
					&& node.misc&NODE_MSGW)
				node.misc&=~NODE_MSGW;
			putnodedat(i,&node); 
		} 
	}
327

328
	sprintf(str,"%smsgs/%4.4u.msg",cfg.data_dir,usernumber);
329
	if(flength(str)<1L)
330
		return(0);
331 332
	if((file=nopen(str,O_RDWR))==-1) {
		errormsg(WHERE,ERR_OPEN,str,O_RDWR);
333 334
		return(errno); 
	}
335
	length=(long)filelength(file);
deuce's avatar
deuce committed
336
	if((buf=(char *)malloc(length+1))==NULL) {
337 338
		close(file);
		errormsg(WHERE,ERR_ALLOC,str,length+1);
339 340
		return(-1); 
	}
341 342
	if(lread(file,buf,length)!=length) {
		close(file);
deuce's avatar
deuce committed
343
		free(buf);
344
		errormsg(WHERE,ERR_READ,str,length);
345 346
		return(errno); 
	}
347 348 349 350 351 352
	chsize(file,0L);
	close(file);
	buf[length]=0;
	getnodedat(cfg.node_num,&thisnode,0);
	if(thisnode.action==NODE_MAIN || thisnode.action==NODE_XFER
		|| sys_status&SS_IN_CTRLP) {
353 354
		CRLF; 
	}
355
	putmsg(buf,P_NOATCODES);
deuce's avatar
deuce committed
356
	free(buf);
357 358

	return(0);
359 360 361 362 363 364 365 366 367
}

/****************************************************************************/
/* This function lists users that are online.                               */
/* If listself is true, it will list the current node.                      */
/* Returns number of active nodes (not including current node).             */
/****************************************************************************/
int sbbs_t::whos_online(bool listself)
{
368 369
    int		i,j;
    node_t	node;
370 371 372 373 374 375 376 377

	CRLF;
	bputs(text[NodeLstHdr]);
	for(j=0,i=1;i<=cfg.sys_nodes && i<=cfg.sys_lastnode;i++) {
		getnodedat(i,&node,0);
		if(i==cfg.node_num) {
			if(listself)
				printnodedat(i,&node);
378 379
			continue; 
		}
380 381 382 383
		if(node.status==NODE_INUSE || (SYSOP && node.status==NODE_QUIET)) {
			printnodedat(i,&node);
			if(!lastnodemsg)
				lastnodemsg=i;
384 385 386
			j++; 
		} 
	}
387 388 389 390 391
	if(!j)
		bputs(text[NoOtherActiveNodes]);
	return(j);
}

392 393 394 395 396 397 398 399 400 401 402 403
void sbbs_t::nodelist(void)
{
	node_t	node;

	CRLF;
	bputs(text[NodeLstHdr]);
	for(int i=1;i<=cfg.sys_nodes && i<=cfg.sys_lastnode;i++) {
		getnodedat(i,&node,0);
		printnodedat(i,&node); 
	}
}

404
static char* node_connection_desc(sbbs_t* sbbs, ushort conn, char* str)
405 406 407
{
	switch(conn) {
		case NODE_CONNECTION_LOCAL:
rswindell's avatar
rswindell committed
408
			return (char*)" Locally";	/* obsolete */
409
		case NODE_CONNECTION_TELNET:
410
			return sbbs->text[NodeConnectionTelnet];
411
		case NODE_CONNECTION_RLOGIN:
412
			return sbbs->text[NodeConnectionRLogin];
413
		case NODE_CONNECTION_SSH:
414
			return sbbs->text[NodeConnectionSSH];
415
		default:
416
			sprintf(str,sbbs->text[NodeConnectionModem],conn);
417 418 419 420 421 422
			break;
	}

	return str;
}

423 424 425 426 427
/****************************************************************************/
/* Displays the information for node number 'number' contained in 'node'    */
/****************************************************************************/
void sbbs_t::printnodedat(uint number, node_t* node)
{
428 429 430
    uint	i;
    char	hour,mer[3];
	char 	tmp[512];
431 432 433 434 435 436

	attr(cfg.color[clr_nodenum]);
	bprintf("%3d  ",number);
	attr(cfg.color[clr_nodestatus]);
	switch(node->status) {
		case NODE_WFC:
437
			bputs(text[NodeStatusWaitingForCall]);
438 439
			break;
		case NODE_OFFLINE:
440
			bputs(text[NodeStatusOffline]);
441 442
			break;
		case NODE_NETTING:
443
			bputs("Networking");	/* obsolete */
444 445
			break;
		case NODE_LOGON:
446 447
			bputs(text[NodeStatusLogon]);
			bputs(node_connection_desc(this, node->connection, tmp));
448 449
			break;
		case NODE_EVENT_WAITING:
450
			bputs(text[NodeStatusEventWaiting]);
451 452
			break;
		case NODE_EVENT_LIMBO:
453
			bprintf(text[NodeStatusEventLimbo],node->aux);
454 455
			break;
		case NODE_EVENT_RUNNING:
456
			bputs(text[NodeStatusEventRunning]);
457 458
			break;
		case NODE_NEWUSER:
459 460
			bputs(text[NodeStatusNewUser]);
			bputs(node_connection_desc(this, node->connection, tmp));
461 462 463
			break;
		case NODE_QUIET:
			if(!SYSOP) {
464
				bputs(text[NodeStatusWaitingForCall]);
465 466
				break; 
			}
467 468 469 470
		case NODE_INUSE:
			if(node->misc&NODE_EXT) {
				getnodeext(number,tmp);
				bputs(tmp);
471 472
				break; 
			}
473 474
			attr(cfg.color[clr_nodeuser]);
			if(node->misc&NODE_ANON && !SYSOP)
475
				bputs(text[UNKNOWN_USER]);
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
			else
				bputs(username(&cfg,node->useron,tmp));
			attr(cfg.color[clr_nodestatus]);
			bputs(" ");
			switch(node->action) {
				case NODE_MAIN:
					bputs("at main menu");
					break;
				case NODE_RMSG:
					bputs("reading messages");
					break;
				case NODE_RMAL:
					bputs("reading mail");
					break;
				case NODE_RSML:
					bputs("reading sent mail");
					break;
				case NODE_RTXT:
					bputs("reading text files");
					break;
				case NODE_PMSG:
					bputs("posting message");
					break;
				case NODE_SMAL:
					bputs("sending mail");
					break;
				case NODE_AMSG:
					bputs("posting auto-message");
					break;
				case NODE_XTRN:
					if(node->aux<1 || node->aux>cfg.total_xtrns)
						bputs("at external program menu");
					else {
						bputs("running ");
						i=node->aux-1;
rswindell's avatar
rswindell committed
511
						if(SYSOP || chk_ar(cfg.xtrn[i]->ar,&useron,&client))
512 513
							bputs(cfg.xtrn[node->aux-1]->name);
						else
514 515
							bputs("external program"); 
					}
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
					break;
				case NODE_DFLT:
					bputs("changing defaults");
					break;
				case NODE_XFER:
					bputs("at transfer menu");
					break;
				case NODE_RFSD:
					bprintf("retrieving from device #%d",node->aux);
					break;
				case NODE_DLNG:
					bprintf("downloading");
					break;
				case NODE_ULNG:
					bputs("uploading");
					break;
				case NODE_BXFR:
					bputs("transferring bidirectional");
					break;
				case NODE_LFIL:
					bputs("listing files");
					break;
				case NODE_LOGN:
					bputs("logging on");
					break;
				case NODE_LCHT:
					bprintf("in local chat with %s",cfg.sys_op);
					break;
				case NODE_MCHT:
					if(node->aux) {
						bprintf("in multinode chat channel %d",node->aux&0xff);
						if(node->aux&0x1f00) { /* password */
							outchar('*');
							if(SYSOP)
550 551 552
								bprintf(" %s",unpackchatpass(tmp,node)); 
						} 
					}
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580
					else
						bputs("in multinode global chat channel");
					break;
				case NODE_PAGE:
					bprintf("paging node %u for private chat",node->aux);
					break;
				case NODE_PCHT:
					if(node->aux)
						bprintf("in private chat with node %u",node->aux);
					else
						bprintf("in local chat with %s",cfg.sys_op);
					break;
				case NODE_GCHT:
					i=node->aux;
					if(i>=cfg.total_gurus)
						i=0;
					bprintf("chatting with %s",cfg.guru[i]->name);
					break;
				case NODE_CHAT:
					bputs("in chat section");
					break;
				case NODE_TQWK:
					bputs("transferring QWK packet");
					break;
				case NODE_SYSP:
					bputs("performing sysop activities");
					break;
				default:
581
					bputs(ultoa(node->action,tmp,10));
582
					break;  }
583
			bputs(node_connection_desc(this, node->connection, tmp));
584 585 586
			if(node->action==NODE_DLNG) {
				if(cfg.sys_misc&SM_MILITARY) {
					hour=node->aux/60;
587 588
					mer[0]=0; 
				}
589 590 591 592 593
				else if((node->aux/60)>=12) {
					if(node->aux/60==12)
						hour=12;
					else
						hour=(node->aux/60)-12;
594 595
					strcpy(mer,"pm"); 
				}
596 597 598 599
				else {
					if((node->aux/60)==0)    /* 12 midnite */
						hour=12;
					else hour=node->aux/60;
600 601
					strcpy(mer,"am"); 
				}
602
				bprintf(" ETA %02d:%02d %s"
603 604 605 606
					,hour,node->aux%60,mer); 
			}
			break; 
	}
607 608 609 610 611 612 613 614 615 616 617 618 619
	i=NODE_LOCK;
	if(node->status==NODE_INUSE || SYSOP)
		i|=NODE_POFF|NODE_AOFF|NODE_MSGW|NODE_NMSG;
	if(node->misc&i) {
		bputs(" (");
		if(node->misc&(i&NODE_AOFF))
			outchar('A');
		if(node->misc&NODE_LOCK)
			outchar('L');
		if(node->misc&(i&(NODE_MSGW|NODE_NMSG)))
			outchar('M');
		if(node->misc&(i&NODE_POFF))
			outchar('P');
620 621
		outchar(')'); 
	}
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
	if(SYSOP && ((node->misc
		&(NODE_ANON|NODE_UDAT|NODE_INTR|NODE_RRUN|NODE_EVENT|NODE_DOWN|NODE_LCHAT))
		|| node->status==NODE_QUIET)) {
		bputs(" [");
		if(node->misc&NODE_ANON)
			outchar('A');
		if(node->misc&NODE_INTR)
			outchar('I');
		if(node->misc&NODE_RRUN)
			outchar('R');
		if(node->misc&NODE_UDAT)
			outchar('U');
		if(node->status==NODE_QUIET)
			outchar('Q');
		if(node->misc&NODE_EVENT)
			outchar('E');
		if(node->misc&NODE_DOWN)
			outchar('D');
		if(node->misc&NODE_LCHAT)
			outchar('C');
642 643
		outchar(']'); 
	}
644 645
	if(node->errors && SYSOP) {
		attr(cfg.color[clr_err]);
646 647
		bprintf(" %d error%c",node->errors, node->errors>1 ? 's' : '\0' ); 
	}
648 649 650
	attr(LIGHTGRAY);
	CRLF;
}