/*	Copyright (c) 1990 UNIX System Laboratories, Inc.	*/
/*	Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF     	*/
/*	UNIX System Laboratories, Inc.                     	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/


#ident	"@(#)bnu:grades.c	1.7.3.1"

#include "uucp.h"
#include <grp.h>

#define G_EXT	0
#define	G_INT	1
#define	G_RES	2
#define	G_ACT	3
#define	G_IDF	4
#define	G_MAX	512	/* max number of fields in the Grades file line */
#define	SMBUF	128

#define PERMP1	1
#define PERMP2	2
#define PERMP3	3
#define	RESTP1	4
#define	RESTP2	5
#define	RESTP3	6
#define	RESTP4	7
#define	NEXTP1	8
#define	NEXTP2	9
#define	NEXTP3	10

#define	TYPE	0
#define FILE1	1
#define	FILE2	2
#define	USER	3
#define	OPTS	4
#define	FILE3	5

static char *grd_err[] = {
	"",
	"User does not have permission to use this service grade",
	"Job has not been queued.",
	"Use (uuglist) to find which service grades you can queue to.",
	"Job size",
	"exceeds maximum number of bytes",
	"allowed into this service grade",
	"Job queued to default grade.",
	"service grade",
	"does not exist on this machine. Job not queued.",
	"Use (uuglist) to find which service grades are available on this machine.",
};

extern int rdfulline(), jsize(), gdirf(), gnamef();
extern void wfcommit();

static void	mailAdmin();		/* Send mail to administrator. */

/* 
 * chkgrp - checks to see the group has permission 
 *		to use a service grade queue.
 *
 * returns
 *
 *	SUCCESS - if the group has permissions
 *	FAIL - if group does not
 *
 */

static int
chkgrp(carray,na)
char **carray;
int na;
{
	struct group *grp;
	int i;
	gid_t gid;

	gid = getgid();
	grp = getgrgid(gid);

	for (i = G_IDF; i < na; i++) 
			if (EQUALS(carray[i], grp->gr_name))
				return(SUCCESS);

	return(FAIL);
}

/*
 * chkusr - checks the permission fields of the Grades file
 *	    to determine if the user can queue to a particular service grade.
 *
 * returns
 *
 *	SUCCESS - if the user can queue to the service grade.
 *	FAIL - if the user can not queue to this service grade.
 *
 */

static int
chkusr(carray, na)
char **carray;
int na;
{
	int i;

	/*
	 * start at the point where the users are supposed to be in the
	 * Grades file. Loop thru until the end of the user list is 
	 * found or the user name is found. If the user name is found then
	 * return TRUE. If the end of the list is found, return FAIL.
	 */

	DEBUG(9, "User (%s)\n", User);

	/* check for any user and return if so */

	if (EQUALS(carray[G_IDF], "Any"))
		return(SUCCESS);

	DEBUG(9, "Members of administrator defined service grade (%s)\n", carray[G_EXT]);

	for (i = G_IDF; i < na; i++) {
		DEBUG(9, "%s\n", carray[i]);
		if (EQUALS(User, carray[i]))
			return(SUCCESS);
	}

	return(FAIL);
}

/*
 *	fgrade - finds the appropiate queue to queue a job into
 *
 *	returns
 *		SUCCESS	-> found a queue
 *		FAIL	-> can't find a queue
 */

int
fgrade(scfile)
struct cs_struct *scfile;
{
	char fdgrade();
	FILE *cfd;
	char line[BUFSIZ];
	char *carray[G_MAX];
	long climit;

	/* Check for the default service grade first */

	if (strcmp(scfile->sgrade, "default") == 0) {
		scfile->grade = fdgrade();
		return(SUCCESS);
	}

	/* open grades file to begin a linear for the grade requested */

	cfd = fopen(GRADES, "r");

	/* loop until the file is empty or we find the grade we want */

	while (rdfulline(cfd, line, BUFSIZ) != 0) {
		(void) getargs(line, carray, G_MAX);

		/* check to see if this is the grade we want */

		if (!EQUALS(scfile->sgrade, carray[G_EXT])) 
			continue;

		if (jsize(scfile, carray[G_RES], &climit) != FAIL) {
			(void) fclose(cfd);
			scfile->grade = *carray[G_INT];
			return(SUCCESS);
		}
	}

	(void) fclose(cfd);

	(void) fprintf(stderr, "%s (%ld bytes) %s (%ld bytes) %s (%s).\n%s\n",
		grd_err[RESTP1], scfile->jsize, grd_err[RESTP2],
		climit, grd_err[RESTP3], scfile->sgrade, grd_err[RESTP4]);

	scfile->grade = fdgrade();
	return(SUCCESS);
}

