/*
**      w3-auth.c       -
**
**
** Copyright (c) 1995-96  Hughes Technologies Pty Ltd
**
** Permission to use, copy, and distribute for non-commercial purposes,
** is hereby granted without fee, providing that the above copyright
** notice appear in all copies and that both the copyright notice and this
** permission notice appear in supporting documentation.
**
** This software is provided "as is" without any expressed or implied warranty.
**
**
*/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <common/portability.h>
#include <msql/msql.h>

#include "y.tab.h"
#include "lite.h"
#include "version.h"


char * HTEscape();
void HTUnEscape();
int HTUU_decode();
void sendAuthHeader();
char *fcrypt();

/*
** Globals
*/
char	*username,
	*passwd,
	*curNS = NULL,
	*urlNS,
	qBuf[2048];
int	sock;



/*
** Auth levels
*/

#define	AUTH_UM		1
#define AUTH_AM		2
#define AUTH_GM		4
#define AUTH_MM		8
#define AUTH_PM		16
#define AUTH_NM		32
#define	AUTH_ANY	255




/****************************************************************************
** Variable handling routines
*/
typedef struct _var {
	char	*name,
		*value;
	struct _var *next;
} var_t;


static	var_t	*varHead = NULL;

void setVariable(var,val)
	char	*var,
		*val;
{
	var_t	*cur,
		*new;

	cur = varHead;
	while(cur)
	{
		if (strcmp(cur->name,var) == 0)
		{
			if (cur->value)
				free(cur->value);
			cur->value = (char *)strdup(val);
			return;
		}
		cur = cur->next;
	}

	new = (var_t *)malloc(sizeof(var_t));
	bzero(new,sizeof(var_t));
	new->name = (char *)strdup(var);
	if (val)
		new->value = (char *)strdup(val);
	else
		new->value = NULL;
	new->next = varHead;
	varHead = new;
}


void appendVariable(var,val)
	char	*var,
		*val;
{
	var_t	*cur,
		*new;
	char	*newVal;

	cur = varHead;
	while(cur)
	{
		if (strcmp(cur->name,var) == 0)
		{
			if (!cur->value)
			{
				cur->value = (char *)strdup(val);
				return;
			}
			newVal = (char *)malloc(strlen(cur->value) +
				strlen(val) + 2);
			sprintf(newVal,"%s%c%s",cur->value,'\001',val);
			free(cur->value);
			cur->value = newVal;
			return;
		}
		cur = cur->next;
	}

	new = (var_t *)malloc(sizeof(var_t));
	bzero(new,sizeof(var_t));
	new->name = (char *)strdup(var);
	if (val)
		new->value = (char *)strdup(val);
	else
		new->value = NULL;
	new->next = varHead;
	varHead = new;
}



char *getVariable(var)
	char	*var;
{
	var_t	*cur;


	/*
	** Look for an internal variable
	*/
	cur = varHead;
	while(cur)
	{
		if (strcmp(cur->name,var) == 0)
		{
			return(cur->value);
		}
		cur = cur->next;
	}
	return(NULL);
}



void parseArgs()
{
        char    *query,
		*method,
		*contentLength,
                *cp1,
                *cp2,
                var[30],
                val[160];
	int	length;


	cp1 = (char *)getenv("QUERY_STRING");
	if (cp1)
	{
		query = (char *)strdup(cp1);
	}

       	method=(char *)getenv("REQUEST_METHOD");
       	if(!method)
               	return;
	if (strcmp(method, "POST") == 0)
	{
		contentLength = (char *)getenv("CONTENT_LENGTH");
		if (!contentLength)
			return;
        	length = atoi(contentLength);
        	query = (char *) malloc(length + 1);
        	read(fileno(stdin), query, length);
        	query[length]='\0';
	}
	
        cp1 = query;
        cp2 = var;
        bzero(var,sizeof(var));
        bzero(val,sizeof(val));
        while(*cp1)
        {
                if (*cp1 == '\\')
                {
                        cp1++;
                        *cp2++ = *cp1++;
                        continue;
                }
                if (*cp1 == '=')
                {
                        cp1++;
                        cp2 = val;
                        continue;
                }
                if (*cp1 == '&')
                {
			HTUnEscape(val);
			if (getVariable(var))
                        	appendVariable(var,val);
			else
                        	setVariable(var,val);
                        bzero(var,sizeof(var));
                        bzero(val,sizeof(val));
                        cp1++;
                        cp2 = var;
                        continue;
                }
                *cp2++ = *cp1++;
        }
	HTUnEscape(val);
	if (getVariable(var))
               	appendVariable(var,val);
	else
               	setVariable(var,val);
}




char *msqlEscape(str)
	char	*str;
{
        char    *new,
                *cp1,
                *cp2;

        new = (char *)malloc(2 * strlen(str));
        cp1 = str;
        cp2 = new;
        while(*cp1)
        {
                if (*cp1 == '\\')
                {
                        *cp2++ = *cp1++;
                }
                else
                if (*cp1=='\'' || *cp1=='[' || *cp1==']' || *cp1=='^' ||
               *cp1=='$' )
                {
                        *cp2++ = '\\';
                }
                *cp2++ = *cp1++;
        }
	*cp2 = 0;
        return(new);
}



void checkAuth(level)
	int	level;
{
	m_result *res;
	m_row	row;
	int	curLevel,
		length;
	char	*authInfo,
		*authBuf,	
		*cryptpw,
		*curpw,
		salt[3],
		errbuf[160],
		*authHost;


	/*
	** Get the username & passwd
	*/
	authInfo = (char *)getenv("HTTP_AUTHORIZATION");
	if (!authInfo)
	{
		sendAuthHeader("No Auth Info");
		exit(1);
	}

	authInfo = (char *)index(authInfo,' ');
	if (!authInfo)
	{
		sendAuthHeader("No Passwd");
		exit(1);
	}
	authInfo++;
	authBuf = (char *)malloc(100);
	bzero(authBuf,100);
	length = HTUU_decode(authInfo, authBuf, 100);
	*(authBuf+length) = 0;
	username = (char *)strtok(authBuf,":");
	passwd = (char *)strtok(NULL,"\n");
	if (!username || !passwd)
	{
		sendAuthHeader("Missing Username or Passwd");
		exit(1);
	}

	/*
	** Connect to the database
	*/
	authHost = (char *)msqlGetCharConf("w3-msql","auth_host");
	sock = msqlConnect(authHost);
	if (sock < 0)
	{
		runError(msqlErrMsg);
		exit(1);
	}
	if (msqlSelectDB(sock, "w3-msql") < 0)
	{
		runError(msqlErrMsg);
		exit(1);
	}

	sprintf(qBuf,
	    "select passwd from users where uname = '%s' and namespace = 'SuperUser'",
		username);
	if (msqlQuery(sock,qBuf) < 0)
	{
		runError(msqlErrMsg);
		exit(1);
	}
	res = msqlStoreResult();
	if (msqlNumRows(res) == 0)
	{
		sendAuthHeader("Invalid Username");
		exit(1);
	}
	row = msqlFetchRow(res);

	curpw = row[0];
	salt[0] = *curpw;
	salt[1] = *(curpw+1);
	salt[2] = 0;
	cryptpw = (char *)fcrypt(passwd, salt);
	if (strcmp(cryptpw,row[0]) != 0)
	{
		sendAuthHeader("Bad Passwd");
		exit(1);
	}
	msqlFreeResult(res);

	sprintf(qBuf,"select level, namespace from perms where uname = '%s'",
		username);
	if (msqlQuery(sock,qBuf) < 0)
	{
		runError(msqlErrMsg);
		exit(1);
	}
	res = msqlStoreResult();
	if (msqlNumRows(res) == 0)
	{
		sendAuthHeader("You have no management permission");
		exit(1);
	}
	row = msqlFetchRow(res);
	while(row)
	{
		if (strcmp(row[1], curNS) == 0)
		{
			if (level == AUTH_ANY)
			{
				return;
			}
			curLevel = atoi(row[0]);
			if (!(curLevel & level))
			{
				sendAuthHeader("Bad Access Level");
				exit(1);
			}
			return;
		}
		row = msqlFetchRow(res);
	}
	sprintf(errbuf, "No Access To Namespace '%s'", curNS);
	sendAuthHeader(errbuf);
	exit(1);
}



void sendFooter()
{
        printf("<p><br><br><br><br>\n");
        printf("</dl></ul></ol></table></pre>\n");
        printf("</h1></h2></h3></h4></h5></h6>\n");
        printf("<center><address>w3-Auth %s by ",SERVER_VERSION);
        printf("<a href=http://www.Hughes.com.au/>");
        printf("Hughes Technologies</a></address>\n\n");
}


void runError(msg)
        char    *msg;
{
        printf("</h1></h2></h3></h4></h5></h6>\n");
        printf("</select></ul></dl></ol></table>\n");
        printf("<h3><CENTER>\n");
        printf("\n\nW3-Auth Error!  -  %s\n\n</CENTER></H3>\n",msg);
	sendFooter();
        fflush(stdout);
}


void sendAuthHeader(msg)
	char	*msg;
{
	printf("Status: 401 Error\n");
	printf("WWW-Authenticate: Basic realm=\" 'W3 Auth' \"\n");
	printf("Server: w3-mSQL_2\n");
	printf("Content-Type: text/html\n\n");
	printf("<BODY BGCOLOR=#FFFFFF TEXT=#0606A0 LINK=#0000FF ");
	printf("VLINK=#0000FF>\n");
	printf("<P><BR><CENTER><IMG SRC=/Hughes/graphics/banner.gif><P><BR>\n");
	printf("<H1><I>Access Denied</I></H1></CENTER><P><BR><BR><BR>\n");
	if (msg)
		printf("<CENTER><H3>%s</H3></CENTER><P><BR><BR><BR>\n",msg);
	sendFooter();
}



void sendOkHeader()
{
	printf("Status: 200 Output Follows\n");
	printf("WWW-Authenticate: Basic\n");
	printf("Server: w3-mSQL_2\n");
	printf("Content-Type: text/html\n\n");
	printf("<BODY BGCOLOR=#FFFFFF TEXT=#0606A0 LINK=#0000FF ");
	printf("VLINK=#0000FF>\n");
	printf("<CENTER><IMG SRC=/Hughes/graphics/banner.gif><P><BR>\n");
	printf("</CENTER>\n");
}



void sendIntro()
{
	m_result *res;
	m_row	row;


	checkAuth(AUTH_ANY);
	sendOkHeader();
	printf("<CENTER><H1><I>Authentication Management</H1></I>\n");
	printf("<P><BR></CENTER><BLOCKQUOTE>\n");
	printf("Welcome to the W3-mSQL authentication management ");
	printf("interface.  Through this interface you can create, ");
	printf("edit, and delete users, groups and secure areas. ");
	printf("Access is check at every step of the process and you ");
	printf("will only be able to access the functions that you ");
	printf("have been granted access to.</BLOCKQUOTE><P><BR><BR>");

	sprintf(qBuf,
	    "select namespace from perms where uname='%s' order by namespace",
	 	username);
	msqlQuery(sock,qBuf);
	res = msqlStoreResult();

	if (msqlNumRows(res) == 0)
	{
		printf("<CENTER><H2>\n");
		printf("No Namespaces Defined!<BR><BR></H2>\n");
		printf("<A HREF=/cgi-bin/w3-auth/NM>");
		printf("Please create a namespace</A>\n");
		sendFooter();
		exit(0);
	}
	printf("<CENTER><H2>Please select a namespace</H2>\n");
	printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/MENU>\n");
	printf("<SELECT NAME=NS>\n");

	row = msqlFetchRow(res);
	while(row)
	{
		printf("<OPTION VALUE=\"%s\">%s\n",row[0],row[0]);
		row = msqlFetchRow(res);
	}
	printf("</SELECT>\n");
	printf("<P><INPUT TYPE=SUBMIT VALUE=Enter></FORM>\n");
	msqlFreeResult(res);
	sendFooter();
}
	


