/*****************************************************************************
 *
 * File ..................: mbsed/reginfo.c
 * Purpose ...............: Buffers for registration information.
 * Last modification date : 31-Mar-1999
 *
 *****************************************************************************
 * Copyright (C) 1997-1999
 *   
 * Michiel Broek		FIDO:		2:2801/16
 * Beekmansbos 10		Internet:	mbroek@ux123.pttnwb.nl
 * 1971 BV IJmuiden
 * the Netherlands
 *
 * This file is part of MBSE BBS.
 *
 * This BBS 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, or (at your option) any
 * later version.
 *
 * MB BBS is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MB BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/


#include "libs.h"
#include "reginfo.h"
#include "logger.h"

#define	RB	5


/*
 * Registration information list
 */
struct _reg_info {
	pid_t		pid;		/* Process id			*/
	char		tty[7];		/* Connected tty		*/
	char		uname[36];	/* User name			*/
	char		prg[15];	/* Calling program name		*/
	char		city[36];	/* Users city			*/
	char		doing[36];	/* What is this user doing	*/
	time_t		started;	/* Time we were created		*/
	unsigned	silent	 : 1;	/* Do not disturb		*/
	unsigned	chatting : 1;	/* Is chatting			*/
	unsigned	ismsg    : 1;	/* Is a message present		*/
	int		channel;	/* Chat channel			*/
	int		ptr_in;		/* Input buffer pointer		*/
	int		ptr_out;	/* Output buffer pointer	*/
	char		fname[RB][36];	/* Message from user		*/
	char		msg[RB][81];	/* The message itself		*/
} reg_info;


char			*reg_fn;	/* Reginfo filename		*/
int			reg_fd;		/* Reginfo filedescriptor	*/
char			*reg_lck;	/* Reginfo lock filename	*/
int			reg_lfd;	/* Reginfo lock filedescriptor	*/
extern int		logdebug;	/* Debug level			*/
long			entrypos;	/* Current entry record		*/
pid_t			client_pid;	/* Current client pid		*/



/************************************************************************
 *
 *  Initialize registration information.
 */


