User:Kireji Haiku/SIMchat headset

From Second Life Wiki
Jump to navigation Jump to search

SIMchat headset

SCRIPT:


This script is being provided "as is". It's been thoroughly tested in May 2010.

If you want to use it, you should drop it into a childprim of a linkset together with the notecard. This setup will give you the possibilty to forward your chat on a certain channel within the whole region your are in and encrypt it. Just make sure anybody you want to talk to has the same variables in the setup-notecard as you do.

You can turn off the SIMchat function (if you are being spammed on that channel...you could of course change the channel, too), get a SLURL to your current location, get the current count of avatars within the region or get a quick reminder of the currently-used-channel and a howto by touching the childprim containing this script and the attached setup notecard.


Go to top! <lsl> string ProtocolSignature; float ProtocolVersion; // can range from 0.0 to 255.255 string Password; integer communicationsChannel; string Header; string strHex; integer Debug; integer listener; integer gListenID = -1; integer X; integer Y; integer Z; key user; list mainmenu = ["RegionCount","SLURL","SIMchatOFF","Chat info","Chat howto"]; integer ConfigRequired = TRUE; string ConfigNotecardSuffix = ".cfg"; float ConfigTimeout = 60.0; integer ConfigLineIndex; key ConfigRequestID; list ConfigCards; string ConfigCardName; integer ConfigCardIndex; string Name; string SLURL; string menu_info_1 = "

   This popup shows up because you touched your SIMchat headset.
       ";

string menu_info_2 = "Choose one of the following."; integer mChannel = 8989; string prim_name; string prim_desc; vector Where; string CONTROLLER_ID = "A"; integer AUTO_START = TRUE; list particle_parameters=[]; list target_parameters=[];

config_init() {

   strHex = "0123456789ABCDEF";
   Password = "P@ssw0rd";
   ProtocolVersion = 0.3;
   ProtocolSignature = "ENC";
   mChannel = 8989;
   communicationsChannel = 9;

}

config_dump() {

   say("You are using channel " + (string)communicationsChannel + " for chatting.");
   say("Your password is: " + Password);
   say("Your protocol version is " + (string)ProtocolVersion + ", your protocol signature " + ProtocolSignature + " and your stringHex is " + strHex);
   say("The menu for the dialog-channel is: " + (string)mChannel);

}

config_parse(string str, string cardName, integer lineNum) {

   str = llStringTrim(str, STRING_TRIM_HEAD);
   if (llGetSubString(str,0,0) == "/") {
       return;
   }
   list ldata  = llParseStringKeepNulls(str, ["="], [""]);
   string cmd  = llToUpper(llStringTrim(llList2String(ldata,0),STRING_TRIM));
   string arg1 = llStringTrim(llList2String(ldata,1),STRING_TRIM);
   if (cmd == "STRHEX")
   {
       strHex = arg1;
   }
   else if (cmd == "CHANNEL")
   {
       communicationsChannel = (integer)arg1;
   }
   else if (cmd == "PASSWORD")
   {
       Password = arg1;
   }
   else if (cmd == "VERSION")
   {
       ProtocolVersion = (float)arg1;
   }
   else if (cmd == "SIGNATURE")
   {
       ProtocolSignature = arg1;
   }
   else if (cmd == "DIALOG")
   {
       mChannel = (integer)arg1;
   }
   else if (cmd == "debug")
   {
       Debug = (integer) arg1;
   }

}

config_done() { if (Debug) { config_dump(); } say("Config done"); }

say(string str) { llSay(0, str); }

debug(string str) { if (Debug) { say(llGetScriptName() + ": " + str); } }

integer next_card() {

   if (ConfigCardIndex >= llGetListLength(ConfigCards)) {
       ConfigCards = [];
       return (FALSE);
   }
   ConfigLineIndex = 0;
   ConfigCardName = llList2String(ConfigCards, ConfigCardIndex);
   ConfigCardIndex++;
   ConfigRequestID = llGetNotecardLine(ConfigCardName, ConfigLineIndex);
   say("Reading " + ConfigCardName);
   return (TRUE);

}

string error(string message) {

   if(Debug) llSay(DEBUG_CHANNEL, message);
   return "";

}

