/* This file is part of the Project Athena Zephyr Notification System.
 * It contains the ZhmStat() function.
 *
 *      Created by:     Marc Horowitz
 *
 *      $Id: ZhmStat.c,v 1.7 2001/01/28 05:03:22 assar Exp $
 *
 *      Copyright (c) 1996 by the Massachusetts Institute of Technology.
 *      For copying and distribution information, see the file
 *      "mit-copyright.h". 
 */

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

#ifndef INADDR_LOOPBACK
#define INADDR_LOOPBACK 0x7f000001
#endif

/*
 * Do a `request' do `hostaddr', or if NULL, to localhost. Return
 * response in `response' that needs to be ZFreeNotice()ed.

 */

static Code_t
ZhmRequest(struct sockaddr_in *hostaddr, char *request, ZNotice_t *response,
	   char *message, int message_len)
{
    struct servent *sp;
    struct sockaddr_in sin;
    ZNotice_t req;
    Code_t code;
    struct timeval tv;
    fd_set readers;
    int fd;

    memset(&sin, 0, sizeof(sin));
    
    if (hostaddr)
	sin = *hostaddr;
    else
	sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

    sp = getservbyname(HM_SVCNAME, "udp");

    sin.sin_port = (sp) ? sp->s_port : HM_SVC_FALLBACK;
    sin.sin_family = AF_INET;

    memset(&req, 0, sizeof(req));
    req.z_kind = STAT;
    req.z_port = 0;
    req.z_class = HM_STAT_CLASS;
    req.z_class_inst = HM_STAT_CLIENT;
    req.z_opcode = request;
    req.z_sender = "";
    req.z_recipient = "";
    req.z_default_format = "";
    req.z_message_len = message_len;
    req.z_message = message;

    if ((code = ZSetDestAddr(&sin)) != ZERR_NONE)
	return(code);

    if ((code = ZSendNotice(&req, ZNOAUTH)) != ZERR_NONE)
	return(code);

    fd = ZGetFD();
    if (fd < 0)
	return(ZERR_NOPORT);

    FD_ZERO(&readers);
    FD_SET(fd, &readers);
    tv.tv_sec = 10;
    tv.tv_usec = 0;
    code = select(fd + 1, &readers, NULL, NULL, &tv);
    if (code < 0 && errno != EINTR)
	return(errno);
    if (code == 0 || (code < 0 && errno == EINTR) || ZPending() == 0)
	return(ZERR_HMDEAD);
    
    return ZReceiveNotice(response, NULL);
}

/*
 * Backward compability function
 */

Code_t
ZhmStat(struct sockaddr_in *hostaddr, ZNotice_t *notice)
{
    return ZhmRequest(hostaddr, HM_GIMMESTATS, notice, NULL, 0);
}

/*
 *
 */

Code_t
ZhmRealmList(struct sockaddr_in *hostaddr, char ***realms, int *num_realms)
{
    ZNotice_t notice;
    Code_t code;
    char *mp;
    char **r = NULL;
    int i;

    code = ZhmRequest (hostaddr, HM_GIMMEREALMS, &notice, NULL, 0);
    if (code)
	return code;
    
    for (i=0, mp = notice.z_message;
	 mp < notice.z_message + notice.z_message_len;
	 i++, mp += strlen(mp) + 1) {
	char **r_n;

	r_n = realloc (r, sizeof(*r) * (i+2));
	if (r_n == NULL) {
	    ZhmFreeRealmList(r);
	    ZFreeNotice(&notice);
	    return ENOMEM;
	}
	r = r_n;
	r[i] = strdup (mp);
	r[i+1] = NULL;
	if (r[i] == NULL) {
	    ZhmFreeRealmList(r);
	    ZFreeNotice(&notice);
	    return ENOMEM;
	}
    }
    
    if (i <= 0)
	return ZERR_HMDEAD;

    *realms = r;
    *num_realms = i;

    return 0;
}

/*
 * This is a almost backward compability function, it works the same
 * way as ZhmStat()
 */

Code_t
ZhmStatOneRealm(struct sockaddr_in *hostaddr, char *realm, ZNotice_t *notice)
{
    return ZhmRequest(hostaddr, HM_GIMMERSTAT, notice, realm, 
		      strlen(realm) + 1);
}

/*
 * Free realms given by ZhmRealmList
 */

void
ZhmFreeRealmList(char **realms)
{
    char **r = realms;
    if (realms == NULL)
	return;
    while (*realms) {
	free(*realms);
	realms++;
    }
    free(r);
}
