llvoiceclient.cpp

Go to the documentation of this file.
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"            // for gUserServerChoice
00046 #include "llfloateractivespeakers.h"    // for LLSpeakerMgr
00047 #include "llbase64.h"
00048 #include "llviewercontrol.h"
00049 #include "llkeyboard.h"
00050 #include "viewer.h"     // for gDisconnected, gDisableVoice
00051 #include "llmutelist.h"  // to check for muted avatars
00052 #include "llagent.h"
00053 #include "llcachename.h"
00054 #include "llimview.h" // for LLIMMgr
00055 #include "llimpanel.h" // for LLVoiceChannel
00056 #include "llparcel.h"
00057 #include "llviewerparcelmgr.h"
00058 #include "llfirstuse.h"
00059 #include "llviewerwindow.h"
00060 
00061 // for base64 decoding
00062 #include "apr-1/apr_base64.h"
00063 
00064 // for SHA1 hash
00065 #include "apr-1/apr_sha1.h"
00066 
00067 // If we are connecting to agni AND the user's last name is "Linden", join this channel instead of looking up the sim name.
00068 // If we are connecting to agni and the user's last name is NOT "Linden", disable voice.
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 // Don't retry connecting to the daemon more frequently than this:
00081 const F32 CONNECT_THROTTLE_SECONDS = 1.0f;
00082 
00083 // Don't send positional updates more frequently than this:
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                         //TODO: throw an error message?
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         /* @name LLIOPipe virtual implementations
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         // Expat control members
00158         XML_Parser              parser;
00159         int                             responseDepth;
00160         bool                    ignoringTags;
00161         bool                    isEvent;
00162         int                             ignoreDepth;
00163 
00164         // Members for processing responses. The values are transient and only valid within a call to processResponse().
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         // Members for processing events. The values are transient and only valid within a call to processResponse().
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         // Members for processing text between tags
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 //virtual 
00225 LLVivoxProtocolParser::~LLVivoxProtocolParser()
00226 {
00227         if (parser)
00228                 XML_ParserFree(parser);
00229 }
00230 
00231 // virtual
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         // MBW -- XXX -- This should no longer be necessary.  Or even possible.
00249         // We've read all the data out of the buffer.  Make sure it doesn't accumulate.
00250 //      buffer->clear();
00251         
00252         // Look for input delimiter(s) in the input buffer.  If one is found, send the message to the xml parser.
00253         int start = 0;
00254         int delim;
00255         while((delim = mInput.find("\n\n\n", start)) != std::string::npos)
00256         {       
00257                 // Turn this on to log incoming XML
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                                 // This is a Set3DPosition response.  Don't print it, since these are way too spammy.
00265                         }
00266                         else if(bar != std::string::npos && (bar < delim))
00267                         {
00268                                 // This is a ParticipantPropertiesEvent response.  Don't print it, since these are way too spammy.
00269                         }
00270                         else
00271                         {
00272                                 llinfos << "parsing: " << mInput.substr(start, delim - start) << llendl;
00273                         }
00274                 }
00275                 
00276                 // Reset internal state of the LLVivoxProtocolParser (no effect on the expat parser)
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 //      llinfos << "at end, mInput is: " << mInput << llendl;
00292         
00293         if(!gVoiceClient->mConnected)
00294         {
00295                 // If voice has been disabled, we just want to close the socket.  This does so.
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         // Reset the text accumulator. We shouldn't have strings that are inturrupted by new tags
00340         textBuffer.clear();
00341         // only accumulate text if we're not ignoring tags.
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                         // Grab the attributes
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                 //llinfos << tag << " (" << responseDepth << ")"  << llendl;
00371         }
00372         else
00373         {
00374                 if (ignoringTags)
00375                 {
00376                         //llinfos << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << llendl;
00377                 }
00378                 else
00379                 {
00380                         //llinfos << tag << " (" << responseDepth << ")"  << llendl;
00381         
00382                         // Ignore the InputXml stuff so we don't get confused
00383                         if (strcmp("InputXml", tag) == 0)
00384                         {
00385                                 ignoringTags = true;
00386                                 ignoreDepth = responseDepth;
00387                                 accumulateText = false;
00388 
00389                                 //llinfos << "starting ignore, ignoreDepth is " << ignoreDepth << llendl;
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                         //llinfos << "end of ignore" << llendl;
00418                         ignoringTags = false;
00419                 }
00420                 else
00421                 {
00422                         //llinfos << "ignoring tag " << tag << " (depth = " << responseDepth << ")" << llendl;
00423                 }
00424         }
00425         
00426         if (!ignoringTags)
00427         {
00428                 //llinfos << "processing tag " << tag << " (depth = " << responseDepth << ")" << llendl;
00429 
00430                 // Closing a tag. Finalize the text we've accumulated and reset
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                         // This closing tag shouldn't clear the accumulated text.
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                         // We finished all of the XML, process the data
00511                         processResponse(tag);
00512                 }
00513         }
00514 }
00515 
00516 // --------------------------------------------------------------------------------
00517 
00518 void LLVivoxProtocolParser::CharData(const char *buffer, int length)
00519 {
00520         /*
00521                 This method is called for anything that isn't a tag, which can be text you
00522                 want that lies between tags, and a lot of stuff you don't want like file formatting
00523                 (tabs, spaces, CR/LF, etc).
00524                 
00525                 Only copy text if we are in accumulate mode...
00526         */
00527         if (accumulateText)
00528                 textBuffer.append(buffer, length);
00529 }
00530 
00531 // --------------------------------------------------------------------------------
00532 
00533 void LLVivoxProtocolParser::processResponse(std::string tag)
00534 {
00535 //      llinfos << tag << llendl;
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                 else if (actionString == "Connector.AccountCreate.1")
00601                 {
00602                         
00603                 }
00604                 else if (actionString == "Connector.MuteLocalMic.1")
00605                 {
00606                         
00607                 }
00608                 else if (actionString == "Connector.MuteLocalSpeaker.1")
00609                 {
00610                         
00611                 }
00612                 else if (actionString == "Connector.SetLocalMicVolume.1")
00613                 {
00614                         
00615                 }
00616                 else if (actionString == "Connector.SetLocalSpeakerVolume.1")
00617                 {
00618                         
00619                 }
00620                 else if (actionString == "Session.ListenerSetPosition.1")
00621                 {
00622                         
00623                 }
00624                 else if (actionString == "Session.SpeakerSetPosition.1")
00625                 {
00626                         
00627                 }
00628                 else if (actionString == "Session.Set3DPosition.1")
00629                 {
00630                         
00631                 }
00632                 else if (actionString == "Session.AudioSourceSetPosition.1")
00633                 {
00634                         
00635                 }
00636                 else if (actionString == "Session.GetChannelParticipants.1")
00637                 {
00638                         
00639                 }
00640                 else if (actionString == "Account.ChannelCreate.1")
00641                 {
00642                         
00643                 }
00644                 else if (actionString == "Account.ChannelUpdate.1")
00645                 {
00646                         
00647                 }
00648                 else if (actionString == "Account.ChannelDelete.1")
00649                 {
00650                         
00651                 }
00652                 else if (actionString == "Account.ChannelCreateAndInvite.1")
00653                 {
00654                         
00655                 }
00656                 else if (actionString == "Account.ChannelFolderCreate.1")
00657                 {
00658                         
00659                 }
00660                 else if (actionString == "Account.ChannelFolderUpdate.1")
00661                 {
00662                         
00663                 }
00664                 else if (actionString == "Account.ChannelFolderDelete.1")
00665                 {
00666                         
00667                 }
00668                 else if (actionString == "Account.ChannelAddModerator.1")
00669                 {
00670                         
00671                 }
00672                 else if (actionString == "Account.ChannelDeleteModerator.1")
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                 // Note: Ignore the specific event value, look up the ones we want
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         /* virtual */ 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);      // called with bad status codes
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                 // A kill with signal number 0 has no effect, just does error checking.  It should return an error if the process no longer exists.
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         // Initial dirty state
00842         mSpatialCoordsDirty = false;
00843         mPTTDirty = true;
00844         mVolumeDirty = true;
00845         mSpeakerVolumeDirty = true;
00846         mMicVolumeDirty = true;
00847         mCaptureDeviceDirty = false;
00848         mRenderDeviceDirty = false;
00849 
00850         // Load initial state from prefs.
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         // Set up our listener to get updates on all prefs values we care about.
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         //  gMuteListp isn't set up at this point, so we defer this until later.
00882 //      gMuteListp->addObserver(&mutelist_listener);
00883         
00884         mParticipantMapChanged = false;
00885 
00886         // stash the pump for later use
00887         // This now happens when init() is called instead.
00888         mPump = NULL;
00889         
00890 #if LL_DARWIN || LL_LINUX
00891                 // MBW -- XXX -- THIS DOES NOT BELONG HERE
00892                 // When the vivox daemon dies, the next write attempt on our socket generates a SIGPIPE, which kills us.
00893                 // This should cause us to ignore SIGPIPE and handle the error through proper channels.
00894                 // This should really be set up elsewhere.  Where should it go?
00895                 signal(SIGPIPE, SIG_IGN);
00896                 
00897                 // Since we're now launching the gateway with fork/exec instead of system(), we need to deal with zombie processes.
00898                 // Ignoring SIGCHLD should prevent zombies from being created.  Alternately, we could use wait(), but I'd rather not do that.
00899                 signal(SIGCHLD, SIG_IGN);
00900 #endif
00901 
00902         // set up state machine
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         // constructor will set up gVoiceClient
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();            // Need to do this now -- bad things happen if the destructor does it later.
00932                 
00933                 // This will do unpleasant things on windows.
00934 //              killGateway();
00935                 
00936                 // Don't do this anymore -- LLSingleton will take care of deleting the object.          
00937 //              delete gVoiceClient;
00938                 
00939                 // Hint to other code not to access the voice client anymore.
00940                 gVoiceClient = NULL;
00941         }
00942 }
00943 
00944 
00946 // utility functions
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 //              llinfos << "sending: " << str << llendl;
00958 
00959                 // MBW -- XXX -- check return code - sockets will fail (broken, etc.)
00960                 err = apr_socket_send(
00961                                 mSocket->getSocket(),
00962                                 (const char*)str.data(),
00963                                 &written);
00964                 
00965                 if(err == 0)
00966                 {
00967                         // Success.
00968                         result = true;
00969                 }
00970                 // MBW -- XXX -- handle partial writes (written is number of bytes written)
00971                 // Need to set socket to non-blocking before this will work.
00972 //              else if(APR_STATUS_IS_EAGAIN(err))
00973 //              {
00974 //                      // 
00975 //              }
00976                 else
00977                 {
00978                         // Assume any socket error means something bad.  For now, just close the socket.
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 // session control messages
00991 void LLVoiceClient::connectorCreate()
00992 {
00993         std::ostringstream stream;
00994         std::string logpath;
00995         std::string loglevel = "0";
00996         
00997         // Transition to stateConnectorStarted when the connector handle comes back.
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         // MBW -- XXX -- Enable this when the bhd.vivox.com server gets a real ssl cert.        
01064         if(sConnectingToAgni)
01065         {
01066                 // Use the release account server
01067                 mAccountServerName = "bhr.vivox.com";
01068                 mAccountServerURI = "https://www." + mAccountServerName + "/api2/";
01069         }
01070         else
01071         {
01072                 // Use the development account server
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                 // Already logged in.  This is an internal error.
01104                 llerrs << "called from wrong state." << llendl;
01105         }
01106         else if ( accountName != mAccountName )
01107         {
01108                 //TODO: error?
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                 // Prevent copy-paste errors when updating this list...
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                 // Prevent copy-paste errors when updating this list...
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                 // The viewer has been disconnected from the sim.  Disable voice.
01214                 setVoiceEnabled(false);
01215         }
01216         
01217         if(!mVoiceEnabled)
01218         {
01219                 if(getState() != stateDisabled)
01220                 {
01221                         // User turned off voice support.  Send the cleanup messages, close the socket, and reset.
01222                         if(!mConnected)
01223                         {
01224                                 // if voice was turned off after the daemon was launched but before we could connect to it, we may need to issue a kill.
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         // Check for parcel boundary crossing
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 //                      llinfos << "Region name = \"" << regionName <<"\", " << "parcel local ID = " << parcelLocalID << llendl;
01256 
01257                         // The region name starts out empty and gets filled in later.  
01258                         // Also, the cap gets filled in a short time after the region cross, but a little too late for our purposes.
01259                         // If either is empty, wait for the next time around.
01260                         if(!regionName.empty() && !capURI.empty())
01261                         {
01262                                 if((parcelLocalID != mCurrentParcelLocalID) || (regionName != mCurrentRegionName))
01263                                 {
01264                                         // We have changed parcels.  Initiate a parcel channel lookup.
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                                 // Voice is locked out, we must not launch the vivox daemon.
01287                                 setState(stateJail);
01288                         }
01289                         else if(!isGatewayRunning())
01290                         {
01291                                 if(true)
01292                                 {
01293                                         // Launch the voice daemon
01294                                         std::string exe_path = gDirUtilp->getAppRODataDir();
01295                                         exe_path += gDirUtilp->getDirDelimiter();
01296 #if LL_WINDOWS
01297                                         exe_path += "SLVoice.exe";
01298 #else
01299                                         // This will be the same for mac and linux
01300                                         exe_path += "SLVoice";
01301 #endif
01302                                         // See if the vivox executable exists
01303                                         llstat s;
01304                                         if(!LLFile::stat(exe_path.c_str(), &s))
01305                                         {
01306                                                 // vivox executable exists.  Build the command line and launch the daemon.
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";        // turn logging off completely
01314                                                 }
01315                                                 
01316                                                 args += " -ll ";
01317                                                 args += loglevel;
01318                                                 
01319 //                                              llinfos << "Args for SLVoice: " << args << llendl;
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                                                 // So retarded.  Windows requires that the second parameter to CreateProcessA be a writable (non-const) string...
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 //                                                      DWORD dwErr = GetLastError();
01336                                                 }
01337                                                 else
01338                                                 {
01339                                                         // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
01340                                                         // CloseHandle(pinfo.hProcess); // stops leaks - nothing else
01341                                                         sGatewayHandle = pinfo.hProcess;
01342                                                         CloseHandle(pinfo.hThread); // stops leaks - nothing else
01343                                                 }               
01344                                                 
01345                                                 delete[] args2;
01346 #else   // LL_WINDOWS
01347                                                 // This should be the same for mac and linux
01348                                                 {
01349                                                         std::vector<std::string> arglist;
01350                                                         arglist.push_back(exe_path.c_str());
01351                                                         
01352                                                         // Split the argument string into separate strings for each argument
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                                                         // create an argv vector for the child process
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                                                                 // child
01375                                                                 execv(exe_path.c_str(), fakeargv);
01376                                                                 
01377                                                                 // If we reach this point, the exec failed.
01378                                                                 // Use _exit() instead of exit() per the vfork man page.
01379                                                                 _exit(0);
01380                                                         }
01381 
01382                                                         // parent
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                                         // We can connect to a client gateway running on another host.  This is useful for testing.
01397                                         // To do this, launch the gateway on a nearby host like this:
01398                                         //  vivox-gw.exe -p tcp -i 0.0.0.0:44124
01399                                         // and put that host's IP address here.
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                                 // Dirty the states we'll need to sync with the daemon when it comes up.
01409                                 mPTTDirty = true;
01410                                 mSpeakerVolumeDirty = true;
01411                                 // These only need to be set if they're not default (i.e. empty string).
01412                                 mCaptureDeviceDirty = !mCaptureDevice.empty();
01413                                 mRenderDeviceDirty = !mRenderDevice.empty();
01414                         }
01415                 break;
01416                 
01417                 case stateDaemonLaunched:
01418 //                      llinfos << "Connecting to vivox daemon" << llendl;
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                                         // If the connect failed, the socket may have been put into a bad state.  Delete it.
01436                                         closeSocket();
01437                                 }
01438                         }
01439                 break;
01440 
01441                 case stateConnecting:
01442                 // Can't do this until we have the pump available.
01443                 if(mPump)
01444                 {
01445                         // MBW -- Note to self: pumps and pipes examples in
01446                         //  indra/test/io.cpp
01447                         //  indra/test/llpipeutil.{cpp|h}
01448 
01449                         // Attach the pumps and pipes
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                         // Initial devices query
01465                         getCaptureDevicesSendMessage();
01466                         getRenderDevicesSendMessage();
01467 
01468                         mLoginRetryCount = 0;
01469                         
01470                         setState(stateConnectorStart);
01471                                 
01472                 break;
01473                 
01474                 case stateConnectorStart:
01475                         if(!mVoiceEnabled)
01476                         {
01477                                 // We were never logged in.  This will shut down the connector.
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:    // waiting for connector handle
01491                         // connectorCreateResponse() will transition from here to stateConnectorStarted.
01492                 break;
01493                 
01494                 case stateConnectorStarted:             // connector handle received
01495                         if(!mVoiceEnabled)
01496                         {
01497                                 // We were never logged in.  This will shut down the connector.
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                         // Both of these behave essentially the same.  The only difference is where the exit transition goes to.
01522                         if(mTuningMode && mVoiceEnabled && !mSessionTerminateRequested)
01523                         {       
01524                                 if(!mTuningCaptureRunning)
01525                                 {
01526                                         // duration parameter is currently unused, per Mike S.
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                                 // transition out of mic tuning
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                                 // First retry -- display a message to the user
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:                    // waiting for account handle
01630                         // loginResponse() will transition from here to stateLoggedIn.
01631                 break;
01632                 
01633                 case stateLoggedIn:                             // account handle received
01634                         // Initial kick-off of channel lookup logic
01635                         parcelChanged();
01636 
01637                         notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LOGGED_IN);
01638 
01639                         // Set up the mute list observer if it hasn't been set up already.
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                                 // MBW -- XXX -- Is this the right way out of this state?
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:               // waiting for session handle
01682                         // sessionCreateResponse() will transition from here to stateSessionJoined.
01683                         if(!mVoiceEnabled)
01684                         {
01685                                 // User bailed out during connect -- jump straight to teardown.
01686                                 setState(stateSessionTerminated);
01687                         }
01688                         else if(mSessionTerminateRequested)
01689                         {
01690                                 if(!mSessionHandle.empty())
01691                                 {
01692                                         // Only allow direct exits from this state in p2p calls (for cancelling an invite).
01693                                         // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
01694                                         if(mSessionP2P)
01695                                         {
01696                                                 sessionTerminateSendMessage();
01697                                                 setState(stateSessionTerminated);
01698                                         }
01699                                 }
01700                         }
01701                 break;
01702                 
01703                 case stateSessionJoined:                // session handle received
01704                         // MBW -- XXX -- It appears that I need to wait for BOTH the Session.Create response and the SessionStateChangeEvent with state 4
01705                         //  before continuing from this state.  They can happen in either order, and if I don't wait for both, things can get stuck.
01706                         // For now, the Session.Create response handler sets mSessionHandle and the SessionStateChangeEvent handler transitions to stateSessionJoined.
01707                         // This is a cheap way to make sure both have happened before proceeding.
01708                         if(!mSessionHandle.empty())
01709                         {
01710                                 // Events that need to happen when a session is joined could go here.
01711                                 // Maybe send initial spatial data?
01712                                 notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_JOINED);
01713 
01714                                 // Dirty state that may need to be sync'ed with the daemon.
01715                                 mPTTDirty = true;
01716                                 mSpeakerVolumeDirty = true;
01717                                 mSpatialCoordsDirty = true;
01718                                 
01719                                 setState(stateRunning);
01720                                 
01721                                 // Start the throttle timer
01722                                 mUpdateTimer.start();
01723                                 mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
01724                         }
01725                         else if(!mVoiceEnabled)
01726                         {
01727                                 // User bailed out during connect -- jump straight to teardown.
01728                                 setState(stateSessionTerminated);
01729                         }
01730                         else if(mSessionTerminateRequested)
01731                         {
01732                                 // Only allow direct exits from this state in p2p calls (for cancelling an invite).
01733                                 // Terminating a half-connected session on other types of calls seems to break something in the vivox gateway.
01734                                 if(mSessionP2P)
01735                                 {
01736                                         sessionTerminateSendMessage();
01737                                         setState(stateSessionTerminated);
01738                                 }
01739                         }
01740                 break;
01741                 
01742                 case stateRunning:                              // steady state
01743                         // sessionTerminateSendMessage() will transition from here to stateLeavingSession
01744                         
01745                         // Disabling voice or disconnect requested.
01746                         if(!mVoiceEnabled || mSessionTerminateRequested)
01747                         {
01748                                 sessionTerminateSendMessage();
01749                         }
01750                         else
01751                         {
01752                                 
01753                                 // Figure out whether the PTT state needs to change
01754                                 {
01755                                         bool newPTT;
01756                                         if(mUsePTT)
01757                                         {
01758                                                 // If configured to use PTT, track the user state.
01759                                                 newPTT = mUserPTTState;
01760                                         }
01761                                         else
01762                                         {
01763                                                 // If not configured to use PTT, it should always be true (otherwise the user will be unable to speak).
01764                                                 newPTT = true;
01765                                         }
01766                                         
01767                                         if(mMuteMic)
01768                                         {
01769                                                 // This always overrides any other PTT setting.
01770                                                 newPTT = false;
01771                                         }
01772                                         
01773                                         // Dirty if state changed.
01774                                         if(newPTT != mPTT)
01775                                         {
01776                                                 mPTT = newPTT;
01777                                                 mPTTDirty = true;
01778                                         }
01779                                 }
01780                                 
01781                                 if(mNonSpatialChannel)
01782                                 {
01783                                         // When in a non-spatial channel, never send positional updates.
01784                                         mSpatialCoordsDirty = false;
01785                                 }
01786                                 else
01787                                 {
01788                                         // Do the calculation that enforces the listener<->speaker tether (and also updates the real camera position)
01789                                         enforceTether();
01790                                 }
01791                                 
01792                                 // Send an update if the ptt state has changed (which shouldn't be able to happen that often -- the user can only click so fast)
01793                                 // or every 10hz, whichever is sooner.
01794                                 if(mVolumeDirty || mPTTDirty || mSpeakerVolumeDirty || mUpdateTimer.hasExpired())
01795                                 {
01796                                         mUpdateTimer.setTimerExpirySec(UPDATE_THROTTLE_SECONDS);
01797                                         sendPositionalUpdate();
01798                                 }
01799                         }
01800                 break;
01801                 
01802                 case stateLeavingSession:               // waiting for terminate session response
01803                         // The handler for the Session.Terminate response will transition from here to stateSessionTerminated.
01804                 break;
01805 
01806                 case stateSessionTerminated:
01807                         // Always reset the terminate request flag when we get here.
01808                         mSessionTerminateRequested = false;
01809                         
01810                         notifyStatusObservers(LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL);
01811 
01812                         if(mVoiceEnabled)
01813                         {
01814                                 // SPECIAL CASE: if going back to spatial but in a parcel with an empty URI, transfer the non-spatial flag now.
01815                                 // This fixes the case where you come out of a group chat in a parcel with voice disabled, and get stuck unable to rejoin spatial chat thereafter.
01816                                 if(mNextSessionSpatial && mNextSessionURI.empty())
01817                                 {
01818                                         mNonSpatialChannel = !mNextSessionSpatial;
01819                                 }
01820                                 
01821                                 // Just leaving a channel, go back to stateNoChannel (the "logged in but have no channel" state).
01822                                 setState(stateNoChannel);
01823                         }
01824                         else
01825                         {
01826                                 // Shutting down voice, continue with disconnecting.
01827                                 logout();
01828                         }
01829                         
01830                 break;
01831                 
01832                 case stateLoggingOut:                   // waiting for logout response
01833                         // The handler for the Account.Logout response will transition from here to stateLoggedOut.
01834                 break;
01835                 case stateLoggedOut:                    // logout response received
01836                         // shut down the connector
01837                         connectorShutdown();
01838                 break;
01839                 
01840                 case stateConnectorStopping:    // waiting for connector stop
01841                         // The handler for the Connector.InitiateShutdown response will transition from here to stateConnectorStopped.
01842                 break;
01843 
01844                 case stateConnectorStopped:             // connector stop received
01845                         // Clean up and reset everything. 
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                         // No way to recover from these.  Yet.
01862                 break;
01863 
01864                 case stateJoinSessionFailed:
01865                         // Transition to error state.  Send out any notifications here.
01866                         llwarns << "stateJoinSessionFailed: (" << mVivoxErrorStatusCode << "): " << mVivoxErrorStatusString << llendl;
01867                         notifyStatusObservers(LLVoiceClientStatusObserver::ERROR_UNKNOWN);
01868                         setState(stateJoinSessionFailedWaiting);
01869                 break;
01870                 
01871                 case stateJoinSessionFailedWaiting:
01872                         // Joining a channel failed, either due to a failed channel name -> sip url lookup or an error from the join message.
01873                         // Region crossings may leave this state and try the join again.
01874                         if(mSessionTerminateRequested)
01875                         {
01876                                 setState(stateSessionTerminated);
01877                         }
01878                 break;
01879                 
01880                 case stateJail:
01881                         // We have given up.  Do nothing.
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                 // Clear the stashed URI so it can't reconnect
01959                 mNextSessionURI.clear();
01960         }
01961         // Only p2p sessions are created with "no reconnect".
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();             // never want to re-use these.
01995         mNextP2PSessionURI.clear();
01996         mNonSpatialChannel = !mNextSessionSpatial;
01997         mSessionResetOnClose = mNextSessionResetOnClose;
01998         mNextSessionResetOnClose = false;
01999         // Joining by session ID is only used to answer p2p invitations, so we know this is a p2p session.
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                         // In this case, we want to pretend the join failed so our state machine doesn't get stuck.
02025                         // Skip the join failed transition state so we don't send out error notifications.
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         // MBW -- XXX -- do something here
02090         llinfos << "called" << llendl;
02091         mCaptureDevices.clear();
02092 }
02093 
02094 void LLVoiceClient::addCaptureDevice(const std::string& name)
02095 {
02096         // MBW -- XXX -- do something here
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         // MBW -- XXX -- do something here
02130         llinfos << "called" << llendl;
02131         mRenderDevices.clear();
02132 }
02133 
02134 void LLVoiceClient::addRenderDevice(const std::string& name)
02135 {
02136         // MBW -- XXX -- do something here
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         // The daemon died, so the connection is gone.  Reset everything and start over.
02308         llwarns << "Connection to vivox daemon lost.  Resetting state."<< llendl;
02309 
02310         closeSocket();
02311         removeAllParticipants();
02312         
02313         // Try to relaunch the daemon
02314         setState(stateDisabled);
02315 }
02316 
02317 void LLVoiceClient::giveUp()
02318 {
02319         // All has failed.  Clean up and stop trying.
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                 // Always send both speaker and listener positions together.
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 //              llinfos << "Sending speaker position " << mSpeakerPosition << llendl;
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 //              llinfos << "Sending listener position " << mListenerPosition << llendl;
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                 // Send a local mute command.
02444                 // NOTE that the state of "PTT" is the inverse of "local mute".
02445                 //   (i.e. when PTT is true, we send a mute command with "false", and vice versa)
02446                 
02447 //              llinfos << "Sending MuteLocalMic command with parameter " << (mPTT?"false":"true") << llendl;
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                                 // Send a mute/unumte command for the user (actually "volume for me").
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         // MBW -- XXX -- Maybe check to make sure the capture/render devices are in the current list here?
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 // Response/Event handlers
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                 // Connector created, move forward.
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         // Status code of 20200 means "bad password".  We may want to special-case that at some point.
02588         
02589         if ( statusCode == 401 )
02590         {
02591                 // Login failure which is probably caused by the delay after a user's password being updated.
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                 // Login succeeded, move forward.
02603                 mAccountHandle = accountHandle;
02604                 // MBW -- XXX -- This needs to wait until the LoginStateChangeEvent is received.
02605 //              if(getState() == stateLoggingIn)
02606 //              {
02607 //                      setState(stateLoggedIn);
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                 // Got the channel list, try to do a lookup.
02622                 std::string uri = findChannelURI(mChannelName);
02623                 if(uri.empty())
02624                 {       
02625                         // Lookup failed, can't join a channel for this area.
02626                         llinfos << "failed to map channel name: " << mChannelName << llendl;
02627                 }
02628                 else
02629                 {
02630                         // We have a sip URL for this area.
02631                         llinfos << "mapped channel " << mChannelName << " to URI "<< uri << llendl;
02632                 }
02633                 
02634                 // switchChannel with an empty uri string will do the right thing (leave channel and not rejoin)
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 //              if(statusCode == 1015)
02645 //              {
02646 //                      if(getState() == stateJoiningSession)
02647 //                      {
02648 //                              // this happened during a real join.  Going to sessionTerminated should cause a retry in appropriate cases.
02649 //                              llwarns << "session handle \"" << sessionHandle << "\", mSessionStateEventHandle \"" << mSessionStateEventHandle << "\""<< llendl;
02650 //                              if(!sessionHandle.empty())
02651 //                              {
02652 //                                      // This session is bad.  Terminate it.
02653 //                                      mSessionHandle = sessionHandle;
02654 //                                      sessionTerminateByHandle(sessionHandle);
02655 //                                      setState(stateLeavingSession);
02656 //                              }
02657 //                              else if(!mSessionStateEventHandle.empty())
02658 //                              {
02659 //                                      mSessionHandle = mSessionStateEventHandle;
02660 //                                      sessionTerminateByHandle(mSessionStateEventHandle);
02661 //                                      setState(stateLeavingSession);
02662 //                              }
02663 //                              else
02664 //                              {
02665 //                                      setState(stateSessionTerminated);
02666 //                              }
02667 //                      }
02668 //                      else
02669 //                      {
02670 //                              // We didn't think we were in the middle of a join.  Don't change state.
02671 //                              llwarns << "Not in stateJoiningSession, ignoring" << llendl;
02672 //                      }
02673 //              }
02674 //              else
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                         // This is also grabbed in the SessionStateChangeEvent handler, but it might be useful to have it early...
02687                         mSessionHandle = sessionHandle;
02688                 }
02689                 else
02690                 {
02691                         // We should never get a session.create response in any state except stateJoiningSession.  Things are out of sync.  Kill this session.
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 //              if(statusCode == 1015)
02703 //              {
02704 //                      llwarns << "terminating existing session" << llendl;
02705 //                      sessionTerminate();
02706 //              }
02707 //              else
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                         // This is probably "(404): Server reporting Failure. Not a member of this conference."
02728                         // Do this so we don't get stuck.
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                 // MBW -- XXX -- Should this ever fail?  do we care if it does?
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                 // MBW -- XXX -- Should this ever fail?  do we care if it does?
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: // I see this when joining the session
02777                         llinfos << "joined session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << llendl;
02778 
02779                         // Session create succeeded, move forward.
02780                         mSessionStateEventHandle = sessionHandle;
02781                         mSessionStateEventURI = uriString;
02782                         if(sessionHandle == mSessionHandle)
02783                         {
02784                                 // This is the session we're joining.
02785                                 if(getState() == stateJoiningSession)
02786                                 {
02787                                         setState(stateSessionJoined);
02788                                         //RN: the uriString being returned by vivox here is actually your account uri, not the channel
02789                                         // you are attempting to join, so ignore it
02790                                         //llinfos << "received URI " << uriString << "(previously " << mSessionURI << ")" << llendl;
02791                                         //mSessionURI = uriString;
02792                                 }
02793                         }
02794                         else if(sessionHandle == mNextSessionHandle)
02795                         {
02796 //                              llinfos << "received URI " << uriString << ", name " << nameString << " for next session (handle " << mNextSessionHandle << ")" << llendl;
02797                         }
02798                         else
02799                         {
02800                                 llwarns << "joining unknown session handle " << sessionHandle << ", URI " << uriString << ", name " << nameString << llendl;
02801                                 // MBW -- XXX -- Should we send a Session.Terminate here?
02802                         }
02803                         
02804                 break;
02805                 case 5: // I see this when leaving the session
02806                         llinfos << "left session " << uriString << ", name " << nameString << " handle " << mNextSessionHandle << llendl;
02807 
02808                         // Set the session handle to the empty string.  If we get back to stateJoiningSession, we'll want to wait for the new session handle.
02809                         if(sessionHandle == mSessionHandle)
02810                         {
02811                                 // MBW -- XXX -- I think this is no longer necessary, now that we've got mNextSessionURI/mNextSessionHandle
02812                                 // mSessionURI.clear();
02813                                 // clear the session handle here just for sanity.
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                                                 // normal transition
02834                                                 llinfos << "left session " << sessionHandle << "in state " << state2string(getState()) << llendl;
02835                                                 setState(stateSessionTerminated);
02836                                         break;
02837                                         
02838                                         case stateSessionTerminated:
02839                                                 // this will happen sometimes -- there are cases where we send the terminate and then go straight to this state.
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                                 // store status values for later notification of observers
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                 According to Mike S., status codes for this event are:
02876                 login_state_logged_out=0,
02877         login_state_logged_in = 1,
02878         login_state_logging_in = 2,
02879         login_state_logging_out = 3,
02880         login_state_resetting = 4,
02881         login_state_error=100   
02882         */
02883         
02884         switch(state)
02885         {
02886                 case 1:
02887                 if(getState() == stateLoggingIn)
02888                 {
02889                         setState(stateLoggedIn);
02890                 }
02891                 break;
02892                 
02893                 default:
02894 //                      llwarns << "unknown state: " << state << llendl;
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 //      llinfos << "state is " << state << llendl;
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: // I see this when a participant joins
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: // I see this when a participant leaves
02961                         participant = findParticipant(uriString);
02962                         if(participant)
02963                         {
02964                                 removeParticipant(participant);
02965                         }
02966                 break;
02967                 default:
02968 //                      llwarns << "unknown state: " << state << llendl;
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 //      llinfos << "got energy " << energy << llendl;
03004         mTuningEnergy = energy;
03005 }
03006 
03007 void LLVoiceClient::muteListChanged()
03008 {
03009         // The user's mute list has been updated.  Go through the current participant list and sync it with the mute list.
03010 
03011         participantMap::iterator iter = mParticipantMap.begin();
03012         
03013         for(; iter != mParticipantMap.end(); iter++)
03014         {
03015                 participantState *p = iter->second;
03016                 
03017                 // Check to see if this participant is on the mute list already
03018                 updateMuteState(p);
03019         }
03020 }
03021 
03023 // Managing list of participants
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                 // Found a matching participant already in the map.
03039                 result = iter->second;
03040         }
03041 
03042         if(!result)
03043         {
03044                 // participant isn't already in one list or the other.
03045                 result = new participantState(uri);
03046                 mParticipantMap.insert(participantMap::value_type(uri, result));
03047                 mParticipantMapChanged = true;
03048                 
03049                 // Try to do a reverse transform on the URI to get the GUID back.
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         // Don't find any participants if we're not connected.  This is so that we don't continue to get stale data
03116         // after the daemon dies.
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         // You'd think this would work, but it doesn't...
03136 //      std::string uri = sipURIFromAvatar(avatar);
03137         
03138         // Currently, the URI is just the account name.
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                         // We just figured out the avatar ID, so the participant list has "changed" from the perspective of anyone who uses that to identify participants.
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         // Currently, the URI is just the account name.
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 //      llinfos << "Adding channel name mapping: " << name << " -> " << uri << llendl;
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                 // If the user is logged in, start a channel lookup.
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                 // The transition to stateLoggedIn needs to kick this off again.
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                         // Always switch to the new URI from these states.
03236                         needsSwitch = true;
03237                 break;
03238                 
03239                 default:
03240                         if(mSessionTerminateRequested)
03241                         {
03242                                 // If a terminate has been requested, we need to compare against where the URI we're already headed to.
03243                                 if(mNextSessionURI != uri)
03244                                         needsSwitch = true;
03245                         }
03246                         else
03247                         {
03248                                 // Otherwise, compare against the URI we're in now.
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                         // Leave any channel we may be in
03267                         llinfos << "leaving channel" << llendl;
03268                 }
03269                 else
03270                 {
03271                         llinfos << "switching to channel " << uri << llendl;
03272                 }
03273                 
03274                 if(getState() <= stateNoChannel)
03275                 {
03276                         // We're already set up to join a channel, just needed to fill in the session URI
03277                 }
03278                 else
03279                 {
03280                         // State machine will come around and rejoin if uri/handle is not empty.
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                 // We're already set up to join a channel, just needed to fill in the session handle
03298         }
03299         else
03300         {
03301                 // State machine will come around and rejoin if uri/handle is not empty.
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                 // User is in a non-spatial chat or joining a non-spatial chat.  Don't switch channels.
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         // Prepending this apparently prevents conflicts with reserved names inside the vivox and diamondware code.
03419         result = "x";
03420         
03421         // Base64 encode and replace the pieces of base64 that are less compatible 
03422         // with e-mail local-parts.
03423         // See RFC-4648 "Base 64 Encoding with URL and Filename Safe Alphabet"
03424         result += LLBase64::encode(rawuuid, UUID_BYTES);
03425         LLString::replaceChar(result, '+', '-');
03426         LLString::replaceChar(result, '/', '_');
03427         
03428         // If you need to transform a GUID to this form on the Mac OS X command line, this will do so:
03429         // echo -n x && (echo e669132a-6c43-4ee1-a78d-6c82fff59f32 |xxd -r -p |openssl base64|tr '/+' '_-')
03430         
03431         return result;
03432 }
03433 
03434 bool LLVoiceClient::IDFromName(const std::string name, LLUUID &uuid)
03435 {
03436         bool result = false;
03437         
03438         // This will only work if the name is of the proper form.
03439         // As an example, the account name for Monroe Linden (UUID 1673cfd3-8229-4445-8d92-ec3570e5e587) is:
03440         // "xFnPP04IpREWNkuw1cOXlhw=="
03441         
03442         if((name.size() == 25) && (name[0] == 'x') && (name[23] == '=') && (name[24] == '='))
03443         {
03444                 // The name appears to have the right form.
03445 
03446                 // Reverse the transforms done by nameFromID
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                         // The decode succeeded.  Stuff the bits into the result's UUID
03456                         // MBW -- XXX -- there's no analogue of LLUUID::toCompressedString that allows you to set a UUID from binary data.
03457                         // The data field is public, so we cheat thusly:
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 //      LLString::toLower(result);
03480 
03481         return result;
03482 }
03483 
03485 // Sending updates of current state
03486 
03487 void LLVoiceClient::enforceTether(void)
03488 {
03489         LLVector3d tethered     = mCameraRequestedPosition;
03490 
03491         // constrain 'tethered' to within 50m of mAvatarPosition.
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 //              llinfos << "leaving channel for teleport/logout" << llendl;
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                         // for now, leave active channel, to auto join when turning voice back on
03605                         //LLVoiceChannel::getCurrentVoiceChannel->deactivate();
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                 // When the user turns on PTT, reset the current state.
03620                 mUserPTTState = false;
03621         }
03622         mUsePTT = usePTT;
03623 }
03624 
03625 void LLVoiceClient::setPTTIsToggle(bool PTTIsToggle)
03626 {
03627         if(!PTTIsToggle && mPTTIsToggle)
03628         {
03629                 // When the user turns off toggle, reset the current state.
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                         // If the call failed, don't match any key.
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                 // The name has been filled in already, which means we know whether we're connecting to agni or not.
03695                 if(!sConnectingToAgni)
03696                 {
03697                         // Only use the setting if we're connecting to a development grid -- always use bhr when on agni.
03698                         mAccountServerName = serverName;
03699                 }
03700         }
03701 }
03702 
03703 void LLVoiceClient::keyDown(KEY key, MASK mask)
03704 {       
03705 //      llinfos << "key is " << LLKeyboard::stringFromKey(key) << llendl;
03706 
03707         if (gKeyboard->getKeyRepeated(key))
03708         {
03709                 // ignore auto-repeat keys
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 // Accessors for data related to nearby speakers
03758 BOOL LLVoiceClient::getVoiceEnabled(const LLUUID& id)
03759 {
03760         BOOL result = FALSE;
03761         participantState *participant = findParticipantByID(id);
03762         if(participant)
03763         {
03764                 // I'm not sure what the semantics of this should be.
03765                 // For now, if we have any data about the user that came through the chat channel, assume they're voice-enabled.
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                 // I'm not sure what the semantics of this should be.
03823                 // Does "using PTT" mean they're configured with a push-to-talk button?
03824                 // For now, we know there's no PTT mechanism in place, so nobody is using it.
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 // External accessiors. Maps 0.0 to 1.0 to internal values 0-400 with .5 == 100
03857 // internal = 400 * external^2
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; // 0-400
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                 // volume can amplify by as much as 4x!
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                 // In case onChange() deleted an entry.
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                 // Reset the error code to make sure it won't be reused later by accident.
03964                 mVivoxErrorStatusCode = 0;
03965         }
03966         
03967         if (status == LLVoiceClientStatusObserver::STATUS_LEFT_CHANNEL 
03968                 //NOT_FOUND || TEMPORARILY_UNAVAILABLE || REQUEST_TIMEOUT
03969                 && (mVivoxErrorStatusCode == 404 || mVivoxErrorStatusCode == 480 || mVivoxErrorStatusCode == 408)) 
03970         {
03971                 // call failed because other user was not available
03972                 // treat this as an error case
03973                 status = LLVoiceClientStatusObserver::ERROR_NOT_AVAILABLE;
03974 
03975                 // Reset the error code to make sure it won't be reused later by accident.
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                 // In case onError() deleted an entry.
03988                 it = mStatusObservers.upper_bound(observer);
03989         }
03990 
03991 }
03992 
03993 //static
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                 //the parcel you are in has changed something about its
04014                 //voice information
04015 
04016                 if ( input.has("body") )
04017                 {
04018                         LLSD body = input["body"];
04019 
04020                         //body has "region_name" (str), "parcel_local_id"(int),
04021                         //"voice_credentials" (map).
04022 
04023                         //body["voice_credentials"] has "channel_uri" (str),
04024                         //body["voice_credentials"] has "channel_credentials" (str)
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                 //You received this messsage (most likely on region cross or
04056                 //teleport)
04057                 if ( input.has("body") && input["body"].has("major_version") )
04058                 {
04059                         int major_voice_version =
04060                                 input["body"]["major_version"].asInteger();
04061 //                      int minor_voice_version =
04062 //                              input["body"]["minor_version"].asInteger();
04063 
04064                         if (gVoiceClient &&
04065                                 (major_voice_version > VOICE_MAJOR_VERSION) )
04066                         {
04067                                 if (!sAlertedUser)
04068                                 {
04069                                         //sAlertedUser = TRUE;
04070                                         gViewerWindow->alertXml("VoiceVersionMismatch");
04071                                         gSavedSettings.setBOOL("EnableVoiceChat", FALSE); // toggles listener
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");

Generated on Thu Jul 1 06:09:37 2010 for Second Life Viewer by  doxygen 1.4.7