#define WANT_DECC_STDIO
#define WANT_DECC_STRING
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <vms/rms.h>

#include <vms/secdef.h>			/* VMS sections			  */
#include <vms/ssdef.h>			/* System service return codes	  */
#include <vms/uafdef.h>			/* Authorization file records	  */
#include <vms/psldef.h>			/* Processor Status Longword defs */
#include <vms/xabdef.h>			/* XAB definition */
#include <vms/stsdef.h>

#include "wpquery.h"

#define UAF$S_USERNAME	(32)
#define UAF$S_OWNERNAME	(32)
#define UAFNAME "SYSUAF"		/* Name of authorization file */
#define DEFNAME "SYS$SYSTEM:.DAT"	/* Default path to authorization file */
#define ERROR(s)	(!((s)&1))

/* #define WPDEBUG 1 */

static char *Global_Section_Name = "MULTINET_FUZZY_DATABASE";

struct token {
	struct token *next;
	char *real;
	char *phonetic;
	};

struct user {
	struct user *next;
	char *real;
	char *phonetic;
    	char *owner;
	struct token *token_list;
	};



static char *upper(p)
register char *p;
{
	register char *n=p;
	while (*n) {
	   *n = _toupper(*n), n++;
	}
	return(p);
}



static char *strdup(s)
	char *s;
{
	char *tmp;
	int len;

	len = strlen(s);
	tmp = (char *)malloc(len+1);
	strcpy(tmp,s);
	return(tmp);
}

static struct token *get_tokens(cp,delimiters)
	char *cp;	/* string to tokenize */
	char *delimiters;
{
	struct token *tlist,*tok,*ptok;
	char c;
	int i;

	/* Tokenize the Owner Name */
	ptok = tlist = NULL;
	while (*cp) {
		cp += strspn(cp,delimiters);
		i = strcspn(cp,delimiters);
		if (!i) break;
    	    	if (i < 2) {
    	    	    cp += i;
    	    	    continue;
    	    	}

		tok = malloc(sizeof(struct token));
		tok->next = NULL;
		tok->phonetic = NULL;
		if (ptok)
			ptok->next = tok;
		else
			tlist = tok;
		ptok = tok;

		c = cp[i];
		cp[i] = '\0';
		tok->real = strdup(cp);
		upper(tok->real);
		tok->phonetic = strdup((char *)CanonGiven(tok->real));
		cp[i] = c;
		cp += i;
		}

	return(tlist);
}


static int free_tokens(tok)
	struct token *tok;
{
	struct token *next;

	while (tok) {
		next = tok->next;
		if (tok->real) free(tok->real);
		if (tok->phonetic) free(tok->phonetic);
		free(tok);
		tok = next;
		}
}

static int print_tokens(tok)
	struct token *tok;
{
	while (tok) {
		printf(" (%s,%s)",tok->real,tok->phonetic);
		tok = tok->next;
		}
}



static int add_user(uaf,ulistptr)
	struct UAF *uaf;
	struct user **ulistptr;
{
	struct user *user;
	struct token *tok,*ptok;
	char username[UAF$S_USERNAME+1];
	char owner[UAF$S_OWNERNAME+1];
	int i;
	int size = 0;

	/* get the user name */
	for (i=0; i<UAF$S_USERNAME; i++) {
		username[i] = uaf->uaf$t_username[i];
		if (username[i] == ' ') {
			username[i] = '\0';
			break;
			}
		}
	username[UAF$S_USERNAME] = '\0';

	strncpy(owner,(char *)&uaf->uaf$t_owner[1],uaf->uaf$t_owner[0]);
	owner[uaf->uaf$t_owner[0]] = '\0';

	/* allocate a user structure */
	user = malloc(sizeof(struct user));

	/* fill it in */
	user->real = strdup(username);
	user->phonetic = (char *)CanonSurn(user->real);
    	user->owner = strdup(owner);
	user->token_list = NULL;

