#include <u.h>
#include <lib9.h>
#include <bio.h>
#include <auth.h>
#include <authsrv.h>
#include "authcmdlib.h"

/* working directory */
Dir	*dirbuf;
int32_t	ndirbuf = 0;

int debug;

int32_t	readdirect(int);
void	douser(Fs*, char*);
void	dodir(Fs*);
int	mail(Fs*, char*, char*, int32_t);
int	mailin(Fs*, char*, int32_t, char*, char*);
void	complain(char*, ...);
int32_t	readnumfile(char*);
void	writenumfile(char*, int32_t);

void
usage(void)
{
	fprint(2, "usage: %s [-n] [-p]\n", argv0);
	exits("usage");
}

void
main(int argc, char **argv)
{
	int which;

	which = 0;
	ARGBEGIN{
	case 'p':
		which |= Plan9;
		break;
	case 'n':
		which |= Securenet;
		break;
	case 'd':
		debug++;
		break;
	default:
		usage();
	}ARGEND
	argv0 = "warning";

	if(!which)
		which |= Plan9 | Securenet;
	if(which & Plan9)
		dodir(&fs[Plan9]);
	if(which & Securenet)
		dodir(&fs[Securenet]);
}

void
dodir(Fs *f)
{
	int nfiles;
	int i, fd;

	if(chdir(f->keys) < 0){
		complain("can't chdir to %s: %r", f->keys);
		return;
	}
 	fd = open(".", OREAD);
	if(fd < 0){
		complain("can't open %s: %r\n", f->keys);
		return;
	}
	nfiles = dirreadall(fd, &dirbuf);
	close(fd);
	for(i = 0; i < nfiles; i++)
		douser(f, dirbuf[i].name);
}

/*
 *  check for expiration
 */
void
douser(Fs *f, char *user)
{
	int n, nwarn;
	char buf[128];
	int32_t rcvrs, et, now;
	char *l;

	sprint(buf, "%s/expire", user);
	et = readnumfile(buf);
	now = time(0);

	/* start warning 2 weeks ahead of time */
	if(et <= now || et > now+14*24*60*60)
		return;

	sprint(buf, "%s/warnings", user);
	nwarn = readnumfile(buf);
	if(et <= now+14*24*60*60 && et > now+7*24*60*60){
		/* one warning 2 weeks before expiration */
		if(nwarn > 0)
			return;
		nwarn = 1;
	} else {
		/* one warning 1 week before expiration */
		if(nwarn > 1)
			return;
		nwarn = 2;
	}

	/*
	 *  if we can't open the who file, just mail to the user and hope
 	 *  for it makes it.
	 */
	if(f->b){
		if(Bseek(f->b, 0, 0) < 0){
			Bterm(f->b);
			f->b = 0;
		}
	}
	if(f->b == 0){
		f->b = Bopen(f->who, OREAD);
		if(f->b == 0){
			if(mail(f, user, user, et) > 0)
				writenumfile(buf, nwarn);
			return;
		}
	}

	/*
	 *  look for matches in the who file and mail to every address on
	 *  matching lines
	 */
	rcvrs = 0;
	while(l = Brdline(f->b, '\n')){
		n = strlen(user);
		if(strncmp(l, user, n) == 0 && (l[n] == ' ' || l[n] == '\t'))
			rcvrs += mailin(f, user, et, l, l+Blinelen(f->b));
	}

	/*
	 *  if no matches, try the user directly
	 */
	if(rcvrs == 0)
		rcvrs = mail(f, user, user, et);
	rcvrs += mail(f, "netkeys", user, et);
	if(rcvrs)
		writenumfile(buf, nwarn);
}

/*
 *  anything in <>'s is an address
 */
int
mailin(Fs *f, char *user, int32_t et, char *l, char *e)
{
	int n;
	int rcvrs;
	char *p;
	char addr[256];

	p = 0;
	rcvrs = 0;
	while(l < e){
		switch(*l){
		case '<':
			p = l + 1;
			break;
		case '>':
			if(p == 0)
				break;
			n = l - p;
			if(n > 0 && n <= sizeof(addr) - 2){
				memmove(addr, p, n);
				addr[n] = 0;
				rcvrs += mail(f, addr, user, et);
			}
			p = 0;
			break;
		}
		l++;
	}
	return rcvrs;
}

/*
 *  send mail
 */
int
mail(Fs *f, char *rcvr, char *user, int32_t et)
{
	int pid, i, fd;
	int pfd[2];
	char *ct, *p;
	Waitmsg *w;
	char buf[128];

	if(pipe(pfd) < 0){
		complain("out of pipes: %r");
		return 0;
	}

	switch(pid = fork()){
	case -1:
		complain("can't fork: %r");
		return 0;
	case 0:
		break;
	default:
		if(debug)
			fprint(2, "started %d\n", pid);
		close(pfd[0]);
		ct = ctime(et);
		p = strchr(ct, '\n');
		*p = '.';
		fprint(pfd[1], "User '%s's %s expires on %s\n", user, f->msg, ct);
		if(f != fs)
			fprint(pfd[1], "If you wish to renew contact your local administrator.\n");
		p = strrchr(f->keys, '/');
		if(p)
			p++;
		else
			p = f->keys;
		sprint(buf, "/adm/warn.%s", p);
		fd = open(buf, OREAD);
		if(fd >= 0){
			while((i = read(fd, buf, sizeof(buf))) > 0)
				write(pfd[1], buf, i);
			close(fd);
		}
		close(pfd[1]);

		/* wait for warning to be mailed */
		for(;;){
			w = wait();
			if(w == nil)
				break;
			if(w->pid == pid){
				if(debug)
					fprint(2, "%d terminated: %s\n", pid, w->msg);
				if(w->msg[0] == 0){
					free(w);
					break;
				}else{
					free(w);
					return 0;
				}
			}else
				free(w);
		}
		return 1;
	}

	/* get out of the current namespace */
	newns("none", 0);

	dup(pfd[0], 0);
	close(pfd[0]);
	close(pfd[1]);
	putenv("upasname", "netkeys");
	if(debug){
		print("\nto %s\n", rcvr);
		execl("/bin/cat", "cat", nil);
	}
	execl("/bin/upas/send", "send", "-r", rcvr, nil);

	/* just in case */
	sysfatal("can't exec send: %r");

	return 0;		/* for compiler */
}

void
complain(char *fmt, ...)
{
	char buf[8192], *s;
	va_list arg;

	s = buf;
	s += sprint(s, "%s: ", argv0);
	va_start(arg, fmt);
	s = vseprint(s, buf + sizeof(buf) / sizeof(*buf), fmt, arg);
	va_end(arg);
	*s++ = '\n';
	write(2, buf, s - buf);
}

int32_t
readnumfile(char *file)
{
	int fd, n;
	char buf[64];

	fd = open(file, OREAD);
	if(fd < 0){
		complain("can't open %s: %r", file);
		return 0;
	}
	n = read(fd, buf, sizeof(buf)-1);
	close(fd);
	if(n < 0){
		complain("can't read %s: %r", file);
		return 0;
	}
	buf[n] = 0;
	return atol(buf);
}

void
writenumfile(char *file, int32_t num)
{
	int fd;

	fd = open(file, OWRITE);
	if(fd < 0){
		complain("can't open %s: %r", file);
		return;
	}
	fprint(fd, "%ld", num);
	close(fd);
}
