Skip to content
Snippets Groups Projects
  • Rob Swindell's avatar
    2b087b8b
    <Deuce> ... billion-and-one result of comparison of constant 100000 warnings. · 2b087b8b
    Rob Swindell authored
    So Clang-FreeBSD was warning (in compiles of scfg/scfg*.c by Deuce):
    result of comparison of constant 100000 with expression of type 'uint16_t'
    (aka 'unsigned short') is always true
    
    Why? Cause a uint16_t's max value is 65535 (less than 100000). Sure we could
    have just lowered the UIFC max number of config items to 65535, but that would
    have been too easy. And why are these compared-with values of type uint16_t to
    begin with? Because most ctrl/*.cnf lists (of configuration items) were
    limited to 65535 entries cause ... 16-bit DOS, historically. Now that *.cnf
    files aren't used, we could just increase these scfg_t.*_total type sizes from
    16 to 32-bits, yeah? The result is this commit.
    
    I went to (signed) int so we could still keep -1 as the special illegal
    sub/dir num value (e.g. INVALID_SUB, which is sometimes used to indicate the
    email message base). Theoretically, 2 billion configuration items could be
    supported in these lists, but SCFG will limit you to 100000 anyway. So there's
    a whole lot of s/uint/int in this commit.
    
    I'd be very surprised if this doesn't result in some new GCC/Clang warnings,
    but at least the old "comparison of constant 100000" warnings are now gone!
    2b087b8b
    History
    <Deuce> ... billion-and-one result of comparison of constant 100000 warnings.
    Rob Swindell authored
    So Clang-FreeBSD was warning (in compiles of scfg/scfg*.c by Deuce):
    result of comparison of constant 100000 with expression of type 'uint16_t'
    (aka 'unsigned short') is always true
    
    Why? Cause a uint16_t's max value is 65535 (less than 100000). Sure we could
    have just lowered the UIFC max number of config items to 65535, but that would
    have been too easy. And why are these compared-with values of type uint16_t to
    begin with? Because most ctrl/*.cnf lists (of configuration items) were
    limited to 65535 entries cause ... 16-bit DOS, historically. Now that *.cnf
    files aren't used, we could just increase these scfg_t.*_total type sizes from
    16 to 32-bits, yeah? The result is this commit.
    
    I went to (signed) int so we could still keep -1 as the special illegal
    sub/dir num value (e.g. INVALID_SUB, which is sometimes used to indicate the
    email message base). Theoretically, 2 billion configuration items could be
    supported in these lists, but SCFG will limit you to 100000 anyway. So there's
    a whole lot of s/uint/int in this commit.
    
    I'd be very surprised if this doesn't result in some new GCC/Clang warnings,
    but at least the old "comparison of constant 100000" warnings are now gone!
getnode.cpp 17.50 KiB
/* Synchronet node information retrieval 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"
#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 */
/****************************************************************************/
int sbbs_t::getnodedat(uint number, node_t *node, bool lockit)
{
	char	str[MAX_PATH+1];
	int		rd=sizeof(node_t);
	int		count;

	if(node==NULL || number<1)
		return(-1);

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

	if(node!=&thisnode)
		memset(node,0,sizeof(node_t));
	SAFEPRINTF(str,"%snode.dab",cfg.ctrl_dir);
	pthread_mutex_lock(&nodefile_mutex);
	if(nodefile==-1) {
		if((nodefile=nopen(str,O_RDWR|O_DENYNONE))==-1) {
			errormsg(WHERE,ERR_OPEN,str,O_RDWR|O_DENYNONE);
			pthread_mutex_unlock(&nodefile_mutex);
			return(errno); 
		}
	}
	else
		utime(str,NULL);		/* NFS fix... utime() forces a cache refresh */

	number--;	/* make zero based */
	for(count=0;count<LOOP_NODEDAB;count++) {
		if(count)
			mswait(100);
		if(lockit && lock(nodefile,(long)number*sizeof(node_t),sizeof(node_t))!=0) {
			unlock(nodefile,(long)number*sizeof(node_t),sizeof(node_t));
			continue;
		}
		lseek(nodefile,(long)number*sizeof(node_t),SEEK_SET);
		rd=read(nodefile,node,sizeof(node_t));
		if(rd!=sizeof(node_t))
			unlock(nodefile,(long)number*sizeof(node_t),sizeof(node_t));
		if(rd==sizeof(node_t))
			break;
	}
	if(!lockit && cfg.node_misc&NM_CLOSENODEDAB) {
		close(nodefile);
		nodefile=-1;
	}

	if(count==LOOP_NODEDAB) {
		errormsg(WHERE,rd==sizeof(node_t) ? ERR_LOCK : ERR_READ,"node.dab",number+1);
		if(nodefile!=-1)
			close(nodefile);
		nodefile=-1;
		pthread_mutex_unlock(&nodefile_mutex);
		return(-2);
	}
	pthread_mutex_unlock(&nodefile_mutex);
	if(count>(LOOP_NODEDAB/2)) {
		SAFEPRINTF2(str,"NODE.DAB (node %d) COLLISION - Count: %d"
			,number+1, count);
		logline(LOG_WARNING,"!!",str); 
	}

	return(0);
}

