/*
 *
 * bbd.c 
 * BIG BROTHER DAEMON PROGRAM
 * Sean MacGuire - The MacLawran Group Inc.
 * Version 1.4
 * Mar 05th, 2000
 *
 * This program is Copyright (c) 1997-2000
 * The MacLawran Group Inc.
 * All Rights Reserved
 *
 * This daemon just listens on a port, accepts single lines of input
 * and handles them.  It doesn't send any acknowledgements back...
 *
 * Two types of actions are possible, depending on the content we get:
 *
 *	1.	Input for the display monitor
 *	2.	Request to send a page to the admin
 */


#include "bb.h"		/* THE BIG BROTHER INCLUDE FILE */
#include <errno.h>
#include <sys/stat.h>
#include <ctype.h>
#include <sys/time.h>
#include <utime.h>	/* SMM NEW */

struct in_addr securedb[MAXLINE];	/* SECURITY DB LIVES HERE */

char bblogdir[MAXLINE];		/* DIRECTORY TO PUT LOG FILES INTO */
char bbhistdir[MAXLINE];	/* DIRECTORY TO PUT HISTORY FILES INTO */
char bbhtmldir[MAXLINE];	/* DIRECTORY TO PUT HTML FILES INTO */
char bbwebdir[MAXLINE];		/* DIRECTORY WHERE HTML STATUS PAGE's FOOTER & HEADER CAN BE FOUND */
char bbpid[MAXLINE];		/* BBD PID FILE */
char security[MAXLINE];		/* WHERE THE BBD SECURITY FILE LIVES */
char pager[MAXLINE];		/* BB-PAGER PROGRAM LOCATION */
char acker[MAXLINE];		/* BB-ACK PROGRAM LOCATION */
char bb[MAXLINE];		/* BB  PROGRAM LOCATION */
char *bbpage;			/* IP ADDRESS OF BB PAGER */
char savelogger[MAXLINE];	/* savelog.sh  PROGRAM LOCATION */
char bbdisable[MAXLINE];	/* bb-disable.sh  PROGRAM LOCATION */
char bbenable[MAXLINE];		/* bb-enable.sh  PROGRAM LOCATION */
char bbdisabledir[MAXLINE];	/* DIRECTORY TO PUT DISABLED HOSTS INTO */
char allhistname[MAXLINE];	/* NAME OF FILE THAT CONTAINS EVENTS LOG SUMMARY FOR ALL LOG EVENTS */
char bbdatadir[MAXLINE];	/* DIRECTORY TO PUT DATA FILES INTO */
char bbnotesdir[MAXLINE];	/* DIRECTORY TO PUT NOTES FILES INTO */

char *pagelevels;
char *savestatuslog;
char *bbweb;
char *bbhosthistlog;		/* SAVE HISTORICAL LOG PER HOST ? */
char *bballhistlog;		/* SAVE HISTORICAL LOG FOR ALL HOSTS ? */

char *ipdotaddr;		/* IP address of the end connection in dot notation */

int purpleoffset;

void loadfiles()
{
extern int getheader(), getfooter();

	debug("in loadfiles\n");

	getheader(bbwebdir,NULL);
	getfooter(bbwebdir,NULL);

	/*
	 * BB SECURITY ENHANCEMENT
	 * ALLOW BB TO RESTRICT ACCESS BASED ON A LIST OF 
	 * NUMERIC HOSTS/MASKS LOCATED IN etc/security
	 */
	getsecurity();			/* GET VALID HOSTS AND MASKS */

	/* Reset the signal */
	signal(SIGHUP, (void *)loadfiles);
}

void bb_enable(req)
char *req;
{
char enable_cmd[MAXLINE];

	/* Double check that there's no buffer overflow */
	if( (strlen(bbenable) + strlen(req) + 2) >= MAXLINE ) {
		debug("<%s> ENABLE MSG IS TOO LARGE\n");
		return;
	}

	sprintf(enable_cmd,"%s \"%s\"",bbenable,req);
	system(enable_cmd);
}

void bb_disable(req)
char *req;
{
char disable_cmd[MAXLINE];

	/* Double check that there's no buffer overflow */
	if( (strlen(bbdisable) + strlen(req) + 2) >= MAXLINE ) {
		debug("<%s> DISABLE MSG IS TOO LARGE\n");
		return;
	}

	sprintf(disable_cmd,"%s \"%s\"",bbdisable,req);
	system(disable_cmd);
}

void bb_online(req)
char *req;
{
char host[MAXLINE];
char filename[MAXLINE];

	if( strlen(req) > (MAXLINE - 2) ) {
		debug("<%s> ONLINE MSG IS TOO LARGE\n");
		return;
	}

	sscanf(req,"%s",host);
	sprintf(filename,"%s/%s",bbdisabledir,host);

	unlink(filename);
}

void bb_offline(req)
char *req;
{

}

