/* zlogin (zlogout) (zflushlogs) -- a program to send a login,
 * logout, flush notice to an arbitrary realm...
 *
 * Created by:	Derek Atkins
 *
 * $Author: warlord $
 * $Source: /afs/media-lab.mit.edu/user/warlord/C/zlogin/RCS/zlogin.c,v $
 */

#ifndef lint
static char rcsid_zlogin_c[] = "$Id: zlogin.c,v 1.1 91/11/18 21:48:49 warlord Exp $";
#endif lint

#include <zephyr/mit-copyright.h>

#include <zephyr/zephyr_internal.h>

#include <pwd.h>
#include <sys/file.h>
#include <sys/param.h>
#include <string.h>
#include <netdb.h>

extern char *getenv();
extern int errno;

int send_login(), send_logout(), send_flush();

static char *exposure;

main(argc, argv)
int argc;
char **argv;
{
  int	retval, functype, arg, stats;
  int	(*f)();
  char	bfr[BUFSIZ], *whoami, *expose;

  whoami = ((whoami = rindex(argv[0], '/')) ? whoami+1 : argv[0]);
  
  if (! strcmp(whoami, "zlogin")) functype = 1;
  else if (! strcmp(whoami, "zlogout")) functype = 2;
  else functype = 3;		/* zflushlogs */

  /* Zephyr Initialization */
  retval = ZInitialize();
  if (retval != ZERR_NONE) {
    com_err(whoami, retval, "while initializing zephyr.");
    exit (1);
  }

  switch(functype) {
  case 1:
    f = send_login;
    expose = ZGetVariable("exposure");
    if (expose == NULL) 
      exposure = EXPOSE_REALMVIS;
    else {
      if (!strcasecmp(expose,EXPOSE_NONE))
	exposure = EXPOSE_NONE;
      if (!strcasecmp(expose,EXPOSE_OPSTAFF))
	exposure = EXPOSE_OPSTAFF;
      if (!strcasecmp(expose,EXPOSE_REALMVIS))
	exposure = EXPOSE_REALMVIS;
      if (!strcasecmp(expose,EXPOSE_REALMANN))
	exposure = EXPOSE_REALMANN;
      if (!strcasecmp(expose,EXPOSE_NETVIS))
	exposure = EXPOSE_NETVIS;
      if (!strcasecmp(expose,EXPOSE_NETANN))
	exposure = EXPOSE_NETANN;
    }
    break;
  case 2:
    f = send_logout;
    break;
  case 3:
    f = send_flush;
    break;
  }

  arg = 1;
  stats = 0;

  for (;arg<argc;arg++) {
    if (*argv[arg] == '-') {
      usage(whoami);

    } else {			/* The REAL McCoy */

      retval = f(argv[arg]);		/* I call! */
      if (retval != ZERR_NONE) {
	sprintf(bfr, "while sending to realm %s", argv[arg]);
	com_err(whoami, retval, bfr);
      }
      stats++;
    }
  }

  if (!stats)			/* stupid user -- didnt supply a realm */
    usage(whoami);

  return(0);
}

usage(whoami)
char *whoami;
{
  fprintf(stderr, "Usage:  %s realm1 [realm2 ...]\n", whoami);
  exit (0);
}

send_login(realm)
char *realm;
{
  return (My_Z_SendLocation(LOGIN_CLASS, exposure, ZAUTH, 
			 "$sender logged in to $1 on $3 at $2", realm));
}

send_logout(realm)
char *realm;
{
  return (My_Z_SendLocation(LOGIN_CLASS, LOGIN_USER_LOGOUT, ZNOAUTH, 
			 "$sender logged out of $1 on $3 at $2", realm));
}

send_flush(realm)
char *realm;
{
  return (My_Z_SendLocation(LOGIN_CLASS, LOGIN_USER_FLUSH, ZAUTH, "", realm));
}