static int getpagingnode(scfg_t* cfg)
{
	for(int i = 1; i <= cfg->sys_nodes; i++) {
		node_t node;
		if(i == cfg->node_num)
			continue;
		if(getnodedat(cfg, i, &node, FALSE, NULL) == 0 && node.action == NODE_PAGE && node.aux == cfg->node_num)
			return i;
	}
	return 0;
}

/****************************************************************************/
/* 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(bool clearline)
{
	char	str[256],today[32];
	int		atr=curatr;

	if(nodesync_inside || !online) 
		return;
	nodesync_inside=1;

	if(thisnode.action!=action) {
		if(getnodedat(cfg.node_num,&thisnode,true)==0) {
			thisnode.action=action;
			putnodedat(cfg.node_num,&thisnode); 
		}
	}

	criterrs=thisnode.errors;

	if(sys_status&SS_USERON) {

		if(thisnode.status==NODE_WFC) {
			lprintf(LOG_ERR, "Node %d NODE STATUS FIXUP", cfg.node_num);
			if(getnodedat(cfg.node_num,&thisnode,true)==0) {
				thisnode.status=NODE_INUSE;
				thisnode.useron=useron.number;
				putnodedat(cfg.node_num,&thisnode); 
			}
		}

		if(!(sys_status&SS_NEWDAY)) {
			now=time(NULL);
			unixtodstr(&cfg,(time32_t)logontime,str);
			unixtodstr(&cfg,(time32_t)now,today);
			if(strcmp(str,today)) { /* New day, clear "today" user vars */
				sys_status|=SS_NEWDAY;	// So we don't keep doing this over&over
				resetdailyuserdat(&cfg, &useron,/* write: */true);
			} 
		}
		if(thisnode.misc&NODE_UDAT && !(useron.rest&FLAG('G'))) {   /* not guest */
			getuserdat(&cfg, &useron);
			if(getnodedat(cfg.node_num,&thisnode,true)==0) {
				thisnode.misc&=~NODE_UDAT;
				putnodedat(cfg.node_num,&thisnode); 
			}
		}
		if(!(sys_status&SS_MOFF)) {
			if(thisnode.misc&NODE_MSGW)
				getsmsg(useron.number, clearline); 	/* getsmsg clears MSGW flag */
			if(thisnode.misc&NODE_NMSG)
				getnmsg(clearline);					/* getnmsg clears NMSG flag */
		}
	}

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

	if(thisnode.misc&NODE_INTR) {
		bputs(text[NodeLocked]);
		logline(LOG_NOTICE,nulstr,"Interrupted");
		hangup();
		nodesync_inside=0;
		return; 
	}

	if(thisnode.misc&NODE_LCHAT) { // pulled into local chat with sysop
		saveline();
		privchat(true);
		restoreline();
	}

	if(thisnode.misc&NODE_FCHAT) { // forced into private chat
		int n = getpagingnode(&cfg);
		if(n) {
			uint save_action = action;
			saveline();
			privchat(true, n);
			action = save_action;
			restoreline();
		}
		if(getnodedat(cfg.node_num, &thisnode, true)==0) {
			thisnode.action = action;
			thisnode.misc &= ~NODE_FCHAT;
			putnodedat(cfg.node_num, &thisnode); 
		}
	}
		
	if(sys_status&SS_USERON && memcmp(&nodesync_user,&useron,sizeof(user_t))) {
		getusrdirs();
		getusrsubs();
		memcpy(&nodesync_user,&useron,sizeof(nodesync_user)); 
	}

	if(sys_status&SS_USERON && online && (timeleft/60)<(5-timeleft_warn)
		&& !SYSOP) {
		timeleft_warn=5-(timeleft/60);
		if(!(sys_status&SS_MOFF)) {
			attr(LIGHTGRAY);
			bprintf(text[OnlyXminutesLeft]
				,((ushort)timeleft/60)+1,(timeleft/60) ? "s" : nulstr); 
		}
	}

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

