#ifndef LINT
static char *rcsid="$Id: rtconfig.c,v 1.13 1999/10/06 15:54:03 crosser Exp $";
#endif

/*
	$Log: rtconfig.c,v $
	Revision 1.13  1999/10/06 15:54:03  crosser
	fix for kluge about initializing private fields
	Initialization for global parameters
	
	Revision 1.12  1999/10/06 11:19:52  crosser
	Handle SIGPIPE in the server
	Fix (kludge) for private fields in the config struct
	Make beta3 version
	
	Revision 1.11  1999/10/05 22:24:16  crosser
	fix incorrect permission chan building.
	Publish beta 2
	
	Revision 1.10  1999/10/05 05:15:46  crosser
	Check for inet_aton and use inet_addr if absent
	
	Revision 1.9  1999/10/03 21:18:03  crosser
	First (probably) working version with new run time config parser.
	Also added listenq parameter (backlog size for listen()) and
	renamed wtest to whoson (and with man page).  Release 2.00beta1.
	
	Revision 1.8  1999/10/02 21:29:30  crosser
	work in progress on rtconfig
	
	Revision 1.7  1999/08/19 17:22:15  crosser
	Move to new config scheme (does not work yet)
	
	Revision 1.6  1999/07/30 23:25:09  crosser
	Automakifying clenups
	
	Revision 1.5  1998/07/05 00:26:18  crosser
	Change copyright

	Revision 1.4  1998/07/02 18:01:15  crosser
	change error reporting to syslog

	Revision 1.3  1998/07/02 15:51:24  crosser
	change DPRINT

	Revision 1.2  1998/07/01 05:18:09  crosser
	minor warnings fix

	Revision 1.1  1998/07/01 05:01:22  crosser
	Initial revision

*/

/*
	WHAT IS IT:
		Implementation of experimental "whoson" protocol
	AUTHOR:
		Eugene G. Crosser <crosser@average.org>
	COPYRIGHT:
		Public domain
*/

/* whoson runtime configuration parser */

#include "config.h"

#include <sys/types.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "rtconfig.h"
#include "checkperm.h"
#include "report.h"

static int rtcfg_init(struct _cfgdesc *cfgdesc,void **priv) {
	int cfgsize=0;
	struct _cfgdesc *tmp;
	char *cfgrec;

	DPRINT(("rtcfg_init: *cfgdesc=%p **priv=%p\n",cfgdesc,priv))
	/* compute the size of cfgrec */
	for (tmp=cfgdesc;tmp->cf_key;tmp++) /* skip to last element */;
	cfgsize=tmp->cf_val_size; /* size of whole structure */
	if (((*priv)=malloc(cfgsize)) == NULL) {
		ERRLOG((LOG_ERR,"[WHOSON] failed to allocate %d bytes",
				cfgsize))
		return -1;
	}
	memset(*priv,0,cfgsize);
	DPRINT(("rtcfg_init: allocated %d bytes at %p\n",cfgsize,*priv))
	cfgrec=(*priv);
	for (tmp=cfgdesc;tmp->cf_key;tmp++) {
		if (tmp->cf_key == (char*)-1) {
			DPRINT(("kludge setting priv field\n"))
			/* kludge: works only for privates of type INT */
			*(int*)(cfgrec+(tmp->cf_val_offset))=-1;
		}
	}
	return 0;
}

