/* if.c - conditional menu commands
 */

#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/utsname.h>
#include "menucmd.h"
#include "hook.h"


typedef int (*menuif__t) (char **);

struct menuif__s {
	char *name;
	menuif__t func;
};

extern char *current_user;

int menu_if__skipdepth;
int menu_if__depth;


/* Return 1 if any of the <name> <value> pairs match. A value of "*" matches
 * anything.
 */
int menu__if__environment(char **a)
{
	char *var;
	char *val;
	char *e;
	int r = 0;

	while (*a) {
		*a = strtok(0, " \011");
		var = *a;
		if (!var)
			return (r);
		if ((!strcmp(var, "&&")) || (!strcmp(var, "||")))
			return (r);
		*a = strtok(0, " \011");
		val = *a;
		if (!val)
			return (r);
		if ((!strcmp(val, "&&")) || (!strcmp(val, "||")))
			return (r);
		e = getenv(var);
		if (!e)
			continue;
		if (!strcmp(val, "*")) {
			r = 1;
		} else {
			if (!strcmp(val, e))
				r = 1;
		}
	}

	return (r);
}


/* Return 1 if <file> exists.
 */
int menu__if__exists(char **a)
{
	struct stat sb;
	char *file;

	*a = strtok(0, " \011");
	file = *a;
	if (!file)
		return (0);
	if ((!strcmp(file, "&&")) || (!strcmp(file, "||")))
		return (0);

	if (file[0] == '/') {
		strncpy(menu_temp, file, 1020);
	} else {
		strncpy(menu_temp, menu_path, 1000);
		strcat(menu_temp, "/");
		strncat(menu_temp, file, 1000 - strlen(menu_temp));
	}

	if (stat(menu_temp, &sb))
		return (0);

	return (1);
}


/* Return 1 if <file> can be linked to and the ACL permits it.
 */
int menu__if__aclok(char **a)
{
	char *file;

	*a = strtok(0, " \011");
	file = *a;
	if (!file)
		return (0);
	if ((!strcmp(file, "&&")) || (!strcmp(file, "||")))
		return (0);

	if (file[0] == '/') {
		strncpy(menu_temp, file, 1020);
	} else {
		strncpy(menu_temp, menu_path, 1000);
		strcat(menu_temp, "/");
		strncat(menu_temp, file, 1000 - strlen(menu_temp));
	}

	if (bbs_hook(HOOK_ACL_CHECK, menu_temp, 0))
		return (0);

	return (1);
}


/* Return 1 if the current user is any of <user>s.
 */
int menu__if__user(char **a)
{
	char *usr;
	char *p;
	int r = 0;

	while (*a) {
		*a = strtok(0, " \011");
		usr = *a;
		if (!usr)
			return (r);
		if ((!strcmp(usr, "&&")) || (!strcmp(usr, "||")))
			return (r);
		p = strchr(usr, '/');
		if (p) {
			if (bbs_hook(HOOK_USERCHECK, usr, current_user))
				r = 1;
		} else {
			if (!strcmp(usr, current_user))
				r = 1;
		}
	}

	return (r);
}


/* Return 1 if the current user is an external user.
 */
int menu__if__external(char **a)
{
	return (bbs_hook(HOOK_IS_EXTERNAL, current_user, 0));
}


/* Return 1 if the current user is a guest user.
 */
int menu__if__guest(char **a)
{
	return (bbs_hook(HOOK_IS_GUEST, current_user, 0));
}


/* Return 1 if the client is running on any of the <hostname>s. The full
 * hostname must match. A value of "*" will match anything.
 */
int menu__if__hostname(char **a)
{
	char *current_host;
	char *host;
	int r = 0;
	struct utsname b;

	uname(&b);
	current_host = b.nodename;	    /* KLUDGE ALERT (need domainname) */

	while (*a) {
		*a = strtok(0, " \011");
		host = *a;
		if (!host)
			return (r);
		if ((!strcmp(host, "&&")) || (!strcmp(host, "||")))
			return (r);
		if (!strcmp(host, "*"))
			r = 1;
		if (!strcmp(host, current_host))
			r = 1;
	}

	return (r);
}


/* Return 1 if the user is logged in from any of the <hostname>s. The full
 * hostname must match. A value of "*" will match anything.
 */
int menu__if__location(char **a)
{
	char *current_host;
	char *host;
	int r = 0;

	current_host = "";
	bbs_hook(HOOK_LOCATION, &current_host, 0);

	while (*a) {
		*a = strtok(0, " \011");
		host = *a;
		if (!host)
			return (r);
		if ((!strcmp(host, "&&")) || (!strcmp(host, "||")))
			return (r);
		if (!strcmp(host, "*"))
			r = 1;
		if (!strcmp(host, current_host))
			r = 1;
	}

	return (r);
}


/* Return 1 if the user's email address stub ends in any of the <value>s.
 * A value of "*" will match anything.
 */