/****************************************************************************/
/* Prints short messages waiting for this node, if any...                   */
/****************************************************************************/
int sbbs_t::getnmsg(bool clearline)
{
	char	str[MAX_PATH+1], *buf;
	int		file;
	long	length;

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

	SAFEPRINTF2(str,"%smsgs/n%3.3u.msg",cfg.data_dir,cfg.node_num);
	if(flength(str)<1L)
		return(0);
	if((file=nopen(str,O_RDWR))==-1) {
		/**
			errormsg(WHERE,ERR_OPEN,str,O_RDWR);
		**/
		return(errno); 
	}
	length=(long)filelength(file);
	if(length <= 0) {
		close(file);
		return(0); 
	}
	if((buf=(char *)malloc(length+1))==NULL) {
		close(file);
		errormsg(WHERE,ERR_ALLOC,str,length+1);
		return(-1); 
	}
	if(read(file,buf,length)!=length) {
		close(file);
		free(buf);
		errormsg(WHERE,ERR_READ,str,length);
		return(errno); 
	}
	int retval = chsize(file,0L);
	close(file);
	buf[length]=0;

	if(clearline)
		this->clearline();
	else if(column)
		CRLF; 
	putmsg(buf,P_NOATCODES);
	free(buf);

	return retval;
}

/****************************************************************************/
/* 'ext' must be at least 128 bytes!                                        */
/****************************************************************************/
int sbbs_t::getnodeext(uint number, char *ext)
{
    char	str[MAX_PATH+1];
    int		rd,count;
	if(!number || number>cfg.sys_nodes) {
		errormsg(WHERE,ERR_CHK,"node number",number);
		return(-1); 
	}

	SAFEPRINTF(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);
		return(errno); 
	}

	number--;   /* make zero based */
	for(count=0;count<LOOP_NODEDAB;count++) {
		if(count)
			mswait(100);
		if(lock(node_ext,(long)number*128L,128)!=0) 
			continue; 
		lseek(node_ext,(long)number*128L,SEEK_SET);
		rd=read(node_ext,ext,128);
		unlock(node_ext,(long)number*128L,128);
		if(rd==128)
			break;
	}
	close(node_ext);
	node_ext=-1;


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

	return(0);
}


/****************************************************************************/
/* Prints short messages waiting for 'usernumber', if any...                */
/* then truncates the file.                                                 */
/****************************************************************************/
int sbbs_t::getsmsg(int usernumber, bool clearline)
{
	char	*buf;
	node_t	node;
	int		i;

	for(i=1;i<=cfg.sys_nodes;i++) {	/* clear msg waiting flag */
		if(getnodedat(i,&node,false) != 0 || node.useron != usernumber)
			continue;
		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); 
		} 
	}

	if((buf = readsmsg(&cfg, usernumber)) == NULL)
		return -1;
	getnodedat(cfg.node_num,&thisnode,0);
	if(clearline)
		this->clearline();
	else
		if(column)
			CRLF;
	putmsg(buf,P_NOATCODES);
	free(buf);

	return(0);
}

/****************************************************************************/
/* 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)
{
    int		i,j;
    node_t	node;

	if(cfg.whosonline_mod[0] != '\0') {
		return exec_bin(cfg.whosonline_mod, &main_csi);
	}

	CRLF;
	bputs(text[NodeLstHdr]);
	for(j=0,i=1;i<=cfg.sys_nodes && i<=cfg.sys_lastnode;i++) {
		getnodedat(i,&node,false);
		if(i==cfg.node_num) {
			if(listself)
				printnodedat(i,&node);
			continue; 
		}
		if(node.status==NODE_INUSE || (SYSOP && node.status==NODE_QUIET)) {
			printnodedat(i,&node);
			if(!lastnodemsg)
				lastnodemsg=i;
			j++; 
		} 
	}
	if(!j)
		bputs(text[NoOtherActiveNodes]);
	return(j);
}

void sbbs_t::nodelist(void)
{
	node_t	node;

	if(cfg.nodelist_mod[0] != '\0') {
		exec_bin(cfg.nodelist_mod, &main_csi);
		return;
	}

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

static char* node_connection_desc(sbbs_t* sbbs, ushort conn, char* str)
{
	switch(conn) {
		case NODE_CONNECTION_LOCAL:
			return (char*)" Locally";	/* obsolete */
		case NODE_CONNECTION_TELNET:
			return sbbs->text[NodeConnectionTelnet];
		case NODE_CONNECTION_RLOGIN:
			return sbbs->text[NodeConnectionRLogin];
		case NODE_CONNECTION_SSH:
			return sbbs->text[NodeConnectionSSH];
		case NODE_CONNECTION_RAW:
			return sbbs->text[NodeConnectionRaw];
		default:
			sprintf(str,sbbs->text[NodeConnectionModem],conn);
			break;
	}

	return str;
}

