/*	    Configuration Manager for RPC			        cm_env.c
**	    =============================
**
**  This module provides address translation facilities for an RPC system,
**  using the process environment (on VMS: logical name tables) as the
**  database.
**
**  Authors:
**
**	Tim Berners-Lee CERN/DD (TBL)
**	Roberto Bagnara CERN, Univ. Pisa (RB)
**	Abramo Bagnara (AB)
**
**  History:
**
**	27 Sep 89   Adapted from part of ts.c (TBL)
**	16 Nov 89   Cleaned up code by RB and AB incorporated
**	23 Nov 89   Standard header files and declarations added
*/

#ifdef OSK
#include <strings.h>		/* For strcpy(), strncpy(), strlen()...	*/
#else
#include <string.h>		/* For strcpy(), strncpy(), strlen()...	*/
#endif
#include <stdio.h>		/* For file handling for trace file	*/

#include "syspec.h"	/* Local variations			*/
#include "rpcrts.h"		/* Public RPC macros			*/
#include "rpc_code.h"		/* Macros for internal use by RPC code	*/


/*  External routines:
*/
#ifdef __STDC__
extern char *getenv(const char * name);	/* Get shell environment variable   */
extern char *malloc(int size);		/* Allocate memory		    */
extern void *free(void *block);		/* Deallocate memory		    */
#else
extern char *getenv();			/* Get shell environment variable   */
extern char *malloc();			/* Allocate memory		    */
extern void free();			/* Deallocate memory		    */
#endif

/*	    Translate a logical name				  cm_translate()
**	    ------------------------
**
**  Given the logical name, this function translates it according to the
**  local process environment, or equivalent.
**
**  On entry,
**
**	initial	    The address of the logical name (zero terminated).
**
**	buffer	    The address of a buffer for the result.
**
**	buffer_size The number of bytes available at *buffer.
**
**  On exit,
**
**	returns	    A status indicating success (GOOD) or failure (BAD).
**		    Inability to translate it counts as BAD.
**		    Buffer overflow counts as bad, in that there is nothing
**		    one can do with a truncated address.
**	*buffer	    If status is GOOD, the physical address (zero terminated).
**		    If status is BAD, undefined.
*/

#ifdef __STDC__
PUBLIC rpc_status cm_translate(
	char	    *initial,	    /* IN  Initial string to be translated */
        char	    *buffer,	    /* OUT Buffer for returned string */
        int	     buffer_size)   /* IN  Maximum length of returned string */
#else
PUBLIC rpc_status cm_translate(initial, buffer, buffer_size)
	char	    *initial;
        char	    *buffer;
        int	     buffer_size;
#endif

{
    if (getenv(initial)) {
	if (strlen(getenv(initial)) > buffer_size) {
	    CTRACE(tfp, "RPC/CM: Buffer overflow translating `%s' to `%s'\n",
		initial,getenv(initial));
	    return RPC_S_BUFFER_OVERFLOW;
	}
	CTRACE(tfp, "RPC/CM: Translated  `%s' to `%s'\n",
		initial, getenv(initial));
	strncpy(buffer, getenv(initial), buffer_size);
	return RPC_S_NORMAL;
    } else {  
	CTRACE(tfp, "RPC/CM: No translation for `%s'.\n",initial);
	return RPC_S_NO_TRANSLATION;
    }
    /*NOTREACHED*/
}

/*	Capitalise a character.					     uppercase()
**	-----------------------
**
**	
**	char uppercase(char c)
**
**  Parameters:	c		character to be upper-cased.
**
**  Return:			upper-cased character.
**
**  History:
**
**	14 Aug 89	Cleaned up. (RB)
**
*/

#ifdef UPPERCASE_LOGICALS

#ifdef __STDC__
char uppercase(char c)
#else
char uppercase(c)
char c;
#endif
{
    return ((c >= 'a') && (c <= 'z')) ? (c - 'a' + 'A') : c;
}

