/* cso.c
   processing for CSO name server support */

     /*---------------------------------------------------------------*/
     /* Xgopher        version 1.1     20 April 1991                  */
     /*                version 1.0     04 March 1991                  */
     /* X window system client for the University of Minnesota        */
     /*                                Internet Gopher System.        */
     /* Allan Tuchman, University of Illinois at Urbana-Champaign     */
     /*                Computing Services Office                      */
     /* Copyright 1992 by                                             */
     /*           the Board of Trustees of the University of Illinois */
     /* Permission is granted to freely copy and redistribute this    */
     /* software with the copyright notice intact.                    */
     /*---------------------------------------------------------------*/


/* Some of the "look and feel" of this panel has been influenced
   by xph, an X window system interface for ph.  xph was developed by
   Bradley C. Spatz, University of Florida, bcs@ufl.edu.
   xph goes beyond the capabilities presented here by providing the
   editing functions for changing attributes.  As gopher is an
   information server, it is prudent to keep the name server
   interface as simple as possible. */

#include <stdio.h>

#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>

#include <X11/Shell.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/AsciiText.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/SmeBSB.h>

#include "cso.h"
#include "help.h"
#include "gui.h"

#define CSO_SHELL_TITLE	"CSO Name Server"

void	textAppend();


static	Widget		topLevel;
static	Widget		csoShell,
			queryText, csoText,
			nameServerLabel;
static	Boolean		csoPanelCreated = False;
static	Boolean		panelActive = False;
static	int		nsFile = -1;
static	int		endOfText = 0;



/* ph interface routines */

#define	DELIM		':'
#define PH_TOKEN_LEN	256
#define DASHES		" -------------------------------------------------\n"
#define PH_BUFFSIZE	256
static	char	phCommand[PH_BUFFSIZE];
static	char	phResponse[PH_BUFFSIZE];

/* these macros are used for every read from the ph name server.  The
   result buffer is global, so it is hard coded. */

#define RD_RESP(s) \
		{int n; \
		if ((n = readDelim(s, phResponse, PH_BUFFSIZE-1, DELIM)) <= 0)\
				{ ph_readError(n); return False; } }
		

#define RD_LINE(s) \
		{int n; \
		if ((n = readLine(s, phResponse, PH_BUFFSIZE-1)) <= 0)\
				{ ph_readError(n); return False; } }

static	char	mailDomain[PH_TOKEN_LEN];


/* ph_readError
   error reading response from ph */

static void
ph_readError(n)
int	n;
{
	if (n == 0)
		showError("End of file from Name Server!");
	else
		showError("Read error from Name Server!");
	
	return;
}


/* ph_id
   send id command to ph */

static Boolean
ph_id(s) 
int	s;
{
	int	n, rc;

	sprintf(phCommand, "id %d\n", getuid());
	write(s, phCommand, strlen(phCommand));

	do {
		RD_RESP(s);
		rc = atoi(phResponse);
		RD_LINE(s);
	} while (rc < 200);

	return True;
}


/* ph_quit
   send quit command to ph */

static Boolean
ph_quit(s) 
int	s;
{
	int	n, rc;

	sprintf(phCommand, "quit\n");
	write(s, phCommand, strlen(phCommand));

	do {
		RD_RESP(s);
		rc = atoi(phResponse);
		RD_LINE(s);
	} while (rc < 200);

	return True;
}


/* ph_siteinfo
   send siteinfo command to ph */

static Boolean
ph_siteinfo(s, mail) 
int	s;
char	*mail;
{
	int	n, rc;

	*mail= '\0';
	sprintf(phCommand, "siteinfo\n");
	write(s, phCommand, strlen(phCommand));

	do {
		RD_RESP(s);
		if ((rc = atoi(phResponse)) >= 200) {
			RD_LINE(s);
			break;
		}

		/* read entry index */

		RD_RESP(s);

		/* read field name */

		RD_RESP(s);
		if (strcmp(phResponse, "maildomain") == 0) {
			RD_RESP(s);
			strncpy(mail, phResponse, PH_TOKEN_LEN);
		} else {
			RD_LINE(s);
		}
	} while (rc < 200);

	return True;
}


/* ph_query
   send query command to ph */