	/* Tokenize the Owner Name */
	ptok = NULL;
	user->token_list = get_tokens(owner," .");

	/* Link it into the list */
	user->next = *ulistptr;
	*ulistptr = user;
}


static free_users(u)
	struct user *u;
{
	struct user *tmp;

	while (u) {
		tmp = u->next;
		if (u->real) free(u->real);
		if (u->phonetic) free(u->phonetic);
    	    	if (u->owner) free(u->owner);
		free_tokens(u->token_list);
		u = tmp;
		}
}



static int size_users(u)
	struct user *u;
{
	struct token *t;
	int size = 0;

	while (u) {
		size += 3;
		size += strlen(u->real) + 1;
		size += strlen(u->phonetic) + 1;
    	    	size += strlen(u->owner) + 1;
		t = u->token_list;
		while (t) {
			size += strlen(t->real) + 1;
			size += strlen(t->phonetic) + 1;
			t = t->next;
			}
		u = u->next;
		}
	return(size);
}

static int dump_users(u,db,timestamp)
	struct user *u;
	char *db;
	int *timestamp;
{
	struct token *t;
	int size = 0;
	char *cp;
	long *lp;

#ifdef NOTDEF
#ifdef WPDEBUG
	printf("sec mod time = [%x,%x]\n",timestamp[1],timestamp[0]);
#endif WPDEBUG
#endif NOTDEF

	lp = (long *)db;
	lp[0] = timestamp[0];
	lp[1] = timestamp[1];
	cp = (db += 8);

	while (u) {
		cp += 2;
		strcpy(cp,u->real);
		cp += strlen(u->real)+1;
		strcpy(cp,u->phonetic);
		cp += strlen(u->phonetic)+1;
		strcpy(cp,u->owner);
		cp += strlen(u->owner)+1;
		t = u->token_list;
		while (t) {
			strcpy(cp,t->real);
			cp += strlen(t->real)+1;
			strcpy(cp,t->phonetic);
			cp += strlen(t->phonetic)+1;
			t = t->next;
			}
		*cp++ = '\0';
		db += (*(short *)db = (cp-db));
		u = u->next;
		}
}


/*
 *	Read in the UAF data
 */
static int convert_uaf(fab,ulist)
	struct FAB *fab;
	struct user **ulist;
{
	struct RAB rab;
	struct UAF uaf;
	unsigned long rc;

	rab = cc$rms_rab;
	rab.rab$w_usz = sizeof(uaf);
	rab.rab$l_ubf = (char *)&uaf;
	rab.rab$l_fab = fab;
	rab.rab$l_rop |= RAB$M_NLK|RAB$M_WAT;
	rab.rab$b_rac = RAB$C_SEQ;

	rc = sys$connect(&rab);
	if (ERROR(rc)) return(rc);

	/*
	 *	Loop through the records in the UAF
	 */
	while(1) {
		/*
		 *	Get the record
		 */
		rc = sys$get(&rab);
		if (ERROR(rc)) break;
		add_user(&uaf,ulist);
		}
exit:
	/*
	 *	Done: close the file, release privileges and return status
	 */
	sys$disconnect(&rab);
	return(rc);
}


static list(u)
	struct user *u;
{
	struct token *t;

	while (u) {
		printf("User [%s,%s,%s]",u->real,u->phonetic,u->owner);
		print_tokens(u->token_list);
		printf("\n");
		u = u->next;
		}
}



static unsigned long free_database(delete)
     int delete;
{
	unsigned long rc;
	unsigned long inadr[2],Retadr[2];
	struct {int Size; char *Ptr;} gsdnam;
	struct user *ulist;
	int size;

	gsdnam.Ptr = Global_Section_Name;
	gsdnam.Size = strlen(Global_Section_Name);

#ifdef WPDEBUG
	    printf("reloading the database!\n");
#endif WPDEBUG

	if (delete) {
		rc = sys$dgblsc(SEC$M_SYSGBL, &gsdnam, 0);
		if (ERROR(rc)) {
			if (rc != SS$_NOSUCHSEC)
				exit(rc);
			}
		}
}


