Jump to content

Multiple Users Using HTTP Requests Post Request (Values overwritten)


Mister Vorhees
 Share

You are about to reply to a thread that has been inactive for 575 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

Hi, 

I'm pretty new to LSL, but I've been getting a good hang of things. 

Could anyone possibly give me a hint on how to keep HTTP Request Data unique? 

EX: I have a textbox users can submit a message to. This message will be sent out VIA Post along with character name. 

My issue is when resident A clicks the box and is typing a message, if another Resident B clicks the box when A hit's Submit it will contain B's Username

 

 

Link to comment
Share on other sites

I think the best way to do it would be to have the form send back the user's key* with the form data. There are multiple ways to achieve that, but if it were me, I'd add the user's key as part of the form url's query string when generating the page's (X)HTML.

*Usernames are changeable; keys are not. Best practice is to use a user's key to identify them, not their name. Use names only for presentation to users.

  • Thanks 1
Link to comment
Share on other sites

If you need to do it in such a way that users can't pretend to be someone else (at least not without that person's cooperation), you need to use encryption. This is exactly the scenario for which I ported the XTEA algorithm to LSL:

(quoted to minimize long code spam)

Quote
key ghRequestURL;
string gsURL;

list gPrivateKey; // set in state_entry()

// based on wikipedia specification of XTEA
// https://en.wikipedia.org/wiki/XTEA
key encipher(integer n_rounds, key data)
{   integer v0 = (integer)("0x"+llGetSubString(data,0,7));
    integer v1 = (integer)("0x"+llGetSubString(data, 9,12)+llGetSubString(data,14,17));
    integer v2 = (integer)("0x"+llGetSubString(data,19,22)+llGetSubString(data,24,27));
    integer v3 = (integer)("0x"+llGetSubString(data,28,35));
    integer sum;
    integer delta = 0x9E3779B9;
    do{
        integer X0 = (sum + llList2Integer(gPrivateKey,(sum & 3)));
        v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ X0;
        v2 += (((v3 << 4) ^ (v3 >> 5)) + v3) ^ X0;
        sum += delta;
        integer X1 = (sum + llList2Integer(gPrivateKey,((sum>>11) & 3)));
        v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ X1;
        v3 += (((v2 << 4) ^ (v2 >> 5)) + v2) ^ X1;
    }while(--n_rounds);
    // convert to key format.
    string ret = int2hex(v0);
    string r = int2hex(v1);
    ret+="-"+llGetSubString(r,0,3)+"-"+llGetSubString(r,4,7);
    r = int2hex(v2);
    ret+="-"+llGetSubString(r,0,3)+"-"+llGetSubString(r,4,7)+int2hex(v3);
    return ret; // automatic string to key conversion.
}
key decipher(integer n_rounds, key data)
{   integer v0 = (integer)("0x"+llGetSubString(data,0,7));
    integer v1 = (integer)("0x"+llGetSubString(data, 9,12)+llGetSubString(data,14,17));
    integer v2 = (integer)("0x"+llGetSubString(data,19,22)+llGetSubString(data,24,27));
    integer v3 = (integer)("0x"+llGetSubString(data,28,35));
    integer delta = 0x9E3779B9;
    integer sum = delta*n_rounds;
    do{
        integer X1 = (sum + llList2Integer(gPrivateKey,((sum>>11) & 3)));
        v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ X1;
        v3 -= (((v2 << 4) ^ (v2 >> 5)) + v2) ^ X1;
        sum -= delta;
        integer X0 = (sum + llList2Integer(gPrivateKey,(sum & 3)));
        v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ X0;
        v2 -= (((v3 << 4) ^ (v3 >> 5)) + v3) ^ X0;
    }while(--n_rounds);
    // convert to key format.
    string ret = int2hex(v0);
    string r = int2hex(v1);
    ret+="-"+llGetSubString(r,0,3)+"-"+llGetSubString(r,4,7);
    r = int2hex(v2);
    ret+="-"+llGetSubString(r,0,3)+"-"+llGetSubString(r,4,7)+int2hex(v3);
    return ret; // automatic string to key conversion.
}
string int2hex(integer i)
{   // lowercase. replace all 39's with 7's for UPPERCASE.
    string ret;
    integer w;
    w = (i&0x70000000)>>28;
    w+= 8*(i<0); // correct for LSL 'arithmetic shift'.
    ret += llChar(0x30+w+39*(w>=10));
    w = (i&0x0F000000)>>24;
    ret += llChar(0x30+w+39*(w>=10));
    w = (i&0x00F00000)>>20;
    ret += llChar(0x30+w+39*(w>=10));
    w = (i&0x000F0000)>>16;
    ret += llChar(0x30+w+39*(w>=10));
    w = (i&0x0000F000)>>12;
    ret += llChar(0x30+w+39*(w>=10));
    w = (i&0x00000F00)>>8;
    ret += llChar(0x30+w+39*(w>=10));
    w = (i&0x000000F0)>>4;
    ret += llChar(0x30+w+39*(w>=10));
    w = (i&0x0000000F);
    ret += llChar(0x30+w+39*(w>=10));
    return ret;
}

string generate_form_page(string who)
{   return 
// yes, this is bad LSL formatting, but it makes my head hurt less.
"<!DOCTYPE xhtml>
<html xmlns=\"http://www.w3.org/1999/xhtml\">
<head><style>
body { font-size: 2.5vw; }
</style></head><body>
<form action=\""+gsURL+"/formResponse?ID="+who+"\" method=\"POST\">
   <label for=\"Question\">Please answer:</label><br/>
   <input type=\"text\" id=\"Question\" name=\"Question\"/><br/>
   <input type=\"submit\" value=\"Submit\"/>
</form></body></html>";
}
string response_confirmation_page =
"<!DOCTYPE xhtml>
<html xmlns=\"http://www.w3.org/1999/xhtml\">
<head><style>
body { font-size: 2.5vw; }
</style></head><body> Thank you for your response. </body></html>";