static Boolean
ph_query(s, string) 
int	s;
char	*string;
{
	int	rc, n, entry, lastEntry=1;
	Boolean	lastLine;
	
	sprintf (phCommand, "ph %s\n", string);
	write(s, phCommand, strlen(phCommand));

	lastLine = False;
	do {
		RD_RESP(s);
		if ((rc = atoi(phResponse)) >= 200) {
			lastLine = True;
			RD_LINE(s);
			textAppend (phResponse);
			break;
		}
		rc = abs(rc);
		if (rc/100 != 2) {
			RD_LINE(s);
				textAppend (phResponse);
			continue;
		}

		/* read entry index */

		RD_RESP(s);
		entry = atoi(phResponse);
		if (entry != lastEntry) {
			textAppend (DASHES);
			lastEntry = entry;
		}

		/* report remainder of line */

		RD_LINE(s);
			textAppend (phResponse);
	} while (! lastLine);

	return True;

}


/* matchToken
   see if a token is contained in a string of tokens.  Tokens are
   delimited by white space (spaces, tabs, newline). */

static Boolean
matchToken(s, token)
char	*s, *token;
{
	char	*p = s;
	int	tokenLen = strlen(token);
	char	*last = s + (strlen(s) - tokenLen);

	while (p < last) {
		while ((*p == ' '  ||  *p == '\t'  ||  *p == '\n')  &&
								p < last) p++;
		if (strncmp(p, token, tokenLen) == 0) {
			p += tokenLen;
			if (*p == ' '  ||  *p == '\t'  ||  *p == '\n')
								return True;
		}
		while ((*p != ' '  &&  *p != '\t'  &&  *p != '\n')  &&
								p < last) p++;
	}
	return False;
}


/* ph_fields
   send fields command to ph */

static Boolean
ph_fields(s, string) 
int	s;
char	*string;
{
	int	rc, n, entry, lastEntry=-1;
	Boolean	lastLine, descriptionLine, use;
	char	fieldName[PH_TOKEN_LEN];
	
	sprintf (phCommand, "fields\n");
	write(s, phCommand, strlen(phCommand));

	sprintf (phResponse, " ----- %s fields -----\n", string);
	textAppend (phResponse);

	lastLine = False;
	do {
		RD_RESP(s);
		if ((rc = atoi(phResponse)) >= 200) {
			lastLine = True;
			RD_LINE(s);
			textAppend (phResponse);
			break;
		}
		rc = abs(rc);
		if (rc/100 != 2) {
			RD_LINE(s);
				textAppend (phResponse);
			continue;
		}

		/* read entry index */

		RD_RESP(s);
		entry = atoi(phResponse);
		if (entry != lastEntry) {
			lastEntry = entry;
			descriptionLine = False;
			use = False;
		} else
			descriptionLine = True;

		/* read field name */

		RD_RESP(s);
		strcpy(fieldName, phResponse);


		if (descriptionLine) {
			RD_LINE(s);
			if (use) {
				sprintf (phCommand, "%s:\t %s",
							fieldName, phResponse);
				textAppend (phCommand);
			} else ;

		} else {
			/* scan remainder of line for desired value */

			RD_LINE(s);

			/* loop through tokens */

			if (matchToken(phResponse, string)) use = True;
		}
	} while (! lastLine);

	return True;
}


/* X interface routines */


/* shutdownNameServer
   remove the panel from the display and reset the file indicators */

shutdownNameServer()
{
	Cardinal	n;
	Arg		args[3];

	XtPopdown(csoShell);

	close(nsFile);
	nsFile = -1;
	panelActive = False;

	n=0;
	XtSetArg(args[n], XtNlabel, "");  n++;
	XtSetValues(nameServerLabel, args, n);
}


/* doneProc
   finish a cso name server session */

static void
doneProc(w, clientData, callData)
Widget		w;
XtPointer	clientData, callData;
{
	ph_quit(nsFile);

	shutdownNameServer();
}


/* fieldsProc
   list fields with some specification */

static void
fieldsProc(w, clientData, callData)
Widget		w;
XtPointer	clientData, callData;
{
	Cardinal	n;
	Arg		args[3];
	char		*fieldName = (char *) clientData;

	/* Lookup,  Indexed, Public, Default */ 

	if (! ph_fields(nsFile, fieldName) ) shutdownNameServer();
}


/* helpProc
   provide help for a cso name server session */