static uafopen(fab,modtime)
	struct FAB *fab;
	unsigned long *modtime;
{
	unsigned int old_privs[2],new_privs[2];
	struct XABDAT xabdat;
	unsigned long rc;
	unsigned long *qtmp;

	/*
	 *	Enable all privileges that we are authorized to have just to
	 *	enhance the possibility of accessing the SYSUAF file.
	 */
	new_privs[0] = -1; new_privs[1] = -1;
	sys$setprv(1,new_privs,0,old_privs);

	/* Initialize the File Access Block (FAB) */
	*fab = cc$rms_fab;
	fab->fab$b_fac |= FAB$M_GET;
	fab->fab$l_fna = UAFNAME;
	fab->fab$b_fns = strlen(UAFNAME);
	fab->fab$l_dna = DEFNAME;
	fab->fab$b_dns = strlen(DEFNAME);
	fab->fab$b_shr |= FAB$M_PUT|FAB$M_GET|FAB$M_DEL|FAB$M_UPD;
	fab->fab$b_rfm = FAB$C_VAR;
/*	fab->fab$b_acmodes |= PSL$C_EXEC<<FAB$V_LNM_MODE;  */
	xabdat = cc$rms_xabdat;
	fab->fab$l_xab = (char *)&xabdat;

	/* Open the file */
	rc = sys$open(fab);
	if (ERROR(rc))
		return(rc);

	/* Get the revision time. */
	qtmp = (unsigned long *)&xabdat.xab$q_rdt;
	modtime[0] = qtmp[0];
	modtime[1] = qtmp[1];

	/* restore the old privileges */
	sys$setprv(0,new_privs,0,0);
	sys$setprv(1,old_privs,0,0);

}



static unsigned long load_database(dbaddr)
     char **dbaddr;
{
	unsigned long rc,maprc;
	unsigned long Inadr[2],Retadr[2];
	struct {int Size; char *Ptr;} gsdnam;
	struct user *ulist=NULL;
	unsigned long *sectime,uaftime[2];
	int size;
	struct FAB fab;

	/* Assume the worst */
	*dbaddr = NULL;

	/* Map in the global section */
	gsdnam.Ptr = Global_Section_Name;
	gsdnam.Size = strlen(Global_Section_Name);
	Inadr[0] = Inadr[1] = 0;
	rc = sys$mgblsc(
			Inadr,
			Retadr,
			0,		/* access mode */
			SEC$M_EXPREG | SEC$M_SYSGBL,
			&gsdnam,	/* section name */
			0,		/* ident */
			0		/* relative page number */
			);		
	if (ERROR(rc) && (rc != SS$_NOSUCHSEC))
		return(rc);

	/* Save the virual address and call status for later */
	*dbaddr = (char *)Retadr[0];
	maprc = rc;

	/* Open the SYSUAF and get the last modification time. */
	rc = uafopen(&fab,uaftime);
	if (ERROR(rc))
		return(rc);
	sectime = (unsigned long *)Retadr[0];

#ifdef NOTDEF
#ifdef WPDEBUG
	if (!ERROR(maprc))
		printf("sec mod time = [%x,%x]\n",sectime[1],sectime[0]);
	printf("uaf mod time = [%x,%x]\n",uaftime[1],uaftime[0]);
#endif WPDEBUG
#endif NOTDEF

	if (maprc != SS$_NOSUCHSEC) 
/*		if (sectime[1] >= uaftime[1]) */
			goto done;

	/*
	 * build the database
	 */
#ifdef WPDEBUG
	printf("Building the database!\n");
#endif WPDEBUG
	ulist = NULL;
	rc = convert_uaf(&fab,&ulist);
	if (ERROR(rc) && (rc != RMS$_EOF))
		goto done;

#ifdef WPDEBUG
	list(ulist);
#endif WPDEBUG

	size = size_users(ulist);

#ifdef WPDEBUG
	printf("Size = %d\n",size);
#endif WPDEBUG
	/*
	 *	Make the global section
	 */

	/* Create the global section */
	size = ((size+512) >> 9);	/* convert to number of pages */
	rc = sys$crmpsc(Inadr,
			Retadr,
			0,
			(SEC$M_GBL | SEC$M_PERM | SEC$M_EXPREG | 
			 SEC$M_SYSGBL | SEC$M_PAGFIL),
			&gsdnam,
			0,		/* ident */
			0,		/* relpag */
			0,		/* Chan */
			size,		/* pagcnt */
			0,		/* vbn */
			0,		/* prot */
			0);		/* pfc */
	if (ERROR(rc))
		goto done;

	/* initialize the global section */
	dump_users(ulist,Retadr[0],uaftime);

	*dbaddr = (char *)Retadr[0];
	rc = SS$_NORMAL;

done:
	if (ulist)
		free_users(ulist);
	sys$close(&fab);
	return(rc);
}