string decrypt(string password, string message) {

   integer signatureLength = llStringLength(ProtocolSignature);
   integer headerLength = signatureLength + 12;
   if(llStringLength(message) < signatureLength + 44)
       return error("Too small for secret message.");
   if(llSubStringIndex(message, ProtocolSignature) != 0)
       return error("Unknown protocol.");
   integer index = signatureLength;
   string major = "0x" + llGetSubString(message, index, ++index);
   string minor = "0x" + llGetSubString(message, ++index, ++index);
   float version = (float)((string)((integer)major) + "." + (string)((integer)minor));
   if(version != ProtocolVersion)
       return error("Unknown version.");
   integer nonce = (integer)("0x" + llGetSubString(message, ++index, index + 7));
   message = llGetSubString(message, headerLength, -1);
   string oneTimePad = llMD5String(password, nonce);
   while(llStringLength(oneTimePad) < (llStringLength(message) / 2 * 3))
   oneTimePad += llMD5String(oneTimePad, nonce);
   oneTimePad = llStringToBase64(oneTimePad);
   message = llXorBase64StringsCorrect(message, oneTimePad);
   message = llBase64ToString(message);
   string digest = llGetSubString(message, 0, 31);
   message = llGetSubString(message, 32, -1);
   if(llMD5String(message, nonce) != digest)
       return error("Message digest was not valid.");
   return message;

}

string hex(integer value) {

   integer digit = value & 0xF;
   string text = llGetSubString(strHex, digit, digit);
   value = (value >> 4) & 0xfffFFFF;
   integer odd = TRUE;
   while(value)
   {
       digit = value & 0xF;
       text = llGetSubString(strHex, digit, digit) + text;
       odd = !odd;
       value = value >> 4;
   }
   if(odd)
       text = "0" + text;
   return text;

}

string encrypt(string password, string message) {

   integer nonce = (integer)llFrand(0x7FFFFFFF);
   message = llMD5String(message, nonce) + message;
   string oneTimePad = llMD5String(password, nonce);
   integer count = (llStringLength(message) - 1) / 32;
   if(count)
       do
           oneTimePad += llMD5String(oneTimePad, nonce);
   while(--count);
   return Header + llGetSubString("00000000" + hex(nonce), -8, -1) + llXorBase64StringsCorrect(llStringToBase64(message), llStringToBase64(oneTimePad));

}

init1() {

   list versions = llParseString2List((string)ProtocolVersion, ["."], []);
   string minor = llList2String(versions, 1);
   integer p = 0;
   while(llGetSubString(minor, --p, p) == "0");
   Header = ProtocolSignature + hex(llList2Integer(versions, 0)) + hex((integer)llGetSubString(minor, 0xFF000000, p));

}

init2() {

   if(listener != 0)
   {
       llListenRemove(listener);
       listener = 0;
   }
   listener = llListen(communicationsChannel, "", NULL_KEY, "");

}

default {

   state_entry() {
       llSetText("", <1.0,1.0,1.0>, 1.0);
       state s_config;
   }

}

state s_reconfig { state_entry() { state s_config; } }

state s_config {

   state_entry() {
       config_init();
       string item;
       ConfigCards = [];
       integer n = llGetInventoryNumber(INVENTORY_NOTECARD);
       while (n-- > 0) { item = llGetInventoryName(INVENTORY_NOTECARD, n); if (llSubStringIndex(item, ConfigNotecardSuffix) != -1) { ConfigCards += [item]; }
       }
       ConfigCardIndex = 0;
       if (next_card()) { llSetTimerEvent(ConfigTimeout); }
       else if (ConfigRequired) { say("Configuration notecard missing."); state s_configRetry; }
       else { state s_active; }
   }

   dataserver(key query_id, string data) {
       if (query_id == ConfigRequestID) {
           if (data == EOF) { if (! next_card()) { config_done(); state s_active; } }
           else { config_parse(data, ConfigCardName, ConfigLineIndex); ConfigRequestID = llGetNotecardLine(ConfigCardName, ++ConfigLineIndex); llSetTimerEvent(ConfigTimeout); }
       }
   }

   timer() { say("Dataserver time out: touch to retry"); state s_configRetry; }

   on_rez(integer num) { state s_reconfig; }

   changed(integer change) {
       if (change & CHANGED_OWNER) { llResetScript(); }
       if (change & CHANGED_INVENTORY) { state s_reconfig; }
   }

   state_exit() { llSetTimerEvent(0); }

}

state s_configRetry {

   touch_start(integer tot) { if (llDetectedKey(0) == llGetOwner()) { state s_config; } }

   changed(integer change) {
       if (change & CHANGED_OWNER) { llResetScript(); }
       if (change & CHANGED_INVENTORY) { state s_config; }
   }

}

state s_unconfigured {

   state_entry() { llSetText("Configuration missing", <1.0,1.0,1.0>, 1.0); }

   changed(integer change) {
       if (change & CHANGED_OWNER) { llResetScript(); }
       if (change & CHANGED_INVENTORY) { state s_reconfig; }
   }

   state_exit() { llSetText("", <1.0,1.0,1.0>, 1.0); }

}

