# include	<ingres.h>
# include	<aux.h>
# include	<catalog.h>
# include	<access.h>
# include	<tree.h>
# include	<symbol.h>
# include	<lock.h>
# include	<pv.h>
# include	<func.h>
# include	"qrymod.h"
# include	<sccs.h>
# include	<errors.h>

SCCSID(@(#)d_prot.c	8.3	2/8/85)



/*
**  D_PROT -- define protection constraint
**
**	A protection constraint as partially defined by the last tree
**	defined by d_tree is defined.
**
**	The stuff that comes through the pipe as parameters is complex.
**	It comes as a sequence of strings:
**	 # The operation set, already encoded in the parser into a
**	   bit map.  If the PRO_RETR permission is set, the PRO_TEST
**	   and PRO_AGGR permissions will also be set.
**	 # The relation name.
**	 # The relation owner.
**	 # The user name.  This must be a user name as specified in
**	   the 'users' file, or the keyword 'all', meaning all users.
**	 # The terminal id.  Must be a string of the form 'ttyx' or
**	   the keyword 'all'.
**	 # The starting time of day, as minutes-since-midnight.
**	 # The ending time of day.
**	 # The starting day-of-week, with 0 = Sunday.
**	 # The ending dow.
**
**	The domain reference set is build automatically from the
**	target list of the tree.  Thus, the target list must exist,
**	but it is not inserted into the tree.  The target list must
**	be a flat sequence of RESDOM nodes with VAR nodes hanging
**	of the rhs; also, the VAR nodes must all be for Qt.qt_resvar.
**	If there is no target list on the tree, the set of all var-
**	iables is assumed.
**
**	The relstat field in the relation relation is updated to
**	reflect any changes.
**
**	It only makes sense for the DBA to execute this command.
**
**	If there is one of the special cases
**		permit all to all
**		permit retrieve to all
**	it is caught, and the effect is achieved by diddling
**	relstat bits instead of inserting into the protect catalog.
**
**	Parameters:
**		none
**
**	Returns:
**		none
**
**	Side Effects:
**		Activity in 'protect' and 'relation' catalogs.
**
**	Trace Flags:
**		59
*/

extern struct admin	Admin;
extern DESC		Prodes;
extern DESC		Reldes;

extern	void	null_fn();
extern          d_prot();
extern short	tTqm[80];

struct fn_def	DefProFn =
{
	"DPROT",
	d_prot,
	null_fn,
	null_fn,
	NULL,
	0,
	tTqm,
	80,
	'Q',
	0
};

d_prot(pc, pv)
int	pc;
PARM	*pv;
{
	struct protect	protup;
	struct tup_id	protid;
	struct protect	prokey;
	struct protect	proxtup;
	char		buf[30];
	char		ubuf[MAXLINE + 1];
	register int	i;
	auto short	ix;
	int		treeid;
	register QTREE	*t;
	QTREE		*root;
	register char	*p;
	struct relation	reltup;
	struct relation	relkey;
	struct tup_id	reltid;
	int		relstat;
	int		all_pro;

	/*
	**  Fill in the protection tuple with the information
	**	from the parser, validating as we go.
	**
	**	Also, determine if we have a PERMIT xx to ALL
	**	with no further qualification case.  The variable
	**	'all_pro' is set to reflect this.
	*/

	clr_tuple(&Prodes, &protup);
	all_pro = TRUE;

	/* read operation set */
	if (pv->pv_type != PV_INT)
		syserr("d_prot: opset");
	protup.proopset = pv->pv_val.pv_int;
	if ((protup.proopset & PRO_RETR) != 0)
		protup.proopset |= PRO_TEST | PRO_AGGR;
	pv++;

	/* read relation name */
	if (pv->pv_type != PV_STR)
		syserr("d_prot: relid");
	pmove(pv->pv_val.pv_str, protup.prorelid, MAXNAME, ' ');
	pv++;

	/* read relation owner */
	if (pv->pv_type != PV_STR)
		syserr("d_prot: relid");
	bmove(pv->pv_val.pv_str, protup.prorelown, 2);
	pv++;

	/* read user name */
	if (pv->pv_type != PV_STR)
		syserr("d_prot: user");
	if (sequal(pv->pv_val.pv_str, "all"))
		bmove("  ", protup.prouser, 2);
	else
	{
		/* look up user in 'users' file */
		if (getnuser(pv->pv_val.pv_str, ubuf))
			qmerror(BADUSRNAME, -1, Qt.qt_resvar, pv->pv_val.pv_str, 0);
		for (p = ubuf; *p != ':' && *p != 0; p++)
			continue;
		bmove(++p, protup.prouser, 2);
		if (p[0] == ':' || p[1] == ':' || p[2] != ':')
			syserr("d_prot: users %s", ubuf);
		all_pro = FALSE;
	}
	pv++;

	/* read terminal id */
	if (pv->pv_type != PV_STR)
		syserr("d_prot: user");
	if (sequal(pv->pv_val.pv_str, "all"))
		pmove("", protup.proterm, sizeof protup.proterm, ' ');
	else
	{
		pmove(pv->pv_val.pv_str, protup.proterm, sizeof protup.proterm, ' ');
		if (!isttyname(pv->pv_val.pv_str))
			qmerror(BADTERM, -1, Qt.qt_resvar, pv->pv_val.pv_str, 0);
		all_pro = FALSE;
	}
	pv++;

	/* read starting time of day */
	if (pv->pv_type != PV_INT)
		syserr("d_prot: btod");
	protup.protodbgn = pv->pv_val.pv_int;
	if (pv->pv_val.pv_int > 0)
		all_pro = FALSE;
	pv++;
	
	/* read ending time of day */
	if (pv->pv_type != PV_INT)
		syserr("d_prot: etod");
	protup.protodend = pv->pv_val.pv_int;
	if (pv->pv_val.pv_int < 24 * 60 - 1)
		all_pro = FALSE;
	pv++;

	/* read beginning day of week */
	if (pv->pv_type != PV_STR)
		syserr("d_prot: bdow");
	i = cvt_dow(pv->pv_val.pv_str);
	if (i < 0)
		qmerror(BADDOW, -1, Qt.qt_resvar, pv->pv_val.pv_str, 0);	/* bad dow */
	protup.prodowbgn = i;
	if (i > 0)
		all_pro = FALSE;
	pv++;

	/* read ending day of week */
	if (pv->pv_type != PV_STR)
		syserr("d_prot: edow");
	i = cvt_dow(pv->pv_val.pv_str);
	if (i < 0)
		qmerror(BADDOW, -1, Qt.qt_resvar, pv->pv_val.pv_str, 0);	/* bad dow */
	protup.prodowend = i;
	if (i < 6)
		all_pro = FALSE;
	pv++;

	/*
	**  Check for valid tree:
	**	There must be a tree defined, and all variables
	**	referenced must be owned by the current user; this
	**	is because you could otherwise get at data by
	**	mentioning it in a permit statement; see protect.c
	**	for a better explanation of this.
	*/

	if (pv->pv_type != PV_QTREE)
		syserr("d_prot: tree");
	root = (QTREE *) pv->pv_val.pv_qtree;
	pv++;

	for (i = 0; i < MAXVAR + 1; i++)
	{
		if (Qt.qt_rangev[i].rngvdesc == NULL)
			continue;
		if (!bequal(Qt.qt_rangev[i].rngvdesc->reldum.relowner, Usercode, UCODE_SZ))
			qmerror(OWNEDNOT, -1, i, 0);
	}

	/* test for dba */
	if (!bequal(Usercode, Admin.adhdr.adowner, UCODE_SZ))
		qmerror(NOTDBA, -1, Qt.qt_resvar, 0);
	
	/* get domain reference set from target list */
	/* (also, find the TREE node) */
	t = root->left;
	if (t->sym.type == TREE)
	{
		for (i = 0; i < 8; i++)
			protup.prodomset[i] = -1;
	}
	else
	{
		for (i = 0; i < 8; i++)
			protup.prodomset[i] = 0;
		for (; t->sym.type != TREE; t = t->left)
		{
			if (t->right->sym.type != VAR ||
			    t->sym.type != RESDOM ||
			    t->right->sym.value.sym_var.varno != Qt.qt_resvar)
				syserr("d_prot: garbage tree");
			lsetbit(t->right->sym.value.sym_var.attno, protup.prodomset);
		}
		all_pro = FALSE;
	}

	/* trim off the target list, since it isn't used again */
	root->left = t;

	/*
	**  Check out the target relation.
	**	We first save the varno of the relation which is
	**	getting the permit stuff.  Also, we check to see
	**	that the relation mentioned is a base relation,
	**	and not a view, since that tuple would never do
	**	anything anyway.  Finally, we clear the Qt.qt_resvar
	**	so that it does not get output to the tree catalog.
	**	This would result in a 'syserr' when we tried to
	**	read it.
	*/

	protup.proresvar = Qt.qt_resvar;
#	ifdef xQTR3
	if (Qt.qt_resvar < 0)
		syserr("d_prot: Rv %d", Qt.qt_resvar);
#	endif
	if (bitset(S_VIEW, Qt.qt_rangev[Qt.qt_resvar].rngvdesc->reldum.relstat))
		qmerror(NOTREALREL, -1, Qt.qt_resvar, 0);	/* is a view */

	/* clear the (unused) Qt.qt_qmode */
#	ifdef xQTR3
	if (Qt.qt_qmode != mdPROT)
		syserr("d_prot: Qt.qt_qmode %d", Qt.qt_qmode);
#	endif
	Qt.qt_qmode = -1;

	/*
	**  Check for PERMIT xx to ALL case.
	**	The relstat bits will be adjusted as necessary
	**	to reflect these special cases.
	**
	**	This is actually a little tricky, since we cannot
	**	afford to turn off any permissions.  If we already
	**	have some form of PERMIT xx to ALL access, we must
	**	leave it.
	*/

	relstat = Qt.qt_rangev[Qt.qt_resvar].rngvdesc->reldum.relstat;
	if (all_pro && (protup.proopset & PRO_RETR) != 0)
	{
		if (protup.proopset == -1)
			relstat &= ~S_PROTALL;
		else
		{
			relstat &= ~S_PROTRET;
			if ((protup.proopset & ~(PRO_RETR|PRO_AGGR|PRO_TEST)) != 0)
			{
				/* some special case: still insert prot tuple */
				all_pro = FALSE;
			}
		}
	}
	else
		all_pro = FALSE;

	/* see if we are adding any tuples */
	if (!all_pro)
		relstat |= S_PROTUPS;
	
	/*
	**  Change relstat field in relation catalog if changed
	*/

	if (relstat != Qt.qt_rangev[Qt.qt_resvar].rngvdesc->reldum.relstat)
	{
		opencatalog("relation", OR_WRITE);
		setkey(&Reldes, &relkey, Qt.qt_rangev[Qt.qt_resvar].rngvdesc->reldum.relid, RELID);
		setkey(&Reldes, &relkey, Qt.qt_rangev[Qt.qt_resvar].rngvdesc->reldum.relowner, RELOWNER);
		i = getequal(&Reldes, &relkey, &reltup, &reltid);
		if (i != 0)
			syserr("d_prot: geteq %d", i);
		reltup.relstat = relstat;
		i = replace(&Reldes, &reltid, &reltup, FALSE);
		if (i != 0)
			syserr("d_prot: repl %d", i);
		if (noclose(&Reldes) != 0)
			syserr("d_prot: noclose(rel)");
	}

	Qt.qt_resvar = -1;

	if (!all_pro)
	{
		/*
		**  Output the created tuple to the protection catalog
		**  after making other internal adjustments and deter-
		**  mining a unique sequence number (with the protect
		**  catalog locked).
		*/

		if (root->right->sym.type != QLEND)
			protup.protree = puttree(root, protup.prorelid, protup.prorelown, mdPROT);
		else
			protup.protree = -1;

		/* compute unique permission id */
		opencatalog("protect", OR_WRITE);
		setrll(A_SLP, Prodes.reltid.ltid, M_EXCL);
		setkey(&Prodes, &prokey, protup.prorelid, PRORELID);
		setkey(&Prodes, &prokey, protup.prorelown, PRORELOWN);
		for (ix = 2; ; ix++)
		{
			setkey(&Prodes, &prokey, &ix, PROPERMID);
			i = getequal(&Prodes, &prokey, &proxtup, &protid);
			if (i < 0)
				syserr("d_prot: geteq");
			else if (i > 0)
				break;
		}
		protup.propermid = ix;

		/* do actual insert */
		i = insert(&Prodes, &protid, &protup, FALSE);
		if (i < 0)
			syserr("d_prot: insert");
		if (noclose(&Prodes) != 0)
			syserr("d_prot: noclose(pro)");
		
		/* clear the lock */
		unlrl(Prodes.reltid.ltid);
	}
}
/*
**  CVT_DOW -- convert day of week
**
**	Converts the day of the week from string form to a number.
**
**	Parameters:
**		sdow -- dow in string form.
**
**	Returns:
**		0 -> 6 -- the encoded day of the week.
**		-1 -- error.
**
**	Side Effects:
**		none
**
**	Defines:
**		Dowlist -- a mapping from day of week to number.
**		cvt_dow
**
**	Called By:
**		d_prot
*/

struct downame
{
	char	*dow_name;
	int	dow_num;
};

struct downame	Dowlist[] =
{
	"sun",		0,
	"sunday",	0,
	"mon",		1,
	"monday",	1,
	"tue",		2,
	"tues",		2,
	"tuesday",	2,
	"wed",		3,
	"wednesday",	3,
	"thu",		4,
	"thurs",	4,
	"thursday",	4,
	"fri",		5,
	"friday",	5,
	"sat",		6,
	"saturday",	6,
	NULL
};

cvt_dow(sdow)
char	*sdow;
{
	register struct downame	*d;
	register char		*s;

	s = sdow;

	for (d = Dowlist; d->dow_name != NULL; d++)
		if (sequal(d->dow_name, s))
			return (d->dow_num);
	return (-1);
}
/*
**  ISTTYNAME -- "is a legal terminal name" predicate
**
**	Returns TRUE if the argument is a legal terminal name,
**	otherwise FALSE.
**
**	It may make sense to have this routine check if the given
**	file name really exists.
**
**	WARNING:
**		This routine may be installation-dependent!
**
**	Parameters:
**		n -- the name to check.
**
**	Returns:
**		TRUE -- n is a legal tty name at this installation.
**		FALSE -- otherwise.
**
**	Side Effects:
**		none
**
**	History:
**		8/1/79 (eric) -- written.
*/


isttyname(n)
register char	*n;
{
	return (sequal(n, "console") || bequal(n, "tty", 3));
}
