#include <stdio.h>
#include <time.h>
#include <ctype.h>
#include <afs/auth.h>
#include <afs/cellconfig.h>
#include <afs/ptclient.h>
#include <afs/pterror.h>

#include <des.h>
#include <krb.h>

#define AFSKEY "afs"
#define AFSINST ""

#define CKLOG_SUCCESS 0
#define CKLOG_USAGE 1
#define CKLOG_SOMETHINGSWRONG 2
#define CKLOG_AFS 3
#define CKLOG_KERBEROS 4
#define CKLOG_TOKEN 5
#define CKLOG_BADPATH 6
#define CKLOG_MISC 7

extern int errno;
static char *progname;

main(argc, argv)
    int argc;
    char **argv;

{
    char pname[ANAME_SZ];
    char pinst[INST_SZ];
    char prealm[REALM_SZ];
    char namebuf[ANAME_SZ+1+INST_SZ+1+REALM_SZ+1];
    char username[ANAME_SZ+1+INST_SZ+1+REALM_SZ+1];
    struct ktc_token token;
    struct ktc_principal krb, afs, me;
    int rc, id;
    extern char  *krb_err_txt[];
    struct namelist nl;
    struct idlist idl;
    char fullcell[MAXCELLCHARS];

    char afs_instance[INST_SZ];
    char afs_realm[REALM_SZ];
    char afs_krb_realm[REALM_SZ];

    KTEXT_ST auth;
    CREDENTIALS c;

    if (argc != 2) {
	fprintf(stderr, "Usage: %s <realm>\n", argv[0]);
	exit(1);
    }

    progname = argv[0];

    rc = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm);
    if (rc != 0) {
	fprintf(stderr, "%s: unable to find ticket file: %s\n", argv[0], krb_err_txt[rc]);
	exit(1);
    }

    get_realm_of_cell(argv[1], fullcell, afs_krb_realm);
    folddown(afs_instance, fullcell);
    foldup(afs_realm, fullcell);

#ifdef DEBUG
    printf("afs_krb_realm == %s\nafs_instance == %s\nafs_realm == %s\n",
	   afs_krb_realm, afs_instance, afs_realm);
#endif

/* Try afs.cell-name@cell-name */
    debug_out(AFSKEY, afs_instance, afs_realm);
    rc = krb_mk_req(&auth, AFSKEY, afs_instance, afs_realm);
    if (rc == 0) {
      rc = krb_get_cred(AFSKEY, afs_instance, afs_realm, &c);
      if (rc != 0) {
	fprintf(stderr,"%s: krb_get_cred: %s\n", argv[0], krb_err_txt[rc]);
	exit(CKLOG_KERBEROS);
      }
    }

/* Try afs.cell-name@krb_realm */
    if (rc != 0) {
      debug_out(AFSKEY, afs_instance, afs_krb_realm);
      rc = krb_mk_req(&auth, AFSKEY, afs_instance, afs_krb_realm);
      if (rc == 0) {
	rc = krb_get_cred(AFSKEY, afs_instance, afs_krb_realm, &c);
	if (rc != 0) {
	  fprintf(stderr,"%s: krb_get_cred: %s\n", argv[0], krb_err_txt[rc]);
	  exit(CKLOG_KERBEROS);
	}
      }
    }

/* Try afs@cell_name */
    if (rc != 0) {
      debug_out(AFSKEY, AFSINST, afs_realm);
      rc = krb_mk_req(&auth, AFSKEY, AFSINST, afs_realm);
      if (rc == 0) {
	rc = krb_get_cred(AFSKEY, AFSINST, afs_realm, &c);
	if (rc != 0) {
	  fprintf(stderr,"%s: krb_get_cred: %s\n", argv[0], krb_err_txt[rc]);
	  exit(CKLOG_KERBEROS);
	}
      }
    }

