#if !defined(lint) && !defined(SAVER)
static char rcsid[] = "$Id: cnfchg.c,v 1.4 1993/10/07 13:58:37 vrt Exp $";
#endif

#include "mkserv.h"
#include <hesiod.h>
#include <pwd.h>

struct line {
	struct line *next;
	int done;
	char *data;
	char *primary;
	char *secondary;
};

static int deluser(), adduser(), addservice(), delservice(), swservice(), conf();

struct serv_chg {
	char *primary;		/* Lookup as command */
	char *secondary;	/* secondary priority - usually NULL 
				 * Will be third argument */
	int (*func)();		/* Function */
} cmds[] = {
      {"deleteuser", NULL, deluser},
      {"adduser", NULL, adduser},
      {"deleteservice", NULL, delservice},
      {"addservice", NULL, addservice},
      {"switchservice", "on", swservice},
      {"switchservice", NULL, swservice},
      {"conf", "default", conf}, 
      {"conf", NULL, conf}
};

#define NCMDS sizeof(cmds) / sizeof(struct serv_chg)

struct text {
	struct text *next;
	char *data;
};

struct configs {
	int rcchange;
	struct text *rc;
	struct text *rcmaster;
	int inetdchange;
	struct text *inetd;
};
static struct text *read_file(char *name);
static struct text *find_line(struct text *t, char *name, int equal, int poind);
static int write_file(char *name, struct text *tin);

int change_configs(char *name)
{
	FILE *f;
	struct line *lines = NULL, *l= NULL;
	char buf[BUFSIZ], *p, *p1;
	struct configs conf;
	int i;
        char *inet;

	bzero(&conf, sizeof(conf));

	if((f = fopen(name, "r")) == NULL) {
		fprintf(stderr, "Unable to open %s for configuration changes\n", name);
		return -1;
	}

	/* Read in lines - removing newlines... */
	while(fgets(buf, BUFSIZ, f) != NULL) {
		if(buf[0] != '\n') {
			buf[strlen(buf) - 1] = '\0'; /* No newline now... */
			if(lines == NULL) {
				lines = (struct line *) malloc(sizeof(struct line));
				l = lines;
			}
			else {
				l->next = (struct line *) malloc(sizeof(struct line));
				l = l->next;
			}
			if(!l) {
				fprintf(stderr, "Out of memory in config_services\n");
				return -1;
			}
			bzero(l, sizeof(struct line));
			l->data = strdup(buf);
			if(!l->data) {
				fprintf(stderr, "Out of memory in config_services\n");
				return -1;
			}
			/* Process primary and secondary info */
			p = index(buf, ' ');
			if(p) {
				*p = '\0';
				p++;
			}
			if((l->primary = strdup(buf)) == NULL) {
				fprintf(stderr, "Out of memory in config change\n");
				return -1;
			}
			if(p) {
				/* Get secondary */
				p1 = index(p, ' ');
				if(p1) {
					p = ++p1;
					while(*p && !isspace(*p)) p++;
					*p = '\0';
					if((l->secondary = strdup(p1))
					   == NULL){
						fprintf(stderr, "Out of memory in cnfcng\n");
						return -1;
					}
				}
			} /* if (p) */
		} /* buf[0] != '\n' */
	} /* while */
	(void) fclose(f);

	/* Ready to process actions */
	if(!l) return 0;
	l = lines;
#ifdef DEBUG
	while(l) {
		printf("%s:%s:%s:\n", l->primary, l->secondary, l->data);
		l = l->next;
	}
#endif

	/* Read services here or when needed? */

	/* Loop over all the options */
	for(i = 0; i < NCMDS; i++) {
		l = lines;
		while(l) {
			if(!l->done && !strcmp(cmds[i].primary, l->primary)) {
				if(!cmds[i].secondary || 
				   !strcmp(cmds[i].secondary, l->secondary)) {
					(*cmds[i].func)(l, &conf);
				}
			}	
			l = l->next;
		}
	}

	/* check for any left */
	l = lines;
	while(l) {
		if(!l->done) {
			fprintf(stderr, "Unable to honor request of %s\n", l->data);
		}
		l = l->next;
	}
	if(conf.rcchange) {
		write_file("/etc/athena/rc.conf", conf.rc);
	}
	if(conf.inetdchange) {
		inet = os_where_inetd();
		write_file(inet, conf.inetd);
	}
	return 0;
}

