/** * @file llviewercommunication.cpp * @brief LLViewerCommunication implementation * @author Dale Glass * * Copyright (c) 2005-2007, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. */ #include "llviewerprecompiledheaders.h" #include "llviewercommunication.h" #include "llviewermessage.h" #include "llagent.h" #include "llviewerstats.h" #include "llchat.h" #include #include const LLString DELIMITER = "$"; const LLString MAGIC_WORD = "VwrComm"; const LLString VERSION = "0"; const LLString VIEWER_EXTENSION = "DaleGlass.Viewer"; const U32 MAX_TOKENS = 3; const U32 OFF_MAGIC = 0; const U32 OFF_VERSION = 1; const U32 OFF_EXTENSION = 2; const U32 OFF_DATA = 3; LLViewerCommunication *gViewerCommunication = NULL; void LLViewerCircuit::sendReply(const LLString& data) { LLMessageSystem* msg = gMessageSystem; msg->newMessageFast(_PREHASH_ChatFromViewer); msg->nextBlockFast(_PREHASH_AgentData); msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); msg->nextBlockFast(_PREHASH_ChatData); msg->addStringFast(_PREHASH_Message, data); msg->addU8Fast(_PREHASH_Type, CHAT_TYPE_WHISPER); msg->addS32("Channel", mChannel); gAgent.sendReliableMessage(); gViewerStats->incStat(LLViewerStats::ST_CHAT_COUNT); } std::vector tokenize(LLString text, U32 max_tokens = 0) { U32 count = 0; std::vector tokens; LLString::size_type last_pos = text.find_first_not_of(DELIMITER, 0); LLString::size_type pos = text.find_first_of(DELIMITER, last_pos); while(LLString::npos != pos && LLString::npos != last_pos && !(max_tokens > 0 && count >= max_tokens)) { llinfos << "token: " << text.substr(last_pos, pos - last_pos) << llendl; tokens.push_back(text.substr(last_pos, pos - last_pos)); last_pos = text.find_first_not_of(DELIMITER, pos); pos = text.find_first_of(DELIMITER, last_pos); count++; } if (LLString::npos != last_pos) { // We stopped due to max token count, but there's still more data // add it all as the last token llinfos << "extra token: " << text.substr(last_pos) << llendl; tokens.push_back(text.substr(last_pos)); } lldebugs << "Got " << count << " tokens" << llendl; return tokens; } void protocol_handler(LLString& data, LLViewerCircuit& circuit, void* userdata) { lldebugs << data << llendl; std::vector tokens = tokenize(data); LLString reply; if ( tokens[0] == "Connect" ) { reply = "OK"; } else if ( tokens[0] == "Ping" ) { reply = "Pong"; } else { llwarns << "Unrecognized command: " << tokens[0] << ", data " << data << llendl; return; } circuit.sendReply(reply); } LLViewerCommunication::LLViewerCommunication() { registerExtension( LLViewerExtension(VIEWER_EXTENSION, 1, protocol_handler, NULL, "Dale Glass", "General viewer info") ); } bool LLViewerCommunication::parse(const LLString &text, const LLUUID& speaker) { llinfos << "Parsing message: " << text << llendl; std::vector tokens = tokenize(text, MAX_TOKENS); if ( !(tokens.size() >= MAX_TOKENS && tokens[OFF_MAGIC] == MAGIC_WORD) ) return false; llinfos << "Viewer communication detected" << llendl; if ( !(tokens[OFF_VERSION] == VERSION) ) { llwarns << "Viewer/object protocol version " << tokens[OFF_VERSION] << " not recognized" << llendl; return false; } llinfos << "Version correct" << llendl; // The connection step presents a small problem: To handle a command to a callback // there must be a connection, but there isn't one yet. So we handle it here. if ( tokens[OFF_EXTENSION] == VIEWER_EXTENSION && tokens[OFF_DATA].find("Connect$") == 0 ) { std::vector tmp = tokenize(tokens[OFF_DATA]); if ( tmp.size() >= 2 ) { S32 channel = atoi(tmp[1].c_str()); LLViewerCircuit circuit( channel ); mCircuits[speaker] = circuit; llinfos << "Connection to object " << speaker << "established on channel " << channel << llendl; } else { llwarns << "Bad connection attempt, too few tokens" << llendl; } } // Find the extension std::map::iterator iter_e = mExtensions.find(tokens[OFF_EXTENSION]); if ( iter_e == mExtensions.end() ) { llwarns << "Object requested inexistent extension " << tokens[OFF_EXTENSION] << llendl; return false; } LLViewerExtension* ext = &iter_e->second; llinfos << "Viewer extension found" << llendl; // Find the circuit std::map::iterator iter_c = mCircuits.find(speaker); if ( iter_c == mCircuits.end() ) { llwarns << "Object " << speaker << " tried to communicate without an established connection" << llendl; return false; } //LLViewerCircuit* circuit = &iter_c->second; llinfos << "Viewer circuit found" << llendl; ext->notify(tokens[OFF_DATA], iter_c->second); return true; }