/* Try afs@krb_realm */
    if (rc != 0) {
      debug_out(AFSKEY, AFSINST, afs_krb_realm);
      rc = krb_mk_req(&auth, AFSKEY, AFSINST, afs_krb_realm);
      if (rc == 0) {
	rc = krb_get_cred(AFSKEY, AFSINST, afs_krb_realm, &c);
	if (rc != 0) {
	  fprintf(stderr,"%s: krb_get_cred: %s\n", argv[0], krb_err_txt[rc]);
	  exit(CKLOG_KERBEROS);
	}
      }
    }

/* Uh oh! no tickey! */
    if (rc != 0) {
      fprintf(stderr, "%s: Unable to request a ticket for cell %s\n", progname, afs_instance);
      exit(CKLOG_KERBEROS);
    }

    /* XXX -- I shouldn't have to do this token store.  This is because
     * AFS/Andrew is screwed and whenever one gets a ticket it gets stored
     * as a token, but the AFS library doesn't like tokens stored as
     * "afs.instance@cell", since it special cases out "afs".  Therefore,
     * if the instance is the cell-name, and it is not equal to the
     * kerberos realm, then ktc_SetToken will store the token, and then ALSO
     * store the afs@realm token, which will not be what you wanted.
     * I can find no way around this without hacking the AFS libraries, 
     * re-writing ktc_SetToken, or fixing the way Andrew deals with
     * Kerberos.  I dont know if other sites will/will not have this problem.
     *
     * -derek <warlord@MIT.EDU> 2 Sep 92
     */

    (void)strcpy(krb.name, c.service);
    (void)strcpy(krb.instance, c.instance);
    folddown(krb.cell, c.realm);
    (void)strcpy(me.name, c.pname);
    (void)strcpy(me.instance, c.pinst);
    folddown(me.cell, prealm);

#ifdef DEBUG
    printf("First token is: %s%s%s@%s\n", c.service, (*(c.instance) == NULL) ?
	   "" : ".", c.instance, c.realm);
    printf("Me: %s%s%s@%s\n", c.pname, (*(c.pinst) == NULL) ? "" : ".",
	   c.pinst, prealm);
#endif

    token.ticketLen = c.ticket_st.length;
    bcopy((char *)&c.ticket_st.dat[0], &token.ticket[0], token.ticketLen);
    bcopy((char *)&c.session[0], (char *)&token.sessionKey, sizeof(token.sessionKey));
    token.kvno = c.kvno;
    token.startTime = c.issue_date;
    token.endTime = krb_life_to_time(c.issue_date,c.lifetime);

    rc = ktc_SetToken(&krb, &token, &me);
    if (rc) {
	fprintf(stderr,"%s: could not store token for afs@%s: %s\n",
		progname, afs_instance, krb_err_txt[rc]);
	exit(1);
    }
    printf("Due to AFS Brokenness, I just had to store a new token for\n");
    printf("afs@%s, which means if you were already authenticated to\n", krb.cell);
    printf("the %s cell, you will have to authenticate again.\n\n", krb.cell);
    printf("Please flame at transarc!  Its their fault.\n");

    nl.namelist_len = 1;
    strcpy(namebuf, pname);
    if (*pinst) {
	strcat(namebuf, ".");
	strcat(namebuf, pinst);
    }
    strcat(namebuf, "@");
    strcat(namebuf, me.cell);
    nl.namelist_val = (prname *)namebuf;
    idl.idlist_len = 0;
    idl.idlist_val = NULL;

    (void)strcpy(afs.name, AFSKEY);
    (void)strcpy(afs.instance, AFSINST);
    (void)strcpy(afs.cell, afs_instance);
    (void)strcpy(me.name, namebuf);
    (void)strcpy(me.instance, AFSINST);
    folddown(me.cell, prealm);

#ifdef DEBUG
    printf("Storing AFS token: %s%s%s@%s\n", afs.name,
	   (*afs.instance == NULL) ? "" : ".", afs.instance, afs.cell);
    printf("For user %s at %s\n", me.name, me.cell);