/*
 *	fdgrade - finds the default queue for this system
 *
 *	returns
 *		a one char name for the default queue
 *
 */

char
fdgrade()
{
	FILE *cfd;
	char line[BUFSIZ];
	char *carray[G_MAX];

	/* Check for the default grade first */

		cfd = fopen(GRADES, "r");

		/* loop until the end of the file is read */

		for (; rdfulline(cfd, line, BUFSIZ) != 0;) {

			/* parse the fields of this line */

			(void) getargs(line, carray, G_MAX);

			/* check to see if the administrator has defined 
			 * a default grade for the machine.
			 */

			if (strcmp(carray[G_EXT], "default") != 0)
				continue;

			/* default must be defined in the file
			 *  close the file, get the queue name, and return.
			 */

			(void) fclose(cfd);
			return(*carray[G_INT]);
		}

		/* no default defined in this file. close file.
		 * get our default queue and return.
		 */

		(void) fclose(cfd);
		return(D_QUEUE);
}

/*
 * job_size - determines the size of a job
 *
 * returns
 *
 *	SUCCESS - if the size of the job can be determined 
 *	FAIL	- otherwise
 */

int
job_size(scfile)
struct cs_struct *scfile;
{
	extern int Dfileused;
	struct stat s;
	FILE *fp;
	char line[BUFSIZ];
	char *carray[G_MAX];
	int na;
	int nodfile = FALSE;
	int ret;

	scfile->jsize = 0;

	fp = fopen(scfile->file, "r");
 
	if (fp == NULL) {
		toCorrupt(scfile->file);
		errent(Ct_OPEN, scfile->file, errno, __FILE__,  __LINE__);
	}

	while (fgets(line, BUFSIZ, fp) != NULL) {
		na = getargs(line, carray, G_MAX);

		if (na < 6) {
			(void) fclose(fp);
			toCorrupt(scfile->file);
			errent("BAD NUMBER OF ARGUMENTS", scfile->file, 0, 
				__FILE__, __LINE__);
		}

		/* if the type of a transfer is not a push
		 * then don't try to determine the size of
		 * the data file, because you can't.
		 */

		if (*carray[TYPE] == 'R')
			continue;

		/* find the data dile that is to be transferred */

		if ((ret = stat(carray[FILE3], &s)) != 0) {
			if (errno == ENOENT) {
				nodfile = TRUE;
				ret = stat(carray[FILE1], &s);
			}
		}
		else
			Dfileused = TRUE;

		/*
		 * check to see if the return code from stat was 0
		 * if return code was not 0, write message to error
		 * log and quit. Otherwise, add size of file to job
		 * size and continue looping.
		 */

		if (ret != 0) {
			(void) fclose(fp);
			errent(Ct_STAT, nodfile ?
				carray[FILE1] : carray[FILE3], errno,
				__FILE__, __LINE__);
		}

		nodfile = FALSE;
		scfile->jsize += s.st_size;
	}
	(void) fclose(fp);
	return(SUCCESS);
}

/*
 * jsize - determines whether if a job is small enough to
 * 	   be placed in the appropiate queue.
 *
 * returns
 *
 *	SUCCESS - if the size of the job is less than or
 *		  equal to the number of bytes in the restriction
 *		  of the GRADES file.
 *
 *	FAIL	- otherwise
 */

int
jsize(scfile, climit, nlimit)
struct cs_struct *scfile;
char *climit;
long *nlimit;
{
#define ONE_K (1024)
#define ONE_MEG ((1024)*(1024))

	static void lcase();
	char rest[SMBUF];
	char msg[BUFSIZ], *p;

	if (EQUALS(climit, "Any"))
		return(SUCCESS);

	lcase(climit, rest, SMBUF);

	if (!(p = strchr(rest, 'k')) && (!(p = strchr(rest, 'm')))) {

		for(p = climit; *p; ++p) {
			if (isdigit(*p))
				continue;

			/* corrupt restriction field in the Grades file.
			 * report it to the uucp administrator.
			 */

			sprintf(msg, "Error encountered in the restrictions field of the Grades file. Field contents (%s).", climit);
			mailAdmin(msg);
			return(SUCCESS);
		}

		*nlimit = atol(climit);
	}
	else if (*p == 'k') {
		*p = '\0';
		*nlimit = (long) (atof(rest) * ONE_K);
	}
	else {
		*p = '\0';
		*nlimit = (long) (atof(rest) * ONE_MEG);
	}

	if (scfile->jsize <= *nlimit)
		return(SUCCESS);
	else
		return(FAIL);
}