struct mectx {
    char name[UAF$S_USERNAME+1];
    char *dbp;
};


int match_exact(
    	struct mectx **mep,
    	char *guess,
    	char *username,
    	int userlen,
    	char *owner,
    	int ownlen)
{
    char *real, *db;
    int size;

    if (*mep == NULL) {
    	*mep = malloc(sizeof(struct mectx));
    	if (*mep == NULL) return 0;
    	if (!$VMS_STATUS_SUCCESS(load_database(&((*mep)->dbp)))) {
    	    free(*mep);
    	    *mep = NULL;
    	    return 0;
    	}
    	(*mep)->dbp += 8;
    	strncpy((*mep)->name, guess, UAF$S_USERNAME);
    	upper((*mep)->name);
    }
    while (1) {
    	if (!(size = *(short *)((*mep)->dbp))) break;
    	real = (*mep)->dbp + 2;
    	if (strcmp(real,(*mep)->name) == 0) {
    	    char *ocp;
    	    ocp = real+strlen(real)+1;  /* to phonetic */
    	    ocp += strlen(ocp)+1;     /* to owner */
#ifdef WPDEBUG
			printf("Exact match! (%s, owner=%s)\n",real,ocp);
#endif WPDEBUG
    	    if (username) strncpy(username,real,userlen);
    	    if (owner) strncpy(owner,ocp,ownlen);
    	    (*mep)->dbp += size;
    	    return 1;
    	}
    	(*mep)->dbp += size;
    }
    free(*mep);
    *mep = NULL;
    return 0;
}

void match_exact_end(struct mectx **mep) {
    free(*mep);
    *mep = NULL;
    return;
}

struct mpuctx {
    char *pname;
    char *dbp;
};


int match_phonetic_user(
    	struct mpuctx **mpup,
    	char *guess,
    	char *username,
    	int  userlen,
    	char *owner,
    	int  ownlen) {
    
    char *pname;
    char *real,*phonetic;
    int size,count = 0;

    if (*mpup == NULL) {
    	*mpup = malloc(sizeof(struct mpuctx));
    	if (*mpup == NULL) return 0;
    	if (!$VMS_STATUS_SUCCESS(load_database(&((*mpup)->dbp)))) {
    	    free(*mpup);
    	    *mpup = NULL;
    	    return 0;
    	}
    	(*mpup)->pname = strdup((char *)CanonNick(guess));
    	(*mpup)->dbp += 8;
    }
    while (1) {
    	if (!(size = *(short *)((*mpup)->dbp))) break;
    	real = (*mpup)->dbp + 2;
    	phonetic = real + strlen(real) + 1;
    	if (strcmp(phonetic,(*mpup)->pname) == 0) {
    	    char *ocp = phonetic+strlen(phonetic)+1;
#ifdef WPDEBUG
			printf("Phonetic username match! (%s ==> %s, owner=%s)\n",
				guess,real,ocp);
#endif WPDEBUG
    	    if (username) strncpy(username, real, userlen);
    	    if (owner) strncpy(owner, ocp, ownlen);
    	    (*mpup)->dbp += size;
    	    return 1;
    	}
    	(*mpup)->dbp += size;
    }
    free(*mpup);
    *mpup = NULL;
    return 0;
}

