Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Free Allow/deny script

Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
01-28-2009 09:02
This script lets you easily add notecard-configurable allow/deny functionality.

Scripters, please scrutinize for bugs or suggestions, before it gets posted to examples.

To read or copy this script, hit the QUOTE button at the bottom, so you see the indentation.

CODE

// Allow/Deny configuration script - Lear Cale - v0b
// Send "ALLOW?" LM to test, get "ALLOW" LM if av is permitted.

// To use this script:
// From another script send "ALLOW?" LM, passing av's key. (Integer parm is ignored.)
// If allowed, an LM is sent (to this prim only), num=1, str="ALLOW", key=av

// Configuration:
// Reads all cards containing the CardName string (anywhere in the card name)
// Reconfigures automatically whenever any of its config cards change.
// Notecards must be full-perm!

string CardName = ".ALLOW";

// In notecards, use lines like these (omitting the // that makes them comments here):

// allow | Joe Avatar
// allow | jane Doe
// deny | griefu Arbuthnot
// allow | ALL
// allow | GROUP
// debug | -1 | all debugging enabled
// debug | 1 | chat allowed/denied
// debug | 0x10 | dump config

// Object owner is always allowed, unless explicitly denied
// Case-sensitive.
// Comments are allowed and begin with "/".
// You can also add a comment to any config line, following another "|":

// deny | Wimpy Jones | this is the moron who spammed everyone

// By default, this script simply does not reply when disallowed.
// Set this to TRUE to get a "deny" LM response in that case.

integer ReplyOnDeny = FALSE;

// If no "allow" lines appear, everyone is allowed except those explicitly denied
// To default to allowing group instead of everyone, change this to TRUE

integer AllowGroupByDefault = FALSE;

// Delimiters for configs: vertical bar. Any number of spaces are allowed.
list DELIMS = [ "|" ];

// End of configurable globals ======================


list Allowed; // list of names of allowed avs
list Denied; // list of names of denied avs
integer AllowGroup; // whether to allow avs wearing group tag of object's group
integer AllowAll; // whether to allow all

// debugs:
// 0x01 = allow/deny
// 0x10 = debug config

integer Debug;

debug(integer mask, string str) {
if (mask & Debug) {
llWhisper(0, llGetScriptName() + ": " + str);
}
}


// Parse a configuration line.
// Ignore unrecognized commands, for forward/backwards compatibility

parse_config_line(string data) {
// strip leading space
data = llStringTrim(data, STRING_TRIM_HEAD);

// skip comments and blank lines
if (llGetSubString(data,0,0) == "/" || llStringLength(data) < 3) {
return;
}

list ldata = llParseStringKeepNulls(data, DELIMS, []);
string cmd = llStringTrim(llList2String(ldata, 0), STRING_TRIM);
string arg1 = llStringTrim(llList2String(ldata, 1), STRING_TRIM);

if (cmd == "allow") {
if (arg1 == "ALL") {
AllowAll = TRUE;
} else if (arg1 == "GROUP") {
AllowGroup = TRUE;
} else {
Allowed += arg1;
}
} else if (cmd == "deny") {
Denied += (list)arg1;


} else if (cmd == "debug") {
Debug += (integer)arg1;
}
}


// Configuration postprocessing
config_done() {
debug(0x10, "Allowed: " + llList2CSV(Allowed));
debug(0x10, "Denied: " + llList2CSV(Denied));

// set default behavior if unconfigured
if (!AllowAll && !AllowGroup && llGetListLength(Allowed) == 0) {
if (AllowGroupByDefault) {
AllowGroup = TRUE;
} else {
AllowAll = TRUE;
}
}

debug(0x10, "Allow All: " + (string)AllowAll + ", Allow Group: " + (string)AllowGroup);
llOwnerSay(llGetScriptName() + ": Config done");
}


// Determine whether given avatar is allowed

integer av_allowed(key id) {
string av = llKey2Name(id);

// Check if av is explicitly denied
if (llListFindList(Denied, (list)av) >= 0) {
return FALSE;
}

// allow owner unless explicitly denied
if (id == llGetOwner()) {
return TRUE;
}

if (AllowAll) {
return TRUE;
}

if (AllowGroup && llSameGroup(id)) {
return TRUE;
}

// Check if av is explicitly allowed
if (llListFindList(Allowed, (list)av) >= 0) {
return TRUE;
}

// Sorry, bub, no joy for you
return FALSE;
}


// Globals for reading card config
integer ConfigLineIndex;
list ConfigCards; // list of names of config cards
string ConfigCardName; // name of card being read
integer ConfigCardIndex; // index of next card to read
key ConfigQueryId;
string ConfigCardKeys; // to see if anything changed

// Get list of cards that contain our config notecard substring.
// Return list of keys of cards, for checking if notecards have
// changed (a notecard gets a new key every time it's saved).

string get_cards() {
ConfigCards = [];
string keys = "";
string item;

integer ix = llGetInventoryNumber(INVENTORY_NOTECARD);
while (ix-- > 0) {
item = llGetInventoryName(INVENTORY_NOTECARD, ix);
if (llSubStringIndex(item, CardName) >= 0) {
ConfigCards += (list) item;
keys += (string)llGetInventoryKey(item);
}
}

// Sorting them shouldn't be necessary, but I think I've seen them out of order
ConfigCards = llListSort(ConfigCards, 1, TRUE);

return keys;
}

// Start reading the next card.
// Return FALSE if there are no more cards to read.