static void
helpProc(w, clientData, callData)
Widget		w;
XtPointer	clientData, callData;
{
        showHelp("cso help");
}


/* doQueryProc
   submit a query to a cso name server */

static void
doQueryProc(w, clientData, callData)
Widget		w;
XtPointer	clientData, callData;
{
	Cardinal	n;
	Arg		args[3];
	char		*string;

	n=0;
	XtSetArg(args[n], XtNstring, &string);  n++;
	XtGetValues(queryText, args, n);

	if (strlen(string) == 0) {
		showError("First enter a name, then do the query.");
	} else {
		if (! ph_query(nsFile, string) ) shutdownNameServer();
	}

}


/* QueryOk
   accept the "do query" action from a keyboard <cr> instead of the
   "do query" button.  The <cr> translation is defined elsewhere.
   Capitalized name is for X action proc convention. */

static void
QueryOk(w, event, parms, nparms)
Widget          w;
XEvent          *event;
String          *parms;
Cardinal        *nparms;
{
        doQueryProc(w, NULL, NULL);
        return;
}


/* clearQueryProc
   clear the name query text */

static void
clearQueryProc(w, clientData, callData)
Widget		w;
XtPointer	clientData, callData;
{
	Cardinal	n;
	Arg		args[3];

	n=0;
	XtSetArg(args[n], XtNstring, "");  n++;
	XtSetValues(queryText, args, n);
}


/* clearTextProc
   clear the text result window */

static void
clearTextProc(w, clientData, callData)
Widget		w;
XtPointer	clientData, callData;
{
	Cardinal	n;
	Arg		args[3];

	n=0;
	XtSetArg(args[n], XtNeditType, XawtextEdit);  n++;
	XtSetValues(csoText, args, n);

	n=0;
	XtSetArg(args[n], XtNstring, "");  n++;
	XtSetValues(csoText, args, n);

	n=0;
	XtSetArg(args[n], XtNeditType, XawtextRead);  n++;
	XtSetValues(csoText, args, n);

	endOfText = 0;
}


/* textAppend
   append a string to the text result window */

static void
textAppend(string)
char	*string;
{
	Cardinal	n;
	Arg		args[3];
	XawTextBlock	textBlock;
	int		length;

	textBlock.firstPos = 0;
	textBlock.length   = strlen(string);
	textBlock.ptr      = string;
	textBlock.format   = FMT8BIT;

	n=0;
	XtSetArg(args[n], XtNeditType, XawtextAppend);  n++;
	XtSetValues(csoText, args, n);

	XawTextReplace(csoText, endOfText, endOfText, &textBlock);  n++;
	endOfText += textBlock.length;

	n=0;
	XtSetArg(args[n], XtNeditType, XawtextRead);  n++;
	XtSetValues(csoText, args, n);
}


/* displayCsoPanel
   display the panel for cso name server queries */

Boolean
displayCsoPanel(title, s)
char		*title;
int		s;
{
	Arg		args[10];
	Cardinal	n;
	Dimension	wt, wc;
	Position	relX, xc, yc;
	static char	 geom[16];


	n=0;
	XtSetArg(args[n], XtNlabel, title);  n++;
	XtSetValues(nameServerLabel, args, n);

	if (panelActive) {
		clearQueryProc(NULL, NULL, NULL);
		close(nsFile);

	} else {

		panelActive = True;

		/* position at the top of the main panel, with its left
		   corner 2/3 across. */

		positionPopup (csoShell, POS_appPanel, topLevel, 66, 0, 0, 0);

		XtPopup (csoShell, XtGrabNone);
	}

	nsFile = s;
	if (! ph_id(nsFile) ) {
		shutdownNameServer();
		return False;
	}

	if (! ph_siteinfo(nsFile, mailDomain) ) {
		shutdownNameServer();
		return False;
	}

	return True;
}


#define N_FIELDS 4
static char *fieldsLabel[] = {"Default", "Lookup", "Indexed", "Public"};

/* makeCsoPanel
   create the X panel for CSO name server queries */

