/*
 * useradmin.c - Full-screen user administration program
 * see copyright.doc for copyright information
 * v1.9
 */

#include <stdio.h>
#include <fcntl.h>
#include <ctype.h>

#ifdef USE_NCURSES
#include <ncurses/curses.h>
#else 
#include <curses.h>
#endif

#include <time.h>
#include <pwd.h>
#include "citadel.h"
#include "axdefs.h"

struct config config;
long atol();
long lseek();

int need_rewrite = 0;

int hash(str)
char str[]; {
	int h = 0;
	int i;

	for (i=0; i<strlen(str); ++i) h=h+((i+1)*tolower(str[i]));
	return(h);
	}

long finduser(file,name)
int file;
char *name; {
	FILE *fp;
	int c=0;
	int uh,fh;
	long pp;
	
	uh=hash(name);
	fp=fopen("hashtab","r");
	while(fread((char *)&fh,sizeof(int),1,fp)>0) {
		if (uh==fh) {
			pp=(long)c * (long)sizeof(struct usersupp);
			lseek(file,pp,0);
			return(pp);
			}
		++c;
		}
	fclose(fp);
	return(-1L);
	}


int struncmp(lstr,rstr,len)
char lstr[],rstr[];
int len; {
	int pos = 0;
	char lc,rc;
	while (pos<len) {
		lc=tolower(lstr[pos]);
		rc=tolower(rstr[pos]);
		if ((lc==0)&&(rc==0)) return(0);
		if (lc<rc) return(-1);
		if (lc>rc) return(1);
		pos=pos+1;
		}
	return(0);
	}

void inverse(onoff)
int onoff; {
	if (onoff==1) {
#ifdef A_REVERSE
		attron(A_REVERSE);
#else
		standout();
#endif
		}
	else {
#ifdef A_REVERSE
		attroff(A_REVERSE);
#else
		standend();
#endif
		}
	}



getlin(yp,xp,string,lim)	/* Gets a line from the terminal */
int yp,xp;			/* Where on the screen to start */
char string[];	 		/* Pointer to string buffer */
int lim;			/* Maximum length - if negative, no-show */
{
int a,b; char flag;

	flag=0;
	if (lim<0) { lim=(0-lim); flag=1; }
	strcpy(string,"");
	move(yp,xp);
	inverse(1);
	for (a=0; a<lim; ++a) addch('-');
	refresh();
	move(yp,xp);
	for (a=0; a<lim; ++a) addch(' ');
GLA:	move(yp,xp+strlen(string));
	refresh();
	a=getch();
	if (a==127) a=8;
	a=(a&127);
	if (a==10) a=13;
	if ((a==8)&&(strlen(string)==0)) goto GLA;
	if ((a!=13)&&(a!=8)&&(strlen(string)==lim)) goto GLA;
	if ((a==8)&&(string[0]!=0)) {
		string[strlen(string)-1]=0;
		move(yp,xp+strlen(string));
		addch(' ');
		goto GLA;
		}
	if ((a==13)||(a==10)) {
		inverse(0);
		move(yp,xp);
		for (a=0; a<lim; ++a) addch(' ');
		mvprintw(yp,xp,"%s",string);
		refresh();
		return(0);
		}
	b=strlen(string);
	string[b]=a;
	string[b+1]=0;
	if (flag==0) addch(a);
	if (flag==1) addch('*');
	goto GLA;
}