state s_active {

   state_entry()
   {
       llListenRemove(gListenID);
       gListenID = llListen(communicationsChannel, "", "", "");
       init1();
       init2();
       llOwnerSay("
           Initialisation complete!
           SIMchat online!
           Type /" + (string)communicationsChannel + " and then your message to talk to people that have their channel set the same.
           Touch headset for options.");
       prim_name = "SIMchat(" + llKey2Name(llGetOwner()) + ")";
       prim_desc = "Last reset was: " + llGetTimestamp();
       llSetObjectName(prim_name);
       llSetObjectDesc(prim_desc);
       particle_parameters = [
           PSYS_PART_START_SCALE,<0.02,0.02,FALSE>,PSYS_PART_END_SCALE,<0.5,0.5, FALSE>,
           PSYS_PART_START_COLOR,<0.94118,0.00000,0.23529>,PSYS_PART_END_COLOR,<1,0,0>,
           PSYS_PART_START_ALPHA,(float)0.5,PSYS_PART_END_ALPHA,(float)0.0,
           PSYS_SRC_BURST_PART_COUNT,(integer)4,
           PSYS_SRC_BURST_RATE,(float)7.5,
           PSYS_PART_MAX_AGE,(float)0.5,
           PSYS_SRC_PATTERN,(integer)1,
           PSYS_PART_FLAGS,(integer)(0 |PSYS_PART_INTERP_COLOR_MASK
                                       |PSYS_PART_INTERP_SCALE_MASK
                                       |PSYS_PART_EMISSIVE_MASK
                                       |PSYS_PART_FOLLOW_SRC_MASK
                                       |PSYS_PART_TARGET_POS_MASK)];
       if ( AUTO_START ) llParticleSystem( particle_parameters );
   }
   on_rez(integer start_param)
   {
       init1();
       init2();
       llResetScript();
   }
   attach(key id)
   {
       if (id)
       {
           llOwnerSay("Headset has been reset.");
           llResetScript();
       }
   }
   touch_start(integer total_number)
   {
       if (llDetectedKey(0) == llGetOwner())
       {
           user = llGetOwner();
           llDialog(user, menu_info_1 + menu_info_2,mainmenu,mChannel);
           llListen(mChannel,llKey2Name(user),user,"");
       }
   }
   listen(integer channel, string name, key id, string message)
   {
       if(id==user)
       {
           if(message=="SLURL")
           {
               Name = llGetRegionName();
               Where = llGetPos();
               X = (integer)Where.x;
               Y = (integer)Where.y;
               Z = (integer)Where.z;
               SLURL = "SLURL for this location is:\n\n" + "http://slurl.com/secondlife/" + Name + "/" + (string)X + "/" + (string)Y + "/" + (string)Z + "/";
               llSay(0, SLURL);
               return;
           }
           if(message=="SIMchatOFF")
           {
               state sleeping;
               return;
           }
           if(message=="Chat info")
           {
               llOwnerSay("Type /" + (string)communicationsChannel + " and then your message to talk to people that have their channel set the same.");
               return;
           }
           if(message=="Chat howto")
           {
               llOwnerSay("Type in local chat: /" + (string)communicationsChannel + " ... \nFor example: /" + (string)communicationsChannel + " hello, my name is Nick. \nEverybody in the same SIM using this SIMchat function on channel " + (string)communicationsChannel + " will be able to read what you write.\nRemember to use channel" + (string)communicationsChannel + "for anything you want to write with this SIMchat-tool. Local chat is limited to 20m radius and will not be encrypted.");
               return;
           }
           if(message=="RegionCount")
           {
               llOwnerSay("   Region count is now:" + (string)llGetRegionAgentCount());
               return;
           }
           else
           {
               //do nothing
           }
       }
       if (channel == communicationsChannel)
       {
           if (id == llGetOwner())
           {
               llRegionSay(communicationsChannel, encrypt(Password, message));
               llOwnerSay("You said: " + message);
           }
           if (llGetAgentSize(id) == ZERO_VECTOR)
           {
               string message = decrypt(Password, message);
               llOwnerSay((string)name + " said: " + (string)message);
           }
       }
   }

}

state sleeping {

   state_entry()
   {
       llOwnerSay("SIMchat offline! Touch headset to go back online.");
       particle_parameters = [];
       if ( AUTO_START ) llParticleSystem( particle_parameters );
   }
   touch_start(integer n)
   {
       if (llDetectedKey(0) == llGetOwner())
       {
           llOwnerSay("SIMchat back online! Touch headset for further informations.");
           state s_active;
       }
   }
   on_rez(integer start_param)
   {
       llResetScript();
   }

} </lsl>

NOTECARD:


  • Name this notecard with an ending ".cfg", for example "SIMchat headset.cfg" or "config.cfg".
  • Commands (like "strhex", "channel", "password", ...) are NOT case sensitive, the values for the commands however ARE case sensitive.
  • It doesn't matter if you write "=" or " = ", both will be fine.
  • The value for "version" can range from 0.0 to 255.255


Go to top! <lsl> strhex = 123456789ABCDEF channel = 9 password = P@ssw0rd version = 0.3 signature = ENC dialog = 8989 debug = 1 </lsl>