void
makeCsoPanel(top)
Widget	top;
{
	Arg		args[10];
	Cardinal	n;
	int		i;
	Widget		csoForm;
	Widget		doneButton, helpButton,
			doQueryButton, clearQueryButton, clearTextButton,
			fieldsMenuHolder, fieldsButton,
			fieldsMenuItem[N_FIELDS],
			queryLabel;
	static XtActionsRec     csoActionsTable[] = {
					{ "queryok", (XtActionProc) QueryOk }
					};


	if (csoPanelCreated) return;

	topLevel = top;


	/* create CSO NAME SERVER shell */

		n=0;
		XtSetArg(args[n], XtNtitle, CSO_SHELL_TITLE);  n++;
	csoShell = XtCreatePopupShell("csoShell",
				topLevelShellWidgetClass,
				topLevel, args, n);


	/* create CSO NAME SERVER main panel form */

		n=0;
	csoForm  = XtCreateManagedWidget("csoForm",
				formWidgetClass,
				csoShell, args, n);
	

	/* create NAME SERVER label */

		n=0;
	nameServerLabel = XtCreateManagedWidget("nameServer",
				labelWidgetClass,
				csoForm, args, n);


	/* create DONE button */

		n=0;
	doneButton = XtCreateManagedWidget("csoDone", commandWidgetClass,
				csoForm, args, n);
		XtAddCallback(doneButton, XtNcallback, doneProc, NULL);
		XtAddCallback(doneButton, XtNcallback, clearQueryProc, NULL);
		XtAddCallback(doneButton, XtNcallback, clearTextProc, NULL);


	/* create HELP button */

		n=0;
	helpButton = XtCreateManagedWidget("csoHelp", commandWidgetClass,
				csoForm, args, n);
		XtAddCallback(helpButton, XtNcallback, helpProc, NULL);


	/* create FIELDS MENU holder */

		n=0;
	fieldsMenuHolder = XtCreateManagedWidget("csoFieldsMenu",
				simpleMenuWidgetClass,
				csoForm, args, n);


	/* create FIELDS MENU ENTRIES */

	for (i=0; i<N_FIELDS; i++) {
			n=0;
			XtSetArg(args[n], XtNlabel, fieldsLabel[i]);  n++;
		fieldsMenuItem[i] = XtCreateManagedWidget(fieldsLabel[i],
					smeBSBObjectClass,
					fieldsMenuHolder, args, n);
			XtAddCallback(fieldsMenuItem[i], XtNcallback,
					fieldsProc, fieldsLabel[i]);
	}


	/* create FIELDS button */

		n=0;
		XtSetArg(args[n], XtNmenuName, "csoFieldsMenu");  n++;
	fieldsButton = XtCreateManagedWidget("csoFields",
				menuButtonWidgetClass,
				csoForm, args, n);


	/* create NAME label */

		n=0;
	queryLabel = XtCreateManagedWidget("csoQueryLabel",
				labelWidgetClass,
				csoForm, args, n);


	/* create NAME TEXT entry */

		n=0;
		XtSetArg(args[n], XtNeditType, XawtextEdit);  n++;
		XtSetArg(args[n], XtNstring, "");  n++;
	queryText = XtCreateManagedWidget("csoQueryText",
				asciiTextWidgetClass,
				csoForm, args, n);
		setTextWidgetSize(queryText, 60, 1);
	

	/* create DO QUERY button */

		n=0;
	doQueryButton = XtCreateManagedWidget("csoDoQuery", commandWidgetClass,
				csoForm, args, n);
		XtAddCallback(doQueryButton, XtNcallback,
				doQueryProc, NULL);


	/* create CLEAR QUERY button */

		n=0;
	clearQueryButton = XtCreateManagedWidget("csoClearQuery",
				commandWidgetClass,
				csoForm, args, n);
		XtAddCallback(clearQueryButton, XtNcallback,
				clearQueryProc, NULL);


	/* create CLEAR TEXT button */

		n=0;
	clearTextButton = XtCreateManagedWidget("csoClearText",
				commandWidgetClass,
				csoForm, args, n);
		XtAddCallback(clearTextButton, XtNcallback,
				clearTextProc, NULL);


	/* create TEXT display */

		n=0;
		XtSetArg(args[n], XtNeditType, XawtextRead);  n++;
		XtSetArg(args[n], XtNstring, "");  n++;
	csoText = XtCreateManagedWidget("csoText", asciiTextWidgetClass,
				csoForm, args, n);
		setTextWidgetSize(csoText, 60, 10);

	XtAddActions(csoActionsTable, XtNumber(csoActionsTable));


	csoPanelCreated = True;
}