void bbsave(req,type)
char *req,*type;
{
FILE *fp;
char filemode[5], *dataptr;
char filename[MAXLINE],*fname;
char filepath[MAXLINE];
int rc;

	if( strcmp(type,"data") == 0 ) {
		/* For "data" we append */
		strcpy(filemode,"a+");
	}
	else if( strcmp(type,"notes") == 0 ) {
		/* For "notes" overwrite */
		strcpy(filemode,"w+");
	}
	else {
		/* It ain't "data" or "notes", get out */
		return;
	}

	/* Check filename validity */
	/* Remove the basename from it */
	filename[0] = '\0';
	rc = sscanf(req,"%s",filename);
	if( rc != 1 ) {
		return;
	}

	fname = (char *) strrchr(filename,'/');
	if( fname == NULL ) {
		fname = filename;	/* Set to filename */
	}
	else {
		fname++;		/* Skip slash */
	}

	/* Name can't start with '.' , '-' or non-print character */
	if( *fname == '.' || *fname == '-' || !isprint(*fname) ) {
		return;
	}

	/* Do we have any real data ? */
	dataptr = (char *) strstr(req,filename);
	if( dataptr == NULL ) {
		/* This should never happen */
		return;
	}

	/* Position at character right after the name */
	dataptr += strlen(filename);

	while( isspace(*dataptr) ) {
		dataptr++;
	}

	/* End of string == No data */
	if( *dataptr == '\0' ) {
		return;
	}

	if( strcmp(type,"data") == 0 ) {
		/* For "data" we append */
		sprintf(filepath,"%s/%s",bbdatadir,fname);
	}
	else if( strcmp(type,"notes") == 0 ) {
		/* For "notes" overwrite */
		sprintf(filepath,"%s/%s",bbnotesdir,fname);
	}

	/* Try to open the file */
	if( (fp=fopen(filepath,filemode)) == NULL ) {
		return;
	}

	fprintf(fp,"%s\n",dataptr);

	fclose(fp);

	return;
}

void bb_utime(filename,timestamp)
char *filename;
time_t timestamp;
{
struct utimbuf tb;

	tb.actime = timestamp;
	tb.modtime = timestamp;

	utime(filename, &tb);
}

/* RETURN VALUES:
 *
 * 0 - not disabled
 * 1 - disabled
 *
 */

int bb_isdisabled(filename)
char *filename;
{
char disfname[MAXLINE];
char logfname[MAXLINE];
struct stat statbuf;
int statres;
time_t timenow;

	/* TRY TO STAT THE EVENT IN THE /disabled DIRECTORY */
       	sprintf(disfname, "%s/%s", bbdisabledir, filename);

	debug("disfname: %s\n",disfname);

	statres=stat(disfname, &statbuf);

	time(&timenow);			/* GET THE TIME */

	/* IS FILE IN disabled/ DIRECTORY */
	if( statres == 0 ) {
		debug("%s exists !\n",disfname);
		/* YUP, HAS IT EXPIRED ? */
		if( statbuf.st_mtime < timenow ) {
			/* YUP, REMOVE IT, IT'S NOT NEEDED ANYMORE */
			debug("%s has expired, remoing it !\n",disfname);
			unlink(disfname);
		}
		else {
			/* DISABLING STILL IN EFFECT DON'T DO ANYTHING ELSE */
			return(1);
		}
	}
	return(0);
}

