/* This file is part of the Project Athena Zephyr Notification System.
 * It contains source for the ZInitialize function.
 *
 *	Created by:	Robert French
 *
 *	Copyright (c) 1987, 1991 by the Massachusetts Institute of Technology.
 *	For copying and distribution information, see the file
 *	"mit-copyright.h". 
 */

#include <internal.h>

RCSID("$Id: ZInit.c,v 1.19 2000/09/22 00:07:59 lha Exp $");

#include <sys/types.h>
#include <sys/socket.h>

#include <arpa/inet.h>
#include <netinet/in.h>

ZNotice_t notice;
#ifdef HAVE_KRB4
#include <krb_err.h>
#endif

#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif

static Code_t
parse_one_realm (ZNotice_t *notice, Z_RealmList *realm)
{
    Code_t code;
    int i;
    char *mp;
    
    for (i=0, mp = notice->z_message;
	 mp < notice->z_message + notice->z_message_len;
	 i++, mp += strlen(mp)+1) {
	if (i%12 == 11) {
	    code = Z_ParseRealmConfig(mp, &realm->realm_config);
	    if (code != ZERR_NONE)
		return(code);
	    
#ifdef HAVE_KRB4
	    strlcpy(realm->krealm,
		    krb_realmofhost(realm->realm_config.server_list[0].name),
		    sizeof(realm->krealm));
	    __realm_list[i/12].last_authent_time = 0;
#endif
	}
    }
    return ZERR_NONE;
}

/*
 *
 */

Code_t 
ZInitialize (void)
{
    struct servent *hmserv;
    ZNotice_t notice;
    Code_t retval;
    char **realms;

#ifdef HAVE_KRB4
    initialize_krb_error_table();
#endif
    initialize_zeph_error_table();
    
    /* Set up local loopback address for HostManager */

    (void) memset((char *)&__HM_addr, 0, sizeof(__HM_addr));
    __HM_addr.sin_family = AF_INET;
    __HM_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

    hmserv = (struct servent *)getservbyname(HM_SVCNAME, "udp");
    if (hmserv)
	__HM_addr.sin_port = hmserv->s_port; 
    else {
	__HM_addr.sin_port = HM_SVC_FALLBACK;
	/* LIBROKEN warnx ("using fallback port for %s/udp", HM_SVCNAME); */
    }

    /* Initialize the input queue */
    __Q_Tail = NULL;
    __Q_Head = NULL;

    /* 
     * if the application is a server, there might not be a zhm.  The
     * code will fall back to something which might not be "right",
     * but this is is ok, since none of the servers call krb_rd_req. 
     */

    if (__Zephyr_server) {
#ifdef HAVE_KRB4
	char realm[REALM_SZ];
	int ret;
#endif

	__realm_list = (Z_RealmList *) malloc(sizeof(Z_RealmList));
	if (__realm_list == NULL)
	    return ENOMEM;
	memset (__realm_list, 0, sizeof(*__realm_list));
#ifdef HAVE_KRB4
	ret = krb_get_lrealm (realm, 0);
	if (ret)
	    return EINVAL;
	
	__realm_list[0].realm_config.realm = strdup (realm);
	if (__realm_list[0].realm_config.realm == 0) {
	    free (__realm_list);
	    return ENOMEM;
	}
#else
	abort();
#endif
	__nrealms = 1;
    } else {
	int code;
	int i;
	const char *def;
	

	/*
	 * Get the list of realms from zhm
	 */

	if ((code = ZOpenPort(NULL)) != ZERR_NONE)
	    return(code);
	
	if ((code = ZhmRealmList(NULL, &realms, &__nrealms)) != ZERR_NONE)
	    return(code);

	if (__nrealms == 0)
	    return ZERR_BADCONFREALM;

	__realm_list = malloc(sizeof(Z_RealmList)*__nrealms);
	if (__realm_list == NULL) {
	    ZhmFreeRealmList(realms);
	    return ENOMEM;
	}
	memset (__realm_list, 0, sizeof(Z_RealmList)*__nrealms);
	
	for (i = 0; i < __nrealms; i++) {
	    memset (&notice, 0, sizeof(notice));
	    code = ZhmStatOneRealm(NULL, realms[i], &notice);
	    if (code) {
		free(__realm_list);
		break;
	    }
	    
	    code = parse_one_realm(&notice, &__realm_list[i]);
	    if (code) {
		free(__realm_list);
		break;
	    }	    
	    ZFreeNotice(&notice);
	}		
	ZhmFreeRealmList(realms);
	ZClosePort();

	if (code) {
	    for (; i >= 0; i--)
		Z_FreeRealmConfig(&__realm_list[i].realm_config);
	    return code;
	}
	
	__default_realm = 0;
	
	def = ZGetVariable("defaultrealm");
	if (def) {
	    for (i=0; i<__nrealms; i++) {
		if (strcasecmp(__realm_list[i].realm_config.realm, def) == 0) {
		    __default_realm = i;
		    break;
		}
	    }
	}
    }
    
    retval = Z_GetMyAddr ();
    if (retval != ZERR_NONE)
	return retval;

    /* Get the sender so we can cache it */

    ZGetSender();

    return (ZERR_NONE);
}