main() {
	char user[100];
	long userpos;
	int file,a;
	FILE *fp;
	struct usersupp usersupp;
	struct passwd *pwptr;
	
	get_config();
	initscr();			/* go into curses windowing mode */
	raw();
	noecho();

	clear();
	move(8,20); printw("Citadel/UX User Administration");
	move(10,20); printw("Enter user name:");
	refresh();
	getlin(10,37,user,25);

	file=open("usersupp",O_RDONLY);
	userpos=finduser(file,user);
	if (userpos<0L) {
		inverse(1);
		printw("No such user.\n");
		inverse(0); refresh(); close(file); endwin();
		exit(1);
		}
	read(file,&usersupp,sizeof(struct usersupp));
	pwcrypt(usersupp.password,PWCRYPT);
	close(file);

	/* display user information */
	clear();
	move(1,0);
	printw("1. User name\n");
	printw("2. User number\n");
	printw("3. Uid attachment\n");
	printw("4. Password\n");
	printw("5. Screen size\n");
	printw("6. Times called\n");
	printw("7. Messages posted\n");
	printw("8. Last call\n");
	printw("9. Access level\n");
	for (a=10; a<=18; ++a) {
		move(a,0);
		printw("%d.\n",a);
		}
	for (a=1; a<=20; ++a) display_field(&usersupp,a);
	dis_regis(&usersupp);
	refresh();

	/* editing loop */
	while(1) {
		move(10,50); addstr("Enter field number to change");
		move(11,50); addstr("0 to exit");
		move(12,50); addstr("-->      ");
		refresh();
		getlin(12,54,user,2);
		refresh();
		a=atoi(user);
		if (a==0) break;
		if (a>0) edit_field(&usersupp,a);
		}

		move(14,50); addstr("Save changes (y/n)? ");
		refresh();
		getlin(14,71,user,1);
		if (tolower(user[0])=='y') {
			file=open("usersupp",O_RDWR);
			pwcrypt(usersupp.password,PWCRYPT);
			lseek(file,userpos,0);
		      	write(file,&usersupp,sizeof(struct usersupp));
			close(file);
#ifdef CONSMSG
			pwptr = (struct passwd *)getpwuid(getuid());
			fp=fopen(CONSMSG,"w");
			fprintf(fp,"useradmin: user <%s> modified by <%s>\n",
				usersupp.fullname,pwptr->pw_gecos);
			fclose(fp);
#endif
			}
		else need_rewrite = 0;
	clear(); refresh();
	endwin();
	printf("useradmin: session ended.\n");
	if (need_rewrite==1) {
		printf("useradmin: rewriting user hash table\n");
		execlp("sysoputil","sysoputil","-h",NULL);
		}
	exit(0);
}

dis_regis(userdata)
struct usersupp *userdata; {
	int a,b;
	char pbuf[20];
	if (!((userdata->flags)&US_REGIS)) {
		move(1,50); inverse(1);
		addstr("No registration online");
		inverse(0); refresh();
		return(1);
		}
	inverse(1);
	move(1,50); printw("%-29s",userdata->USname);
	move(2,50); printw("%-29s",userdata->USaddr);
	while (strlen(userdata->USzip)<9) strcat(userdata->USzip," ");
	move(3,50); printw("%-14s %2s %c%c%c%c%c-%c%c%c%c ",
		userdata->UScity,userdata->USstate,
		userdata->USzip[0],userdata->USzip[1],userdata->USzip[2],
		userdata->USzip[3],userdata->USzip[4],userdata->USzip[5],
		userdata->USzip[6],userdata->USzip[7],userdata->USzip[8]);
	strcpy(pbuf,userdata->USphone);
	userdata->USphone[0]=0;
	for (a=0; a<strlen(pbuf); ++a) {
		if ((pbuf[a]>='0')&&(pbuf[a]<='9')) {
			b=strlen(userdata->USphone);
			userdata->USphone[b]=pbuf[a];
			userdata->USphone[b+1]=0;
			}
		}
	while(strlen(userdata->USphone)<10) {
		strcpy(pbuf,userdata->USphone);
		strcpy(userdata->USphone," ");
		strcat(userdata->USphone,pbuf);
		}

	move(4,50);
	printw("(%c%c%c) %c%c%c-%c%c%c%c               ",
		userdata->USphone[0],userdata->USphone[1],
		userdata->USphone[2],userdata->USphone[3],
		userdata->USphone[4],userdata->USphone[5],
		userdata->USphone[6],userdata->USphone[7],
		userdata->USphone[8],userdata->USphone[9]);
	inverse(0); refresh();
	}