int menu__if__site(char **a)
{
	char *host;
	int r = 0;

	while (*a) {
		*a = strtok(0, " \011");
		host = *a;
		if (!host)
			return (r);
		if ((!strcmp(host, "&&")) || (!strcmp(host, "||")))
			return (r);
		if (!strcmp(host, "*"))
			r = 1;
		if (bbs_hook(HOOK_SITECHECK, host, 0))
			r = 1;
	}

	return (r);
}


/* Return 1 if the user's terminal type matches any of the <value>s given.
 * A value of "*" will match anything.
 */
int menu__if__terminal(char **a)
{
	char *t;
	char *term;
	int r = 0;

	t = getenv("TERM");
	if (!t)
		t = "";

	while (*a) {
		*a = strtok(0, " \011");
		term = *a;
		if (!term)
			return (r);
		if ((!strcmp(term, "&&")) || (!strcmp(term, "||")))
			return (r);
		if (!strcmp(term, "*"))
			r = 1;
		if (!strcmp(term, t))
			r = 1;
	}

	return (r);
}


/* Return 1 if the user's terminal (of the form "ttyp7") matches any of the
 * <value>s given. A value of "*" will match anything.
 */
int menu__if__tty(char **a)
{
	char *t;
	char *term;
	char *p;
	int r = 0;

	t = ttyname(STDIN_FILENO);
	if (!t)
		t = "";
	p = strrchr(t, '/');		    /* strip directories from pathname */
	if (p)
		t = p + 1;

	while (*a) {
		*a = strtok(0, " \011");
		term = *a;
		if (!term)
			return (r);
		if ((!strcmp(term, "&&")) || (!strcmp(term, "||")))
			return (r);
		if (!strcmp(term, "*"))
			r = 1;
		if (!strcmp(term, t))
			r = 1;
	}

	return (r);
}


/* IF [NOT] <condition> [&& ... | || ...]
 *
 * ENVIRONMENT <name> <value> [...]
 * EXISTS <file>
 * EXTERNAL
 * GUEST
 * HOSTNAME <host> [...]
 * LOCATION <host> [...]
 * SITE <address> [...]
 * TERMINAL <type> [...]
 * TTY <tty> [...]
 * USER <user> [...]
 * ACLOK <file>
 */
int menu__if(menu_cmddat_t d)
{
	static struct menuif__s cmds[] = {
		{"ACLOK", menu__if__aclok},
		{"ENVIRONMENT", menu__if__environment},
		{"EXISTS", menu__if__exists},
		{"EXTERNAL", menu__if__external},
		{"GUEST", menu__if__guest},
		{"HOSTNAME", menu__if__hostname},
		{"LOCATION", menu__if__location},
		{"SITE", menu__if__site},
		{"TERMINAL", menu__if__terminal},
		{"TTY", menu__if__tty},
		{"USER", menu__if__user},
		{0, 0}
	};
	char *a;
	int c, i, l, r, v;
	int first = 1;

	if (!d->special)
		menu_if__depth++;

	if (menu_f_skip)
		return (1);
	if (!d->buf)
		return (0);

	a = strtok(d->buf, " \011");

	l = 1;
	r = 0;
	v = 0;

	while (a) {
		if (!strcasecmp(a, "NOT")) {
			a = strtok(0, " \011");
			v = 1;
			continue;
		}
		for (i = 0;
		     (cmds[i].name != 0) &&
		     (strncasecmp(cmds[i].name, a, strlen(a)) != 0); i++);
		if (cmds[i].func) {
			c = cmds[i].func(&a);
			if (v)
				c = 1 - c;
			if (l)
				r |= c;
			else {
				if (first)
					r = c;
				else
					r &= c;
			}
			first = 0;
		}
		v = 0;
		if (a) {
			if (!strcmp(a, "&&"))
				l = 0;
			else if (!strcmp(a, "||"))
				l = 1;
			a = strtok(0, " \011");
		}
	}

	if (!d->special) {
		menu_if__skipdepth = menu_if__depth;
		if (!r)
			menu_f_skip = 1;
	}

	return (r);
}


/* ELSE
 */
int menu__else(menu_cmddat_t d)
{
	if ((menu_f_skip) && (menu_if__depth > menu_if__skipdepth))
		return (1);
	menu_if__skipdepth = menu_if__depth;
	menu_f_skip = 1 - menu_f_skip;
	return (1);
}


/* ENDIF
 */
int menu__endif(menu_cmddat_t d)
{
	if (menu_if__depth == menu_if__skipdepth)
		menu_f_skip = 0;
	menu_if__depth--;
	if (menu_if__depth < -1)
		menu_if__depth = -1;
	if (menu_if__skipdepth > menu_if__depth)
		menu_if__skipdepth = menu_if__depth;
	return (1);
}

/* EOF */