void reg_init(char *base)
{
	reg_fn  = calloc(128, sizeof(char));
	reg_lck = calloc(128, sizeof(char));

	sprintf(reg_fn,  "%s/var/reginfo.mbsed", base);
	sprintf(reg_lck, "%s/var/reginfo.LCK", base);

	/*
	 * For initialisation, we just truncate or create a zero
	 * byte file.
	 */
	reg_fd = open(reg_fn, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
	if (reg_fd == -1) {
		perror("reginfo open");
		exit(1);
	}
	close(reg_fd);
}



/***********************************************************************
 *
 *  Global functions
 */


/*
 * Open registration datafile with lock
 */
int reg_open(void);
int reg_open(void)
{
	int	Tries = 0;

	while ((reg_lfd = creat(reg_lck, 0)) == -1 && errno == EACCES) {
		if (logdebug >= 2) {
			mbselog((char *)"+", (char *)"Reginfo file locked %d", Tries +1);
			usleep(150000);
		}
		if (++Tries >= 30) {
			mbselog((char *)"?", (char *)"$Error locking reginfo");
			return -1;
		}
	}
	close(reg_lfd);

	if ((reg_fd = open(reg_fn, O_RDWR)) == -1) {
		unlink(reg_lck);
		mbselog((char *)"?", (char *)"$Error open reginfo");
		return -1;
	}

	return 0;
}


/*
 * Search for a pid.
 */
long reg_find(char *);
long reg_find(char *pids)
{
	int	cnt;

	(void)lseek(reg_fd, 0, SEEK_SET);

	while (1) {
		if ((cnt = read(reg_fd, &reg_info, sizeof(reg_info))) == -1) {
			mbselog((char *)"?", (char *)"$reginfo read error");
			return -1;
		} else {
			if (cnt != sizeof(reg_info))
				return -1;

			if ((int)reg_info.pid == atoi(pids))
				return lseek(reg_fd, - sizeof(reg_info), SEEK_CUR);
		}
	}
}



/*
 * Update, close and unlock.
 */
int reg_write_close(void);
int reg_write_close(void)
{
	int	result = 0;

	if (write(reg_fd, &reg_info, sizeof(reg_info)) != sizeof(reg_info)) {
		mbselog((char *)"?", (char *)"Error writing reginfo");
		result = -1;
	}
	
	close(reg_fd);
	unlink(reg_lck);

	return result;
}



/*
 * Close without update
 */
void reg_read_close(void);
void reg_read_close(void)
{
	close(reg_fd);
	unlink(reg_lck);
}



/***********************************************************************
 *
 *  Registrate a new connection and fill the data.
 */

int reg_newcon(char *data)
{
	char *cnt, *pid, *tty, *uid, *prg, *city;

	/*
	 * First we check if there is an empty record in the
	 * registration datafile we can use. If so, we use it.
	 * If there is no empty record, we append a new record.
	 */
	if (reg_open() == -1)
		return -1;

	cnt = strtok(data, ",");
	pid = strtok(NULL, ",");
	tty = strtok(NULL, ",");
	uid = strtok(NULL, ",");
	prg = strtok(NULL, ",");
	city = strtok(NULL, ";");
	client_pid = atoi(pid);

	/*
	 * Append if no empty record is found 
	 */
	if (reg_find((char *)"0") == -1) {
		lseek(reg_fd, 0, SEEK_END);
	} 

	memset((char *)&reg_info, 0, sizeof(reg_info));
	reg_info.pid = atoi(pid);
	strncpy((char *)&reg_info.tty, tty, 6);
	strncpy((char *)&reg_info.uname, uid, 35);
	strncpy((char *)&reg_info.prg, prg, 14); 
	strncpy((char *)&reg_info.city, city, 35);
	strcpy((char *)&reg_info.doing, "-"); 
	reg_info.started = time(NULL); 

	/*
	 * Everyone says do not disturb, unless the flag
	 * is cleared by the owner of this process.
	 */
	reg_info.silent = 1;

	mbselog((char *)"-", (char *)"Client pgm \"%s\", pid %s", prg, pid);
	return reg_write_close();
}



int reg_closecon(char *data)
{
	char *cnt, *pid;

	if (reg_open() == -1)
		return -1;

	cnt = strtok(data, ",");
	pid = strtok(NULL, ";");
	if (reg_find(pid) == -1) {
		mbselog((char *)"?", (char *)"Panic, pid %s not found", pid);
		reg_read_close();
		return 0;
	}

	memset((char *)&reg_info, 0, sizeof(reg_info)); 
	return reg_write_close();
}



void reg_panic(void)
{
	char buf[32];

	sprintf(buf, "1,%d;", client_pid);
	mbselog((char *)"?", (char *)"Panic client close %s", buf);
	reg_closecon(buf);
}




/*
 * Update doing information for this process.
 */
int reg_doing(char *data)
{
	char *cnt, *pid, *line;

	if (reg_open() == -1)
		return -1;

	cnt = strtok(data, ",");
	pid = strtok(NULL, ",");
	line = strtok(NULL, ";");

	if (reg_find(pid) == -1) {
		mbselog((char *)"?", (char *)"Panic, pid %s not found", pid);
		reg_read_close();
		return -1;
	}

	strncpy((char *)&reg_info.doing, line, 35);
	return reg_write_close();
}



/*
 * Update tty information for this process.
 */
int reg_tty(char *data)
{
	char *cnt, *pid, *tty;

	if (reg_open() == -1)
		return -1;

	cnt = strtok(data, ",");
	pid = strtok(NULL, ",");
	tty = strtok(NULL, ";");

	if (reg_find(pid) == -1) {
		mbselog((char *)"?", (char *)"Panic, pid %s not found", pid);
		reg_read_close();
		return -1;
	}

	strncpy((char *)&reg_info.tty, tty, 6);
	return reg_write_close();
}



/*
 * Update the "do not disturb" flag.
 */
int reg_silent(char *data)
{
	char *cnt, *pid, *line;

	if (reg_open() == -1)
		return -1;

	cnt = strtok(data, ",");
	pid = strtok(NULL, ",");
	line = strtok(NULL, ";");

	if (reg_find(pid) == -1) {
		mbselog((char *)"?", (char *)"Panic, pid %s not found", pid);
		reg_read_close();
		return -1;
	}

	reg_info.silent = atoi(line);
	return reg_write_close();
}



/*
 * Update username and city for this process.
 */
int reg_user(char *data)
{
	char *cnt, *pid, *user, *city;

	if (reg_open() == -1)
		return -1;

	cnt  = strtok(data, ",");
	pid  = strtok(NULL, ",");
	user = strtok(NULL, ",");
	city = strtok(NULL, ";");

	if (reg_find(pid) == -1) {
		mbselog((char *)"?", (char *)"Panic, pid %s not found", pid);
		reg_read_close();
		return -1;
	}

	strncpy((char *)&reg_info.uname, user, 35);
	strncpy((char *)&reg_info.city,  city, 35);
	return reg_write_close();
}



char *reg_ipm(char *data)
{
	char		*cnt, *pid;
	static char	buf[128];

	buf[0] = '\0';

	sprintf(buf, "100:0;");
	if (reg_open() == -1)
		return buf;

	cnt = strtok(data, ",");
	pid = strtok(NULL, ";");

	if (reg_find(pid) == -1) {
		mbselog((char *)"?", (char *)"Panic, pid %s not found", pid);
		reg_read_close();
		return buf;
	}

	if (!reg_info.ismsg) {
		reg_read_close();
		return buf;
	}

	buf[0] = '\0';
	sprintf(buf, "100:2,%s,%s;", reg_info.fname[reg_info.ptr_out], reg_info.msg[reg_info.ptr_out]);
	if (reg_info.ptr_out < RB)
		reg_info.ptr_out++;
	else
		reg_info.ptr_out = 0;
	if (reg_info.ptr_out == reg_info.ptr_in)
		reg_info.ismsg = FALSE;
	mbselog((char *)"+", (char *)"reg_ipm: in=%d out=%d ismsg=%d", reg_info.ptr_in, reg_info.ptr_out, reg_info.ismsg);
	reg_write_close();

	return buf;
}



int reg_spm(char *data)
{
	char	*cnt, *from, *too, *txt;

	cnt  = strtok(data, ",");
	from = strtok(NULL, ",");
	too  = strtok(NULL, ",");
	txt  = strtok(NULL, ";");

	if (reg_open() == -1) {
		return -1;
	}

	lseek(reg_fd, 0, SEEK_SET);

	while (read(reg_fd, &reg_info, sizeof(reg_info)) == sizeof(reg_info)) {
		if (reg_info.pid && (strcasecmp(reg_info.uname, too) == 0)) {
			/*
			 *  If the in and out pointers are the same and the 
			 *  message present flag is still set, then this user
			 *  can't get anymore new messages.
			 */
			if (reg_info.ismsg && (reg_info.ptr_in == reg_info.ptr_out)) {
				reg_read_close();
				return 2;
			}
			/*
			 *  If user has the "do not distrurb" flag set.
			 */
			if (reg_info.silent) {
				reg_read_close();
				return 1;
			}
			/*
			 *  If all is well, insert the new message.
			 */
			strncpy((char *)&reg_info.fname[reg_info.ptr_in], from, 35);
			strncpy((char *)&reg_info.msg[reg_info.ptr_in], txt, 80);
			if (reg_info.ptr_in < RB)
				reg_info.ptr_in++;
			else
				reg_info.ptr_in = 0;
			reg_info.ismsg = TRUE;
			lseek(reg_fd, - sizeof(reg_info), SEEK_CUR);
			reg_write_close();
			mbselog((char *)"+", (char *)"reg_spm: in=%d out=%d ismsg=%d", reg_info.ptr_in, reg_info.ptr_out, reg_info.ismsg);
			return 0;
		}
	}

	/*
	 * User not found
	 */
	reg_read_close();
	return 3;
}



char *reg_fre(void)
{
	static char	buf[80];
	int		users = 0, utils = 0;

	buf[0] = '\0';

	if (reg_open() == -1) {
		sprintf(buf, "201:1,16;");
		return buf;
	}

	while ((read(reg_fd, &reg_info, sizeof(reg_info)) == sizeof(reg_info))) {
		if (reg_info.pid) {
			if (!strncmp(reg_info.prg, "mbsebbs", 7))
				users++;

			if ((!strncmp(reg_info.prg, "mbfido", 6)) ||
			    (!strncmp(reg_info.prg, "mball", 5)) ||
			    (!strncmp(reg_info.prg, "mbaff", 5)) ||
			    (!strncmp(reg_info.prg, "mbcico", 6)) ||
			    (!strncmp(reg_info.prg, "mbfile", 6)) ||
			    (!strncmp(reg_info.prg, "mbmsg", 5)) ||
			    (!strncmp(reg_info.prg, "mbindex", 7)) ||
			    (!strncmp(reg_info.prg, "mbdiff", 6)) ||
			    (!strncmp(reg_info.prg, "mbuser", 6)))
				utils++;
		}
	}
	reg_read_close();

	if (users || utils)
		sprintf(buf, "102:1,Connected utilities: %02d  users: %02d;", utils, users);
	else
		sprintf(buf, "100:0;");
	return buf;
}



/*
 * Get registration information. The first time the parameter
 * must be 1, for the next searches 0. Returns 301:0; if there
 * is an error or the end of file is reached.
 */
char *get_reginfo(int first)
{
	static char buf[80];
	int result;

	buf[0] = '\0';
	sprintf(buf, "301:0;");

	if (first == 1) 
		entrypos = 0;

	if (reg_open() == -1) {
		return buf;
	}

	/*
	 * Loop forever until an error occours, eof is reached or
	 * the data is valid. Only in the last case valid data is
	 * returned to the caller.
	 */
	for (;;) {

		result = lseek(reg_fd, entrypos, SEEK_SET);
		if (result == -1) {
			reg_read_close();
			return buf;
		}

		result = read(reg_fd, &reg_info, sizeof(reg_info));
		if (result != sizeof(reg_info)) {
			reg_read_close();
			return buf;
		}

		entrypos = entrypos + sizeof(reg_info);
		if ((int)reg_info.pid != 0) {
			sprintf(buf, "300:7,%d,%s,%s,%s,%s,%s,%d;", 
				reg_info.pid, reg_info.tty,
				reg_info.uname, reg_info.prg,
				reg_info.city, reg_info.doing,
				(int)reg_info.started);
			reg_read_close();
			return buf;
		}
	}
	/* never reached */
}


