#include "lawloader.h"

/* law file is of the form
 * <user|group> <username|groupname> <operation> <operand1> <comparison> <operand2>
 */
int main (int argc, char **argv) 
{
	FILE *lawfile;
	FILE *lsmlgi;
	struct law_struct compiled_law[1000];
	int rc = 0,index = 0;
	int lineno = 0;
	char line[LAWLINELENGTH];
	char *ch;
	char *word;
	char *filename;
	/* for username/group parsing */
	int uid = -1, gid = -1;	
	int user = 0;
	struct passwd *pw;
	struct group *gr;
	/* operation */
	int oper = -1;
	/* operands */
	int oper1 = -1,oper2 = -1;
	/* comparison */
	int comp = -1;
	
	int c, debug = 0;
	extern char *optarg;
	extern int optind, optopt, opterr;

	while ((c = getopt(argc, argv, ":df:")) != -1) {
		switch(c) {
		case 'd':
			debug = 1;
			break;
		case 'f':
			filename = optarg;
			break;
		case ':':
			printf("-%c without filename\n", optopt);
			usage(argv[0]);
			return(1);
			break;
		case '?':
			printf("unknown arg %c\n", optopt);
			usage(argv[0]);
			return(1);
			break;
		}
	}
	if (filename == NULL) {
		fprintf(stderr, "Error: no filename provided\n");
		usage(argv[0]);
		return(2);
	}

	if (( lawfile = fopen( filename, "r")) == NULL) {
		fprintf( stderr, "Error: %s opening file %s\n",
			strerror(errno), filename);
		exit(2);
	}	
	else {
		/* create a law buffer */
		if ((lsmlgi = fopen("/proc/lsmlgi", "w")) == NULL) {
			fprintf(stderr, "Error: %s opening lawfile\n",strerror(errno));
			exit(3);
		}
		/* we've opened the file, now read it one line at a time */
		while (fgets(line,LAWLINELENGTH, lawfile) != NULL ) {
			ch = (char *) &line;	

			/* first word should be either user or group */
			word = ch;
			ch = get_next_word(ch);
			if (word == NULL) {
				fprintf( stderr, 
				"Error: unable to parse word on line %i of %s\n",
				lineno,argv[1]);	
			}	
			if (!strcmp(word,"user")) user = 1;
			else if (!strcmp(word,"group")) user = 0;
			else if (strcmp(word,"#") > 0) {
				/* this line is a comment in the law file */
				continue;
			}
			else {
				fprintf( stderr,
					"Error: user should be user or group at line %i\n",lineno);
			}

			/* second word should be a username or group */
			word = ch;
			ch = get_next_word(ch);
			if (word == NULL) {
				fprintf( stderr, 
				"Error: unable to parse word on line %i of %s\n",
				lineno,argv[1]);	
			}	
			if (user) {
				/* convert this username to a uid */
				pw = getpwnam(word);
				if (pw == NULL) {
					fprintf( stderr,
						"Error: cannot convert username to uid for %s\n",
						word);
					return errno;
				} else {
					uid = pw->pw_uid;
				}
				
			} else {
				/* convert this gropu to a gid */
				gr = getgrnam(word);
				if (gr == NULL) {
					fprintf(stderr,
						"Error: cannot convert group to gid for %s\n",
						word);
					return errno;
				} else {
					gid = gr->gr_gid;
				}
			}
				
			/* third word is the operation */
			word = ch;
			ch = get_next_word(ch);
			if (!strcmp(word,"read")) oper = LSMLGI_READ;
			else if (!strcmp(word,"write")) oper = LSMLGI_WRITE;
			else if (!strcmp(word,"exec")) oper = LSMLGI_EXEC;
			else if (!strcmp(word,"del")) oper = LSMLGI_DEL;
			else {
				fprintf(stderr,
					"Error: operation should be read,write, exec or del not %s\n",
					oper);
				return -3;
			}

			/* rest of the line is the conditional */
			word = ch;
			ch = get_next_word(ch);
			oper1 = operand_from_string(word);

			word = ch;
			ch = get_next_word(ch);
			comp = comparison_from_string(word);

			word = ch;
			ch = get_next_word(ch);
			oper2 = operand_from_string(word);

			/* line is parsed, write out to file */
			if (debug) {
			printf("%i %i:%i:%i:%i:%i:%i\n",
				lineno,
				uid,
				gid,
				oper,
				oper1,
				comp,
				oper2
				);
			}
			compiled_law[lineno].uid=uid;
			compiled_law[lineno].gid=gid;
			compiled_law[lineno].oper=oper;
			compiled_law[lineno].oper1=oper1;
			compiled_law[lineno].comp=comp;
			compiled_law[lineno].oper2=oper2;

			++lineno;
			/* reset everything */
			uid = gid = -1;
			oper = oper1 = comp = oper2 = -1;
		}
	}
	
	/* now dump the struct out to file */
	
	/* first few bytes are the number of lines */
	//write(fileno(lsmlgi), &lineno, sizeof(lineno));
	/* next dump the compiled law */
	write(fileno(lsmlgi),(void *) compiled_law,sizeof(struct law_struct)*(lineno));
	fclose(lsmlgi);

	return rc;
}

char *get_next_word(char *ch)
{
	char *start = ch;

	while (*ch && (*ch != ' ')) {
		ch++;
	}
	/* word should be at least one character */
	if (ch == start)  return NULL;
	while(*ch == ' ' || *ch == '{' || *ch == '}') *(ch++)='\0';
	return ch;
}

int operand_from_string(char *string) 
{
	int operand;
	long constant;
	
	constant = strtol(string,NULL,0);

	if ((constant < LONG_MAX) && (constant > 0)) return -constant;
	else if (!strcmp(string,"read")) operand =  LSMLGI_READ;
	else if (!strcmp(string,"write")) operand =  LSMLGI_WRITE;
	else if (!strcmp(string,"exec")) operand =  LSMLGI_EXEC;
	else if (!strcmp(string,"del")) operand =  LSMLGI_DEL;
	else if (!strcmp(string,"sid")) operand = LSMLGI_SID;
	else if (!strcmp(string,"tsid")) operand = LSMLGI_TSID;
	else if (!strcmp(string,"fsid")) operand = LSMLGI_FSID;
	else {
		fprintf(stderr,
		"Error: operand must be read, write, exec, del, sid, tsid or fsid, not %s\n",string);
		exit(-3);
	}
	return operand;
}

int comparison_from_string(char * string)
{
	int comp;
	if (!strcmp(string,"<")) comp = LSMLGI_LT;
	else if (!strcmp(string,"<=")) comp = LSMLGI_LE;
	else if (!strcmp(string,">")) comp = LSMLGI_GT;
	else if (!strcmp(string,">=")) comp = LSMLGI_GE;
	else if (!strcmp(string,"==")) comp = LSMLGI_EQ;
	else if (!strcmp(string,"!=")) comp = LSMLGI_NE;
	else {
		fprintf(stderr,
			"Error: comparison must be >, <, or =, not %s\n",string);
		exit(3);
	}
	return comp;
}

void usage(char *string)
{
	printf("usage: %s [-d] -f <filename>\n",string);
}