static void
lcase(s, t, lim)
char s[], t[];
int lim;
{
	char *p;
	int i;
	

	p = s;

	for (i = 0; i < lim-1 && *p; i++)
		if (isupper(*p))
			t[i] = tolower(*p++);
		else
			t[i] = *p++;

	t[i] = '\0';
	return;
}

/*
 * mailAdmin - mail a message to the uucp administrator.
 *
 * returns:
 *
 *	nothing
 */

static void
mailAdmin (msg)

char *	msg;

{
	char	cmd[BUFSIZ];		/* Place to build mail command. */
	FILE *	mail;			/* Channel to write mail on. */

	(void) sprintf(cmd, "%s %s %s", PATH, MAIL, "uucp");
	if ((mail = popen(cmd, "w")) != (FILE *) NULL)
	{
		(void) fprintf(mail, "\n%s\n", msg);
		(void) pclose(mail);
	}

	/*
	 * Ignore popen failure.  There is not much that we can do if
	 * it fails, since we are already trying to notify the administrator
	 * of a problem.
	 */
	return;
}

/*
 * putdfiles - moves any and all of the D. to the spool directory for
 * 	       a C. file.
 *
 * returns
 *
 *	nothing
 */

void
putdfiles(scfile)
struct cs_struct scfile;
{
	FILE *fp;
	char line[BUFSIZ];
	char *carray[G_MAX];
	int na;
	struct stat s;

	fp = fopen(scfile.file, "r");

	if (fp == NULL) {
		toCorrupt(scfile.file);
		errent(Ct_OPEN, scfile.file, errno, __FILE__, __LINE__);
	}

	while (fgets(line, BUFSIZ, fp) != NULL) {

		na = getargs(line, carray, G_MAX);
		if (na < 6) {
			(void) fclose(fp);
			toCorrupt(scfile.file);
			errent("BAD NUMBER OF ARGUMENTS", scfile.file, 0,
				__FILE__, __LINE__);
		}

		if (*carray[TYPE] == 'R')
			continue;

	    	/* move D. file to the spool area */

		if (stat(carray[FILE3], &s) != -1)
			wfcommit(carray[FILE3], carray[FILE3], scfile.sys);
	}

	(void) fclose(fp);
	return;
}

/*
 * reads a line from a file and takes care of comment lines
 * and continuations (\) in last column.
 *
 * return:
 *	the number of chars that are placed in line.
 */

int
rdfulline(fd, line, lim)
FILE *fd;
char *line;
int lim;
{
	register char *p, *c;
	char buf[BUFSIZ];
	size_t blr, btox;

	p = line;
	for (;fgets(buf, BUFSIZ, fd) != NULL;) {
		/* check to see if it is a comment */

		if (buf[0] == '#')
			continue;

		/* remove trailing white space */
		c = &buf[strlen(buf)-1];
		while (c>=buf && (*c == '\n' || *c == '\t' || *c == ' ') )
			*c-- = NULLCHAR;

		if (buf[0] == '\n' || buf[0] == NULLCHAR)
			continue;

		blr = lim - 1 - (p - line);
		btox = blr < strlen(buf) ? blr : strlen(buf);

		if (btox <= 0)
			break;

		(void) strncpy(p, buf, btox);
		p += btox - 1;

		if ( *(p-1) == '\\')
			p--;
		else
			break;
	}

	*++p = '\0';
	return(p-line-1);
}

/*	upermit - checks to determine if the user has permissions
 *	to use administrator defined service grade.
 *
 *	returns
 *		SUCCESS -> if the user can queue to this service grade.
 *		FAIL -> if the user cannot queue to this service grade.
 */

int
upermit(carray, na)
char **carray;
int na;
{
#define G_USR "user"
#define G_NUSR "non-user"
#define G_GRP "group"
#define G_NGRP "non-group"

	static void lcase();
	char actn[SMBUF];
	char ufld[SMBUF];
	char msg[BUFSIZ];

	(void) strcpy(actn, carray[G_ACT]);
 
	lcase(actn, ufld, SMBUF);

	if (EQUALS(ufld, G_USR))
		return(chkusr(carray,na));

	if (EQUALS(ufld, G_NUSR))
		return((chkusr(carray, na) != SUCCESS) ? SUCCESS : FAIL);

	if (EQUALS(ufld, G_GRP))
		return(chkgrp(carray, na));

	if (EQUALS(ufld, G_NGRP))
		return((chkgrp(carray, na) != SUCCESS) ? SUCCESS : FAIL);

	(void) sprintf(msg, "Error encountered in action field of the Grades file. Field contents (%s).", carray[G_ACT]);
	mailAdmin(msg);
	return(FAIL);
}