static char host[MAXHOSTNAMELEN], mytty[MAXPATHLEN];
static int reenter = 0;

My_Z_SendLocation(class, opcode, auth, format, realm)
    char *class;
    char *opcode;
    int (*auth)();
    char *format;
{
    char *ttyname(), *ctime();

    char recp[REALM_SZ+2];
    int retval;
    long ourtime;
    ZNotice_t notice, retnotice;
    char *bptr[3];
#ifdef X11
    char *display;
#endif /* X11 */
    char *ttyp;
    struct hostent *hent;
    short wg_port = ZGetWGPort();

    strcpy(recp, "*@");
    strcat(recp, realm);

    (void) bzero((char *)&notice, sizeof(notice));
    notice.z_kind = ACKED;
    notice.z_port = (u_short) ((wg_port == -1) ? 0 : wg_port);
    notice.z_class = class;
    notice.z_class_inst = ZGetSender();
    notice.z_opcode = opcode;
    notice.z_sender = 0;
    notice.z_recipient = recp;
    notice.z_num_other_fields = 0;
    notice.z_default_format = format;

    /*
      keep track of what we said before so that we can be consistent
      when changing location information.
      This is done mainly for the sake of the WindowGram client.
     */

    if (!reenter) {
	    if (gethostname(host, MAXHOSTNAMELEN) < 0)
		    return (errno);

	    hent = gethostbyname(host);
	    if (hent)
		    (void) strcpy(host, hent->h_name);
	    bptr[0] = host;
#ifdef X11
	    if ((display = getenv("DISPLAY")) && *display) {
		    (void) strcpy(mytty, display);
		    bptr[2] = mytty;
	    } else {
#endif /* X11 */
		    ttyp = ttyname(0);
		    if (ttyp) {
			bptr[2] = rindex(ttyp, '/');
			if (bptr[2])
			    bptr[2]++;
			else
			    bptr[2] = ttyp;
		    }
		    else
			bptr[2] = "unknown";
		    (void) strcpy(mytty, bptr[2]);
#ifdef X11
	    }
#endif /* X11 */
	    reenter = 1;
    } else {
	    bptr[0] = host;
	    bptr[2] = mytty;
    }

    ourtime = time((long *)0);
    bptr[1] = ctime(&ourtime);
    bptr[1][strlen(bptr[1])-1] = '\0';

	
    if ((retval = ZSendList(&notice, bptr, 3, auth)) != ZERR_NONE)
	return (retval);

    retval = Z_WaitForNotice (&retnotice, ZCompareUIDPred, &notice.z_uid,
			      SRV_TIMEOUT);
    if (retval != ZERR_NONE)
      return retval;

    if (retnotice.z_kind == SERVNAK) {
	if (!retnotice.z_message_len) {
	    ZFreeNotice(&retnotice);
	    return (ZERR_SERVNAK);
	}
	if (!strcmp(retnotice.z_message, ZSRVACK_NOTSENT)) {
	    ZFreeNotice(&retnotice);
	    return (ZERR_AUTHFAIL);
	}
	if (!strcmp(retnotice.z_message, ZSRVACK_FAIL)) {
	    ZFreeNotice(&retnotice);
	    return (ZERR_LOGINFAIL);
	}
	ZFreeNotice(&retnotice);
	return (ZERR_SERVNAK);
    } 
	
    if (retnotice.z_kind != SERVACK) {
	ZFreeNotice(&retnotice);
	return (ZERR_INTERNAL);
    }

    if (!retnotice.z_message_len) {
	ZFreeNotice(&retnotice);
	return (ZERR_INTERNAL);
    }

    if (strcmp(retnotice.z_message, ZSRVACK_SENT) &&
	strcmp(retnotice.z_message, ZSRVACK_NOTSENT)) {
	ZFreeNotice(&retnotice);
	return (ZERR_INTERNAL);
    }

    ZFreeNotice(&retnotice);
	
    return (ZERR_NONE);
}