void match_phonetic_user_end(struct mpuctx **mpup) {
    if ((*mpup)->pname) free((*mpup)->pname);
    free(*mpup);
    *mpup = NULL;
    return;
}

struct moctx {
    char *dbp;
    struct token *gtoks;
};

int match_owner(
    	struct moctx **mop,
    	char *guess,
    	char *username,
    	int userlen,
    	char *owner,
    	int ownlen) {

    struct token *pat;
    char *real,*ocp,*t;
    int size = 0;

    if (*mop == NULL) {
    	*mop = malloc(sizeof(struct moctx));
    	if (*mop == NULL) return 0;
    	(*mop)->gtoks = get_tokens(guess," .");
    	if ((*mop)->gtoks == NULL) {
    	    free(*mop);
    	    *mop = NULL;
    	    return 0;
    	}
    	if (!$VMS_STATUS_SUCCESS(load_database(&((*mop)->dbp)))) {
    	    free_tokens((*mop)->gtoks);
    	    free(*mop);
    	    *mop = NULL;
    	    return 0;
    	}
    	(*mop)->dbp += 8;
    }

    while (1) {
    	if (!(size = *(short *)((*mop)->dbp))) break;
    	real = (*mop)->dbp + 2;			/* point to USERNAME */
    	pat = (*mop)->gtoks;			/* reset pattern to start */
    	t = real + strlen(real) + 1;	    	/* skip to phonetic username */
    	ocp = t + strlen(t) + 1;    	    	/* skip to owner */
    	t = ocp + strlen(ocp) + 1;	    	/* skip to first token */
    	while (*t) {
    	    if (strcmp(t,pat->real) == 0) { 
    	    	pat = pat->next;
    	    	if (pat == NULL) {
#ifdef WPDEBUG
			    printf("Ownername match! (%s ==> %s, owner=%s)\n",
					guess,real,ocp);
#endif WPDEBUG
    	    	    if (username) strncpy(username,real,userlen);
    	    	    if (owner) strncpy(owner,ocp,ownlen);
    	    	    (*mop)->dbp += size;
    	    	    return 1;
    	    	} 
    	    }
    	    t += strlen(t) + 1;		/* skip to phonetic token */
    	    t += strlen(t) + 1;		/* skip to next token */
    	}
    	(*mop)->dbp += size;
    }

    free_tokens((*mop)->gtoks);
    free(*mop);
    *mop = NULL;

    return 0;

}

void match_owner_end(struct moctx **mop) {
    free_tokens((*mop)->gtoks);
    free(*mop);
    *mop = NULL;
    return;
}

struct mpoctx {
    char *dbp;
    struct token *gtoks;
};
    

