#include	"db.h"
#include	<stdio.h>
#include	<ndbm.h>
#include	<sys/file.h>
#include	"inet.h"
#include	<ctype.h>
#include	<signal.h>
#include	<errno.h>
#include	<sys/types.h>
#include	<sys/wait.h>
#include	<fcntl.h>

#include <krb.h>
#include <netinet/in.h>
#include <time.h>


#define ISWHITESPACE(foo) ((foo) == ' ' || (foo) == '\t')

#define	NOTFOUND	3

/*
** For authorization purposes:  Operations you can do on a form
*/

#define	FORM_READ	101
#define	FORM_WRITE	102

DBM			*db;	/* dbm database */
int			dbfd;	/* file descriptor for database lock file */

int                     sockfd, clilen, childpid;
struct sockaddr_in      cli_addr,serv_addr;

void	fireman();
unsigned char	*ReadBinaryData();

int	newsockfd;

char	username[LOGINLEN];	/* set when authenticated */
static char	*superusers[] = {"heskett", "rlgogol", "jenlu",NULL};


AUTH_DAT	*ValidateAuth();

/*
** Table of which functions get called for each command.
*/

static void Open();
static void Authenticate();
static void Get();
static void Checkout();
static void Checkin();
static void Newnamed();
static void Put();
static void Delete();
static void Quit();
static void Iam();
static void Showassign();

typedef struct { 
        char *string;
        void (* function)();
} Command;

Command	commands[] = {
	{ "Open", Open },
	{ "Authenticate", Authenticate },
	{ "Get", Get },
	{ "Checkout", Checkout },
	{ "Checkin", Checkin },
	{ "Newnamed", Newnamed },
	{ "Put", Put },
	{ "Delete", Delete },
	{ "Quit", Quit },
	{ "Iam", Iam },
	{ "Showassign", Showassign },
	{ NULL, NULL }
};

/*
** List of field names and what offset into the StudentRec structure
** they correspond to.  "RecOffset" is based on XtOffset from Intrinsic.h.
*/

typedef struct { 
        char	*string;
	int	offset;
	int	type;
} Datarec;

#define RecOffset(field) ((unsigned int)&(((StudentRecPtr)NULL)->field))



#define	TYPESTRING	1
#define	TYPEINT		2
#define	TYPEBOOLEAN	3

Datarec	records[] = {
	{ "login",	RecOffset(login),			TYPESTRING },
	{ "course1",	RecOffset(selections[0].course),	TYPESTRING },
	{ "course2",	RecOffset(selections[1].course),	TYPESTRING },
	{ "course3",	RecOffset(selections[2].course),	TYPESTRING },
	{ "course4",	RecOffset(selections[3].course),	TYPESTRING },
	{ "course5",	RecOffset(selections[4].course),	TYPESTRING },
	{ "course6",	RecOffset(selections[5].course),	TYPESTRING },
	{ "section1",	RecOffset(selections[0].section),	TYPEINT },
	{ "section2",	RecOffset(selections[1].section),	TYPEINT },
	{ "section3",	RecOffset(selections[2].section),	TYPEINT },
	{ "section4",	RecOffset(selections[3].section),	TYPEINT },
	{ "section5",	RecOffset(selections[4].section),	TYPEINT },
	{ "section6",	RecOffset(selections[5].section),	TYPEINT },
	{ "coursea",	RecOffset(assignment.course),		TYPESTRING },
	{ "sectiona",	RecOffset(assignment.section),		TYPEINT },
	{ "deenroll",	RecOffset(deenroll),			TYPEBOOLEAN },
	{ "manualentry", RecOffset(manualentry),		TYPEBOOLEAN },
	{ "messagesent", RecOffset(messagesent),		TYPEBOOLEAN },
	{ "owner",	RecOffset(owner),			TYPESTRING },
	{ NULL,		NULL,					NULL }
};


/*
** Code taken from 
** /afs/athena.mit.edu/astaff/project/forms/src/tcpexample/server.c
** and ~jis/cmi.dir/cmisrv.c
*/

struct sockaddr_in	cli_addr,serv_addr;
int			kerberized = True;

