llvoiceclient.cpp

Go to the documentation of this file.
00001 
00032 #include "llviewerprecompiledheaders.h"
00033 #include "llvoiceclient.h"
00034 
00035 #include <boost/tokenizer.hpp>
00036 
00037 #include "llsdutil.h"
00038 
00039 #include "llvoavatar.h"
00040 #include "llbufferstream.h"
00041 #include "llfile.h"
00042 #include "expat/expat.h"
00043 #include "llcallbacklist.h"
00044 #include "llviewerregion.h"
00045 #include "llviewernetwork.h"            // for gGridChoice
00046 #include "llbase64.h"
00047 #include "llviewercontrol.h"
00048 #include "llkeyboard.h"
00049 #include "llappviewer.h"        // for gDisconnected, gDisableVoice
00050 #include "llmutelist.h"  // to check for muted avatars
00051 #include "llagent.h"
00052 #include "llcachename.h"
00053 #include "llimview.h" // for LLIMMgr
00054 #include "llimpanel.h" // for LLVoiceChannel
00055 #include "llparcel.h"
00056 #include "llviewerparcelmgr.h"
00057 #include "llfirstuse.h"
00058 #include "llviewerwindow.h"
00059 
00060 // for base64 decoding
00061 #include "apr-1/apr_base64.h"
00062 
00063 // for SHA1 hash
00064 #include "apr-1/apr_sha1.h"
00065 
00066 // If we are connecting to agni AND the user's last name is "Linden", join this channel instead of looking up the sim name.
00067 // If we are connecting to agni and the user's last name is NOT "Linden", disable voice.
00068 #define AGNI_LINDENS_ONLY_CHANNEL "SL"
00069 static bool sConnectingToAgni = false;
00070 F32 LLVoiceClient::OVERDRIVEN_POWER_LEVEL = 0.7f;
00071 
00072 const F32 SPEAKING_TIMEOUT = 1.f;
00073 
00074 const int VOICE_MAJOR_VERSION = 1;
00075 const int VOICE_MINOR_VERSION = 0;
00076 
00077 LLVoiceClient *gVoiceClient = NULL;
00078 
00079 // Don't retry connecting to the daemon more frequently than this:
00080 const F32 CONNECT_THROTTLE_SECONDS = 1.0f;
00081 
00082 // Don't send positional updates more frequently than this:
00083 const F32 UPDATE_THROTTLE_SECONDS = 0.1f;
00084 
00085 const F32 LOGIN_RETRY_SECONDS = 10.0f;
00086 const int MAX_LOGIN_RETRIES = 12;
00087 
00088 class LLViewerVoiceAccountProvisionResponder :
00089         public LLHTTPClient::Responder
00090 {
00091 public:
00092         LLViewerVoiceAccountProvisionResponder(int retries)
00093         {
00094                 mRetries = retries;
00095         }
00096 
00097         virtual void error(U32 status, const std::string& reason)
00098         {
00099                 if ( mRetries > 0 )
00100                 {
00101                         if ( gVoiceClient ) gVoiceClient->requestVoiceAccountProvision(
00102                                 mRetries - 1);
00103                 }
00104                 else
00105                 {
00106                         //TODO: throw an error message?
00107                         if ( gVoiceClient ) gVoiceClient->giveUp();
00108                 }
00109         }
00110 
00111         virtual void result(const LLSD& content)
00112         {
00113                 if ( gVoiceClient )
00114                 {
00115                         gVoiceClient->login(
00116                                 content["username"].asString(),
00117                                 content["password"].asString());
00118                 }
00119         }
00120 
00121 private:
00122         int mRetries;
00123 };
00124 
00132 class LLVivoxProtocolParser : public LLIOPipe
00133 {
00134         LOG_CLASS(LLVivoxProtocolParser);
00135 public:
00136         LLVivoxProtocolParser();
00137         virtual ~LLVivoxProtocolParser();
00138 
00139 protected:
00140         /* @name LLIOPipe virtual implementations
00141          */
00143 
00146         virtual EStatus process_impl(
00147                 const LLChannelDescriptors& channels,
00148                 buffer_ptr_t& buffer,
00149                 bool& eos,
00150                 LLSD& context,
00151                 LLPumpIO* pump);
00153         
00154         std::string     mInput;
00155         
00156         // Expat control members
00157         XML_Parser              parser;
00158         int                             responseDepth;
00159         bool                    ignoringTags;
00160         bool                    isEvent;
00161         int                             ignoreDepth;
00162 
00163         // Members for processing responses. The values are transient and only valid within a call to processResponse().
00164         int                             returnCode;
00165         int                             statusCode;
00166         std::string             statusString;
00167         std::string             uuidString;
00168         std::string             actionString;
00169         std::string             connectorHandle;
00170         std::string             accountHandle;
00171         std::string             sessionHandle;
00172         std::string             eventSessionHandle;
00173 
00174         // Members for processing events. The values are transient and only valid within a call to processResponse().
00175         std::string             eventTypeString;
00176         int                             state;
00177         std::string             uriString;
00178         bool                    isChannel;
00179         std::string             nameString;
00180         std::string             audioMediaString;
00181         std::string             displayNameString;
00182         int                             participantType;
00183         bool                    isLocallyMuted;
00184         bool                    isModeratorMuted;
00185         bool                    isSpeaking;
00186         int                             volume;
00187         F32                             energy;
00188 
00189         // Members for processing text between tags
00190         std::string             textBuffer;
00191         bool                    accumulateText;
00192         
00193         void                    reset();
00194 
00195         void                    processResponse(std::string tag);
00196 
00197 static void XMLCALL ExpatStartTag(void *data, const char *el, const char **attr);
00198 static void XMLCALL ExpatEndTag(void *data, const char *el);
00199 static void XMLCALL ExpatCharHandler(void *data, const XML_Char *s, int len);
00200 
00201         void                    StartTag(const char *tag, const char **attr);
00202         void                    EndTag(const char *tag);
00203         void                    CharData(const char *buffer, int length);
00204         
00205 };
00206 
00207 LLVivoxProtocolParser::LLVivoxProtocolParser()
00208 {
00209         parser = NULL;
00210         parser = XML_ParserCreate(NULL);
00211         
00212         reset();
00213 }
00214 
00215 void LLVivoxProtocolParser::reset()
00216 {
00217         responseDepth = 0;
00218         ignoringTags = false;
00219         accumulateText = false;
00220         textBuffer.clear();
00221 }
00222 
00223 //virtual 
00224 LLVivoxProtocolParser::~LLVivoxProtocolParser()
00225 {
00226         if (parser)
00227                 XML_ParserFree(parser);
00228 }
00229 
00230 // virtual
00231 LLIOPipe::EStatus LLVivoxProtocolParser::process_impl(
00232         const LLChannelDescriptors& channels,
00233         buffer_ptr_t& buffer,
00234         bool& eos,
00235         LLSD& context,
00236         LLPumpIO* pump)
00237 {
00238         LLBufferStream istr(channels, buffer.get());
00239         std::ostringstream ostr;
00240         while (istr.good())
00241         {
00242                 char buf[1024];
00243                 istr.read(buf, sizeof(buf));
00244                 mInput.append(buf, istr.gcount());
00245         }
00246         
00247         // MBW -- XXX -- This should no longer be necessary.  Or even possible.
00248         // We've read all the data out of the buffer.  Make sure it doesn't accumulate.
00249 //      buffer->clear();
00250         
00251         // Look for input delimiter(s) in the input buffer.  If one is found, send the message to the xml parser.
00252         int start = 0;
00253         int delim;
00254         while((delim = mInput.find("\n\n\n", start)) != std::string::npos)
00255         {       
00256                 // Turn this on to log incoming XML
00257                 if(0)
00258                 {
00259                         int foo = mInput.find("Set3DPosition", start);
00260                         int bar = mInput.find("ParticipantPropertiesEvent", start);
00261                         if(foo != std::string::npos && (foo < delim))
00262                         {
00263                                 // This is a Set3DPosition response.  Don't print it, since these are way too spammy.
00264                         }
00265                         else if(bar != std::string::npos && (bar < delim))
00266                         {
00267                                 // This is a ParticipantPropertiesEvent response.  Don't print it, since these are way too spammy.
00268                         }
00269                         else
00270                         {
00271                                 LL_INFOS("Voice") << "parsing: " << mInput.substr(start, delim - start) << LL_ENDL;
00272                         }
00273                 }
00274                 
00275                 // Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser)
00276                 reset();
00277                 
00278                 XML_ParserReset(parser, NULL);
00279                 XML_SetElementHandler(parser, ExpatStartTag, ExpatEndTag);
00280                 XML_SetCharacterDataHandler(parser, ExpatCharHandler);
00281                 XML_SetUserData(parser, this);  
00282                 XML_Parse(parser, mInput.data() + start, delim - start, false);
00283                 
00284                 start = delim + 3;
00285         }
00286         
00287         if(start != 0)
00288                 mInput = mInput.substr(start);
00289 
00290         LL_DEBUGS("Voice") << "at end, mInput is: " << mInput << LL_ENDL;
00291         
00292         if(!gVoiceClient->mConnected)
00293         {
00294                 // If voice has been disabled, we just want to close the socket.  This does so.
00295                 LL_INFOS("Voice") << "returning STATUS_STOP" << LL_ENDL;
00296                 return STATUS_STOP;
00297         }
00298         
00299         return STATUS_OK;
00300 }
00301 
00302 void XMLCALL LLVivoxProtocolParser::ExpatStartTag(void *data, const char *el, const char **attr)
00303 {
00304         if (data)
00305         {
00306                 LLVivoxProtocolParser   *object = (LLVivoxProtocolParser*)data;
00307                 object->StartTag(el, attr);
00308         }
00309 }
00310 
00311 // --------------------------------------------------------------------------------
00312 
00313 void XMLCALL LLVivoxProtocolParser::ExpatEndTag(void *data, const char *el)
00314 {
00315         if (data)
00316         {
00317                 LLVivoxProtocolParser   *object = (LLVivoxProtocolParser*)data;
00318                 object->EndTag(el);
00319         }
00320 }
00321 
00322 // --------------------------------------------------------------------------------
00323 
00324 void XMLCALL LLVivoxProtocolParser::ExpatCharHandler(void *data, const XML_Char *s, int len)
00325 {
00326         if (data)
00327         {
00328                 LLVivoxProtocolParser   *object = (LLVivoxProtocolParser*)data;
00329                 object->CharData(s, len);
00330         }
00331 }
00332 
00333 // --------------------------------------------------------------------------------
00334 
00335 
00336 void LLVivoxProtocolParser::StartTag(const char *tag, const char **attr)
00337 {
00338         // Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags
00339         textBuffer.clear();
00340         // only accumulate text if we're not ignoring tags.
00341         accumulateText = !ignoringTags;
00342         
00343         if (responseDepth == 0)
00344         {       
00345                 isEvent = strcmp("Event", tag) == 0;
00346                 
00347                 if (strcmp("Response", tag) == 0 || isEvent)
00348                 {
00349                         // Grab the attributes
00350                         while (*attr)
00351                         {
00352                                 const char      *key = *attr++;
00353                                 const char      *value = *attr++;
00354                                 
00355                                 if (strcmp("requestId", key) == 0)
00356                                 {
00357                                         uuidString = value;
00358                                 }
00359                                 else if (strcmp("action", key) == 0)
00360                                 {
00361                                         actionString = value;
00362                                 }
00363                                 else if (strcmp("type", key) == 0)
00364                                 {
00365                                         eventTypeString = value;
00366                                 }
00367                         }
00368                 }
00369                 LL_DEBUGS("Voice") << tag << " (" << responseDepth << ")"  << LL_ENDL;
00370         }
00371         else
00372         {
00373                 if (ignoringTags)
00374                 {
00375                         LL_DEBUGS("Voice") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
00376                 }
00377                 else
00378                 {
00379                         LL_DEBUGS("Voice") << tag << " (" << responseDepth << ")"  << LL_ENDL;
00380         
00381                         // Ignore the InputXml stuff so we don't get confused
00382                         if (strcmp("InputXml", tag) == 0)
00383                         {
00384                                 ignoringTags = true;
00385                                 ignoreDepth = responseDepth;
00386                                 accumulateText = false;
00387 
00388                                 LL_DEBUGS("Voice") << "starting ignore, ignoreDepth is " << ignoreDepth << LL_ENDL;
00389                         }
00390                         else if (strcmp("CaptureDevices", tag) == 0)
00391                         {
00392                                 gVoiceClient->clearCaptureDevices();
00393                         }
00394                         else if (strcmp("RenderDevices", tag) == 0)
00395                         {
00396                                 gVoiceClient->clearRenderDevices();
00397                         }
00398                 }
00399         }
00400         responseDepth++;
00401 }
00402 
00403 // --------------------------------------------------------------------------------
00404 
00405 void LLVivoxProtocolParser::EndTag(const char *tag)
00406 {
00407         const char      *string = textBuffer.c_str();
00408         bool clearbuffer = true;
00409 
00410         responseDepth--;
00411 
00412         if (ignoringTags)
00413         {
00414                 if (ignoreDepth == responseDepth)
00415                 {
00416                         LL_DEBUGS("Voice") << "end of ignore" << LL_ENDL;
00417                         ignoringTags = false;
00418                 }
00419                 else
00420                 {
00421                         LL_DEBUGS("Voice") << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
00422                 }
00423         }
00424         
00425         if (!ignoringTags)
00426         {
00427                 LL_DEBUGS("Voice") << "processing tag " << tag << " (depth = " << responseDepth << ")" << LL_ENDL;
00428 
00429                 // Closing a tag. Finalize the text we've accumulated and reset
00430                 if (strcmp("ReturnCode", tag) == 0)
00431                         returnCode = strtol(string, NULL, 10);
00432                 else if (strcmp("StatusCode", tag) == 0)
00433                         statusCode = strtol(string, NULL, 10);
00434                 else if (strcmp("ConnectorHandle", tag) == 0)
00435                         connectorHandle = string;
00436                 else if (strcmp("AccountHandle", tag) == 0)
00437                         accountHandle = string;
00438                 else if (strcmp("SessionHandle", tag) == 0)
00439                 {
00440                         if (isEvent)
00441                                 eventSessionHandle = string;
00442                         else
00443                                 sessionHandle = string;
00444                 }
00445                 else if (strcmp("StatusString", tag) == 0)
00446                         statusString = string;
00447                 else if (strcmp("State", tag) == 0)
00448                         state = strtol(string, NULL, 10);
00449                 else if (strcmp("URI", tag) == 0)
00450                         uriString = string;
00451                 else if (strcmp("IsChannel", tag) == 0)
00452                         isChannel = strcmp(string, "true") == 0;
00453                 else if (strcmp("Name", tag) == 0)
00454                         nameString = string;
00455                 else if (strcmp("AudioMedia", tag) == 0)
00456                         audioMediaString = string;
00457                 else if (strcmp("ChannelName", tag) == 0)
00458                         nameString = string;
00459                 else if (strcmp("ParticipantURI", tag) == 0)
00460                         uriString = string;
00461                 else if (strcmp("DisplayName", tag) == 0)
00462                         displayNameString = string;
00463                 else if (strcmp("AccountName", tag) == 0)
00464                         nameString = string;
00465                 else if (strcmp("ParticipantTyppe", tag) == 0)
00466                         participantType = strtol(string, NULL, 10);
00467                 else if (strcmp("IsLocallyMuted", tag) == 0)
00468                         isLocallyMuted = strcmp(string, "true") == 0;
00469                 else if (strcmp("IsModeratorMuted", tag) == 0)
00470                         isModeratorMuted = strcmp(string, "true") == 0;
00471                 else if (strcmp("IsSpeaking", tag) == 0)
00472                         isSpeaking = strcmp(string, "true") == 0;
00473                 else if (strcmp("Volume", tag) == 0)
00474                         volume = strtol(string, NULL, 10);
00475                 else if (strcmp("Energy", tag) == 0)
00476                         energy = (F32)strtod(string, NULL);
00477                 else if (strcmp("MicEnergy", tag) == 0)
00478                         energy = (F32)strtod(string, NULL);
00479                 else if (strcmp("ChannelName", tag) == 0)
00480                         nameString = string;
00481                 else if (strcmp("ChannelURI", tag) == 0)
00482                         uriString = string;
00483                 else if (strcmp("ChannelListResult", tag) == 0)
00484                 {
00485                         gVoiceClient->addChannelMapEntry(nameString, uriString);
00486                 }
00487                 else if (strcmp("Device", tag) == 0)
00488                 {
00489                         // This closing tag shouldn't clear the accumulated text.
00490                         clearbuffer = false;
00491                 }
00492                 else if (strcmp("CaptureDevice", tag) == 0)
00493                 {
00494                         gVoiceClient->addCaptureDevice(textBuffer);
00495                 }
00496                 else if (strcmp("RenderDevice", tag) == 0)
00497                 {
00498                         gVoiceClient->addRenderDevice(textBuffer);
00499                 }
00500 
00501                 if(clearbuffer)
00502                 {
00503                         textBuffer.clear();
00504                         accumulateText= false;
00505                 }
00506                 
00507                 if (responseDepth == 0)
00508                 {
00509                         // We finished all of the XML, process the data
00510                         processResponse(tag);
00511                 }
00512         }
00513 }
00514 
00515 // --------------------------------------------------------------------------------
00516 
00517 void LLVivoxProtocolParser::CharData(const char *buffer, int length)
00518 {
00519         /*
00520                 This method is called for anything that isn't a tag, which can be text you
00521                 want that lies between tags, and a lot of stuff you don't want like file formatting
00522                 (tabs, spaces, CR/LF, etc).
00523                 
00524                 Only copy text if we are in accumulate mode...
00525         */
00526         if (accumulateText)
00527                 textBuffer.append(buffer, length);
00528 }
00529 
00530 // --------------------------------------------------------------------------------
00531 
00532 void LLVivoxProtocolParser::processResponse(std::string tag)
00533 {
00534         LL_DEBUGS("Voice") << tag << LL_ENDL;
00535 
00536         if (isEvent)
00537         {
00538                 if (eventTypeString == "LoginStateChangeEvent")
00539                 {
00540                         gVoiceClient->loginStateChangeEvent(accountHandle, statusCode, statusString, state);
00541                 }
00542                 else if (eventTypeString == "SessionNewEvent")
00543                 {
00544                         gVoiceClient->sessionNewEvent(accountHandle, eventSessionHandle, state, nameString, uriString);
00545                 }
00546                 else if (eventTypeString == "SessionStateChangeEvent")
00547                 {
00548                         gVoiceClient->sessionStateChangeEvent(uriString, statusCode, statusString, eventSessionHandle, state, isChannel, nameString);
00549                 }
00550                 else if (eventTypeString == "ParticipantStateChangeEvent")
00551                 {
00552                         gVoiceClient->participantStateChangeEvent(uriString, statusCode, statusString, state,  nameString, displayNameString, participantType);
00553                         
00554                 }
00555                 else if (eventTypeString == "ParticipantPropertiesEvent")
00556                 {
00557                         gVoiceClient->participantPropertiesEvent(uriString, statusCode, statusString, isLocallyMuted, isModeratorMuted, isSpeaking, volume, energy);
00558                 }
00559                 else if (eventTypeString == "AuxAudioPropertiesEvent")
00560                 {
00561                         gVoiceClient->auxAudioPropertiesEvent(energy);
00562                 }
00563         }
00564         else
00565         {
00566                 if (actionString == "Connector.Create.1")
00567                 {
00568                         gVoiceClient->connectorCreateResponse(statusCode, statusString, connectorHandle);
00569                 }
00570                 else if (actionString == "Account.Login.1")
00571                 {
00572                         gVoiceClient->loginResponse(statusCode, statusString, accountHandle);
00573                 }
00574                 else if (actionString == "Session.Create.1")
00575                 {
00576                         gVoiceClient->sessionCreateResponse(statusCode, statusString, sessionHandle);                   
00577                 }
00578                 else if (actionString == "Session.Connect.1")
00579                 {
00580                         gVoiceClient->sessionConnectResponse(statusCode, statusString);                 
00581                 }
00582                 else if (actionString == "Session.Terminate.1")
00583                 {
00584                         gVoiceClient->sessionTerminateResponse(statusCode, statusString);                       
00585                 }
00586                 else if (actionString == "Account.Logout.1")
00587                 {
00588                         gVoiceClient->logoutResponse(statusCode, statusString);                 
00589                 }
00590                 else if (actionString == "Connector.InitiateShutdown.1")
00591                 {
00592                         gVoiceClient->connectorShutdownResponse(statusCode, statusString);                      
00593                 }
00594                 else if (actionString == "Account.ChannelGetList.1")
00595                 {
00596                         gVoiceClient->channelGetListResponse(statusCode, statusString);
00597                 }
00598 /*
00599                 else if (actionString == "Connector.AccountCreate.1")
00600                 {
00601                         
00602                 }
00603                 else if (actionString == "Connector.MuteLocalMic.1")
00604                 {
00605                         
00606                 }
00607                 else if (actionString == "Connector.MuteLocalSpeaker.1")
00608                 {
00609                         
00610                 }
00611                 else if (actionString == "Connector.SetLocalMicVolume.1")
00612                 {
00613                         
00614                 }
00615                 else if (actionString == "Connector.SetLocalSpeakerVolume.1")
00616                 {
00617                         
00618                 }
00619                 else if (actionString == "Session.ListenerSetPosition.1")
00620                 {
00621                         
00622                 }
00623                 else if (actionString == "Session.SpeakerSetPosition.1")
00624                 {
00625                         
00626                 }
00627                 else if (actionString == "Session.Set3DPosition.1")
00628                 {
00629                         
00630                 }
00631                 else if (actionString == "Session.AudioSourceSetPosition.1")
00632                 {
00633                         
00634                 }
00635                 else if (actionString == "Session.GetChannelParticipants.1")
00636                 {
00637                         
00638                 }
00639                 else if (actionString == "Account.ChannelCreate.1")
00640                 {
00641                         
00642                 }
00643                 else if (actionString == "Account.ChannelUpdate.1")
00644                 {
00645                         
00646                 }
00647                 else if (actionString == "Account.ChannelDelete.1")
00648                 {
00649                         
00650                 }
00651                 else if (actionString == "Account.ChannelCreateAndInvite.1")
00652                 {
00653                         
00654                 }
00655                 else if (actionString == "Account.ChannelFolderCreate.1")
00656                 {
00657                         
00658                 }
00659                 else if (actionString == "Account.ChannelFolderUpdate.1")
00660                 {
00661                         
00662                 }
00663                 else if (actionString == "Account.ChannelFolderDelete.1")
00664                 {
00665                         
00666                 }
00667                 else if (actionString == "Account.ChannelAddModerator.1")
00668                 {
00669                         
00670                 }
00671                 else if (actionString == "Account.ChannelDeleteModerator.1")
00672                 {
00673                         
00674                 }
00675 */
00676         }
00677 }
00678 
00680 
00681 class LLVoiceClientMuteListObserver : public LLMuteListObserver
00682 {
00683         /* virtual */ void onChange()  { gVoiceClient->muteListChanged();}
00684 };
00685 static LLVoiceClientMuteListObserver mutelist_listener;
00686 static bool sMuteListListener_listening = false;
00687 
00689 
00690 class LLVoiceClientCapResponder : public LLHTTPClient::Responder
00691 {
00692 public:
00693         LLVoiceClientCapResponder(void){};
00694 
00695         virtual void error(U32 status, const std::string& reason);      // called with bad status codes
00696         virtual void result(const LLSD& content);
00697 
00698 private:
00699 };
00700 
00701 void LLVoiceClientCapResponder::error(U32 status, const std::string& reason)
00702 {
00703         LL_WARNS("Voice") << "LLVoiceClientCapResponder::error("
00704                 << status << ": " << reason << ")"
00705                 << LL_ENDL;
00706 }
00707 
00708 void LLVoiceClientCapResponder::result(const LLSD& content)
00709 {
00710         LLSD::map_const_iterator iter;
00711         for(iter = content.beginMap(); iter != content.endMap(); ++iter)
00712         {
00713                 LL_DEBUGS("Voice") << "LLVoiceClientCapResponder::result got " 
00714                         << iter->first << LL_ENDL;
00715         }
00716 
00717         if ( content.has("voice_credentials") )
00718         {
00719                 LLSD voice_credentials = content["voice_credentials"];
00720                 std::string uri;
00721                 std::string credentials;
00722 
00723                 if ( voice_credentials.has("channel_uri") )
00724                 {
00725                         uri = voice_credentials["channel_uri"].asString();
00726                 }
00727                 if ( voice_credentials.has("channel_credentials") )
00728                 {
00729                         credentials =
00730                                 voice_credentials["channel_credentials"].asString();
00731                 }
00732 
00733                 gVoiceClient->setSpatialChannel(uri, credentials);
00734         }
00735 }
00736 
00737 
00738 
00739 #if LL_WINDOWS
00740 static HANDLE sGatewayHandle = 0;
00741 
00742 static bool isGatewayRunning()
00743 {
00744         bool result = false;
00745         if(sGatewayHandle != 0)         
00746         {
00747                 DWORD waitresult = WaitForSingleObject(sGatewayHandle, 0);
00748                 if(waitresult != WAIT_OBJECT_0)
00749                 {
00750                         result = true;
00751                 }                       
00752         }
00753         return result;
00754 }
00755 static void killGateway()
00756 {
00757         if(sGatewayHandle != 0)
00758         {
00759                 TerminateProcess(sGatewayHandle,0);
00760         }
00761 }
00762 
00763 #else // Mac and linux
00764 
00765 static pid_t sGatewayPID = 0;
00766 static bool isGatewayRunning()
00767 {
00768         bool result = false;
00769         if(sGatewayPID != 0)
00770         {
00771                 // A kill with signal number 0 has no effect, just does error checking.  It should return an error if the process no longer exists.
00772                 if(kill(sGatewayPID, 0) == 0)
00773                 {
00774                         result = true;
00775                 }
00776         }
00777         return result;
00778 }
00779 
00780 static void killGateway()
00781 {
00782         if(sGatewayPID != 0)
00783         {
00784                 kill(sGatewayPID, SIGTERM);
00785         }
00786 }
00787 
00788 #endif
00789 
00791 
00792 LLVoiceClient::LLVoiceClient()
00793 {       
00794         gVoiceClient = this;
00795         mWriteInProgress = false;
00796         mAreaVoiceDisabled = false;
00797         mPTT = true;
00798         mUserPTTState = false;
00799         mMuteMic = false;
00800         mSessionTerminateRequested = false;
00801         mCommandCookie = 0;
00802         mNonSpatialChannel = false;
00803         mNextSessionSpatial = true;
00804         mNextSessionNoReconnect = false;
00805         mSessionP2P = false;
00806         mCurrentParcelLocalID = 0;
00807         mLoginRetryCount = 0;
00808         mVivoxErrorStatusCode = 0;
00809 
00810         mNextSessionResetOnClose = false;
00811         mSessionResetOnClose = false;
00812         mSpeakerVolume = 0;
00813         mMicVolume = 0;
00814 
00815         // Initial dirty state
00816         mSpatialCoordsDirty = false;
00817         mPTTDirty = true;
00818         mVolumeDirty = true;
00819         mSpeakerVolumeDirty = true;
00820         mMicVolumeDirty = true;
00821         mCaptureDeviceDirty = false;
00822         mRenderDeviceDirty = false;
00823 
00824         // Load initial state from prefs.
00825         mVoiceEnabled = gSavedSettings.getBOOL("EnableVoiceChat");
00826         mUsePTT = TRUE; //gSavedSettings.getBOOL("EnablePushToTalk");
00827         std::string keyString = gSavedSettings.getString("PushToTalkButton");
00828         setPTTKey(keyString);
00829         mPTTIsToggle = gSavedSettings.getBOOL("PushToTalkToggle");
00830         mEarLocation = gSavedSettings.getS32("VoiceEarLocation");
00831         setVoiceVolume(gSavedSettings.getBOOL("MuteVoice") ? 0.f : gSavedSettings.getF32("AudioLevelVoice"));
00832         std::string captureDevice = gSavedSettings.getString("VoiceInputAudioDevice");
00833         setCaptureDevice(captureDevice);
00834         std::string renderDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
00835         setRenderDevice(renderDevice);
00836         mLipSyncEnabled = gSavedSettings.getU32("LipSyncEnabled");
00837         
00838         mTuningMode = false;
00839         mTuningEnergy = 0.0f;
00840         mTuningMicVolume = 0;
00841         mTuningMicVolumeDirty = true;
00842         mTuningSpeakerVolume = 0;
00843         mTuningSpeakerVolumeDirty = true;
00844                                         
00845         //  gMuteListp isn't set up at this point, so we defer this until later.
00846 //      gMuteListp->addObserver(&mutelist_listener);
00847         
00848         mParticipantMapChanged = false;
00849 
00850         // stash the pump for later use
00851         // This now happens when init() is called instead.
00852         mPump = NULL;
00853         
00854 #if LL_DARWIN || LL_LINUX
00855                 // MBW -- XXX -- THIS DOES NOT BELONG HERE
00856                 // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us.
00857                 // This should cause us to ignore SIGPIPE and handle the error through proper channels.
00858                 // This should really be set up elsewhere.  Where should it go?
00859                 signal(SIGPIPE, SIG_IGN);
00860                 
00861                 // Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes.
00862                 // Ignoring SIGCHLD should prevent zombies from being created.  Alternately, we could use wait(), but I'd rather not do that.
00863                 signal(SIGCHLD, SIG_IGN);
00864 #endif
00865 
00866         // set up state machine
00867         setState(stateDisabled);
00868         
00869         gIdleCallbacks.addFunction(idle, this);
00870 }
00871 
00872 //---------------------------------------------------
00873 
00874 LLVoiceClient::~LLVoiceClient()
00875 {
00876 }
00877 
00878 //----------------------------------------------
00879 
00880 
00881 
00882 void LLVoiceClient::init(LLPumpIO *pump)
00883 {
00884         // constructor will set up gVoiceClient
00885         LLVoiceClient::getInstance()->mPump = pump;
00886 }
00887 
00888 void LLVoiceClient::terminate()
00889 {
00890         if(gVoiceClient)
00891         {
00892                 gVoiceClient->sessionTerminateSendMessage();
00893                 gVoiceClient->logout();
00894                 gVoiceClient->connectorShutdown();
00895                 gVoiceClient->closeSocket();            // Need to do this now -- bad things happen if the destructor does it later.
00896                 
00897                 // This will do unpleasant things on windows.
00898 //              killGateway();
00899                 
00900                 // Don't do this anymore -- LLSingleton will take care of deleting the object.          
00901 //              delete gVoiceClient;
00902                 
00903                 // Hint to other code not to access the voice client anymore.
00904                 gVoiceClient = NULL;
00905         }
00906 }
00907 
00908 
00910 // utility functions
00911 
00912 bool LLVoiceClient::writeString(const std::string &str)
00913 {
00914         bool result = false;
00915         if(mConnected)
00916         {
00917                 apr_status_t err;
00918                 apr_size_t size = (apr_size_t)str.size();
00919                 apr_size_t written = size;
00920         
00921                 LL_DEBUGS("Voice") << "sending: " << str << LL_ENDL;
00922 
00923                 // MBW -- XXX -- check return code - sockets will fail (broken, etc.)
00924                 err = apr_socket_send(
00925                                 mSocket->getSocket(),
00926                                 (const char*)str.data(),
00927                                 &written);
00928                 
00929                 if(err == 0)
00930                 {
00931                         // Success.
00932                         result = true;
00933                 }
00934                 // MBW -- XXX -- handle partial writes (written is number of bytes written)
00935                 // Need to set socket to non-blocking before this will work.
00936 //              else if(APR_STATUS_IS_EAGAIN(err))
00937 //              {
00938 //                      // 
00939 //              }
00940                 else
00941                 {
00942                         // Assume any socket error means something bad.  For now, just close the socket.
00943                         char buf[MAX_STRING];
00944                         LL_WARNS("Voice") << "apr error " << err << " ("<< apr_strerror(err, buf, MAX_STRING) << ") sending data to vivox daemon." << LL_ENDL;
00945                         daemonDied();
00946                 }
00947         }
00948                 
00949         return result;
00950 }
00951 
00952 
00954 // session control messages
00955 void LLVoiceClient::connectorCreate()
00956 {
00957         std::ostringstream stream;
00958         std::string logpath;
00959         std::string loglevel = "0";
00960         
00961         // Transition to stateConnectorStarted when the connector handle comes back.
00962         setState(stateConnectorStarting);
00963 
00964         std::string savedLogLevel = gSavedSettings.getString("VivoxDebugLevel");
00965         
00966         if(savedLogLevel != "-1")
00967         {
00968                 LL_DEBUGS("Voice") << "creating connector with logging enabled" << LL_ENDL;
00969                 loglevel = "10";
00970                 logpath = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "");
00971         }
00972         
00973         stream 
00974         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.Create.1\">"
00975                 << "<ClientName>V2 SDK</ClientName>"
00976                 << "<AccountManagementServer>" << mAccountServerURI << "</AccountManagementServer>"
00977                 << "<Logging>"
00978                         << "<Enabled>false</Enabled>"
00979                         << "<Folder>" << logpath << "</Folder>"
00980                         << "<FileNamePrefix>Connector</FileNamePrefix>"
00981                         << "<FileNameSuffix>.log</FileNameSuffix>"
00982                         << "<LogLevel>" << loglevel << "</LogLevel>"
00983                 << "</Logging>"
00984         << "</Request>\n\n\n";
00985         
00986         writeString(stream.str());
00987 }
00988 
00989 void LLVoiceClient::connectorShutdown()
00990 {
00991         setState(stateConnectorStopping);
00992         
00993         if(!mConnectorHandle.empty())
00994         {
00995                 std::ostringstream stream;
00996                 stream
00997                 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.InitiateShutdown.1\">"
00998                         << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
00999                 << "</Request>"
01000                 << "\n\n\n";
01001                 
01002                 mConnectorHandle.clear();
01003                 
01004                 writeString(stream.str());
01005         }
01006 }
01007 
01008 void LLVoiceClient::userAuthorized(const std::string& firstName, const std::string& lastName, const LLUUID &agentID)
01009 {
01010         mAccountFirstName = firstName;
01011         mAccountLastName = lastName;
01012 
01013         mAccountDisplayName = firstName;
01014         mAccountDisplayName += " ";
01015         mAccountDisplayName += lastName;
01016 
01017         LL_INFOS("Voice") << "name \"" << mAccountDisplayName << "\" , ID " << agentID << LL_ENDL;
01018 
01019         std::string gridname = gGridName;
01020         LLString::toLower(gridname);
01021         if((gGridChoice == GRID_INFO_AGNI) || 
01022                 ((gGridChoice == GRID_INFO_OTHER) && (gridname.find("agni") != std::string::npos)))
01023         {
01024                 sConnectingToAgni = true;
01025         }
01026 
01027         // MBW -- XXX -- Enable this when the bhd.vivox.com server gets a real ssl cert.        
01028         if(sConnectingToAgni)
01029         {
01030                 // Use the release account server
01031                 mAccountServerName = "bhr.vivox.com";
01032                 mAccountServerURI = "https://www." + mAccountServerName + "/api2/";
01033         }
01034         else
01035         {
01036                 // Use the development account server
01037                 mAccountServerName = gSavedSettings.getString("VivoxDebugServerName");
01038                 mAccountServerURI = "https://www." + mAccountServerName + "/api2/";
01039         }
01040 
01041         mAccountName = nameFromID(agentID);
01042 }
01043 
01044 void LLVoiceClient::requestVoiceAccountProvision(S32 retries)
01045 {
01046         if ( gAgent.getRegion() && mVoiceEnabled )
01047         {
01048                 std::string url = 
01049                         gAgent.getRegion()->getCapability(
01050                                 "ProvisionVoiceAccountRequest");
01051 
01052                 if ( url == "" ) return;
01053 
01054                 LLHTTPClient::post(
01055                         url,
01056                         LLSD(),
01057                         new LLViewerVoiceAccountProvisionResponder(retries));
01058         }
01059 }
01060 
01061 void LLVoiceClient::login(
01062         const std::string& accountName,
01063         const std::string &password)
01064 {
01065         if((getState() >= stateLoggingIn) && (getState() < stateLoggedOut))
01066         {
01067                 // Already logged in.  This is an internal error.
01068                 LL_ERRS("Voice") << "Can't login again. Called from wrong state." << LL_ENDL;
01069         }
01070         else if ( accountName != mAccountName )
01071         {
01072                 //TODO: error?
01073                 LL_WARNS("Voice") << "Wrong account name! " << accountName
01074                                 << " instead of " << mAccountName << LL_ENDL;
01075         }
01076         else
01077         {
01078                 mAccountPassword = password;
01079         }
01080 }
01081 
01082 void LLVoiceClient::idle(void* user_data)
01083 {
01084         LLVoiceClient* self = (LLVoiceClient*)user_data;
01085         self->stateMachine();
01086 }
01087 
01088 const char *LLVoiceClient::state2string(LLVoiceClient::state inState)
01089 {
01090         const char *result = "UNKNOWN";
01091         
01092                 // Prevent copy-paste errors when updating this list...
01093 #define CASE(x)  case x:  result = #x;  break
01094 
01095         switch(inState)
01096         {
01097                 CASE(stateDisabled);
01098                 CASE(stateStart);
01099                 CASE(stateDaemonLaunched);
01100                 CASE(stateConnecting);
01101                 CASE(stateIdle);
01102                 CASE(stateConnectorStart);
01103                 CASE(stateConnectorStarting);
01104                 CASE(stateConnectorStarted);
01105                 CASE(stateLoginRetry);
01106                 CASE(stateLoginRetryWait);
01107                 CASE(stateNeedsLogin);
01108                 CASE(stateLoggingIn);
01109                 CASE(stateLoggedIn);
01110                 CASE(stateNoChannel);
01111                 CASE(stateMicTuningStart);
01112                 CASE(stateMicTuningRunning);
01113                 CASE(stateMicTuningStop);
01114                 CASE(stateSessionCreate);
01115                 CASE(stateSessionConnect);
01116                 CASE(stateJoiningSession);
01117                 CASE(stateSessionJoined);
01118                 CASE(stateRunning);
01119                 CASE(stateLeavingSession);
01120                 CASE(stateSessionTerminated);
01121                 CASE(stateLoggingOut);
01122                 CASE(stateLoggedOut);
01123                 CASE(stateConnectorStopping);
01124                 CASE(stateConnectorStopped);
01125                 CASE(stateConnectorFailed);
01126                 CASE(stateConnectorFailedWaiting);
01127                 CASE(stateLoginFailed);
01128                 CASE(stateLoginFailedWaiting);
01129                 CASE(stateJoinSessionFailed);
01130                 CASE(stateJoinSessionFailedWaiting);
01131                 CASE(stateJail);
01132                 CASE(stateMicTuningNoLogin);
01133         }
01134 
01135 #undef CASE
01136         
01137         return result;
01138 }
01139 
01140 const char *LLVoiceClientStatusObserver::status2string(LLVoiceClientStatusObserver::EStatusType inStatus)
01141 {
01142         const char *result = "UNKNOWN";
01143         
01144                 // Prevent copy-paste errors when updating this list...
01145 #define CASE(x)  case x:  result = #x;  break
01146 
01147         switch(inStatus)
01148         {
01149                 CASE(STATUS_LOGIN_RETRY);
01150                 CASE(STATUS_LOGGED_IN);
01151                 CASE(STATUS_JOINING);
01152                 CASE(STATUS_JOINED);
01153                 CASE(STATUS_LEFT_CHANNEL);
01154                 CASE(BEGIN_ERROR_STATUS);
01155                 CASE(ERROR_CHANNEL_FULL);
01156                 CASE(ERROR_CHANNEL_LOCKED);
01157                 CASE(ERROR_NOT_AVAILABLE);
01158                 CASE(ERROR_UNKNOWN);
01159         default:
01160                 break;
01161         }
01162 
01163 #undef CASE
01164         
01165         return result;
01166 }
01167 
01168 void LLVoiceClient::setState(state inState)
01169 {
01170         LL_DEBUGS("Voice") << "entering state " << state2string(inState) << LL_ENDL;
01171         
01172         mState = inState;
01173 }
01174 
01175 void LLVoiceClient::stateMachine()
01176 {
01177         if(gDisconnected)
01178         {
01179                 // The viewer has been disconnected from the sim.  Disable voice.
01180                 setVoiceEnabled(false);
01181         }
01182         
01183         if(!mVoiceEnabled)
01184         {
01185                 if(getState() != stateDisabled)
01186                 {
01187                         // User turned off voice support.  Send the cleanup messages, close the socket, and reset.
01188                         if(!mConnected)
01189                         {
01190                                 // if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill.
01191                                 LL_INFOS("Voice") << "Disabling voice before connection to daemon, terminating." << LL_ENDL;
01192                                 killGateway();
01193                         }
01194                         
01195                         sessionTerminateSendMessage();
01196                         logout();
01197                         connectorShutdown();
01198                         closeSocket();
01199                         removeAllParticipants();
01200 
01201                         setState(stateDisabled);
01202                 }
01203         }
01204         
01205         // Check for parcel boundary crossing
01206         {
01207                 LLViewerRegion *region = gAgent.getRegion();
01208                 LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel();
01209                 
01210                 if(region && parcel)
01211                 {
01212                         S32 parcelLocalID = parcel->getLocalID();
01213                         std::string regionName = region->getName();
01214                         std::string capURI = region->getCapability("ParcelVoiceInfoRequest");
01215                 
01216                         LL_DEBUGS("Voice") << "Region name = \"" << regionName <<"\", " << "parcel local ID = " << parcelLocalID << LL_ENDL;
01217 
01218                         // The region name starts out empty and gets filled in later.  
01219                         // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes.
01220                         // If either is empty, wait for the next time around.
01221                         if(!regionName.empty() && !capURI.empty())
01222                         {
01223                                 if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName))
01224                                 {
01225                                         // We have changed parcels.  Initiate a parcel channel lookup.
01226                                         mCurrentParcelLocalID = parcelLocalID;
01227                                         mCurrentRegionName = regionName;
01228                                         
01229                                         parcelChanged();
01230                                 }
01231                         }
01232                 }
01233         }
01234 
01235         switch(getState())
01236         {
01237                 case stateDisabled:
01238                         if(mVoiceEnabled && (!mAccountName.empty() || mTuningMode))
01239                         {
01240                                 setState(stateStart);
01241                         }
01242                 break;
01243                 
01244                 case stateStart:
01245                         if(gSavedSettings.getBOOL("CmdLineDisableVoice"))
01246                         {
01247                                 // Voice is locked out, we must not launch the vivox daemon.
01248                                 setState(stateJail);
01249                         }
01250                         else if(!isGatewayRunning())
01251                         {
01252                                 if(true)
01253                                 {
01254                                         // Launch the voice daemon
01255                                         std::string exe_path = gDirUtilp->getAppRODataDir();
01256                                         exe_path += gDirUtilp->getDirDelimiter();
01257 #if LL_WINDOWS
01258                                         exe_path += "SLVoice.exe";
01259 #else
01260                                         // This will be the same for mac and linux
01261                                         exe_path += "SLVoice";
01262 #endif
01263                                         // See if the vivox executable exists
01264                                         llstat s;
01265                                         if(!LLFile::stat(exe_path.c_str(), &s))
01266                                         {
01267                                                 // vivox executable exists.  Build the command line and launch the daemon.
01268                                                 std::string args = " -p tcp -h -c";
01269                                                 std::string cmd;
01270                                                 std::string loglevel = gSavedSettings.getString("VivoxDebugLevel");
01271                                                 
01272                                                 if(loglevel.empty())
01273                                                 {
01274                                                         loglevel = "-1";        // turn logging off completely
01275                                                 }
01276                                                 
01277                                                 args += " -ll ";
01278                                                 args += loglevel;
01279                                                 
01280                                                 LL_DEBUGS("Voice") << "Args for SLVoice: " << args << LL_ENDL;
01281 
01282 #if LL_WINDOWS
01283                                                 PROCESS_INFORMATION pinfo;
01284                                                 STARTUPINFOA sinfo;
01285                                                 memset(&sinfo, 0, sizeof(sinfo));
01286                                                 std::string exe_dir = gDirUtilp->getAppRODataDir();
01287                                                 cmd = "SLVoice.exe";
01288                                                 cmd += args;
01289                                                 
01290                                                 // So retarded.  Windows requires that the second parameter to CreateProcessA be a writable (non-const) string...
01291                                                 char *args2 = new char[args.size() + 1];
01292                                                 strcpy(args2, args.c_str());
01293 
01294                                                 if(!CreateProcessA(exe_path.c_str(), args2, NULL, NULL, FALSE, 0, NULL, exe_dir.c_str(), &sinfo, &pinfo))
01295                                                 {
01296 //                                                      DWORD dwErr = GetLastError();
01297                                                 }
01298                                                 else
01299                                                 {
01300                                                         // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
01301                                                         // CloseHandle(pinfo.hProcess); // stops leaks - nothing else
01302                                                         sGatewayHandle = pinfo.hProcess;
01303                                                         CloseHandle(pinfo.hThread); // stops leaks - nothing else
01304                                                 }               
01305                                                 
01306                                                 delete[] args2;
01307 #else   // LL_WINDOWS
01308                                                 // This should be the same for mac and linux
01309                                                 {
01310                                                         std::vector<std::string> arglist;
01311                                                         arglist.push_back(exe_path.c_str());
01312                                                         
01313                                                         // Split the argument string into separate strings for each argument
01314                                                         typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
01315                                                         boost::char_separator<char> sep(" ");
01316                                                         tokenizer tokens(args, sep);
01317                                                         tokenizer::iterator token_iter;
01318 
01319                                                         for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
01320                                                         {
01321                                                                 arglist.push_back(*token_iter);
01322                                                         }
01323                                                         
01324                                                         // create an argv vector for the child process
01325                                                         char **fakeargv = new char*[arglist.size() + 1];
01326                                                         int i;
01327                                                         for(i=0; i < arglist.size(); i++)
01328                                                                 fakeargv[i] = const_cast<char*>(arglist[i].c_str());
01329 
01330                                                         fakeargv[i] = NULL;
01331                                                         
01332                                                         pid_t id = vfork();
01333                                                         if(id == 0)
01334                                                         {
01335                                                                 // child
01336                                                                 execv(exe_path.c_str(), fakeargv);
01337                                                                 
01338                                                                 // If we reach this point, the exec failed.
01339                                                                 // Use _exit() instead of exit() per the vfork man page.
01340                                                                 _exit(0);
01341                                                         }
01342 
01343                                                         // parent
01344                                                         delete[] fakeargv;
01345                                                         sGatewayPID = id;
01346                                                 }
01347 #endif  // LL_WINDOWS
01348                                                 mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost").c_str(), gSavedSettings.getU32("VoicePort"));
01349                                         }       
01350                                         else
01351                                         {
01352                                                 LL_INFOS("Voice") << exe_path << "not found." << LL_ENDL
01353                                         }       
01354                                 }
01355                                 else
01356                                 {               
01357                                         // We can connect to a client gateway running on another host.  This is useful for testing.
01358                                         // To do this, launch the gateway on a nearby host like this:
01359                                         //  vivox-gw.exe -p tcp -i 0.0.0.0:44124
01360                                         // and put that host's IP address here.
01361                                         mDaemonHost = LLHost(gSavedSettings.getString("VoiceHost").c_str(), gSavedSettings.getU32("VoicePort"));
01362                                 }
01363 
01364                                 mUpdateTimer.start();
01365                                 mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
01366 
01367                                 setState(stateDaemonLaunched);
01368                                 
01369                                 // Dirty the states we'll need to sync with the daemon when it comes up.
01370                                 mPTTDirty = true;
01371                                 mSpeakerVolumeDirty = true;
01372                                 // These only need to be set if they're not default (i.e. empty string).
01373                                 mCaptureDeviceDirty = !mCaptureDevice.empty();
01374                                 mRenderDeviceDirty = !mRenderDevice.empty();
01375                         }
01376                 break;
01377                 
01378                 case stateDaemonLaunched:
01379                         LL_DEBUGS("Voice") << "Connecting to vivox daemon" << LL_ENDL;
01380                         if(mUpdateTimer.hasExpired())
01381                         {
01382                                 mUpdateTimer.setTimerExpirySec(CONNECT_THROTTLE_SECONDS);
01383 
01384                                 if(!mSocket)
01385                                 {
01386                                         mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP);    
01387                                 }
01388                                 
01389                                 mConnected = mSocket->blockingConnect(mDaemonHost);
01390                                 if(mConnected)
01391                                 {
01392                                         setState(stateConnecting);
01393                                 }
01394                                 else
01395                                 {
01396                                         // If the connect failed, the socket may have been put into a bad state.  Delete it.
01397                                         closeSocket();
01398                                 }
01399                         }
01400                 break;
01401 
01402                 case stateConnecting:
01403                 // Can't do this until we have the pump available.
01404                 if(mPump)
01405                 {
01406                         // MBW -- Note to self: pumps and pipes examples in
01407                         //  indra/test/io.cpp
01408                         //  indra/test/llpipeutil.{cpp|h}
01409 
01410                         // Attach the pumps and pipes
01411                                 
01412                         LLPumpIO::chain_t readChain;
01413 
01414                         readChain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(mSocket)));
01415                         readChain.push_back(LLIOPipe::ptr_t(new LLVivoxProtocolParser()));
01416 
01417                         mPump->addChain(readChain, NEVER_CHAIN_EXPIRY_SECS);
01418 
01419                         setState(stateIdle);
01420                 }
01421 
01422                 break;
01423                 
01424                 case stateIdle:
01425                         // Initial devices query
01426                         getCaptureDevicesSendMessage();
01427                         getRenderDevicesSendMessage();
01428 
01429                         mLoginRetryCount = 0;
01430                         
01431                         setState(stateConnectorStart);
01432                                 
01433                 break;
01434                 
01435                 case stateConnectorStart:
01436                         if(!mVoiceEnabled)
01437                         {
01438                                 // We were never logged in.  This will shut down the connector.
01439                                 setState(stateLoggedOut);
01440                         }
01441                         else if(!mAccountServerURI.empty())
01442                         {
01443                                 connectorCreate();
01444                         }
01445                         else if(mTuningMode)
01446                         {
01447                                 mTuningExitState = stateConnectorStart;
01448                                 setState(stateMicTuningStart);
01449                         }
01450                 break;
01451                 
01452                 case stateConnectorStarting:    // waiting for connector handle
01453                         // connectorCreateResponse() will transition from here to stateConnectorStarted.
01454                 break;
01455                 
01456                 case stateConnectorStarted:             // connector handle received
01457                         if(!mVoiceEnabled)
01458                         {
01459                                 // We were never logged in.  This will shut down the connector.
01460                                 setState(stateLoggedOut);
01461                         }
01462                         else if(!mAccountName.empty())
01463                         {
01464                                 LLViewerRegion *region = gAgent.getRegion();
01465                                 
01466                                 if(region)
01467                                 {
01468                                         if ( region->getCapability("ProvisionVoiceAccountRequest") != "" )
01469                                         {
01470                                                 if ( mAccountPassword.empty() )
01471                                                 {
01472                                                         requestVoiceAccountProvision();
01473                                                 }
01474                                                 setState(stateNeedsLogin);
01475                                         }
01476                                 }
01477                         }
01478                 break;
01479                                 
01480                 case stateMicTuningStart:
01481                         if(mUpdateTimer.hasExpired())
01482                         {
01483                                 if(mCaptureDeviceDirty || mRenderDeviceDirty)
01484                                 {
01485                                         // These can't be changed while in tuning mode.  Set them before starting.
01486                                         std::ostringstream stream;
01487 
01488                                         if(mCaptureDeviceDirty)
01489                                         {
01490                                                 buildSetCaptureDevice(stream);
01491                                         }
01492 
01493                                         if(mRenderDeviceDirty)
01494                                         {
01495                                                 buildSetRenderDevice(stream);
01496                                         }
01497 
01498                                         mCaptureDeviceDirty = false;
01499                                         mRenderDeviceDirty = false;
01500 
01501                                         if(!stream.str().empty())
01502                                         {
01503                                                 writeString(stream.str());
01504                                         }                               
01505 
01506                                         // This will come around again in the same state and start the capture, after the timer expires.
01507                                         mUpdateTimer.start();
01508                                         mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
01509                                 }
01510                                 else
01511                                 {
01512                                         // duration parameter is currently unused, per Mike S.
01513                                         tuningCaptureStartSendMessage(10000);
01514 
01515                                         setState(stateMicTuningRunning);
01516                                 }
01517                         }
01518                         
01519                 break;
01520                 
01521                 case stateMicTuningRunning:
01522                         if(!mTuningMode || !mVoiceEnabled || mSessionTerminateRequested || mCaptureDeviceDirty || mRenderDeviceDirty)
01523                         {
01524                                 // All of these conditions make us leave tuning mode.
01525                                 setState(stateMicTuningStop);
01526                         }
01527                         else
01528                         {
01529                                 // process mic/speaker volume changes
01530                                 if(mTuningMicVolumeDirty || mTuningSpeakerVolumeDirty)
01531                                 {
01532                                         std::ostringstream stream;
01533                                         
01534                                         if(mTuningMicVolumeDirty)
01535                                         {
01536                                                 LL_INFOS("Voice") << "setting tuning mic level to " << mTuningMicVolume << LL_ENDL;
01537                                                 stream
01538                                                 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetMicLevel.1\">"
01539                                                 << "<Level>" << mTuningMicVolume << "</Level>"
01540                                                 << "</Request>\n\n\n";
01541                                         }
01542                                         
01543                                         if(mTuningSpeakerVolumeDirty)
01544                                         {
01545                                                 stream
01546                                                 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetSpeakerLevel.1\">"
01547                                                 << "<Level>" << mTuningSpeakerVolume << "</Level>"
01548                                                 << "</Request>\n\n\n";
01549                                         }
01550                                         
01551                                         mTuningMicVolumeDirty = false;
01552                                         mTuningSpeakerVolumeDirty = false;
01553 
01554                                         if(!stream.str().empty())
01555                                         {
01556                                                 writeString(stream.str());
01557                                         }
01558                                 }
01559                         }
01560                 break;
01561                 
01562                 case stateMicTuningStop:
01563                 {
01564                         // transition out of mic tuning
01565                         tuningCaptureStopSendMessage();
01566                         
01567                         setState(mTuningExitState);
01568                         
01569                         // if we exited just to change devices, this will keep us from re-entering too fast.
01570                         mUpdateTimer.start();
01571                         mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
01572                         
01573                 }
01574                 break;
01575                                                                 
01576                 case stateLoginRetry:
01577                         if(mLoginRetryCount == 0)
01578                         {
01579                                 // First retry -- display a message to the user
01580                                 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGIN_RETRY);
01581                         }
01582                         
01583                         mLoginRetryCount++;
01584                         
01585                         if(mLoginRetryCount > MAX_LOGIN_RETRIES)
01586                         {
01587                                 LL_WARNS("Voice") << "too many login retries, giving up." << LL_ENDL;
01588                                 setState(stateLoginFailed);
01589                         }
01590                         else
01591                         {
01592                                 LL_INFOS("Voice") << "will retry login in " << LOGIN_RETRY_SECONDS << " seconds." << LL_ENDL;
01593                                 mUpdateTimer.start();
01594                                 mUpdateTimer.setTimerExpirySec(LOGIN_RETRY_SECONDS);
01595                                 setState(stateLoginRetryWait);
01596                         }
01597                 break;
01598                 
01599                 case stateLoginRetryWait:
01600                         if(mUpdateTimer.hasExpired())
01601                         {
01602                                 setState(stateNeedsLogin);
01603                         }
01604                 break;
01605                 
01606                 case stateNeedsLogin:
01607                         if(!mAccountPassword.empty())
01608                         {
01609                                 setState(stateLoggingIn);
01610                                 loginSendMessage();
01611                         }               
01612                 break;
01613                 
01614                 case stateLoggingIn:                    // waiting for account handle
01615                         // loginResponse() will transition from here to stateLoggedIn.
01616                 break;
01617                 
01618                 case stateLoggedIn:                             // account handle received
01619                         // Initial kick-off of channel lookup logic
01620                         parcelChanged();
01621 
01622                         notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
01623 
01624                         // Set up the mute list observer if it hasn't been set up already.
01625                         if((!sMuteListListener_listening))
01626                         {
01627                                 LLMuteList::getInstance()->addObserver(&mutelist_listener);
01628                                 sMuteListListener_listening = true;
01629                         }
01630 
01631                         setState(stateNoChannel);
01632                 break;
01633                                         
01634                 case stateNoChannel:
01635                         if(mSessionTerminateRequested || !mVoiceEnabled)
01636                         {
01637                                 // MBW -- XXX -- Is this the right way out of this state?
01638                                 setState(stateSessionTerminated);
01639                         }
01640                         else if(mTuningMode)
01641                         {
01642                                 mTuningExitState = stateNoChannel;
01643                                 setState(stateMicTuningStart);
01644                         }
01645                         else if(!mNextSessionHandle.empty())
01646                         {
01647                                 setState(stateSessionConnect);
01648                         }
01649                         else if(!mNextSessionURI.empty())
01650                         {
01651                                 setState(stateSessionCreate);
01652                         }
01653                 break;
01654 
01655                 case stateSessionCreate:
01656                         sessionCreateSendMessage();
01657                         notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
01658                         setState(stateJoiningSession);
01659                 break;
01660                 
01661                 case stateSessionConnect:
01662                         sessionConnectSendMessage();
01663                         notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINING);
01664                         setState(stateJoiningSession);
01665                 break;
01666                 
01667                 case stateJoiningSession:               // waiting for session handle
01668                         // sessionCreateResponse() will transition from here to stateSessionJoined.
01669                         if(!mVoiceEnabled)
01670                         {
01671                                 // User bailed out during connect -- jump straight to teardown.
01672                                 setState(stateSessionTerminated);
01673                         }
01674                         else if(mSessionTerminateRequested)
01675                         {
01676                                 if(!mSessionHandle.empty())
01677                                 {
01678                                         // Only allow direct exits from this state in p2p calls (for cancelling an invite).
01679                                         // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
01680                                         if(mSessionP2P)
01681                                         {
01682                                                 sessionTerminateSendMessage();
01683                                                 setState(stateSessionTerminated);
01684                                         }
01685                                 }
01686                         }
01687                 break;
01688                 
01689                 case stateSessionJoined:                // session handle received
01690                         // MBW -- XXX -- It appears that I need to wait for BOTH the Session.Create response and the SessionStateChangeEvent with state 4
01691                         //  before continuing from this state.  They can happen in either order, and if I don't wait for both, things can get stuck.
01692                         // For now, the Session.Create response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
01693                         // This is a cheap way to make sure both have happened before proceeding.
01694                         if(!mSessionHandle.empty())
01695                         {
01696                                 // Events that need to happen when a session is joined could go here.
01697                                 // Maybe send initial spatial data?
01698                                 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
01699 
01700                                 // Dirty state that may need to be sync'ed with the daemon.
01701                                 mPTTDirty = true;
01702                                 mSpeakerVolumeDirty = true;
01703                                 mSpatialCoordsDirty = true;
01704                                 
01705                                 setState(stateRunning);
01706                                 
01707                                 // Start the throttle timer
01708                                 mUpdateTimer.start();
01709                                 mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
01710                         }
01711                         else if(!mVoiceEnabled)
01712                         {
01713                                 // User bailed out during connect -- jump straight to teardown.
01714                                 setState(stateSessionTerminated);
01715                         }
01716                         else if(mSessionTerminateRequested)
01717                         {
01718                                 // Only allow direct exits from this state in p2p calls (for cancelling an invite).
01719                                 // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
01720                                 if(mSessionP2P)
01721                                 {
01722                                         sessionTerminateSendMessage();
01723                                         setState(stateSessionTerminated);
01724                                 }
01725                         }
01726                 break;
01727                 
01728                 case stateRunning:                              // steady state
01729                         // sessionTerminateSendMessage() will transition from here to stateLeavingSession
01730                         
01731                         // Disabling voice or disconnect requested.
01732                         if(!mVoiceEnabled || mSessionTerminateRequested)
01733                         {
01734                                 sessionTerminateSendMessage();
01735                         }
01736                         else
01737                         {
01738                                 
01739                                 // Figure out whether the PTT state needs to change
01740                                 {
01741                                         bool newPTT;
01742                                         if(mUsePTT)
01743                                         {
01744                                                 // If configured to use PTT, track the user state.
01745                                                 newPTT = mUserPTTState;
01746                                         }
01747                                         else
01748                                         {
01749                                                 // If not configured to use PTT, it should always be true (otherwise the user will be unable to speak).
01750                                                 newPTT = true;
01751                                         }
01752                                         
01753                                         if(mMuteMic)
01754                                         {
01755                                                 // This always overrides any other PTT setting.
01756                                                 newPTT = false;
01757                                         }
01758                                         
01759                                         // Dirty if state changed.
01760                                         if(newPTT != mPTT)
01761                                         {
01762                                                 mPTT = newPTT;
01763                                                 mPTTDirty = true;
01764                                         }
01765                                 }
01766                                 
01767                                 if(mNonSpatialChannel)
01768                                 {
01769                                         // When in a non-spatial channel, never send positional updates.
01770                                         mSpatialCoordsDirty = false;
01771                                 }
01772                                 else
01773                                 {
01774                                         // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position)
01775                                         enforceTether();
01776                                 }
01777                                 
01778                                 // Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast)
01779                                 // or every 10hz, whichever is sooner.
01780                                 if(mVolumeDirty || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired())
01781                                 {
01782                                         mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
01783                                         sendPositionalUpdate();
01784                                 }
01785                         }
01786                 break;
01787                 
01788                 case stateLeavingSession:               // waiting for terminate session response
01789                         // The handler for the Session.Terminate response will transition from here to stateSessionTerminated.
01790                 break;
01791 
01792                 case stateSessionTerminated:
01793                         // Always reset the terminate request flag when we get here.
01794                         mSessionTerminateRequested = false;
01795                         
01796                         notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
01797 
01798                         if(mVoiceEnabled)
01799                         {
01800                                 // SPECIAL CASE: if going back to spatial but in a parcel with an empty URI, transfer the non-spatial flag now.
01801                                 // This fixes the case where you come out of a group chat in a parcel with voice disabled, and get stuck unable to rejoin spatial chat thereafter.
01802                                 if(mNextSessionSpatial && mNextSessionURI.empty())
01803                                 {
01804                                         mNonSpatialChannel = !mNextSessionSpatial;
01805                                 }
01806                                 
01807                                 // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
01808                                 setState(stateNoChannel);
01809                         }
01810                         else
01811                         {
01812                                 // Shutting down voice, continue with disconnecting.
01813                                 logout();
01814                         }
01815                         
01816                 break;
01817                 
01818                 case stateLoggingOut:                   // waiting for logout response
01819                         // The handler for the Account.Logout response will transition from here to stateLoggedOut.
01820                 break;
01821                 case stateLoggedOut:                    // logout response received
01822                         // shut down the connector
01823                         connectorShutdown();
01824                 break;
01825                 
01826                 case stateConnectorStopping:    // waiting for connector stop
01827                         // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
01828                 break;
01829 
01830                 case stateConnectorStopped:             // connector stop received
01831                         // Clean up and reset everything. 
01832                         closeSocket();
01833                         removeAllParticipants();
01834                         setState(stateDisabled);
01835                 break;
01836 
01837                 case stateConnectorFailed:
01838                         setState(stateConnectorFailedWaiting);
01839                 break;
01840                 case stateConnectorFailedWaiting:
01841                 break;
01842 
01843                 case stateLoginFailed:
01844                         setState(stateLoginFailedWaiting);
01845                 break;
01846                 case stateLoginFailedWaiting:
01847                         // No way to recover from these.  Yet.
01848                 break;
01849 
01850                 case stateJoinSessionFailed:
01851                         // Transition to error state.  Send out any notifications here.
01852                         LL_WARNS("Voice") << "stateJoinSessionFailed: (" << mVivoxErrorStatusCode << "): " << mVivoxErrorStatusString << LL_ENDL;
01853                         notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
01854                         setState(stateJoinSessionFailedWaiting);
01855                 break;
01856                 
01857                 case stateJoinSessionFailedWaiting:
01858                         // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message.
01859                         // Region crossings may leave this state and try the join again.
01860                         if(mSessionTerminateRequested)
01861                         {
01862                                 setState(stateSessionTerminated);
01863                         }
01864                 break;
01865                 
01866                 case stateJail:
01867                         // We have given up.  Do nothing.
01868                 break;
01869 
01870                 case stateMicTuningNoLogin:
01871                         // *TODO: Implement me.
01872                         LL_WARNS("Voice") << "stateMicTuningNoLogin not handled" << LL_ENDL;
01873                 break;
01874         }
01875 
01876         if(mParticipantMapChanged)
01877         {
01878                 mParticipantMapChanged = false;
01879                 notifyObservers();
01880         }
01881 
01882 }
01883 
01884 void LLVoiceClient::closeSocket(void)
01885 {
01886         mSocket.reset();
01887         mConnected = false;     
01888 }
01889 
01890 void LLVoiceClient::loginSendMessage()
01891 {
01892         std::ostringstream stream;
01893         stream
01894         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Login.1\">"
01895                 << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
01896                 << "<AccountName>" << mAccountName << "</AccountName>"
01897                 << "<AccountPassword>" << mAccountPassword << "</AccountPassword>"
01898                 << "<AudioSessionAnswerMode>VerifyAnswer</AudioSessionAnswerMode>"
01899         << "</Request>\n\n\n";
01900         
01901         writeString(stream.str());
01902 }
01903 
01904 void LLVoiceClient::logout()
01905 {
01906         mAccountPassword = "";
01907         setState(stateLoggingOut);
01908         logoutSendMessage();
01909 }
01910 
01911 void LLVoiceClient::logoutSendMessage()
01912 {
01913         if(!mAccountHandle.empty())
01914         {
01915                 std::ostringstream stream;
01916                 stream
01917                 << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.Logout.1\">"
01918                         << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
01919                 << "</Request>"
01920                 << "\n\n\n";
01921 
01922                 mAccountHandle.clear();
01923 
01924                 writeString(stream.str());
01925         }
01926 }
01927 
01928 void LLVoiceClient::channelGetListSendMessage()
01929 {
01930         std::ostringstream stream;
01931         stream
01932         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Account.ChannelGetList.1\">"
01933                 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
01934         << "</Request>\n\n\n";
01935 
01936         writeString(stream.str());
01937 }
01938 
01939 void LLVoiceClient::sessionCreateSendMessage()
01940 {
01941         LL_DEBUGS("Voice") << "requesting join: " << mNextSessionURI << LL_ENDL;
01942 
01943         mSessionURI = mNextSessionURI;
01944         mNonSpatialChannel = !mNextSessionSpatial;
01945         mSessionResetOnClose = mNextSessionResetOnClose;
01946         mNextSessionResetOnClose = false;
01947         if(mNextSessionNoReconnect)
01948         {
01949                 // Clear the stashed URI so it can't reconnect
01950                 mNextSessionURI.clear();
01951         }
01952         // Only p2p sessions are created with "no reconnect".
01953         mSessionP2P = mNextSessionNoReconnect;
01954 
01955         std::ostringstream stream;
01956         stream
01957         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Create.1\">"
01958                 << "<AccountHandle>" << mAccountHandle << "</AccountHandle>"
01959                 << "<URI>" << mSessionURI << "</URI>";
01960 
01961         static const std::string allowed_chars =
01962                                 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
01963                                 "0123456789"
01964                                 "-._~";
01965 
01966         if(!mNextSessionHash.empty())
01967         {
01968                 stream
01969                         << "<Password>" << LLURI::escape(mNextSessionHash, allowed_chars) << "</Password>"
01970                         << "<PasswordHashAlgorithm>SHA1UserName</PasswordHashAlgorithm>";
01971         }
01972         
01973         stream
01974                 << "<Name>" << mChannelName << "</Name>"
01975         << "</Request>\n\n\n";
01976         writeString(stream.str());
01977 }
01978 
01979 void LLVoiceClient::sessionConnectSendMessage()
01980 {
01981         LL_DEBUGS("Voice") << "connecting to session handle: " << mNextSessionHandle << LL_ENDL;
01982         
01983         mSessionHandle = mNextSessionHandle;
01984         mSessionURI = mNextP2PSessionURI;
01985         mNextSessionHandle.clear();             // never want to re-use these.
01986         mNextP2PSessionURI.clear();
01987         mNonSpatialChannel = !mNextSessionSpatial;
01988         mSessionResetOnClose = mNextSessionResetOnClose;
01989         mNextSessionResetOnClose = false;
01990         // Joining by session ID is only used to answer p2p invitations, so we know this is a p2p session.
01991         mSessionP2P = true;
01992         
01993         std::ostringstream stream;
01994         
01995         stream
01996         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Connect.1\">"
01997                 << "<SessionHandle>" << mSessionHandle << "</SessionHandle>"
01998                 << "<AudioMedia>default</AudioMedia>"
01999         << "</Request>\n\n\n";
02000         writeString(stream.str());
02001 }
02002 
02003 void LLVoiceClient::sessionTerminate()
02004 {
02005         mSessionTerminateRequested = true;
02006 }
02007 
02008 void LLVoiceClient::sessionTerminateSendMessage()
02009 {
02010         LL_DEBUGS("Voice") << "leaving session: " << mSessionURI << LL_ENDL;
02011 
02012         switch(getState())
02013         {
02014                 case stateNoChannel:
02015                         // In this case, we want to pretend the join failed so our state machine doesn't get stuck.
02016                         // Skip the join failed transition state so we don't send out error notifications.
02017                         setState(stateJoinSessionFailedWaiting);
02018                 break;
02019                 case stateJoiningSession:
02020                 case stateSessionJoined:
02021                 case stateRunning:
02022                         if(!mSessionHandle.empty())
02023                         {
02024                                 sessionTerminateByHandle(mSessionHandle);
02025                                 setState(stateLeavingSession);
02026                         }
02027                         else
02028                         {
02029                                 LL_WARNS("Voice") << "called with no session handle" << LL_ENDL;        
02030                                 setState(stateSessionTerminated);
02031                         }
02032                 break;
02033                 case stateJoinSessionFailed:
02034                 case stateJoinSessionFailedWaiting:
02035                         setState(stateSessionTerminated);
02036                 break;
02037                 
02038                 default:
02039                         LL_WARNS("Voice") << "called from unknown state" << LL_ENDL;
02040                 break;
02041         }
02042 }
02043 
02044 void LLVoiceClient::sessionTerminateByHandle(std::string &sessionHandle)
02045 {
02046         LL_DEBUGS("Voice") << "Sending Session.Terminate with handle " << sessionHandle << LL_ENDL;     
02047 
02048         std::ostringstream stream;
02049         stream
02050         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Terminate.1\">"
02051                 << "<SessionHandle>" << sessionHandle << "</SessionHandle>"
02052         << "</Request>"
02053         << "\n\n\n";
02054         
02055         writeString(stream.str());
02056 }
02057 
02058 void LLVoiceClient::getCaptureDevicesSendMessage()
02059 {
02060         std::ostringstream stream;
02061         stream
02062         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetCaptureDevices.1\">"
02063         << "</Request>\n\n\n";
02064         
02065         writeString(stream.str());
02066 }
02067 
02068 void LLVoiceClient::getRenderDevicesSendMessage()
02069 {
02070         std::ostringstream stream;
02071         stream
02072         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.GetRenderDevices.1\">"
02073         << "</Request>\n\n\n";
02074         
02075         writeString(stream.str());
02076 }
02077 
02078 void LLVoiceClient::clearCaptureDevices()
02079 {
02080         // MBW -- XXX -- do something here
02081         LL_DEBUGS("Voice") << "called" << LL_ENDL;
02082         mCaptureDevices.clear();
02083 }
02084 
02085 void LLVoiceClient::addCaptureDevice(const std::string& name)
02086 {
02087         // MBW -- XXX -- do something here
02088         LL_DEBUGS("Voice") << name << LL_ENDL;
02089 
02090         mCaptureDevices.push_back(name);
02091 }
02092 
02093 LLVoiceClient::deviceList *LLVoiceClient::getCaptureDevices()
02094 {
02095         return &mCaptureDevices;
02096 }
02097 
02098 void LLVoiceClient::setCaptureDevice(const std::string& name)
02099 {
02100         if(name == "Default")
02101         {
02102                 if(!mCaptureDevice.empty())
02103                 {
02104                         mCaptureDevice.clear();
02105                         mCaptureDeviceDirty = true;     
02106                 }
02107         }
02108         else
02109         {
02110                 if(mCaptureDevice != name)
02111                 {
02112                         mCaptureDevice = name;
02113                         mCaptureDeviceDirty = true;     
02114                 }
02115         }
02116 }
02117 
02118 void LLVoiceClient::clearRenderDevices()
02119 {
02120         // MBW -- XXX -- do something here
02121         LL_DEBUGS("Voice") << "called" << LL_ENDL;
02122         mRenderDevices.clear();
02123 }
02124 
02125 void LLVoiceClient::addRenderDevice(const std::string& name)
02126 {
02127         // MBW -- XXX -- do something here
02128         LL_DEBUGS("Voice") << name << LL_ENDL;
02129         mRenderDevices.push_back(name);
02130 }
02131 
02132 LLVoiceClient::deviceList *LLVoiceClient::getRenderDevices()
02133 {
02134         return &mRenderDevices;
02135 }
02136 
02137 void LLVoiceClient::setRenderDevice(const std::string& name)
02138 {
02139         if(name == "Default")
02140         {
02141                 if(!mRenderDevice.empty())
02142                 {
02143                         mRenderDevice.clear();
02144                         mRenderDeviceDirty = true;      
02145                 }
02146         }
02147         else
02148         {
02149                 if(mRenderDevice != name)
02150                 {
02151                         mRenderDevice = name;
02152                         mRenderDeviceDirty = true;      
02153                 }
02154         }
02155         
02156 }
02157 
02158 void LLVoiceClient::tuningStart()
02159 {
02160         mTuningMode = true;
02161         if(getState() >= stateNoChannel)
02162         {
02163                 sessionTerminate();
02164         }
02165 }
02166 
02167 void LLVoiceClient::tuningStop()
02168 {
02169         mTuningMode = false;
02170 }
02171 
02172 bool LLVoiceClient::inTuningMode()
02173 {
02174         bool result = false;
02175         switch(getState())
02176         {
02177         case stateMicTuningRunning:
02178                 result = true;
02179                 break;
02180         default:
02181                 break;
02182         }
02183         return result;
02184 }
02185 
02186 void LLVoiceClient::tuningRenderStartSendMessage(const std::string& name, bool loop)
02187 {               
02188         mTuningAudioFile = name;
02189         std::ostringstream stream;
02190         stream
02191         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStart.1\">"
02192     << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
02193     << "<Loop>" << (loop?"1":"0") << "</Loop>"
02194         << "</Request>\n\n\n";
02195         
02196         writeString(stream.str());
02197 }
02198 
02199 void LLVoiceClient::tuningRenderStopSendMessage()
02200 {
02201         std::ostringstream stream;
02202         stream
02203         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.RenderAudioStop.1\">"
02204     << "<SoundFilePath>" << mTuningAudioFile << "</SoundFilePath>"
02205         << "</Request>\n\n\n";
02206         
02207         writeString(stream.str());
02208 }
02209 
02210 void LLVoiceClient::tuningCaptureStartSendMessage(int duration)
02211 {
02212         LL_DEBUGS("Voice") << "sending CaptureAudioStart" << LL_ENDL;
02213         
02214         std::ostringstream stream;
02215         stream
02216         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStart.1\">"
02217     << "<Duration>" << duration << "</Duration>"
02218         << "</Request>\n\n\n";
02219         
02220         writeString(stream.str());
02221 }
02222 
02223 void LLVoiceClient::tuningCaptureStopSendMessage()
02224 {
02225         LL_DEBUGS("Voice") << "sending CaptureAudioStop" << LL_ENDL;
02226         
02227         std::ostringstream stream;
02228         stream
02229         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.CaptureAudioStop.1\">"
02230         << "</Request>\n\n\n";
02231         
02232         writeString(stream.str());
02233 
02234         mTuningEnergy = 0.0f;
02235 }
02236 
02237 void LLVoiceClient::tuningSetMicVolume(float volume)
02238 {
02239         int scaledVolume = ((int)(volume * 100.0f)) - 100;
02240         if(scaledVolume != mTuningMicVolume)
02241         {
02242                 mTuningMicVolume = scaledVolume;
02243                 mTuningMicVolumeDirty = true;
02244         }
02245 }
02246 
02247 void LLVoiceClient::tuningSetSpeakerVolume(float volume)
02248 {
02249         // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
02250         // Map it as follows: 0.0 -> -100, 0.5 -> 24, 1.0 -> 50
02251         
02252         volume -= 0.5f;         // offset volume to the range [-0.5 ... 0.5], with 0 at the default.
02253         int scaledVolume = 24;  // offset scaledVolume by its default level
02254         if(volume < 0.0f)
02255                 scaledVolume += ((int)(volume * 248.0f));       // (24 - (-100)) * 2
02256         else
02257                 scaledVolume += ((int)(volume * 52.0f));        // (50 - 24) * 2
02258 
02259         if(scaledVolume != mTuningSpeakerVolume)
02260         {
02261                 mTuningSpeakerVolume = scaledVolume;
02262                 mTuningSpeakerVolumeDirty = true;
02263         }
02264 }
02265                                 
02266 float LLVoiceClient::tuningGetEnergy(void)
02267 {
02268         return mTuningEnergy;
02269 }
02270 
02271 bool LLVoiceClient::deviceSettingsAvailable()
02272 {
02273         bool result = true;
02274         
02275         if(!mConnected)
02276                 result = false;
02277         
02278         if(mRenderDevices.empty())
02279                 result = false;
02280         
02281         return result;
02282 }
02283 
02284 void LLVoiceClient::refreshDeviceLists(bool clearCurrentList)
02285 {
02286         if(clearCurrentList)
02287         {
02288                 clearCaptureDevices();
02289                 clearRenderDevices();
02290         }
02291         getCaptureDevicesSendMessage();
02292         getRenderDevicesSendMessage();
02293 }
02294 
02295 void LLVoiceClient::daemonDied()
02296 {
02297         // The daemon died, so the connection is gone.  Reset everything and start over.
02298         LL_WARNS("Voice") << "Connection to vivox daemon lost.  Resetting state."<< LL_ENDL;
02299 
02300         closeSocket();
02301         removeAllParticipants();
02302         
02303         // Try to relaunch the daemon
02304         setState(stateDisabled);
02305 }
02306 
02307 void LLVoiceClient::giveUp()
02308 {
02309         // All has failed.  Clean up and stop trying.
02310         closeSocket();
02311         removeAllParticipants();
02312         
02313         setState(stateJail);
02314 }
02315 
02316 void LLVoiceClient::sendPositionalUpdate(void)
02317 {       
02318         std::ostringstream stream;
02319         
02320         if(mSpatialCoordsDirty)
02321         {
02322                 LLVector3 l, u, a;
02323                 
02324                 // Always send both speaker and listener positions together.
02325                 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.Set3DPosition.1\">"               
02326                         << "<SessionHandle>" << mSessionHandle << "</SessionHandle>";
02327                 
02328                 stream << "<SpeakerPosition>";
02329 
02330                 l = mAvatarRot.getLeftRow();
02331                 u = mAvatarRot.getUpRow();
02332                 a = mAvatarRot.getFwdRow();
02333                         
02334                 LL_DEBUGS("Voice") << "Sending speaker position " << mAvatarPosition << LL_ENDL;
02335 
02336                 stream 
02337                         << "<Position>"
02338                                 << "<X>" << mAvatarPosition[VX] << "</X>"
02339                                 << "<Y>" << mAvatarPosition[VZ] << "</Y>"
02340                                 << "<Z>" << mAvatarPosition[VY] << "</Z>"
02341                         << "</Position>"
02342                         << "<Velocity>"
02343                                 << "<X>" << mAvatarVelocity[VX] << "</X>"
02344                                 << "<Y>" << mAvatarVelocity[VZ] << "</Y>"
02345                                 << "<Z>" << mAvatarVelocity[VY] << "</Z>"
02346                         << "</Velocity>"
02347                         << "<AtOrientation>"
02348                                 << "<X>" << l.mV[VX] << "</X>"
02349                                 << "<Y>" << u.mV[VX] << "</Y>"
02350                                 << "<Z>" << a.mV[VX] << "</Z>"
02351                         << "</AtOrientation>"
02352                         << "<UpOrientation>"
02353                                 << "<X>" << l.mV[VZ] << "</X>"
02354                                 << "<Y>" << u.mV[VY] << "</Y>"
02355                                 << "<Z>" << a.mV[VZ] << "</Z>"
02356                         << "</UpOrientation>"
02357                         << "<LeftOrientation>"
02358                                 << "<X>" << l.mV [VY] << "</X>"
02359                                 << "<Y>" << u.mV [VZ] << "</Y>"
02360                                 << "<Z>" << a.mV [VY] << "</Z>"
02361                         << "</LeftOrientation>";
02362 
02363                 stream << "</SpeakerPosition>";
02364 
02365                 stream << "<ListenerPosition>";
02366 
02367                 LLVector3d      earPosition;
02368                 LLVector3       earVelocity;
02369                 LLMatrix3       earRot;
02370                 
02371                 switch(mEarLocation)
02372                 {
02373                         case earLocCamera:
02374                         default:
02375                                 earPosition = mCameraPosition;
02376                                 earVelocity = mCameraVelocity;
02377                                 earRot = mCameraRot;
02378                         break;
02379                         
02380                         case earLocAvatar:
02381                                 earPosition = mAvatarPosition;
02382                                 earVelocity = mAvatarVelocity;
02383                                 earRot = mAvatarRot;
02384                         break;
02385                         
02386                         case earLocMixed:
02387                                 earPosition = mAvatarPosition;
02388                                 earVelocity = mAvatarVelocity;
02389                                 earRot = mCameraRot;
02390                         break;
02391                 }
02392 
02393                 l = earRot.getLeftRow();
02394                 u = earRot.getUpRow();
02395                 a = earRot.getFwdRow();
02396 
02397                 LL_DEBUGS("Voice") << "Sending listener position " << earPosition << LL_ENDL;
02398 
02399                 stream 
02400                         << "<Position>"
02401                                 << "<X>" << earPosition[VX] << "</X>"
02402                                 << "<Y>" << earPosition[VZ] << "</Y>"
02403                                 << "<Z>" << earPosition[VY] << "</Z>"
02404                         << "</Position>"
02405                         << "<Velocity>"
02406                                 << "<X>" << earVelocity[VX] << "</X>"
02407                                 << "<Y>" << earVelocity[VZ] << "</Y>"
02408                                 << "<Z>" << earVelocity[VY] << "</Z>"
02409                         << "</Velocity>"
02410                         << "<AtOrientation>"
02411                                 << "<X>" << l.mV[VX] << "</X>"
02412                                 << "<Y>" << u.mV[VX] << "</Y>"
02413                                 << "<Z>" << a.mV[VX] << "</Z>"
02414                         << "</AtOrientation>"
02415                         << "<UpOrientation>"
02416                                 << "<X>" << l.mV[VZ] << "</X>"
02417                                 << "<Y>" << u.mV[VY] << "</Y>"
02418                                 << "<Z>" << a.mV[VZ] << "</Z>"
02419                         << "</UpOrientation>"
02420                         << "<LeftOrientation>"
02421                                 << "<X>" << l.mV [VY] << "</X>"
02422                                 << "<Y>" << u.mV [VZ] << "</Y>"
02423                                 << "<Z>" << a.mV [VY] << "</Z>"
02424                         << "</LeftOrientation>";
02425 
02426                 stream << "</ListenerPosition>";
02427 
02428                 stream << "</Request>\n\n\n";
02429         }       
02430 
02431         if(mPTTDirty)
02432         {
02433                 // Send a local mute command.
02434                 // NOTE that the state of "PTT" is the inverse of "local mute".
02435                 //   (i.e. when PTT is true, we send a mute command with "false", and vice versa)
02436                 
02437                 LL_DEBUGS("Voice") << "Sending MuteLocalMic command with parameter " << (mPTT?"false":"true") << LL_ENDL;
02438 
02439                 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalMic.1\">"
02440                         << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
02441                         << "<Value>" << (mPTT?"false":"true") << "</Value>"
02442                         << "</Request>\n\n\n";
02443 
02444         }
02445         
02446         if(mVolumeDirty)
02447         {
02448                 participantMap::iterator iter = mParticipantMap.begin();
02449                 
02450                 for(; iter != mParticipantMap.end(); iter++)
02451                 {
02452                         participantState *p = iter->second;
02453                         
02454                         if(p->mVolumeDirty)
02455                         {
02456                                 int volume = p->mOnMuteList?0:p->mUserVolume;
02457                                 
02458                                 LL_INFOS("Voice") << "Setting volume for avatar " << p->mAvatarID << " to " << volume << LL_ENDL;
02459                                 
02460                                 // Send a mute/unumte command for the user (actually "volume for me").
02461                                 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Session.SetParticipantVolumeForMe.1\">"
02462                                         << "<SessionHandle>" << mSessionHandle << "</SessionHandle>"
02463                                         << "<ParticipantURI>" << p->mURI << "</ParticipantURI>"
02464                                         << "<Volume>" << volume << "</Volume>"
02465                                         << "</Request>\n\n\n";
02466 
02467                                 p->mVolumeDirty = false;
02468                         }
02469                 }
02470         }
02471         
02472         if(mSpeakerMuteDirty)
02473         {
02474                 const char *muteval = ((mSpeakerVolume == -100)?"true":"false");
02475                 LL_INFOS("Voice") << "Setting speaker mute to " << muteval  << LL_ENDL;
02476                 
02477                 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.MuteLocalSpeaker.1\">"
02478                         << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
02479                         << "<Value>" << muteval << "</Value>"
02480                         << "</Request>\n\n\n";          
02481         }
02482         
02483         if(mSpeakerVolumeDirty)
02484         {
02485                 LL_INFOS("Voice") << "Setting speaker volume to " << mSpeakerVolume  << LL_ENDL;
02486 
02487                 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalSpeakerVolume.1\">"
02488                         << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
02489                         << "<Value>" << mSpeakerVolume << "</Value>"
02490                         << "</Request>\n\n\n";          
02491         }
02492         
02493         if(mMicVolumeDirty)
02494         {
02495                 LL_INFOS("Voice") << "Setting mic volume to " << mMicVolume  << LL_ENDL;
02496 
02497                 stream << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Connector.SetLocalMicVolume.1\">"
02498                         << "<ConnectorHandle>" << mConnectorHandle << "</ConnectorHandle>"
02499                         << "<Value>" << mMicVolume << "</Value>"
02500                         << "</Request>\n\n\n";          
02501         }
02502 
02503         
02504         // MBW -- XXX -- Maybe check to make sure the capture/render devices are in the current list here?
02505         if(mCaptureDeviceDirty)
02506         {
02507                 buildSetCaptureDevice(stream);
02508         }
02509 
02510         if(mRenderDeviceDirty)
02511         {
02512                 buildSetRenderDevice(stream);
02513         }
02514         
02515         mSpatialCoordsDirty = false;
02516         mPTTDirty = false;
02517         mVolumeDirty = false;
02518         mSpeakerVolumeDirty = false;
02519         mMicVolumeDirty = false;
02520         mSpeakerMuteDirty = false;
02521         mCaptureDeviceDirty = false;
02522         mRenderDeviceDirty = false;
02523         
02524         if(!stream.str().empty())
02525         {
02526                 writeString(stream.str());
02527         }
02528 }
02529 
02530 void LLVoiceClient::buildSetCaptureDevice(std::ostringstream &stream)
02531 {
02532         LL_DEBUGS("Voice") << "Setting input device = \"" << mCaptureDevice << "\"" << LL_ENDL;
02533         
02534         stream 
02535         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetCaptureDevice.1\">"
02536                 << "<CaptureDeviceSpecifier>" << mCaptureDevice << "</CaptureDeviceSpecifier>"
02537         << "</Request>"
02538         << "\n\n\n";
02539 }
02540 
02541 void LLVoiceClient::buildSetRenderDevice(std::ostringstream &stream)
02542 {
02543         LL_DEBUGS("Voice") << "Setting output device = \"" << mRenderDevice << "\"" << LL_ENDL;
02544 
02545         stream
02546         << "<Request requestId=\"" << mCommandCookie++ << "\" action=\"Aux.SetRenderDevice.1\">"
02547                 << "<RenderDeviceSpecifier>" << mRenderDevice << "</RenderDeviceSpecifier>"
02548         << "</Request>"
02549         << "\n\n\n";
02550 }
02551 
02553 // Response/Event handlers
02554 
02555 void LLVoiceClient::connectorCreateResponse(int statusCode, std::string &statusString, std::string &connectorHandle)
02556 {       
02557         if(statusCode != 0)
02558         {
02559                 LL_WARNS("Voice") << "Connector.Create response failure: " << statusString << LL_ENDL;
02560                 setState(stateConnectorFailed);
02561         }
02562         else
02563         {
02564                 // Connector created, move forward.
02565                 mConnectorHandle = connectorHandle;
02566                 if(getState() == stateConnectorStarting)
02567                 {
02568                         setState(stateConnectorStarted);
02569                 }
02570         }
02571 }
02572 
02573 void LLVoiceClient::loginResponse(int statusCode, std::string &statusString, std::string &accountHandle)
02574 { 
02575         LL_DEBUGS("Voice") << "Account.Login response (" << statusCode << "): " << statusString << LL_ENDL;
02576         
02577         // Status code of 20200 means "bad password".  We may want to special-case that at some point.
02578         
02579         if ( statusCode == 401 )
02580         {
02581                 // Login failure which is probably caused by the delay after a user's password being updated.
02582                 LL_INFOS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
02583                 setState(stateLoginRetry);
02584         }
02585         else if(statusCode != 0)
02586         {
02587                 LL_WARNS("Voice") << "Account.Login response failure (" << statusCode << "): " << statusString << LL_ENDL;
02588                 setState(stateLoginFailed);
02589         }
02590         else
02591         {
02592                 // Login succeeded, move forward.
02593                 mAccountHandle = accountHandle;
02594                 // MBW -- XXX -- This needs to wait until the LoginStateChangeEvent is received.
02595 //              if(getState() == stateLoggingIn)
02596 //              {
02597 //                      setState(stateLoggedIn);
02598 //              }
02599         }
02600 }
02601 
02602 void LLVoiceClient::channelGetListResponse(int statusCode, std::string &statusString)
02603 {
02604         if(statusCode != 0)
02605         {
02606                 LL_WARNS("Voice") << "Account.ChannelGetList response failure: " << statusString << LL_ENDL;
02607                 switchChannel();
02608         }
02609         else
02610         {
02611                 // Got the channel list, try to do a lookup.
02612                 std::string uri = findChannelURI(mChannelName);
02613                 if(uri.empty())
02614                 {       
02615                         // Lookup failed, can't join a channel for this area.
02616                         LL_INFOS("Voice") << "failed to map channel name: " << mChannelName << LL_ENDL;
02617                 }
02618                 else
02619                 {
02620                         // We have a sip URL for this area.
02621                         LL_INFOS("Voice") << "mapped channel " << mChannelName << " to URI "<< uri << LL_ENDL;
02622                 }
02623                 
02624                 // switchChannel with an empty uri string will do the right thing (leave channel and not rejoin)
02625                 switchChannel(uri);
02626         }
02627 }
02628 
02629 void LLVoiceClient::sessionCreateResponse(int statusCode, std::string &statusString, std::string &sessionHandle)
02630 {       
02631         if(statusCode != 0)
02632         {
02633                 LL_WARNS("Voice") << "Session.Create response failure (" << statusCode << "): " << statusString << LL_ENDL;
02634 //              if(statusCode == 1015)
02635 //              {
02636 //                      if(getState() == stateJoiningSession)
02637 //                      {
02638 //                              // this happened during a real join.  Going to sessionTerminated should cause a retry in appropriate cases.
02639 //                              LL_WARNS("Voice") << "session handle \"" << sessionHandle << "\", mSessionStateEventHandle \"" << mSessionStateEventHandle << "\""<< LL_ENDL;
02640 //                              if(!sessionHandle.empty())
02641 //                              {
02642 //                                      // This session is bad.  Terminate it.
02643 //                                      mSessionHandle = sessionHandle;
02644 //                                      sessionTerminateByHandle(sessionHandle);
02645 //                                      setState(stateLeavingSession);
02646 //                              }
02647 //                              else if(!mSessionStateEventHandle.empty())
02648 //                              {
02649 //                                      mSessionHandle = mSessionStateEventHandle;
02650 //                                      sessionTerminateByHandle(mSessionStateEventHandle);
02651 //                                      setState(stateLeavingSession);
02652 //                              }
02653 //                              else
02654 //                              {
02655 //                                      setState(stateSessionTerminated);
02656 //                              }
02657 //                      }
02658 //                      else
02659 //                      {
02660 //                              // We didn't think we were in the middle of a join.  Don't change state.
02661 //                              LL_WARNS("Voice") << "Not in stateJoiningSession, ignoring" << LL_ENDL;
02662 //                      }
02663 //              }
02664 //              else
02665                 {
02666                         mVivoxErrorStatusCode = statusCode;             
02667                         mVivoxErrorStatusString = statusString;
02668                         setState(stateJoinSessionFailed);
02669                 }
02670         }
02671         else
02672         {
02673                 LL_DEBUGS("Voice") << "Session.Create response received (success), session handle is " << sessionHandle << LL_ENDL;
02674                 if(getState() == stateJoiningSession)
02675                 {
02676                         // This is also grabbed in the SessionStateChangeEvent handler, but it might be useful to have it early...
02677                         mSessionHandle = sessionHandle;
02678                 }
02679                 else
02680                 {
02681                         // We should never get a session.create response in any state except stateJoiningSession.  Things are out of sync.  Kill this session.
02682                         sessionTerminateByHandle(sessionHandle);
02683                 }
02684         }
02685 }
02686 
02687 void LLVoiceClient::sessionConnectResponse(int statusCode, std::string &statusString)
02688 {
02689         if(statusCode != 0)
02690         {
02691                 LL_WARNS("Voice") << "Session.Connect response failure (" << statusCode << "): " << statusString << LL_ENDL;
02692 //              if(statusCode == 1015)
02693 //              {
02694 //                      LL_WARNS("Voice") << "terminating existing session" << LL_ENDL;
02695 //                      sessionTerminate();
02696 //              }
02697 //              else
02698                 {
02699                         mVivoxErrorStatusCode = statusCode;             
02700                         mVivoxErrorStatusString = statusString;
02701                         setState(stateJoinSessionFailed);
02702                 }
02703         }
02704         else
02705         {
02706                 LL_DEBUGS("Voice") << "Session.Connect response received (success)" << LL_ENDL;
02707         }
02708 }
02709 
02710 void LLVoiceClient::sessionTerminateResponse(int statusCode, std::string &statusString)
02711 {       
02712         if(statusCode != 0)
02713         {
02714                 LL_WARNS("Voice") << "Session.Terminate response failure: (" << statusCode << "): " << statusString << LL_ENDL;
02715                 if(getState() == stateLeavingSession)
02716                 {
02717                         // This is probably "(404): Server reporting Failure. Not a member of this conference."
02718                         // Do this so we don't get stuck.
02719                         setState(stateSessionTerminated);
02720                 }
02721         }
02722         
02723 }
02724 
02725 void LLVoiceClient::logoutResponse(int statusCode, std::string &statusString)
02726 {       
02727         if(statusCode != 0)
02728         {
02729                 LL_WARNS("Voice") << "Account.Logout response failure: " << statusString << LL_ENDL;
02730                 // MBW -- XXX -- Should this ever fail?  do we care if it does?
02731         }
02732         
02733         if(getState() == stateLoggingOut)
02734         {
02735                 setState(stateLoggedOut);
02736         }
02737 }
02738 
02739 void LLVoiceClient::connectorShutdownResponse(int statusCode, std::string &statusString)
02740 {
02741         if(statusCode != 0)
02742         {
02743                 LL_WARNS("Voice") << "Connector.InitiateShutdown response failure: " << statusString << LL_ENDL;
02744                 // MBW -- XXX -- Should this ever fail?  do we care if it does?
02745         }
02746         
02747         mConnected = false;
02748         
02749         if(getState() == stateConnectorStopping)
02750         {
02751                 setState(stateConnectorStopped);
02752         }
02753 }
02754 
02755 void LLVoiceClient::sessionStateChangeEvent(
02756                 std::string &uriString, 
02757                 int statusCode, 
02758                 std::string &statusString, 
02759                 std::string &sessionHandle,
02760                 int state,  
02761                 bool isChannel, 
02762                 std::string &nameString)
02763 {
02764         switch(state)
02765         {
02766                 case 4: // I see this when joining the session
02767                         LL_INFOS("Voice") << "joined session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << LL_ENDL;
02768 
02769                         // Session create succeeded, move forward.
02770                         mSessionStateEventHandle = sessionHandle;
02771                         mSessionStateEventURI = uriString;
02772                         if(sessionHandle == mSessionHandle)
02773                         {
02774                                 // This is the session we're joining.
02775                                 if(getState() == stateJoiningSession)
02776                                 {
02777                                         setState(stateSessionJoined);
02778                                         //RN: the uriString being returned by vivox here is actually your account uri, not the channel
02779                                         // you are attempting to join, so ignore it
02780                                         //LL_DEBUGS("Voice") << "received URI " << uriString << "(previously " << mSessionURI << ")" << LL_ENDL;
02781                                         //mSessionURI = uriString;
02782                                 }
02783                         }
02784                         else if(sessionHandle == mNextSessionHandle)
02785                         {
02786 //                              LL_DEBUGS("Voice") << "received URI " << uriString << ", name " << nameString << " for next session (handle " << mNextSessionHandle << ")" << LL_ENDL;
02787                         }
02788                         else
02789                         {
02790                                 LL_WARNS("Voice") << "joining unknown session handle " << sessionHandle << ", URI " << uriString << ", name " << nameString << LL_ENDL;
02791                                 // MBW -- XXX -- Should we send a Session.Terminate here?
02792                         }
02793                         
02794                 break;
02795                 case 5: // I see this when leaving the session
02796                         LL_INFOS("Voice") << "left session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << LL_ENDL;
02797 
02798                         // Set the session handle to the empty string.  If we get back to stateJoiningSession, we'll want to wait for the new session handle.
02799                         if(sessionHandle == mSessionHandle)
02800                         {
02801                                 // MBW -- XXX -- I think this is no longer necessary, now that we've got mNextSessionURI/mNextSessionHandle
02802                                 // mSessionURI.clear();
02803                                 // clear the session handle here just for sanity.
02804                                 mSessionHandle.clear();
02805                                 if(mSessionResetOnClose)
02806                                 {
02807                                         mSessionResetOnClose = false;
02808                                         mNonSpatialChannel = false;
02809                                         mNextSessionSpatial = true;
02810                                         parcelChanged();
02811                                 }
02812                         
02813                                 removeAllParticipants();
02814                                 
02815                                 switch(getState())
02816                                 {
02817                                         case stateJoiningSession:
02818                                         case stateSessionJoined:
02819                                         case stateRunning:
02820                                         case stateLeavingSession:
02821                                         case stateJoinSessionFailed:
02822                                         case stateJoinSessionFailedWaiting:
02823                                                 // normal transition
02824                                                 LL_INFOS("Voice") << "left session " << sessionHandle << "in state " << state2string(getState()) << LL_ENDL;
02825                                                 setState(stateSessionTerminated);
02826                                         break;
02827                                         
02828                                         case stateSessionTerminated:
02829                                                 // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state.
02830                                                 LL_WARNS("Voice") << "left session " << sessionHandle << "in state " << state2string(getState()) << LL_ENDL;
02831                                         break;
02832                                         
02833                                         default:
02834                                                 LL_WARNS("Voice") << "unexpected SessionStateChangeEvent (left session) in state " << state2string(getState()) << LL_ENDL;
02835                                                 setState(stateSessionTerminated);
02836                                         break;
02837                                 }
02838 
02839                                 // store status values for later notification of observers
02840                                 mVivoxErrorStatusCode = statusCode;             
02841                                 mVivoxErrorStatusString = statusString;
02842                         }
02843                         else
02844                         {
02845                                 LL_INFOS("Voice") << "leaving unknown session handle " << sessionHandle << ", URI " << uriString << ", name " << nameString << LL_ENDL;
02846                         }
02847 
02848                         mSessionStateEventHandle.clear();
02849                         mSessionStateEventURI.clear();
02850                 break;
02851                 default:
02852                         LL_WARNS("Voice") << "unknown state: " << state << LL_ENDL;
02853                 break;
02854         }
02855 }
02856 
02857 void LLVoiceClient::loginStateChangeEvent(
02858                 std::string &accountHandle, 
02859                 int statusCode, 
02860                 std::string &statusString, 
02861                 int state)
02862 {
02863         LL_DEBUGS("Voice") << "state is " << state << LL_ENDL;
02864         /*
02865                 According to Mike S., status codes for this event are:
02866                 login_state_logged_out=0,
02867         login_state_logged_in = 1,
02868         login_state_logging_in = 2,
02869         login_state_logging_out = 3,
02870         login_state_resetting = 4,
02871         login_state_error=100   
02872         */
02873         
02874         switch(state)
02875         {
02876                 case 1:
02877                 if(getState() == stateLoggingIn)
02878                 {
02879                         setState(stateLoggedIn);
02880                 }
02881                 break;
02882                 
02883                 default:
02884                         //Used to be a commented out warning
02885                         LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL;
02886                 break;
02887         }
02888 }
02889 
02890 void LLVoiceClient::sessionNewEvent(
02891                 std::string &accountHandle, 
02892                 std::string &eventSessionHandle, 
02893                 int state, 
02894                 std::string &nameString, 
02895                 std::string &uriString)
02896 {
02897         LL_DEBUGS("Voice") << "state is " << state << LL_ENDL;
02898         
02899         switch(state)
02900         {
02901                 case 0:
02902                         {
02903                                 LL_DEBUGS("Voice") << "session handle = " << eventSessionHandle << ", name = " << nameString << ", uri = " << uriString << LL_ENDL;
02904 
02905                                 LLUUID caller_id;
02906                                 if(IDFromName(nameString, caller_id))
02907                                 {
02908                                         gIMMgr->inviteToSession(
02909                                                 LLIMMgr::computeSessionID(
02910                                                         IM_SESSION_P2P_INVITE,
02911                                                         caller_id),
02912                                                 LLString::null,
02913                                                 caller_id, 
02914                                                 LLString::null, 
02915                                                 IM_SESSION_P2P_INVITE, 
02916                                                 LLIMMgr::INVITATION_TYPE_VOICE,
02917                                                 eventSessionHandle);
02918                                 }
02919                                 else
02920                                 {
02921                                         LL_WARNS("Voice") << "Could not generate caller id from uri " << uriString << LL_ENDL;
02922                                 }
02923                         }
02924                 break;
02925                 
02926                 default:
02927                         LL_WARNS("Voice") << "unknown state: " << state << LL_ENDL;
02928                 break;
02929         }
02930 }
02931 
02932 void LLVoiceClient::participantStateChangeEvent(
02933                 std::string &uriString, 
02934                 int statusCode, 
02935                 std::string &statusString, 
02936                 int state,  
02937                 std::string &nameString, 
02938                 std::string &displayNameString, 
02939                 int participantType)
02940 {
02941         participantState *participant = NULL;
02942         LL_DEBUGS("Voice") << "state is " << state << LL_ENDL;
02943 
02944         switch(state)
02945         {
02946                 case 7: // I see this when a participant joins
02947                         participant = addParticipant(uriString);
02948                         if(participant)
02949                         {
02950                                 participant->mName = nameString;
02951                                 LL_DEBUGS("Voice") << "added participant \"" << participant->mName 
02952                                                 << "\" (" << participant->mAvatarID << ")"<< LL_ENDL;
02953                         }
02954                 break;
02955                 case 9: // I see this when a participant leaves
02956                         participant = findParticipant(uriString);
02957                         if(participant)
02958                         {
02959                                 removeParticipant(participant);
02960                         }
02961                 break;
02962                 default:
02963                         LL_DEBUGS("Voice") << "unknown state: " << state << LL_ENDL;
02964                 break;
02965         }
02966 }
02967 
02968 void LLVoiceClient::participantPropertiesEvent(
02969                 std::string &uriString, 
02970                 int statusCode, 
02971                 std::string &statusString, 
02972                 bool isLocallyMuted, 
02973                 bool isModeratorMuted, 
02974                 bool isSpeaking, 
02975                 int volume, 
02976                 F32 energy)
02977 {
02978         participantState *participant = findParticipant(uriString);
02979         if(participant)
02980         {
02981                 participant->mPTT = !isLocallyMuted;
02982                 participant->mIsSpeaking = isSpeaking;
02983                 participant->mIsModeratorMuted = isModeratorMuted;
02984                 if (isSpeaking)
02985                 {
02986                         participant->mSpeakingTimeout.reset();
02987                 }
02988                 participant->mPower = energy;
02989                 participant->mVolume = volume;
02990         }
02991         else
02992         {
02993                 LL_WARNS("Voice") << "unknown participant: " << uriString << LL_ENDL;
02994         }
02995 }
02996 
02997 void LLVoiceClient::auxAudioPropertiesEvent(F32 energy)
02998 {
02999         LL_DEBUGS("Voice") << "got energy " << energy << LL_ENDL;
03000         mTuningEnergy = energy;
03001 }
03002 
03003 void LLVoiceClient::muteListChanged()
03004 {
03005         // The user's mute list has been updated.  Go through the current participant list and sync it with the mute list.
03006 
03007         participantMap::iterator iter = mParticipantMap.begin();
03008         
03009         for(; iter != mParticipantMap.end(); iter++)
03010         {
03011                 participantState *p = iter->second;
03012                 
03013                 // Check to see if this participant is on the mute list already
03014                 updateMuteState(p);
03015         }
03016 }
03017 
03019 // Managing list of participants
03020 LLVoiceClient::participantState::participantState(const std::string &uri) : 
03021          mURI(uri), mPTT(false), mIsSpeaking(false), mIsModeratorMuted(false), mPower(0.0), mServiceType(serviceTypeUnknown),
03022          mOnMuteList(false), mUserVolume(100), mVolumeDirty(false), mAvatarIDValid(false)
03023 {
03024 }
03025 
03026 LLVoiceClient::participantState *LLVoiceClient::addParticipant(const std::string &uri)
03027 {
03028         participantState *result = NULL;
03029 
03030         participantMap::iterator iter = mParticipantMap.find(uri);
03031         
03032         if(iter != mParticipantMap.end())
03033         {
03034                 // Found a matching participant already in the map.
03035                 result = iter->second;
03036         }
03037 
03038         if(!result)
03039         {
03040                 // participant isn't already in one list or the other.
03041                 result = new participantState(uri);
03042                 mParticipantMap.insert(participantMap::value_type(uri, result));
03043                 mParticipantMapChanged = true;
03044                 
03045                 // Try to do a reverse transform on the URI to get the GUID back.
03046                 {
03047                         LLUUID id;
03048                         if(IDFromName(uri, id))
03049                         {
03050                                 result->mAvatarIDValid = true;
03051                                 result->mAvatarID = id;
03052 
03053                                 updateMuteState(result);
03054                         }
03055                 }
03056                 
03057                 LL_DEBUGS("Voice") << "participant \"" << result->mURI << "\" added." << LL_ENDL;
03058         }
03059         
03060         return result;
03061 }
03062 
03063 void LLVoiceClient::updateMuteState(participantState *p)
03064 {
03065         if(p->mAvatarIDValid)
03066         {
03067                 bool isMuted = LLMuteList::getInstance()->isMuted(p->mAvatarID, LLMute::flagVoiceChat);
03068                 if(p->mOnMuteList != isMuted)
03069                 {
03070                         p->mOnMuteList = isMuted;
03071                         p->mVolumeDirty = true;
03072                         mVolumeDirty = true;
03073                 }
03074         }
03075 }
03076 
03077 void LLVoiceClient::removeParticipant(LLVoiceClient::participantState *participant)
03078 {
03079         if(participant)
03080         {
03081                 participantMap::iterator iter = mParticipantMap.find(participant->mURI);
03082                                 
03083                 LL_DEBUGS("Voice") << "participant \"" << participant->mURI <<  "\" (" << participant->mAvatarID << ") removed." << LL_ENDL;
03084 
03085                 mParticipantMap.erase(iter);
03086                 delete participant;
03087                 mParticipantMapChanged = true;
03088         }
03089 }
03090 
03091 void LLVoiceClient::removeAllParticipants()
03092 {
03093         LL_DEBUGS("Voice") << "called" << LL_ENDL;
03094 
03095         while(!mParticipantMap.empty())
03096         {
03097                 removeParticipant(mParticipantMap.begin()->second);
03098         }
03099 }
03100 
03101 LLVoiceClient::participantMap *LLVoiceClient::getParticipantList(void)
03102 {
03103         return &mParticipantMap;
03104 }
03105 
03106 
03107 LLVoiceClient::participantState *LLVoiceClient::findParticipant(const std::string &uri)
03108 {
03109         participantState *result = NULL;
03110         
03111         // Don't find any participants if we're not connected.  This is so that we don't continue to get stale data
03112         // after the daemon dies.
03113         if(mConnected)
03114         {
03115                 participantMap::iterator iter = mParticipantMap.find(uri);
03116         
03117                 if(iter != mParticipantMap.end())
03118                 {
03119                         result = iter->second;
03120                 }
03121         }
03122                         
03123         return result;
03124 }
03125 
03126 
03127 LLVoiceClient::participantState *LLVoiceClient::findParticipantByAvatar(LLVOAvatar *avatar)
03128 {
03129         participantState * result = NULL;
03130 
03131         // You'd think this would work, but it doesn't...
03132 //      std::string uri = sipURIFromAvatar(avatar);
03133         
03134         // Currently, the URI is just the account name.
03135         std::string loginName = nameFromAvatar(avatar);
03136         result = findParticipant(loginName);
03137         
03138         if(result != NULL)
03139         {
03140                 if(!result->mAvatarIDValid)
03141                 {
03142                         result->mAvatarID = avatar->getID();
03143                         result->mAvatarIDValid = true;
03144                         
03145                         // We just figured out the avatar ID, so the participant list has "changed" from the perspective of anyone who uses that to identify participants.
03146                         mParticipantMapChanged = true;
03147                         
03148                         updateMuteState(result);
03149                 }
03150                 
03151 
03152         }
03153 
03154         return result;
03155 }
03156 
03157 LLVoiceClient::participantState* LLVoiceClient::findParticipantByID(const LLUUID& id)
03158 {
03159         participantState * result = NULL;
03160 
03161         // Currently, the URI is just the account name.
03162         std::string loginName = nameFromID(id);
03163         result = findParticipant(loginName);
03164 
03165         return result;
03166 }
03167 
03168 
03169 void LLVoiceClient::clearChannelMap(void)
03170 {
03171         mChannelMap.clear();
03172 }
03173 
03174 void LLVoiceClient::addChannelMapEntry(std::string &name, std::string &uri)
03175 {
03176         LL_DEBUGS("Voice") << "Adding channel name mapping: " << name << " -> " << uri << LL_ENDL;
03177         mChannelMap.insert(channelMap::value_type(name, uri));
03178 }
03179 
03180 std::string LLVoiceClient::findChannelURI(std::string &name)
03181 {
03182         std::string result;
03183         
03184         channelMap::iterator iter = mChannelMap.find(name);
03185 
03186         if(iter != mChannelMap.end())
03187         {
03188                 result = iter->second;
03189         }
03190         
03191         return result;
03192 }
03193 
03194 void LLVoiceClient::parcelChanged()
03195 {
03196         if(getState() >= stateLoggedIn)
03197         {
03198                 // If the user is logged in, start a channel lookup.
03199                 LL_DEBUGS("Voice") << "sending ParcelVoiceInfoRequest (" << mCurrentRegionName << ", " << mCurrentParcelLocalID << ")" << LL_ENDL;
03200 
03201                 std::string url = gAgent.getRegion()->getCapability("ParcelVoiceInfoRequest");
03202                 LLSD data;
03203                 LLHTTPClient::post(
03204                         url,
03205                         data,
03206                         new LLVoiceClientCapResponder);
03207         }
03208         else
03209         {
03210                 // The transition to stateLoggedIn needs to kick this off again.
03211                 LL_INFOS("Voice") << "not logged in yet, deferring" << LL_ENDL;
03212         }
03213 }
03214 
03215 void LLVoiceClient::switchChannel(
03216         std::string uri,
03217         bool spatial,
03218         bool noReconnect,
03219         std::string hash)
03220 {
03221         bool needsSwitch = false;
03222         
03223         LL_DEBUGS("Voice") << "called in state " << state2string(getState()) << " with uri \"" << uri << "\"" << LL_ENDL;
03224         
03225         switch(getState())
03226         {
03227                 case stateJoinSessionFailed:
03228                 case stateJoinSessionFailedWaiting:
03229                 case stateNoChannel:
03230                         // Always switch to the new URI from these states.
03231                         needsSwitch = true;
03232                 break;
03233                 
03234                 default:
03235                         if(mSessionTerminateRequested)
03236                         {
03237                                 // If a terminate has been requested, we need to compare against where the URI we're already headed to.
03238                                 if(mNextSessionURI != uri)
03239                                         needsSwitch = true;
03240                         }
03241                         else
03242                         {
03243                                 // Otherwise, compare against the URI we're in now.
03244                                 if(mSessionURI != uri)
03245                                         needsSwitch = true;
03246                         }
03247                 break;
03248         }
03249         
03250         if(needsSwitch)
03251         {
03252                 mNextSessionURI = uri;
03253                 mNextSessionHash = hash;
03254                 mNextSessionHandle.clear();
03255                 mNextP2PSessionURI.clear();
03256                 mNextSessionSpatial = spatial;
03257                 mNextSessionNoReconnect = noReconnect;
03258                 
03259                 if(uri.empty())
03260                 {
03261                         // Leave any channel we may be in
03262                         LL_DEBUGS("Voice") << "leaving channel" << LL_ENDL;
03263                         notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_VOICE_DISABLED);
03264                 }
03265                 else
03266                 {
03267                         LL_DEBUGS("Voice") << "switching to channel " << uri << LL_ENDL;
03268                 }
03269                 
03270                 if(getState() <= stateNoChannel)
03271                 {
03272                         // We're already set up to join a channel, just needed to fill in the session URI
03273                 }
03274                 else
03275                 {
03276                         // State machine will come around and rejoin if uri/handle is not empty.
03277                         sessionTerminate();
03278                 }
03279         }
03280 }
03281 
03282 void LLVoiceClient::joinSession(std::string handle, std::string uri)
03283 {
03284         mNextSessionURI.clear();
03285         mNextSessionHash.clear();
03286         mNextP2PSessionURI = uri;
03287         mNextSessionHandle = handle;
03288         mNextSessionSpatial = false;
03289         mNextSessionNoReconnect = false;
03290 
03291         if(getState() <= stateNoChannel)
03292         {
03293                 // We're already set up to join a channel, just needed to fill in the session handle
03294         }
03295         else
03296         {
03297                 // State machine will come around and rejoin if uri/handle is not empty.
03298                 sessionTerminate();
03299         }
03300 }
03301 
03302 void LLVoiceClient::setNonSpatialChannel(
03303         const std::string &uri,
03304         const std::string &credentials)
03305 {
03306         switchChannel(uri, false, false, credentials);
03307 }
03308 
03309 void LLVoiceClient::setSpatialChannel(
03310         const std::string &uri,
03311         const std::string &credentials)
03312 {
03313         mSpatialSessionURI = uri;
03314         mAreaVoiceDisabled = mSpatialSessionURI.empty();
03315 
03316         LL_DEBUGS("Voice") << "got spatial channel uri: \"" << uri << "\"" << LL_ENDL;
03317         
03318         if(mNonSpatialChannel || !mNextSessionSpatial)
03319         {
03320                 // User is in a non-spatial chat or joining a non-spatial chat.  Don't switch channels.
03321                 LL_INFOS("Voice") << "in non-spatial chat, not switching channels" << LL_ENDL;
03322         }
03323         else
03324         {
03325                 switchChannel(mSpatialSessionURI, true, false, credentials);
03326         }
03327 }
03328 
03329 void LLVoiceClient::callUser(LLUUID &uuid)
03330 {
03331         std::string userURI = sipURIFromID(uuid);
03332 
03333         switchChannel(userURI, false, true);
03334 }
03335 
03336 void LLVoiceClient::answerInvite(std::string &sessionHandle, LLUUID& other_user_id)
03337 {
03338         joinSession(sessionHandle, sipURIFromID(other_user_id));
03339 }
03340 
03341 void LLVoiceClient::declineInvite(std::string &sessionHandle)
03342 {
03343         sessionTerminateByHandle(sessionHandle);
03344 }
03345 
03346 void LLVoiceClient::leaveNonSpatialChannel()
03347 {
03348         switchChannel(mSpatialSessionURI);
03349 }
03350 
03351 std::string LLVoiceClient::getCurrentChannel()
03352 {
03353         if((getState() == stateRunning) && !mSessionTerminateRequested)
03354         {
03355                 return mSessionURI;
03356         }
03357         
03358         return "";
03359 }
03360 
03361 bool LLVoiceClient::inProximalChannel()
03362 {
03363         bool result = false;
03364         
03365         if((getState() == stateRunning) && !mSessionTerminateRequested)
03366         {
03367                 result = !mNonSpatialChannel;
03368         }
03369         
03370         return result;
03371 }
03372 
03373 std::string LLVoiceClient::sipURIFromID(const LLUUID &id)
03374 {
03375         std::string result;
03376         result = "sip:";
03377         result += nameFromID(id);
03378         result += "@";
03379         result += mAccountServerName;
03380         
03381         return result;
03382 }
03383 
03384 std::string LLVoiceClient::sipURIFromAvatar(LLVOAvatar *avatar)
03385 {
03386         std::string result;
03387         if(avatar)
03388         {
03389                 result = "sip:";
03390                 result += nameFromID(avatar->getID());
03391                 result += "@";
03392                 result += mAccountServerName;
03393         }
03394         
03395         return result;
03396 }
03397 
03398 std::string LLVoiceClient::nameFromAvatar(LLVOAvatar *avatar)
03399 {
03400         std::string result;
03401         if(avatar)
03402         {
03403                 result = nameFromID(avatar->getID());
03404         }       
03405         return result;
03406 }
03407 
03408 std::string LLVoiceClient::nameFromID(const LLUUID &uuid)
03409 {
03410         std::string result;
03411         U8 rawuuid[UUID_BYTES + 1]; 
03412         uuid.toCompressedString((char*)rawuuid);
03413         
03414         // Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code.
03415         result = "x";
03416         
03417         // Base64 encode and replace the pieces of base64 that are less compatible 
03418         // with e-mail local-parts.
03419         // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet"
03420         result += LLBase64::encode(rawuuid, UUID_BYTES);
03421         LLString::replaceChar(result, '+', '-');
03422         LLString::replaceChar(result, '/', '_');
03423         
03424         // If you need to transform a GUID to this form on the Mac OS X command line, this will do so:
03425         // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-')
03426         
03427         return result;
03428 }
03429 
03430 bool LLVoiceClient::IDFromName(const std::string name, LLUUID &uuid)
03431 {
03432         bool result = false;
03433         
03434         // This will only work if the name is of the proper form.
03435         // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is:
03436         // "xFnPP04IpREWNkuw1cOXlhw=="
03437         
03438         if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '='))
03439         {
03440                 // The name appears to have the right form.
03441 
03442                 // Reverse the transforms done by nameFromID
03443                 std::string temp = name;
03444                 LLString::replaceChar(temp, '-', '+');
03445                 LLString::replaceChar(temp, '_', '/');
03446 
03447                 U8 rawuuid[UUID_BYTES + 1]; 
03448                 int len = apr_base64_decode_binary(rawuuid, temp.c_str() + 1);
03449                 if(len == UUID_BYTES)
03450                 {
03451                         // The decode succeeded.  Stuff the bits into the result's UUID
03452                         // MBW -- XXX -- there's no analogue of LLUUID::toCompressedString that allows you to set a UUID from binary data.
03453                         // The data field is public, so we cheat thusly:
03454                         memcpy(uuid.mData, rawuuid, UUID_BYTES);
03455                         result = true;
03456                 }
03457         }
03458         
03459         return result;
03460 }
03461 
03462 std::string LLVoiceClient::displayNameFromAvatar(LLVOAvatar *avatar)
03463 {
03464         return avatar->getFullname();
03465 }
03466 
03467 std::string LLVoiceClient::sipURIFromName(std::string &name)
03468 {
03469         std::string result;
03470         result = "sip:";
03471         result += name;
03472         result += "@";
03473         result += mAccountServerName;
03474 
03475 //      LLString::toLower(result);
03476 
03477         return result;
03478 }
03479 
03481 // Sending updates of current state
03482 
03483 void LLVoiceClient::enforceTether(void)
03484 {
03485         LLVector3d tethered     = mCameraRequestedPosition;
03486 
03487         // constrain 'tethered' to within 50m of mAvatarPosition.
03488         {
03489                 F32 max_dist = 50.0f;
03490                 LLVector3d camera_offset = mCameraRequestedPosition - mAvatarPosition;
03491                 F32 camera_distance = (F32)camera_offset.magVec();
03492                 if(camera_distance > max_dist)
03493                 {
03494                         tethered = mAvatarPosition + 
03495                                 (max_dist / camera_distance) * camera_offset;
03496                 }
03497         }
03498         
03499         if(dist_vec(mCameraPosition, tethered) > 0.1)
03500         {
03501                 mCameraPosition = tethered;
03502                 mSpatialCoordsDirty = true;
03503         }
03504 }
03505 
03506 void LLVoiceClient::setCameraPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
03507 {
03508         mCameraRequestedPosition = position;
03509         
03510         if(mCameraVelocity != velocity)
03511         {
03512                 mCameraVelocity = velocity;
03513                 mSpatialCoordsDirty = true;
03514         }
03515         
03516         if(mCameraRot != rot)
03517         {
03518                 mCameraRot = rot;
03519                 mSpatialCoordsDirty = true;
03520         }
03521 }
03522 
03523 void LLVoiceClient::setAvatarPosition(const LLVector3d &position, const LLVector3 &velocity, const LLMatrix3 &rot)
03524 {
03525         if(dist_vec(mAvatarPosition, position) > 0.1)
03526         {
03527                 mAvatarPosition = position;
03528                 mSpatialCoordsDirty = true;
03529         }
03530         
03531         if(mAvatarVelocity != velocity)
03532         {
03533                 mAvatarVelocity = velocity;
03534                 mSpatialCoordsDirty = true;
03535         }
03536         
03537         if(mAvatarRot != rot)
03538         {
03539                 mAvatarRot = rot;
03540                 mSpatialCoordsDirty = true;
03541         }
03542 }
03543 
03544 bool LLVoiceClient::channelFromRegion(LLViewerRegion *region, std::string &name)
03545 {
03546         bool result = false;
03547         
03548         if(region)
03549         {
03550                 name = region->getName();
03551         }
03552         
03553         if(!name.empty())
03554                 result = true;
03555         
03556         return result;
03557 }
03558 
03559 void LLVoiceClient::leaveChannel(void)
03560 {
03561         if(getState() == stateRunning)
03562         {
03563                 LL_DEBUGS("Voice") << "leaving channel for teleport/logout" << LL_ENDL;
03564                 mChannelName.clear();
03565                 sessionTerminate();
03566         }
03567 }
03568 
03569 void LLVoiceClient::setMuteMic(bool muted)
03570 {
03571         mMuteMic = muted;
03572 }
03573 
03574 void LLVoiceClient::setUserPTTState(bool ptt)
03575 {
03576         mUserPTTState = ptt;
03577 }
03578 
03579 bool LLVoiceClient::getUserPTTState()
03580 {
03581         return mUserPTTState;
03582 }
03583 
03584 void LLVoiceClient::toggleUserPTTState(void)
03585 {
03586         mUserPTTState = !mUserPTTState;
03587 }
03588 
03589 void LLVoiceClient::setVoiceEnabled(bool enabled)
03590 {
03591         if (enabled != mVoiceEnabled)
03592         {
03593                 mVoiceEnabled = enabled;
03594                 if (enabled)
03595                 {
03596                         LLVoiceChannel::getCurrentVoiceChannel()->activate();
03597                 }
03598                 else
03599                 {
03600                         // for now, leave active channel, to auto join when turning voice back on
03601                         //LLVoiceChannel::getCurrentVoiceChannel->deactivate();
03602                 }
03603         }
03604 }
03605 
03606 bool LLVoiceClient::voiceEnabled()
03607 {
03608         return gSavedSettings.getBOOL("EnableVoiceChat") && !gSavedSettings.getBOOL("CmdLineDisableVoice");
03609 }
03610 
03611 void LLVoiceClient::setLipSyncEnabled(U32 enabled)
03612 {
03613         mLipSyncEnabled = enabled;
03614 }
03615 
03616 U32 LLVoiceClient::lipSyncEnabled()
03617 {
03618            
03619         if ( mVoiceEnabled && stateDisabled != getState() )
03620         {
03621                 return mLipSyncEnabled;
03622         }
03623         else
03624         {
03625                 return 0;
03626         }
03627 }
03628 
03629 void LLVoiceClient::setUsePTT(bool usePTT)
03630 {
03631         if(usePTT && !mUsePTT)
03632         {
03633                 // When the user turns on PTT, reset the current state.
03634                 mUserPTTState = false;
03635         }
03636         mUsePTT = usePTT;
03637 }
03638 
03639 void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle)
03640 {
03641         if(!PTTIsToggle && mPTTIsToggle)
03642         {
03643                 // When the user turns off toggle, reset the current state.
03644                 mUserPTTState = false;
03645         }
03646         
03647         mPTTIsToggle = PTTIsToggle;
03648 }
03649 
03650 
03651 void LLVoiceClient::setPTTKey(std::string &key)
03652 {
03653         if(key == "MiddleMouse")
03654         {
03655                 mPTTIsMiddleMouse = true;
03656         }
03657         else
03658         {
03659                 mPTTIsMiddleMouse = false;
03660                 if(!LLKeyboard::keyFromString(key, &mPTTKey))
03661                 {
03662                         // If the call failed, don't match any key.
03663                         key = KEY_NONE;
03664                 }
03665         }
03666 }
03667 
03668 void LLVoiceClient::setEarLocation(S32 loc)
03669 {
03670         if(mEarLocation != loc)
03671         {
03672                 LL_DEBUGS("Voice") << "Setting mEarLocation to " << loc << LL_ENDL;
03673                 
03674                 mEarLocation = loc;
03675                 mSpatialCoordsDirty = true;
03676         }
03677 }
03678 
03679 void LLVoiceClient::setVoiceVolume(F32 volume)
03680 {
03681         LL_DEBUGS("Voice") << "volume is " << volume << LL_ENDL;
03682 
03683         // incoming volume has the range [0.0 ... 1.0], with 0.5 as the default.
03684         // Map it as follows: 0.0 -> -100, 0.5 -> 24, 1.0 -> 50
03685         
03686         volume -= 0.5f;         // offset volume to the range [-0.5 ... 0.5], with 0 at the default.
03687         int scaledVolume = 24;  // offset scaledVolume by its default level
03688         if(volume < 0.0f)
03689                 scaledVolume += ((int)(volume * 248.0f));       // (24 - (-100)) * 2
03690         else
03691                 scaledVolume += ((int)(volume * 52.0f));        // (50 - 24) * 2
03692         
03693         if(scaledVolume != mSpeakerVolume)
03694         {
03695                 if((scaledVolume == -100) || (mSpeakerVolume == -100))
03696                 {
03697                         mSpeakerMuteDirty = true;
03698                 }
03699 
03700                 mSpeakerVolume = scaledVolume;
03701                 mSpeakerVolumeDirty = true;
03702         }
03703 }
03704 
03705 void LLVoiceClient::setMicGain(F32 volume)
03706 {
03707         int scaledVolume = ((int)(volume * 100.0f)) - 100;
03708         if(scaledVolume != mMicVolume)
03709         {
03710                 mMicVolume = scaledVolume;
03711                 mMicVolumeDirty = true;
03712         }
03713 }
03714 
03715 void LLVoiceClient::setVivoxDebugServerName(std::string &serverName)
03716 {
03717         if(!mAccountServerName.empty())
03718         {
03719                 // The name has been filled in already, which means we know whether we're connecting to agni or not.
03720                 if(!sConnectingToAgni)
03721                 {
03722                         // Only use the setting if we're connecting to a development grid -- always use bhr when on agni.
03723                         mAccountServerName = serverName;
03724                 }
03725         }
03726 }
03727 
03728 void LLVoiceClient::keyDown(KEY key, MASK mask)
03729 {       
03730         LL_DEBUGS("Voice") << "key is " << LLKeyboard::stringFromKey(key) << LL_ENDL;
03731 
03732         if (gKeyboard->getKeyRepeated(key))
03733         {
03734                 // ignore auto-repeat keys
03735                 return;
03736         }
03737 
03738         if(!mPTTIsMiddleMouse)
03739         {
03740                 if(mPTTIsToggle)
03741                 {
03742                         if(key == mPTTKey)
03743                         {
03744                                 toggleUserPTTState();
03745                         }
03746                 }
03747                 else if(mPTTKey != KEY_NONE)
03748                 {
03749                         setUserPTTState(gKeyboard->getKeyDown(mPTTKey));
03750                 }
03751         }
03752 }
03753 void LLVoiceClient::keyUp(KEY key, MASK mask)
03754 {
03755         if(!mPTTIsMiddleMouse)
03756         {
03757                 if(!mPTTIsToggle && (mPTTKey != KEY_NONE))
03758                 {
03759                         setUserPTTState(gKeyboard->getKeyDown(mPTTKey));
03760                 }
03761         }
03762 }
03763 void LLVoiceClient::middleMouseState(bool down)
03764 {
03765         if(mPTTIsMiddleMouse)
03766         {
03767                 if(mPTTIsToggle)
03768                 {
03769                         if(down)
03770                         {
03771                                 toggleUserPTTState();
03772                         }
03773                 }
03774                 else
03775                 {
03776                         setUserPTTState(down);
03777                 }
03778         }
03779 }
03780 
03782 // Accessors for data related to nearby speakers
03783 BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id)
03784 {
03785         BOOL result = FALSE;
03786         participantState *participant = findParticipantByID(id);
03787         if(participant)
03788         {
03789                 // I'm not sure what the semantics of this should be.
03790                 // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled.
03791                 result = TRUE;
03792         }
03793         
03794         return result;
03795 }
03796 
03797 BOOL LLVoiceClient::getIsSpeaking(const LLUUID& id)
03798 {
03799         BOOL result = FALSE;
03800 
03801         participantState *participant = findParticipantByID(id);
03802         if(participant)
03803         {
03804                 if (participant->mSpeakingTimeout.getElapsedTimeF32() > SPEAKING_TIMEOUT)
03805                 {
03806                         participant->mIsSpeaking = FALSE;
03807                 }
03808                 result = participant->mIsSpeaking;
03809         }
03810         
03811         return result;
03812 }
03813 
03814 BOOL LLVoiceClient::getIsModeratorMuted(const LLUUID& id)
03815 {
03816         BOOL result = FALSE;
03817 
03818         participantState *participant = findParticipantByID(id);
03819         if(participant)
03820         {
03821                 result = participant->mIsModeratorMuted;
03822         }
03823         
03824         return result;
03825 }
03826 
03827 F32 LLVoiceClient::getCurrentPower(const LLUUID& id)
03828 {               
03829         F32 result = 0;
03830         participantState *participant = findParticipantByID(id);
03831         if(participant)
03832         {
03833                 result = participant->mPower;
03834         }
03835         
03836         return result;
03837 }
03838 
03839 
03840 LLString LLVoiceClient::getDisplayName(const LLUUID& id)
03841 {
03842         LLString result;
03843         participantState *participant = findParticipantByID(id);
03844         if(participant)
03845         {
03846                 result = participant->mDisplayName;
03847         }
03848         
03849         return result;
03850 }
03851 
03852 
03853 BOOL LLVoiceClient::getUsingPTT(const LLUUID& id)
03854 {
03855         BOOL result = FALSE;
03856 
03857         participantState *participant = findParticipantByID(id);
03858         if(participant)
03859         {
03860                 // I'm not sure what the semantics of this should be.
03861                 // Does "using PTT" mean they're configured with a push-to-talk button?
03862                 // For now, we know there's no PTT mechanism in place, so nobody is using it.
03863         }
03864         
03865         return result;
03866 }
03867 
03868 BOOL LLVoiceClient::getPTTPressed(const LLUUID& id)
03869 {
03870         BOOL result = FALSE;
03871         
03872         participantState *participant = findParticipantByID(id);
03873         if(participant)
03874         {
03875                 result = participant->mPTT;
03876         }
03877         
03878         return result;
03879 }
03880 
03881 BOOL LLVoiceClient::getOnMuteList(const LLUUID& id)
03882 {
03883         BOOL result = FALSE;
03884         
03885         participantState *participant = findParticipantByID(id);
03886         if(participant)
03887         {
03888                 result = participant->mOnMuteList;
03889         }
03890 
03891         return result;
03892 }
03893 
03894 // External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100
03895 // internal = 400 * external^2
03896 F32 LLVoiceClient::getUserVolume(const LLUUID& id)
03897 {
03898         F32 result = 0.0f;
03899         
03900         participantState *participant = findParticipantByID(id);
03901         if(participant)
03902         {
03903                 S32 ires = participant->mUserVolume; // 0-400
03904                 result = sqrtf(((F32)ires) / 400.f);
03905         }
03906 
03907         return result;
03908 }
03909 
03910 void LLVoiceClient::setUserVolume(const LLUUID& id, F32 volume)
03911 {
03912         participantState *participant = findParticipantByID(id);
03913         if (participant)
03914         {
03915                 // volume can amplify by as much as 4x!
03916                 S32 ivol = (S32)(400.f * volume * volume);
03917                 participant->mUserVolume = llclamp(ivol, 0, 400);
03918                 participant->mVolumeDirty = TRUE;
03919                 mVolumeDirty = TRUE;
03920         }
03921 }
03922 
03923 
03924 
03925 LLVoiceClient::serviceType LLVoiceClient::getServiceType(const LLUUID& id)
03926 {
03927         serviceType result = serviceTypeUnknown;
03928 
03929         participantState *participant = findParticipantByID(id);
03930         if(participant)
03931         {
03932                 result = participant->mServiceType;
03933         }
03934         
03935         return result;
03936 }
03937 
03938 std::string LLVoiceClient::getGroupID(const LLUUID& id)
03939 {
03940         std::string result;
03941 
03942         participantState *participant = findParticipantByID(id);
03943         if(participant)
03944         {
03945                 result = participant->mGroupID;
03946         }
03947         
03948         return result;
03949 }
03950 
03951 BOOL LLVoiceClient::getAreaVoiceDisabled()
03952 {
03953         return mAreaVoiceDisabled;
03954 }
03955 
03956 void LLVoiceClient::addObserver(LLVoiceClientParticipantObserver* observer)
03957 {
03958         mObservers.insert(observer);
03959 }
03960 
03961 void LLVoiceClient::removeObserver(LLVoiceClientParticipantObserver* observer)
03962 {
03963         mObservers.erase(observer);
03964 }
03965 
03966 void LLVoiceClient::notifyObservers()
03967 {
03968         for (observer_set_t::iterator it = mObservers.begin();
03969                 it != mObservers.end();
03970                 )
03971         {
03972                 LLVoiceClientParticipantObserver* observer = *it;
03973                 observer->onChange();
03974                 // In case onChange() deleted an entry.
03975                 it = mObservers.upper_bound(observer);
03976         }
03977 }
03978 
03979 void LLVoiceClient::addStatusObserver(LLVoiceClientStatusObserver* observer)
03980 {
03981         mStatusObservers.insert(observer);
03982 }
03983 
03984 void LLVoiceClient::removeStatusObserver(LLVoiceClientStatusObserver* observer)
03985 {
03986         mStatusObservers.erase(observer);
03987 }
03988 
03989 void LLVoiceClient::notifyStatusObservers(LLVoiceClientStatusObserver::EStatusType status)
03990 {
03991         if(status == LLVoiceClientStatusObserver::ERROR_UNKNOWN)
03992         {
03993                 switch(mVivoxErrorStatusCode)
03994                 {
03995                 case 20713:             status = LLVoiceClientStatusObserver::ERROR_CHANNEL_FULL;               break;
03996                 case 20714:             status = LLVoiceClientStatusObserver::ERROR_CHANNEL_LOCKED;     break;
03997                 case 20715:
03998                         //invalid channel, we may be using a set of poorly cached
03999                         //info
04000                         status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
04001                         break;
04002                 case 1009:
04003                         //invalid username and password
04004                         status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
04005                         break;
04006                 }
04007 
04008                 // Reset the error code to make sure it won't be reused later by accident.
04009                 mVivoxErrorStatusCode = 0;
04010         }
04011         
04012         if (status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL 
04013                 //NOT_FOUND || TEMPORARILY_UNAVAILABLE || REQUEST_TIMEOUT
04014                 && (mVivoxErrorStatusCode == 404 || mVivoxErrorStatusCode == 480 || mVivoxErrorStatusCode == 408)) 
04015         {
04016                 // call failed because other user was not available
04017                 // treat this as an error case
04018                 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
04019 
04020                 // Reset the error code to make sure it won't be reused later by accident.
04021                 mVivoxErrorStatusCode = 0;
04022         }
04023         
04024         LL_DEBUGS("Voice") << " " << LLVoiceClientStatusObserver::status2string(status)  << ", session URI " << mSessionURI << LL_ENDL;
04025 
04026         for (status_observer_set_t::iterator it = mStatusObservers.begin();
04027                 it != mStatusObservers.end();
04028                 )
04029         {
04030                 LLVoiceClientStatusObserver* observer = *it;
04031                 observer->onChange(status, mSessionURI, !mNonSpatialChannel);
04032                 // In case onError() deleted an entry.
04033                 it = mStatusObservers.upper_bound(observer);
04034         }
04035 
04036 }
04037 
04038 //static
04039 void LLVoiceClient::onAvatarNameLookup(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* user_data)
04040 {
04041         participantState* statep = gVoiceClient->findParticipantByID(id);
04042 
04043         if (statep)
04044         {
04045                 statep->mDisplayName = llformat("%s %s", first, last);
04046         }
04047         
04048         gVoiceClient->notifyObservers();
04049 }
04050 
04051 class LLViewerParcelVoiceInfo : public LLHTTPNode
04052 {
04053         virtual void post(
04054                 LLHTTPNode::ResponsePtr response,
04055                 const LLSD& context,
04056                 const LLSD& input) const
04057         {
04058                 //the parcel you are in has changed something about its
04059                 //voice information
04060 
04061                 if ( input.has("body") )
04062                 {
04063                         LLSD body = input["body"];
04064 
04065                         //body has "region_name" (str), "parcel_local_id"(int),
04066                         //"voice_credentials" (map).
04067 
04068                         //body["voice_credentials"] has "channel_uri" (str),
04069                         //body["voice_credentials"] has "channel_credentials" (str)
04070                         if ( body.has("voice_credentials") )
04071                         {
04072                                 LLSD voice_credentials = body["voice_credentials"];
04073                                 std::string uri;
04074                                 std::string credentials;
04075 
04076                                 if ( voice_credentials.has("channel_uri") )
04077                                 {
04078                                         uri = voice_credentials["channel_uri"].asString();
04079                                 }
04080                                 if ( voice_credentials.has("channel_credentials") )
04081                                 {
04082                                         credentials =
04083                                                 voice_credentials["channel_credentials"].asString();
04084                                 }
04085 
04086                                 gVoiceClient->setSpatialChannel(uri, credentials);
04087                         }
04088                 }
04089         }
04090 };
04091 
04092 class LLViewerRequiredVoiceVersion : public LLHTTPNode
04093 {
04094         static BOOL sAlertedUser;
04095         virtual void post(
04096                 LLHTTPNode::ResponsePtr response,
04097                 const LLSD& context,
04098                 const LLSD& input) const
04099         {
04100                 //You received this messsage (most likely on region cross or
04101                 //teleport)
04102                 if ( input.has("body") && input["body"].has("major_version") )
04103                 {
04104                         int major_voice_version =
04105                                 input["body"]["major_version"].asInteger();
04106 //                      int minor_voice_version =
04107 //                              input["body"]["minor_version"].asInteger();
04108 
04109                         if (gVoiceClient &&
04110                                 (major_voice_version > VOICE_MAJOR_VERSION) )
04111                         {
04112                                 if (!sAlertedUser)
04113                                 {
04114                                         //sAlertedUser = TRUE;
04115                                         gViewerWindow->alertXml("VoiceVersionMismatch");
04116                                         gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener
04117                                 }
04118                         }
04119                 }
04120         }
04121 };
04122 BOOL LLViewerRequiredVoiceVersion::sAlertedUser = FALSE;
04123 
04124 LLHTTPRegistration<LLViewerParcelVoiceInfo>
04125     gHTTPRegistrationMessageParcelVoiceInfo(
04126                 "/message/ParcelVoiceInfo");
04127 
04128 LLHTTPRegistration<LLViewerRequiredVoiceVersion>
04129     gHTTPRegistrationMessageRequiredVoiceVersion(
04130                 "/message/RequiredVoiceVersion");

Generated on Fri May 16 08:34:22 2008 for SecondLife by  doxygen 1.5.5