/*****************************************************************************
 *
 * File ..................: mbuser/mbuser.c
 * Purpose ...............: User Pack Util
 * Last modification date : 12-Jun-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.
 *
 * MBSE 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 MBSE BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/


#include "../include/libs.h"
#include "../include/structs.h"
#include "../include/records.h"
#include "../common/common.h"
#include "../dbase/dbcfg.h"
#include "mbuser.h"


extern	int e_pid;			/* External pid			     */
extern	int do_quiet;			/* Quiet flag			     */
time_t	t_start;			/* Start time			     */
time_t	t_end;				/* End time			     */
int	Days, Level;			/* Kill days and up to level	     */
struct	userhdr	usrhdr;			/* Database header		     */
struct	user	usr;			/* Database record		     */
mode_t	oldmask;			/* Old umask value		     */


int main(int argc, char **argv)
{
	int	i, pack = FALSE;
	char	*cmd;
	struct	passwd *pw;

	InitConfig();
	TermInit(1);
	Days = 0;
	Level = 0;

	time(&t_start);

	if (argc < 2)
		Help();

	cmd = xstrcpy((char *)"Command line:");

	for (i = 1; i < argc; i++) {
		cmd = xstrcat(cmd, (char *)" ");
		cmd = xstrcat(cmd, tl(argv[i]));

		if (strncasecmp(tl(argv[i]), "-q", 2) == 0)
			do_quiet = TRUE;
		if (strncasecmp(tl(argv[i]), "p", 1) == 0)
			pack = TRUE;
		if (strncasecmp(tl(argv[i]), "k", 1) == 0) {
			if (argc  <= (i + 2))
				Help();
			i++;
			cmd = xstrcat(cmd, (char *)" ");
			cmd = xstrcat(cmd, argv[i]);
			Days = atoi(argv[i]);
			i++;
			cmd = xstrcat(cmd, (char *)" ");
			cmd = xstrcat(cmd, argv[i]);
			Level = atoi(argv[i]);

			if ((Days == 0) || (Level == 0))
				Help();
		}
	}

	if ((Days + Level + pack) == 0)
		Help();

	ProgName();
	pw = getpwuid(getuid());
	InitClient(pw->pw_name, (char *)"mbuser", CFG.location, CFG.logfile, CFG.util_loglevel);
	Syslog(' ', " ");
	Syslog(' ', "MBUSER v%s", MBSEVersion);
	Syslog(' ', cmd);

	oldmask = umask(002);
	if (!do_quiet)
		colour(3, 0);
	UserPack(Days, Level, pack);
	umask(oldmask);

	time(&t_end);
	Syslog(' ', "MBUSER finished in %s", t_elapsed(t_start, t_end));

	if (!do_quiet)
		colour(7, 0);
	ExitClient(0);
	return 0;
}



/*
 * Program header
 */
void ProgName(void)
{
	if (do_quiet)
		return;

	colour(15, 0);
	printf("\nMBUSER: MBSE BBS %s - User maintenance utility\n", MBSEVersion);
	colour(14, 0);
	printf("        %s\n\n", Copyright);
	colour(7, 0);
}



void Help(void)
{
	do_quiet = FALSE;
	ProgName();

	colour(11, 0);
	printf("\nUsage:	mbuser [commands] <options>\n\n");
	colour(9, 0);
	printf("	Commands are:\n\n");
	colour(3, 0);
	printf("	kill [n] [l]	Kill users not called in \"n\" days below level \"l\"\n");
	printf("	pack		Pack the userbase\n");
	colour(9, 0);
	printf("\n	Options are:\n\n");
	colour(3, 0);
	printf("	-quiet		Quiet mode, (no screen output)\n\n");

	colour(7, 0);
	printf("\n");
	ExitClient(1);
}



/*
 * Userpack routine
 */
