// Provides an interface for communicating requests to the server. // Copyright (C) 2005-2006 Francisco V. Saldana // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License // as published by the Free Software Foundation; either version 2 // of the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // Francisco V. Saldana can be contacted using his email account // username: dressedinblue, at domain: gmail.com // and in Second Life by IMming Christopher Omega // ====================== XMLRPC request constants: ====================== \\ // Sent by the client to the server when the server polls // and the client has no data to send. integer NO_REQUESTS = 5000; // Sent by the server to the client when a poll occurs. integer SERVER_POLL = 6000; // Sent by client to server requesting that the server stop polling. integer STOP_POLLING = 6001; // Sent by server to client that tells client that the RPC string contains multiple command seperated // by COMMAND_SEPERATOR. integer MULTI_COMMAND = 6002; // Sent by server to client requesting that the client restart. integer TERMINAL_RESET = 6003; // Sent by the client to the server (or external entity) requesting a // piece of content: integer GET_CATEGORY = 7001; // String value: catagory requested integer GET_NOTECARD = 7002; // String value: Notecard name requested. integer GET_PREV = 7003; // String value: "" integer GET_NEXT = 7004; // String value: "" integer GET_KEYWORD_SEARCH = 7005; // String value: Keyword requested. integer GET_SUMMARY = 7007; integer GET_BUTTON = 7008; // Sent by the server to the client, between MENU_BEGIN and MENU_END // requesting that it display text. integer SET_CATEGORY = 8001; // String value: Catagory name to display. integer SET_NOTECARD = 8002; // String value: Notecard name to display. integer SET_PREV = 8003; // String value ignored integer SET_NEXT = 8004; // String value ignored. integer SET_KEYWORD_SEARCH = 8005; // String value ignored. integer SET_TEXT = 8006; // String value: any text. integer SET_SUMMARY = 8007; integer SET_BUTTON = 8008; // Sent by the server to the client specifying the start // and end of a menu. integer MENU_BEGIN = 9001; // String value: Menu title integer MENU_END = 9002; // ====================== /XMLRPC request constants ====================== \\ string CATEGORY_SEPERATOR = "]_["; string SERVER_EMAIL_ADDRESS = "slpl@zerocubed.com"; string STARTUP_MESSAGE_COMMAND = "Register"; string SHUTDOWN_MESSAGE_COMMAND = "Shutdown"; string MESSAGE_SUBJECT = "SL Library Terminal"; string PROTOCOL_VERSION = "1.0"; string TYPE_INBOUND = "INBOUND"; string TYPE_OUTBOUND = "OUTBOUND"; // The number of scripts that have RPC channels. integer RPC_SCRIPT_COUNT = 2; list rpcChannelKeys; list rpcChannelTypes; // ========== For method invocation ========== string randomStr(string chars, integer len) { integer numChars = llStringLength(chars); string ret; integer i; for (i = 0; i < len; i++) { integer randIndex = llFloor(llFrand(numChars)); ret += llGetSubString(chars, randIndex, randIndex); } return ret; } string SEPERATOR_CHARS = "`~!@#$%^&*()-_+[]{}\|'\";/?.>,<"; integer SEPERATOR_LEN = 3; string dumpList2String(list src) { // Generate a seperator not present in any of the // elements in the list. string chars = (string) src; // Squashes all elements together. string seperator; do { seperator = randomStr(SEPERATOR_CHARS, SEPERATOR_LEN); } while (llSubStringIndex(chars, seperator) != -1); return seperator + llDumpList2String(src, seperator); } list parseStringKeepNulls(string src) { // The seperator should be the first SEPERATOR_LEN // characters in the string. return llParseStringKeepNulls(llDeleteSubString(src, 0, SEPERATOR_LEN - 1), [llGetSubString(src, 0, SEPERATOR_LEN - 1)], []); } callMethod(integer callId, string methodName, list parameters) { llMessageLinked(LINK_THIS, callId, dumpList2String(parameters), methodName); } returnValue(string methodName, integer methodIdentifyer, list value) { llMessageLinked(LINK_THIS, methodIdentifyer, dumpList2String(value), methodName + "_ret"); } // ============================================= trigger_displayStatusMessage(string msg) { callMethod(0, "displayStatusMessage", [msg]); } trigger_sendRpcData(integer iData, string sData) { callMethod(0, "sendRpcData", [iData, sData]); } trigger_sendRpcControlData(integer iData, string sData) { callMethod(0, "sendRpcControlData", [iData, sData]); } integer getOutboundChannelReturnVal; trigger_getOutboundChannel() { getOutboundChannelReturnVal = (integer) llFrand(1824214); callMethod(getOutboundChannelReturnVal, "getOutboundChannel", []); } integer getInboundChannelReturnVal; trigger_getInboundChannel() { getInboundChannelReturnVal = (integer) llFrand(29481); callMethod(getInboundChannelReturnVal, "getInboundChannel", []); } integer sendEmailDataReturnVal; trigger_sendEmailData(string address, string subject, string message) { sendEmailDataReturnVal = (integer) llFrand(119333); callMethod(sendEmailDataReturnVal, "sendEmailData", [address, subject, message]); } trigger_pong(string moduleName) { callMethod(0, "pong", [moduleName]); } sendServerEmail(string command, list commandParams) { commandParams = [PROTOCOL_VERSION, command] + commandParams; trigger_sendEmailData(SERVER_EMAIL_ADDRESS, MESSAGE_SUBJECT, llDumpList2String(commandParams, " ")); } integer trySend() { // If we have keys from all rpc scripts: if (llGetListLength(rpcChannelKeys) == RPC_SCRIPT_COUNT) { // This email is in the format: // <version> <terminalKey> OUTBOUND <rpcKey> INBOUND <rpcKey> list emailParams = [ "TerminalUUID", llGetKey()]; integer i; for(i = 0; i < RPC_SCRIPT_COUNT; i++) { emailParams += llList2String(rpcChannelTypes, i); emailParams += llList2String(rpcChannelKeys, i); } sendServerEmail(STARTUP_MESSAGE_COMMAND, emailParams); returnValue("connectionStartup", startupCommandCallId, [TRUE]); return TRUE; } return FALSE; } integer startupCommandCallId; string this; default { state_entry() { this = llGetScriptName(); } link_message(integer sender, integer num, string parameters, key methodName) { if (methodName == "connectionStartup") { // Method signature: // integer connectionStartup() startupCommandCallId = num; state starting; } else if (methodName == "ping") { list paramList = parseStringKeepNulls(parameters); // Method signature: // ping(string moduleName) string moduleName = llList2String(paramList, 0); if (moduleName == this) trigger_pong(this); } } } state starting { state_entry() { trigger_getInboundChannel(); trigger_getOutboundChannel(); rpcChannelKeys = rpcChannelTypes = []; } link_message(integer sender, integer callId, string parameters, key methodName) { if (methodName == "getInboundChannel_ret" && callId == getInboundChannelReturnVal) { list paramList = parseStringKeepNulls(parameters); // Method sig: // getInboundChannel_ret(key channel) key rpcChan = (key) llList2String(paramList, 0); rpcChannelKeys += rpcChan; rpcChannelTypes += TYPE_INBOUND; if(trySend()) state started; } else if (methodName == "getOutboundChannel_ret" && callId == getOutboundChannelReturnVal) { list paramList = parseStringKeepNulls(parameters); // Method sig: // getOutboundCHannel_ret(key channel) key rpcChan = (key) llList2String(paramList, 0); rpcChannelKeys += rpcChan; rpcChannelTypes += TYPE_OUTBOUND; if (trySend()) state started; } } } state started { link_message(integer sender, integer num, string parameters, key methodName) { if (methodName == "requestMenuCategory") { list paramList = parseStringKeepNulls(parameters); // Method signature: // requestMenuCategory(integer ID) integer id = (integer) llList2String(paramList, 0); trigger_sendRpcData(GET_CATEGORY, CATEGORY_SEPERATOR + (string) id); trigger_displayStatusMessage("Sent data to server, waiting for reply..."); } else if (methodName == "requestMenuSummary") { list paramList = parseStringKeepNulls(parameters); // Method signature: // requestMenuSummary(integer ID) integer id = (integer) llList2String(paramList, 0); trigger_sendRpcData(GET_SUMMARY, CATEGORY_SEPERATOR + (string) id); trigger_displayStatusMessage("Sent data to server, waiting for reply..."); } else if (methodName == "requestMenuKeywordSearch") { list paramList = parseStringKeepNulls(parameters); // Method signature: // requestKeywordSearch(string keywords) string keywords = llList2String(paramList, 0); trigger_sendRpcData(GET_KEYWORD_SEARCH, keywords); trigger_displayStatusMessage("Sent data to server, waiting for reply..."); } else if (methodName == "connectionShutdown") { // Method signature: // requestPollingShutdown() list emailParams = [ "TerminalUUID", llGetKey()]; sendServerEmail(SHUTDOWN_MESSAGE_COMMAND, emailParams); state default; } else if (methodName == "connectionStartup") { // Method signature: // integer connectionStartup() startupCommandCallId = num; state starting; } else if (methodName == "ping") { list paramList = parseStringKeepNulls(parameters); // Method signature: // ping(string moduleName) string moduleName = llList2String(paramList, 0); if (moduleName == this) trigger_pong(this); } } }