/*	Capitalise a string.					       capital()
**	--------------------
**
**	
**	void capital(char *string)
**
**  Parameters:	string		pointer to string to be capitalised.
**
**  Return:	none
**
**  On entry:	string[]	null terminated string.
**
**  On exit:	string[]	same as above but capitalised.
**
**  Description:
**
**	Self explanatory.
**
**  History:
**
**	14 Aug 89	Cleaned up. (RB)
**
*/

#ifdef __STDC__
void capital(char *string)
#else
void capital(string)
char *string;
#endif
{
    register char *p;

    for (p = string; *p; p++)
	*p = uppercase(*p);
}
#endif

/*	Recursively translates logical names.			    translated()
**	-------------------------------------
**
**	
**	BOOLEAN translated(char *buffer, int buflen, int curlevel,
**				    int maxlevel, char *previous[])
**
**  Parameters:	buffer		pointer to string containing the name to
**				be translated on entry, its translation
**				on exit;
**
**		buflen		maximum length of the translation;
**
**		curlevel	current recursion level;
**
**		maxlevel	maximum recursion level;
**
**		previous	array of pointers to previous translation
**				strings.
**
**  Return:			FALSE if a translation loop was detected,
**				TRUE otherwise.
**
**  On entry:	buffer[]	null terminated string containing the name to
**				translate;
**
**		previous[]	contains in positions 0 to curlevel-1
**				pointers to the previous translation strings.
**
**  On exit:	buffer[]	null terminated translation string.
**
**  Description:
**
**	Recursively gets the translation of the string contained in 'buffer'
**	from the environment. Recursion stops when either the maximum
**	recursion level 'maxlevel' is reached, or there is no translation,
**	or a translation loop is detected (in this case FALSE is returned).
**
**  History:
**
**	14 Aug 89	Rewritten. (RB)
**
*/

#ifdef __STDC__
BOOLEAN translated(char *buffer, int buflen, int curlevel, int maxlevel, 
                   char *previous[])
#else
BOOLEAN translated(buffer, buflen, curlevel, maxlevel, previous)
    char    *buffer;
    int	    buflen;
    int	    curlevel;
    int	    maxlevel;
    char    *previous[];
#endif
{
    int status;

    if (curlevel < maxlevel) {

        extern char *getenv();
	char *translation;
        char *aux_buf;

	/* Copy the input string into our private buffer. */

	aux_buf = (char *)malloc(buflen);
	(void) strcpy(aux_buf, buffer);

#ifdef UPPERCASE_LOGICALS
	capital(aux_buf);   /* Convert to uppercase before look-up */
#endif

	/* Save pointer to name we are trying to translate. */

	previous[curlevel] = aux_buf;

	/* Try translating. */

        if ((translation = getenv(aux_buf)) != NULL) {

	    int k;

	    /* Translation worked: save and retry. */

	    (void) strncpy(buffer, translation, buflen);
            buffer[buflen] = '\0';

	    CTRACE(tfp, "RPC/CM: Translated  `%s' to `%s'\n", aux_buf, buffer);

	    /* Check for translation loop. */

	    for (k = 0; k <= curlevel; k++)
		if (strcmp(previous[k], buffer) == 0) {
		    UTRACE(tfp, "RPC/CM: Translation loop detected, fail.\n");
		    free(aux_buf);
		    return FALSE;
		}

	    status = translated(buffer, buflen, curlevel+1, maxlevel, previous);

	} else {

	    /* Translation didn't work, say it's good anyway. */

	    CTRACE(tfp, "RPC/CM: No translation for `%s'.\n", aux_buf);

	    status = TRUE;
	}
	free(aux_buf);

    } else

	/* Maximum translation level reached, return ok. */

 	status = TRUE;
 
    return status;
}