/*ARGSUSED*/
static int deluser(struct line *lin, struct configs *conf)
{
	char *p;
	char buf[1024];
	/* We need to know if hesiod or not */
	if((p = index(lin->data, ' ')) == NULL) {
		fprintf(stderr, "Error in parsing line in deluser\n");
		return -1;
	}
	strcpy(buf, ++p);
	if((p = index(buf, ' ')) == NULL) {
		fprintf(stderr, "Error in parsing line in deluser\n");
		return -1;
	}
	*p = '\0';
	lin->done = 1;
	return os_remove_passwd(buf);
}

/*ARGSUSED*/
static int adduser(struct line *lin, struct configs *conf)
{
	char *p;
	char buf[1024];
	struct passwd *pwd;

	/* We need to know if hesiod or not */
	if((p = index(lin->data, ' ')) == NULL) {
		fprintf(stderr, "Error in parsing line in adduser\n");
		return -1;
	}
	strcpy(buf, ++p);
	if((p = index(buf, ' ')) == NULL) {
		fprintf(stderr, "Error in parsing line in adduser\n");
		return -1;
	}
	*p = '\0';
	p++;

	/* Find out if hesiod or not */
	if(!strcmp(buf, "hesiod")) {
		if(((pwd = hes_getpwnam(lin->secondary)) == NULL) ||
		   (pwd->pw_dir[0] == 0)) {
			if(hes_error() == HES_ER_NOTFOUND) 
				fprintf(stderr, "Could not add user %s - unknown\n", lin->secondary);
			else fprintf(stderr, "Could not lookup user %s due to network failure\n", lin->secondary);
			return -1;
		}
		/* Have the pwd structure */
	} else {
		if(strcmp(buf, "nohesiod")) {
			fprintf(stderr, "Add user command line wrong\n");
			return -1;
		}
		p = lin->data + (p - buf);
		/* Skip over nohesiod and space */
		while(*p && !isspace(*p)) p++;
		while(*p && isspace(*p))  p++;
		strcpy(buf, p);
		pwd = os_convert_hesiod_to_pwd(buf);
	}
	lin->done = 1;
	return(os_add_to_passwd(pwd));
}

static int delservice(struct line *lin, struct configs *conf)
{
	char buf[BUFSIZ];
	struct text *l, *t;
	char *p;
	int len;
        char *inet;
 
	inet = os_where_inetd();
	if(!conf->inetd) {
		if((conf->inetd = read_file(inet)) == NULL)
			return -1;
	}

	/* We need the service name */
	if((p = index(lin->data, ' ')) == NULL) {
		fprintf(stderr, "Error in parsing line in delervice\n");
		return -1;
	}
	strcpy(buf, ++p);
	if((p = index(buf, ' ')) != NULL) {
	  *p = '\0';
	}

	len = strlen(buf);
	t = conf->inetd;
	l = t;
	while(t) {
		if(!strncmp(t->data, buf, len) && (isspace(t->data[len]))) {
			if(t == conf->inetd) {
				t = conf->inetd->next;
				free(conf->inetd->data);
				free(conf->inetd);
				conf->inetd = t;
			} else {
				l->next = t->next;
				free(t->data);
				free(t);
			}

			conf->inetdchange = 1;
			lin->done = 1;
			return 0;
		}
		l = t;
		t = t->next;
	} /* while */

	lin->done = 1;
	return 0;
}