void sendMainMenu()
{
	checkAuth(AUTH_ANY);
	sendOkHeader();
	if (!curNS)
	{
		sendOkHeader();
		printf("<P><BR><H2>Missing Namespace.  Exiting<P><BR>\n");
		sendFooter();
		exit(1);
	}


	printf("<CENTER><H1><I>Authentication Management</H1></I>\n");
	printf("</CENTER><P><BR>\n");

	printf("<CENTER><TABLE BORDER><TR>\n");
	printf("<TH ALIGN=left>Current Username </TH>\n");
	printf("<TD>%s</TD>\n",username);
	printf("</TR><TR>");
	printf("<TH ALIGN=left>Current Namespace</TH>\n");
	printf("<TD>%s</TD>\n", curNS);
	printf("</TR></TABLE></CENTER><P><BR>\n");

	printf("<CENTER><TABLE><TR><TD><FONT SIZE=+1>\n");

	printf("<IMG ALIGN=middle SRC=/Hughes/graphics/area.gif>");
	printf("<A HREF=/cgi-bin/w3-auth/AM?NS=%s>Area Management</A>\n",
		urlNS);
	printf("<BR CLEAR=ALL>\n");

	printf("<HR>\n");

	printf("<IMG ALIGN=middle SRC=/Hughes/graphics/group.gif>");
	printf("<A HREF=/cgi-bin/w3-auth/GM?NS=%s>Group Management</A>",
		urlNS);
	printf("<BR CLEAR=ALL>\n");

	printf("<IMG ALIGN=middle SRC=/Hughes/graphics/group.gif>");
	printf("<A HREF=/cgi-bin/w3-auth/MM?NS=%s>", urlNS);
	printf("Group Member Management</A>");
	printf("<BR CLEAR=ALL>\n");

	printf("<HR>\n");

	printf("<IMG ALIGN=middle SRC=/Hughes/graphics/user.gif>");
	printf("<A HREF=/cgi-bin/w3-auth/UM?NS=%s>User Management</A>\n",
		urlNS);
	printf("<BR CLEAR=ALL>\n");

	printf("<IMG ALIGN=middle SRC=/Hughes/graphics/logout.gif>");
	printf("<A HREF=/cgi-bin/w3-auth/LO>Forced Log Out</A><br>");
	printf("</TD></TR></TABLE></CENTER><p><br>\n");
	sendFooter();
}


void sendSuperUserMenu()
{
	checkAuth(AUTH_ANY);
	sendOkHeader();
	if (!curNS)
	{
		sendOkHeader();
		printf("<P><BR><H2>Missing Namespace.  Exiting<P><BR>\n");
		sendFooter();
		exit(1);
	}


	printf("<CENTER><H1><I>Super User Management</H1></I>\n");
	printf("</CENTER><P><BR>\n");

	printf("<CENTER><TABLE><TR><TD><FONT SIZE=+1>\n");

	printf("<IMG ALIGN=middle SRC=/Hughes/graphics/area.gif>");
	printf("<A HREF=/cgi-bin/w3-auth/NM?NS=%s>Namespace Management</A>",
		urlNS);
	printf("<BR CLEAR=ALL>\n");

	printf("<HR>\n");

	printf("<IMG ALIGN=middle SRC=/Hughes/graphics/user.gif>");
	printf("<A HREF=/cgi-bin/w3-auth/UM?NS=%s>User Management</A>\n",
		urlNS);
	printf("<BR CLEAR=ALL>\n");

	printf("<IMG ALIGN=middle SRC=/Hughes/graphics/priv.gif>");
	printf("<A HREF=/cgi-bin/w3-auth/PM?NS=%s>",urlNS);
	printf("Privilege Management</A>\n");
	printf("<BR CLEAR=ALL><BR><BR>\n");

	printf("<IMG ALIGN=middle SRC=/Hughes/graphics/logout.gif>");
	printf("<A HREF=/cgi-bin/w3-auth/LO>Forced Log Out</A><br>");
	printf("</TD></TR></TABLE></CENTER><p><br>\n");
	sendFooter();
}



void logOut(opt)
	char	*opt[];
{
	if (opt[1] == NULL)
	{
		sendOkHeader();
		printf("<CENTER><H1><I>Forced Log Out</H1></I>\n");
		printf("</CENTER><P><BR><BR><BR>\n");
		printf("<BLOCKQUOTE>\n");
		printf("Due to browsers such as Netscape \"remembering\"");
		printf(" your password for a WWW site, the only way you");
		printf(" can ensure that you have \"logged out\" of");
		printf(" w3-Auth is to follow the link below and enter");
		printf(" an invalid username or password.  You can then");
		printf(" be sure that even if someone else uses your");
		printf(" machine, they still can not access w3-Auth as");
		printf(" you.<p>");
		printf("To force a logout, just follow the link below.  ");
		printf("If you are prompted to retry authorisation, just");
		printf(" hit OK and enter an invalid username or password.  ");
		printf(" When you are informed it is invalid, just hit");
		printf(" Cancel. <p><br><br>\n");
		printf("</BLOCKQUOTE><CENTER>\n");
		printf("<FONT SIZE=+1><A HREF=/cgi-bin/w3-auth/LO/LO>Log Out</A>\n");
		sendFooter();
		exit(0);
	}
	sendAuthHeader(NULL);
}




/**************************************************************************
***************************************************************************
**	User Management Routines
**
*/