display_field(userdata,fieldnum)
struct usersupp *userdata;
int fieldnum; {
	struct tm *tm;
	char *strtime;
	struct passwd *pwd;
	move (fieldnum,(fieldnum<10 ? 20 : 5));
	switch(fieldnum) {
	case 1:		printw("%-30s",userdata->fullname);
			break;
	case 2:		printw("%-10ld",userdata->eternal);
			break;
	case 3:		pwd=(struct passwd *)getpwuid(userdata->USuid);
			printw("%-5d (%s)",userdata->USuid,
				pwd->pw_name);
			break;
	case 4:		printw("%-20s",userdata->password);
			break;
	case 5:		printw("%d x %d      ",
				userdata->USscreenwidth,
				userdata->USscreenheight);
			break;
	case 6:		printw("%-5d",userdata->timescalled);
			break;
	case 7:		printw("%-5d",userdata->posted);
			break;
	case 8:		tm=localtime(&userdata->lastcall);
			printw("%s",asctime(tm));
			break;
	case 9:		printw("%1d (%s)",userdata->axlevel,
				axdefs[userdata->axlevel]);
			break;
	case 10:	if (userdata->flags & US_PERM)
				addstr("Do not scroll off          ");
			else	addstr("Scroll off after two months");
			break;
	case 11:	if (userdata->flags & US_LASTOLD)
				addstr("Print last old message with new");
			else	addstr("Do not print last old message  ");
			break;
	case 12:	if (userdata->flags & US_EXPERT)
				addstr("Experienced user: suppress hints");
			else	addstr("Print help blurbs and hints     ");
			break;
	case 13:	if (userdata->flags & US_UNLISTED)
				addstr("Do not list in userlog");
			else	addstr("List in userlog       ");
			break;
	case 14:	if (userdata->flags & US_NOPROMPT)
				addstr("Do not prompt after each message");
			else	addstr("Prompt after each message       ");
			break;
	case 15:	if (userdata->flags & US_REGIS)
				addstr("Registered with name and address ");
			else	addstr("Not registered                   ");
			break;
	case 16:	if (userdata->flags & US_PAGINATOR)
				addstr("Pause after each screen of text  ");
			else	addstr("Do not pause after each screen   ");
			break;
	case 17:	if (userdata->flags & US_INTERNET)
				addstr("Has access to UUCP/SMTP gateways ");
			else	addstr("No access to UUCP/SMTP gateways  ");
			break;
	case 18:	if (userdata->flags & US_DISAPPEAR)
				addstr("Use 'disappearing' message prompt");
			else	addstr("No 'disappearing' message prompt ");
			break;
	default:	break;
	}
	refresh();
	return(0);
	}

edit_field(userdata,fieldnum)
struct usersupp *userdata;
int fieldnum; {
	char new[100];
	int i;
	if (fieldnum<10) { 
		move(fieldnum,20);
		addstr("                              ");
		refresh();
		}
	switch(fieldnum) {
	case 1:		getlin(fieldnum,20,userdata->fullname,25);
			need_rewrite=1;
			break;
	case 2:		getlin(fieldnum,20,new,10);
			userdata->eternal=atol(new);
			break;
	case 3:		getlin(fieldnum,20,new,10);
			userdata->USuid=atol(new);
			break;
	case 4:		getlin(fieldnum,20,userdata->password,19);
			break;
	case 5:		mvprintw(fieldnum,25,"(enter screen width)");
			getlin(fieldnum,20,new,3);
			userdata->USscreenwidth = atoi(new);
			mvprintw(fieldnum,25,"(enter screen height)");
			getlin(fieldnum,20,new,3);
			userdata->USscreenheight = atoi(new);
			mvprintw(fieldnum,25,"                     ");
			break;
	case 6:		getlin(fieldnum,20,new,5);
			userdata->timescalled=atoi(new);
			break;
	case 7:		getlin(fieldnum,20,new,5);
			userdata->posted=atoi(new);
			break;
	case 8:		time(&userdata->lastcall);
			break;
	case 9:		for (i=0; i<(sizeof(axdefs)/sizeof(char *)); ++i) {
				inverse(1);
				mvprintw(15+i,50,"%d",i);
				inverse(0);
				mvprintw(15+i,52,"%s",axdefs[i]);
				}
			getlin(fieldnum,20,new,1);
			for (i=0; i<(sizeof(axdefs)/sizeof(char *)); ++i)
				mvprintw(15+i,50,"%30s","");
			userdata->axlevel=atoi(new);
			break;
	case 10:	userdata->flags=(userdata->flags^US_PERM);
			break;
	case 11:	userdata->flags=(userdata->flags^US_LASTOLD);
			break;
	case 12:	userdata->flags=(userdata->flags^US_EXPERT);
			break;
	case 13:	userdata->flags=(userdata->flags^US_UNLISTED);
			break;
	case 14:	userdata->flags=(userdata->flags^US_NOPROMPT);
			break;
	case 15:	userdata->flags=(userdata->flags^US_REGIS);
			break;
	case 16:	userdata->flags=(userdata->flags^US_PAGINATOR);
			break;
	case 17:	userdata->flags=(userdata->flags^US_INTERNET);
			break;
	case 18:	userdata->flags=(userdata->flags^US_DISAPPEAR);
			break;
	default:	break;
		}
	display_field(userdata,fieldnum);
	return(0);
}


pwcrypt(text,code)
char text[];
int code; {
	int a;
	for (a=0; a<strlen(text); ++a) text[a]=(text[a]^(((code|128)^a)&0xFF));
	return(0);
	}