/*ARGSUSED*/
static int addservice(struct line *lin, struct configs *conf)
{
	char buf[BUFSIZ], buf1[BUFSIZ];
	struct text *l;
	char *p, *p1;
        char *inet;

	inet = os_where_inetd();
	if(!conf->inetd) {
		if((conf->inetd = read_file(inet)) == NULL)
			return -1;
	}

	/* We need the service name */
	if((p = index(lin->data, ' ')) == NULL) {
		fprintf(stderr, "Error in parsing line in addservice\n");
		return -1;
	}
	strcpy(buf, ++p);
	p1 = p;
	if(((p = index(buf, ' ')) == NULL) && ((p = index(buf, '\t')) == NULL)) {
		fprintf(stderr, "Error in parsing line in addservice\n");
		return -1;
	}
	*p = '\0';

	strcpy(buf1, p1);
	strcat(buf1, "\n");
	p1 = buf1;
	p = os_convert_service_to_inetd(buf1);
	/* Need a newline on p */
	/* Look up line, but no lines with # at beginning */
	if((l = find_line(conf->inetd, buf, 0, 1)) == NULL) {
		/* Easy - just add the service */
		l = conf->inetd;
		while(l && l->next) l=l->next;
		if(!l) {
			fprintf(stderr, "An empty inetd.conf file exists\n");
			return -1;
		}
		l->next = (struct text *) malloc(sizeof(struct text));
		l = l->next;
		if(!l || ((l->data = strdup(p)) == NULL)) {
			fprintf(stderr, "Out of memory in addserice\n");
			return -1;
		}
		l -> next = NULL;
		lin->done = 1;
		conf->inetdchange = 1;
	} else {
		/* Hard - need to possibly change something */
		if(!strcmp(l->data, p)) {
			lin->done = 1;
			return 0;
		}
		/* Will drop old entry for a new one... Que sera Sera */
		fprintf(stderr, "Warning overwriting old %s entry in inetd.conf\n", buf);
		if(p1 = strdup(p)) {
			fprintf(stderr, "Out of memory in addservice\n");
			return -1;
		}
		free(l->data);
		l->data = p1;
		lin->done = 1;
		conf->inetdchange = 1;
	}
	return 0;

}

static int swservice(struct line *lin, struct configs *conf)
{
	char buf[BUFSIZ];
	struct text *l;
	char *p;
	char *inet;

	if(!strcmp(lin->secondary, "on") && !strcmp(lin->secondary, "off")) {
		fprintf(stderr, "swservice unknown option:%s\n", lin->data);
		return -1;
	}

	inet = os_where_inetd();

	if(!conf->inetd) {
		if((conf->inetd = read_file(inet)) == NULL)
			return -1;
	}
	/* We need the service name */
	if((p = index(lin->data, ' ')) == NULL) {
		fprintf(stderr, "Error in parsing line in switchservice\n");
		return -1;
	}
	strcpy(buf, ++p);
	if((p = index(buf, ' ')) == NULL) {
		fprintf(stderr, "Error in parsing line in switchservice\n");
		return -1;
	}
	*p = '\0';

	if((l = find_line(conf->inetd, buf, 0, 0)) == NULL) {
		fprintf(stderr, "Unable to switch nonexistant inetd.conf entry %s\n", buf);
		return -1;
	}
	p = os_switch_service(l->data, lin->secondary);
	if(p != NULL) {
		l->data = p;
	} else {
		fprintf(stderr, "Unable to switch service %s\n", buf);
		return -1;
	}
	conf->inetdchange = 1;
	lin->done = 1;
	return 0;
}
		