main(argc, argv)
int argc;
char *argv[];
{
 	int status, sockfd, newsockfd, child, port, len, ttllen;
        struct sockaddr_in serv_addr, cli_addr;
	void timeout();
	static char linebuf[MAXLINE];
	static char msgbuf[MAXBUF];		/* INCOMING MESSAGE BUFFER */
	int on=1;
	struct in_addr inc;
	char *bbhome;			/* WHERE ALL BB LIVES */
	FILE *bbf;
	char *purpledelay;

	debug("*** Starting bbd ***\n");

	bbpage=(char *)getenv("BBPAGE");
	if (!bbpage) {
		fprintf(stderr,"bbd: BBPAGE is not set in the environment!\n");
		fprintf(stderr,"Make sure you're running bbd from runbb.sh!\n");
		exit(1);
	}

	bbhome=(char *)getenv("BBHOME");
	if (bbhome) {
		len=strlen(bbhome);
		if (len > MAXLINE - 50) {		/* CHECK THIS... */
			fprintf(stderr,"bbd: BBHOME is too large - go away!\n");
			exit(1);
		}
        	sprintf(security, "%s/etc/security", bbhome);
		sprintf(bblogdir, "%s/www/logs", bbhome);
		sprintf(bbhistdir, "%s/www/hist", bbhome);
		sprintf(allhistname,"%s/allevents",bbhistdir);
		sprintf(bbhtmldir, "%s/www/html", bbhome);
		sprintf(bbwebdir, "%s/web", bbhome);
		sprintf(bbpid, "%s/tmp/BBPID", bbhome);
		sprintf(pager, "%s/bin/bb-page.sh", bbhome);
		sprintf(acker, "%s/bin/bb-doack.sh", bbhome);
		sprintf(bb, "%s/bin/bb", bbhome);
		sprintf(savelogger, "%s/bin/savelog.sh", bbhome);
		sprintf(bbdisable, "%s/bin/bb-disable.sh", bbhome);
		sprintf(bbenable, "%s/bin/bb-enable.sh", bbhome);
		sprintf(bbdisabledir, "%s/disabled", bbhome);
		sprintf(bbdatadir, "%s/www/data", bbhome);
		sprintf(bbnotesdir, "%s/www/notes", bbhome);
	}
	else {
		fprintf(stderr,"bbd: BBHOME is not set in the environment!\n");
		fprintf(stderr,"Make sure you're running bbd from runbb.sh!\n");
		exit(1);
	}

	pagelevels= (char *) getenv("PAGELEVELS");
	if ( pagelevels == NULL )
		pagelevels="";
	savestatuslog = (char *) getenv("SAVESTATUSLOG");
	if ( savestatuslog == NULL )
		savestatuslog="";
	bbweb = (char *) getenv("BBWEB");
	if ( bbweb == NULL )
		bbweb="";

	purpledelay = (char *) getenv("PURPLEDELAY");
	if ( purpledelay == NULL ) {
		purpledelay = "";
		purpleoffset = 30 * 60;
	}
	else {
		purpleoffset = 0;
		purpleoffset = atoi(purpledelay);
		if( purpleoffset <= 0 ) {
			purpleoffset = 30 * 60;
		}
		else {
			purpleoffset *= 60;
		}
	}

	bbhosthistlog = (char *) getenv("BBHOSTHISTLOG");
	if ( bbhosthistlog == NULL )
		bbhosthistlog="";

	bballhistlog = (char *) getenv("BBALLHISTLOG");
	if ( bballhistlog == NULL )
		bballhistlog="";

	port = PORT;

        debug("MAKING SOCKET CONNECTION...\n");
        if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
                fprintf(stderr, "bbd: Can't open socket\n");
		exit(1);
        }
        debug("SOCKET CONNECTED OK... : %d\n",sockfd);

	/*
	 * FINALLY FIX THE REUSE BUGLET
	 * THANKS TO Gary Mills <mills@cc.UManitoba.CA>
	 */
	(void)setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on);

        bzero((char *) &serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_addr.sin_port = htons(port);

        debug("BINDING TO SOCKET...\n");
        if (bind(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0) {
		/* DON'T HARDCODE PORT NUMBER: frank@bluegrass.net */
                fprintf(stderr,"bbd: Can't bind to port %d - ", port);
		fprintf(stderr, "is bbd already running?\n");
		exit(2);
        }
        debug("SOCKET BOUND OK...\n");

        /*
	 * NOW THE PARENT EXITS AND THE CHILD DETACHES FROM THE CONTROLLING TTY
	 * SET UP TO IGNORE SIGNALS SENT BY EXITING CHILDREN
         */

        if ((child = fork()) != 0) {
        	debug("TRYING TO OPEN BBFILE %s\n", bbpid);
        	bbf = fopen(bbpid, "w");
		if (bbf) {
			fprintf(bbf, "%d ", child);
			fclose(bbf);
		}
		exit(0);             /* PARENT EXITS */
	}

	setsid();					/* RELEASE TTY */

#ifndef ZOMBIE
        signal(SIGCHLD, SIG_IGN);
#endif /* NO ZOMBIE */

	/* Load up the standard header/footer file */
	signal(SIGHUP, (void *)loadfiles);
	loadfiles();

	/*
	 * NOW SET UP THE LISTENING LOOP
         */
        listen(sockfd, 250);

        for ( ;; ) {

		debug("about to accept()\n");
                len = sizeof(cli_addr);
                newsockfd = accept(sockfd,(struct sockaddr*)&cli_addr,&len);
		/*
		 * THANKS TO Marc L. Allen (allen@chesapeake.rps.slb.com)
		 */

                if (newsockfd < 0 ) {
        		debug("accept(): %d : %d\n", newsockfd, errno);
                        if (errno == EINTR) continue;
			else {
				/* 
				 * Jac Kersing *kersing@the-box.com)
				 * Just retry. Might be a killed client 
				 */
				sleep(5);
				/* On some OS you must reset */
				signal(SIGHUP, (void *)loadfiles);
				continue;
			}
		}

  		/*
                 * SMM BBD SECURITY CHECK
                 */
                inc.s_addr = cli_addr.sin_addr.s_addr;
		ipdotaddr = (char *)inet_ntoa( cli_addr.sin_addr );
                debug("INCOMING CONNECTION FROM: %u\n", inc.s_addr);
		if (checksecurity(inc.s_addr) == 0) { /* FAIL */
                	debug("SECURITY FAILED: %u\n");
			close(newsockfd);
			continue;
		}

		/*
		 * NOW FORK OFF AND LET THE CHILD HANDLE THE REQUEST
		 */
 		if ((child = fork()) < 0) die("fork error");

		if (child != 0) {			/* THE PARENT */
			close(newsockfd);
#ifdef ZOMBIE
			debug("WAIT FOR CHILD EXIT...\n");
			wait(&status);
			debug("CLEANED UP CHILD: %d\n", status);
#endif /* ZOMBIE */
		}
		else {					/* THE CHILD */
			/* No funnies if someone tries SIGHUPping the child */
			signal(SIGHUP, SIG_IGN);
			close(sockfd); 
			/*
			 * NOW GET THE LINE FROM THE CLIENT
			 * AND HANDLE THE REQUEST...
			 * IGNORE BAD DATA...
			 * 1.07c ADDED SUPPORT FOR MESSAGES UP TO 8K
			 */
			ttllen = 0;
			memset(msgbuf,'\0',sizeof(msgbuf));
			memset(linebuf,'\0',sizeof(linebuf));
			debug("NEW INCOMING MESSAGE\n");

			/*
			 * THIS IS TO HANDLE CONNECTIONS THAT GET
			 * "STUCK" - MOSTLY NT TALKING TO SOLARIS
			 * THANKS TO jrivel@dti.net 
			 */
			signal(SIGALRM, (void *)timeout);
			alarm(5);	/* SMM */
			while ( (len = recv(newsockfd, linebuf, MAXLINE - 1, 0)) && (ttllen != -1)) {
				alarm(5);	/* SMM */
				linebuf[len]='\0';		/* FOR ANDREY */
				debug("MESSAGE LEN: %d MSG: [%s]\n", len, linebuf);
				ttllen += len;
				if (ttllen > (MAXBUF-1)) {
					ttllen -= len;
					len = MAXBUF-1 - ttllen - 30;
					if ( len > 0 ) {
						linebuf[len] = '\0';
						strcat(msgbuf,linebuf);
					}
					strcat(msgbuf, "\n\n... DATA TRUNCATED ...\n"); /* 25 chars ! */
					break;
				}
				else {
					strcat(msgbuf, linebuf);
					memset(linebuf,'\0',sizeof(linebuf));
				}
			}
			if (ttllen != 0) { 
				do_bb(msgbuf);
			}
			close(newsockfd);
                	exit(0);
		}
	}
}

/*
 * HANDLE INCOMING BIG BROTHER REQUESTS
 * PARSE THE LINE WE'VE RECEIVED AND HANDLE IT
 */
do_bb(req)
char *req;
{
char filename[MAXLINE];

	debug("REC'D: %s\n", req);
	/*
	 * IF THE REQUEST STARTS WITH "page" THEN CALL THE PAGING SCRIPT
	 * IF THE REQUEST STARTS WITH "status" THEN LOG THE MESSAGE
	 * IF THE REQUEST STARTS WITH "summary" THEN LOG THE MESSAGE
	 * IF THE REQUEST STARTS WITH "ack" THEN PROCEED WITH AN ACKNOWLEDGEMENT
	 * IF THE REQUEST STARTS WITH "enable" THEN ENABLE THE GIVEN HOSTS
	 * IF THE REQUEST STARTS WITH "disable" THEN DISABLE THE GIVEN HOSTS
	 * IF THE REQUEST STARTS WITH "offline" THEN SET UP THE OFFLINED HOST
	 * IF THE REQUEST STARTS WITH "online" THEN SET UP THE ONLINED HOST
	 * IF THE REQUEST STARTS WITH "data" THEN APPEND DATA TO THE GIVEN FILE NAME
	 * IF THE REQUEST STARTS WITH "notes" THEN REPLACE THE NOTES FILE IN www/notes
	 * IF THE REQUEST STARTS WITH "test" THEN DO NOTHING (MAYBE LATER DO SOMETHING)
	 * OTHERWISE IGNORE IT (FOR SAFETY SAKE)
	 */
	if (strncmp(req, "page ", 5) == 0) {
		req += 5;			/* JUMP OVER page */
		debug("CALL PAGER: %s\n", req);
		/* Minor bugs spotted by */
		/* Andrew Dalgleish <andrewd@axonet.com.au> */
		if ( *req ) {
			sscanf(req,"%s",filename);
			if( !bb_isdisabled(filename) ) {
				/* Make sure pager was set */
				if (strcmp("/bin/bb-page.sh",pager)) {
					execlp(pager, pager, req, (char *)NULL);
					die("bbd: Could not execute bb-page.sh\n");
				}
				else die("PAGE FAILED!\n");
			}
		}
		else debug("NULL PAGE MSG IGNORED\n");
	}
	else if (strncmp(req, "status", 6) == 0) {
		req += 6;			/* JUMP OVER status */
		if (*req == ' ') req++;		/* SKIP THE SPACE , IF IT'S NOT IN THE status+<delay> FORMAT*/
		/* Minor bugs spotted by */
		/* Andrew Dalgleish <andrewd@axonet.com.au> */
		if ( *req ) {
			debug("LOG STATUS MSG: %s\n", req);
			bblog(req,"status");		/* LOG THE REQUEST */
		}
		else debug("NULL STATUS MSG IGNORED\n");
	}
	else if (strncmp(req, "summary ", 8) == 0) {
		req += 8;			/* JUMP OVER summary */
		/* Minor bugs spotted by */
		/* Andrew Dalgleish <andrewd@axonet.com.au> */
		if ( *req ) {
			debug("LOG SUMMARY MSG: %s\n", req);
			bblog(req,"summary");		/* LOG THE REQUEST */
		}
		else debug("NULL SUMMARY MSG IGNORED\n");
	}
	else if (strncmp(req, "ack ", 4) == 0) {
		req += 4;                       /* JUMP OVER ack */
		debug("DO ACK: %s\n", req);
		/* Minor bugs spotted by */
		/* Andrew Dalgleish <andrewd@axonet.com.au> */
		if ( *req ) {
			/* Make sure acker is set */
			if (strcmp("/bin/bb-doack.sh",acker)) {
				execlp(acker, acker, req, (char *)NULL);
				die("bbd: Could not execute bb-doack.sh\n");
			}
			else die("ACK FAILED!\n");
		}
		else debug("NULL ACK MSG IGNORED\n");
	}
#ifdef ENABLE_DISABLE
	else if ( strncmp(req, "enable ", 7) == 0 ) {
		req += 7;                       /* JUMP OVER enable */
		debug("DO ENABLE: %s\n", req);
		/* Minor bugs spotted by */
		/* Andrew Dalgleish <andrewd@axonet.com.au> */
		if ( *req && (strlen(req) < (MAXLINE-strlen(bbenable))) ) {
			/* Set host(s) to enable */
			bb_enable(req);
		}
		else if( strlen(req) < (MAXLINE-strlen(bbenable)) ) {
			debug("<%s> ENABLE MSG IS TOO LARGE\n");
		}
		else debug("NULL ENABLE MSG IGNORED\n");
	}
	else if ( strncmp(req, "disable ", 8) == 0 ) {
		req += 8;                       /* JUMP OVER disable */
		debug("DO DISABLE: %s\n", req);
		/* Minor bugs spotted by */
		/* Andrew Dalgleish <andrewd@axonet.com.au> */
		if ( *req && (strlen(req) < (MAXLINE-strlen(bbdisable))) ) {
			/* Set host(s) to disable */
			bb_disable(req);
		}
		else if( strlen(req) < (MAXLINE-strlen(bbdisable)) ) {
			debug("<%s> DISABLE MSG IS TOO LARGE\n");
		}
		else debug("NULL DISABLE MSG IGNORED\n");
	}
	else if ( strncmp(req, "online ", 7) == 0 ) {
		req += 7;                       /* JUMP OVER online */
		debug("DO ONLINE: %s\n", req);
		/* Minor bugs spotted by */
		/* Andrew Dalgleish <andrewd@axonet.com.au> */
		if ( *req && (strlen(req) < (MAXLINE-2)) ) {
			/* Set host(s) to enable */
			bb_online(req);
		}
		else debug("NULL ONLINE MSG IGNORED\n");
	}
	else if ( strncmp(req, "offline ", 8) == 0 ) {
		req += 8;                       /* JUMP OVER offline */
		debug("DO OFFLINE: %s\n", req);
		/* Minor bugs spotted by */
		/* Andrew Dalgleish <andrewd@axonet.com.au> */
		if ( *req && (strlen(req) < (MAXLINE-2)) ) {
			/* Set host(s) to enable */
			bblog(req,"offline");
		}
		else debug("NULL OFFLINE MSG IGNORED\n");
	}
#endif
#ifdef DATAMSG
	else if (strncmp(req, "data ", 5) == 0) {
		/* "data <filename> <data>" */
		req += 5;			/* JUMP OVER data */
		bbsave(req,"data");
		debug("GOT DATA: %s\n",req);
	}
#endif
#ifdef NOTESMSG
	else if (strncmp(req, "notes ", 6) == 0) {
		/* "data <filename> <data>" */
		req += 6;			/* JUMP OVER notes */
		bbsave(req,"notes");
		debug("GOT NOTES: %s\n",req);
	}
#endif
	else if (strncmp(req, "test", 4) == 0) {
		req += 4;
		debug("GOT TEST\n");
	}
	else
		debug("IGNORE MESSAGE: %s\n", req);
}

/*
 * LOG A BIG BROTHER STATUS MESSAGE
 * THE MESSAGE GOES INTO THE DIRECTORY SPECIFIED BY BBLOG
 * MESSAGE FORMAT IS:
 * 	machine.area status message
 * 	summaryname.area summary message
 *
 * THE MESSAGE IS PLACED IN A FILE NAMED machine.area
 */

bblog(msg,msgtype)
char *msg;
char *msgtype;
{
        FILE *bbfile;
        FILE *bbhist;
        FILE *bbhtml;
        int len,child, first, statres;
        char fname[MAXLINE];				/* FILENAME */
        char disfname[MAXLINE];				/* DISABLED FILENAME */
        char histname[MAXLINE];				/* HISTORY FILENAME */
        char hosthistname[MAXLINE];				/* HISTORY FILENAME */
        char htmlname[MAXLINE];				/* HTML FILENAME */
	char oldcolor[MAXLINE];				/* FOR COLOR MATCHING */
	char newcolor[MAXLINE];
	char filename[MAXLINE];
	char tmpfilename[MAXLINE];
	char dur[MAXLINE];
	char sentby[MAXLINE];
	char request[MAXLINE];
	char newcol[3],oldcol[3];
	char *hosthistnamep, *hosthistnamepp, *service;
	int state;
	time_t duration, timenow, exptime, lasttime = 0;
	long fwhere;	/* SMM */
	char validfor[MAXLINE];		/* HOW LONG IS THE MESSAGE GOOD FOR */
	int offset;			/* THE ABOVE AS A NUMBER */
	struct utimbuf *tb;		/* FOR THE ABOVE */
	double f;
	struct stat statbuf;
        char *name, *timebuf, *exptimebuf,timenowbuf[32],lasttimebuf[32];
	int savelog;
	char savelog_cmd[MAXLINE];
	char *hostsvcdata,*newmsg,*strp,*periodstrp,*bblogstatus,*recvfromstrp,*statunchstrp;
	extern char* dohostsvc();
	time_t ticks;
	char msg_offline[MAXLINE];
	char *bbdisplay,*bbpager;

        if (bblogdir || !strcmp(msgtype,"offline") ) {

		recvfromstrp= (char *) getenv("RECVFROMMSG");
		if( recvfromstrp == NULL )
			recvfromstrp = "Status message received from";

		statunchstrp= (char *) getenv("STATUNCHMSG");
		if( statunchstrp == NULL )
			statunchstrp = "Status unchanged in";

		if (*msg == '+') {
			msg++;		/* SKIP THE PLUS SIGN */
                	sscanf(msg, "%s", validfor);
                	len = strlen(validfor);       
			msg += len;
			while (isspace(*msg))
				msg++;	/* SKIP SPACE(S) */
			offset = atoi(validfor);
			offset *= 60; 	/* IN SECONDS */
			debug("VALID FOR: %s %d\n", validfor, offset);
		}
		else offset = purpleoffset;

		dur[0] = '\0';
		duration = 0;
		time(&timenow);			/* GET THE TIME */
		timebuf = ctime(&timenow);	/* CONVERT TO ASCII */
		*(timebuf + 24) = '\0';		/* LOSE THE NEWLINE */
		strcpy(timenowbuf,timebuf);

		/* offline message has filename and maxlenght of disabling */
		if( !strcmp(msgtype,"offline") ) {
			sscanf(msg, "%s %d", filename, &offset);
			offset *= 60;
			strcpy(newcolor,"blue");

        		sprintf(disfname, "%s/%s", bbdisabledir, filename);

			/* create an empty disable tag file */
        		bbfile = fopen(disfname, "w");
			if (bbfile) {
				fclose(bbfile);
			}
			else {
				return;
			}
			ticks = time(&ticks) + offset;
			bb_utime(disfname, timenow + offset);

			/* IF THIS HOST IS JUST A BBPAGER AND NOT A BBDISPLAY THEN */
			/* NOTHING ELSE IS REQUIRED */
			bbdisplay = (char *) getenv("BBDISPLAY");
			bbpager = (char *) getenv("BBPAGER");

			/* Is this host BBDISPLAY ? */
			/* Is the variable BBDISPLAY set ? */
			if( bbdisplay ) {
				/* IS BBDISPLAY set to TRUE ? */
				if( strcmp(bbdisplay,"TRUE") ) {
					/* NOPE, THEN THIS HOST IS ONLY A BBPAGER */
					/* DON'T NEED TO DO ANYTHING ELSE */
					return;
				}
			}

			exptime = timenow + offset;
			exptimebuf = ctime(&exptime);		/* CONVERT TO ASCII */
			*(exptimebuf + 24) = '\0';		/* LOSE THE NEWLINE */
			sprintf(msg_offline,"%s blue   %s    OFFLINE UNTIL %s ",filename,timenowbuf,exptimebuf);
			msg = msg_offline;

			/* Give some slack on the log file, so purple won't hit */
			/* immediately when purple hits */
			offset += purpleoffset;
		}
		else {
			sscanf(msg, "%s %s", filename, newcolor);
		}

                len = strlen(filename);       
               	if (strlen(bblogdir) + len + 2 > MAXLINE) {
                        /* too large for allocated memory, skip it */
			debug("FILENAME TOO BIG - IGNORED\n");
                        return;
                }

                /* 
		 * THANKS TO Jac Kersing <J.Kersing@the-box.com>
		 * SMM: IGNORE ATTEMPTS TO WRITE TO PARENT DIRECTORIES
		 * AND CHECK FOR BUFFER OVERFLOWS
                 */
                name = (char *)strrchr(filename,'/');
                if (name != (char *)NULL) {		/* FOUND A FUNNY */
			debug("ILLEGAL FILE NAME - IGNORED\n");
			return;				/* GO AWAY */
		}

		/*
		 * SMM BB 1.09: EVENT HISTORY AND DURATION
		 * IF THE COLOR OF AN INCOMING MESSAGE IS NOT THE SAME
		 * AS THE CURRENT STATUS, WRITE A LINE IN THE HISTORY
		 * FILE.  OTHERWISE JUST UPDATE THE LOG FILE WITH AN
		 * INDICATION OF THE DURATION OF THE CURRENT STATUS
		 */

       		sprintf(fname, "%s/%s", bblogdir, filename);

		/* Don't do hist for summaries */
		if ( !strcmp(msgtype,"summary") ) {
			goto nohistpls;
		}

        	sprintf(histname, "%s/%s", bbhistdir, filename);

		oldcolor[0]='\0';		/* FOR ROB */

        	debug("TRYING TO OPEN BBFILE %s\n", fname);
        	bbfile = fopen(fname, "r");
		if (bbfile) {
			fscanf(bbfile, "%s", oldcolor);
			fclose(bbfile);
		}

		debug("oldcolor: %s , msgtype: %s\n",oldcolor,msgtype);

		/* IF NOT AN OFFLINE MESSAGE */
		if( strcmp(msgtype,"offline") ) {
			/* IS THE EVENT DISABLED ? */
			if( bb_isdisabled(filename) ) {
				/* YES, DOES IT HAVE A STATUS FILE ? */
				if( !bbfile ) {
					/* NO, CREATE A DUMMY ONE, THE OLD ONE MIGHT */
					/* HAVE BEEN DELETED */
        				bbfile = fopen(fname, "w");
					if (bbfile) {
						sprintf(msg_offline,"blue   %s    OFFLINE UNTIL %s \n%s\n%s",timenowbuf,"undetermined",recvfromstrp,"undetermined",statunchstrp,"undertermined");
						fputs( msg_offline , bbfile);
						fclose(bbfile);
					}
				}
				return;
			}
		}
		else {
			/* If the log file was not found, we assume this is a BBPAGER */
			/* only host that received it */
			if( !bbfile ) {
				return;
			}
		}

		savelog=0;

		statres=stat(histname, &statbuf);	/* HISTORY AVAILABLE? */

		if ((strcmp(oldcolor, newcolor) != 0) || (statres == -1)) {
			savelog=1;
        		debug("TRYING TO OPEN HISTORY FILE %s\n", histname);
			if (statres == -1)		/* CREATE IT */
        			bbhist = fopen(histname, "a+");
			else				/* OPEN IT */
        			bbhist = fopen(histname, "r+");

			if (bbhist) {
				/*
				 * ICKY THINGS HERE... BACK UP OVER THE
				 * OLD TIME, AND WRITE THE DURATION IN
				 * IT'S PLACE...
				 */
				fseek(bbhist, -11L, SEEK_END);
				fwhere=ftell(bbhist);
				if (fwhere > 0) {
					fscanf(bbhist, "%ld", &lasttime);
					fseek(bbhist, -11L, SEEK_END);
					fprintf(bbhist, " %ld %ld\n%s %s  %ld\n", lasttime, timenow - lasttime, timenowbuf, newcolor, timenow);
					debug("%s: FWHERE: %ld LASTIME %ld\n", histname, fwhere, lasttime);
				}
				else {
					fprintf(bbhist, "%s %s  %ld\n", timenowbuf, newcolor, timenow);
				}
				fclose(bbhist);
			}

			if( !strcmp(bbhosthistlog,"TRUE") || !strcmp(bballhistlog,"TRUE") ) {
				/* Save the event logs per host */
				if(strlen(oldcolor)==0)
					state = -1;
				else {
					state = getcolor(newcolor) - getcolor(oldcolor);

					if( state > 0 )
						state = 2;		/* Escalated */
					else if( state < 0 )
						state = 1;		/* Recovered */
					else
						state = 0;		/* No change - if 2 colors ever are equal this is useful */
				}
				strcpy(hosthistname,histname);
				/* Point to last subdir, upper directories may contain a '.' */
				hosthistnamep = (char *)strrchr(hosthistname,'/');
				if(hosthistnamep)
					hosthistnamep++;
				else
					hosthistnamep = hosthistname;
				service = (char *) strrchr(hosthistnamep,'.');
				hosthistnamepp = hosthistnamep;
				while( (hosthistnamepp=(char *)strchr(hosthistnamepp,',')) != NULL)
					*hosthistnamepp = '.';
				timebuf = ctime(&lasttime);	/* CONVERT TO ASCII */
				*(timebuf + 24) = '\0';		/* LOSE THE NEWLINE */
				strcpy(lasttimebuf,timebuf);
				memset(newcol,'\0',sizeof(newcol));
				memset(oldcol,'\0',sizeof(oldcol));
				strncpy(newcol,newcolor,2);
				strncpy(oldcol,oldcolor,2);
				if( service ) {
					*service = '\0';			/* Terminate host name */
					service++;
					if( !strcmp(bbhosthistlog,"TRUE") ) {
						bbhist = fopen(hosthistname, "a+");
						if(bbhist) {
							fprintf(bbhist,"%s %ld %ld %ld %s %s %d\n",service,timenow,lasttime,timenow-lasttime,newcol,strlen(oldcolor)!=0 ? oldcol : "-",state);
							fclose(bbhist);
						}
					}
					if( !strcmp(bbhosthistlog,"TRUE") ) {
						bbhist = fopen(allhistname, "a+");
						if(bbhist) {
							fprintf(bbhist,"%s %s %ld %ld %ld %s %s %d\n",hosthistnamep,service,timenow,lasttime,timenow-lasttime,newcol,strlen(oldcolor)!=0 ? oldcol : "-",state);
							fclose(bbhist);
						}
					}
				}
			}

			/*
			 * NOW FORK OFF AND LET THE CHILD HANDLE THE REQUEST
			 * Send a rm_event ack message to remove the tmp/np_* tagfile
			 * associated with this event as the color if not defined in
			 * in the PAGELEVELS variable (pagelevels token is bbwarnsetup.cfg)
			 * Also used for recovery message
			*/
			if( !pagelevels || (strlen(pagelevels) == 0) )
				pagelevels = PAGELEVELSDEFAULT; 
			if ( strstr(pagelevels,oldcolor) && !strstr(pagelevels,newcolor) ) {
				if ((child = fork()) < 0) die("fork error");
				/* child does work, thanks to svangari@uu.net */
				if (child == 0) {                       /* THE CHILD */
					sprintf(request,"ack rm_event %s %d",filename,timenow-lasttime);
					execlp(bb, bb, bbpage, request, (char *)NULL);
				}
				/* Parent continues */
			}
		}
		else {
			if (statres == 0) {
				duration = timenow - statbuf.st_mtime;
			}
		}

		if (duration > 86400) {
			f = duration / (double) 86400.00;
			periodstrp= (char *) getenv("DAYS");
			if( periodstrp == NULL )
				periodstrp = "days";
			sprintf(dur, "%s %.2f %s\n", statunchstrp, f, periodstrp);
		}
		else if (duration > 3600) {
			f = duration / (double) 3600.00;
			periodstrp= (char *) getenv("HOURS");
			if( periodstrp == NULL )
				periodstrp = "hours";
			sprintf(dur, "%s %.2f %s\n", statunchstrp, f, periodstrp);
		}
		else {
			f = duration / (double) 60.00;
			periodstrp= (char *) getenv("MINS");
			if( periodstrp == NULL )
				periodstrp = "minutes";
			sprintf(dur, "%s %.2f %s\n", statunchstrp, f, periodstrp);
		}

nohistpls:
		sprintf(sentby,"%s %s\n",recvfromstrp,ipdotaddr);

        	debug("CREATING BBFILE %s\n", fname);
		/*
		 * THANKS TO: Andrey Alekseyev <fetch@muffin.arcadia.spb.ru> 
		 * More checking...
		 */
        	bbfile = fopen(fname, "w+");            /* APPEND IT */
		if (bbfile) {
        		msg += len + 1;                 /* SKIP FIRST ARGS */
        		debug("MSG IS %s....\n", msg);
        		fprintf(bbfile, "%s\n\n%s%s", msg, dur,sentby);   /* PUKE */
        		fclose(bbfile);                 /* CLOSE */

        		/* FIX THE TIMESTAMP */
			bb_utime(fname, timenow + offset);
		}
		else debug("FAILED TO OPEN STATUS FILE %s\n", bbfile);

		/* Create an HTML version of the status file */

		/* Don't do html for summaries */
		/* Thanks to holger@marzen.de */
		if ( !strcmp(msgtype,"summary") )
			 return;

		bblogstatus = (char *) getenv("BBLOGSTATUS");
		if( bblogstatus == NULL )
			bblogstatus = "STATIC";

		if( strcmp(bblogstatus,"STATIC") == 0 ) {
			strcpy(tmpfilename,filename);
			replacestr(tmpfilename,sizeof(tmpfilename),",",".");
        		sprintf(htmlname, "%s/%s.html", bbhtmldir, tmpfilename);
        		debug("CREATING HTML FILE %s\n", htmlname);
        		bbhtml = fopen(htmlname, "w");
			if (bbhtml) {
				newmsg = (char *) malloc(strlen(msg)+1024);
				if( newmsg != NULL ) {
					sprintf(newmsg,"%s\n\n%s%s",msg,dur,sentby);
					hostsvcdata = dohostsvc(newmsg,filename,bbweb,bbwebdir,timenowbuf);
					if( hostsvcdata != NULL ) {
						fwrite(hostsvcdata,strlen(hostsvcdata),1,bbhtml);
						free(hostsvcdata);
					}
					else {
						fprintf(bbhtml,"<HTML><BODY>Couldn't build the HTML status page - out of memory</BODY></HTML>\n");
					}
					free(newmsg);
				}
				else {
					fprintf(bbhtml,"<HTML><BODY>Couldn't build the HTML status page - out of memory</BODY></HTML>\n");
				}
				fclose(bbhtml);
			}
			else debug("FAILED TO OPEN HTML FILE %s\n", htmlname);
		}
		if ( savelog && savestatuslog && !strcmp(savestatuslog,"TRUE") ) {
			sprintf(savelog_cmd,"%s %s \"%s\"",savelogger,fname,timenowbuf);
			system(savelog_cmd);
		}
	}
	else die("BB LOGGING DIRECTORY UNDEFINED\n");

	debug("DONE IN bblog()\n");
}

/*
 * PUT AN ERROR INTO THE LOGFILE AND stderr AND DIE
 */
int die(msg)
char *msg;
{
        static char message[MAXLINE];
        sprintf(message, "bbd: %s\n", msg);

        syslog(LOG_DAEMON | LOG_CRIT , message);
        fprintf(stderr, message);
        exit(1);
}

/*
 * SMM: v.1.06b CLEAN THIS UP...
 * THANKS TO: Andrey Alekseyev <fetch@muffin.arcadia.spb.ru>
 * READS IN THE DATABASE OF IP ADDRESSES AND NETMASKS
 * FROM etc/security
 */
getsecurity() {
	FILE *sec;
	char line[MAXLINE];
	struct in_addr iaddr;
	int cnt;

	debug("INTO GETSECURITY\n");

	memset(securedb,'\0',sizeof(securedb));
	cnt = 0;

	sec = fopen(security, "r");
	if (sec) {
		while(fgets(line, MAXLINE, sec)) {
			debug("LINE IS: %s\n", line);

			iaddr.s_addr = inet_addr(line);
			if ((iaddr.s_addr != -1)&&(cnt < MAXLINE - 1)) {
				debug("ADDING LINE %d %s\n", cnt, line);
				securedb[cnt].s_addr = iaddr.s_addr;
				cnt++;
			}
			else debug("DISCARDING: %s\n", line);
		}
		fclose(sec);		/* CLOSE THE FILE */
	}
}

checkrequest(ipnum,req)
unsigned long ipnum;
char *req;
{
	/* GET THE REQUEST TYPE */

	/* IF OFFLINE/ONLINE */
	/* MAKE SURE REQUEST CAME FROM A BBDISPLAY */

}
checksecurity(ipnum) 
unsigned long ipnum;
{
	int cnt = 0;
	int retval = 1;			/* TRUE */
	struct in_addr chk;

	while (chk.s_addr = securedb[cnt].s_addr) {
		retval = 0;		/* FALSE */
		debug("CHECKSECURITY CHECKING %u ", ipnum);
		debug("VS AUTHORIZED ADDR %u ", chk.s_addr);
		/*
		 * SEEMS THE CAST TO (int) BREAKS THIS TEST ON Solaris 2.6
		 * FIXES FROM Henrik Stroem hstroem@pvv.org
		 */
		if ((ipnum & chk.s_addr) == chk.s_addr) {
			debug("------> PASSED\n");
			return(1);
		}
		else debug("--> FAILED\n");
		cnt++;
	}
	debug("CHECKSECUITY DEFAULT RETURNING %d\n", retval);
	return(retval);
}

/*
 * BBD ALARM
 */
void
timeout()
{
	fprintf(stderr,"BBD: hung from %s (dropped)\n", ipdotaddr);
	exit(1);
}

