/* password.c */
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mp.h>
#include <libsec.h>
#include "SConn.h"
#include "secstore.h"

static Biobuf*
openPW(char *id, int mode)
{
	Biobuf *b;
	int nfn = strlen(SECSTORE_DIR)+strlen(id)+20;
	char *fn = emalloc(nfn);

	snprint(fn, nfn, "%s/who/%s", SECSTORE_DIR, id);
	b = Bopen(fn, mode);
	free(fn);
	return b;
}

static ulong
mtimePW(char *id)
{
	Dir *d;
	int nfn = strlen(SECSTORE_DIR)+strlen(id)+20;
	char *fn = emalloc(nfn);
	ulong mt;

	snprint(fn, nfn, "%s/who/%s", SECSTORE_DIR, id);
	d = dirstat(fn);
	free(fn);
	mt = d->mtime;
	free(d);
	return mt;
}

PW *
getPW(char *id, int dead_or_alive)
{
	uint now = time(0);
	Biobuf *bin;
	PW *pw;
	char *f1, *f2; // fields 1, 2 = attribute, value

	if((bin = openPW(id, OREAD)) == 0){
		id = "FICTITIOUS";
		if((bin = openPW(id, OREAD)) == 0){
			werrstr("account does not exist");
			return nil;
		}
	}
	pw = emalloc(sizeof(*pw));
	pw->id = estrdup(id);
	pw->status |= Enabled;
	while( (f1 = Brdline(bin, '\n')) != 0){
		f1[Blinelen(bin)-1] = 0;
		for(f2 = f1; *f2 && (*f2!=' ') && (*f2!='\t'); f2++){}
		if(*f2)
			for(*f2++ = 0; *f2 && (*f2==' ' || *f2=='\t'); f2++){}
		if(strcmp(f1, "exp") == 0){
			pw->expire = strtoul(f2, 0, 10);
		}else if(strcmp(f1, "DISABLED") == 0){
			pw->status &= ~Enabled;
		}else if(strcmp(f1, "STA") == 0){
			pw->status |= STA;
		}else if(strcmp(f1, "failed") == 0){
			pw->failed = strtoul(f2, 0, 10);
		}else if(strcmp(f1, "other") == 0){
			pw->other = estrdup(f2);
		}else if(strcmp(f1, "PAK-Hi") == 0){
			pw->Hi = strtomp(f2, nil, 64, nil);
		}
	}
	Bterm(bin);
	if(dead_or_alive)
		return pw;  // return PW entry for editing, whether currently valid or not
	if(pw->expire <= now){
		werrstr("account expired");
		freePW(pw);
		return nil;
	}
	if((pw->status & Enabled) == 0){
		werrstr("account disabled");
		freePW(pw);
		return nil;
	}
	if(pw->failed < 10)
		return pw;  // success
	if(now < mtimePW(id)+300){
		werrstr("too many failures; try again in five minutes");
		freePW(pw);
		return nil;
	}
	pw->failed = 0;
	putPW(pw);  // reset failed-login-counter after five minutes
	return pw;
}

int
putPW(PW *pw)
{
	Biobuf *bout;
	char *hexHi;

	if((bout = openPW(pw->id, OWRITE|OTRUNC)) ==0){
		werrstr("can't open PW file");
		return -1;
	}
	Bprint(bout, "exp	%lud\n", pw->expire);
	if(!(pw->status & Enabled))
		Bprint(bout, "DISABLED\n");
	if(pw->status & STA)
		Bprint(bout, "STA\n");
	if(pw->failed)
		Bprint(bout, "failed\t%d\n", pw->failed);
	if(pw->other)
		Bprint(bout,"other\t%s\n", pw->other);
	hexHi = mptoa(pw->Hi, 64, nil, 0);
	Bprint(bout, "PAK-Hi\t%s\n", hexHi);
	free(hexHi);
	return 0;
}

void
freePW(PW *pw)
{
	if(pw == nil)
		return;
	free(pw->id);
	free(pw->other);
	mpfree(pw->Hi);
	free(pw);
}