void userManagementAction(opt)
	char	*opt[];
{
	m_result *res;
	m_row	row;


	/*
	** Add User
	*/
	if (strcmp(opt[1],"AU") == 0)
	{
		char	*uname,
			*passwd,
			*cryptpw,
			*full_name;

		printf("<CENTER><H1><I>Add User</H1></I>\n");
		printf("</CENTER><P><BR>\n");

		printf("<CENTER><TABLE BORDER><TR>\n");
		printf("<TH ALIGN=left>Current Username </TH>\n");
		printf("<TD>%s</TD>\n",username);
		printf("</TR><TR>");
		printf("<TH ALIGN=left>Current Namespace</TH>\n");
		printf("<TD>%s</TD>\n", curNS);
		printf("</TR></TABLE></CENTER><P><BR>\n");

		printf("<P><BR><FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		uname = getVariable("UNAME");
		passwd = getVariable("PASSWD");
		cryptpw = (char *)fcrypt(passwd, "AA");
		full_name = getVariable("FULL_NAME");

		if (!uname || !passwd || !full_name)
		{
			runError("You must complete all fields!");
			exit(1);
		}
		if (strlen(uname)==0 || strlen(passwd)==0 || 
			strlen(full_name)==0)
		{
			runError("You must complete all fields!");
			exit(1);
		}
		uname = msqlEscape(uname);
		full_name = msqlEscape(full_name);

		sprintf(qBuf,"select full_name from users where uname='%s' and namespace='%s'",
			uname, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (row)
		{
			runError("Username already exists in this namespace!");
			exit(1);
		}
		msqlFreeResult(res);

		sprintf(qBuf,"insert into users (namespace,uname,passwd,full_name) values ('%s','%s','%s','%s')",curNS,uname,cryptpw,full_name);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		printf("New user '%s' added<p><br><br><br>\n",uname);
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");

		sendFooter();
		exit(0);
	}


	/*
	** Delete User
	*/
	if (strcmp(opt[1],"DU") == 0)
	{
		char	*uname;

		printf("<CENTER><H1><I>Delete User</H1></I>\n");
		printf("</CENTER><P><BR>\n");

		printf("<CENTER><TABLE BORDER><TR>\n");
		printf("<TH ALIGN=left>Current Username </TH>\n");
		printf("<TD>%s</TD>\n",username);
		printf("</TR><TR>");
		printf("<TH ALIGN=left>Current Namespace</TH>\n");
		printf("<TD>%s</TD>\n", curNS);
		printf("</TR></TABLE></CENTER><P><BR>\n");

		printf("<P><BR><FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		uname = getVariable("UNAME");
		if (!uname)
		{
			runError("Username Missing!");
			exit(1);
		}
		sprintf(qBuf,"delete from users where uname='%s' and namespace='%s'",
			uname, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		sprintf(qBuf,"delete from perms where namespace='%s' and uname='%s'",curNS,uname);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		printf("User Delete<p><br><br>\n");
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");
		sendFooter();
		exit(0);
	}


	/*
	** Edit User 
	*/
	if (strcmp(opt[1],"EU") == 0)
	{
		char	*uname,
			*passwd,
			*cryptpw,
			*oldpasswd,
			*full_name,
			*olduname;

		printf("<CENTER><H1><I>Edit User</H1></I>\n");
		printf("</CENTER><P><BR>\n");

		printf("<CENTER><TABLE BORDER><TR>\n");
		printf("<TH ALIGN=left>Current Username </TH>\n");
		printf("<TD>%s</TD>\n",username);
		printf("</TR><TR>");
		printf("<TH ALIGN=left>Current Namespace</TH>\n");
		printf("<TD>%s</TD>\n", curNS);
		printf("</TR></TABLE></CENTER><P><BR>\n");

		printf("<P><BR><FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		uname = getVariable("UNAME");
		passwd = getVariable("PASSWD");
		cryptpw = (char *)fcrypt(passwd, "AA");
		full_name = getVariable("FULL_NAME");
		oldpasswd = getVariable("OLD_PASSWD");
		olduname = getVariable("OLD_UNAME");

		if (!uname || !passwd || !full_name)
		{
			runError("You must complete all fields!");
			exit(1);
		}
		uname = msqlEscape(uname);
		if (strcmp(passwd,"***") == 0)
			passwd = msqlEscape(oldpasswd);
		else
			passwd = msqlEscape(passwd);
		full_name = msqlEscape(full_name);

		/* Check for existing user of that name */
		if (strcmp(olduname,uname) != 0)
		{
			sprintf(qBuf,"select full_name from users where uname='%s' and namespace='%s'",
				uname, curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			res = msqlStoreResult();
			row = msqlFetchRow(res);
			if (row)
			{
				runError("Username already exists!");
				exit(1);
			}
		}

		/* Blow away the old info */
		sprintf(qBuf,"delete from users where uname='%s' and namespace='%s'",
			olduname, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}


		/* Insert the new stuff */
		sprintf(qBuf,"insert into users (namespace,uname,passwd,full_name) values ('%s','%s', '%s', '%s')",curNS,uname,cryptpw, full_name);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		
		printf("User Edited<p><br><br>\n");
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");
		sendFooter();
		exit(0);
	}
}

void userManagement(opt)
	char	*opt[];
{
	m_result *res;
	m_row	row;
	char	*action;


	/*
	** Check authority etc
	*/
	checkAuth(AUTH_UM);
	sendOkHeader();
	if (!curNS)
	{
		sendOkHeader();
		printf("<P><BR><H2>Missing Namespace.  Exiting<P><BR>\n");
		sendFooter();
		exit(1);
	}

	/*
	** Is this an action from a previous UM screen?
	*/
	if (opt[1] != NULL)
	{
		userManagementAction(opt);
	}


	/*
	** OK, it's a config screen.  Work out which one and do the
	** right thing.
	*/
	action = getVariable("ACTION");


	/* User Management Menu */
	if (action == NULL)
	{
		printf("<CENTER><H1><I>User Management</H1></I>\n");
		printf("</CENTER><P><BR>\n");

		printf("<CENTER><TABLE BORDER><TR>\n");
		printf("<TH ALIGN=left>Current Username </TH>\n");
		printf("<TD>%s</TD>\n",username);
		printf("</TR><TR>");
		printf("<TH ALIGN=left>Current Namespace</TH>\n");
		printf("<TD>%s</TD>\n", curNS);
		printf("</TR></TABLE></CENTER><P><BR>\n");

		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/UM>\n");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<SELECT SIZE=8 NAME=uname>\n");

		sprintf(qBuf,
		"select uname from users where namespace='%s' order by uname",
			curNS);
		if(msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		while(row)
		{
			printf("<OPTION>%s\n",row[0]);
			row = msqlFetchRow(res);
		}
		printf("</SELECT>\n<p>");
		printf("<TABLE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/add.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/delete.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/edit.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/view.gif>\n");
		printf("<TR>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=ADD>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=DELETE>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=EDIT>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=VIEW>\n");
		printf("<TR></TABLE><P>\n");
		printf("<INPUT TYPE=SUBMIT VALUE=Execute></FORM>\n");
		printf("<BR>");
		printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
		printf("</A>\n");
		printf("</CENTER><p>\n");
		sendFooter();
		exit(1);
	}

	/*
	** View user option
	*/
	if (strcmp(action, "VIEW") == 0)
	{
		char	*uname;
		int	level;

		printf("<CENTER><H1><I>View User</H1></I>\n");
		printf("</CENTER><P><BR>\n");

		printf("<CENTER><TABLE BORDER><TR>\n");
		printf("<TH ALIGN=left>Current Username </TH>\n");
		printf("<TD>%s</TD>\n",username);
		printf("</TR><TR>");
		printf("<TH ALIGN=left>Current Namespace</TH>\n");
		printf("<TD>%s</TD>\n", curNS);
		printf("</TR></TABLE></CENTER><P><BR>\n");

		printf("<P><BR><FONT SIZE=+1><CENTER>\n");
		uname = getVariable("uname");
		if (!uname)
		{
			runError("Missing username");
			exit(1);
		}
		uname = msqlEscape(uname);
		sprintf(qBuf,
		"select level from perms where uname='%s' and namespace='%s'",
			uname, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
			level = 0;
		else
			level = atoi(row[0]);
		msqlFreeResult(res);
		
		sprintf(qBuf,
		"select full_name from users where uname='%s' and namespace='%s'",
			uname, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			runError("Unknown user!");
			exit(1);
		}
		printf("<TABLE BORDER>\n");
		printf("<TH ALIGN=LEFT>Username<TD>%s<TR>\n",uname);
		printf("<TH ALIGN=LEFT>Full Name<TD>%s<TR>\n",row[0]);
		printf("<TH ALIGN=LEFT>Namespace Management Access<TD>%s<TR>\n",
			(level & AUTH_NM)?"Yes":"No");
		printf("<TH ALIGN=LEFT>User Management Access<TD>%s<TR>\n",
			(level & AUTH_UM)?"Yes":"No");
		printf("<TH ALIGN=LEFT>Area Management Access<TD>%s<TR>\n",
			(level & AUTH_AM)?"Yes":"No");
		printf("<TH ALIGN=LEFT>Group Management Access<TD>%s<TR>\n",
			(level & AUTH_GM)?"Yes":"No");
		printf("<TH ALIGN=LEFT>Group Member Management Access");
		printf("<TD>%s<TR>\n", (level & AUTH_MM)?"Yes":"No");
		printf("<TH ALIGN=LEFT>Privilege Management Access<TD>%s<TR>\n",
			(level & AUTH_PM)?"Yes":"No");
		printf("</TABLE>");
		sendFooter();
		exit(1);
	}


	/*
	** Add user option
	*/
	if (strcmp(action, "ADD") == 0)
	{
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/UM/AU>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<CENTER><H1><I>Add User</H1></I>\n");
		printf("</CENTER><P><BR>\n");

		printf("<CENTER><TABLE BORDER><TR>\n");
		printf("<TH ALIGN=left>Current Username </TH>\n");
		printf("<TD>%s</TD>\n",username);
		printf("</TR><TR>");
		printf("<TH ALIGN=left>Current Namespace</TH>\n");
		printf("<TD>%s</TD>\n", curNS);
		printf("</TR></TABLE></CENTER><P><BR>\n");

		printf("<P><BR><FONT SIZE=+1><CENTER>\n");
		printf("<TABLE BORDER>\n");
		printf("<TH ALIGN=LEFT>Username\n");
		printf("<TD><INPUT SIZE=30 NAME=UNAME><TR>\n");
		printf("<TH ALIGN=LEFT>Full Name\n");
		printf("<TD><INPUT SIZE=30 NAME=FULL_NAME><TR>\n");
		printf("<TH ALIGN=LEFT>Password\n");
		printf("<TD><INPUT SIZE=30 TYPE=PASSWORD NAME=PASSWD><TR>\n");
		printf("</TABLE>");
		printf("<p><INPUT TYPE=SUBMIT VALUE=Execute>");
		sendFooter();
		exit(1);
	}


	/*
	** Delete user option
	*/
	if (strcmp(action, "DELETE") == 0)
	{
		char	*uname;

		uname = getVariable("uname");
		if (!uname)
		{
			runError("Missing username");
			exit(1);
		}
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/UM/DU>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<CENTER><H1><I>Delete User</H1></I>\n");
		printf("<P><BR>\n");

		printf("<CENTER><TABLE BORDER><TR>\n");
		printf("<TH ALIGN=left>Current Username </TH>\n");
		printf("<TD>%s</TD>\n",username);
		printf("</TR><TR>");
		printf("<TH ALIGN=left>Current Namespace</TH>\n");
		printf("<TD>%s</TD>\n", curNS);
		printf("</TR></TABLE></CENTER><P><BR>\n");

		printf("<FONT SIZE=+1><P><BR>\n");
		printf("Are you sure you want to delete '%s'?\n",uname);
		uname = msqlEscape(uname);
		printf("<INPUT TYPE=HIDDEN NAME=UNAME VALUE=\"%s\">\n", uname);
		printf("<p><INPUT TYPE=SUBMIT VALUE=Yes>\n");
		printf("</FORM><p>\n");
		sendFooter();
	}


	/*
	** Edit user option
	*/
	if (strcmp(action, "EDIT") == 0)
	{
		char	*uname,
			*oldUname;

		uname = getVariable("uname");
		if (!uname)
		{
			runError("Missing username");
			exit(1);
		}
		oldUname = (char *)strdup(uname);
		uname = msqlEscape(uname);
		sprintf(qBuf,
		    "select full_name,passwd from users where uname='%s' and namespace = '%s'",
			uname, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			runError("Unknown user!");
			exit(1);
		}
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/UM/EU>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<CENTER><H1><I>Edit User</H1></I>\n");
		printf("<P><BR>\n");

		printf("<CENTER><TABLE BORDER><TR>\n");
		printf("<TH ALIGN=left>Current Username </TH>\n");
		printf("<TD>%s</TD>\n",username);
		printf("</TR><TR>");
		printf("<TH ALIGN=left>Current Namespace</TH>\n");
		printf("<TD>%s</TD>\n", curNS);
		printf("</TR></TABLE></CENTER><P><BR>\n");

		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<TABLE BORDER>\n");
		printf("<TH ALIGN=LEFT>Username\n");
		printf("<TD><INPUT SIZE=30 NAME=UNAME VALUE=\"%s\"><TR>\n",
			oldUname);
		printf("<TH ALIGN=LEFT>Full Name\n");
		printf("<TD><INPUT SIZE=30 NAME=FULL_NAME VALUE=\"%s\"><TR>\n",
			row[0]);
		printf("<TH ALIGN=LEFT>Password\n");
		printf("<TD><INPUT SIZE=30 TYPE=PASSWORD NAME=PASSWD VALUE=\"***\"><TR>\n");
		printf("</TABLE>");
		printf("<INPUT TYPE=HIDDEN NAME=OLD_PASSWD VALUE=\"%s\">",
			row[1]);
		printf("<INPUT TYPE=HIDDEN NAME=OLD_UNAME VALUE=\"%s\">",
			oldUname);
		printf("<p><INPUT TYPE=SUBMIT VALUE=Execute>");
		printf("</FORM><p>\n");
		sendFooter();
		exit(1);
	}
}


/**************************************************************************
***************************************************************************
**	Namespace Management Routines
**
*/

void namespaceManagementAction(opt)
	char	*opt[];
{
	m_result *res;
	m_row	row;


	/*
	** Add Namespace
	*/
	if (strcmp(opt[1],"AN") == 0)
	{
		char	*ns,
			*descr,
			*admins,
			*curAdmin;

		printf("<CENTER><H1><I>Add Namespace</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		ns = getVariable("NAME");
		descr = getVariable("DESCR");
		admins = getVariable("ADMINS");

		if (!ns || !descr || !admins)
		{
			runError("You must complete all fields!");
			exit(1);
		}
		if (strlen(ns)==0 || strlen(descr)==0 || strlen(admins)==0)
		{
			runError("You must complete all fields!");
			exit(1);
		}
		sprintf(qBuf,
			"select descr from namespaces where namespace='%s'",
			ns);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (row)
		{
			runError("Namespace already exists!");
			exit(1);
		}
		msqlFreeResult(res);
		sprintf(qBuf,"insert into namespaces (namespace,descr)values('%s','%s')",
			ns,descr);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		curAdmin = (char *)strtok(admins,"\001");
		while(curAdmin)
		{
			sprintf(qBuf,"insert into perms (namespace,uname,level)values('%s','%s',0)",ns,curAdmin);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			curAdmin = (char *)strtok(NULL,"\001");
		}
		printf("New namespace added<p><br><br><br>\n");
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");

		sendFooter();
		exit(0);
	}


	/*
	** Delete Namespace
	*/
	if (strcmp(opt[1],"DN") == 0)
	{
		char	*ns;

		printf("<CENTER><H1><I>Delete Namespace</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		ns = getVariable("NAMESPACE");
		if (!ns)
		{
			runError("Namepsace name Missing!");
			exit(1);
		}
		sprintf(qBuf,"delete from namespaces where namespace='%s'",ns);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		sprintf(qBuf,"delete from perms where namespace='%s'",ns);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		sprintf(qBuf,"delete from users where namespace='%s'",ns);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		sprintf(qBuf,"delete from areas where namespace='%s'",ns);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		sprintf(qBuf,"delete from groups where namespace='%s'",ns);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		printf("Namespace Delete<p><br><br>\n");
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");
		sendFooter();
		exit(0);
	}


	/*
	** Edit Namespace 
	*/
	if (strcmp(opt[1],"EN") == 0)
	{
		char	*oldName,
			*name,
			*oldDescr,
			*descr,
			*addAdmins,
			*delAdmins,
			*curAdmin;

		printf("<CENTER><H1><I>Edit Namespace</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		name = getVariable("NAMESPACE");
		oldName = getVariable("OLDNS");
		descr = getVariable("DESCR");
		oldDescr = getVariable("OLDDESCR");
		addAdmins = getVariable("ADD");
		delAdmins = getVariable("REMOVE");

		if (!name || !descr)
		{
			runError("You must specify a name and a description!");
			exit(1);
		}
		if (strlen(name)==0 || strlen(descr)==0)
		{
			runError("You must specify a name and a description!");
			exit(1);
		}
		if (strcmp(oldName,name) != 0)
		{
			sprintf(qBuf,
			    "select descr from namespaces where namespace='%s'",
			    name);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			res = msqlStoreResult();
			row = msqlFetchRow(res);
			if (row)
			{
				runError("Namespace already exists!");
				exit(1);
			}
			msqlFreeResult(res);
		}

		/*
		** Blow away the old stuff
		*/
		sprintf(qBuf,"delete from namespaces where namespace='%s'",
			oldName);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		/*
		** update the users etc
		*/
		sprintf(qBuf,
			"update users set namespace='%s' where namespace='%s'",
			name,oldName);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		
		sprintf(qBuf,
			"update perms set namespace='%s' where namespace='%s'",
			name,oldName);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		sprintf(qBuf,
			"update areas set namespace='%s' where namespace='%s'",
			name,oldName);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		sprintf(qBuf,
			"update group_members set namespace='%s' where namespace='%s'",
			name,oldName);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		sprintf(qBuf,
			"update groups set namespace='%s' where namespace='%s'",
			name,oldName);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		sprintf(qBuf,
			"update group_admin set namespace='%s' where namespace='%s'",
			name,oldName);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		/*
		** Delete and removed admins
		*/
		curAdmin = (char *)strtok(delAdmins,"\001");
		while(curAdmin)
		{
			sprintf(qBuf,"delete from perms where namespace='%s' and uname='%s'",
			name,curAdmin);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			curAdmin = (char *)strtok(NULL,"\001");
		}

		/*
		** Insert the new stuff
		*/
		sprintf(qBuf,"insert into namespaces (namespace,descr)values('%s','%s')",
			name,descr);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		curAdmin = (char *)strtok(addAdmins,"\001");
		while(curAdmin)
		{
			sprintf(qBuf,"insert into perms (namespace,uname,level) values ('%s','%s',0)",
			name,curAdmin);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			curAdmin = (char *)strtok(NULL,"\001");
		}

	
		printf("Namespace edited<p><br><br><br>\n");
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");

		sendFooter();
		exit(0);
	}
}



void namespaceManagement(opt)
	char	*opt[];
{
	m_result *res, *res2;
	m_row	row, row2;
	char	*action;


	/*
	** Check authority etc
	*/
	checkAuth(AUTH_NM);
	sendOkHeader();

	/*
	** Is this an action from a previous NM screen?
	*/
	if (opt[1] != NULL)
	{
		namespaceManagementAction(opt);
	}


	/*
	** OK, it's a config screen.  Work out which one and do the
	** right thing.
	*/
	action = getVariable("ACTION");


	/* Namespace Management Menu */
	if (action == NULL)
	{
		printf("<CENTER><H1><I>Namespace Management</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/NM>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<SELECT SIZE=8 NAME=NAMESPACE>\n");

		if(msqlQuery(sock,
		     "select namespace from namespaces order by namespace")<0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		while(row)
		{
			printf("<OPTION>%s\n",row[0]);
			row = msqlFetchRow(res);
		}
		printf("</SELECT>\n<p>");
		printf("<TABLE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/add.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/delete.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/edit.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/view.gif>\n");
		printf("<TR>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=ADD>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=DELETE>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=EDIT>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=VIEW>\n");
		printf("<TR></TABLE><P>\n");
		printf("<INPUT TYPE=SUBMIT VALUE=Execute></FORM>\n");
		printf("<BR>");
		printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
		printf("</A>\n");
		printf("</CENTER><p>\n");
		sendFooter();
		exit(1);
	}

	/*
	** View namespace option
	*/
	if (strcmp(action, "VIEW") == 0)
	{
		char	*namespace;

		printf("<CENTER><H1><I>View Namespace</H1></I>\n");
		printf("</CENTER><P><BR><BR><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		namespace = getVariable("NAMESPACE");
		if (!namespace)
		{
			runError("Missing namespace name");
			exit(1);
		}
		namespace = msqlEscape(namespace);

		sprintf(qBuf, 
			"select descr from namespaces where namespace='%s'", 
			namespace);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			runError("Unknown namespace!");
			exit(1);
		}
		printf("<TABLE BORDER>\n");
		printf("<TH ALIGN=LEFT>Namespace Name<TD>%s<TR>\n",
			namespace);
		printf("<TH ALIGN=LEFT>Namespace Description<TD>%s<TR>\n",
			row[0]);
		printf("<TH VALIGN=TOP ALIGN=LEFT>Namespace Administrators");
		printf("<TD>\n");

		msqlFreeResult(res);
		sprintf(qBuf,"select uname from perms where namespace='%s'",
			namespace);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		while(row)
		{
			printf("%s\n",row[0]);
			row = msqlFetchRow(res);
			if (row)
				printf("<BR>");
		}
		printf("</TABLE><P>\n");

		sendFooter();
		exit(1);
	}


	/*
	** Add namespace option
	*/
	if (strcmp(action, "ADD") == 0)
	{
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/NM/AN>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<CENTER><H1><I>Add Namespace</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER><TABLE>\n");
		printf("<TH ALIGN=left>Namespace Name<TD>\n");
		printf("<INPUT SIZE=20 NAME=NAME>\n<TR>");
		printf("<TH ALIGN=left>Namespace Description<TD>\n");
		printf("<INPUT SIZE=20 NAME=DESCR>\n<TR>");
		printf("<TH VALIGN=TOP ALIGN=left>Namespace Administrators\n");
		printf("<TD><SELECT SIZE=8 NAME=ADMINS MULTIPLE>\n");

		sprintf(qBuf,
			"select uname from users where namespace='SuperUser'");
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		while(row)
		{
			printf("<OPTION VALUE=\"%s\">%s\n",row[0],row[0]);
			row = msqlFetchRow(res);
			if (row)
				printf("<BR>\n");
		}
		printf("</SELECT>\n");
		printf("</TABLE>\n");
		printf("<p><INPUT TYPE=SUBMIT VALUE=Execute>");
		sendFooter();
		exit(1);
	}


	/*
	** Delete user option
	*/
	if (strcmp(action, "DELETE") == 0)
	{
		char	*ns;

		ns = getVariable("NAMESPACE");
		if (!ns)
		{
			runError("Missing Namespace");
			exit(1);
		}
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/NM/DN>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<CENTER><H1><I>Delete Namespace</H1></I>\n");
		printf("<FONT SIZE=+1><P><BR>\n");
		printf("Are you sure you want to delete '%s' and all its users?\n",ns);
		ns = msqlEscape(ns);
		printf("<INPUT TYPE=HIDDEN NAME=NAMESPACE VALUE=\"%s\">\n", ns);
		printf("<p><INPUT TYPE=SUBMIT VALUE=Yes>\n");
		printf("</FORM><p>\n");
		sendFooter();
	}


	/*
	** Edit user option
	*/
	if (strcmp(action, "EDIT") == 0)
	{
		char	*ns,
			*oldNS;

		ns = getVariable("NAMESPACE");
		if (!ns)
		{
			runError("Missing Namespace");
			exit(1);
		}
		oldNS = (char *)strdup(ns);
		oldNS = msqlEscape(ns);
		sprintf(qBuf,
		    "select descr from namespaces where namespace='%s'",ns);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			runError("Unknown namespace!");
			exit(1);
		}
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/NM/EN>");
		printf("<CENTER><H1><I>Edit Namespace</H1></I>\n");
		printf("<FONT SIZE=+1><CENTER><TABLE>\n");
		printf("<TH ALIGN=left>Namespace Name<TD>\n");
		printf("<INPUT SIZE=20 NAME=NAMESPACE VALUE=\"%s\">\n<TR>",ns);
		printf("<TH ALIGN=left>Namespace Description<TD>\n");
		printf("<INPUT SIZE=20 NAME=DESCR VALUE=\"%s\">\n<TR>", row[0]);
		printf("<TH VALIGN=TOP ALIGN=left>Remove Administrators<TD>\n");

		printf("<INPUT TYPE=HIDDEN NAME=OLDNS VALUE=\"%s\">\n",ns);
		printf("<INPUT TYPE=HIDDEN NAME=OLDDESCR VALUE=\"%s\">\n",
			row[0]);
		printf("<SELECT NAME=REMOVE SIZE=5 MULTIPLE>\n");
		sprintf(qBuf,"select uname from perms where namespace='%s'",
			oldNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		while(row)
		{
			printf("<OPTION VALUE=\"%s\">%s\n",row[0], row[0]);
			row = msqlFetchRow(res);
			if (row)
				printf("<BR>");
		}
		printf("</SELECT><TR>\n");

		printf("<TH VALIGN=TOP ALIGN=left>Add Administrators<TD>\n");
		printf("<SELECT NAME=ADD SIZE=5 MULTIPLE>\n");
		sprintf(qBuf,"select uname from users where namespace='%s'",
			"SuperUser");
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res2 = msqlStoreResult();
		row2 = msqlFetchRow(res2);
		while(row2)
		{
			msqlDataSeek(res,0);
			row = msqlFetchRow(res);
			while(row)
			{
				if (strcmp(row[0],row2[0]) == 0)
				{
					break;
				}
				row = msqlFetchRow(res);
			}
			if (!row)
			{
				printf("<OPTION VALUE=\"%s\">%s\n",
					row2[0], row2[0]);
			}
			row2 = msqlFetchRow(res2);
		}
		printf("</SELECT><TD>\n");

		printf("</TABLE>\n");
		msqlFreeResult(res);

		printf("<INPUT TYPE=HIDDEN NAME=OLD_NAME VALUE=\"%s\">",
			oldNS);
		printf("<p><INPUT TYPE=SUBMIT VALUE=Execute>");
		printf("</FORM><p>\n");
		sendFooter();
		exit(1);
	}
}


/**************************************************************************
***************************************************************************
**	Area Management Routines
**
*/

void areaManagementAction(opt)
	char	*opt[];
{
	m_result *res;
	m_row	row;


	/*
	** Add Area
	*/
	if (strcmp(opt[1],"AA") == 0)
	{
		char	*url,
			*name,
			*aclIndex,
			*aclGroup,
			*aclAcl,
			*msqlNS,
			nameBuf[20];
		int	index,
			indexVal;

		printf("<CENTER><H1><I>Add Area</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		url = getVariable("URL");
		name = getVariable("NAME");

		if (!url || !name)
		{
			runError("You must specify a name and a URL!");
			exit(1);
		}
		if (strlen(url)==0 || strlen(name)==0)
		{
			runError("You must specify a name and a URL!");
			exit(1);
		}

		url = msqlEscape(url);
		name = msqlEscape(name);
		msqlNS = msqlEscape(curNS);

		sprintf(qBuf,"select url from areas where name='%s' and namespace = '%s'",name, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (row)
		{
			runError("Area already exists!");
			exit(1);
		}
		msqlFreeResult(res);
		sprintf(qBuf,
 		 "insert into areas (name,url,namespace)values('%s','%s','%s')",
			name,url,curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		for (index=1; index<=5; index++)
		{
			sprintf(nameBuf,"INDEX_%d",index);
			aclIndex = getVariable(nameBuf);
			indexVal = atoi(aclIndex);
			if (indexVal == 0)
				continue;
			sprintf(nameBuf,"GROUP_%d",index);
			aclGroup = getVariable(nameBuf);
			sprintf(nameBuf,"ACL_%d",index);
			aclAcl = getVariable(nameBuf);
			sprintf(qBuf,"insert into area_acl (name,offset,group,acl,namespace)values('%s',%d,'%s','%s','%s')",name,indexVal,aclGroup,aclAcl,curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
		}
	
		printf("New area added<p><br><br><br>\n");
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");

		sendFooter();
		exit(0);
	}


	/*
	** Delete Area
	*/
	if (strcmp(opt[1],"DA") == 0)
	{
		char	*area;

		printf("<CENTER><H1><I>Delete Area</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		area = getVariable("AREA");
		if (!area)
		{
			runError("Area name Missing!");
			exit(1);
		}
		sprintf(qBuf,"delete from areas where name='%s' and namespace='%s'",area, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		sprintf(qBuf,"delete from area_acl where name='%s' and namespace='%s'",area, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		printf("Area Delete<p><br><br>\n");
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");
		sendFooter();
		exit(0);
	}


	/*
	** Edit Area 
	*/
	if (strcmp(opt[1],"EA") == 0)
	{
		char	*url,
			*oldName,
			*name,
			*aclIndex,
			*aclGroup,
			*aclAcl,
			nameBuf[20];
		int	index,
			indexVal;

		printf("<CENTER><H1><I>Edit Area</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		url = getVariable("URL");
		name = getVariable("NAME");
		oldName = getVariable("OLD_NAME");

		if (!url || !name)
		{
			runError("You must specify a name and a URL!");
			exit(1);
		}
		if (strlen(url)==0 || strlen(name)==0)
		{
			runError("You must specify a name and a URL!");
			exit(1);
		}
		if (strcmp(oldName,name) != 0)
		{
			sprintf(qBuf,"select url from areas where name='%s' and namespace='%s'",
				name, curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			res = msqlStoreResult();
			row = msqlFetchRow(res);
			if (row)
			{
				runError("Area already exists!");
				exit(1);
			}
			msqlFreeResult(res);
		}

		/*
		** Blow away the old stuff
		*/
		sprintf(qBuf,"delete from areas where name='%s' and namespace = '%s'",oldName, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		sprintf(qBuf,"delete from area_acl where name='%s' and namespace='%s'",oldName, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}



		/*
		** Insert the new stuff
		*/
		sprintf(qBuf,"insert into areas (name,url,namespace)values('%s','%s','%s')",
			name,url, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		for (index=1; index<=5; index++)
		{
			sprintf(nameBuf,"INDEX_%d",index);
			aclIndex = getVariable(nameBuf);
			indexVal = atoi(aclIndex);
			if (indexVal == 0)
				continue;
			sprintf(nameBuf,"GROUP_%d",index);
			aclGroup = getVariable(nameBuf);
			sprintf(nameBuf,"ACL_%d",index);
			aclAcl = getVariable(nameBuf);
			sprintf(qBuf,"insert into area_acl (name,offset,group,acl,namespace)values('%s',%d,'%s','%s','%s')",name,indexVal,aclGroup,aclAcl,curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
		}
	
		printf("Area edited<p><br><br><br>\n");
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");

		sendFooter();
		exit(0);
	}
}

void areaManagement(opt)
	char	*opt[];
{
	m_result *res, *res2;
	m_row	row, row2;
	char	*action;


	/*
	** Check authority etc
	*/
	if (!curNS)
	{
		sendOkHeader();
		printf("<P><BR><H2>Missing Namespace.  Exiting<P><BR>\n");
		sendFooter();
		exit(1);
	}
	checkAuth(AUTH_AM);
	sendOkHeader();

	/*
	** Is this an action from a previous AM screen?
	*/
	if (opt[1] != NULL)
	{
		areaManagementAction(opt);
	}


	/*
	** OK, it's a config screen.  Work out which one and do the
	** right thing.
	*/
	action = getVariable("ACTION");


	/* Area Management Menu */
	if (action == NULL)
	{
		printf("<CENTER><H1><I>Area Management</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/AM>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<SELECT SIZE=8 NAME=AREA>\n");

		sprintf(qBuf,
		  "select name from areas where namespace='%s' order by name",
			curNS);
		if(msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		while(row)
		{
			printf("<OPTION>%s\n",row[0]);
			row = msqlFetchRow(res);
		}
		printf("</SELECT>\n<p>");
		printf("<TABLE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/add.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/delete.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/edit.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/view.gif>\n");
		printf("<TR>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=ADD>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=DELETE>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=EDIT>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=VIEW>\n");
		printf("<TR></TABLE><P>\n");
		printf("<INPUT TYPE=SUBMIT VALUE=Execute></FORM>\n");
		printf("<BR>");
		printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
		printf("</A>\n");
		printf("</CENTER><p>\n");
		sendFooter();
		exit(1);
	}

	/*
	** View area option
	*/
	if (strcmp(action, "VIEW") == 0)
	{
		char	*area;

		printf("<CENTER><H1><I>View Area</H1></I>\n");
		printf("</CENTER><P><BR><BR><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		area = getVariable("AREA");
		if (!area)
		{
			runError("Missing area name");
			exit(1);
		}
		area = msqlEscape(area);

		sprintf(qBuf, "select url from areas where name='%s' and namespace = '%s'", area, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			runError("Unknown area!");
			exit(1);
		}
		printf("<TABLE>\n");
		printf("<TH ALIGN=LEFT>Area Name<TD>%s<TR>\n",area);
		printf("<TH ALIGN=LEFT>Area URL<TD>%s<TR>\n",row[0]);
		printf("</TABLE><P>\n");

		sprintf(qBuf,
			"select offset,group,acl from area_acl where name='%s' and namespace='%s' order by offset", area, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			runError("Unknown area!");
			exit(1);
		}
		printf("<TABLE BORDER>\n");
		printf("<TH>Index<TH>Access Group<TH>Access Control<TR>\n");
		while(row)
		{
			printf("<TD>%s<TD>%s<TD>%s<TR>\n",row[0],row[1],row[2]);
			row = msqlFetchRow(res);
		}
		printf("</TABLE>");
		sendFooter();
		exit(1);
	}


	/*
	** Add area option
	*/
	if (strcmp(action, "ADD") == 0)
	{
		int	index;

		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/AM/AA>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<CENTER><H1><I>Add Area</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER><TABLE>\n");
		printf("<TH ALIGN=left>Area Name<TD>\n");
		printf("<INPUT SIZE=20 NAME=NAME>\n<TR>");
		printf("<TH ALIGN=left>Area URL<TD>\n");
		printf("<INPUT SIZE=20 NAME=URL>\n<TR>");
		printf("</TABLE>\n");

		printf("<BR><TABLE BORDER>\n");
		printf("<TH>Index<TH>Access Group<TH>Access Control<TR>\n");
		sprintf(qBuf,"select group from groups where namespace='%s' order by group",curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();

		for (index=1; index<=5; index++)
		{
			printf("<TD><SELECT SIZE=1 NAME=INDEX_%d>\n",index);
			printf("<OPTION>Inactive\n");
			printf("<OPTION>%d\n",index);
			printf("</SELECT>\n");

			msqlDataSeek(res,0);
			row = msqlFetchRow(res);
			printf("<TD><SELECT SIZE=1 NAME=GROUP_%d>\n",index);
			printf("<OPTION>** Public **\n");
			while (row)
			{
				printf("<OPTION>%s\n",row[0]);
				row = msqlFetchRow(res);
			}
			printf("</SELECT>\n");
	
			printf("<TD><INPUT SIZE=20 NAME=ACL_%d>\n",index);
			printf("<TR>\n");
		}
		
		printf("</TABLE>\n");
		printf("<p><INPUT TYPE=SUBMIT VALUE=Execute>");
		sendFooter();
		exit(1);
	}


	/*
	** Delete user option
	*/
	if (strcmp(action, "DELETE") == 0)
	{
		char	*area;

		area = getVariable("AREA");
		if (!area)
		{
			runError("Missing area name");
			exit(1);
		}
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/AM/DA>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<CENTER><H1><I>Delete Area</H1></I>\n");
		printf("<FONT SIZE=+1><P><BR>\n");
		printf("Are you sure you want to delete '%s'?\n",area);
		area = msqlEscape(area);
		printf("<INPUT TYPE=HIDDEN NAME=AREA VALUE=\"%s\">\n", area);
		printf("<p><INPUT TYPE=SUBMIT VALUE=Yes>\n");
		printf("</FORM><p>\n");
		sendFooter();
	}


	/*
	** Edit user option
	*/
	if (strcmp(action, "EDIT") == 0)
	{
		char	*area,
			*oldArea;
		int	index;

		area = getVariable("AREA");
		if (!area)
		{
			runError("Missing area name");
			exit(1);
		}
		oldArea = (char *)strdup(area);
		area = msqlEscape(area);
		sprintf(qBuf,
		    "select url from areas where name='%s' and namespace='%s'",area, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			runError("Unknown area!");
			exit(1);
		}
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/AM/EA>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<CENTER><H1><I>Edit Area</H1></I>\n");
		printf("<FONT SIZE=+1><CENTER><TABLE>\n");
		printf("<TH ALIGN=left>Area Name<TD>\n");
		printf("<INPUT SIZE=20 NAME=NAME VALUE=\"%s\">\n<TR>",area);
		printf("<TH ALIGN=left>Area URL<TD>\n");
		printf("<INPUT SIZE=20 NAME=URL VALUE=\"%s\">\n<TR>", row[0]);
		printf("</TABLE>\n");
		msqlFreeResult(res);

		printf("<BR><TABLE BORDER>\n");
		printf("<TH>Index<TH>Access Group<TH>Access Control<TR>\n");
		sprintf(qBuf,"select group from groups order by group");
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		sprintf(qBuf,"select offset,group,acl from area_acl where name='%s' and namespace = '%s' order by offset",area, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res2 = msqlStoreResult();
		row2 = msqlFetchRow(res2);

		for (index=1; index<=5; index++)
		{
			printf("<TD><SELECT SIZE=1 NAME=INDEX_%d>\n",index);

			if (row2)
			{
				if (atoi(row2[0]) == index)
				{
					printf("<OPTION>Inactive\n");
					printf("<OPTION SELECTED>%d\n",index);
				}
				else
				{
					printf("<OPTION SELECTED>Inactive\n");
					printf("<OPTION >%d\n",index);
				}
				
			}
			else
			{
				printf("<OPTION SELECTED>Inactive\n");
				printf("<OPTION>%d\n",index);
			}
			printf("</SELECT>\n");

			msqlDataSeek(res,0);
			row = msqlFetchRow(res);
			printf("<TD><SELECT SIZE=1 NAME=GROUP_%d>\n",index);
			printf("<OPTION>** Public **\n");
			while (row)
			{
				if (row2)
				{
					if (strcmp(row[0],row2[1]) == 0)
						printf("<OPTION selected>%s\n",
							row[0]);
					else
						printf("<OPTION>%s\n",row[0]);
				}
				else
				{
					printf("<OPTION>%s\n",row[0]);
				}
				row = msqlFetchRow(res);
			}
			printf("</SELECT>\n");

			if (row2)
				printf( "<TD><INPUT SIZE=20 NAME=ACL_%d VALUE=\"%s\">\n", index, row2[2]);
			else
				printf( "<TD><INPUT SIZE=20 NAME=ACL_%d>\n", index);
			printf("<TR>\n");
			if (row2)
				row2 = msqlFetchRow(res2);
		}
		printf("</TABLE>\n");

		printf("<INPUT TYPE=HIDDEN NAME=OLD_NAME VALUE=\"%s\">",
			oldArea);
		printf("<p><INPUT TYPE=SUBMIT VALUE=Execute>");
		printf("</FORM><p>\n");
		sendFooter();
		exit(1);
	}
}



/**************************************************************************
***************************************************************************
**	Group Management Routines
**
*/

void groupManagementAction(opt)
	char	*opt[];
{
	m_result *res;
	m_row	row;


	/*
	** Add Group
	*/
	if (strcmp(opt[1],"AG") == 0)
	{
		char	*group,
			*descr,
			*admins,
			*admin;

		printf("<CENTER><H1><I>Add Group</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		group = getVariable("GROUP");
		descr = getVariable("DESCR");
		admins = getVariable("ADMINS");


		if (!group || !descr || !admins)
		{
			runError("You must complete all fields!");
			exit(1);
		}
		if (strlen(group)==0 || strlen(descr)==0 || 
			strlen(admins)==0)
		{
			runError("You must complete all fields!");
			exit(1);
		}

		group = msqlEscape(group);
		descr = msqlEscape(descr);

		sprintf(qBuf,"select group from groups where group='%s' and namespace='%s'",
			group, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (row)
		{
			runError("Group already exists!");
			exit(1);
		}
		msqlFreeResult(res);
		sprintf(qBuf,"insert into groups(group,descr,namespace)values('%s','%s','%s')",
			group,descr, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		admin = (char *)strtok(admins,"\001");
		while(admin)
		{
			sprintf(qBuf,
			"insert into group_admin(group,uname,namespace)values('%s','%s','%s')",
			group,admin,curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			admin = (char *)strtok(NULL,"\001");
		}
		printf("New group '%s' added<p><br><br><br>\n",group);
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");

		sendFooter();
		exit(0);
	}


	/*
	** Delete Group
	*/
	if (strcmp(opt[1],"DG") == 0)
	{
		char	*group;

		printf("<CENTER><H1><I>Delete Group</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		group = getVariable("GROUP");
		if (!group)
		{
			runError("Group name Missing!");
			exit(1);
		}
		group = msqlEscape(group);
		sprintf(qBuf,"delete from groups where group='%s' and namespace='%s'",
			group, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		sprintf(qBuf,"delete from group_admin where group='%s' and namespace='%s'",group, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		printf("Group Deleted<p><br><br>\n");
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");
		sendFooter();
		exit(0);
	}


	/*
	** Edit Group 
	*/
	if (strcmp(opt[1],"EG") == 0)
	{
		char	*group,
			*oldGroup,
			*descr,
			*delete,
			*add,
			*admin;

		printf("<CENTER><H1><I>Edit Group</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		group = getVariable("GROUP");
		oldGroup = getVariable("OLD_GROUP");
		descr = getVariable("DESCR");
		delete = getVariable("DELETE");
		add = getVariable("ADD");

		if (!group || !descr)
		{
			runError("You must supply a name and description");
			exit(1);
		}
		group = msqlEscape(group);
		descr = msqlEscape(descr);
		oldGroup = msqlEscape(oldGroup);

		/* Check for existing group of that name */
		if (strcmp(oldGroup,group) != 0)
		{
			sprintf(qBuf,"select descr from groups where group='%s' and namespace='%s'",
				group, curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			res = msqlStoreResult();
			row = msqlFetchRow(res);
			if (row)
			{
				runError("Group name already exists!");
				exit(1);
			}
		}

		/* Blow away the old info */
		sprintf(qBuf,"delete from groups where group='%s' and namespace='%s'",
			oldGroup, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}


		/* Insert the new stuff */
		sprintf(qBuf,"insert into groups (group,descr,namespace) values ('%s','%s','%s')",group,descr,curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		/* Update the admin info */
		if (strcmp(group, oldGroup) != 0)
		{
			sprintf(qBuf,"update group_admin set group='%s' where group='%s' and namespace='%s'", group, oldGroup, curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
		}
		admin = (char *)strtok(delete,"\001");
		while(admin)
		{
			sprintf(qBuf,"delete from group_admin where uname='%s' and group = '%s' and namespace='%s'",
				admin, group, curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			admin = (char *)strtok(NULL,"\001");
		}
		admin = (char *)strtok(add,"\001");
		while(admin)
		{
			sprintf(qBuf,
			"insert into group_admin(group,uname,namespace)values('%s','%s','%s')",
				group, admin, curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			admin = (char *)strtok(NULL,"\001");
		}
		
		printf("Group Edited<p><br><br>\n");
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");
		sendFooter();
		exit(0);
	}
}



void groupManagement(opt)
	char	*opt[];
{
	m_result *res,
		*res2;
	m_row	row,
		row2;
	char	*action;


	/*
	** Check authority etc
	*/
	if (!curNS)
	{
		sendOkHeader();
		printf("<P><BR><H2>Missing Namespace.  Exiting<P><BR>\n");
		sendFooter();
		exit(1);
	}
	checkAuth(AUTH_GM);
	sendOkHeader();

	/*
	** Is this an action from a previous GM screen?
	*/
	if (opt[1] != NULL)
	{
		groupManagementAction(opt);
	}


	/*
	** OK, it's a config screen.  Work out which one and do the
	** right thing.
	*/
	action = getVariable("ACTION");


	/* Group Management Menu */
	if (action == NULL)
	{
		printf("<CENTER><H1><I>Group Management</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/GM>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<SELECT SIZE=8 NAME=group_name>\n");

		sprintf(qBuf,"select group from groups where namespace='%s' order by group", curNS);
		if(msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		while(row)
		{
			printf("<OPTION>%s\n",row[0]);
			row = msqlFetchRow(res);
		}
		printf("</SELECT>\n<p>");
		printf("<TABLE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/add.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/delete.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/edit.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/view.gif>\n");
		printf("<TR>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=ADD>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=DELETE>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=EDIT>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=VIEW>\n");
		printf("<TR></TABLE><P>\n");
		printf("<INPUT TYPE=SUBMIT VALUE=Execute></FORM>\n");
		printf("<BR>");
		printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
		printf("</A>\n");
		printf("</CENTER><p>\n");
		sendFooter();
		exit(1);
	}

	/*
	** View group option
	*/
	if (strcmp(action, "VIEW") == 0)
	{
		char	*group,
			*origGroup;

		printf("<CENTER><H1><I>View Group</H1></I>\n");
		printf("</CENTER><P><BR><BR><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		group = getVariable("group_name");
		if (!group)
		{
			runError("Missing group name");
			exit(1);
		}
		origGroup = (char *)strdup(group);
		group = msqlEscape(group);
		sprintf(qBuf,
			"select groups.descr,group_admin.uname from groups, group_admin where groups.group = '%s' and group_admin.group = '%s' and groups.namespace='%s' and group_admin.namespace='%s' order by group_admin.uname", group, group, curNS, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			runError("Unknown group!");
			exit(1);
		}
		printf("<TABLE BORDER>\n");
		printf("<TH ALIGN=LEFT>Group Name<TD>%s<TR>\n",origGroup);
		printf("<TH ALIGN=LEFT>Group Description<TD>%s<TR>\n",row[0]);
		printf("<TH ALIGN=LEFT>Group Administrators<TD>\n");
		while(row)
		{
			printf("%s<br>\n",row[1]);
			row = msqlFetchRow(res);
		}
		printf("</TABLE>");
		sendFooter();
		exit(1);
	}


	/*
	** Add group option
	*/
	if (strcmp(action, "ADD") == 0)
	{

		sprintf(qBuf,
		        "select uname,level from perms where level>0 and namespace='%s' order by uname",
			curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/GM/AG>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<CENTER><H1><I>Add Group</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<TABLE BORDER>\n");
		printf("<TH ALIGN=LEFT>Group Name\n");
		printf("<TD><INPUT SIZE=30 NAME=GROUP><TR>\n");
		printf("<TH ALIGN=LEFT>Group Description\n");
		printf("<TD><INPUT SIZE=30 NAME=DESCR><TR>\n");
		printf("<TH ALIGN=LEFT>Group Administrators\n");
		printf("<TD><SELECT MULTIPLE SIZE=8 NAME=ADMINS>\n");

		while(row)
		{
			if (atoi(row[1]) & AUTH_MM)
				printf("<OPTION>%s\n",row[0]);
			row = msqlFetchRow(res);
		}

		printf("</SELECT></TABLE>");
		printf("<p><INPUT TYPE=SUBMIT VALUE=Execute>");
		sendFooter();
		exit(1);
	}


	/*
	** Delete group option
	*/
	if (strcmp(action, "DELETE") == 0)
	{
		char	*group;

		group = getVariable("group_name");
		if (!group)
		{
			runError("Missing group name");
			exit(1);
		}
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/GM/DG>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<CENTER><H1><I>Delete Group</H1></I>\n");
		printf("<FONT SIZE=+1><P><BR>\n");
		printf("Are you sure you want to delete '%s'?\n",group);
		group = msqlEscape(group);
		printf("<INPUT TYPE=HIDDEN NAME=GROUP VALUE=\"%s\">\n", group);
		printf("<p><INPUT TYPE=SUBMIT VALUE=Yes>\n");
		printf("</FORM><p>\n");
		sendFooter();
	}


	/*
	** Edit group option
	*/
	if (strcmp(action, "EDIT") == 0)
	{
		char	*group,
			*oldGroup;

		group = getVariable("group_name");
		if (!group)
		{
			runError("Missing group name");
			exit(1);
		}
		oldGroup = (char *)strdup(group);
		group = msqlEscape(group);
		sprintf(qBuf,
		    "select descr from groups where group ='%s' and namespace='%s'", group, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			runError("Unknown group!");
			exit(1);
		}
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/GM/EG>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<CENTER><H1><I>Edit Group</H1></I>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<TABLE BORDER>\n");
		printf("<TH ALIGN=LEFT>Group name\n");
		printf("<TD><INPUT SIZE=30 NAME=GROUP VALUE=\"%s\"><TR>\n",
			oldGroup);
		printf("<TH ALIGN=LEFT>Description\n");
		printf("<TD><INPUT SIZE=30 NAME=DESCR VALUE=\"%s\"><TR>\n",
			row[0]);
		printf("<INPUT TYPE=HIDDEN NAME=OLD_GROUP VALUE=\"%s\">",
			oldGroup);

		/* Current admins */
		sprintf(qBuf,"select uname from group_admin where group='%s' and namespace='%s'",
			group,curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		printf("<TH ALIGN=LEFT>Delete Admins\n");
		printf("<TD><SELECT MULTIPLE SIZE=5 NAME=DELETE>\n");
		while(row)
		{
			printf("<OPTION>%s\n",row[0]);
			row = msqlFetchRow(res);
		}
		printf("</SELECT><TR>\n");


		/* New admins */
		sprintf(qBuf,"select uname,level from perms where level > 0 and namespace='%s'", curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res2 = msqlStoreResult();
		row2 = msqlFetchRow(res2);
		printf("<TH ALIGN=LEFT>Add Admins\n");
		printf("<TD><SELECT MULTIPLE SIZE=5 NAME=ADD>\n");
		while(row2)
		{
			msqlDataSeek(res,0);
			row = msqlFetchRow(res);
			while(row)
			{
				if (strcmp(row[0], row2[0]) == 0)
				{
					break;
				}
				row = msqlFetchRow(res);
			}
			if (!row)
			{
				if (atoi(row2[1]) & AUTH_MM)
					printf("<OPTION>%s\n",row2[0]);
			}
			row2 = msqlFetchRow(res2);
		}
		printf("</SELECT><TR>\n");


		printf("</TABLE>\n");
		printf("<p><INPUT TYPE=SUBMIT VALUE=Execute>");
		printf("</FORM><p>\n");
		sendFooter();
		exit(1);
	}
}

/**************************************************************************
***************************************************************************
**	Group Member Management Routines
**
*/

void memberManagementAction(opt)
	char	*opt[];
{
	m_result *res;
	m_row	row;


	/*
	** Add Group
	*/
	if (strcmp(opt[1],"AM") == 0)
	{
		char	*group,
			*users,
			*user;

		printf("<CENTER><H1><I>Add Group Members</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		group = getVariable("GROUP");
		users = getVariable("USERS");


		if (!group)
		{
			runError("Missing group name!");
			exit(1);
		}
		if (!users)
		{
			runError("You have not selected any users");
			exit(1);
		}
		if (strlen(group)==0)
		{
			runError("Missing group name!");
			exit(1);
		}
		if (strlen(users)==0)
		{
			runError("You have not selected any users");
			exit(1);
		}

		group = msqlEscape(group);

		user = (char *)strtok(users,"\001");
		while(user)
		{
			sprintf(qBuf,
			"insert into group_members(namespace,group,uname)values('%s','%s','%s')",
			curNS,group,user);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			user = (char *)strtok(NULL,"\001");
		}
		printf("New group members added<p><br><br><br>\n");
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");

		sendFooter();
		exit(0);
	}


	/*
	** Delete Group Members
	*/
	if (strcmp(opt[1],"DM") == 0)
	{
		char	*group,
			*users,
			*user;

		printf("<CENTER><H1><I>Delete Group Members</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		group = getVariable("GROUP");
		users = getVariable("USERS");


		if (!group)
		{
			runError("Missing group name!");
			exit(1);
		}
		if (!users)
		{
			runError("You have not selected any users");
			exit(1);
		}
		if (strlen(group)==0)
		{
			runError("Missing group name!");
			exit(1);
		}
		if (strlen(users)==0)
		{
			runError("You have not selected any users");
			exit(1);
		}

		group = msqlEscape(group);

		user = (char *)strtok(users,"\001");
		while(user)
		{
			sprintf(qBuf,
			"delete from group_members where group='%s' and uname='%s' and namespace='%s'",
			group,user,curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			user = (char *)strtok(NULL,"\001");
		}
		printf("Group members delete<p><br><br><br>\n");
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");

		sendFooter();
		exit(0);
	}


	/*
	** Edit Group 
	*/
	if (strcmp(opt[1],"EG") == 0)
	{
		char	*group,
			*oldGroup,
			*descr,
			*delete,
			*add,
			*admin;

		printf("<CENTER><H1><I>Edit Group</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		group = getVariable("GROUP");
		oldGroup = getVariable("OLD_GROUP");
		descr = getVariable("DESCR");
		delete = getVariable("DELETE");
		add = getVariable("ADD");

		if (!group || !descr)
		{
			runError("You must supply a name and description");
			exit(1);
		}
		group = msqlEscape(group);
		descr = msqlEscape(descr);
		oldGroup = msqlEscape(oldGroup);

		/* Check for existing group of that name */
		if (strcmp(oldGroup,group) != 0)
		{
			sprintf(qBuf,"select descr from groups where group='%s' and namespace='%s'",
				group, curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			res = msqlStoreResult();
			row = msqlFetchRow(res);
			if (row)
			{
				runError("Group name already exists!");
				exit(1);
			}
		}

		/* Blow away the old info */
		sprintf(qBuf,
		    "delete from groups where group='%s' and namespace='%s'",
			oldGroup, curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}


		/* Insert the new stuff */
		sprintf(qBuf,"insert into groups (group,descr,namespace) values ('%s','%s','%s')",group,descr,curNS);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		/* Update the admin info */
		if (strcmp(group, oldGroup) != 0)
		{
			sprintf(qBuf,"update group_admin set group='%s' where group='%s' and namespace='%s'", group, oldGroup, curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
		}
		admin = (char *)strtok(delete,"\001");
		while(admin)
		{
			sprintf(qBuf,"delete from group_admin where uname='%s' and group = '%s' and namespace='%s'",
				admin, group, curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			admin = (char *)strtok(NULL,"\001");
		}
		admin = (char *)strtok(add,"\001");
		while(admin)
		{
			sprintf(qBuf,
			"insert into group_admin(group,uname,namespace)values('%s','%s','%s')",
				group, admin, curNS);
			if (msqlQuery(sock,qBuf) < 0)
			{
				runError(msqlErrMsg);
				exit(1);
			}
			admin = (char *)strtok(NULL,"\001");
		}
		
		printf("Group Edited<p><br><br>\n");
                printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
                printf("</A>\n");
		sendFooter();
		exit(0);
	}
}



void memberManagement(opt)
	char	*opt[];
{
	m_result *res,
		*res2;
	m_row	row,
		row2;
	char	*action;


	/*
	** Check authority etc
	*/
	if (!curNS)
	{
		sendOkHeader();
		printf("<P><BR><H2>Missing Namespace.  Exiting<P><BR>\n");
		sendFooter();
		exit(1);
	}
	checkAuth(AUTH_MM);
	sendOkHeader();

	/*
	** Is this an action from a previous MM screen?
	*/
	if (opt[1] != NULL)
	{
		memberManagementAction(opt);
	}


	/*
	** OK, it's a config screen.  Work out which one and do the
	** right thing.
	*/
	action = getVariable("ACTION");


	/* Member Management Menu */
	if (action == NULL)
	{
		printf("<CENTER><H1><I>Group Member Management</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/MM>");
		printf("<SELECT SIZE=8 NAME=group_name>\n");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);

		sprintf(qBuf,"select group from group_admin where uname='%s' and namespace='%s' order by group", username, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		while(row)
		{
			printf("<OPTION>%s\n",row[0]);
			row = msqlFetchRow(res);
		}
		printf("</SELECT>\n<p>");
		printf("<TABLE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/add.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/delete.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/view.gif>\n");
		printf("<TR>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=ADD>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=DELETE>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=VIEW>\n");
		printf("<TR></TABLE><P>\n");
		printf("<INPUT TYPE=SUBMIT VALUE=Execute></FORM>\n");
		printf("<BR>");
		printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
		printf("</A>\n");
		printf("</CENTER><p>\n");
		sendFooter();
		exit(1);
	}

	/*
	** View members option
	*/
	if (strcmp(action, "VIEW") == 0)
	{
		char	*group,
			*origGroup;

		printf("<CENTER><H1><I>View Group Members</H1></I>\n");
		printf("</CENTER><P><BR><BR><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		group = getVariable("group_name");
		if (!group)
		{
			runError("Missing group name");
			exit(1);
		}
		origGroup = (char *)strdup(group);
		group = msqlEscape(group);
		sprintf(qBuf, "select users.uname, users.full_name from users, group_members where users.uname = group_members.uname and group_members.group = '%s' and group_members.namespace='%s'",group, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			runError("Unknown group or no members!");
			exit(1);
		}
		printf("Members of group '%s'<p>\n",origGroup);
		printf("<TABLE BORDER>\n");
		printf("<TH>Username");
		printf("<TH><PRE>   </PRE>\n");
		printf("<TH>Full Name<TR>\n");
		while(row)
		{
			printf("<TD>%s<TD><TD>%s<TR>\n",row[0],row[1]);
			row = msqlFetchRow(res);
		}
		printf("</TABLE>");
		sendFooter();
		exit(1);
	}


	/*
	** Add members option
	*/
	if (strcmp(action, "ADD") == 0)
	{
		char	*group;

		group = getVariable("group_name");
		if (!group)
		{
			runError("Missing group name");
			exit(1);
		}

		/* Get all the known users in this namespace */
		sprintf(qBuf, "select uname from users where namespace='%s' order by uname", curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();

		/* Get all the current group members */
		sprintf(qBuf, 
		"select uname from group_members where group='%s' and namespace='%s'", group, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res2 = msqlStoreResult();


		row = msqlFetchRow(res);
		
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/MM/AM>");
		printf("<CENTER><H1><I>Add Group Members</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<INPUT TYPE=HIDDEN NAME=GROUP VALUE=\"%s\">",
			group);
		printf("<SELECT MULTIPLE SIZE=8 NAME=USERS>\n");

		while(row)
		{
			msqlDataSeek(res2,0);
			row2 = msqlFetchRow(res2);
			while(row2)
			{
				if (strcmp(row[0],row2[0]) == 0)
					break;
				row2 = msqlFetchRow(res2);
			}
			if (!row2)
				printf("<OPTION>%s\n",row[0]);
			row = msqlFetchRow(res);
		}

		printf("</SELECT>");
		printf("<p><INPUT TYPE=SUBMIT VALUE=Execute>");
		sendFooter();
		exit(1);
	}


	/*
	** Delete group option
	*/
	if (strcmp(action, "DELETE") == 0)
	{
		char	*group;

		group = getVariable("group_name");
		if (!group)
		{
			runError("Missing group name");
			exit(1);
		}
		/* Get all the current group members */
		sprintf(qBuf, 
		"select uname from group_members where group='%s' and namespace='%s'", group, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();

		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/MM/DM>");
		printf("<CENTER><H1><I>Delete Group Members</H1></I>\n");
		printf("<FONT SIZE=+1><P><BR>\n");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<INPUT TYPE=HIDDEN NAME=GROUP VALUE=\"%s\">",
			group);
		printf("<SELECT MULTIPLE SIZE=8 NAME=USERS>\n");

		row = msqlFetchRow(res);
		while(row)
		{
			printf("<OPTION>%s\n",row[0]);
			row = msqlFetchRow(res);
		}
		printf("</SELECT>");
		printf("<p><INPUT TYPE=SUBMIT VALUE=Execute>");
		printf("</FORM><p>\n");
		sendFooter();
	}


	/*
	** Edit group option
	*/
	if (strcmp(action, "EDIT") == 0)
	{
		char	*group,
			*oldGroup;

		group = getVariable("group_name");
		if (!group)
		{
			runError("Missing group name");
			exit(1);
		}
		oldGroup = (char *)strdup(group);
		group = msqlEscape(group);
		sprintf(qBuf,
		    "select descr from groups where group ='%s' and namespace='%s'", group, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			runError("Unknown group!");
			exit(1);
		}
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/GM/EG>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<CENTER><H1><I>Edit Group</H1></I>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<TABLE BORDER>\n");
		printf("<TH ALIGN=LEFT>Group name\n");
		printf("<TD><INPUT SIZE=30 NAME=GROUP VALUE=\"%s\"><TR>\n",
			oldGroup);
		printf("<TH ALIGN=LEFT>Description\n");
		printf("<TD><INPUT SIZE=30 NAME=DESCR VALUE=\"%s\"><TR>\n",
			row[0]);
		printf("<INPUT TYPE=HIDDEN NAME=OLD_GROUP VALUE=\"%s\">",
			oldGroup);

		/* Current admins */
		sprintf(qBuf,"select uname from group_admin where group='%s' and namespace='%s'",
			group, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		printf("<TH ALIGN=LEFT>Delete Admins\n");
		printf("<TD><SELECT MULTIPLE SIZE=5 NAME=DELETE>\n");
		while(row)
		{
			printf("<OPTION>%s\n",row[0]);
			row = msqlFetchRow(res);
		}
		printf("</SELECT><TR>\n");


		/* New admins */
		sprintf(qBuf,"select uname from users where level > 0");
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res2 = msqlStoreResult();
		row2 = msqlFetchRow(res2);
		printf("<TH ALIGN=LEFT>Add Admins\n");
		printf("<TD><SELECT MULTIPLE SIZE=5 NAME=ADD>\n");
		while(row2)
		{
			msqlDataSeek(res,0);
			row = msqlFetchRow(res);
			while(row)
			{
				if (strcmp(row[0], row2[0]) == 0)
				{
					break;
				}
				row = msqlFetchRow(res);
			}
			if (!row)
				printf("<OPTION>%s\n",row2[0]);
			row2 = msqlFetchRow(res2);
		}
		printf("</SELECT><TR>\n");


		printf("</TABLE>\n");
		printf("<p><INPUT TYPE=SUBMIT VALUE=Execute>");
		printf("</FORM><p>\n");
		sendFooter();
		exit(1);
	}
}


/**************************************************************************
***************************************************************************
**	Privilege Management Routines
**
*/

void privilegeManagementAction(opt)
	char	*opt[];
{
	m_result *res;
	m_row	row;


	/*
	** Edit Priv 
	*/
	if (strcmp(opt[1],"EP") == 0)
	{
		char	*uname,
                        *space;
                int     level;

		printf("<CENTER><H1><I>Edit Privilege</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		uname = getVariable("UNAME");
		space = getVariable("SPACE");

		if (!uname || !space)
		{
			runError("Missing data!");
			exit(1);
		}

		sprintf(qBuf,
		  "select level from perms where namespace='%s' and uname='%s'",
		  space, uname);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		level = atoi(row[0]);

		printf("<P><BR>Edit privileges for user '%s' in '%s'<P><BR>\n",
			uname, space);
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/PM/UP>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<INPUT TYPE=HIDDEN NAME=UNAME VALUE=\"%s\">\n",uname);
		printf("<INPUT TYPE=HIDDEN NAME=SPACE VALUE=\"%s\">\n",space);
		printf("<TABLE BORDER>\n");
		printf("<TR><TD>User Management</TD>\n");
		printf("<TD><INPUT TYPE=CHECKBOX NAME=USER_P %s></TD></TR>\n",
			(level & AUTH_UM)?"CHECKED":"");
		printf("<TR><TD>Area Management</TD>\n");
		printf("<TD><INPUT TYPE=CHECKBOX NAME=AREA_P %s></TD></TR>\n",
			(level & AUTH_AM)?"CHECKED":"");
		printf("<TR><TD>Group Management</TD>\n");
		printf("<TD><INPUT TYPE=CHECKBOX NAME=GROUP_P %s></TD></TR>\n",
			(level & AUTH_GM)?"CHECKED":"");
		printf("<TR><TD>Group Member Management</TD>\n");
		printf("<TD><INPUT TYPE=CHECKBOX NAME=MEM_P %s></TD></TR>\n",
			(level & AUTH_MM)?"CHECKED":"");
		printf("<TR><TD>Privilege Management</TD>\n");
		printf("<TD><INPUT TYPE=CHECKBOX NAME=PRIV_P %s></TD></TR>\n",
			(level & AUTH_PM)?"CHECKED":"");
		printf("<TR><TD>Namespace Management</TD>\n");
		printf("<TD><INPUT TYPE=CHECKBOX NAME=NAME_P %s></TD></TR>\n",
			(level & AUTH_NM)?"CHECKED":"");
		printf("</TABLE>\n");
		printf("<P><INPUT TYPE=SUBMIT VALUE=Submit></FORM>\n");
		sendFooter();
		exit(0);
	}


	/*
	** Update Priv 
	*/
	if (strcmp(opt[1],"UP") == 0)
	{
		char	*uname,
                        *space,
			*up,
			*ap,
			*gp,
			*gmp,
			*pp,
			*np;
                int     level;

		printf("<CENTER><H1><I>Edit Privilege</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");

		uname = getVariable("UNAME");
		space = getVariable("SPACE");
		up = getVariable("USER_P");
		ap = getVariable("AREA_P");
		gp = getVariable("GROUP_P");
		gmp = getVariable("MEM_P");
		pp = getVariable("PRIV_P");
		np = getVariable("NAME_P");

		if (!uname || !space )
		{
			runError("Missing data!");
			exit(1);
		}

		level = 0;
		if (up)
			level |= AUTH_UM;
		if (ap)
			level |= AUTH_AM;
		if (gp)
			level |= AUTH_GM;
		if (gmp)
			level |= AUTH_MM;
		if (pp)
			level |= AUTH_PM;
		if (np)
			level |= AUTH_NM;

		sprintf(qBuf,
		"update perms set level=%d where namespace='%s' and uname='%s'",
		  level, space, uname);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}

		printf("<H2>User updated</H2><P><BR>\n");
		printf("<A HREF=/cgi-bin/w3-auth>Return to menu</A>");
		exit(0);
	}
}



void privilegeManagement(opt)
	char	*opt[];
{
	m_result *res;
	m_row	row;
	char	*action;


	/*
	** Check authority etc
	*/
	if (!curNS)
	{
		sendOkHeader();
		printf("<P><BR><H2>Missing Namespace.  Exiting<P><BR>\n");
		sendFooter();
		exit(1);
	}

	checkAuth(AUTH_PM);
	sendOkHeader();

	/*
	** Is this an action from a previous PM screen?
	*/
	if (opt[1] != NULL)
	{
		privilegeManagementAction(opt);
	}


	/*
	** OK, it's a config screen.  Work out which one and do the
	** right thing.
	*/
	action = getVariable("ACTION");


	/* Privilege Management Menu */
	if (action == NULL)
	{
		printf("<CENTER><H1><I>Privilege Management</H1></I>\n");
		printf("</CENTER><P><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		printf("<p>\n");
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/PM>");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=%s>\n",urlNS);
		printf("<SELECT SIZE=10 NAME=uname>\n");

		if(msqlQuery(sock,
		"select uname from users where namespace='SuperUser' order by uname")<0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		while(row)
		{
			printf("<OPTION>%s\n",row[0]);
			row = msqlFetchRow(res);
		}
		printf("</SELECT>\n<p>");
		printf("<TABLE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/edit.gif>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD><IMG SRC=/Hughes/graphics/view.gif>\n");
		printf("<TR>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=EDIT>\n");
		printf("<TD><PRE>   </PRE>\n");
		printf("<TD ALIGN=CENTER>\n");
		printf("<INPUT TYPE=RADIO NAME=ACTION VALUE=VIEW>\n");
		printf("<TR></TABLE><P>\n");
		printf("<INPUT TYPE=SUBMIT VALUE=Execute></FORM>\n");
		printf("<BR>");
		printf("<A HREF=/cgi-bin/w3-auth>Return to Main Menu");
		printf("</A>\n");
		printf("</CENTER><p>\n");
		sendFooter();
		exit(1);
	}

	/*
	** View privileged user option
	*/
	if (strcmp(action, "VIEW") == 0)
	{
		char	*uname;
		int	level;

		printf("<CENTER><H1><I>View Privileged User</H1></I>\n");
		printf("</CENTER><P><BR><BR><BR>\n");
		printf("<FONT SIZE=+1><CENTER>\n");
		uname = getVariable("uname");
		if (!uname)
		{
			runError("Missing username");
			exit(1);
		}
		uname = msqlEscape(uname);
		sprintf(qBuf,
			"select full_name from users where uname='%s'", uname);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			runError("Unknown user!");
			exit(1);
		}
		printf("<TABLE BORDER>\n");
		printf("<TH ALIGN=LEFT>Username<TD>%s<TR>\n",uname);
		printf("<TH ALIGN=LEFT>Full Name<TD>%s<TR>\n",row[0]);
		printf("</TABLE><P>\n");
		msqlFreeResult(res);

		sprintf(qBuf,
			"select namespace, level from perms where uname='%s'",
			uname);
		if (msqlQuery(sock,qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		
		printf("<TABLE>\n");
		printf("<TR><TH>Namespace</TH><TH>Privilege</TH>");
		printf("<TH>Access</TH></TR>\n");
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		while(row)
		{
			level = atoi(row[1]);
			printf("<TR><TD>%s</TD><TD>User</TD><TD>%s</TD></TR>\n",
				row[0], (level & AUTH_UM)?"Yes":"No");
			printf("<TR><TD></TD><TD>Area</TD><TD>%s</TD></TR>\n",
				(level & AUTH_AM)?"Yes":"No");
			printf("<TR><TD></TD><TD>Group</TD><TD>%s</TD></TR>\n",
				(level & AUTH_GM)?"Yes":"No");
			printf("<TR><TD></TD><TD>Group Member</TD>");
			printf("<TD>%s</TD></TR>\n",
				(level & AUTH_MM)?"Yes":"No");
			printf("<TR><TD></TD><TD>Privilege</TD>");
			printf("<TD>%s</TD></TR>\n",
				(level & AUTH_PM)?"Yes":"No");
			printf("<TR><TD></TD><TD>Namespace</TD>");
			printf("<TD>%s</TD></TR>\n",
				(level & AUTH_NM)?"Yes":"No");
			printf("<TR><TD COLSPAN=3><HR></TD></TR>\n");
			row = msqlFetchRow(res);
		}
		msqlFreeResult(res);
		printf("</TABLE>\n");
		sendFooter();
		exit(1);
	}


	/*
	** Edit privileged user option
	*/
	if (strcmp(action, "EDIT") == 0)
	{
		char	*uname;

		uname = getVariable("uname");
		if (!uname)
		{
			runError("Missing username");
			exit(1);
		}
		sprintf(qBuf,"select full_name from users where uname='%s' and namespace='%s'",
			uname, curNS);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			runError("Unknown user!");
			exit(1);
		}
		msqlFreeResult(res);

		/*
		** List the namespaces that this person is AUTH for
		*/
		sprintf(qBuf,"select namespace from perms where uname='%s'",
			uname);
		if (msqlQuery(sock, qBuf) < 0)
		{
			runError(msqlErrMsg);
			exit(1);
		}
		res = msqlStoreResult();
		row = msqlFetchRow(res);
		if (!row)
		{
			printf("<H3><CENTER><P><BR>\n");
			printf("This user is not an admin for any namespace!");
			printf("</H3>\n");
			sendFooter();
			exit(0);
		}
		printf("<FORM METHOD=POST ACTION=/cgi-bin/w3-auth/PM/EP>");
		printf("<CENTER><H1><I>Edit Privileges</H1></I>\n");
		printf("<P><H3>Select a namespace</H3>\n");
		printf("<FONT SIZE=+1><P><BR>\n");
		printf("<INPUT TYPE=HIDDEN NAME=NS VALUE=\"%s\">",
			urlNS);
		printf("<INPUT TYPE=HIDDEN NAME=UNAME VALUE=\"%s\">",
			uname);
		printf("<SELECT SIZE=8 NAME=SPACE>\n");

		while(row)
		{
			printf("<OPTION VALUE=\"%s\">%s\n",row[0],row[0]);
			row = msqlFetchRow(res);
		}
		msqlFreeResult(res);
		printf("</SELECT>\n");
		printf("<p><INPUT TYPE=SUBMIT VALUE=Select>");
		printf("</FORM><p>\n");
		sendFooter();
		exit(1);
	}
}


void main(argc,argv)
	int	argc;
	char	*argv[];
{
	int	index;
	char	*auth,
		*options,
		*opt[5],
		*tok;

	chdir("/tmp");

	/*
	** Make sure we have an authenticated user
	*/
	auth = (char *)getenv("HTTP_AUTHORIZATION");
	if (auth)
	{
		if (*auth == 0)
			auth = NULL;
	}
	if (!auth)
	{
		sendAuthHeader("No Auth Info");
		exit(0);
	}

	/*
	** Work out what we are here for
	*/
	parseArgs();
	tok = (char *)getVariable("NS");
	if (tok)
		curNS = (char *)strdup(tok);
	else
		curNS = (char *)strdup("SuperUser");
	urlNS = (char *)strdup(HTEscape(curNS));
	options = (char *)getenv("PATH_INFO");
	if (options)
	{
		if (*options == 0 || (*options == '/' && *(options+1)==0))
			options = NULL;
	}
	if (!options)
	{
		sendIntro();
		exit(0);
	}
	tok = (char *)strtok(options,"/");
	index = 0;
	opt[0] = opt[1] = opt[2] = opt[3] = opt[4] = NULL;
	while(tok)
	{
		opt[index] = tok;
		tok = (char *)strtok(NULL,"/");
		index++;
		if (index > 5)
		{
			sendOkHeader();
			runError("Bad option specification");
			sendFooter();
			exit(1);
		}
	}

	/*
	** Split off to the specified option
	*/
	if (!opt[0])
	{
		sendIntro();
		exit(0);
	}
	if (strcmp(opt[0],"MENU") == 0)
	{
		if (strcmp(curNS,"SuperUser") == 0)
			sendSuperUserMenu();
		else
			sendMainMenu();
		exit(0);
	}
	if (strcmp(opt[0],"LO") == 0)
	{
		logOut(opt);
		exit(0);
	}
	if (strcmp(opt[0],"NM") == 0)
	{
		namespaceManagement(opt);
		exit(0);
	}
	if (strcmp(opt[0],"UM") == 0)
	{
		userManagement(opt);
		exit(0);
	}
	if (strcmp(opt[0],"AM") == 0)
	{
		areaManagement(opt);
		exit(0);
	}
	if (strcmp(opt[0],"GM") == 0)
	{
		groupManagement(opt);
		exit(0);
	}
	if (strcmp(opt[0],"MM") == 0)
	{
		memberManagement(opt);
		exit(0);
	}
	if (strcmp(opt[0],"PM") == 0)
	{
		privilegeManagement(opt);
		exit(0);
	}
	sendOkHeader();
	printf("<PRE>\nDATA = '%s'\n</PRE>",options);
	sendMainMenu();
}

