Yumi Murakami
DoIt!AttachTheEarOfACat!
Join date: 27 Sep 2005
Posts: 6,860
|
12-31-2005 09:49
I've not put this in the library yet because I'm not sure it's exactly what's needed - but here's an attempt at a generic open script that can be including in a money handling device to offer the new owner a guarantee that it won't rip them off by having away money it hasn't made. The link messages for communicating with it are listed at the top of the script. Of course, the script can't actually disable payments if the master script has a money() event, but the disable/enable feature was added to avoid the presence of this script forcing "pay" to become an option even when the master script doesn't have a money() event. // "Breakeven-or-better" money security script // This should be inserted WITH COPY/MODIFY PERMISSION in object to be secured // Objects using this should make ONLY ONE request for debit permission
// Set to true to test object: // No money paid out, all incoming payments returned, debug messages displayed integer test = FALSE;
// Link message commands for this script integer PAY_COMMAND = 250000; // Command to pay out: string = L$, key = payee integer REQUEST_PERM_COMMAND = 250001; // Command to try permissions request again integer ENABLE_PAYMENT_COMMAND = 250002; // Command to enable payments integer DISABLE_PAYMENT_COMMAND = 250003; // Command to disable payments integer ACK_PAYMENT = 250004; // Acknowledge payments
integer PAY_SUCCESS = 250005; // Returned if pay succeeded integer PAY_FAILED = 250006; // Returned if pay failed: string = reason integer PERM_GRANTED = 250007; // Returned if permission was granted by user integer PERM_DENIED = 250008; // Returned if permission was denied by user integer PAYMENTS_ENABLED = 250009; // Returned if payments are enabled integer PAYMENTS_DISABLED = 250010; // Returned if payments are disabled integer PAYMENT_PENDING = 250011; // Return if command was refused because a payment is pending
integer got_permission = FALSE; // Do we have debit permission? integer pending_payack = FALSE; // Payment acknowledgement pending? integer credit = 0; // Amount of credit key credit_owner; // Owner of credit // (This prevent somebody running up a big credit by paying money to themselves, then // giving the object to you and letting the "credit" be paid out of your account)
checkOwner() { if (credit_owner != llGetOwner()) { // If not still owned by credit owner credit = 0; // Clear credit credit_owner = llGetOwner(); // And credit owner is now object owner got_permission = FALSE; // Need permission from the new owner } }
payCommand(string str_msg, key key_msg) { checkOwner(); if (!got_permission) { if (test) llSay(0,"TEST MODE: Tried to pay L$" + str_msg + " to " + llKey2Name(key_msg) + " without debit permission."); llMessageLinked(LINK_THIS,PAY_FAILED,"Permission denied",NULL_KEY); return; } integer amount = (integer)str_msg; if (((string)amount) != str_msg) { if (test) llSay(0,"TEST MODE: Tried to play mangled amount, '" + str_msg + "'."); llMessageLinked(LINK_THIS,PAY_FAILED,"Bad amount: " + str_msg,NULL_KEY); return; } if (amount > credit) { if (test) llSay(0,"TEST MODE: Tried to pay L$" + str_msg + " to " + llKey2Name(key_msg) + " but insufficient credit (credit only L$" + (string)credit + ".)"); llMessageLinked(LINK_THIS,PAY_FAILED,"Insufficient credit",NULL_KEY); return; } credit -= amount; if (test) { llSay(0,"TEST MODE: Virtually paying L$" + (string)amount + " to " + llKey2Name(key_msg) + ". (Virtual credit is L$" + (string)credit + ".)"); } else { llGiveMoney(key_msg,amount); } llMessageLinked(LINK_THIS,PAY_SUCCESS,"",NULL_KEY); }
requestPermCommand() { checkOwner(); if (got_permission) { llMessageLinked(LINK_THIS,PERM_GRANTED,"",NULL_KEY); return; } llRequestPermissions(llGetOwner(),PERMISSION_DEBIT); }
default { // Always returns to zero credit when script or object is reset. state_entry() { got_permission = FALSE; credit = 0; credit_owner = llGetOwner(); pending_payack = FALSE; state nopayments; } // Credit value retained through re-rezzing, as long as owner is same. on_rez(integer junk) { got_permission = FALSE; checkOwner(); state nopayments; } }
state nopayments { state_entry() { llMessageLinked(LINK_THIS,PAYMENTS_DISABLED,"",NULL_KEY); } on_rez(integer junk) { got_permission = FALSE; checkOwner(); } run_time_permissions(integer permissions) { if (permissions & PERMISSION_DEBIT) { got_permission = TRUE; llMessageLinked(LINK_THIS,PERM_GRANTED,"",NULL_KEY); } else { llMessageLinked(LINK_THIS,PERM_DENIED,"",NULL_KEY); } } link_message(integer sender, integer int_msg, string str_msg, key key_msg) { if (sender == llGetLinkNumber()) { if (int_msg == PAY_COMMAND) { payCommand(str_msg,key_msg); return; } if (int_msg == REQUEST_PERM_COMMAND) { requestPermCommand(); return; } if (int_msg == ENABLE_PAYMENT_COMMAND) { state payments; } if (int_msg == DISABLE_PAYMENT_COMMAND) { llMessageLinked(LINK_THIS,PAYMENTS_DISABLED,"",NULL_KEY); } } } }
state payments { money(key giver, integer amount) { // We know 'amount' has been paid to current owner now, but check past credit checkOwner(); // If a previous payment still hasn't been acknowledged by master, return money if (pending_payack) { llGiveMoney(giver,amount); return; } credit += amount; if (test) { llSay(0,"TEST MODE: Repaying L$" + (string)amount + " payment. (Virtual credit is L$" + (string)credit + ".)"); llGiveMoney(giver,amount); } } state_entry() { llMessageLinked(LINK_THIS,PAYMENTS_ENABLED,"",NULL_KEY); } on_rez(integer junk) { got_permission = FALSE; checkOwner(); } run_time_permissions(integer permissions) { if (permissions & PERMISSION_DEBIT) { got_permission = TRUE; llMessageLinked(LINK_THIS,PERM_GRANTED,"",NULL_KEY); } else { llMessageLinked(LINK_THIS,PERM_DENIED,"",NULL_KEY); } } link_message(integer sender, integer int_msg, string str_msg, key key_msg) { if (sender == llGetLinkNumber()) { if (int_msg == ACK_PAYMENT) { pending_payack = FALSE; } if (int_msg == PAY_COMMAND) { if (pending_payack) { llMessageLinked(LINK_THIS,PAYMENT_PENDING,"",NULL_KEY); return; } payCommand(str_msg,key_msg); return; } if (int_msg == REQUEST_PERM_COMMAND) { requestPermCommand(); return; } if (int_msg == ENABLE_PAYMENT_COMMAND) { llMessageLinked(LINK_THIS,PAYMENTS_ENABLED,"",NULL_KEY); } if (int_msg == DISABLE_PAYMENT_COMMAND) { if (pending_payack) { llMessageLinked(LINK_THIS,PAYMENT_PENDING,"",NULL_KEY); return; } state nopayments; } } } }
(Edit: removed a nice livelock bug...)
|
Haravikk Mistral
Registered User
Join date: 8 Oct 2005
Posts: 2,482
|
12-31-2005 10:22
Hmm, this looks interesting, but I'm not 100% sure what this is doing. If the master script requests debit permissions itself, it seems like this script won't stop it (unless I'm being silly). Should this script not force debit permission to be handled by it? So if the 'master' script requests debit permission itself, it will call the run_time_permissions event in this script, if that happens then this script can simply clear the permissions using llRequestPermissions(FALSE); to thwart the master script from doing anything malicious? Although I like the idea of what you're trying to achieve, I'm not 100% certain how it's supposed to work. I'm finishing up a vendor you see, and if I have enough free memory for the link messages then I'd love to support this idea by including it in my script, but I'm unsure if this is the best way to do it. In fact, does it even need link_messages? Surely this script should just be doing things like checking if debit permission was given and noting how much money has been paid in and so-on? Also you should use the php tags, they look neater 
|
Yumi Murakami
DoIt!AttachTheEarOfACat!
Join date: 27 Sep 2005
Posts: 6,860
|
12-31-2005 10:29
From: Haravikk Mistral Hmm, this looks interesting, but I'm not 100% sure what this is doing. If the master script requests debit permissions itself, it seems like this script won't stop it (unless I'm being silly). It won't stop it, and AFAIK it can't stop another script requesting permissions, but it will lead to the overall object requesting twice, which should be suspicious to the user if they have read the comment at the top. From: someone In fact, does it even need link_messages? Surely this script should just be doing things like checking if debit permission was given and noting how much money has been paid in and so-on? It can't stop the money being paid out if that's the case, though.
|
Haravikk Mistral
Registered User
Join date: 8 Oct 2005
Posts: 2,482
|
01-01-2006 06:56
Hmm, but when a script in an object requests permissions, surely all scripts in the object receive the run_time_permissions() event? So if the master script (being ridiculously simple) looked like this: default { state_entry() { llRequestPermissions(llGetOwner(),PERMISSION_DEBIT); } } Then the checking script could do this: default { run_time_permissions(integer perm_mask) { if (perm_mask & PERMISSION_DEBIT) llRequestPermissions(llGetOwner(),FALSE); } } As I understand it, that would immediately disable the permissions for the entire object (without prompting the user with a dialog, though including an error message would be helpful). Meaning if the master script then attempted to give out money, it would fail. That example is far too simplistic I think, but it gives the idea hopefully? If debit permission is given AND a flag was not set (to indicate that it was given via the money checker script) then the master script is acting on its own with possible malicious intent. So a more useful example for money checking: integer validRequest;
default { link_message(integer x, integer type, string msg, key id) { if (type == 1) { // Debit permission requested validRequest = TRUE; // Request is being made through us, so it is valid llRequestPermissions(llGetOwner(),PERMISSION_DEBIT); // Request permission } }
run_time_permissions(integer perm_mask) { integer debitGiven = perm_mask & PERMISSION_DEBIT; // Has debit permission been received?
if (debitGiven && validRequest) validRequest = FALSE; // Allow this request only else if (debitGiven) { // Debit permission received using invalid method! llRequestPermissions(llGetOwner(),FALSE); // Remove all permissions! llOwnerSay("Script has requested permissions via invalid method!"); } } }
|
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
|
01-01-2006 16:40
From: Haravikk Mistral Hmm, but when a script in an object requests permissions, surely all scripts in the object receive the run_time_permissions() event? So if the master script (being ridiculously simple) looked like this: default { state_entry() { llRequestPermissions(llGetOwner(),PERMISSION_DEBIT); } } Then the checking script could do this: default { run_time_permissions(integer perm_mask) { if (perm_mask & PERMISSION_DEBIT) llRequestPermissions(llGetOwner(),FALSE); } } As I understand it, that would immediately disable the permissions for the entire object (without prompting the user with a dialog, though including an error message would be helpful). Meaning if the master script then attempted to give out money, it would fail. Please excuse my bluntness atm, getting over the new-years festivities is... taxing.  The scenerio you present, however desireable as it seems, doesnt work. Permissions act like script-scope (global) variables; each script in an object can have a differing set of permissions. Therefore, if script A requests PERMISSION_DEBIT, assuming all scripts in the object receive the resulting run_time_permissions event (which is unlikely), and script B, upon receiving the run_time_permissions event, requests FALSE, script A will still retain its PERMISSION_DEBIT, if granted by the user. Its still worth testing whether the run_time_permissions event is heard by all scripts in the object. Its very unlikely though - given the permissions' scope. If it is, then instead of trying to automatically handle it, notify the owner. Sorry  ==Chris
|
Yumi Murakami
DoIt!AttachTheEarOfACat!
Join date: 27 Sep 2005
Posts: 6,860
|
01-02-2006 14:10
I'm pretty sure not all scripts in the object recieve the permissions event. It would make doing scripts for docking animations a nightmare  Just to clarify, the above script is written on the basis that it can't stop the superscript getting PERMISSION_DEBIT if it asks, but at least it can be mighty suspicious that the object asks for it twice.
|
Haravikk Mistral
Registered User
Join date: 8 Oct 2005
Posts: 2,482
|
01-02-2006 16:32
Damn, double checked it and you're right, only the calling script receives the event.
Ignore me then, completely misunderstood permissions!
|