static int rtcfg_next(struct _cfgdesc *cfgdesc,char *key,char *val,void **priv) {
	struct _cfgdesc *tmp;
	char *cfgrec=(*priv);
	struct _perm *perms;

	DPRINT(("rtcfg_next: *cfgdesc=%p cfgrec=%p, key=\"%s\", val=\"%s\"\n",
			cfgdesc,cfgrec,key,val))
	for (tmp=cfgdesc;tmp->cf_key;tmp++) {
		/* DPRINT(("scan tab type=%d key=%s\n",tmp->cf_type,
			(tmp->cf_key == (char*)-1)?"<-1>":tmp->cf_key)) */
		if (tmp->cf_type == cf_perm) {
			if ((strcasecmp(key,"allow") == 0) ||
			    (strcasecmp(key,"deny") == 0)) break;
		} else {
			if (strcasecmp(key,tmp->cf_key) == 0) break;
		}
	}
	if (!tmp->cf_key) {
		ERRLOG((LOG_ERR,"bad keyword \"%s\"\n",key))
		return -1;
	}
	/* OK, found matching description entry */
	switch(tmp->cf_type) {
	case cf_bool:
		if ((strcasecmp(key,"yes") == 0) ||
		    (strcasecmp(key,"on") == 0))
			*(int*)(cfgrec+(tmp->cf_val_offset))=1;
		else if ((strcasecmp(key,"no") == 0) ||
			 (strcasecmp(key,"off") == 0))
			*(int*)(cfgrec+(tmp->cf_val_offset))=0;
		else {
			ERRLOG((LOG_ERR,"bad boolean value \"%s\"\n",val))
			return -1;
		}
		break;
	case cf_int:
		if (strspn(val,"0123456789") == strlen(val))
			*(int*)(cfgrec+(tmp->cf_val_offset))=atoi(val);
		else {
			ERRLOG((LOG_ERR,"bad boolean value \"%s\"\n",val))
			return -1;
		}
		break;
	case cf_addr:
#ifdef HAVE_INET_ATON
		if (inet_aton(val,
			(struct in_addr*)(cfgrec+(tmp->cf_val_offset))) == 0)
#else
		if ((*(long*)(cfgrec+(tmp->cf_val_offset))=inet_addr(val))
									== -1L)
#endif
									      {
			ERRLOG((LOG_ERR,"bad address \"%s\"\n",val))
			return -1;
		}
		break;
	case cf_str:
		if (strlen(val) > tmp->cf_val_size) {
			ERRLOG((LOG_ERR,"value \"%s\" longer than max %d\n",
				val,tmp->cf_val_size))
			return -1;
		}
		strcpy(cfgrec+(tmp->cf_val_offset),val);
		break;
	case cf_perm:
		if ((perms=wso_perm_parse((strcasecmp(key,"allow")==0),val))) {
			perms->next=
				*(struct _perm**)(cfgrec+(tmp->cf_val_offset));
			*(struct _perm**)(cfgrec+(tmp->cf_val_offset))=perms;
		} else {
			ERRLOG((LOG_ERR,"bad %s value \"%s\"\n",key,val))
		}
		break;
	}
	return 0;
}

static int rtcfg_end(struct _cfgdesc *cfgdesc,void **priv) {
	DPRINT(("rtcfg_end: *cfgdesc=%p **priv=%p\n",cfgdesc,priv))
	/* I wonder if this one might be needed at all... */
	return 0;
}

struct _servdesc *wso_read_config(char *fn,int do_server) {
	int rc=-1;
	int lineno=0;
	int endfile=0;
	FILE *fp;
	char buf[512];
	char *p,*q=NULL,*v;
	enum {no_entry,client_entry,server_entry} current=no_entry;
	struct _cfgrec *cfgrec=NULL;
	struct _servdesc *start=NULL,**curr=&start;

	if ((fp=fopen(fn,"r")) == NULL) {
		ERRLOG((LOG_ERR,"[WHOSON] %s open failed: %m",fn))
		return NULL;
	}

