code: purgatorio

ref: 51bcc63cc6bce3f9dab27cf6bd7155512b82c8fa
dir: /appl/spree/lib/allow.b/

View raw version
implement Allow;
include "sys.m";
	sys: Sys;
include "draw.m";
include "sets.m";
	sets: Sets;
	Set, set, None: import sets;
include "../spree.m";
	spree: Spree;
	Attributes, Range, Object, Clique, Member, rand: import spree;
include "allow.m";

Action: adt {
	tag:		int;
	member:	ref Member;
	action:	string;
};

actions: list of Action;
clique: ref Clique;

init(srvmod: Spree, g: ref Clique)
{
	sys = load Sys Sys->PATH;
	sets = load Sets Sets->PATH;
	(clique, spree) = (g, srvmod);
}

ILLEGALNAME: con "/";	# illegal char in member names, ahem.
archive(archiveobj: ref Object)
{
	i := 0;
	for (al := actions; al != nil; al = tl al) {
		a := hd al;
		pname: string;
		if (a.member != nil)
			pname = a.member.name;
		else
			pname = ILLEGALNAME;
		archiveobj.setattr(
			"allow" + string i++,
			sys->sprint("%d %s %s", a.tag, pname, a.action),
			None
		);
	}
}

unarchive(archiveobj: ref Object)
{
	for (i := 0; (s := archiveobj.getattr("allow" + string i)) != nil; i++) {
		(n, toks) := sys->tokenize(s, " ");
		p: ref Member = nil;
		if (hd tl toks != ILLEGALNAME) {
			# if the member is no longer around, ignore the action. XXX do we still need to do this?
			if ((p = clique.membernamed(hd tl toks)) == nil)
				continue;
		}
		sys->print("allow: adding action %d, %ux, %s\n", int hd toks, p, concat(tl tl toks));
		actions = Action(int hd toks, p, concat(tl tl toks)) :: actions;
	}
}

add(tag: int, member: ref Member, action: string)
{
#	sys->print("allow: add %d, member %ux, action: %s\n", tag, member, action);
	actions = (tag, member, action) :: actions;
}

del(tag: int, member: ref Member)
{
#	sys->print("allow: del %d\n", tag);
	na: list of Action;
	for (a := actions; a != nil; a = tl a) {
		action := hd a;
		if (action.tag == tag && (member == nil || action.member == member))
			continue;
		na = action :: na;
	}
	actions = na;
}

action(member: ref Member, cmd: string): (string, int, list of string)
{
	for (al := actions; al != nil; al = tl al) {
		a := hd al;
		if (a.member == nil || a.member == member) {
			(e, v) := match(member, a.action, cmd);
			if (e != nil || v != nil)
				return (e, a.tag, v);
		}
	}
	return ("you can't do that", -1, nil);
}

match(member: ref Member, pat, action: string): (string, list of string)
{
#	sys->print("allow: matching pat: '%s' against action '%s'\n", pat, action);
	toks: list of string;
	na := len action;
	if (na > 0 && action[na - 1] == '\n')
		na--;

	(i, j) := (0, 0);
	for (;;) {
		for (; i < len pat; i++)
			if (pat[i] != ' ')
				break;
		for (; j < na; j++)
			if (action[j] != ' ')
				break;
		for (i1 := i; i1 < len pat; i1++)
			if (pat[i1] == ' ')
				break;
		for (j1 := j; j1 < na; j1++)
			if (action[j1] == ' ')
				break;
		if (i == i1) {
			if (j == j1)
				break;
			return (nil, nil);
		}
		if (j == j1) {
			if (pat == "&")
				break;
			return (nil, nil);
		}
		pw := pat[i : i1];
		w := action[j : j1];
		case pw[0] {
		'*' =>
			toks = w :: toks;
		'&' =>
			toks = w :: toks;
			pat = "&";
			i1 = 0;
		'%' =>
			(ok, nw) := checkformat(member, pw[1], w);
			if (!ok)
				return ("invalid field value", nil);
			toks = nw :: toks;
		* =>
			if (w != pw)
				return (nil, nil);
			toks = w :: toks;
		}
		(i, j) = (i1, j1);
	}
	return (nil, revs(toks));
}

revs(l: list of string): list of string
{
	m: list of string;
	for (; l != nil; l = tl l)
		m = hd l :: m;
	return m;
}

checkformat(p: ref Member, fmt: int, w: string): (int, string)
{
	case fmt {
	'o' =>
		# object id
		if (isnum(w) && (o := p.obj(int w)) != nil)
			return (1, string o.id);
	'd' =>
		# integer
		if (isnum(w))
			return (1, w);
	'p' =>
		# member id
		if (isnum(w) && (member := clique.member(int w)) != nil)
			return (1, w);
	}
	return (0, nil);
}

isnum(w: string): int
{
	# XXX lazy for the time being...
	if (w != nil && ((w[0] >= '0' && w[0] <= '9') || w[0] == '-'))
		return 1;
	return 0;
}

concat(v: list of string): string
{
	if (v == nil)
		return nil;
	s := hd v;
	for (v = tl v; v != nil; v = tl v)
		s += " " + hd v;
	return s;
}