/* This file is part of the Project Athena Zephyr Notification System.
 * It contains source for asynchronous location functions.
 *
 *	Created by:	Marc Horowitz
 *
 *	Copyright (c) 1990,1991 by the Massachusetts Institute of Technology.
 *	For copying and distribution information, see the file
 *	"mit-copyright.h". 
 */

#include <internal.h>

RCSID("$Id: ZAsyncLocate.c,v 1.12 2000/06/14 07:27:35 lha Exp $");

Code_t
ZRequestLocations(char *dest_realm, char *user, 
		  ZAsyncLocateData_t *zald, 
		  ZNotice_Kind_t  kind, Z_AuthProc auth)
{
    int retval, allocated_inst = 0;
    ZNotice_t notice;
    char *realm_str, *realm = NULL, srealm[REALM_SZ]; /* XXX */

    if (ZGetFD() < 0)
	if ((retval = ZOpenPort((u_short *)0)) != ZERR_NONE)
	    return (retval);

    realm_str = strchr (user, '@');
    if (dest_realm) {
	realm = ZExpandRealm(dest_realm, srealm, sizeof(srealm));
    } else if (realm_str) {
	realm = realm_str + 1;
    } else {
	realm = ZGetDefaultRealm();
    }
    
    memset(&notice, 0, sizeof(notice));
    notice.z_kind = kind;
    notice.z_port = __Zephyr_port;
    notice.z_class = LOCATE_CLASS;
    notice.z_opcode = LOCATE_LOCATE;
    if (realm_str == NULL) {
	asprintf(&notice.z_class_inst, "%s@%s", user, realm);
	if (notice.z_class_inst == NULL)
	    return (ENOMEM);
	allocated_inst = 1;
    } else
	notice.z_class_inst = user;
    notice.z_sender = 0;
    notice.z_recipient = "";
    notice.z_default_format = "";
    notice.z_message_len = 0;
    notice.z_dest_realm = realm;

    retval = ZSendNotice(&notice, auth);

    if (allocated_inst)
	free(notice.z_class_inst);

    if (retval != ZERR_NONE)
	return(retval);

    zald->user = strdup (user);
    if (zald->user == NULL)
	return(ENOMEM);
    zald->version = strdup(notice.z_version);
    if (zald->version == NULL) {
	free (zald->user);
	return(ENOMEM);
    }
    zald->uid = notice.z_multiuid;

    return(ZERR_NONE);
}

Code_t
ZParseLocations(ZNotice_t *notice,
		ZAsyncLocateData_t *zald,
		int *nlocs,
		char **user)
{
    char *ptr, *end;
    int i;

    ZFlushLocations();    /* This never fails (this function is part of the
			     library, so it is allowed to know this). */

    /* non-matching protocol version numbers means the
       server is probably an older version--must punt */

    if (zald && strcmp(notice->z_version, zald->version))
      return(ZERR_VERS);

    if (notice->z_kind == SERVNAK)
      return (ZERR_SERVNAK);

    /* flag ACKs as special */
    if (notice->z_kind == SERVACK &&
	!strcmp(notice->z_opcode, LOCATE_LOCATE)) {
	*nlocs = -1;
	return(ZERR_NONE);
    }

    if (notice->z_kind != ACKED)
	return (ZERR_INTERNAL);

    end = notice->z_message+notice->z_message_len;

    __locate_num = 0;
	
    for (ptr=notice->z_message;ptr<end;ptr++)
      if (!*ptr)
	__locate_num++;

    __locate_num /= 3;

    if (__locate_num)
      {
      __locate_list = (ZLocations_t *)malloc((unsigned)__locate_num*
					     sizeof(ZLocations_t));
      if (!__locate_list)
	return (ENOMEM);
    } else {
      __locate_list = 0;
    }

    for (ptr=notice->z_message, i=0; i<__locate_num; i++) {
       unsigned int len;

       len = strlen (ptr) + 1;
       __locate_list[i].host = (char *) malloc(len);
       if (!__locate_list[i].host)
	  return ENOMEM; /* XXX free */
       memmove(__locate_list[i].host, ptr, len);
       ptr += len;

       len = strlen (ptr) + 1;
       __locate_list[i].time = (char *) malloc(len);
       if (!__locate_list[i].time)
	  return ENOMEM; /* XXX free */
       memmove(__locate_list[i].time, ptr, len);
       ptr += len;

       len = strlen (ptr) + 1;
       __locate_list[i].tty = (char *) malloc(len);
       if (!__locate_list[i].tty)
	  return ENOMEM; /* XXX free */
       memmove(__locate_list[i].tty, ptr, len);
       ptr += len;
    }

    __locate_next = 0;
    *nlocs = __locate_num;
    if (user) {
	if (zald)
	    *user = strdup(zald->user);
	else
	    *user = strdup(notice->z_class_inst);
	if (*user == NULL)
	    return ENOMEM; /* XXX free */
    }
    return (ZERR_NONE);
}

int
ZCompareALDPred(ZNotice_t *notice, void *zald)
{
    return(ZCompareUID(&(notice->z_multiuid),
		       &(((ZAsyncLocateData_t *) zald)->uid)));
}

void
ZFreeALD (ZAsyncLocateData_t *zald)
{
   if (!zald) return;

   if (zald->user) free(zald->user);
   if (zald->version) free(zald->version);
   memset(zald, 0, sizeof(*zald));
}