	while (!endfile) {
		endfile=(fgets(buf,sizeof(buf)-1,fp) == NULL);
		lineno++;
		buf[sizeof(buf)-1]='\0'; /* paranoia */
		if (!endfile && (buf[strlen(buf)-1] != '\n')) {
			ERRLOG((LOG_ERR,"[WHOSON] %s(%d): line too long\n",fn,lineno))
			rc=-1;
			goto exit;
		}
		if (!endfile) { /* cut off comments */
			if ((q=strchr(buf,'#'))) *q='\0';
			for (q=buf;(*q) && isspace(*q);q++) /*nothing*/;
			if (*q == '\0') continue;
		}

		DPRINT(("New line %d: %s[%sendfile current=%d]\n",
			lineno,buf,endfile?"":"no ",current))

		if ((endfile || !isspace(buf[0])) &&
		    (((current == client_entry) && (!do_server)) ||
		     ((current == server_entry) && (do_server)))) {

			if (cfgrec && cfgrec->cfgdesc && (*curr)) {
				rc=rtcfg_end(cfgrec->cfgdesc,&((*curr)->priv));
			} else rc=-1;

			if (rc) {
				ERRLOG((LOG_ERR,"[WHOSON] %s(%d): previous entry bad\n",
							fn,lineno))
				rc=-1;
				goto exit;
			}
			if (*curr) curr=&((*curr)->next);
			current=no_entry; /* paranoia */
			cfgrec=NULL;
		}
		if (endfile) continue;

		if (isspace(buf[0])) { /* continuation line */
			if (current == no_entry) {
				ERRLOG((LOG_ERR,"[WHOSON] %s(%d): homeless continuation line\n",
							fn,lineno))
				rc=-1;
				goto exit;
			}
			if (((current == client_entry) && (do_server)) ||
			    ((current == server_entry) && (!do_server)))
				continue;
		} else { /* start of entry */
			p=buf;
			while ((*p) && !isspace(*p)) p++;
			if (*p) {
				*p='\0';
				p++;
				while ((*p) && isspace(*p)) p++;
				q=p;
				while ((*q) && !isspace(*q)) q++;
				if (*q) {
					*q='\0';
					q++;
					while ((*q) && isspace(*q)) q++;
				}
			}

			DPRINT(("start entry: buf=%s p=%s q=%s\n",buf,p,q))

			if (strcasecmp(buf,"client") == 0) {
				current=client_entry;
				if (do_server) continue;
			} else if (strcasecmp(buf,"server") == 0) {
				current=server_entry;
				if (!do_server) continue;
			} else {
				ERRLOG((LOG_ERR,"[WHOSON] %s(%d): invalid entry name \"%s\"\n",
							fn,lineno,buf))
				rc=-1;
				goto exit;
			}
				
			for (cfgrec=wso_cfglist;cfgrec->name;cfgrec++) {
				if (strcasecmp(p,cfgrec->name) == 0) break;
			}

			if (cfgrec->cfgdesc) {
				(*curr)=(struct _servdesc*)malloc
					(sizeof(struct _servdesc));
				memset(*curr,0,
					sizeof(struct _servdesc));
				(*curr)->root.init=cfgrec->root.init;
				rc=rtcfg_init(cfgrec->cfgdesc,&((*curr)->priv));
			} else rc=-1;

			if (rc) {
				ERRLOG((LOG_ERR,"[WHOSON] %s(%d): unsupported %s type \"%s\"\n",
							fn,lineno,buf,p))
				goto exit;
			}
		}
		/* parse parameters; 'q' points to the start of params */
		while (*q) {
			p=q;
			while ((*p) && !isspace(*p)) p++;
			if (*p) {
				*p='\0';
				p++;
				while ((*p) && isspace(*p)) p++;
			}
			if ((v=strchr(q,'='))) *v++='\0';

			if (cfgrec && cfgrec->cfgdesc)
				rc=rtcfg_next(cfgrec->cfgdesc,q,v,&((*curr)->priv));
			else rc=-1;

			if (rc) {
				ERRLOG((LOG_ERR,"[WHOSON] %s(%d): bad key %s=%s\n",
						fn,lineno,q,v?v:"(null)"))
				goto exit;
			}
			q=p;
		}
	}

exit:
	fclose(fp);
	return rc?NULL:start;
}


syntax highlighted by Code2HTML, v. 0.9.1