/****************************************************************************/
/* Displays the information for node number 'number' contained in 'node'    */
/****************************************************************************/
void sbbs_t::printnodedat(uint number, node_t* node)
{
    int		i;
    char	hour,mer[3];
	char 	tmp[512];

	attr(cfg.color[clr_nodenum]);
	bprintf("%3d  ",number);
	attr(cfg.color[clr_nodestatus]);
	switch(node->status) {
		case NODE_WFC:
			bputs(text[NodeStatusWaitingForCall]);
			break;
		case NODE_OFFLINE:
			bputs(text[NodeStatusOffline]);
			break;
		case NODE_NETTING:
			bputs("Networking");	/* obsolete */
			break;
		case NODE_LOGON:
			bputs(text[NodeStatusLogon]);
			bputs(node_connection_desc(this, node->connection, tmp));
			break;
		case NODE_LOGOUT:
			bprintf(text[NodeStatusLogout]
				,(node->misc&NODE_ANON) && !SYSOP ? text[UNKNOWN_USER] : username(&cfg,node->useron,tmp));
			break;
		case NODE_EVENT_WAITING:
			bputs(text[NodeStatusEventWaiting]);
			break;
		case NODE_EVENT_LIMBO:
			bprintf(text[NodeStatusEventLimbo],node->aux);
			break;
		case NODE_EVENT_RUNNING:
			bputs(text[NodeStatusEventRunning]);
			break;
		case NODE_NEWUSER:
			bputs(text[NodeStatusNewUser]);
			bputs(node_connection_desc(this, node->connection, tmp));
			break;
		case NODE_QUIET:
			if(!SYSOP) {
				bputs(text[NodeStatusWaitingForCall]);
				break; 
			}
		case NODE_INUSE:
			if(node->misc&NODE_EXT) {
				getnodeext(number,tmp);
				bputs(tmp);
				break; 
			}
			attr(cfg.color[clr_nodeuser]);
			if(node->misc&NODE_ANON && !SYSOP)
				bputs(text[UNKNOWN_USER]);
			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;
						if(SYSOP || chk_ar(cfg.xtrn[i]->ar,&useron,&client))
							bputs(cfg.xtrn[node->aux-1]->name);
						else
							bputs("external program"); 
					}
					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)
								bprintf(" %s",unpackchatpass(tmp,node)); 
						} 
					}
					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:
					bputs(ultoa(node->action,tmp,10));
					break;  }
			bputs(node_connection_desc(this, node->connection, tmp));
			if(node->action==NODE_DLNG) {
				if(cfg.sys_misc&SM_MILITARY) {
					hour=node->aux/60;
					mer[0]=0; 
				}
				else if((node->aux/60)>=12) {
					if(node->aux/60==12)
						hour=12;
					else
						hour=(node->aux/60)-12;
					strcpy(mer,"pm"); 
				}
				else {
					if((node->aux/60)==0)    /* 12 midnite */
						hour=12;
					else hour=node->aux/60;
					strcpy(mer,"am"); 
				}
				bprintf(" ETA %02d:%02d %s"
					,hour,node->aux%60,mer); 
			}
			break; 
	}
	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)))
			outchar('M');
		if(node->misc&(i&(NODE_NMSG)))
			outchar('N');
		if(node->misc&(i&NODE_POFF))
			outchar('P');
		outchar(')'); 
	}
	if(SYSOP && ((node->misc
		&(NODE_ANON|NODE_UDAT|NODE_INTR|NODE_RRUN|NODE_EVENT|NODE_DOWN|NODE_LCHAT|NODE_FCHAT))
		|| 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->misc&NODE_EVENT)
			outchar('E');
		if(node->misc&NODE_DOWN)
			outchar('D');
		if(node->misc&NODE_LCHAT)
			outchar('C');
		if(node->misc&NODE_FCHAT)
			outchar('F');
		if(node->status==NODE_QUIET)
			outchar('Q');
		outchar(']'); 
	}
	if(node->errors && SYSOP) {
		attr(cfg.color[clr_err]);
		bprintf(" %d error%c",node->errors, node->errors>1 ? 's' : '\0' ); 
	}
	attr(LIGHTGRAY);
	CRLF;
}

uint sbbs_t::count_nodes(bool self)
{
	uint count = 0;

	for(int i=1; i<=cfg.sys_nodes && i<=cfg.sys_lastnode; i++) {
	    node_t	node;
		if(getnodedat(i, &node, false) != 0)
			continue;
		if(!self && i==cfg.node_num)
			continue;
		if(node.status != NODE_INUSE)
			continue;
		count++;
	}
	return count;
}