main (argc, argv)
int	argc;
char	*argv[];
{
	int			sockfd, clilen, childpid;

	if (argc > 1 && !strcmp (argv[1], "-nokerb"))
		kerberized = False;

	username[0] = '\0';


/*
**  Open a TCP socket.
*/
	if ( (sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
		err_dump ("can't open stream socket");

/*
**  Bind our local address so that the client can send to us.
*/

	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 (SERV_TCP_PORT);

	if (bind (	sockfd, 
			(struct sockaddr *) &serv_addr, 
			sizeof (serv_addr)) < 0)

		err_dump ("can't bind local address");

	listen(sockfd, 5);

/*
**  Watch for dying children
*/
	signal (SIGCHLD, fireman);

/*
**  I'm a concurrent server.  Wait for a connection from a client and
**  fork a copy of myself to deal with it.
*/

	for (; ; ) {
		clilen = sizeof (cli_addr);
ACCEPT:
		newsockfd = accept(	sockfd, 
					(struct sockaddr*) &cli_addr,
					&clilen);

/*
** Dying children cause an interrupt; we need to restart the accept.
** Otherwise, we terminate.
*/
		if (newsockfd < 0)
			if (errno == EINTR)
				goto ACCEPT;
			else
				err_dump("accept error");
		
#ifdef	CONCURRENT
		if ( (childpid = fork()) < 0)
			err_dump("fork error");
/*
** If I'm the child, close the original socket, process the request, halt.
*/
		else if (childpid == 0) {
			close (sockfd);
#endif
			if (fcntl(newsockfd, F_SETOWN, getpid()) < 0)
				err_dump("fcntl error");
			HandleClient (newsockfd);
#ifdef	CONCURRENT
			exit(0);
		}
#endif

		close (newsockfd);
	}
}



/*
** Read the stream socket one line at a time, figure out which command
** it is, and run the appropriate routine.
** Return when the connection is terminated.
*/

HandleClient (sfd)
int	sfd;
{
	int	n;
	char	line[MAXLINE];

	username[0] = '\0';
/*
** Get a file descriptor for the database lock file.
*/

	dbfd = open (DATAFILE, O_RDWR, 0600);

	if (dbfd == -1) {
		dump ("691 Cannot locate database lock file\n");
		err_dump("HandleClient:  lock file error");
	}


	for (; ;) {
		n = readline (sfd, line, MAXLINE);
		if (n == 0) {
			flock (dbfd, LOCK_UN);
			return;		/* connection terminated */
		}
		else if (n < 0) {
			flock (dbfd, LOCK_UN);
			err_dump("HandleClient:  readline error");
		}

		for (n = 0; commands[n].string; n++) {
			if (!strncasecmp (line, 
					commands[n].string, 
					strlen(commands[n].string))) {
				(commands[n].function) (line);
				break;
			}
		}
		if (!commands[n].string)
			dump ("500 Unknown command\n");
	}
}

/*
** Each of these action routines is passed the command line issued by
** the client.  The routines respond to the client and return.
*/

static void
Open(commandline)
char	*commandline;
{
	int	version;
	dump	("001 I'm opening\n");
}

/*
** Format of an authentication line is 
** "Authenticate (bytecount) newline bytes newline"
** where "bytes" are a Kerberos authenticator.
*/

static void
Authenticate(commandline)
char	*commandline;
{
	int	incomingbytes;
	char	*leftparen, *rightparen;
	unsigned char	*bindata;
	int	invalid;
	AUTH_DAT	*data;
/*
** Look for the byte count and prepare to read that many bytes
*/

	leftparen = (char *) strchr(commandline, '(');

	if (!leftparen) {
		dump ("502 no byte count for binary data\n");
		return;
	}

	incomingbytes = atoi (leftparen + 1);
	debug ("Expecting %d incoming bytes of binary data\n", incomingbytes);

	bindata = ReadBinaryData(incomingbytes);
	if (!bindata) {
		dump ("520 Error while reading authenticator\n");
		username[0] = '\0';
		return;
	}

	if (!kerberized) {
		free (bindata);
		dump ("001 Got your ticket, but I'm running non-Kerberized anyway\n");
		return;
	}

	data = ValidateAuth(bindata);
	if (data) {
		dump ("001 Authenticated as %s with incremented checksum of %d\n", data->pname , data->checksum + 1);
		strcpy (username, data->pname);
	}
	else {
		dump ("520 Kerberos authentication failed\n");
		username[0] = '\0';
	}
	free (bindata);
}

/*
** Read "incomingbytes" of binhexed data and convert it as we go.
** With binhex's 2:1 expansion ratio, we need to read 2 x incomingtypes
** characters, plus the newline.
*/

unsigned char	*
ReadBinaryData(incomingbytes)
int	incomingbytes;
{
	unsigned char	*mptr;
	int	n, i;
	char	binhexdata[3];
	unsigned char	*bindata;
	char	tempbuf;

	bindata = (unsigned char *) calloc(incomingbytes, sizeof(unsigned char));
	if (!bindata) {
		dump("691 ReadBinaryData:  Cannot malloc!\n");
		return (NULL);
	}
	binhexdata[2] = '\0';

	for (i = 0, mptr = bindata; i < incomingbytes; i++, mptr++) {
		n = readn (newsockfd, binhexdata, 2);
		if (n < 2) {
			debug("501 ReadBinaryData:  premature end of data\n");
			dump("501 ReadBinaryData:  premature end of data\n");
			free (bindata);
			return (NULL);
		}
/*
** Doing it this roundabout way to avoid unaligned data access complaints.
*/
		sscanf ( binhexdata, "%02x", &tempbuf);
		*mptr = tempbuf;

	}
/*
** Look for and eat the newline
*/
	n = readn (newsockfd, binhexdata, 1);
	if (n <= 0) {
		debug("401 ReadBinaryData:  No newline\n");
		dump("401 ReadBinaryData:  No newline\n");
	}

	if (binhexdata[0] != '\n') {
		debug("401 ReadBinaryData:  Extra data provided\n");
		dump("401 ReadBinaryData:  Extra data provided\n");
		while (binhexdata[0] != '\n' && n == 1)
			n = readn (newsockfd, binhexdata, 1);
	}
	return (bindata);
}


#define	TOKENSIZE	25
static void
Get(commandline)
char	*commandline;
{

	char	type[TOKENSIZE];		/* what type of form */
	char	id[TOKENSIZE];		/* which one of these forms */
	datum	data;
	datum	key;
	StudentRecPtr value;
	StudentRec	realrecord;

	sscanf (commandline, "%*s %s %s", type, id);

	debug ("Getting data for form '%s' of type '%s'\n", id, type);

	key.dptr = id;
	key.dsize = strlen(id);

/*
** Get exclusive access to the database, then open it and read the record.
*/

	flock (dbfd, LOCK_EX);

	db = dbm_open(DATAFILE, O_RDWR, 0600);
	if (db == NULL) {
		dump ("691 can't open database file\n");
		flock (dbfd, LOCK_UN);
		return;
	}

	data = dbm_fetch(db, key);
	if (!data.dptr) {
		dump ("512 Not found\n");
		dbm_close(db);
		flock (dbfd, LOCK_UN);
		return;
	}
	bcopy (data.dptr, &realrecord, sizeof (StudentRec)); 

	dbm_close(db);
	flock (dbfd, LOCK_UN);
	value = &realrecord;

/*
** Am I authorized to look at this form?
*/
	if (!Authorized(value, FORM_READ)) {
		dump ("531 Insufficent authorization to read this form\n");
		return;
	}

	SendToNetwork(value);
}

/*
** Simplistic authorization routine.  Look at the "login" field of this
** form, and return True if the current user is either a superuser or
** the person in "login."  "forwhat" is currently ignored.
*/

Authorized (record, forwhat)
StudentRecPtr	record;
int		forwhat;
{
	int	i;

	if (!kerberized)
		return (True);

	if (!strcmp (record->login, username))
		return (True);

	for (i = 0; superusers[i]; i++) {
		if (!strcmp (superusers[i], username))
			return (True);
	}

	return (False);
}

static void
Checkout(commandline)
char	*commandline;
{
char	*type;		/* what type of form */
char	*id;	/* which one of these forms */
	dump ("001 You want it?  you got it!\n");

}

static void
Checkin (commandline)
char	*commandline;
{
char	*type;		/* what type of form */
char	*id;	/* which one of these forms */
	dump ("001 You don't want it?  Away it goes!\n");
}

/*
ma*
*/

static void
Iam (commandline)
char	*commandline;
{
	int	i;

	for (i = 0; superusers[i]; i++) {
		if (!strcmp (superusers[i], username)){
			sscanf (commandline, "%*s %s", username);
			dump ("001 Operating as user %s\n", username);
			return;
		}
	}
	dump ("530 Not authorized to run this command\n");
	return;
}

/*
** Given a set of new values for a form instance, replace the current
** values and store the updated instance in the database.
*/

static void
Put (commandline)
char	*commandline;
{
	char	type[TOKENSIZE];		/* what type of form */
	char	id[TOKENSIZE];		/* which one of these forms */
	datum	data;
	datum	key;
	StudentRec	realrecord;
	int	i, paircount;
	int	n, retval;
	char	line[MAXLINE];
	char	*stringversion;

	sscanf (commandline, "%*s %s %s (%d)", type, id, &paircount);

	debug ("Expecting %d new values for form '%s' of type '%s'\n", paircount, id, type);

/*
** Get current record for this instance
*/
	key.dptr = id;
	key.dsize = strlen(id);

	flock (dbfd, LOCK_EX);
	db = dbm_open(DATAFILE, O_RDWR, 0600);

	if (db == NULL) {
		dump ("691 can't open database file\n");
		flock (dbfd, LOCK_UN);
		return;
	}
	data = dbm_fetch(db, key);

	if (!data.dptr) {
		debug ("Form %s does not exist\n", id);
		dump ("512 Form does not exist\n");
		dbm_close(db);
		flock (dbfd, LOCK_UN);
		return;
	}

	bcopy (data.dptr, &realrecord, sizeof (StudentRec)); 
	dbm_close(db);
	flock (dbfd, LOCK_UN);

	if (!Authorized(&realrecord, FORM_WRITE)) {
		debug ("Insufficent authorization to write this form\n");
		dump ("534 Insufficent authorization to write this form\n");
		return;
	}

	dump ("001 ready to receive\n");

/*
** Parse the incoming field-value pairs and update the record with
** the new values.
*/

	for (i = 0; i < paircount; i++) {
		n = readline (newsockfd, line, MAXLINE);
		if (n <= 0) {
			debug("Put:  readline error\n");
			dump("502 Put:  readline error\n");
			return;
		}

/*
** We don't want the newline.
*/

		if (line[n - 1] == '\n')
			line[n - 1] = '\0';

		if (ReplaceValue(&realrecord, line) == 0)
			dump ("001 value received\n");
	}

	data.dptr = (char *) &realrecord;

	flock (dbfd, LOCK_EX);
	db = dbm_open(DATAFILE, O_RDWR, 0600);
	if (db == NULL) {
		dump ("690 can't open database file\n");
		flock (dbfd, LOCK_UN);
		return;
	}
	retval = dbm_store(db, key, data, DBM_REPLACE);
	if (retval)
		dump ("690 Can't store into database file\n");
	dbm_close(db);
	flock (dbfd, LOCK_UN);
}

/*
** Given a student record and a line containing a field-value pair,
** locate the matching field in the record and give it the new value.
** Assume there's no newline before the NULL byte..
*/

ReplaceValue(record, line)
StudentRecPtr	record;
char		*line;
{
	int	n;
	char	token[MAXLINE];
	char	*value;
	void	*newptr;
	char	*stringptr;
	int	*intptr;

/*
** Pull out the whitespace-delimited field name from the input line.
*/
	for (n = 0; n < MAXLINE && line[n]; n++) {
		if (ISWHITESPACE(line[n]))
			break;
		else
			token[n] = line[n];
	}
	token[n] = '\0';

/*
** Look for the field name ("token") in the list of records.
*/
	for (n = 0; records[n].string; n++) {
		if (!strcasecmp (token, records[n].string))
			break;
	}

	if (!records[n].string) {
		dump ("551 Fieldname not found\n");
		return(1);
	}

/*
** We've found the field name.  Now skip past whitespace to find the value.
*/
	for (value = &line[strlen(token)]; *value; value++) {
		if (! ISWHITESPACE(*value))
			break;
	}

/* Let's rethink this...Passing no value means set the value to NULL

	if (!*value) {
		dump ("401 No value for field provided\n");
		return(1);
	}
*/

	newptr = (void *) ( (int) record + (int) records[n].offset);

/*
** Okay, now cast pointers all over the place and put the new value
** into the data structure.
*/
	switch (records[n].type) {

	case	TYPESTRING:
		stringptr = (char *) newptr;
/*
		debug (	"Old value of field %s was %s\n",
			records[n].string, stringptr);
*/
		strcpy (stringptr, value);
		debug (	"New value of field %s is %s\n",
			records[n].string, stringptr);
		break;

	case	TYPEINT:
		intptr = (int *) newptr;
/*
		debug (	"Old value of field %s was %d\n",
			records[n].string, *intptr);
*/
		*intptr = atoi(value);
		debug (	"New value of field %s is %d\n",
			records[n].string, *intptr);
		break;

	case	TYPEBOOLEAN:
		intptr = (int *) newptr;
/*
		debug (	"Old value of field %s was %s\n",
			records[n].string,
			*intptr ? "True" : "False");
*/

		if (strcasecmp (value, "true"))
			*intptr = 0;
		else
			*intptr = 1;

		debug (	"New value of field %s is %s\n",
			records[n].string,
			*intptr ? "True" : "False");
		break;
	}
	return (0);
}

/*
** Create the record for a given login
*/

static void
Newnamed(commandline)
char	*commandline;
{
	char	type[TOKENSIZE];		/* what type of form */
	char	id[TOKENSIZE];		/* which one of these forms */
	datum	data;
	datum	key;
	StudentRecPtr value;
	int	retval;

	sscanf (commandline, "%*s %s %s", type, id);
	debug ("Creating form '%s' of type '%s'\n", id, type);

	key.dptr = id;
	key.dsize = strlen(id);
	flock (dbfd, LOCK_EX);
	db = dbm_open(DATAFILE, O_RDWR, 0600);
	if (db == NULL) {
		dump ("690 can't open database file\n");
		flock (dbfd, LOCK_UN);
		return;
	}
	data = dbm_fetch(db, key);

	if (data.dptr) {
		debug ("Form %s already exists, not creating it\n", id);
		dump ("513 Form already exists\n");
		dbm_close(db);
		flock (dbfd, LOCK_UN);
		return;
	}

	value = (StudentRecPtr) calloc(1, sizeof(StudentRec));
	if (!value) {
		dump("691 Newnamed:  Cannot malloc!\n");
		dbm_close(db);
		flock (dbfd, LOCK_UN);
		return;
	}

	strcpy (value->login, id);

	data.dptr = (char *) value;
	data.dsize = sizeof(StudentRec);

	retval = dbm_store(db, key, data, DBM_INSERT);
	dbm_close(db);
	flock (dbfd, LOCK_UN);

	if (retval == 1) {
		debug ("Form %s already exists, not creating it (How'd I get here?)\n", id);
		dump ("513 Form already exists (How'd I get here?)\n");
		return;
	}
/*
	ComplexDump(value);
*/
	dump ("001 Created\n");
	free(value);
}

/*
** Delete the record for a given login
*/

static void
Delete(commandline)
char	*commandline;
{

	char	type[TOKENSIZE];		/* what type of form */
	char	id[TOKENSIZE];		/* which one of these forms */
	datum	data;
	datum	key;
	StudentRecPtr value;
	StudentRec	realrecord;
	int	retval;
	sscanf (commandline, "%*s %s %s", type, id);

	debug ("Deleting form '%s' of type '%s'\n", id, type);

	key.dptr = id;
	key.dsize = strlen(id);

/*
** Get exclusive access to the database, then open it and delete the record.
*/

	flock (dbfd, LOCK_EX);

	db = dbm_open(DATAFILE, O_RDWR, 0600);
	if (db == NULL) {
		dump ("691 can't open database file\n");
		flock (dbfd, LOCK_UN);
		return;
	}

	retval = dbm_delete(db, key);

	if (retval) {
		dump ("512 Not able to delete record\n");
		dbm_close(db);
		flock (dbfd, LOCK_UN);
		return;
	}

	dbm_close(db);
	flock (dbfd, LOCK_UN);
	dump ("001 Deleted okay\n");
	return;
}



#ifdef DEBUG
DebugDump(value)
StudentRecPtr value;
{
	int	i;
	printf ("Dump of %d:\n", value);
	printf ("login(%d):	%s\n", &(value->login), value->login);

	for (i = 0; i < 6; i++) {
		printf ("Selection %d (%d) : %s section %d\n",
				i,
				&(value->selections[i]),
				value->selections[i].course,
				value->selections[i].section);
	}
	printf ("Assignment (%d): %s section %d\n",
			&(value->assignment),
			value->assignment.course,
			value->assignment.section);

	printf ("Deenroll flag (%d) is %s\n", &(value->deenroll), value->deenroll  ? "True" : "False");
	printf ("Manualentry flag (%d) is %s\n", &(value->manualentry), value->manualentry  ? "True" : "False");
	printf ("Messagesent flag (%d) is %s\n", &(value->messagesent), value->messagesent  ? "True" : "False");

	printf ("Checked out by (%d) %s\n", value->owner, value->owner);
}
#endif

/*
** Given a student record, dump its field-value pairs to the output socket
** in printable strings.
*/

SendToNetwork(value)
StudentRecPtr value;
{
	int	i;
	dump ("001 (19)\n");

	dump ("login \"%s\"\n", value->login);

	for (i = 0; i < 6; i++) {
		dump ("course%d %s\n",
				i + 1,
				value->selections[i].course);

		dump ("section%d %d\n",
				i + 1,
				value->selections[i].section);
	}

	dump ("coursea %s\n", value->assignment.course);
	dump ("sectiona %d\n", value->assignment.section);

	dump ("deenroll %s\n", value->deenroll ? "True" : "False");
	dump ("manualentry %s\n", value->manualentry ? "True" : "False");
	dump ("messagesent %s\n", value->messagesent ? "True" : "False");

	dump ("owner %s\n", value->owner);
}

dump (format, a, b, c, d, e, f, g)
char	*format;
void	*a, *b, *c, *d, *e, *f, *g;
{
	char	outputbuffer[MAXLINE];
	int	bufferlen;

	sprintf (outputbuffer, format, a, b, c, d, e, f, g);
	bufferlen = strlen(outputbuffer);

	if (writen (newsockfd, outputbuffer, bufferlen) != bufferlen)
		 err_dump ("str_echo:  writen error");
}

debug (format, a, b, c, d, e, f, g)
char	*format;
void	*a, *b, *c, *d, *e, *f, *g;
{
#ifdef	DEBUG
	fprintf (stderr, format, a, b, c, d, e, f, g);
	fflush (stderr);
#endif
}

/*
** Catch dying children
*/

void
fireman()
{
	union wait	wstatus;

	while (wait3 (&wstatus, WNOHANG, NULL) > 0)
		;
}

ComplexDump(record)
StudentRecPtr	record;
{
	int	n;
	void	*newptr;

	debug ("ComplexDump of record %d\n", (int) record);
	for (n = 0; records[n].string; n++) {

	newptr = (void *) ( (int) record + (int) records[n].offset);
	debug ("Off %d, ptr %d: ", records[n].offset, newptr);

	switch (records[n].type) {
	case	TYPESTRING:
		debug (	"(String) field %s is %s\n",
			records[n].string,
			(char *) newptr);
		break;

	case	TYPEINT:
		debug (	"(Int) field %s is %d\n",
			records[n].string, 
			* (int *) newptr);
		break;

	case	TYPEBOOLEAN:
		debug (	"(Bool) field %s is %s\n",
			records[n].string,
			(* (int *) newptr) ? "True" : "False");
		break;
	}
	}
}

/*
** Check the validity of a Kerberos authenticator and return an AUTH_DAT
** if valid, NULL if invalid.
*/

AUTH_DAT	*
ValidateAuth(bindata)
unsigned char	*bindata;
{
	KTEXT_ST	authent;
	static AUTH_DAT	ad;
	int		status;
	char		*cp;
	char		sinst[INST_SZ];
	char		*krb_get_phost();


	(void) bcopy(bindata, (char *) &authent, sizeof(authent));

	cp = krb_get_phost(SERV_HOST_NAME);
	strcpy(sinst, cp);

	status = krb_rd_req(	&authent, "rcmd", sinst,
				cli_addr.sin_addr.s_addr, &ad, "");

	if (status != KSUCCESS) {
		debug("ValidateAuth: Kerberos failure: %s\n", krb_err_txt[status]);
		return(NULL);
	}
	if ((strlen(ad.pinst) != 0) || (strcmp(ad.prealm, "ATHENA.MIT.EDU"))) {
		debug("cmisrv: %s.%s@%s -- Not null instance ATHENA realm.\n",
				ad.pname, ad.pinst, ad.prealm);
		return(NULL);
	} 
	return (&ad);
}

static void
Quit (commandline)
char	*commandline;
{
	exit(1);
}

static void
Showassign (commandline)
char	*commandline;
{
	char	systemline[100];
	int	retval;

	if (username[0] == '\0') {
		dump ("531 Insufficent authorization to read this form\n");
		return;
	}
	sprintf (systemline, "date > /var/tmp/logfile");
	retval = system (systemline);

	/*sprintf (systemline, "/var/hass-d/bin/kicksend %s\n", username);*/
	sprintf (systemline, "/var/pelott-dev/bin/kicksend %s\n", username);
	retval = system (systemline);
	if (retval == -1)
		dump ("690 Failed to read assignment file\n");
	else
		dump ("001 Resend of assignment kicked off, status %d\n", retval);
	return;
}