integer next_card()
{
if (ConfigCardIndex >= llGetListLength(ConfigCards)) {
ConfigCards = [];
return (FALSE);
}

ConfigLineIndex = 0;
ConfigCardName = llList2String(ConfigCards, ConfigCardIndex);
ConfigCardIndex++;
ConfigQueryId = llGetNotecardLine(ConfigCardName, ConfigLineIndex);
llOwnerSay(llGetScriptName() + ": Reading " + ConfigCardName);
return (TRUE);
}


// In this state, we read the notecards

default {
state_entry() {
ConfigCardKeys = get_cards();
ConfigCardIndex = 0;

// start reading cards
if (! next_card()) {
// No cards to read: we're done
state s_on;
}
}

dataserver(key query_id, string data) {
if (query_id != ConfigQueryId) {
return; // ignore data for other scripts
}

if (data == EOF) {
// done with this card, start next one
if (next_card()) {
return;
}

// No more cards; we're done reading.
state s_on;
}

// Parse config line and request the next line.
parse_config_line(llStringTrim(data, STRING_TRIM));
ConfigQueryId = llGetNotecardLine(ConfigCardName, ++ConfigLineIndex);
}

// If any of our notecards changed, bag it and start over.
changed(integer change) {
if (change & CHANGED_INVENTORY) {
if (get_cards() != ConfigCardKeys) {
llResetScript();
}
}
}

state_exit() {
config_done();
}
}


// Operational state. Respond to "ALLOW?" queries.

// You can replace the rest of this script with your own code,
// using the av_allowed() function, rather than doing the
// authentication and your behavior in separate scripts.

state s_on {

link_message(integer sender, integer num, string str, key id) {
if (str == "ALLOW?") {
if (av_allowed(id)) {
debug(1, llKey2Name(id) + " allowed");
llMessageLinked(LINK_THIS, 0, "ALLOW", id);
} else {
debug(1, llKey2Name(id) + " denied");
if (ReplyOnDeny) {
llMessageLinked(LINK_THIS, 0, "DENY", id);
}
}
}
}

// PUT THIS IN EVERY STATE so the script will re-read its config if it changes.
changed(integer change) {
if (change & CHANGED_INVENTORY) {
if (get_cards() != ConfigCardKeys) {
llResetScript();
}
}
}
}

Ruthven Willenov
Darkness in your light
Join date: 16 Jan 2008
Posts: 965
01-28-2009 09:33
that's pretty cool, couldn't you use llToLower or llToUpper when loading the lists and checking the name to make it non case sensitive?


list ldata = llParseStringKeepNulls(data, DELIMS, []);
string cmd = llStringTrim(llList2String(ldata, 0), STRING_TRIM);
string arg1 = llStringTrim(llList2String(ldata, 1), STRING_TRIM);
arg1 = llToUpper(arg1);
.............................
......................

// Determine whether given avatar is allowed

integer av_allowed(key id) {
string av = llToUpper(llKey2Name(id));
Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
01-28-2009 10:08
I could, but there are people with the same name except for case.

Really.
Ruthven Willenov
Darkness in your light
Join date: 16 Jan 2008
Posts: 965
01-28-2009 11:33
lol, i did not know that, but the chance of them being on the same list is probably pretty slim. and especially one being banned and one not?
Rolig Loon
Not as dumb as I look
Join date: 22 Mar 2007
Posts: 2,482
01-28-2009 11:52
Gee, so all I have to do is join a group with multiple avs with names like Rolig, rolig, rOlig, roLig, ...... so if one gets banned or ejected, the others still survive on the list? That never occurrred to me before. (Of course, I've never been banned either.... :p )
Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
01-28-2009 12:12
I *hope* that SL doesn't permit it any more, because Search doesn't work properly for these poor souls. I know a guy, TOM B***, and there's also a Tom B***. If I search for my friend in the People tab, I get the other guy (only).

In general, I prefer a tool that does what it's supposed to do, rather than one that makes it easier for people to be sloppy but might not work in certain cases.

Still, maybe that would be a good change. Admittedly the chances of incorrect behavior are low.
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
01-28-2009 12:36
Nice, and very well written!

I haven't tried it yet, but through inspection I've come up with one defect and a bit of discussion.

The defect: You never call config_init(). I think it is intended to be at the beginning of default/state_entry. Always those pesky init functions that get ya! ;)

The other thing is that llGetInventoryKey() only works for inventory items that have full permissions. I understand this script is mostly intended for folks to actively manage, so it's probably not a big deal. You just might want to include some kind of warning/disclaimer. This could especially be problematic in a collaborative management scheme where someone has accidentally changed next-owner permissions.

As a memory optimization you could also hash the result returned by get_cards() using something like llMD5String(). It'd cost a tiny bit of performance, but only use up 32 characters for the update checking functionality no matter how many notecards are used. It's not going to really help in the common case of only one notecard being used though, so it may not be worth the change.
Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
01-28-2009 12:48
From: Hewee Zetkin
The defect: You never call config_init(). I think it is intended to be at the beginning of default/state_entry. Always those pesky init functions that get ya! ;)
EEK! Thanks Hewee!

Thanks for the other tips, too. The point about cards needing to be full perms never occurred to me.
Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
01-29-2009 06:58
Actually, my mistake was forgetting to delete the init function. It's left over from scripts that don't reset on a config change, but just reread the config.
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
01-29-2009 11:46
From: Lear Cale
Actually, my mistake was forgetting to delete the init function. It's left over from scripts that don't reset on a config change, but just reread the config.


Oh yeah. Sorry. I was just operating on the assumption that it didn't reset as well, since you designed it such that half the script could be replaced by contextual logic. :o
Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
01-29-2009 12:09
No apology necessary! I clearly had a mistake; thanks for pointing it out.

Yet another half-baked script, courtesy of Lear Cale. :)

Hopefully we can get this one fully baked soon enough.