/*
 *	vergrd - verify if the grade name is a valid administrator
 *		 defined service grade name and if the user has the
 *		 appropiate permission to use this grade.
 *
 *	returns
 *		SUCCESS	-> grade is valid and user is 
 *			   permitted to use this grade.
 *		FAIL	-> otherwise
 *
 */

int
vergrd(grade)
char *grade;
{
	FILE *cfd;
	char line[BUFSIZ];
	char *carray[G_MAX];
	int na;

	/* Check for the default grade first */

	if (EQUALS(grade, "default"))
		return(SUCCESS);

	/* open grades file to begin a linear for the grade requested */

	cfd = fopen(GRADES, "r");

	/* loop until the file is empty or we find the grade we want */

	while (rdfulline(cfd, line, BUFSIZ) != 0) {
		na = getargs(line, carray, G_MAX);

		/* check to see if this is the grade we want */

		if (!EQUALS(grade, carray[G_EXT])) 
			continue;

		/* check for the permission on this grade */

		if (upermit(carray, na) != FAIL) {
			(void) fclose(cfd);
			return(SUCCESS);
		}
		else {
			(void) fclose(cfd);
			(void) fprintf(stderr, "%s (%s).\n %s\n%s\n", grd_err[PERMP1],
				grade, grd_err[PERMP2], grd_err[PERMP3]);
			return(FAIL);

		}
	}

	(void) fclose(cfd);

	(void) fprintf(stderr, "%s (%s) %s\n%s\n", grd_err[NEXTP1],
		grade, grd_err[NEXTP2], grd_err[NEXTP3]);
	return(FAIL);
}

/*
 * wfremove - removes a C. file from the Workspace directory and all of its
 * D. files.
 */

void
wfremove(file)
char *file;
{
	FILE *fp;
	char line[BUFSIZ];
	char *carray[G_MAX];
	int na;
	struct stat s;

	fp = fopen(file, "r");

	if (fp == NULL) {
		toCorrupt(file);
		errent(Ct_OPEN, file, errno, __FILE__, __LINE__);
	}
 
	while (fgets(line, BUFSIZ, fp) != NULL) {
		na = getargs(line, carray, G_MAX);

		if (na < 6) {
			(void) fclose(fp);
			toCorrupt(file);
			errent("BAD NUMBER OF ARGUMENTS", file, 0,
				__FILE__, __LINE__);
		}

		if (*carray[TYPE] == 'R')
			continue;

	    	/* remove D. file */

	    	DEBUG(4, "Removing data file (%s)\n", carray[FILE3]);

		if ((stat(carray[FILE3], &s) != -1) && (unlink(carray[FILE3]) != 0)) {
			(void) fclose(fp);
			toCorrupt(file);
			toCorrupt(carray[FILE3]);
			errent(Ct_UNLINK, carray[FILE3], errno, __FILE__,
				__LINE__);
		}
	}

	(void) fclose(fp);

	DEBUG(4, "Removing work file (%s)\n", file);

	if (unlink(file) != 0) {
		toCorrupt(file);
		errent(Ct_UNLINK, file, errno, __FILE__, __LINE__);
	}
	return;
}

/*
 * findgrade - finds the highest priority job grade that is not locked
 * and that has jobs.
 *
 * job grade name is null, if no job grade is found.
 */

void
findgrade(dir, jobgrade)
char *dir, *jobgrade;
{
	char prevgrade[MAXBASENAME+1], curgrade[MAXBASENAME+1],
	     gradedir[MAXBASENAME+1];
	char lockname[MAXFULLNAME];
	char Cfile[MAXBASENAME+1];
	DIR *p, *q;

	*prevgrade = NULLCHAR;
	p = opendir(dir);
	ASSERT(p != NULL, Ct_OPEN, dir, errno);

	while (gdirf(p, gradedir, dir) == TRUE) {
		(void) sprintf(lockname, "%s.%.*s.%s", LOCKPRE, SYSNSIZE,
		    Rmtname, gradedir);
		if (cklock(lockname) == FAIL)
			continue;
		q = opendir(gradedir);		
		ASSERT(q != NULL, Ct_OPEN, gradedir, errno);
		while (gnamef(q, Cfile) == TRUE) {
			if (Cfile[0] == CMDPRE) {
				if (*prevgrade == NULLCHAR) {
					(void) strcpy(prevgrade, gradedir);
					break;
				}
				(void) strcpy(curgrade, gradedir);
				if (strcmp(curgrade, prevgrade) < 0)
					(void) strcpy(prevgrade, curgrade);
			}
		}
		closedir(q);
	}
	closedir(p);
	(void) strncpy(jobgrade, prevgrade, MAXBASENAME);
	jobgrade[MAXBASENAME] = NULLCHAR;
	return;
}
	