int match_phonetic_owner(
    	struct mpoctx **mpop,
    	char *guess,
    	char *username,
    	int userlen,
    	char *owner,
    	int ownlen) {

    struct token *pat;
    char *real,*ocp,*t;
    char *pname;
    int size = 0;

    if (*mpop == NULL) {
    	*mpop = malloc(sizeof(struct mpoctx));
    	if (*mpop == NULL) return 0;
    	(*mpop)->gtoks = get_tokens(guess," .");
    	if ((*mpop)->gtoks == NULL) {
    	    free(*mpop);
    	    *mpop = NULL;
    	    return 0;
    	}
    	if (!$VMS_STATUS_SUCCESS(load_database(&((*mpop)->dbp)))) {
    	    free_tokens((*mpop)->gtoks);
    	    free(*mpop);
    	    *mpop = NULL;
    	    return 0;
    	}
    	(*mpop)->dbp += 8;
    }

    while (1) {
    	if (!(size = *(short *)((*mpop)->dbp))) break;
    	real = (*mpop)->dbp + 2;		/* point to USERNAME */
    	pat = (*mpop)->gtoks;			/* reset pattern to start */
    	t = real + strlen(real) + 1;	/* skip to phonetic username */
    	ocp = t + strlen(t) + 1;  	/* skip to owner */
	t = ocp + strlen(ocp) + 1;	/* skip to first token */
    	while (*t) {
    	    t += strlen(t) + 1;		/* skip to phonetic token */
    	    if (strcmp(t,pat->phonetic) == 0) {
    	    	pat = pat->next;
    	    	if (pat == NULL) {
#ifdef WPDEBUG
			    printf("Phonetic ownername match! (%s ==> %s, owner=%s)\n",
				    guess,real,ocp);
#endif WPDEBUG
    	    	    if (username) strncpy(username,real,userlen);
    	    	    if (owner) strncpy(owner,ocp,ownlen);
    	    	    (*mpop)->dbp += size;
    	    	    return 1;
    	    	}
    	    }
    	    t += strlen(t) + 1;
    	}
    	(*mpop)->dbp += size;
    }

    free_tokens((*mpop)->gtoks);
    free(*mpop);
    *mpop = NULL;

    return 0;

}

void match_phonetic_owner_end(struct mpoctx **mpop) {
    free_tokens((*mpop)->gtoks);
    free(*mpop);
    *mpop = NULL;
    return;
}

struct findctx {
    int flags;
    char *guess;
    void *ctx;
    int  level;
};


int find_user(
    	struct findctx **fp,
    	int  flags,
    	char *guess,
    	char *username,
    	int  userlen,
    	char *owner,
    	int  ownlen,
    	int  *match_type) {

    static int (*match_routine[])() = {
    	match_exact, match_owner, match_phonetic_user,
    	match_phonetic_owner};
    struct mpoctx *mpop;
    int i;

    if (*fp == NULL) {
    	*fp = malloc(sizeof(struct findctx));
    	if (*fp == NULL) return 0;
    	(*fp)->flags = (flags == 0 ? WP_M_ANY : flags);
    	(*fp)->guess = strdup(guess);
    	upper((*fp)->guess);
    	(*fp)->ctx = NULL;
    	(*fp)->level = 0;
    }

    while (1) {
    	if ((*fp)->ctx == NULL) {
    	    if ((*fp)->flags == 0) break;
    	    while (((*fp)->level < 4) & !((1<<(*fp)->level)&(*fp)->flags)) {
    	    	(*fp)->level += 1;
    	    }
    	    if ((*fp)->level > WP_V_PHONETIC_OWNER) break;
    	}
    	if ((*(match_routine[(*fp)->level]))(&((*fp)->ctx), (*fp)->guess,
    	    	    username, userlen, owner, ownlen)) {
    	    if (match_type) *match_type = (*fp)->level;
    	    (*fp)->flags = 0;
    	    return 1;
    	}
    	(*fp)->level += 1;
    }

    /*
     * Do we need to clean up when we're done?
     */
    if (!strcmp(guess,"-reload")) {
	    free_database(1);
    }


    free((*fp)->guess);
    free(*fp);
    *fp = NULL;
    return 0;
}

void find_user_end(struct findctx **fp) {
    static void (*end_routine[])() = {
    	match_exact_end, match_owner_end,
    	match_phonetic_user_end,match_phonetic_owner_end};

    if ((*fp)->ctx) (*(end_routine[(*fp)->level]))(&((*fp)->ctx));
    free((*fp)->guess);
    free(*fp);
    *fp = NULL;

}