/*	Satisfy requests for configuration items.		    cm_request()
**	-----------------------------------------
**
**	
**	rpc_status cm_request(const char *name, char *expansion, int explen)
**
**  Parameters:	name		pointer to string containing the name to be
**				looked up;
**
**		expansion	pointer to string where the expansion for
**				'name' will be stored;
**
**		explen		maximum length of the expansion for 'name',
**
**  Return:			status RPC_S_NORMAL if translation succeeded,
**				RPC_S_NOTRANS if a translation loop has been
**				detected.
**
**  On entry:	name[]		Null terminated string containing the name to
**				looked up.
**
**  On exit:	expansion[]	Null terminated string containing the expansion
**				for name, possibly truncated to explen-1
**				characters.
**
**  Description:
**
**	Initialises for calling the 'translated' recursive function
**	that will do the real job.
**
**  History:
**
**	14 Aug 89	Rewritten. (RB)
**
*/

/*ARGSUSED*/
#ifdef __STDC__
rpc_status cm_request(
    const char *name,
    int	version,
    int	compatibility,
    char *expansion,
    int explen)
#else
rpc_status cm_request(name, version, compatibility, expansion, explen)
    char *name;
    int	 version;
    int	 compatibility;
    char *expansion;
    int  explen;
#endif
{
#define MAXLEVEL 3

    /* This array is used to detect translation loops. */

    char *previous[MAXLEVEL];

    /* Copy name to expansion, possibly truncated. */

    (void) strncpy(expansion, name, RPC_NAME_LENGTH);

    /* Make sure it is null terminated. */

    expansion[RPC_NAME_LENGTH] = '\0';

    /* Do the actual translation. */

    return translated(expansion, explen, 0, MAXLEVEL, previous)
	? RPC_S_NORMAL
	: RPC_S_NO_TRANSLATION;
}

/*	Register a service					   cm_register()
**	------------------
**
**	This routine records the provision of a particular version of a
**	service at a given RPC address.
**
**  On entry,
**
**	service	    points to the zero-terminated package name of the service
**
**	version	    is the actual version of the service
**
**	compatibility is the lowest previous version of client which is
**		    also supported by this version of the server.
**
**	address	    points to the zero-terminated rpc address at which the
**		    service is provided.
**
**  On exit,
**
**	returns	    a status indicating GOOD or BAD completion.
**		    If good, the service has been registered and should
**		    eventually be unregistered.
**		    If bad, the service has not been registered.
*/

#ifdef __STDC__
rpc_status cm_register(
    char *  service,
    int	    version,
    int	    compatibility,
    char *  address)
#else
rpc_status cm_register(service, version, compatibility, address)
    char *  service;
    int	    version;
    int	    compatibility;
    char *  address;
#endif

{
    CTRACE(tfp, "CM: Registering %s version %d (compat:%d) at %s",
		service, version, compatibility, address);

    return RPC_S_NOT_IMPLEMENTED;
}

/*	Remove a registration 					 cm_unregister()
**	---------------------
**
**	This routine removes the record of the provision of a service at a
**	particular given RPC address.
**
**  On entry,
**
**	service	    points to the zero-reminated package name of the service
**
**	version	    is the actual version of the service
**
**	address	    points to the zero-terminated rpc address
**
**  On exit,
**
**	returns	    a status indicating GOOD or BAD completion.
**		    If good, the registration has been removed.
**		    If bad, it has not -- because the registrationwas not
**		    found or for another reason.
*/
#ifdef __STDC__
rpc_status cm_unregister(
    char *  service,
    int	    version,
    char *  address)
#else
rpc_status cm_unregister(service, version, address)
    char *  service;
    int	    version;
    char *  address;
#endif

{
    CTRACE(tfp, "CM: Unregistering of %s version %d at %s",
		service, version, address);

    return RPC_S_NOT_IMPLEMENTED;
}

/*	Initialse This Module					       cm_init()
**	---------------------
**
**  On entry,
**	No prerequisites.
**
**  On exit,
**	CM may be used.
**
*/
#ifdef __STDC__
rpc_status cm_init(void)
#else
rpc_status cm_init()
#endif
{
    return RPC_S_NORMAL;
}