// Filename:   User.c
// Contents:   the methods for the User object
// Author: Greg Shaw
// Created:    6/1/93

/*
This file 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.

In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file with other programs, and to distribute
those programs without any restriction coming from the use of this
file.  (The General Public License restrictions do apply in other
respects; for example, they cover modification of the file, and
distribution when not linked into another program.)

This file 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 this program; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#ifndef _USER_C_
#define _USER_C_

#include "bbshdr.h"

// Function:   append
// Purpose:    append a new user record to the user file
// Input:  object attributes
// Output: (file is written)
// Author: Greg Shaw
// Created:    7/20/93

int User::append(void)
{
	FILE   *ofile;
	char   finame[255];         // filename
	char   line[255];           // one line in file (255 max)
	char   bbspath[225];        // bbs path

	if (strcpy(bbspath,getenv("BBSDIR")), bbspath == NULL)
	{
		sprintf(finame,"user.save: BBSDIR env var not set for %s",login_name);
		ap_log(finame);
		return(-1);
	}
	strcpy(line,bbspath);
	strcat(line,"/admin/userlog");
	if (ofile = bopen(line,"a"), ofile == NULL)
	{
		sprintf(line,"user.save: Unable to open userlog file ");
		ap_log(line);
		return(-1);
	}
	//[A login_name firstname lastname state city]
	fprintf(ofile,"[A %s %s %s %s %s %s ]\n", login_name, fname, lname, alias, state, city);
	//[B terminal_type last_logon #_logins downloads uploads card_color]
	fprintf(ofile,"[B %s %ld %d %d %d %s ]\n", uterm,
	last_login, logins, downloads, uploads, card->colr);
	//[C private_msgs public_msgs credited_time color_capable editor lines cols language]
	fprintf(ofile,"[C %d %d %d %d %s %d %d %s]\n",priv_msgs,pub_msgs,
	credited, color_capable, editor, lines, cols, lang);
	//[D flags access_level timelimit timeused_last_call anniversary kused expiration ]
	fprintf(ofile,"[D %lx %d %d %d %ld %d %ld ]\n",flags, acl, timelimit, timeused, anniversary, kused, expiration);
	bclose(ofile);
	return(0);
};


// Function:   change_acl
// Purpose:        change the user's access level (possibly permanently)
// Input:      acl - the timlit to change to
//             perm - permanent or not
// Output:     none
// Author:     Greg Shaw
// Created:        6/8/93

int User::change_acl(int nacl, char perm)
{
	if (perm)
		acl = nacl;
	else
		tmpacl = nacl;
	return(0);
}


// Function:   change_flags
// Purpose:        change the user's timelimit (possibly permanently)
// Input:      flags - the flags to change to
//             perm - permanent or not
//         or - or into user flags or and into user flags
// Output:     none
// Author:     Greg Shaw
// Created:        6/9/93

int User::change_flags(unsigned long fl, char perm, char or)
{
	if (perm)
		if (or)
			flags |= fl;
	else
		flags &= fl;
	else
		if (or)
			tmpflags |= fl;
	else
		tmpflags &= fl;
	return(0);
}


// Function:   change_tl
// Purpose:        change the user's timelimit (possibly permanently)
// Input:      tl - the timlit to change to
//             perm - permanent or not
// Output:     none
// Author:     Greg Shaw
// Created:        6/8/93

int User::change_tl(int tl, char perm)
{
	if (perm)
		timelimit = tl;
	else
		tmptl = tl;
	return(0);
}


// Function:   check_card
// Purpose:        check the user's access information vs. the card color he has
// Input:      none
// Output:     (user may be changed)
// Author:     Greg Shaw

int User::check_card(void)
{
	char tmpstr[100];

	if (card = cardinfo(card_color), card == NULL)
	{
		sprintf(tmpstr,"Unable to find card info for color %d ",card_color);
		ap_log(tmpstr);
	}
	if (acl < card->acl)        // only update info if card better
		acl = card->acl;
	if (timelimit < card->tl)
		timelimit = card->tl;
	flags |= card->flags;
	return(0);
};


// Function:   check_info
// Purpose:    allow the user to change their environment information
// Input:      none
// Output:     the user's information may be changed
// Author:     Greg Shaw
// Created:    1/1/95


int User::check_info(void)
{
	char   loop;                // loop counter
	char   done;                // loop counter
	char   entered;             // entered anything?
	char   tmpstr[255];
	char   anotherstr[50];      // another string
	int    selected;

	loop = 1;
	entered = 0;
	while (loop)
	{
		clear_scr();            // clear screen
		cr();
		cr();
		cr();
		sstrcrl("ENTERFOLLOWING");
		cr();
		sprintf(tmpstr,gstrl("LOGINNAME"),login_name);
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("REALNAME"),fname,lname);
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("ALIASNAME"),alias);
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("CALLINGFROM"),city,state);
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("LANGUAGECHOSEN"),lang);
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("TERMINALTYPE"),uterm);
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("TERMINALCAPABILITIES"),color_capable?gstrl("YES"):gstrl("NO"));
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("EDITOR"),editor);
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("LINESCOLUMNS"),lines,cols);
		sstrcr_c(tmpstr);
		cr();
		cr();
		sstrl("CORRECT");
		if (yesno())
		{
			loop = 0;
			continue;
		}
		// show menu
		#ifndef ONE_LANGUAGE_ONLY
		sstrcrl("USEREDITMENU");
		#else
		sstrcrl("USEREDITMENUNOLANG");
		#endif
		cr();
		sstrcrl("RETURNTOEXIT");
		cr();
		sstrl("YOURCHOICE");
		gstr(tmpstr,3);
		selected = 0;
		#ifndef ONE_LANGUAGE_ONLY
		if (sscanf(tmpstr,"%d",&selected) != 1 || (selected < 1 || selected > 11))
			continue;
		#else
		if (sscanf(tmpstr,"%d",&selected) != 1 || (selected < 1 || selected > 10))
			continue;
		#endif
		switch(selected)
		{
			case 1:             //  first name
				cr();
				done = 0;
				while (!done)
				{
					cr();
					sstrl("USERFIRSTNAME");
					if (gstr(tmpstr,20), strcmp(tmpstr,"") ==
						0)
					continue;
					strcpy(fname,tmpstr);
					sprintf(tmpstr,gstrl("YOUENTERED"),fname);
					sstrcr_c(tmpstr);
					sstrl("CORRECT");
					if (!yesno())
						continue;
					done++;
				}
				break;
			case 2:
				cr();
				done = 0;
				while (!done)
				{
					cr();
					sstrl("USERLASTNAME");
					if (gstr(tmpstr,20), strcmp(tmpstr,"") ==
						0)
					continue;
					strcpy(lname,tmpstr);
					sprintf(tmpstr,gstrl("YOUENTERED"),lname);
					sstrcr_c(tmpstr);
					sstrl("CORRECT");
					if (!yesno())
						continue;
					done++;
				}
				break;
			case 3:
				cr();
				done = 0;
				while (!done)
				{
					sstrl("USERINFOCITY");
					if (gstr(tmpstr,20), strcmp(tmpstr,"") == 0)
						continue;
					else
						strcpy(city,tmpstr);
					sprintf(tmpstr,gstrl("YOUENTERED"),city);
					sstrcr_c(tmpstr);
					sstrl("CORRECT");
					if (!yesno())
						continue;
					done++;
				}
				break;
			case 4:
				cr();
				done = 0;
				while (!done)
				{
					sstrl("USERINFOSTATE");
					if (gstr(tmpstr,2), strcmp(tmpstr,"") == 0)
						continue;
					else
						strcpy(state,tmpstr);
					sprintf(tmpstr,gstrl("YOUENTERED"),state);
					sstrcr_c(tmpstr);
					sstrl("CORRECT");
					if (!yesno())
						continue;
					done++;
				}
				break;
			case 5:
				cr();
				sstrcrl("USERINFOALIAS0");
				cr();
				sstrl("USERINFOALIAS1");
				if (yesno())
					strcpy(alias,login_name);
				else
				{
					done = 0;
					while (!done)
					{
						sstrl("USERINFOALIAS2");
						if (gstr(tmpstr,20), strcmp(tmpstr,"") == 0)
							continue;
						else
							if (sscanf(tmpstr,"%s%s",alias,anotherstr) != 1)
								continue;
						sprintf(tmpstr,gstrl("YOUENTERED"),alias);
						sstrcr_c(tmpstr);
						sstrl("CORRECT");
						if (!yesno())
							continue;

						done++;
					}
				}
				break;
			case 6:             // terminal type
				done = 0;
				while (!done)
				{
					sstrcrl("USERINFOTERM0");
					cr();
					sstrcrl("USERINFOTERM1");
					cr();
					sstrcrl("USERINFOTERM2");
					cr();
					sstrl("USERINFOTERM3");
					if (gstr(tmpstr,20), strcmp(tmpstr,"") == 0)
						continue;
					else
						strcpy(uterm,tmpstr);
					sprintf(tmpstr,gstrl("YOUENTERED"),uterm);
					sstrcr_c(tmpstr);
					sstrl("CORRECT");
					if (!yesno())
						continue;
					// export to environ
					export_hard(1);
					done++;
				}
				break;
			case 7:             // lines on screen
				cr();
				cr();
				done = 0;
				while (!done)
				{
					sstrl("USERINFOLINES");
					if (gstr(tmpstr,5), strcmp(tmpstr,"") == 0)
						continue;
					if (tmpstr[0] == 'x')
					{
						lines = 24;
					}
					else
						sscanf(tmpstr,"%d",&lines);
					sprintf(tmpstr,gstrl("YOUENTEREDNUM"),lines);
					sstrcr_c(tmpstr);
					sstrl("CORRECT");
					if (!yesno())
						continue;
					// export to environment
					export_hard(2);
					done++;
				}
				break;
			case 8:             // number of columns on screen
				cr();
				done = 0;
				while (!done)
				{
					sstrl("USERINFOCOLUMNS");
					if (gstr(tmpstr,5), strcmp(tmpstr,"") == 0)
						continue;
					if (tmpstr[0] == 'x')
					{
						cols = 80;
					}
					else
						sscanf(tmpstr,"%d",&cols);
					sprintf(tmpstr,gstrl("YOUENTEREDNUM"),cols);
					sstrcr_c(tmpstr);
					sstrl("CORRECT");
					if (!yesno())
						continue;
					// export to environment
					export_hard(3);
					done++;
				}
				break;
			case 9:             // terminal supports color?
				cr();
				cr();
				sstrl("USERINFOCOLOR");
				if (yesno())
					color_capable = 1;
				else
					color_capable = 0;
				cr();
				break;
			case 10:            // editor
				cr();
				sprintf(tmpstr,"Viewing %s",tmpstr);
				sysopstrcr(tmpstr);
				sprintf(tmpstr,"%s/text/editors",getenv("BBSDIR"));
				// display file with paging
				display_file(tmpstr,1);
				cr();
				cr();
				done = 0;
				while (!done)
				{
					sstrl("USERINFOEDITOR");
					if (gstr(tmpstr,14), strcmp(tmpstr,"") == 0)
						continue;
					else
						strcpy(editor,tmpstr);
					sprintf(tmpstr,gstrl("YOUENTERED"),editor);
					sstrcr_c(tmpstr);
					sstrl("CORRECT");
					if (!yesno())
						continue;
					// export to environment
					export_hard(0);
					done++;
				}
				break;
				#ifndef ONE_LANGUAGE_ONLY
			case 11:            // language
				done = 0;
				while (!done)
				{
					sprintf(tmpstr,gstrl("YOUAREUSINGLANGUAGE"),lang);
					sstrcr_c(tmpstr);
					sstrl("CORRECT");
					if (yesno())
					{
						continue;
					}
					select_lang(lang);
				}
				#endif
		}
	}
	if (save(login_name))
	{
		ap_log("Unable to save user.");
	}
	return(0);
};


// Function:   delete_user
// Purpose:    delete a user from the BBS
// Input:  name - the login name of the user to delete
// Output: none
// Author: Greg Shaw
// Created:    4/11/94

int User::delete_user(char *name)
{
	return(0);
};


// Function:   display_info
// Purpose:    display the user's information
// Input:  logon - has the user just logged on?  (it gives 'welcome' message
// Output: user information is formatted and printed.
// Author: Greg Shaw
// Created:    7/2793

int User::display_info(int logon)
{
	char   tmpstr[255];         // string
	char   *str2;               // used for time
	double udratio;             // upload/download ratio

	clear_scr();
	if (logon)
		sprintf(tmpstr,gstrl("WELCOMEBACK"),fname,lname,city,state);
	else
		sprintf(tmpstr,gstrl("YOUARE"),fname,lname,city,state);
	sstrcr_c(tmpstr);
	str2 = ctime(&login_time);
	str2[strlen(str2)-1] = 0;   // chop \n
	cr();
	sstrcrl("USERINFORMATION");
	sstr_c("    ");
	sprintf(tmpstr,gstrl("ONSINCE"),str2);
	sstrcr_c(tmpstr);
	str2 = ctime(&last_login);
	str2[strlen(str2)-1] = 0;   // chop \n
	sstr_c("    ");
	sprintf(tmpstr,gstrl("LOGINSLAST"),logins,str2);
	sstrcr_c(tmpstr);
	sstr_c("    ");
	sprintf(tmpstr,gstrl("ALIASEDITOR"),alias,editor);
	sstrcr_c(tmpstr);
	sstr_c("    ");
	sprintf(tmpstr,gstrl("TERMINALINFO"),uterm,color_capable?gstrl("COLOR"):gstrl("MONOCHROME"),lines,cols);
	sstrcr_c(tmpstr);
	cr();
	sprintf(tmpstr,gstrl("CARDINFORMATION"),card->colr);
	sstrcr_c(tmpstr);
	sstr_c("    ");
	sprintf(tmpstr,gstrl("TIMELIMITINFO"),timelimit,waittime());
	sstrcr_c(tmpstr);
	sstr_c("    ");
	sprintf(tmpstr,gstrl("CREDITEDTIME"),credited);
	sstrcr_c(tmpstr);
	sstr_c("    ");
	if (card->kbytes < 0)       // unlimited download?
		strcpy(tmpstr,gstrl("DOWNLOADCAPACITY1"));
	else
		sprintf(tmpstr,gstrl("DOWNLOADCAPACITY2"),card->kbytes);
	sstrcr_c(tmpstr);
	cr();
	sstrcrl("FILETRANSFERS");
	sstr_c("    ");
	sprintf(tmpstr,gstrl("PRIORDOWNLOADS"),downloads);
	sstrcr_c(tmpstr);
	sstr_c("    ");
	sprintf(tmpstr,gstrl("PRIORUPLOADS"),uploads);
	sstrcr_c(tmpstr);
	if (downloads > 0)
		udratio = (double)uploads/downloads;
	else
		udratio = uploads;
	sstr_c("    ");
	sprintf(tmpstr,gstrl("RESULTRATIO"),udratio);
	sstrcr_c(tmpstr);
	cr();
	sstrcrl("LASTCALL");
	sstr_c("    ");
	sprintf(tmpstr,gstrl("TIMEUSED"),timeused);
	sstrcr_c(tmpstr);
	sstr_c("    ");
	sprintf(tmpstr,gstrl("DOWNLOADSIZE"),kused);
	sstrcr_c(tmpstr);
	waitcr();
	return(0);
};


// Function:   export_hard
// Purpose:    export the user's environment variables
// Input:  num - the item to overwrite the user's environment var
//     0 - editor
//     1 - term
//     2 - lines
//     3 - columns
// Output: one environment var is exported
// Author: Greg Shaw
// Created:    7/27/93
// Notes:  This function will nuke a user's environment variable based
//         on the number passed in.  This is so the user can change
//     his settings and expect them to take effect.

int User::export_hard(int num)
{
	char tmpmsg[100];
	char *outmsg;

	switch (num)
	{
		case 0:                 // editor
			sprintf(tmpmsg,"EDITOR=%s",editor);
			if (outmsg = (char *) malloc(strlen(tmpmsg)+1), outmsg == NULL)
			{
				ap_log("Unable to malloc more environment space.");
				return(0);
			}
			strcpy(outmsg,tmpmsg);
			if (putenv(outmsg) != 0)
				ap_log("Unable to export EDITOR variable.");
			sprintf(tmpmsg,"VISUAL=%s",editor);
			if (outmsg = (char *) malloc(strlen(tmpmsg)+1), outmsg == NULL)
			{
				ap_log("Unable to malloc more environment space.");
				return(0);
			}
			strcpy(outmsg,tmpmsg);
			if (putenv(outmsg) != 0)
				ap_log("Unable to export VISUAL variable.");
			break;
		case 1:
			sprintf(tmpmsg,"TERM=%s",uterm);
			if (outmsg = (char *) malloc(strlen(tmpmsg)+1), outmsg == NULL)
			{
				ap_log("Unable to malloc more environment space.");
				return(0);
			}
			strcpy(outmsg,tmpmsg);
			if (putenv(outmsg) != 0)
				ap_log("Unable to export TERM variable.");
			break;
		case 2:
			sprintf(tmpmsg,"LINES=%d",lines);
			if (outmsg = (char *) malloc(strlen(tmpmsg)+1), outmsg == NULL)
			{
				ap_log("Unable to malloc more environment space.");
				return(0);
			}
			strcpy(outmsg,tmpmsg);
			if (putenv(outmsg) != 0)
				ap_log("Unable to export LINES variable.");
			break;
		case 3:
			sprintf(tmpmsg,"COLUMNS=%d",cols);
			if (outmsg = (char *) malloc(strlen(tmpmsg)+1), outmsg == NULL)
			{
				ap_log("Unable to malloc more environment space.");
				return(0);
			}
			strcpy(outmsg,tmpmsg);
			if (putenv(outmsg) != 0)
				ap_log("Unable to export COLUMNS variable.");
			break;
	}
	return(0);
};


// Function:   export_soft
// Purpose:    export the user's environment variables
// Input:  none
// Output: environment vars are exported if not already available
// Author: Greg Shaw
// Created:    7/2793
// Notes:  This is a good "check" of the user's environment.  However,
//     it should not be used when the user has a wrong setting
//     (too many lines, for example) and wants to set it 'right'.

int User::export_soft(void)
{
	char tmpmsg[100];
	char *outmsg;

	// only export EDITOR if not already exported
	if (outmsg = getenv("EDITOR"), outmsg == NULL)
	{
		sprintf(tmpmsg,"EDITOR=%s",editor);
		if (outmsg = (char *) malloc(strlen(tmpmsg)+1), outmsg == NULL)
		{
			ap_log("Unable to malloc more environment space.");
		}
		strcpy(outmsg,tmpmsg);
		if (putenv(outmsg) != 0)
			ap_log("Unable to export EDITOR variable.");
	}
	// only export VISUAL if not already exported
	if (outmsg = getenv("VISUAL"), outmsg == NULL)
	{
		sprintf(tmpmsg,"VISUAL=%s",editor);
		if (outmsg = (char *) malloc(strlen(tmpmsg)+1), outmsg == NULL)
		{
			ap_log("Unable to malloc more environment space.");
		}
		strcpy(outmsg,tmpmsg);
		if (putenv(outmsg) != 0)
			ap_log("Unable to export VISUAL variable.");
	}
	// only export TERM if not already exported
	if (outmsg = getenv("TERM"), outmsg == NULL)
	{
		sprintf(tmpmsg,"TERM=%s",uterm);
		if (outmsg = (char *) malloc(strlen(tmpmsg)+1), outmsg == NULL)
		{
			ap_log("Unable to malloc more environment space.");
		}
		strcpy(outmsg,tmpmsg);
		if (putenv(outmsg) != 0)
			ap_log("Unable to export TERM variable.");
	}
	// only export LINES if not already exported
	if (outmsg = getenv("LINES"), outmsg == NULL)
	{
		sprintf(tmpmsg,"LINES=%d",lines);
		if (outmsg = (char *) malloc(strlen(tmpmsg)+1), outmsg == NULL)
		{
			ap_log("Unable to malloc more environment space.");
		}
		strcpy(outmsg,tmpmsg);
		if (putenv(outmsg) != 0)
			ap_log("Unable to export LINES variable.");
	}
	// only export COLUMNS if not already exported
	if (outmsg = getenv("COLUMNS"), outmsg == NULL)
	{
		sprintf(tmpmsg,"COLUMNS=%d",cols);
		if (outmsg = (char *) malloc(strlen(tmpmsg)+1), outmsg == NULL)
		{
			ap_log("Unable to malloc more environment space.");
		}
		strcpy(outmsg,tmpmsg);
		if (putenv(outmsg) != 0)
			ap_log("Unable to export COLUMNS variable.");
	}
	return(0);
};


// Function:   get
// Purpose:        get the user's info from the user list
// Input:      none
// Output:     object is loaded or an error is returned
// Author:     Greg Shaw
// Created:        7/13/93
// Notes:      the user file is a plain text file (to allow editing).
//             its format is such:
//                 [A login_name firstname lastname]
//                 [B terminal_type last_logon #_logins downloads uploads card color]
//                 [C private_msgs public_msgs credited_time color_capable editor lines cols language ]
//                 [D flags access_level timelimit timeused_last_call]
//             the above format includes the brackets and lines (for reference)

int User::get(char *name)
{
	FILE   *ufile;
	char   *u;
	char   finame[255];         // filename
	char   line[255];           // one line in file (255 max)
	char   logname[10];         // user login name
	char   tmpstr[255];         // temporary string
	char   found;               // user found?
	char   linenum;             // used to look for appropriate line
	char   c;                   // input char
	unsigned char  of;          // offset into line
	int    x;
	int    done;                // boolean
	int    numwords;            // number of words found in gecos
	time_t now;                 // current time
	struct passwd *userent;     // user entry in password file

	if (name == NULL)
		// get user's login information
		strcpy(logname,username());
	else
		strcpy(logname,name);
	if (strcpy(finame,getenv("BBSDIR")), finame == NULL)
	{
		sprintf(finame,"user.get: BBSDIR environment variable not set for %s",logname);
		ap_log(finame);
		return(-1);
	}
	//sprintf(finame,"user.get: BBSDIR environment variable not set for %s",logname);
	//ap_log(finame);
	strcat(finame,"/admin/userlog");
	linenum = 0;                // look for first line first
	if (ufile = bopen(finame,"r"), ufile != NULL)
	{
		found = 0;
		while (!found && !feof(ufile))
		{
			of = 0;
			while (c = fgetc(ufile), c != '\r' && c != '\n' && !feof(ufile))
				if (c != ']')   // skip trailing left bracket
					line[of++] = c;
			line[of] = 0;       // add null
			switch(linenum)
			{
				case 0:         // first line
					// got line 1?
					if (line[0] == '[' && line[1] == 'A')
					{
						x = sscanf(&line[2],"%s %s %s %s %s %s",login_name,
						fname, lname, alias, state, city);
						if (u = strchr(city,']'), u != NULL)
						{       // convert any left brackets to null
							u++;
							u[0] = 0;
						}
						if (strcmp(login_name,logname) == 0)
						{
							// get next line
							linenum++;
						}
					}
					break;
				case 1:         // second line
					// got line 2?
					if (line[0] == '[' && line[1] == 'B')
					{
						if (sscanf(&line[2],"%s %ld %d %d %d %s",uterm,
							&last_login, &logins, &downloads, &uploads,
						card->colr) != 6)
						{
							ap_log("user.get: bad read from user file.  Corrupted?");
							sprintf(finame,"user.get: bad user record was %s, line B",login_name);
							ap_log(finame);
							return(-1);
						}
						// get next line
						linenum++;
						if (!strcmp(card->colr,"red"))
							card_color = 0;
						else if (!strcmp(card->colr,"blue"))
							card_color = 1;
						else if (!strcmp(card->colr,"green"))
							card_color = 2;
						else if (!strcmp(card->colr,"white"))
							card_color = 3;
						else if (!strcmp(card->colr,"grey"))
							card_color = 4;
						else if (!strcmp(card->colr,"pink"))
							card_color = 5;
						else if (!strcmp(card->colr,"yellow"))
							card_color = 6;
						else if (!strcmp(card->colr,"black"))
							card_color = 7;
					}
					break;
				case 2:         // third line
					// got line 3?
					if (line[0] == '[' && line[1] == 'C')
					{
						if (sscanf(&line[2],"%d %d %d %d %s %d %d %s",&priv_msgs,&pub_msgs,&credited, &color_capable, editor, &lines, &cols, lang) != 8)
						{
							ap_log("user.get: bad read from user file.  Corrupted?");
							sprintf(finame,"user.get: bad user record was %s, line C",login_name);
							ap_log(finame);
							return(-1);
						}
						// get next line
						linenum++;
					}
					break;
				case 3:         // fourth line
					// got line 4?
					if (line[0] == '[' && line[1] == 'D')
					{
						if (sscanf(&line[2],"%lx %d %d %d %ld %d %ld",&flags,
							&acl, &timelimit,
						&timeused,
						&anniversary,&kused,
						&expiration) != 7)
						{
							ap_log("user.get: bad read from user file.  Corrupted?");
							sprintf(finame,"user.get: bad user record was %s, line D",login_name);
							ap_log(finame);
							return(-1);
						}
						// get next line
						linenum++;
					}
					break;
			}
			if (linenum == 4)   // got him.
			{
				found++;
				bclose(ufile);
				logins++;
				// open the language, but only if different
				if (strcmp(DEFAULT_LANGUAGE,lang) != 0)
					l_open(lang);
				time(&now);
				if ((now - expiration)/60 < (waittime()*60))
				{
					if ((timelimit - timeused) < 5)
					{
						clear_scr();
						sstrcrl("NOUNUSEDTIME");
						sprintf(finame,gstrl("TOOSOONERROR"),waittime());
						sstrcr_c(finame);
						cr();
						cr();
						waitcr();
						sstrcrl("THANKSFORCALLING");
						exit(0);
					}
				}
				else
				{
					kused = 0;  // start all counters over
					time(&expiration);
					timeused = 0;
					credited = 0;
				}
				tmpflags = flags;
				tmpacl = acl;
				tmptl = timelimit;
				if (name == NULL)
				{
					sprintf(line,"Logon for %s %s",fname,lname);
					ap_log(line);
					// export his environment variables
					export_soft();
					// check his access level vs. card
					check_card();
				}
				return(1);      // return 1 for new user only
			}
		}
	}
	bclose(ufile);              // close just in case
	// didn't find him in user file.  Need to get his information
	if (name != NULL)           // error - unable to find user in userlog
	{
		sprintf(line,"Unable to find %s in userlog.",name);
		ap_log(line);
		return(0);
	}
	strcpy(login_name,logname); // get login name
	sstrcrl("UNABLETOFINDUSER");
	sstrl("WOULDLIKETOREGISTER");
	if (!yesno())
	{
		fflush(stdout);
		sprintf(finame,"user.get: %s decided not to register for bbs.",logname);
		ap_log(finame);
		return(-1);
	}
	cr();
	if (userent = getpwnam((char *)username()), userent == NULL)
	{
		sprintf(finame,"user.get: Unable to get user %s's password entry!",logname);
		ap_log(finame);
		return(-1);
	}
	// get user name
	// check to see if unusual user field in the passwd file
	// don't care about more than 2
	if (numwords = sscanf(userent->pw_gecos,"%s %s %s",fname,lname,tmpstr), numwords < 2)
	{
		// don't know what I got from the passwd file, so,
		// get their first and last names
		clear_scr();
		sstrcrl("USERNAMENOTFOUND");
		cr();
		sstrcrl("PLEASEENTERNAME");
		cr();
		done = 0;
		while (!done)
		{
			cr();
			sstrl("USERFIRSTNAME");
			if (gstr(tmpstr,20), strcmp(tmpstr,"") == 0)
				continue;
			strcpy(fname,tmpstr);
			sprintf(tmpstr,gstrl("YOUENTERED"),fname);
			sstrcr_c(tmpstr);
			sstrl("CORRECT");
			if (!yesno())
				continue;
			done++;

		}
		cr();
		done = 0;
		while (!done)
		{
			cr();
			sstrl("USERLASTNAME");
			if (gstr(tmpstr,20), strcmp(tmpstr,"") == 0)
				continue;
			strcpy(lname,tmpstr);
			sprintf(tmpstr,gstrl("YOUENTERED"),lname);
			sstrcr_c(tmpstr);
			sstrl("CORRECT");
			if (!yesno())
				continue;
			done++;
		}
	}
	getinfo();                  // get user information
	export_soft();              // export his environment variables
	check_card();               // check his access level vs. card
	append();
	sprintf(line,"Logon for %s %s",fname,lname);
	ap_log(line);
	return(0);
}


// Function:   getinfo
// Purpose:        prompt the user for information to setup the user object
// Input:      none
// Output:     the user is prompted for their environment
// Author:     Greg Shaw
// Created:        7/25/93

int User::getinfo(void)
{
	char   loop;                // loop counter
	char   done;                // loop counter
	char   entered;             // entered anything?
	char   tmpstr[255];
	char   anotherstr[50];      // another string
	char   finame[255];         // filename
	char   logname[10];         // user login name

	loop = 1;
	entered = 0;
	while (loop)
	{
		clear_scr();            // clear screen
		if (entered)
		{
			cr();
			cr();
			cr();
			sstrcrl("ENTERFOLLOWING");
			cr();
			sprintf(finame,gstrl("LOGINNAME"),login_name);
			sstrcr_c(finame);
			sprintf(finame,gstrl("REALNAME"),fname,lname);
			sstrcr_c(finame);
			sprintf(finame,gstrl("ALIASNAME"),alias);
			sstrcr_c(finame);
			sprintf(finame,gstrl("CALLINGFROM"),city,state);
			sstrcr_c(finame);
			sprintf(finame,gstrl("LANGUAGECHOSEN"),lang);
			sstrcr_c(finame);
			sprintf(finame,gstrl("TERMINALTYPE"),uterm);
			sstrcr_c(finame);
			sprintf(finame,gstrl("TERMINALCAPABILITIES"),color_capable?gstrl("YES"):gstrl("NO"));
			sstrcr_c(finame);
			sprintf(finame,gstrl("EDITOR"),editor);
			sstrcr_c(finame);
			sprintf(finame,gstrl("LINESCOLUMNS"),lines,cols);
			sstrcr_c(finame);
			cr();
			cr();
			sstrl("CORRECT");
			if (yesno())
				loop = 0;
		}
		if (loop)               // still looping?
		{
			#ifndef ONE_LANGUAGE_ONLY
			// get language
			select_lang(lang);
			#else
			strcpy(lang,DEFAULT_LANGUAGE);
			#endif
			// get user's login information
			strcpy(logname,username());
			cr();
			done = 0;
			while (!done)
			{
				sstrl("USERINFOCITY");
				if (gstr(tmpstr,20), strcmp(tmpstr,"") == 0)
					continue;
				else
					strcpy(city,tmpstr);
				done++;
			}
			cr();
			done = 0;
			while (!done)
			{
				sstrl("USERINFOSTATE");
				if (gstr(tmpstr,2), strcmp(tmpstr,"") == 0)
					continue;
				else
					strcpy(state,tmpstr);
				done++;
			}
			cr();
			done = 0;
			while (!done)
			{
				sstrcrl("USERINFOTERM0");
				cr();
				sstrcrl("USERINFOTERM1");
				cr();
				sstrcrl("USERINFOTERM2");
				cr();
				sstrl("USERINFOTERM3");
				if (gstr(tmpstr,20), strcmp(tmpstr,"") == 0)
					continue;
				else
					strcpy(uterm,tmpstr);
				done++;
			}
			cr();
			cr();
			sstrl("USERINFOCOLOR");
			if (yesno())
				color_capable = 1;
			else
				color_capable = 0;
			cr();
			cr();
			if (strcpy(finame,getenv("BBSDIR")), finame == NULL)
			{
				sprintf(finame,"user.get: BBSDIR env var not set for %s",logname);
				ap_log(finame);
				return(-1);
			}
			strcat(finame,"/text/editors");
			sprintf(tmpstr,"Viewing %s",finame);
			sysopstrcr(tmpstr);
			// display file with paging
			display_file(finame,1);
			cr();
			cr();
			done = 0;
			while (!done)
			{
				sstrl("USERINFOEDITOR");
				if (gstr(tmpstr,14), strcmp(tmpstr,"") == 0)
					continue;
				else
					strcpy(editor,tmpstr);
				done++;
			}
			cr();
			cr();
			sstrcrl("USERINFOALIAS0");
			cr();
			sstrl("USERINFOALIAS1");
			if (yesno())
				strcpy(alias,logname);
			else
			{
				done = 0;
				while (!done)
				{
					sstrl("USERINFOALIAS2");
					if (gstr(tmpstr,20), strcmp(tmpstr,"") == 0)
						continue;
					else
						if (sscanf(tmpstr,"%s%s",alias,anotherstr) != 1)
							continue;
					done++;
				}
			}
			cr();
			cr();
			done = 0;
			while (!done)
			{
				sstrl("USERINFOLINES");
				if (gstr(tmpstr,5), strcmp(tmpstr,"") == 0)
					continue;
				if (tmpstr[0] == 'x')
				{
					lines = 24;
				}
				else
					sscanf(tmpstr,"%d",&lines);
				done++;
			}
			cr();
			cr();
			done = 0;
			while (!done)
			{
				sstrl("USERINFOCOLUMNS");
				if (gstr(tmpstr,5), strcmp(tmpstr,"") == 0)
					continue;
				if (tmpstr[0] == 'x')
				{
					cols = 80;
				}
				else
					sscanf(tmpstr,"%d",&cols);
				done++;
			}
			entered++;
		}
	}
	export_soft();
	if (save(login_name))
	{
		ap_log("Unable to save user.");
	}
	return(0);
};


// Function:   inactive_list
// Purpose:    list the inactive (people who haven't called lately) users
// Input:  none
// Output: A list of users who haven't called in a specified time
// Author: Greg Shaw
// Created:    4/3/94

int User::inactive_list(void)
{
	char   tmpstr[255];
	char   line[150];
	char   c;
	char   key[MAX_FILENAMELENGTH+1];
	char   uname[10];
	char   fullname[50];
	char   fname[15];
	char   lname[15];
	char   tmp[2][15];
	char   state[50];
	char   city[50];
	char   llogon[10];
	char   from[50];
	char   *sptr;
	int    logins;
	int    uls,dls;
	time_t laston;
	time_t today;
	struct tm *tmdata;
	int    off;
	int    found;
	int    days;
	FILE   *infile;


	clear_scr();
	time(&today);
	// get number of days inactive to search for
	sstrcrl("USERSEARCH0");
	cr();
	sstrl("USERSEARCH1");
	gstr(key,MAX_FILENAMELENGTH);
	if (key[0] == 0 || sscanf(key,"%d",&days) != 1)
		return(0);
	sprintf(tmpstr,"%s/admin/userlog",getenv("BBSDIR"));
	if (infile = bopen(tmpstr,"r"), infile == NULL)
	{
		ap_log("Unable to open userlog for read.");
		return(0);
	}
	found = 0;
	sstrl("USERSEARCH2");
	while (!feof(infile))
	{
		off = 0;
		while (c = fgetc(infile), c != '\n' && c != '\r' && !feof(infile))
			line[off++] = c;
		line[off] = 0;
		if (line[0] == '[')
		{
			if (line[1] == 'A')
			{
				sscanf(&line[2],"%s %s %s %s %s %s %s %s", uname, fname, lname, alias, state, city,tmp[0], tmp[1]);
				strcat(city,tmp[0]);
				strcat(city,tmp[1]);
				if (sptr = strchr(city,']'), sptr != NULL)
					sptr[0] = 0;
			} else if (line[1] == 'B')
			{
				sscanf(&line[2],"%*s %ld %d %d %d %*s",&laston, &logins,&uls,&dls);
				sprintf(fullname,"%s %s",fname,lname);
				sprintf(from,"%s, %s",city,state);
				tmdata = localtime(&laston);
				strftime(llogon, 12, "%b %d,%Y", tmdata);
				if ((today - laston)/86400 >= days)
				{
					sprintf(tmpstr,"%-15s %-15s %s   %d    %d",fullname,from,llogon,uls,dls);
					sstrcr_c(tmpstr);
					found++;
				}
				if (found == 17)
				{
					found = 0;
					waitcr();
					sstrl("CONTINUELISTING");
					if (!yesno())
					{
						bclose(infile);
						return(0);
					}
				}
			}
		}
	}
	cr();
	sstrcrl("NOMOREFOUND");
	bclose(infile);
	waitcr();
	return(0);
};


// Function:   inactive_delete
// Purpose:    delete inactive users on the BBS
// Input:  none
// Output: A list of users who haven't called in a specified time
// Author: Greg Shaw
// Created:    4/21/94

int User::inactive_delete(void)
{
	char   tmpstr[255];
	char   line[150];
	char   c,b;
	char   key[MAX_FILENAMELENGTH+1];
	char   uname[10];
	char   fullname[50];
	char   fname[15];
	char   lname[15];
	char   tmp[2][15];
	char   state[50];
	char   city[50];
	char   llogon[10];
	char   from[50];
	char   *sptr;
	int    x;
	int    logins;
	int    uls,dls;
	time_t laston;
	time_t today;
	struct tm *tmdata;
	int    off;
	int    found;
	int    days;
	FILE   *infile;
	FILE   *outfile;


	clear_scr();
	time(&today);
	// get number of days inactive to search for
	sstrcrl("SEARCHTODELETE0");
	cr();
	sstrl("SEARCHTODELETE1");
	gstr(key,MAX_FILENAMELENGTH);
	if (key[0] == 0 || sscanf(key,"%d",&days) != 1)
		return(0);
	sprintf(tmpstr,"Will delete users %d days.",days);
	sstrcr_c(tmpstr);
	cr();
	sstrl("SEARCHTODELETE2");
	while (b = tolower(gch(1)), b != 'a' && b != 'i');
	cr();
	if (b == 'i')               // interactively
	{
		sstrcrl("PROMPTENABLED");
		waitcr();
	}
	sprintf(tmpstr,"%s/admin/userlog",getenv("BBSDIR"));
	if (infile = bopen(tmpstr,"r"), infile == NULL)
	{
		ap_log("Unable to open userlog for read.");
		sstrcrl("NOOPENREAD");
		waitcr();
		return(0);
	}
	strcat(tmpstr,".new");      // get 'new' filename
	if (outfile = bopen(tmpstr,"w"), outfile == NULL)
	{
		ap_log("Unable to open userlog.new for write.");
		sstrcrl("NOOPENWRITE");
		waitcr();
		return(0);
	}
	found = 0;
	while (!feof(infile))
	{
		off = 0;
		while (c = fgetc(infile), c != '\n' && c != '\r' && !feof(infile))
			line[off++] = c;
		line[off] = 0;
		if (line[0] == '[')
		{
			if (line[1] == 'A')
			{
				sscanf(&line[2],"%s %s %s %s %s %s %s %s", uname, fname, lname, alias, state, city,tmp[0], tmp[1]);
				strcat(city,tmp[0]);
				strcat(city,tmp[1]);
				if (sptr = strchr(city,']'), sptr != NULL)
					sptr[0] = 0;
			} else if (line[1] == 'B')
			{
				if (sscanf(&line[2],"%s %ld %d %d %d %s",uterm, &laston, &logins,&uls,&dls,card->colr) != 6)
				{
					sprintf(tmpstr,"Error found in line B userlog in entry %s %s",fname,lname);
					ap_log(tmpstr);
					sstrcr_c("Error found in userlog.  Clean aborted.");
					waitcr();
					return(0);
				}
				sprintf(fullname,"%s %s",fname,lname);
				sprintf(from,"%s, %s",city,state);
				tmdata = localtime(&laston);
				strftime(llogon, 12, "%b %d,%Y", tmdata);
				if ((today - laston)/86400 >= days)
				{
					sprintf(tmpstr,"%-15s %-15s %s   %d    %d",fullname,from,llogon,uls,dls);
					sstrcr_c(tmpstr);
					// interactive?
					if (b == 'i')
					{
						sstrl("DELETEUSER");
						if (yesno())
							continue;
					}
					else
						continue;
				}
				for (x=0; x<2; x++)
					if (strlen(tmp[x]) < 2)
						tmp[x][0] = 0;
				// save info and next two lines
				fprintf(outfile,"[A %s %s %s %s %s %s %s %s ]\n", uname, fname, lname, alias, state, city, tmp[0], tmp[1]);
				fprintf(outfile,"%s\n",line);
				x = 0;
				while (x < 2)
				{
					if (c = fgetc(infile), c == '\n' || c == '\r')
						x++;
					fputc(c,outfile);
				}
			}
		}
	}
	cr();
	bclose(outfile);
	sprintf(tmpstr,"%s/admin/userlog",getenv("BBSDIR"));
	sprintf(line,"%s/admin/userlog.old",getenv("BBSDIR"));
	rename(tmpstr,line);
	sprintf(tmpstr,"%s/admin/userlog.new",getenv("BBSDIR"));
	sprintf(line,"%s/admin/userlog",getenv("BBSDIR"));
	rename(tmpstr,line);
	waitcr();
	return(0);
};


// Function:   list
// Purpose:    return a user record
// Input:  path - the path to the user file
// Output: a list of users on the bbs -
// Author: Greg Shaw
// Created:    6/8/93

int User::list(int search, int sysop)
{
	char   tmpstr[255];
	char   line[150];
	char   c;
	char   key[MAX_FILENAMELENGTH+1];
	char   uname[10];
	char   alias[25];
	char   fname[15];
	char   lname[15];
	char   tmp[2][15];
	char   state[50];
	char   city[50];
	char   *sptr;
	int    logins;
	time_t laston;
	int    off;
	int    found;
	FILE   *infile;


	clear_scr();
	sprintf(tmpstr,"%s/admin/userlog",getenv("BBSDIR"));
	if (infile = bopen(tmpstr,"r"), infile == NULL)
	{
		ap_log("Unable to open userlog for read.");
		return(0);
	}
	if (search)
	{
		sstrcrl("SEARCHFORUSER");
		cr();
		sstrl("SEARCHFORCHARS");
		gstr(key,MAX_FILENAMELENGTH);
		if (key[0] == 0)
			return(0);
		// search for characters in userlog
		found = 0;
		while (!feof(infile))
		{
			off = 0;
			while (c = fgetc(infile), c != '\n' && c != '\r' && !feof(infile))
				line[off++] = c;
			line[off] = 0;
			if (line[0] == '[')
			{
				if (line[1] == 'A')
				{
					sscanf(&line[2],"%s %s %s %s %s %s %s %s", uname, fname, lname, alias, state, city,tmp[0], tmp[1]);
					strcat(city,tmp[0]);
					strcat(city,tmp[1]);
					if (sptr = strchr(city,']'), sptr != NULL)
						sptr[0] = 0;
					if (strstr(line,key) != NULL)
					{
						sprintf(tmpstr,gstrl("NAMEFROM"),fname,lname,city,state);
						sstrcr_c(tmpstr);
						sprintf(tmpstr,gstrl("LOGINALIAS"),uname,alias);
						sstrcr_c(tmpstr);
						found++;
					}
				} else if (found && line[1] == 'B')
				{
					sscanf(&line[2],"%*s %ld %d %*d %*d %*s",&laston, &logins);
					sptr = ctime(&laston);
					// chop \n
					sptr[strlen(sptr)-1] = 0;
					sprintf(tmpstr,gstrl("NUMLOGONS"),logins,sptr);
					sstrcr_c(tmpstr);
					waitcr();
					if (sysop)
					{
						sstrl("CONTINUEEDITDELETE");
						while (c = toupper(gch(1)), c != 'D' && c != 'C' && c != 'E');
						if (c == 'D')
						{
							bclose(infile);
							delete_user(uname);
							return(0);
						}
						else if (c == 'E')
						{
							bclose(infile);
							if (get(uname))
							{
								check_card();
								sysop_edit();
							}
							else
							{
								sprintf(tmpstr,"Unable to get %s for sysop edit.",uname);
								ap_log(tmpstr);
							}
							return(0);
						}
					}
					else
					{
						sstrl("CONTINUESEARCH");
						if (!yesno())
						{
							bclose(infile);
							return(0);
						}
					}
					found = 0;
				}
			}
		}
		bclose(infile);
	}
	else
	{
		found = 0;
		while (!feof(infile))
		{
			off = 0;
			while (c = fgetc(infile), c != '\n' && c != '\r' && !feof(infile))
				line[off++] = c;
			if (line[0] == '[')
			{
				if (line[1] == 'A')
				{
					sscanf(&line[2],"%s %s %s %s %s %s %s %s", uname, fname, lname, alias, state, city,tmp[0], tmp[1]);
					strcat(city,tmp[0]);
					strcat(city,tmp[1]);
					if (sptr = strchr(city,']'), sptr != NULL)
						sptr[0] = 0;
					sprintf(tmpstr,"%s %s from %s, %s",fname,lname,city,state);
					sstrcr_c(tmpstr);
					sprintf(tmpstr,gstrl("LOGINALIAS"),uname,alias);
					sstrcr_c(tmpstr);
				} else if (line[1] == 'B')
				{
					sscanf(&line[2],"%*s %ld %d %*d %*d %*s",&laston, &logins);
					sptr = ctime(&laston);
					// chop \n
					sptr[strlen(sptr)-1] = 0;
					sprintf(tmpstr,gstrl("NUMLOGONS"),logins,sptr);
					sstrcr_c(tmpstr);
					cr();
					if (found == 5)
					{
						found = 0;
						waitcr();
						sstrl("CONTINUELISTING");
						if (!yesno())
						{
							bclose(infile);
							return(0);
						}
						clear_scr();
					}
					found++;
				}
			}
		}
		cr();
	}
	cr();
	sstrcrl("NOMOREFOUND");
	waitcr();
	return(0);
}


// Function:   mailavail
// Purpose:    return true if mail for the user is available
// Input:  none
// Output: 1 for mail.  0 for no mail
// Author:     Greg Shaw
// Created:        8/10/93

int User::mailavail(void)
{
	struct stat fistat;         // file status record
	char   tmpstr[255];         // tmpstr
	time_t now = 0L;            // current time
	static time_t  then = 0L;   // previous check time

	if (!checkmail())           // check?
		return(0);              // no, just exit
	time(&now);
	// seconds elapsed?
	if (abs(now - then) > mailchecktime())
	{
		sprintf(tmpstr,"%s/%s",mailspool(),login_name);
		if (stat(tmpstr,&fistat) == 0 && S_ISREG(fistat.st_mode))
		{
			if (mail_check == 0)// just check size the first time
			{
				// save modification time
				mail_check = fistat.st_mtime;
				if (mail_size = fistat.st_size, mail_size > 0)
					return(1);
			}
			else                // check date of last mail update
			{
				if (fistat.st_mtime != mail_check &&
					// new mail (mailbox bigger)
					fistat.st_size > mail_size)
				{
					return(1);
				}
				mail_check = fistat.st_mtime;
				mail_size = fistat.st_size;
			}
		}
	}
	return(0);
};


// Function:   save
// Purpose:    save the current User to the User file
// Input:      path - the path to the User file
// Output:     returns non-zero on failure
// Author:     Greg Shaw
// Created:        6/5/93

int User::save(char *name)
{
	FILE   *ufile;
	FILE   *ofile;
	char   finame[255];         // filename
	char   line[255];           // one line in file (255 max)
	char   *bbspath;            // bbs path
	char   logname[10];         // user login name
	char   found;               // user found?
	char   linenum;             // used to look for appropriate line
	char   c;                   // input character
	unsigned char  of;          // offset into line
	time_t now;




	last_login = login_time;    // set to new login time
	time(&now);
	// update time used field.
	timeused += (now - login_time)/60;
	if (name == NULL)
		// get user's log information
		strcpy(logname,username());
	else
		strcpy(logname,name);
	if (bbspath = getenv("BBSDIR"), bbspath == NULL)
	{
		sprintf(finame,"user.save: BBSDIR env var not set for %s",logname);
		ap_log(finame);
		return(-1);
	}
	sprintf(line,"%s/admin/nuserlog",bbspath);
	if (ofile = bopen(line,"w"), ofile == NULL)
	{
		sprintf(line,"user.save: Unable to open new userlog file: %s",logname);
		ap_log(line);
		ap_log("Please check permissions of admin directory and rocat program.");
		ap_log("See section in the manual pertaining to file permissions.");
		return(-1);
	}
	// change permissions
	chmod(line,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
	sprintf(finame,"%s/admin/userlog",bbspath);
	linenum = 0;                // look for first line first
	if (ufile = bopen(finame,"r"), ufile == NULL)
	{
		// create the bugger so that you can continue
		ufile = bopen(finame,"w");
		bclose(ufile);
		if (ufile = bopen(finame,"r"), ufile == NULL)
		{                       // still can't open the file!  AARGH!
			sprintf(line,"user.save: Unable to open userlog file.");
			ap_log(line);
			return(-1);
		}
	}
	else
	{
		found = 0;
		while (!found && !feof(ufile))
		{
			// get first line
			of = 0;
			while (c = fgetc(ufile), c != '\r' && c != '\n' && !feof(ufile))
				if (c != ']')   // skip trailing left bracket
					line[of++] = c;
			line[of] = 0;       // add null
			// end of file, skip.
			if (!feof(ufile) && of > 1)
			{
				switch(linenum)
				{
					case 0:     // first line
						// got line 1?
						if (line[0] == '[' && line[1] == 'A')
						{
							sscanf(&line[2],"%s %*s %*s %*s %*s %*s",logname);
							if (strcmp(login_name,logname) == 0)
							{
								// get next line
								linenum++;
								fprintf(ofile,"[A %s %s %s %s %s %s ]\n", login_name, fname, lname, alias, state, city);
							}
							else
								fprintf(ofile,"%s]\n",line);
							//                 [A login_name firstname lastname state city]
						}
						else
							fprintf(ofile,"%s]\n",line);
						break;
					case 1:     // second line
						// got line 2?
						if (line[0] == '[' && line[1] == 'B')
						{
							fprintf(ofile,"[B %s %ld %d %d %d %s ]\n", uterm,
							last_login, logins, downloads, uploads, card->colr);
							//                 [B terminal_type last_logon #_logins downloads uploads]
							// get next line
							linenum++;
						}
						else
							fprintf(ofile,"%s]\n",line);
						break;
					case 2:     // third line
						// got line 3?
						if (line[0] == '[' && line[1] == 'C')
						{
							fprintf(ofile,"[C %d %d %d %d %s %d %d %s ]\n",priv_msgs,
							pub_msgs, credited,
							color_capable, editor,
							lines, cols, lang);
							// save next line
							linenum++;
							//                 [C private_msgs public_msgs credited_time color_capable editor lines cols language]
						}
						else
							fprintf(ofile,"%s]\n",line);
						break;
					case 3:     // fourth line
						// got line 4?
						if (line[0] == '[' && line[1] == 'D')
						{
							fprintf(ofile,"[D %lx %d %d %d %ld %d %ld ]\n",
							flags, acl, timelimit, timeused,anniversary,kused, expiration);
							//                 [D flags access_level timelimit timeused_last_call anniversary kused expiration ]
							// save next line
							linenum++;
						}
						else
							fprintf(ofile,"%s]\n",line);
						break;
				}
				// got him.
				if (linenum == 4)
				{
					found++;
				}
			}
		}
		// Ok, got the guy.  copy the rest of the file
		while (!feof(ufile))
		{
			c = fgetc(ufile);
			if (!feof(ufile))
				fputc(c,ofile);
		}
	}
	// now rename the userfile to old userfile
	bclose(ufile);
	bclose(ofile);
	strcpy(line,bbspath);
	strcat(line,"/admin/userlog.old");
	if (rename(finame, line) != 0)
	{
		sprintf(finame,"user.save: unable to rename userlog to userlog.old");
		ap_log(finame);
		return(-1);
	}
	strcpy(line,bbspath);
	strcat(line,"/admin/nuserlog");
	strcpy(finame,bbspath);
	strcat(finame,"/admin/userlog");
	if (rename(line, finame) != 0)
	{
		sprintf(finame,"user.save: unable to rename new userlog to userlog");
		ap_log(finame);
		return(-1);
	}
	// change permissions
	chmod(finame,S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH);
	return(0);
}


// Function:       sysop_edit
// Purpose:        allow the sysop to edit a user
// Input:      firsttime - is this the first logon by user?
// Output:     (user questions)
// Author:     Greg Shaw
// Created:        4/11/94

int User::sysop_edit(void)
{
	char   c;
	char   loop;                // loop counter
	char   changed;             // changed anything?
	char   tmpstr[255];
	char   str1[50], str2[50];
	int    selected;            // which selected?
	int    tmp;
	int    x;
	struct tm *tmdata;          // time stuff

	loop = 1;
	changed = 0;
	while (loop)
	{
		clear_scr();            // clear screen
		cr();
		cr();
		cr();
		sstrcrl("CURRENTUSER");
		cr();
		sprintf(tmpstr,gstrl("LOGINFULLNAME"),login_name,fname,lname,city,state);
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("ALIASDOWNLOADS"),alias, downloads, uploads, logins);
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("ACLFLAGS"),acl, flags, timelimit);
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("TERMTYPECOLOR"),uterm,card->colr,lines,cols);
		sstrcr_c(tmpstr);
		tmdata = localtime(&anniversary);
		strftime(str1,49,"%c",tmdata);
		tmdata = localtime(&last_login);
		strftime(str2,49,"%c",tmdata);
		sprintf(tmpstr,gstrl("FIRSTLOGON"),str1);
		sstrcr_c(tmpstr);
		sprintf(tmpstr,gstrl("LASTLOGON"),str2,timeused);
		sstrcr_c(tmpstr);
		cr();
		cr();
		sstrl("CORRECT");
		if (yesno())
		{
			if (changed)
			{
				sstrcrl("SAVEUSERINFO");
				if (save(login_name))
				{
					ap_log("Unable to save user.");
				}
			}
			return(0);
		}
		cr();
		clear_scr();
		sstrcrl("EDITUSERMENU");
		sstrcrl("RETURNTOEXIT");
		cr();
		sstrl("YOURCHOICE");
		gstr(str1,3);
		selected = 0;
		#ifndef ONE_LANGUAGE_ONLY
		if (sscanf(str1,"%d",&selected) != 1 || (selected < 1 || selected > 16))
			continue;
		#else
		if (sscanf(str1,"%d",&selected) != 1 || (selected < 1 || selected > 15))
			continue;
		#endif
		switch(selected)
		{
			case 1:             //  get login name
				sstrcrl("NONAMECHANGE");
				cr();
				sstrcrl("RETURNTOEXIT");
				waitcr();
				break;          // not done at this time (requires
				// modification of passwd file)
			case 2:             // get first name
				sprintf(tmpstr,gstrl("CURRENTFIRSTNAME"),fname);
				sstrcr_c(tmpstr);
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("CHANGEFIRSTNAME");
				gstr(str1,20);
				if (sscanf(str1,"%s",str2) != 1)
				{
					sstrcrl("NOTCHANGED");
					waitcr();
					continue;
				}
				strcpy(fname,str2);
				changed++;
				break;
			case 3:             // get last name
				sprintf(tmpstr,gstrl("CURRENTLASTNAME"),lname);
				sstrcr_c(tmpstr);
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("CHANGELASTNAME");
				gstr(str1,20);
				if (sscanf(str1,"%s",str2) != 1)
				{
					sstrcrl("NOTCHANGED");
					waitcr();
					continue;
				}
				strcpy(lname,str2);
				changed++;
				break;
			case 4:             // get city
				sprintf(tmpstr,gstrl("CURRENTCITY"),city);
				sstrcr_c(tmpstr);
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("CHANGECITY");
				gstr(str1,20);
				if (sscanf(str1,"%s",str2) != 1)
				{
					sstrcrl("NOTCHANGED");
					waitcr();
					continue;
				}
				strcpy(city,str2);
				changed++;
				break;
			case 5:             // get state
				sprintf(tmpstr,gstrl("CURRENTSTATE"),state);
				sstrcr_c(tmpstr);
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("CHANGESTATE");
				gstr(str1,14);
				if (sscanf(str1,"%s",str2) != 1)
				{
					sstrcrl("NOTCHANGED");
					waitcr();
					continue;
				}
				strcpy(state,str2);
				changed++;
				break;
			case 6:             // get alias
				sprintf(tmpstr,gstrl("CURRENTALIAS"),alias);
				sstrcr_c(tmpstr);
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("CHANGEALIAS");
				gstr(str1,20);
				if (sscanf(str1,"%s",str2) != 1)
				{
					sstrcrl("NOTCHANGED");
					waitcr();
					continue;
				}
				strcpy(alias,str2);
				changed++;
				break;
			case 7:             // get downloads
				sprintf(tmpstr,gstrl("CURRENTDOWNLOADS"),downloads);
				sstrcr_c(tmpstr);
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("CHANGEDOWNLOADS");
				gstr(str1,4);
				if (sscanf(str1,"%d",&tmp) != 1)
				{
					sstrcrl("NOTCHANGED");
					waitcr();
					continue;
				}
				downloads = tmp;
				changed++;
				break;
			case 8:             // get uploads
				sprintf(tmpstr,gstrl("CURRENTUPLOADS"),uploads);
				sstrcr_c(tmpstr);
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("CHANGEUPLOADS");
				gstr(str1,4);
				if (sscanf(str1,"%d",&tmp) != 1)
				{
					sstrcrl("NOTCHANGED");
					waitcr();
					continue;
				}
				uploads = tmp;
				changed++;
				break;
			case 9:             // get access level
				sprintf(tmpstr,gstrl("CURRENTACL"),acl);
				sstrcr_c(tmpstr);
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("CHANGEACL");
				gstr(str1,5);
				if (sscanf(str1,"%d",&tmp) != 1)
				{
					sstrcrl("NOTCHANGED");
					waitcr();
					continue;
				}
				acl = tmp;
				changed++;
				break;
			case 10:            // get flags
				sstrcrl("CURRENTFLAGS");
				for (x=0; x<32; x++)
				{
					if (x%8 == 0 && x != 0)
						cr();
					sprintf(tmpstr,"%.2d:%d ",x,flags&1<<x?1:0);
					sstr_c(tmpstr);
				}
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("SETORCLEAR");
				gstr(str1,2);
				for (c=0; c<strlen(str1); c++)
					str1[c] = tolower(str1[c]);
				if (strchr(str1,'s') != NULL)
				{
					sstrl("SETFLAG");
					gstr(str1,3);
					if (sscanf(str1,"%d",&tmp) != 1 || (tmp < 0 || tmp > 31))
					{
						sstrcrl("NOTCHANGED");
						waitcr();
						continue;
					}
					flags |= 1<<tmp;
				}
				else if (strchr(str1,'c') != NULL)
				{
					sstrl("CLEARFLAG");
					gstr(str1,3);
					if (sscanf(str1,"%d",&tmp) != 1 || (tmp < 0 || tmp > 31))
					{
						sstrcrl("NOTCHANGED");
						waitcr();
						continue;
					}
					flags &= ~(1<<tmp);
				}
				else
				{
					sstrcrl("NOTCHANGED");
					waitcr();
					continue;
				}
				changed++;
				break;
			case 11:            // get timelimit
				sprintf(tmpstr,gstrl("CURRENTTIMELIMIT"),timelimit);
				sstrcr_c(tmpstr);
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("CHANGETIMELIMIT");
				gstr(str1,5);
				if (sscanf(str1,"%d",&tmp) != 1)
				{
					sstrcrl("NOTCHANGED");
					waitcr();
					continue;
				}
				timelimit = tmp;
				changed++;
				break;
			case 12:            // get terminal types
				sprintf(tmpstr,gstrl("CURRENTTERMTYPE"),uterm);
				sstrcr_c(tmpstr);
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("CHANGETYPE");
				gstr(str1,20);
				if (sscanf(str1,"%s",str2) != 1)
				{
					sstrcrl("NOTCHANGED");
					waitcr();
					continue;
				}
				strcpy(uterm,str2);
				changed++;
				break;
			case 13:            // get card color
				switch(card_color)
				{
					case 0:
						strcpy(str2,gstrl("RED"));
						break;
					case 1:
						strcpy(str2,gstrl("BLUE"));
						break;
					case 2:
						strcpy(str2,gstrl("GREEN"));
						break;
					case 3:
						strcpy(str2,gstrl("WHITE"));
						break;
					case 4:
						strcpy(str2,gstrl("GREY"));
						break;
					case 5:
						strcpy(str2,gstrl("PINK"));
						break;
					case 6:
						strcpy(str2,gstrl("YELLOW"));
						break;
					case 7:
						strcpy(str2,gstrl("BLACK"));
				}
				sprintf(tmpstr,gstrl("CURRENTCARDCOLOR"),str2);
				sstrcr_c(tmpstr);
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrcrl("CHANGECOLOR");
				sstrcrl("COLORMENU");
				cr();
				cr();
				sstrl("YOURCHOICE");
				while (c = gch(1), c != '\r' && c != '\n' && (c < '1' || c > '8') );
				if (c != '\r' && c != '\n')
				{
					card_color = c-'0'-1;
					check_card();
					changed++;
				}
				break;
			case 14:            // get lines
				sprintf(tmpstr,gstrl("CURRENTLINES"),lines);
				sstrcr_c(tmpstr);
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("CHANGELINES");
				gstr(str1,4);
				if (sscanf(str1,"%d",&tmp) != 1)
				{
					sstrcrl("NOTCHANGED");
					waitcr();
					continue;
				}
				lines = tmp;
				changed++;
				break;
			case 15:            // get columns
				sprintf(tmpstr,gstrl("CURRENTCOLUMNS"),cols);
				sstrcr_c(tmpstr);
				cr();
				sstrcrl("RETURNTOEXIT");
				cr();
				sstrl("CHANGECOLUMNS");
				gstr(str1,4);
				if (sscanf(str1,"%d",&tmp) != 1)
				{
					sstrcrl("NOTCHANGED");
					waitcr();
					continue;
				}
				cols = tmp;
				changed++;
				break;
				#ifndef ONE_LANGUAGE_ONLY
			case 16:            // language
				// get language
				select_lang(lang);
				int done = 0;
				while (!done)
				{
					sprintf(tmpstr,gstrl("YOUAREUSINGLANGUAGE"),lang);
					sstrcr_c(tmpstr);
					sstrl("CORRECT");
					if (yesno())
					{
						done++;
						continue;
					}
					select_lang(lang);
				}
				#endif

		}
	}
	return(0);
};


// Function:   constructor
// Purpose:        initialize all object variables
// Input:      none
// Output:     (object is initialized)
// Author:     Greg Shaw
// Created:        6/1/93

User::User()
{
	card = NULL;
	if (card_color = def_card(), card_color == -1)
		ap_log("Unable to get default card color.");
	if (card_color != -1 && (card = cardinfo(card_color), card == NULL))
	{
		ap_log("Unable to get card information.");
		sstrcr("The BBS has a configuration problem.\n");
		exit(0);
	}
	fname[0] = 0;
	lname[0] = 0;
	alias[0] = 0;
	login_name[0] = 0;
	editor[0] = 0;
	city[0] = 0;
	state[0] = 0;
	uterm[0] = 0;
	time(&last_login);
	time(&login_time);
	time(&anniversary);
	time(&expiration);
	mail_check = 0;
	logins = 1;
	lines = 24;
	cols = 80;
	downloads = 0;
	uploads = 0;
	priv_msgs = 0;
	pub_msgs = 0;
	credited = 0;
	flags = 0;
	tmpflags = 0;
	acl = card->acl;
	tmpacl = acl;
	timelimit = card->tl;
	tmptl = timelimit;
	credited = 0;
	timeused = 0;
	kused = 0;
	color_capable = 0;
}


// Function:   destructor
// Purpose:        clean up the object
// Input:      none
// Output:     none ( null destructor at this point )
// Author:     Greg Shaw
// Created:        6/1/93

User::~User()
{
}


#endif                          // _USER_C_