void UserPack(int days, int level, int pack)
{
	FILE	*fin, *fout;
	char	fnin[81], fnout[81];
	long	oldsize, curpos;
	int	updated, delete = 0, rc;
	struct	passwd	*pw;

	sprintf(fnin,  "%s/etc/users.data", getenv("MBSE_ROOT"));
	sprintf(fnout, "%s/etc/users.temp", getenv("MBSE_ROOT"));

	/*
	 * First copy the users database, all packing will be done
	 * on a the copy.
	 */
	if ((fin = fopen(fnin, "r")) == NULL) {
		WriteError("Can't open %s", fnin);
		return;
	}
	if ((fout = fopen(fnout, "w+")) == NULL) {
		WriteError("Can't create %s", fnout);
		fclose(fin);
		return;
	}
	fread(&usrhdr, sizeof(usrhdr), 1, fin);
	oldsize = usrhdr.recsize;
	if (oldsize != sizeof(usr)) {
		updated = TRUE;
		Syslog('+', "Userbase recordsize is changed, making update");
	} else
		updated = FALSE;

	usrhdr.hdrsize = sizeof(usrhdr);
	usrhdr.recsize = sizeof(usr);
	fwrite(&usrhdr, sizeof(usrhdr), 1, fout);

	/*
	 * The datarecord is filled with zero's before each read
	 * so that if the record format changed, the new fields will
	 * be empty by default.
	 */
	memset(&usr, 0, sizeof(usr));
	while (fread(&usr, oldsize, 1,fin) == 1) {
		fwrite(&usr, sizeof(usr), 1, fout);
		memset(&usr, 0, sizeof(usr));
		if (CFG.slow_util && do_quiet)
			usleep(1);
	}
	fclose(fin);

	/*
	 * Handle packing for days below level
	 */
	if ((days) && (level)) {
		fseek(fout, sizeof(usrhdr), SEEK_SET);
		curpos = sizeof(usrhdr);

		while (fread(&usr, sizeof(usr), 1, fout)  == 1) {
			/*
			 * Wow, killing on the second exact!. Don't kill
			 * the guest accounts.
			 */
			if ((((t_start - usr.tLastLoginDate) / 86400) > days) &&
			    (usr.Security.level < level) && (!usr.Guest) &&
			    (usr.sUserName[0] != '\0') && (!usr.NeverDelete)) {
				Syslog('+', "Mark user %s", usr.sUserName);
				if (!do_quiet) {
					printf("Mark user %s\n", usr.sUserName);
					fflush(stdout);
				}
				delete++;
				updated = TRUE;
				fseek(fout, - sizeof(usr), SEEK_CUR);
				/*
				 * Just mark for deletion
				 */
				usr.Deleted = TRUE;
				fwrite(&usr, sizeof(usr), 1, fout);
			}
			if (CFG.slow_util && do_quiet)
				usleep(1);
		}
		Syslog('+', "Marked %d users to delete", delete);
 	}

	/*
	 * Pack the userbase if told so
	 */
	if (pack) {
		Syslog('+', "Packing userbase");
		delete = 0;
		fseek(fout, sizeof(usrhdr), SEEK_SET);
		while (fread(&usr, sizeof(usr), 1, fout) == 1) {
			if (CFG.slow_util && do_quiet)
				usleep(1);

			if (usr.Deleted) {
				if (!do_quiet) {
					printf("Delete user %s\n", usr.Name);
					fflush(stdout);
				}
				if (usr.Name[0] != '\0') {
					if ((setuid(0) == -1) || (setgid(0) == -1)) {
						WriteError("Cannot setuid(root) or setgid(root)");
						WriteError("Cannot delete unix account %s", usr.Name);
					} else {
						rc = execute((char *)"/usr/sbin/userdel ", usr.Name,NULL,(char *)"/dev/null",(char *)"/dev/null",(char *)"/dev/null");
						if (chdir(CFG.bbs_usersdir) == 0)
							rc = execute((char *)"/bin/rm -Rf ", usr.Name,NULL,(char *)"/dev/null",(char *)"/dev/null",(char *)"/dev/null");
					}
				}

				fseek(fout, - sizeof(usr), SEEK_CUR);
				/*
				 * Blank the deleted records for reuse.
				 */
				memset(&usr, 0, sizeof(usr));
				fwrite(&usr, sizeof(usr), 1, fout);
				delete++;
				updated = TRUE;
			}
		}
		Syslog('+', "Deleted %d records", delete);
	}
	fclose(fout);

	if (updated) {
		if ((rename(fnout, fnin)) == 0) {
			Syslog('+', "Userbase is updated");
			pw = getpwnam("mbse");
			chown(fnin, pw->pw_uid, pw->pw_gid);
		}
	}
	unlink(fnout);
}



