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