/* $Xorg: auth.c,v 1.4 2000/08/17 19:54:01 cpqbld Exp $ */

/************************************************************************/
/* Copyright (c) 1993 Quarterdeck Office Systems			*/
/*									*/
/* Permission to use, copy, modify, distribute, and sell this software	*/
/* and software and its documentation for any purpose is hereby granted	*/
/* without fee, provided that the above copyright notice appear in all	*/
/* copies and that both that copyright notice and this permission	*/
/* notice appear in supporting documentation, and that the name		*/
/* Quarterdeck Office Systems, Inc. not be used in advertising or	*/
/* publicity pertaining to distribution of this software without	*/
/* specific, written prior permission.					*/
/*									*/
/* THIS SOFTWARE IS PROVIDED `AS-IS'.  QUARTERDECK OFFICE SYSTEMS,	*/
/* INC., DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,		*/
/* INCLUDING WITHOUT LIMITATION ALL IMPLIED WARRANTIES OF		*/
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR		*/
/* NONINFRINGEMENT.  IN NO EVENT SHALL QUARTERDECK OFFICE SYSTEMS,	*/
/* INC., BE LIABLE FOR ANY DAMAGES WHATSOEVER, INCLUDING SPECIAL,	*/
/* INCIDENTAL OR CONSEQUENTIAL DAMAGES, INCLUDING LOSS OF USE, DATA, OR	*/
/* PROFITS, EVEN IF ADVISED OF THE POSSIBILITY THEREOF, AND REGARDLESS	*/
/* OF WHETHER IN AN ACTION IN CONTRACT, TORT OR NEGLIGENCE, ARISING OUT	*/
/* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.	*/
/************************************************************************/
/* $XFree86: xc/programs/rstart/auth.c,v 1.4 2001/01/17 23:45:03 dawes Exp $ */

#include <stdio.h>
#include <X11/Xos.h>
#include <errno.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/wait.h>

#include "server.h"

static char *Strupr ( char *s0 );
static struct auth_info *find_or_create_auth ( char *s );
static char * expand ( char *s, int ac, char **av );

struct list_of_argv {
	struct list_of_argv *next;
	int	argc;
	char	**argv;
};

struct auth_info {
	struct auth_info	*next;
	char			*name;
	struct list_of_argv	*data;
	char			**program;
	char			**input;
};

struct auth_info *auth_schemes = NULL;

static char *
Strupr(char *s0)
{
	char *s;

	for(s = s0; *s; s++) {
		if(islower(*s)) *s = toupper(*s);
	}
	return s0;
}

/* Argument "s" is overwritten and the memory used, so it must not be	*/
/* deallocated or subsequently used by the caller.			*/
struct auth_info *
find_or_create_auth(char *s)
{
	struct auth_info *auth;

	Strupr(s);

	for(auth = auth_schemes; auth; auth=auth->next) {
		if(!strcmp(s, auth->name)) return auth;
	}

	auth = (struct auth_info *)malloc(sizeof(*auth));
	if(!auth) nomem();

	auth->next = auth_schemes;
	auth->name = s;
	auth->data = NULL;
	auth->program = NULL;
	auth->input = NULL;
	auth_schemes = auth;

	return auth;
}

void
key_auth(int ac, char **av)
{
	struct list_of_argv *lav;
	struct auth_info *auth;

	if(ac < 2) {
		printf(
		    "%s: Failure: Malformed AUTH\n",myname);
		exit(255);
	}

	auth = find_or_create_auth(av[1]);

	lav = (struct list_of_argv *)malloc(sizeof(*lav));
	if(!lav) nomem();

	lav->next = auth->data;
	lav->argc = ac-2;
	lav->argv = av+2;
	auth->data = lav;
}

void
key_internal_auth_program(int ac, char **av)
{
	struct auth_info *auth;

	if(ac < 4) {
		printf(
		    "%s: Failure: Malformed INTERNAL-AUTH-PROGRAM\n",myname);
		exit(255);
	}

	auth = find_or_create_auth(av[1]);
	auth->program = av + 2;
}

void
key_internal_auth_input(int ac, char **av)
{
	struct auth_info *auth;

	if(ac < 2) {
		printf(
		    "%s: Failure: Malformed INTERNAL-AUTH-INPUT\n",myname);
		exit(255);
	}

	auth = find_or_create_auth(av[1]);
	auth->input = av + 2;
}

void
do_auth(void)
{
	struct auth_info *auth;
	int p[2];
	char **pp;
	struct list_of_argv *lav;
	char *s;
	int pid;
	int status;

	for(auth = auth_schemes; auth; auth = auth->next) {
		if(!auth->data) continue;
		if(!auth->program) {
			printf(
"%s: Warning: no %s authorization program specified in this context\n",myname,
				auth->name);
			continue;
		}

		if(pipe(p)) {
			printf("%s: Error: pipe - %s\n",myname, strerror(errno));
			exit(255);
		}

		fflush(stdout);		/* Can't hurt. */

		switch(pid = fork()) {
		    case -1:
			printf("%s: Error: fork - %s\n",myname, strerror(errno));
			exit(255);
		    case 0:	/* kid */
			close(0);
			dup(p[0]);
			close(p[0]);
			close(p[1]);
			execvp(auth->program[0], auth->program+1);
			printf("%s: Error: %s - %s\n",myname, auth->program[0],
				strerror(errno));
			exit(255);
			break;
		    default:	/* parent */
			close(p[0]);
			for(lav = auth->data; lav; lav=lav->next) {
				for(pp = auth->input; *pp; pp++) {
					s = expand(*pp, lav->argc, lav->argv);
					write(p[1], s, strlen(s));
					write(p[1], "\n", 1);
				}
			}
			close(p[1]);
			while(wait(&status) != pid) /* LOOP */;
			if(status) {
				printf(
				"%s: Warning: %s authorization setup failed\n",myname, auth->name);
			}
			break;
		}
	}
}

char *
expand(char *s, int ac, char **av)
{
	static char buf[BUFSIZ];
	char *p;
	int i;

	p = buf;
	while(*s) {
		if(*s == '$') {
			s++;
			if(*s == '$') {
				*p++ = *s++;
				continue;
			}
			if(!isdigit(*s)) {
				printf(
	"%s: Failure: bad $ in configuration: non-digit after $\n",myname);
				exit(255);
			}
			i = (int)strtol(s, &s, 10);
			if(i > ac) {
				printf(
		"%s: Failure: not enough arguments to AUTH\n",myname);
				exit(255);
			}
			strcpy(p, av[i-1]);
			p += strlen(p);
		} else *p++ = *s++;
	}
	*p = '\0';

	return buf;
}
