
/* GUI to Kerberos kinit/klist/kdestroy.
 */
#import <defaults.h>
#import <appkit/Panel.h>
#import <appkit/TextField.h>
#import "Subprocess.h"
#import "Reporter.h"
#import "Kauth.h"

int strlen(char *s);
char *strcpy(char *s1, char *s2);
int strcmp(char *s1, char *s2);
char *malloc(unsigned u);
char *realloc(char *b, unsigned u);
void free(char *b);
int system(char *cmd);
char *getname(int uid);
int getuid();

/* Return last nonempty line of a buffer---usually an error message. 
 */
char *lastLineOf(buf)
 char *buf;
{
   char *ep;
   if (!buf || !strlen(buf)) { 
      return "";
   } else {
      ep= buf+strlen(buf)-1;
      if (ep!=buf && *ep=='\n') ep--;
      while (ep>buf && *ep!='\n') ep--;
      if (*ep=='\n') ep++;
      return ep;
   }
}      
   
/* Decide if buffer ends with given string. */
int bufferEndsWith(buf, pat)
  char *buf;
  char *pat;
{
   int bl, pl;
   if (!buf || !pat) return 0;
   bl= strlen(buf);  pl= strlen(pat);
   if (bl < pl) return 0;
   return !strcmp(buf+bl-pl, pat);
}
 
/* ====================================================================== */ 
@implementation Kauth

/* ---------------------------------------------------------------------- */
/* Put up the Authenticate panel and start a kinit process */
- showAuthenticate:sender
{
    outputBuffer= NULL;
    if (!kinitProcess) {
	kinitProcess= [Subprocess new];
    }    
    [kinitProcess init:"/usr/local/bin/kinit -r" withDelegate:self
    	      andPtySupport:YES andStdErr:YES];
    kinitStage= STARTED;
    [principalTextField selectText:sender];
    [passwordTextField setStringValue:""];
    [authenticateButton setEnabled:NO]; /* enable when kinit prompts */
    [authenticatePanel makeKeyAndOrderFront:sender];
    return self;
}

/* ---------------------------------------------------------------------- */
/* Send authentication information to the kinit process */
- authenticate:sender
{
   if (kinitStage==GOTNAME) {
      [kinitProcess send:[principalTextField stringValue]];
      kinitStage=SENTNAME;
   } else {
      printf("Authenticate button enabled at strange time\n");
   }
   [authenticateButton setEnabled:NO]; /* enable when kinit prompts */
   return self;
}

/* ---------------------------------------------------------------------- */
/* Put up the Info panel, with output from klist in it */
- showInfo:sender
{
    [klistReporter refresh:sender];
    [infoPanel makeKeyAndOrderFront:sender];
    return self;
}


/* ====================================================================== */
/* Subprocess Delegation */

/* ---------------------------------------------------------------------- */
/* Deal with output from kinit process: recognize the banner and prompts, 
 * updating the display and responding to kinit as needed.
 */
-subprocessOutput:(char *)buffer
{
    if (outputBuffer) {
	outputBuffer=
	   realloc(outputBuffer,strlen(outputBuffer)+strlen(buffer)+2);
    } else {
	outputBuffer= malloc(strlen(buffer)+1);
	outputBuffer[0]= '\0';
    }
    strcpy(outputBuffer+strlen(outputBuffer), buffer);
    /* deal with banner and prompts, in a state machine */
    switch (kinitStage) {
      case STARTED: /* waiting for banner and name prompt */
	if (bufferEndsWith(outputBuffer,"Kerberos name: ")) {
	    [kinitBannerTextField setStringValue:outputBuffer];
	    free(outputBuffer); outputBuffer= NULL;
	    [authenticateButton setEnabled:YES];
	    kinitStage= GOTNAME;
	}
	break;
	/* the authenticate method will set kinitStage= SENTNAME */
      case SENTNAME: /* waiting for realm prompt */
	if (bufferEndsWith(outputBuffer,"Kerberos realm: ")) {
	    free(outputBuffer); outputBuffer= NULL;
	    [kinitProcess send:[realmTextField stringValue]];
	    kinitStage= SENTREALM;
	}
	break;
      case SENTREALM: /* waiting for password prompt */
	if (bufferEndsWith(outputBuffer,"Password: ")) {
	    free(outputBuffer); outputBuffer= NULL;
	    [kinitProcess send:[passwordTextField stringValue]];
	    [passwordTextField setStringValue:""];
	    kinitStage= SENTPASSWD;
	}
	break;
      case SENTPASSWD: 
        /* got an error message: deal with it in subprocessDone */
	break;
      default:
	printf("Kauth: strange value of kinitStage (%d)\n", kinitStage);
	break;
	}
    return self;
}

/* ---------------------------------------------------------------------- */
/* Deal with end of kinit process: decide what happened.
 * Close the Authenticate panel if all is well.
 * Otherwise, put up an error message.
 */
- subprocessDone
{
    [passwordTextField setStringValue:""];
    [klistReporter refresh:self];
    if (kinitStage==STARTED) {
	NXRunAlertPanel(0, 
		 "Problem starting kinit.  Is Kerberos installed?", 0, 0, 0);
	[authenticatePanel performClose:self];
    } else if (outputBuffer && strlen(outputBuffer)>2) {
	NXRunAlertPanel(0, lastLineOf(outputBuffer), 0, 0, 0);
	/* restart so that Authenticate button will become re-enabled */
	[self showAuthenticate:self]; 
    } else if (kinitStage==SENTPASSWD) {
	[authenticatePanel performClose:self]; /* successful */
    } else {
        NXRunAlertPanel(0, "Kinit started, but then died.", 0, 0, 0);
	[self showAuthenticate:self]; 
    }
    return self;
}

/* ---------------------------------------------------------------------- */
/* Deal with error form subprocess: put up a panel to tell the user */
- subprocessError:(const char *)errorString
{
    [passwordTextField setStringValue:""];
    NXRunAlertPanel(0, errorString, 0, 0, 0);
    return self;
}

/* ---------------------------------------------------------------------- */
/* Save Principal and Realm to defaults database */
- saveDefaults:sender
{
    NXWriteDefault("kerberos", "principal", [principalTextField stringValue]);
    NXWriteDefault("kerberos", "realm", [realmTextField stringValue]);    
    return self;
}

/* ====================================================================== */
/* Application Object Delegation */

- appDidInit:sender
{
	const NXDefaultsVector kerberosDefaults = {
	{ "principal", "nobody" },
	{ "realm", "NOWHERE" },
	{ NULL, NULL}
	};
    NXRegisterDefaults("kerberos", kerberosDefaults);
    
    [klistReporter setCommand:"/usr/local/bin/klist"];
    [principalTextField 
    	setStringValue:NXGetDefaultValue("kerberos", "principal")];
    [realmTextField setStringValue:NXGetDefaultValue("kerberos", "realm")];
    /* [principalTextField setStringValue:getname(getuid())]; */
    
    [self showAuthenticate:sender];
    return self;
}

- appWillTerminate:sender
{
    /* [self destroyTickets:sender]; */
    (void) system("/usr/local/bin/kdestroy"); /* low-tech but reliable */
    return self;
}

@end