default
{
    state_entry()
    {   ghRequestURL = llRequestURL();
    
        // XTEA stuff:
        key k = llGenerateKey(); // a random key. DO NOT USE THE KEY OF THE ROOT PRIM!
        gPrivateKey =
        [   (integer)("0x"+llGetSubString(k,0,7)),
            (integer)("0x"+llGetSubString(k, 9,12)+llGetSubString(k,14,17)),
            (integer)("0x"+llGetSubString(k,19,22)+llGetSubString(k,24,27)),
            (integer)("0x"+llGetSubString(k,28,35))
        ];
    }
    touch_start(integer n)
    {   while(~--n)
        {   key who = llDetectedKey(n);
            llRegionSayTo(who,0,"form url: ["+gsURL+"/form?ID="+(string)encipher(30,who)+" click here].");
        }
    }
    changed(integer c)
    {   if(c&(CHANGED_REGION_START|CHANGED_REGION))
        {   llReleaseURL(gsURL);
            ghRequestURL = llRequestURL();
        }
    }
    http_request(key ID, string Method, string Body)
    {   if(ID==ghRequestURL)
        {   gsURL=Body;
            llOwnerSay("URL set.");
        }else
        {   // we're using Method to disambiguate request types, in a more complicated setup, you really ought to use the path:
            // string path = llGetHTTPHeader(ID,"x-path-info"); 
            string query = llGetHTTPHeader(ID,"x-query-string");
            query = llDeleteSubString(query,0,llSubStringIndex(query,"=")); // remove "ID=" prefix.
            if("GET"==Method)
            {   string page = generate_form_page(query);
                llSetContentType(ID,CONTENT_TYPE_XHTML);
                llHTTPResponse(ID,200,page);
            }else if("POST"==Method)
            {   query = decipher(30,query);
                llSay(0,"Got response: "+Body+" from secondlife:///app/agent/"+query+"/inspect");
                llSetContentType(ID,CONTENT_TYPE_XHTML);
                llHTTPResponse(ID,200,response_confirmation_page);
                // alternatively, 
                // llHTTPResponse(ID,204,"no content"); 
            }
        }
    }
    on_rez(integer i)
    {   llResetScript();
    }
}

 

 

Edited by Quistess Alpha
Link to comment
Share on other sites

When SL sim servers call an external server, they send additional HTTP headers, which include region, location, and user name. So you can tell which request came from which user.

This is not entirely secure, because someone could make a program to impersonate SL outgoing HTTP connections. But it's good enough for most purposes.

But that may not be the problem the original poster really has. Usually, the way you'd do this in LSL is that you'd pop up the dialog box, let the user type in, and when they push "Submit", all the info goes to an outside web server as one request.

  • Like 1
Link to comment
Share on other sites

On 2/22/2023 at 1:38 PM, Mister Vorhees said:

EX: I have a textbox users can submit a message to

My issue is when resident A clicks the box and is typing a message, if another Resident B clicks the box when A hit's Submit it will contain B's Username

we have to manage multiple users ourselves

is a number of ways to do this.  I wrote this example off the top of my head, so not tested. But I think can help to explain/show the basics of managing multiple users and listeners

list users;  // [userid, listener, timestamp]

integer channelrange;

default
{
     
    touch_start(integer num)
    {
        // get user id (key)
        key thisuser = llDetectedKey(0);
        
        // remove thisuser from users list if exists, ignoring previous touches/actions from thisuser
        // that have not yet been submitted
        integer i = llListFindList(users, [thisuser]);
        if (~i)  // found so remove
        {
           // remove previous assigned listener to thisuser
            llListenRemove(llList2Integer(users, i + 1)); 
            
            // remove thisuser from users list
            users = llDeleteSubList(users, i, i + 2);         
        }
        
        // generate a unique channel within a range. As wrote range is 1000 unique channel numbers 
        // in range of the base channel -1234567
        // this is not absolutely necessary for this script as wrote, but it does show a way to
        // create unique channel numbers within a range should we also need/want to track these in users list       
        channelrange = ++channelrange % 1000;       
        integer thischannel = -1234567 - channelrange; 
        
        // generate a unique listener, Listen only for thisuser on thischannel using thislistener
        integer thislistener = llListen(thischannel, "", thisuser, "");
        
        
        // append thisuser to our current list of users
        users += [thisuser, thislistener, llGetUnixTime()]; 
       
        // display textbox to user
        llTextBox(thisuser, "", thischannel);
    }
    
    listen(integer channel, string name, key id, string text)
    {
        // find thisuser (key id) in users list
        integer i = llListFindList(users, [id]);
        if (~i)  // found
        {   
            // remove thislistener for thisuser's textbox
            llListenRemove(llList2Integer(users, i + 1)); 
            
            // remove thisuser from users list
            users = llDeleteSubList(users, i, i + 2);


            // ...here we process text from thisuser (key id, string text)...
            
        }    
    
        // ... here we might want to remove (age out) users/listeners that exceed some time
        // ... so our users list won't potentially blow up due to incomplete submissions
        for (i = -llGetListLength(users); i > 1; i += 3)
        {
            if (llGetUnixTime() > 300 + llList2Integer(users, i))  // 300 is 5 minutes. Change to suit
            {
                llListenRemove(llList2Integer(users, i + 1));
                users = llDeleteSubList(users, i + 2, i);                   
            }
        }
    }    
}

 

Edited by elleevelyn
typo
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 575 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...