static int conf(struct line *lin, struct configs *conf)
{
	struct text *l, *lmaster;
	char buf[200];
	char *p, *p1;
	int def = 0;

	if(!strcmp(lin->secondary, "default")) {
		def = 1;
	}
	if(!conf->rc) {
		if((conf->rc = read_file("/etc/athena/rc.conf")) == NULL)
			return -1;
	}
	if(!conf->rcmaster && def) {
		if((conf->rcmaster = read_file("/srvd/etc/athena/rc.conf")) == NULL)
			return -1;
	}

	/* We need the variable name */
	if((p = index(lin->data, ' ')) == NULL) {
		fprintf(stderr, "Error in parsing line in conf\n");
		return -1;
	}
	strcpy(buf, ++p);
	if((p = index(buf, ' ')) == NULL) {
		fprintf(stderr, "Error in parsing line in conf\n");
		return -1;
	}
	*p = '\0';

	if((l = find_line(conf->rc, buf, 1, 0)) == NULL) {
		fprintf(stderr, "Unable to change configuration element %s\n", buf);
		return -1;
	}
	if(def) {
		if((lmaster = find_line(conf->rcmaster, buf, 1, 0)) == NULL) {
			fprintf(stderr, "Unable to find master entry for %s\n", buf);
			return -1;
		}
		p = strdup(lmaster->data);
		if(!p) {
			fprintf(stderr, "Out of memory in conf\n");
			return -1;
		}
		free(l->data);
		l->data = p;
	} else { /* default */
		/* l->secondary has the value */
		/* We process the line that is found. 
		 * After = replace text up to ;
		 */
		strcpy(buf, l->data);
		if((p = index(buf, '=')) == NULL) {
			fprintf(stderr, "Error in parsing line in rc.conf\n");
			return -1;
		}
		p++; /* p points to beggining of data for buf */
		*p = '\0';
		if((p1 = index(l->data+ (p-buf), ';')) == NULL) {
			fprintf(stderr, "Error in parsing line in rc.conf\n");
			return -1;
		}
		/* p=point of insertion in buf p1=start of ; of in l->data */
		strcat(p, lin->secondary);
		strcat(buf, p1);
		if(!(p = strdup(buf))) {
			fprintf(stderr, "Out of memory in conf\n");
			return -1;
		}
		free(l->data);
		l->data = p;
	} /* default */

	lin->done = 1;	   
	conf->rcchange = 1;
	return 0;
}


static struct text *read_file(char *name)
{
	char buf[BUFSIZ];
	FILE *f;
	struct text *t = NULL, *tret = NULL;
	
	if((f = fopen(name, "r")) == NULL) {
		fprintf(stderr, "Could not open %s for read\n", name);
		return NULL;
	}

	while(fgets(buf, BUFSIZ, f) != NULL) {
		if(t) {
			t->next = (struct text *) malloc(sizeof(struct text));
			t = t->next;
		} else {
			t = (struct text *) malloc(sizeof(struct text));
			tret = t;
		}
		if(!t || ((t->data = strdup(buf)) == NULL)) {
			fprintf(stderr, "Out of memory in reading %s\n", name);
			return NULL;
		}
		t -> next = NULL;
	}

	fclose(f);
	return tret;
}

/* Finds service lines. Checks also for lines beginning with # */
/* If equal is set, then name may be followed by = */
static struct text *find_line(struct text *tin, char *name, int equal, int pound) 
{
	struct text *t;
	int pass=0, len = strlen(name);
	if(pound) pound = 1;
	else pound = 2;

	for(pass = 0; pass < pound; pass++) {
		t = tin;
		while(t) {
			if(pass == 0) {	
				if(!strncmp(t->data, name, len) && 
				   (isspace(t->data[len]) ||
				    (equal && t->data[len] == '='))) {
					return t;
				}
			} else { /* pass 1 */
				if(t->data[0] == '#' && 
				   !strncmp(t->data + 1, name, len) && 
				   (isspace(t->data[len+1])|| 
				    (equal && t->data[len+1] == '='))) {
					return t;
				}
			}
			t = t->next;
		} /* while */
	}
	return NULL;
}

static int write_file(char *name, struct text *tin)
{
	char old[MAXPATHLEN+1], new[MAXPATHLEN+1];	
	FILE *f;

	sprintf(old, "%s.old", name);
	sprintf(new, "%s.new", name);
	
	(void) unlink(old);
	(void) unlink(new);
	if((f = fopen(new, "w")) == NULL) {
		fprintf(stderr, "Unable to open %s for writing\n", name);
		return -1;
	}
	while(tin) {
		fputs(tin->data, f);
		if(ferror(f)) {
			fprintf(stderr, "Error in writing %s\n", name);
			(void) fclose(f);
			return -1;
		}
		tin = tin->next;
	}
	if(fclose(f) == EOF) {
		fprintf(stderr, "Error in close of %s\n", name);
		return -1;
	}
	if(link(name, old)) {
		fprintf(stderr, "Problem linking %s to %s\n", name, old);
		return -1;
	}
	/* Break link */
	if(rename(new, name)) {
		fprintf(stderr, "Problem in rename %s to 5s\n", new, name);
		return -1;
	}
	return 0;
}