#endif

    rc = ktc_SetToken(&afs, &token, &me);
    if (rc) {
	fprintf(stderr,"%s: could not store token for afs@%s: %s\n",
		progname, afs_instance, krb_err_txt[rc]);
	exit(1);
    }

    rc = pr_Initialize(1L, AFSCONF_CLIENTNAME, afs_instance);
    if (rc) {
	fprintf(stderr,"%s: could not intialize protection package\n",argv[0]);
	fprintf(stderr,"%s: remote cell may be down\n",afs_instance);
	exit(1);
    }

    id = 0;

    rc = pr_NameToId(&nl, &idl);
    if (rc) {
	com_err(argv[0], PRNOENT, "so cannot confirm cross cell token for %s", namebuf);
	fprintf(stderr,"%s: remote cell may be down\n",afs_instance);
	exit(1);
    }

    /* entry DNE */
    if (idl.idlist_val[0] == ANONYMOUSID) {
      printf("doing first-time registration of %s at %s\n", namebuf, afs_instance);
      rc = pr_CreateUser(namebuf, &id);
      
      if (rc) {
	com_err (argv[0], rc, "so could not create cross cell entry for %s", namebuf);
	fprintf(stderr,"%s: remote cell may be down\n",afs_instance);
	exit(1);
      }
      
      printf("created cross cell entry for %s at %s\n", namebuf, afs_instance);
    } else 
      /* Set the id number to store */
      id = idl.idlist_val[0];


    if (id != ANONYMOUSID && id != 0) {
      sprintf(username, "AFS ID %d", id);
      strcpy(me.name, username);
#ifdef DEBUG
    printf("Storing AFS token: %s%s%s@%s\n", afs.name,
	   (*afs.instance == NULL) ? "" : ".", afs.instance, afs.cell);
    printf("For user %s at %s\n", me.name, me.cell);
#endif
      rc = ktc_SetToken(&afs, &token, &me);
      if (rc) {
	fprintf(stderr,"%s: could not store token for afs@%s: %s\n",
		progname, afs_instance, krb_err_txt[rc]);
	exit(1);
      }
    }

    rx_Finalize();
}

foldup (out,in)
char *in,*out;
{
    for (;*in; in++) {
	if (islower(*in))
	    *out = toupper(*in);
	else
	    *out = *in;
	out++;
    }
    *out = 0;
}

folddown (out,in)
char *in,*out;
{
    for (;*in; in++) {
	if (isupper(*in))
	    *out = tolower(*in);
	else
	    *out = *in;
	out++;
    }
    *out = 0;
}

/* This function will get the kerberos realm-name of the first host of
 * cell in question.  Added by Derek Atkins <warlord@MIT.EDU> based upon
 * the source for aklog from MIT/Athena.
 */
get_realm_of_cell(cell, fullcell, realm)
char *cell, *fullcell, *realm;
{
  struct afsconf_cell cellconfig; /* General information about the cell */
  int status = 0;

  struct afsconf_dir *configdir;
  
  bzero((char *)&cellconfig, sizeof(cellconfig));
  
    if (!(configdir = afsconf_Open(AFSCONF_CLIENTNAME))) {
      fprintf(stderr, 
	      "%s: can't get afs configuration (afsconf_Open(%s))\n",
	      progname, AFSCONF_CLIENTNAME);
      exit(CKLOG_AFS);
    }
  
  if (afsconf_GetCellInfo(configdir, cell, NULL, &cellconfig)) {
    fprintf(stderr, "%s: Can't get information about cell %s.\n",
	    progname, cell);
    exit(CKLOG_AFS);
  }

  strcpy(fullcell, cellconfig.name);
  
  (void) afsconf_Close(configdir);
  
  strcpy(realm, krb_realmofhost(cellconfig.hostName[0]));

#ifdef DEBUG
  printf("Realm of host %s is %s\n", cellconfig.hostName[0], realm);
#endif

  return(status);
}

debug_out(princ, inst, realm)
char *princ, *inst, *realm;
{
#ifdef DEBUG
  printf("Trying to get ticket for %s.%s@%s\n", princ, inst, realm);
#endif
}
